packages feed

moonlight-homology-0.1.0.0: src-topology/Moonlight/Homology/Pure/Topology/MacroScaffold/Direction.hs

module Moonlight.Homology.Pure.Topology.MacroScaffold.Direction
  ( DirectionSymmetryOrder,
    DirectionSymmetryOrderError (..),
    unDirectionSymmetryOrder,
    mkDirectionSymmetryOrder,
    DirectionPhase,
    DirectionPhaseError (..),
    unDirectionPhase,
    mkDirectionPhase,
    DirectionCoefficient,
    DirectionCoefficientError (..),
    unDirectionCoefficient,
    mkDirectionCoefficient,
    DirectionFieldEncoding (..),
    DirectionField,
    DirectionFieldError (..),
    directionFieldCarrier,
    directionFieldSymmetryOrder,
    directionFieldEncoding,
    mkDirectionField,
    mkDirectionAngleField,
    mkDirectionCochainField,
  )
where

import Data.Fixed (mod')
import Data.Kind (Type)
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Moonlight.Core
  ( mkFiniteWith,
    mkPositiveIntWith,
  )
import Moonlight.Homology.Pure.Carrier
  ( BasisCellRef,
    CellCarrier,
    carrierCells,
  )

type DirectionSymmetryOrder :: Type
newtype DirectionSymmetryOrder = DirectionSymmetryOrder
  { unDirectionSymmetryOrder :: Int
  }
  deriving stock (Eq, Ord, Show)

type DirectionSymmetryOrderError :: Type
data DirectionSymmetryOrderError
  = NonPositiveDirectionSymmetryOrder Int
  deriving stock (Eq, Show)

mkDirectionSymmetryOrder :: Int -> Either DirectionSymmetryOrderError DirectionSymmetryOrder
mkDirectionSymmetryOrder =
  mkPositiveIntWith NonPositiveDirectionSymmetryOrder DirectionSymmetryOrder

type DirectionPhase :: Type
newtype DirectionPhase = DirectionPhase
  { unDirectionPhase :: Double
  }
  deriving stock (Eq, Ord, Show)

type DirectionPhaseError :: Type
data DirectionPhaseError
  = NonFiniteDirectionPhase Double
  deriving stock (Eq, Show)

mkDirectionPhase :: Double -> Either DirectionPhaseError DirectionPhase
mkDirectionPhase =
  mkFiniteWith NonFiniteDirectionPhase DirectionPhase

type DirectionCoefficient :: Type
newtype DirectionCoefficient = DirectionCoefficient
  { unDirectionCoefficient :: Double
  }
  deriving stock (Eq, Ord, Show)

type DirectionCoefficientError :: Type
data DirectionCoefficientError
  = NonFiniteDirectionCoefficient Double
  deriving stock (Eq, Show)

mkDirectionCoefficient :: Double -> Either DirectionCoefficientError DirectionCoefficient
mkDirectionCoefficient =
  mkFiniteWith NonFiniteDirectionCoefficient DirectionCoefficient

type DirectionFieldEncoding :: Type
data DirectionFieldEncoding
  = DirectionAngleEncoding (Map.Map BasisCellRef DirectionPhase)
  | DirectionCochainEncoding (Map.Map BasisCellRef DirectionCoefficient)
  deriving stock (Eq, Show)

type DirectionField :: Type
data DirectionField = DirectionField
  { directionFieldCarrier :: CellCarrier,
    directionFieldSymmetryOrder :: DirectionSymmetryOrder,
    directionFieldEncoding :: DirectionFieldEncoding
  }
  deriving stock (Eq, Show)

type DirectionFieldError :: Type
data DirectionFieldError = DirectionFieldCoverageMismatch
  { directionFieldMissingCells :: [BasisCellRef],
    directionFieldExtraneousCells :: [BasisCellRef]
  }
  deriving stock (Eq, Show)

mkDirectionField ::
  CellCarrier ->
  DirectionSymmetryOrder ->
  DirectionFieldEncoding ->
  Either DirectionFieldError DirectionField
mkDirectionField carrierValue symmetryOrderValue encodingValue =
  let normalizedEncoding = normalizeDirectionFieldEncoding symmetryOrderValue encodingValue
      carrierDomain = Set.fromList (carrierCells carrierValue)
      encodingDomain = directionFieldEncodingDomain normalizedEncoding
      missingCells = Set.toAscList (carrierDomain `Set.difference` encodingDomain)
      extraneousCells = Set.toAscList (encodingDomain `Set.difference` carrierDomain)
   in case (missingCells, extraneousCells) of
        ([], []) ->
          Right
            DirectionField
              { directionFieldCarrier = carrierValue,
                directionFieldSymmetryOrder = symmetryOrderValue,
                directionFieldEncoding = normalizedEncoding
              }
        _ ->
          Left
            DirectionFieldCoverageMismatch
              { directionFieldMissingCells = missingCells,
                directionFieldExtraneousCells = extraneousCells
              }

mkDirectionAngleField ::
  CellCarrier ->
  DirectionSymmetryOrder ->
  Map.Map BasisCellRef DirectionPhase ->
  Either DirectionFieldError DirectionField
mkDirectionAngleField carrierValue symmetryOrderValue phaseMap =
  mkDirectionField carrierValue symmetryOrderValue (DirectionAngleEncoding phaseMap)

mkDirectionCochainField ::
  CellCarrier ->
  DirectionSymmetryOrder ->
  Map.Map BasisCellRef DirectionCoefficient ->
  Either DirectionFieldError DirectionField
mkDirectionCochainField carrierValue symmetryOrderValue coefficientMap =
  mkDirectionField carrierValue symmetryOrderValue (DirectionCochainEncoding coefficientMap)

directionFieldEncodingDomain :: DirectionFieldEncoding -> Set.Set BasisCellRef
directionFieldEncodingDomain encodingValue =
  case encodingValue of
    DirectionAngleEncoding phaseMap ->
      Map.keysSet phaseMap
    DirectionCochainEncoding coefficientMap ->
      Map.keysSet coefficientMap

normalizeDirectionFieldEncoding :: DirectionSymmetryOrder -> DirectionFieldEncoding -> DirectionFieldEncoding
normalizeDirectionFieldEncoding symmetryOrderValue encodingValue =
  case encodingValue of
    DirectionAngleEncoding phaseMap ->
      DirectionAngleEncoding (Map.map (normalizeDirectionPhase symmetryOrderValue) phaseMap)
    DirectionCochainEncoding _ ->
      encodingValue

normalizeDirectionPhase :: DirectionSymmetryOrder -> DirectionPhase -> DirectionPhase
normalizeDirectionPhase symmetryOrderValue phaseValue =
  let period = fundamentalDirectionPeriod symmetryOrderValue
      normalizedPhase = unDirectionPhase phaseValue `mod'` period
   in DirectionPhase normalizedPhase

fundamentalDirectionPeriod :: DirectionSymmetryOrder -> Double
fundamentalDirectionPeriod symmetryOrderValue =
  (2 * pi) / fromIntegral (unDirectionSymmetryOrder symmetryOrderValue)