packages feed

moonlight-category-0.1.0.0: src-simplicial/Moonlight/Category/Pure/Simplicial/Validation/Internal.hs

module Moonlight.Category.Pure.Simplicial.Validation.Internal
  ( SimplicialLawEquality,
    simplicialLawEq,
    SimplicialLawKind (..),
    allSimplicialLawKinds,
    SimplicialLawIndices (..),
    SimplicialLawObstruction (..),
    lawObstructionKind,
    SimplicialLawCarrier (..),
    SimplicialLawCheck,
    checkObstructions,
    dimensionsStrictlyBelow,
    checkFaceFaceLawBy,
    checkDegeneracyDegeneracyLawBy,
    checkFaceDegeneracyLawBy,
    checkSimplicialLawsBy,
  )
where

import Data.Kind (Type)
import Data.List.NonEmpty (NonEmpty (..))
import Numeric.Natural (Natural)

type SimplicialLawEquality :: Type -> Type
type SimplicialLawEquality simplex = Maybe simplex -> Maybe simplex -> Bool

simplicialLawEq :: Eq simplex => SimplicialLawEquality simplex
simplicialLawEq = (==)

type SimplicialLawKind :: Type
data SimplicialLawKind
  = FaceFaceLaw
  | DegeneracyDegeneracyLaw
  | FaceDegeneracyLaw
  deriving stock (Eq, Ord, Show, Read, Enum, Bounded)

allSimplicialLawKinds :: [SimplicialLawKind]
allSimplicialLawKinds = [minBound .. maxBound]

type SimplicialLawIndices :: Type
data SimplicialLawIndices
  = FaceFaceIndices Natural Natural
  | DegeneracyDegeneracyIndices Natural Natural
  | FaceDegeneracyIndices Natural Natural
  deriving stock (Eq, Ord, Show)

type SimplicialLawObstruction :: Type -> Type
data SimplicialLawObstruction simplex = SimplicialLawObstruction
  { lawObstructionDimension :: Natural,
    lawObstructionSimplex :: simplex,
    lawObstructionIndices :: SimplicialLawIndices,
    lawObstructionLeftResult :: Maybe simplex,
    lawObstructionRightResult :: Maybe simplex
  }
  deriving stock (Eq, Show)

lawIndicesKind :: SimplicialLawIndices -> SimplicialLawKind
lawIndicesKind indices =
  case indices of
    FaceFaceIndices _ _ -> FaceFaceLaw
    DegeneracyDegeneracyIndices _ _ -> DegeneracyDegeneracyLaw
    FaceDegeneracyIndices _ _ -> FaceDegeneracyLaw

lawObstructionKind :: SimplicialLawObstruction simplex -> SimplicialLawKind
lawObstructionKind =
  lawIndicesKind . lawObstructionIndices

type SimplicialLawCarrier :: Type -> Type
data SimplicialLawCarrier simplex = SimplicialLawCarrier
  { lawCarrierUpperBound :: Natural,
    lawCarrierSimplicesAtDimension :: Natural -> [simplex],
    lawCarrierFaceAtDimension :: Natural -> Natural -> simplex -> Maybe simplex,
    lawCarrierDegeneracyAtDimension :: Natural -> Natural -> simplex -> Maybe simplex
  }

type SimplicialLawCheck :: Type -> Type
type SimplicialLawCheck simplex = Either (NonEmpty (SimplicialLawObstruction simplex)) ()

checkObstructions :: [obstruction] -> Either (NonEmpty obstruction) ()
checkObstructions obstructions =
  case obstructions of
    [] -> Right ()
    firstObstruction : remainingObstructions -> Left (firstObstruction :| remainingObstructions)

dimensionsStrictlyBelow :: Natural -> [Natural]
dimensionsStrictlyBelow upperBound =
  if upperBound == 0
    then []
    else [0 .. upperBound - 1]

dimensionsAtLeastTwoBelowBound :: Natural -> [Natural]
dimensionsAtLeastTwoBelowBound upperBound =
  if upperBound < 2
    then []
    else [0 .. upperBound - 2]

obstructionUnlessEqual ::
  SimplicialLawEquality simplex ->
  Natural ->
  simplex ->
  SimplicialLawIndices ->
  Maybe simplex ->
  Maybe simplex ->
  [SimplicialLawObstruction simplex]
obstructionUnlessEqual areEqual dimensionValue simplexValue indices leftResult rightResult =
  if areEqual leftResult rightResult
    then []
    else
      [ SimplicialLawObstruction
          { lawObstructionDimension = dimensionValue,
            lawObstructionSimplex = simplexValue,
            lawObstructionIndices = indices,
            lawObstructionLeftResult = leftResult,
            lawObstructionRightResult = rightResult
          }
      ]

