{-# LANGUAGE NoImplicitPrelude
           , GADTs
           , TypeOperators
           , FlexibleContexts
  #-}

-- | A data structure to reify combinatorial species.
module Math.Combinatorics.Species.Algebra 
    (
      SpeciesAlgT(..)
    , SpeciesAlg(..)
    , needsZT, needsZ

    , reify
    , reflectT
    , reflect
    
    ) where

import Math.Combinatorics.Species.Class
import Math.Combinatorics.Species.Types

import qualified Algebra.Additive as Additive
import qualified Algebra.Ring as Ring
import qualified Algebra.Differential as Differential

import NumericPrelude
import PreludeBase hiding (cycle)

-- | Reified combinatorial species.  Note that 'SpeciesAlgT' has a
--   phantom type parameter which also reflects the structure, so we
--   can do case analysis on species at both the value and type level.
--
--   Of course, the non-uniform type parameter means that
--   'SpeciesAlgT' cannot be an instance of the 'Species' class; for
--   that purpose the existential wrapper 'SpeciesAlg' is provided.
data SpeciesAlgT s where
   O        :: SpeciesAlgT Z
   I        :: SpeciesAlgT (S Z)
   X        :: SpeciesAlgT X
   (:+:)    :: (ShowF (StructureF f), ShowF (StructureF g)) 
            => SpeciesAlgT f -> SpeciesAlgT g -> SpeciesAlgT (f :+: g)
   (:*:)    :: (ShowF (StructureF f), ShowF (StructureF g))
            => SpeciesAlgT f -> SpeciesAlgT g -> SpeciesAlgT (f :*: g)
   (:.:)    :: (ShowF (StructureF f), ShowF (StructureF g)) 
            => SpeciesAlgT f -> SpeciesAlgT g -> SpeciesAlgT (f :.: g)
   Der      :: (ShowF (StructureF f)) 
            => SpeciesAlgT f -> SpeciesAlgT (Der f)
   E        :: SpeciesAlgT E
   C        :: SpeciesAlgT C
   OfSize   :: SpeciesAlgT f -> (Integer -> Bool) -> SpeciesAlgT f
   OfSizeExactly :: SpeciesAlgT f -> Integer -> SpeciesAlgT f

--   (:.)     :: (ShowF (StructureF f), ShowF (StructureF g))
--            => SpeciesAlgT f -> SpeciesAlgT g -> SpeciesAlgT (f :. g)

-- XXX improve this
instance Show (SpeciesAlgT s) where
  show O = "0"
  show I = "1"
  show X = "X"
  show (f :+: g) = "(" ++ show f ++ " + " ++ show g ++ ")"
  show (f :*: g) = "(" ++ show f ++ " * " ++ show g ++ ")"
  show (f :.: g) = "(" ++ show f ++ " . " ++ show g ++ ")"
  show (Der f)   = show f ++ "'"
  show E         = "E"
  show C         = "C"
  show (OfSize f p) = "<" ++ show f ++ ">"
  show (OfSizeExactly f n) = show f ++ "_" ++ show n

--  show (f :. g)  = show f ++ ".:" ++ show g

-- | 'needsZT' is a predicate which checks whether a species uses any
--   of the operations which are not supported directly by ordinary
--   generating functions (composition and differentiation), and hence
--   need cycle index series.
needsZT :: SpeciesAlgT s -> Bool
needsZT (f :+: g)    = needsZT f || needsZT g
needsZT (f :*: g)    = needsZT f || needsZT g
needsZT (_ :.: _)    = True
needsZT (Der _)      = True
needsZT (OfSize f _) = needsZT f
needsZT (OfSizeExactly f _) = needsZT f
needsZT _            = False

-- | An existential wrapper to hide the phantom type parameter to
--   'SpeciesAlgT', so we can make it an instance of 'Species'.
data SpeciesAlg where
  SA :: (ShowF (StructureF s)) => SpeciesAlgT s -> SpeciesAlg

-- | A version of 'needsZT' for 'SpeciesAlg'.
needsZ :: SpeciesAlg -> Bool
needsZ (SA s) = needsZT s

instance Show SpeciesAlg where
  show (SA f) = show f

instance Additive.C SpeciesAlg where
  zero   = SA O
  (SA f) + (SA g) = SA (f :+: g)
  negate = error "negation is not implemented yet!  wait until virtual species..."

instance Ring.C SpeciesAlg where
  (SA f) * (SA g) = SA (f :*: g)
  one = SA I

instance Differential.C SpeciesAlg where
  differentiate (SA f) = SA (Der f)

instance Species SpeciesAlg where
  singleton              = SA X
  set                    = SA E
  cycle                  = SA C
  o (SA f) (SA g)        = SA (f :.: g)
  ofSize (SA f) p        = SA (OfSize f p)
  ofSizeExactly (SA f) n = SA (OfSizeExactly f n)

-- | Reify a species expression into a tree.  Of course, this is just
--   the identity function with a usefully restricted type.  For example:
--
-- > > reify octopus
-- > (C . C'_+)
reify :: SpeciesAlg -> SpeciesAlg
reify = id

-- | Reflect a species back into any instance of the 'Species' class.
reflectT :: Species s => SpeciesAlgT f -> s
reflectT O = zero
reflectT I = one
reflectT X = singleton
reflectT (f :+: g) = reflectT f + reflectT g
reflectT (f :*: g) = reflectT f * reflectT g
reflectT (f :.: g) = reflectT f `o` reflectT g
reflectT (Der f)   = oneHole (reflectT f)
reflectT E = set
reflectT C = cycle
reflectT (OfSize f p) = ofSize (reflectT f) p
reflectT (OfSizeExactly f n) = ofSizeExactly (reflectT f) n

-- | A version of 'reflectT' for the existential wrapper 'SpeciesAlg'.
reflect :: Species s => SpeciesAlg -> s
reflect (SA f) = reflectT f