packages feed

moonlight-core-0.1.0.0: src-term/Moonlight/Core/Term/Database/Table.hs

{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Moonlight.Core.Term.Database.Table where

import Data.IntMap.Strict (IntMap)
import Data.IntMap.Strict qualified as IntMap
import Data.Map.Strict qualified as Map
import Moonlight.Core.DenseKey (DenseKey (..))
import Moonlight.Core.Language (Language)
import Moonlight.Core.Term.Database.Encode
import Moonlight.Core.Term.Database.OperatorTable
import Moonlight.Core.Term.Database.Types
import Prelude

insertTuple ::
  (DenseKey key, Language f) =>
  key ->
  f key ->
  Database f key ->
  Database f key
insertTuple resultValue tupleValue =
  insertEncodedDatabaseRow
    (extractOperator tupleValue)
    (encodedRow resultValue tupleValue)
{-# INLINE insertTuple #-}

insertTuples ::
  forall key f.
  (DenseKey key, Language f) =>
  [(key, f key)] ->
  Database f key ->
  Database f key
insertTuples entries database =
  snd (insertTuplesWithInsertedRows entries database)

insertTuplesWithInsertedRows ::
  forall key f.
  (DenseKey key, Language f) =>
  [(key, f key)] ->
  Database f key ->
  (IntMap [(RowId, DatabaseRow)], Database f key)
insertTuplesWithInsertedRows entries database =
  IntMap.foldlWithKey'
    insertOperatorRows
    (IntMap.empty, internedDatabase)
    rowsByOperatorId
  where
    (internedDatabase, rowsByOperatorId) =
      foldl'
        collectRowsByOperatorId
        (database, IntMap.empty)
        entries

    insertOperatorRows ::
      (IntMap [(RowId, DatabaseRow)], Database f key) ->
      Int ->
      (Operator f, [DatabaseRow]) ->
      (IntMap [(RowId, DatabaseRow)], Database f key)
    insertOperatorRows (insertedRows, currentDatabase) operatorId (operator, reversedRows) =
      ( insertOperatorInsertedRows operatorId operatorInsertedRows insertedRows,
        updatedDatabase
      )
      where
        (operatorInsertedRows, updatedDatabase) =
          insertEncodedDatabaseRowsWithInsertedRows
            operatorId
            operator
            (reverse reversedRows)
            currentDatabase

    collectRowsByOperatorId ::
      (Database f key, IntMap (Operator f, [DatabaseRow])) ->
      (key, f key) ->
      (Database f key, IntMap (Operator f, [DatabaseRow]))
    collectRowsByOperatorId (currentDatabase, operatorRows) (resultValue, tupleValue) =
      ( internedOperatorDatabase,
        IntMap.insertWith
          prependOperatorRows
          operatorId
          (operator, [encodedRow resultValue tupleValue])
          operatorRows
      )
      where
        operator =
          extractOperator tupleValue
        (operatorId, internedOperatorDatabase) =
          internOperator operator currentDatabase

    prependOperatorRows ::
      (Operator f, [DatabaseRow]) ->
      (Operator f, [DatabaseRow]) ->
      (Operator f, [DatabaseRow])
    prependOperatorRows (_newOperator, newRows) (operator, existingRows) =
      (operator, newRows <> existingRows)

    insertOperatorInsertedRows ::
      Int ->
      [(RowId, DatabaseRow)] ->
      IntMap [(RowId, DatabaseRow)] ->
      IntMap [(RowId, DatabaseRow)]
    insertOperatorInsertedRows operatorId rows insertedRows =
      case rows of
        [] ->
          insertedRows
        _ ->
          IntMap.insert operatorId rows insertedRows

insertEncodedDatabaseRow ::
  (Foldable f, forall a. Ord a => Ord (f a)) =>
  Operator f ->
  DatabaseRow ->
  Database f key ->
  Database f key
insertEncodedDatabaseRow operator@(Operator shape) row database =
  case nextRowId updatedTable == nextRowId table of
    True ->
      internedDatabase
    False ->
      invalidateOperatorArrangements operatorId $
        internedDatabase
          { operatorTables =
              IntMap.insert operatorId updatedTable (operatorTables internedDatabase)
          }
  where
    (operatorId, internedDatabase) =
      internOperator operator database
    table =
      IntMap.findWithDefault
        (emptyOperatorTable shape)
        operatorId
        (operatorTables internedDatabase)
    updatedTable =
      insertEncodedRow row table
{-# INLINE insertEncodedDatabaseRow #-}

insertEncodedDatabaseRowsWithInsertedRows ::
  Foldable f =>
  Int ->
  Operator f ->
  [DatabaseRow] ->
  Database f key ->
  ([(RowId, DatabaseRow)], Database f key)
insertEncodedDatabaseRowsWithInsertedRows _operatorId _operator [] database =
  ([], database)
insertEncodedDatabaseRowsWithInsertedRows operatorId (Operator shape) rows database =
  case insertedRows of
    [] ->
      ([], database)
    _ ->
      ( insertedRows,
        invalidateOperatorArrangements operatorId $
          database
            { operatorTables =
                IntMap.insert operatorId updatedTable (operatorTables database)
            }
      )
  where
    table =
      IntMap.findWithDefault
        (emptyOperatorTable shape)
        operatorId
        (operatorTables database)
    (insertedRows, updatedTable) =
      insertEncodedRowsWithInsertedRows rows table

internOperator ::
  (forall a. Ord a => Ord (f a)) =>
  Operator f ->
  Database f key ->
  (Int, Database f key)
internOperator operator database =
  case Map.lookup operator (operatorIds database) of
    Just operatorId ->
      (operatorId, database)
    Nothing ->
      ( operatorId,
        database
          { operatorIds = Map.insert operator operatorId (operatorIds database),
            operatorShapes = IntMap.insert operatorId operator (operatorShapes database),
            nextOperatorId = operatorId + 1
          }
      )
      where
        operatorId =
          nextOperatorId database
{-# INLINE internOperator #-}

deleteRow ::
  (forall a. Ord a => Ord (f a)) =>
  Operator f ->
  RowId ->
  Database f key ->
  Database f key
deleteRow operator (RowId rowKey) database =
  deleteOperatorRows operator (deleteEncodedRow rowKey) database

deleteTuple ::
  (DenseKey key, Language f) =>
  key ->
  f key ->
  Database f key ->
  Database f key
deleteTuple resultValue tupleValue database =
  deleteOperatorRows operator (deleteEncodedRows row) database
  where
    operator =
      extractOperator tupleValue
    row =
      encodedRow resultValue tupleValue

deleteOperatorRows ::
  (forall a. Ord a => Ord (f a)) =>
  Operator f ->
  (OperatorTable f -> OperatorTable f) ->
  Database f key ->
  Database f key
deleteOperatorRows operator deleteRows database =
  case operatorIdFor operator database of
    Nothing ->
      database
    Just operatorId ->
      deleteOperatorRowsByOperatorId operatorId deleteRows database

deleteOperatorRowsByOperatorId ::
  Int ->
  (OperatorTable f -> OperatorTable f) ->
  Database f key ->
  Database f key
deleteOperatorRowsByOperatorId operatorId deleteRows database =
  case IntMap.lookup operatorId (operatorTables database) of
    Nothing ->
      database
    Just table ->
      let updatedTable =
            deleteRows table
       in if tableLiveRowCount updatedTable == tableLiveRowCount table
            then database
            else
              invalidateOperatorArrangements operatorId $
                database
                  { operatorTables =
                      IntMap.update
                        (const (keepNonEmptyTable updatedTable))
                        operatorId
                        (operatorTables database)
                  }

ensureOperatorDerivedIndexes ::
  Int ->
  Database f key ->
  Database f key
ensureOperatorDerivedIndexes operatorId database =
  case IntMap.lookup operatorId (operatorTables database) of
    Just table
      | derivedIndexWatermark table /= nextRowId table ->
          database
            { operatorTables =
                IntMap.insert
                  operatorId
                  (ensureDerivedIndexes table)
                  (operatorTables database)
            }
    _ ->
      database
{-# INLINE ensureOperatorDerivedIndexes #-}

invalidateOperatorArrangements :: Int -> Database f key -> Database f key
invalidateOperatorArrangements operatorId database =
  case IntMap.null (arrangements database) of
    True ->
      database
    False ->
      database
        { arrangements =
            IntMap.delete operatorId (arrangements database)
        }
{-# INLINE invalidateOperatorArrangements #-}

compact :: Foldable f => Database f key -> Database f key
compact database =
  database
    { operatorTables =
        IntMap.mapMaybe compactOperatorTable (operatorTables database),
      arrangements = IntMap.empty
    }
{-# INLINE compact #-}

compactOperatorTable :: Foldable f => OperatorTable f -> Maybe (OperatorTable f)
compactOperatorTable table =
  keepNonEmptyTable (operatorTableFromRows (opShape table) (snd <$> operatorTableRows table))
{-# INLINE compactOperatorTable #-}