packages feed

moonlight-algebra-0.1.0.0: src-finite-lattice/Moonlight/FiniteLattice/Internal/Layout.hs

{-# LANGUAGE GHC2024 #-}

module Moonlight.FiniteLattice.Internal.Layout
  ( checkedRelationWordCount,
    checkedPairCellCount,
  )
where

import Data.Word (Word64)
import Foreign.Storable (sizeOf)
import Moonlight.FiniteLattice.Internal.Key
  ( contextKeySetChunkCount,
  )
import Moonlight.FiniteLattice.Internal.Types
  ( ContextCompileLimits (..),
    ContextLatticeCompileError (..),
    ContextRepresentation (..),
  )
import Numeric.Natural (Natural)

data ContextLayoutFailure
  = ContextLayoutCountOverflow !Integer
  | ContextLayoutLimitExceeded !Integer !Natural
  deriving stock (Eq, Ord, Show, Read)

checkedRelationWordCount ::
  ContextCompileLimits ->
  Int ->
  Either (ContextLatticeCompileError c) Int
checkedRelationWordCount limits size =
  case
    checkedRepresentationProduct
      (cclMaximumRelationBytes limits)
      (sizeOf (0 :: Word64))
      2
      [size, contextKeySetChunkCount size]
    of
    Left (ContextLayoutCountOverflow requestedCount) ->
      Left (ContextLatticeRepresentationOverflow ContextRelationWords requestedCount)
    Left (ContextLayoutLimitExceeded requestedBytes maximumBytes) ->
      Left
        ( ContextLatticeRepresentationLimitExceeded
            ContextRelationWords
            requestedBytes
            maximumBytes
        )
    Right wordCount -> Right wordCount

checkedPairCellCount ::
  ContextCompileLimits ->
  Int ->
  Maybe Int
checkedPairCellCount limits size =
  either
    (const Nothing)
    Just
    ( checkedRepresentationProduct
        (cclMaximumBinaryTableBytes limits)
        (sizeOf (0 :: Int))
        2
        [size, size]
    )

checkedRepresentationProduct ::
  Maybe Natural ->
  Int ->
  Integer ->
  [Int] ->
  Either ContextLayoutFailure Int
checkedRepresentationProduct byteLimit bytesPerElement retainedCopies factors =
  case countResult of
    Left overflow ->
      Left overflow
    Right tableEntryCount ->
      case limitResult tableEntryCount of
        Left limitError -> Left limitError
        Right () -> Right (fromInteger tableEntryCount)
  where
    requestedCount =
      product (fmap toInteger factors)

    countResult
      | requestedCount <= toInteger (maxBound :: Int) =
          Right requestedCount
      | otherwise =
          Left (ContextLayoutCountOverflow requestedCount)

    requestedBytes requestedCountValue =
      requestedCountValue
        * toInteger bytesPerElement
        * retainedCopies

    limitResult requestedCountValue =
      case byteLimit of
        Nothing ->
          Right ()
        Just maximumBytes
          | requestedBytes requestedCountValue <= toInteger maximumBytes ->
              Right ()
          | otherwise ->
              Left
                ( ContextLayoutLimitExceeded
                    (requestedBytes requestedCountValue)
                    maximumBytes
                )