faceFaceLawObstructionsBy :: SimplicialLawEquality simplex -> SimplicialLawCarrier simplex -> [SimplicialLawObstruction simplex]
faceFaceLawObstructionsBy areEqual carrier =
  [ obstruction
  | dimensionValue <- [2 .. lawCarrierUpperBound carrier],
    simplexValue <- lawCarrierSimplicesAtDimension carrier dimensionValue,
    leftFaceIndex <- [0 .. dimensionValue - 1],
    rightFaceIndex <- [leftFaceIndex + 1 .. dimensionValue],
    let leftResult =
          lawCarrierFaceAtDimension carrier (dimensionValue - 1) leftFaceIndex
            =<< lawCarrierFaceAtDimension carrier dimensionValue rightFaceIndex simplexValue,
    let rightResult =
          lawCarrierFaceAtDimension carrier (dimensionValue - 1) (rightFaceIndex - 1)
            =<< lawCarrierFaceAtDimension carrier dimensionValue leftFaceIndex simplexValue,
    obstruction <-
      obstructionUnlessEqual
        areEqual
        dimensionValue
        simplexValue
        (FaceFaceIndices leftFaceIndex rightFaceIndex)
        leftResult
        rightResult
  ]

degeneracyDegeneracyLawObstructionsBy :: SimplicialLawEquality simplex -> SimplicialLawCarrier simplex -> [SimplicialLawObstruction simplex]
degeneracyDegeneracyLawObstructionsBy areEqual carrier =
  [ obstruction
  | dimensionValue <- dimensionsAtLeastTwoBelowBound (lawCarrierUpperBound carrier),
    simplexValue <- lawCarrierSimplicesAtDimension carrier dimensionValue,
    leftDegeneracyIndex <- [0 .. dimensionValue],
    rightDegeneracyIndex <- [leftDegeneracyIndex .. dimensionValue],
    let leftResult =
          lawCarrierDegeneracyAtDimension carrier (dimensionValue + 1) leftDegeneracyIndex
            =<< lawCarrierDegeneracyAtDimension carrier dimensionValue rightDegeneracyIndex simplexValue,
    let rightResult =
          lawCarrierDegeneracyAtDimension carrier (dimensionValue + 1) (rightDegeneracyIndex + 1)
            =<< lawCarrierDegeneracyAtDimension carrier dimensionValue leftDegeneracyIndex simplexValue,
    obstruction <-
      obstructionUnlessEqual
        areEqual
        dimensionValue
        simplexValue
        (DegeneracyDegeneracyIndices leftDegeneracyIndex rightDegeneracyIndex)
        leftResult
        rightResult
  ]

expectedFaceDegeneracy ::
  SimplicialLawCarrier simplex ->
  Natural ->
  simplex ->
  Natural ->
  Natural ->
  Maybe simplex
expectedFaceDegeneracy carrier dimensionValue simplexValue faceIndex degeneracyIndex
  | faceIndex < degeneracyIndex =
      lawCarrierDegeneracyAtDimension carrier (dimensionValue - 1) (degeneracyIndex - 1)
        =<< lawCarrierFaceAtDimension carrier dimensionValue faceIndex simplexValue
  | faceIndex == degeneracyIndex = Just simplexValue
  | faceIndex == degeneracyIndex + 1 = Just simplexValue
  | otherwise =
      lawCarrierDegeneracyAtDimension carrier (dimensionValue - 1) degeneracyIndex
        =<< lawCarrierFaceAtDimension carrier dimensionValue (faceIndex - 1) simplexValue

faceDegeneracyLawObstructionsBy :: SimplicialLawEquality simplex -> SimplicialLawCarrier simplex -> [SimplicialLawObstruction simplex]
faceDegeneracyLawObstructionsBy areEqual carrier =
  [ obstruction
  | dimensionValue <- dimensionsStrictlyBelow (lawCarrierUpperBound carrier),
    simplexValue <- lawCarrierSimplicesAtDimension carrier dimensionValue,
    degeneracyIndex <- [0 .. dimensionValue],
    faceIndex <- [0 .. dimensionValue + 1],
    let leftResult =
          lawCarrierFaceAtDimension carrier (dimensionValue + 1) faceIndex
            =<< lawCarrierDegeneracyAtDimension carrier dimensionValue degeneracyIndex simplexValue,
    let rightResult = expectedFaceDegeneracy carrier dimensionValue simplexValue faceIndex degeneracyIndex,
    obstruction <-
      obstructionUnlessEqual
        areEqual
        dimensionValue
        simplexValue
        (FaceDegeneracyIndices faceIndex degeneracyIndex)
        leftResult
        rightResult
  ]

checkFaceFaceLawBy :: SimplicialLawEquality simplex -> SimplicialLawCarrier simplex -> SimplicialLawCheck simplex
checkFaceFaceLawBy areEqual =
  checkObstructions . faceFaceLawObstructionsBy areEqual

checkDegeneracyDegeneracyLawBy :: SimplicialLawEquality simplex -> SimplicialLawCarrier simplex -> SimplicialLawCheck simplex
checkDegeneracyDegeneracyLawBy areEqual =
  checkObstructions . degeneracyDegeneracyLawObstructionsBy areEqual

checkFaceDegeneracyLawBy :: SimplicialLawEquality simplex -> SimplicialLawCarrier simplex -> SimplicialLawCheck simplex
checkFaceDegeneracyLawBy areEqual =
  checkObstructions . faceDegeneracyLawObstructionsBy areEqual

checkSimplicialLawsBy :: SimplicialLawEquality simplex -> SimplicialLawCarrier simplex -> SimplicialLawCheck simplex
checkSimplicialLawsBy areEqual carrier =
  checkObstructions
    ( faceFaceLawObstructionsBy areEqual carrier
        <> degeneracyDegeneracyLawObstructionsBy areEqual carrier
        <> faceDegeneracyLawObstructionsBy areEqual carrier
    )