{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Math.Combinatorics.Species.CycleIndex
( zToEGF
, zToGF
, zCoeff
, zFix
, aut
, intPartitions
, cyclePower
) where
import Math.Combinatorics.Species.Class
import Math.Combinatorics.Species.Labeled
import Math.Combinatorics.Species.Types
import Math.Combinatorics.Species.NewtonRaphson
import qualified MathObj.FactoredRational as FQ
import qualified MathObj.Monomial as Monomial
import qualified MathObj.MultiVarPolynomial as MVP
import qualified MathObj.PowerSeries as PowerSeries
import qualified Algebra.Ring as Ring
import qualified Algebra.ZeroTestable as ZeroTestable
import Control.Arrow ((&&&))
import Data.Function (on)
import Data.List (genericDrop,
genericIndex,
genericReplicate,
groupBy, sort)
import qualified Data.Map as M
import NumericPrelude
#if MIN_VERSION_numeric_prelude(0,2,0)
#else
import PreludeBase hiding (cycle)
#endif
instance Species CycleIndex where
singleton = CI $ MVP.x 1
set = ciFromMonomials . map partToMonomial . concatMap intPartitions $ [0..]
cycle = ciFromMonomials . concatMap cycleMonomials $ [1..]
bracelet = ciFromMonomials . concatMap braceletMonomials $ [1..]
o = liftCI2 MVP.compose
(><) = liftCI2 . MVP.lift2 $ hadamard
(@@) = zFComp
ofSize s p = (liftCI . MVP.lift1 $ filter (p . Monomial.pDegree)) s
ofSizeExactly s n
= (liftCI . MVP.lift1 $
( takeWhile ((==n) . Monomial.pDegree)
. dropWhile ((<n) . Monomial.pDegree))) s
rec f = case newtonRaphsonRec f 10 of
Nothing -> error $
"Unable to express " ++ show f ++ " in the form T = TX*R(T)."
Just ls -> ls
partToMonomial :: CycleType -> Monomial.T Rational
partToMonomial js = Monomial.Cons (ezCoeff js) (M.fromList js)
ezCoeff :: CycleType -> Rational
ezCoeff js = toRational . recip $ aut js
aut :: CycleType -> FQ.T
aut = product . map (\(b,e) -> FQ.factorial e * (fromInteger b)^e)
intPartitions :: Integer -> [CycleType]
intPartitions n = intPartitions' n n
where intPartitions' :: Integer -> Integer -> [[(Integer,Integer)]]
intPartitions' 0 _ = [[]]
intPartitions' _ 0 = []
intPartitions' n k =
[ if (j == 0) then js else (k,j):js
| j <- reverse [0..n `div` k]
, js <- intPartitions' (n - j*k) (min (k-1) (n - j*k)) ]
cycleMonomials :: Integer -> [Monomial.T Rational]
cycleMonomials n = map cycleMonomial ds
where n' = fromIntegral n
ds = sort . FQ.divisors $ n'
cycleMonomial d = Monomial.Cons (FQ.eulerPhi (n' / d) % n)
(M.singleton (n `div` (toInteger d)) (toInteger d))
braceletMonomials :: Integer -> [Monomial.T Rational]
braceletMonomials 1 = [ Monomial.Cons 1 (M.singleton 1 1)]
braceletMonomials 2 = [ Monomial.Cons (1%2) (M.singleton 2 1)
, Monomial.Cons (1%2) (M.singleton 1 2)
]
braceletMonomials n = sort $ flips : map rotations ds
where n' = fromIntegral n
ds = sort . FQ.divisors $ n'
rotations k
= Monomial.Cons
((FQ.eulerPhi k + (if kI == 2 then n `div` kI else 0)) % (2*n))
(M.singleton kI (n `div` kI))
where
kI = toInteger k
flips
| odd n = Monomial.Cons (1 % 2) (M.fromList [(1,1), (2, n `div` 2)])
| otherwise = Monomial.Cons (1 % 4) (M.fromList [(1,2), (2, n `div` 2 - 1)])
zToEGF :: CycleIndex -> EGF
zToEGF (CI (MVP.Cons xs))
= EGF . PowerSeries.fromCoeffs
. insertZeros
. concatMap (\(c,as) -> case as of { [] -> [(0,c)] ; [(1,p)] -> [(p,c)] ; _ -> [] })
. map (Monomial.coeff &&& (M.assocs . Monomial.powers))
$ xs
zToGF :: CycleIndex -> GF
zToGF (CI (MVP.Cons xs))
= GF . PowerSeries.fromCoeffs . map numerator
. insertZeros
. map ((fst . head) &&& (sum . map snd))
. groupBy ((==) `on` fst)
. map ((sum . map (uncurry (*)) . M.assocs . Monomial.powers) &&& Monomial.coeff)
$ xs
insertZeros :: Ring.C a => [(Integer, a)] -> [a]
insertZeros = insertZeros' [0..]
where
insertZeros' _ [] = []
insertZeros' (n:ns) ((pow,c):pcs)
| n < pow = genericReplicate (pow - n) zero
++ insertZeros' (genericDrop (pow - n) (n:ns)) ((pow,c):pcs)
| otherwise = c : insertZeros' ns pcs
hadamard :: (Ring.C a, ZeroTestable.C a) => [Monomial.T a] -> [Monomial.T a] -> [Monomial.T a]
hadamard = MVP.merge False zap
where zap m1 m2 = Monomial.Cons (Monomial.coeff m1 * Monomial.coeff m2 *
(fromInteger . toInteger . aut . M.assocs . Monomial.powers $ m1))
(Monomial.powers m1)
cyclePower :: CycleType -> Integer -> CycleType
cyclePower [] _ = []
cyclePower s n = concatMap jCycles [1..maximum (map fst s)]
where jCycles j = let snj = sum . map (\(k,sk) -> if j*gcd n k == k then gcd n k * sk else 0) $ s
in [ (j, snj) | snj > 0 ]
zCoeff :: CycleIndex -> CycleType -> Rational
zCoeff (CI (MVP.Cons z)) ix = c
where ixm = Monomial.mkMonomial 1 ix
z' = dropWhile (<ixm) z
c = case z' of
[] -> 0
(m:_) -> if (Monomial.powers m == Monomial.powers ixm)
then Monomial.coeff m
else 0
zFix :: CycleIndex -> CycleType -> Integer
zFix z n = numerator $ toRational (aut n) * zCoeff z n
zFComp :: CycleIndex -> CycleIndex -> CycleIndex
zFComp f g = ciFromMonomials $
concat $ for [0..] $ \n ->
for (intPartitions n) $ \nn ->
Monomial.mkMonomial
(toRational (recip (aut nn)) * (zFix f (gnn nn n) % 1))
nn
where for = flip map
gEGF = labeled $ zToEGF g
gnn :: CycleType -> Integer -> CycleType
gnn [] _ = []
gnn nn n = (gnn' nn) `truncToPartitionOf` (gEGF `genericIndex` n)
gnn' :: CycleType -> CycleType
gnn' nn = concat $ for [1..] $ \k -> let xk = gnnk nn k
in [ (k,xk) | xk > 0 ]
gnnk :: CycleType -> Integer -> Integer
gnnk nn k = (`div` k) . sum $
for (FQ.divisors k') $ \d ->
FQ.mu (k'/d) * zFix g (cyclePower nn (toInteger d))
where k' = fromIntegral k
truncToPartitionOf :: CycleType -> Integer -> CycleType
truncToPartitionOf _ 0 = []
truncToPartitionOf p n = map snd $ takeUntil ((>=n) . fst) partials
where partials = zip (tail $ scanl (\soFar cyc -> soFar + uncurry (*) cyc) 0 p) p
takeUntil _ [] = []
takeUntil p (x:xs) | p x = [x]
| otherwise = x : takeUntil p xs