packages feed

moonlight-homology-0.1.0.0: src-chain/Moonlight/Homology/Pure/FiniteAbelian.hs

module Moonlight.Homology.Pure.FiniteAbelian
  ( FiniteAbelianTorsion,
    mkFiniteAbelianTorsion,
    torsionFromHomologyGroup,
    finiteAbelianInvariants,
    normalizeTorsionOrders,
    finiteAbelianSummandCount,
    finiteAbelianCyclicSummandMultiplicity,
    finiteAbelianFilteredCardinality,
    finiteAbelianCardinality,
    finiteAbelianExponent,
    finiteAbelianOrderSupport,
    finiteAbelianPrimaryOrderSupport,
    finiteAbelianExactOrderElementCount,
    isPrime,
    matchesOptional,
  )
where

import Data.Kind (Type)
import Data.Function ((&))
import qualified Data.List as List
import Moonlight.Algebra.Pure.NumberTheory
  ( countExactOrderElements,
    divisorsOf,
    isPrime,
    primePowerPart,
  )
import Moonlight.Homology.Pure.Group (HomologyGroup (..))

type FiniteAbelianTorsion :: Type
newtype FiniteAbelianTorsion = FiniteAbelianTorsion
  { finiteAbelianInvariants :: [Integer]
  }
  deriving stock (Eq, Show)

mkFiniteAbelianTorsion :: [Integer] -> FiniteAbelianTorsion
mkFiniteAbelianTorsion =
  FiniteAbelianTorsion . normalizeInvariantFactors

torsionFromHomologyGroup :: HomologyGroup Integer -> FiniteAbelianTorsion
torsionFromHomologyGroup =
  mkFiniteAbelianTorsion . torsionInvariants

finiteAbelianSummandCount :: Maybe Integer -> FiniteAbelianTorsion -> Integer
finiteAbelianSummandCount orderConstraint =
  toInteger . length . finiteAbelianMatchingInvariants orderConstraint

finiteAbelianCyclicSummandMultiplicity :: Integer -> FiniteAbelianTorsion -> Int
finiteAbelianCyclicSummandMultiplicity orderValue =
  length . finiteAbelianMatchingInvariants (Just orderValue)

finiteAbelianFilteredCardinality :: Maybe Integer -> FiniteAbelianTorsion -> Integer
finiteAbelianFilteredCardinality orderConstraint =
  product . finiteAbelianMatchingInvariants orderConstraint

finiteAbelianCardinality :: FiniteAbelianTorsion -> Integer
finiteAbelianCardinality =
  finiteAbelianFilteredCardinality Nothing

finiteAbelianExponent :: FiniteAbelianTorsion -> Integer
finiteAbelianExponent =
  foldr lcm 1 . finiteAbelianInvariants

finiteAbelianOrderSupport :: FiniteAbelianTorsion -> [Integer]
finiteAbelianOrderSupport torsionValue =
  finiteAbelianExponent torsionValue
    & divisorsOf
    & filter (> 1)
    & normalizeTorsionOrders

finiteAbelianPrimaryOrderSupport :: Integer -> FiniteAbelianTorsion -> Maybe [Integer]
finiteAbelianPrimaryOrderSupport primeValue torsionValue =
  if not (isPrime primeValue)
    then Nothing
    else
      let primaryExponent =
            finiteAbelianInvariants torsionValue
              & fmap (primePowerPart primeValue)
              & foldr lcm 1
       in Just (primePowerOrdersUpToExponent primeValue primaryExponent)

finiteAbelianExactOrderElementCount :: Integer -> FiniteAbelianTorsion -> Integer
finiteAbelianExactOrderElementCount orderValue =
  countExactOrderElements orderValue . finiteAbelianInvariants

finiteAbelianMatchingInvariants :: Maybe Integer -> FiniteAbelianTorsion -> [Integer]
finiteAbelianMatchingInvariants orderConstraint torsionValue =
  finiteAbelianInvariants torsionValue
    & filter (matchesOptional (fmap abs orderConstraint) . abs)

matchesOptional :: Eq a => Maybe a -> a -> Bool
matchesOptional = maybe (const True) (==)

normalizeInvariantFactors :: [Integer] -> [Integer]
normalizeInvariantFactors =
  List.sort . filter (> 1) . fmap abs

normalizeTorsionOrders :: [Integer] -> [Integer]
normalizeTorsionOrders =
  deduplicateAdjacentValues . List.sort . filter (> 1) . fmap abs

deduplicateAdjacentValues :: Eq value => [value] -> [value]
deduplicateAdjacentValues =
  foldr
    ( \value deduplicatedValues ->
        case deduplicatedValues of
          nextValue : _
            | value == nextValue -> deduplicatedValues
          _ -> value : deduplicatedValues
    )
    []

primePowerOrdersUpToExponent :: Integer -> Integer -> [Integer]
primePowerOrdersUpToExponent primeValue exponentValue =
  let normalizedPrime = abs primeValue
      normalizedExponent = abs exponentValue
   in if not (isPrime normalizedPrime) || normalizedExponent <= 1
        then []
        else
          iterate (* normalizedPrime) normalizedPrime
            & takeWhile (<= normalizedExponent)