packages feed

moonlight-homology-0.1.0.0: src-matrix/Moonlight/Homology/Pure/Rank/GF2.hs

{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE StandaloneKindSignatures #-}

module Moonlight.Homology.Pure.Rank.GF2
  ( GF2RankFailure (..),
    PreparedGF2Boundary,
    pgbIncidence,
    pgbPackedMatrix,
    prepareGF2Boundary,
    rankPreparedGF2Boundary,
    gf2BoundaryRank,
  )
where

import Data.Bifunctor
  ( first,
  )
import Data.Kind
  ( Type,
  )
import Data.Maybe
  ( mapMaybe,
  )
import Moonlight.Homology.Boundary.LinAlg
  ( BoundaryEntry,
    BoundaryIncidence,
    BoundaryIncidenceShapeError,
    boundaryCoefficient,
    boundaryEntries,
    mkBoundaryIncidence,
    sourceCardinality,
    sourceIndex,
    targetCardinality,
    targetIndex,
  )
import Moonlight.LinAlg.Pure.Dense.GF2
  ( GF2 (..),
    GF2MatrixEntry (..),
    GF2PackedMatrix,
    GF2PackedMatrixFailure,
    mkGF2PackedMatrix,
    rankGF2PackedMatrix,
  )
import Numeric.Natural
  ( Natural,
  )

type GF2RankFailure :: Type
data GF2RankFailure
  = GF2RankNegativeSourceCardinality !Int
  | GF2RankNegativeTargetCardinality !Int
  | GF2RankBoundaryShapeInvalid !BoundaryIncidenceShapeError
  | GF2RankPackedMatrixInvalid !GF2PackedMatrixFailure
  deriving stock (Eq, Show)

type PreparedGF2Boundary :: Type
data PreparedGF2Boundary = PreparedGF2Boundary
  { preparedBoundaryIncidence :: !(BoundaryIncidence GF2),
    preparedBoundaryPackedMatrix :: !GF2PackedMatrix
  }
  deriving stock (Eq, Show)

pgbIncidence :: PreparedGF2Boundary -> BoundaryIncidence GF2
pgbIncidence =
  preparedBoundaryIncidence

pgbPackedMatrix :: PreparedGF2Boundary -> GF2PackedMatrix
pgbPackedMatrix =
  preparedBoundaryPackedMatrix

prepareGF2Boundary ::
  BoundaryIncidence GF2 ->
  Either GF2RankFailure PreparedGF2Boundary
prepareGF2Boundary incidence = do
  sourceDimension <- sourceCardinalityNatural incidence
  targetDimension <- targetCardinalityNatural incidence
  canonicalIncidence <-
    first GF2RankBoundaryShapeInvalid $
      mkBoundaryIncidence sourceDimension targetDimension (boundaryEntries incidence)
  packedMatrix <-
    first GF2RankPackedMatrixInvalid $
      mkGF2PackedMatrix
        targetDimension
        sourceDimension
        (boundaryEntryMatrixEntry <$> nonzeroEntries canonicalIncidence)
  Right
    PreparedGF2Boundary
      { preparedBoundaryIncidence = canonicalIncidence,
        preparedBoundaryPackedMatrix = packedMatrix
      }
{-# INLINEABLE prepareGF2Boundary #-}

rankPreparedGF2Boundary :: PreparedGF2Boundary -> Int
rankPreparedGF2Boundary =
  rankGF2PackedMatrix . preparedBoundaryPackedMatrix
{-# INLINE rankPreparedGF2Boundary #-}

gf2BoundaryRank ::
  BoundaryIncidence GF2 ->
  Either GF2RankFailure Int
gf2BoundaryRank =
  fmap rankPreparedGF2Boundary . prepareGF2Boundary
{-# INLINEABLE gf2BoundaryRank #-}

nonzeroEntries :: BoundaryIncidence GF2 -> [BoundaryEntry GF2]
nonzeroEntries =
  mapMaybe nonzeroEntry . boundaryEntries
  where
    nonzeroEntry entry =
      case boundaryCoefficient entry of
        GF2Zero -> Nothing
        GF2One -> Just entry
{-# INLINE nonzeroEntries #-}

boundaryEntryMatrixEntry :: BoundaryEntry GF2 -> GF2MatrixEntry
boundaryEntryMatrixEntry entry =
  GF2MatrixEntry
    { gf2EntryRow = targetIndex entry,
      gf2EntryColumn = sourceIndex entry
    }
{-# INLINE boundaryEntryMatrixEntry #-}

sourceCardinalityNatural ::
  BoundaryIncidence r ->
  Either GF2RankFailure Natural
sourceCardinalityNatural incidence =
  nonnegativeNatural
    (GF2RankNegativeSourceCardinality (sourceCardinality incidence))
    (sourceCardinality incidence)
{-# INLINE sourceCardinalityNatural #-}

targetCardinalityNatural ::
  BoundaryIncidence r ->
  Either GF2RankFailure Natural
targetCardinalityNatural incidence =
  nonnegativeNatural
    (GF2RankNegativeTargetCardinality (targetCardinality incidence))
    (targetCardinality incidence)
{-# INLINE targetCardinalityNatural #-}

nonnegativeNatural ::
  failure ->
  Int ->
  Either failure Natural
nonnegativeNatural failureValue value =
  if value < 0
    then Left failureValue
    else Right (fromIntegral value)
{-# INLINE nonnegativeNatural #-}