module MAAM.GaloisTransformer where

import FP

class GaloisTransformer ς m | m  ς where
  αGT   a b. (ς a  ς b)  (a  m b)
  γGT   a b. (a  m b)  (ς a  ς b)

class Inject ι ς | ς  ι where
  inject  ι a  ς a

-- # Identity

instance GaloisTransformer ID ID where
  αGT   a b. (ID a  ID b)  (a  ID b)
  αGT f = f  ID
  γGT   a b. (a  ID b)  (ID a  ID b)
  γGT f = f  runID

instance Inject ID ID where
  inject = id

-- # Galois Transformers

-- ## Monad Transformers

-- StateT defined in FP.Prelude.Effects and FP.Prelude.Monads

isoToStateTMorph  ((a,s)  m (b,s))  (a  StateT s m b)
isoToStateTMorph f x = StateT $ \ s  f (x,s)

isoFromStateTMorph  (a  StateT s m b)  ((a,s)  m (b,s))
isoFromStateTMorph f (x,s) = runStateT (f x) s

-- PolyStateT

-- Has a polymorphic (rather than functorial) implementation for POrd and
-- JoinLattice. This goes "on top" of nondeterminism monads, whereas the
-- builtin state goes "on bottom".

newtype PolyStateT s m a = PolyStateT { runPolyStateT  StateT s m a }
  deriving (Functor,Monad,MonadBot,MonadJoin,MonadJoinLattice,MonadState s)

isoToPolyStateTMorph  ((a,s)  m (b,s))  (a  PolyStateT s m b)
isoToPolyStateTMorph f x = PolyStateT $ StateT $ \ s  f (x,s)

isoFromPolyStateTMorph  (a  PolyStateT s m b)  ((a,s)  m (b,s))
isoFromPolyStateTMorph f (x,s) = runStateT (runPolyStateT (f x)) s

instance (Polymorphic JoinLattice m)  Bot (PolyStateT s m a) where
  bot =
    with (polymorphic  W (JoinLattice (m (a,s)))) $
    PolyStateT $ StateT bot
instance (Polymorphic JoinLattice m)  Join (PolyStateT s m a) where
  PolyStateT (StateT x)  PolyStateT (StateT y) =
    with (polymorphic  W (JoinLattice (m (a,s)))) $
    PolyStateT $ StateT $ x  y
instance (Polymorphic JoinLattice m)  JoinLattice (PolyStateT s m a)
instance (Polymorphic JoinLattice m)  Polymorphic JoinLattice (PolyStateT s m) where polymorphic = W

-- NondetT defined in FP.Prelude.Effects and FP.Prelude.Monads

isoToNondetJoinTMorph  (a  m (𝒫ᵇ b))  (a  NondetJoinT m b)
isoToNondetJoinTMorph f = NondetJoinT  f

isoFromNondetJoinTMorph  (a  NondetJoinT m b)  (a  m (𝒫ᵇ b))
isoFromNondetJoinTMorph f = runNondetJoinT  f

-- FlowT defined in FP.Prelude.Effects and FP.Prelude.Monads

isoToFlowJoinTMorph  ((a,s)  m (b  s))  (a  FlowJoinT s m b)
isoToFlowJoinTMorph f x = FlowJoinT $ \ s  f (x,s)

isoFromFlowJoinTMorph  (a  FlowJoinT s m b)  ((a,s)  m (b  s))
isoFromFlowJoinTMorph f (x,s) = runFlowJoinT (f x) s

-- ## Transition Systems

newtype StateΠ s ς a = StateΠ { runStateΠ  ς (a,s) }
makePrettyUnion ''StateΠ
instance (OrdFunctorial POrd ς,POrd s,POrd a,Ord s,Ord a)  POrd (StateΠ s ς a) where
  () =
    with (ordFunctorial  W (POrd (ς (a,s)))) $
    () `on` runStateΠ
instance (OrdFunctorial POrd ς,POrd s,Ord s)  OrdFunctorial POrd (StateΠ s ς) where ordFunctorial = W
instance (Functorial JoinLattice ς,JoinLattice s,JoinLattice a)  Bot (StateΠ s ς a) where
  bot =
    with (functorial  W (JoinLattice (ς (a,s)))) $
    StateΠ bot
instance (Functorial JoinLattice ς,JoinLattice s,JoinLattice a)  Join (StateΠ s ς a) where
  StateΠ x  StateΠ y =
    with (functorial  W (JoinLattice (ς (a,s)))) $
    StateΠ $ x  y
instance (Functorial JoinLattice ς,JoinLattice s,JoinLattice a)  JoinLattice (StateΠ s ς a)
instance (Functorial JoinLattice ς,JoinLattice s)  Functorial JoinLattice (StateΠ s ς) where functorial = W
instance (Difference (ς (a,s)))  Difference (StateΠ s ς a) where
  StateΠ x  StateΠ y = StateΠ (x  y)

instance (Functor ς)  Functor (StateΠ s ς) where
  map f = StateΠ  map (mapFst f)  runStateΠ

isoToStateΠMorph  (ς (a,s)  ς (b,s))  (StateΠ s ς a  StateΠ s ς b)
isoToStateΠMorph f = StateΠ  f  runStateΠ

isoFromStateΠMorph  (StateΠ s ς a  StateΠ s ς b)  (ς (a,s)  ς (b,s))
isoFromStateΠMorph f = runStateΠ  f  StateΠ

newtype PolyStateΠ s ς a = PolyStateΠ { runPolyStateΠ  ς (a,s) }
makePrettyUnion ''PolyStateΠ

isoPolyStateΠ  PolyStateΠ s ς a  ς (a,s)
isoPolyStateΠ = Iso runPolyStateΠ PolyStateΠ

instance (OrdPolymorphic POrd ς,Ord s,Ord a)  POrd (PolyStateΠ s ς a) where
  () =
    with (ordPolymorphic  W (POrd (ς (a,s)))) $
    () `on` runPolyStateΠ
instance (OrdPolymorphic POrd ς,Ord s)  OrdPolymorphic POrd (PolyStateΠ s ς) where ordPolymorphic = W
instance (Polymorphic JoinLattice ς)  Bot (PolyStateΠ s ς a) where
  bot =
    with (polymorphic  W (JoinLattice (ς (a,s)))) $
    PolyStateΠ bot
instance (Polymorphic JoinLattice ς)  Join (PolyStateΠ s ς a) where
  PolyStateΠ x  PolyStateΠ y =
    with (polymorphic  W (JoinLattice (ς (a,s)))) $
    PolyStateΠ $ x  y
instance (Polymorphic JoinLattice ς)  JoinLattice (PolyStateΠ s ς a)
instance (Polymorphic JoinLattice ς)  Polymorphic JoinLattice (PolyStateΠ s ς) where polymorphic = W
instance (Difference (ς (a,s)))  Difference (PolyStateΠ s ς a) where
  PolyStateΠ x  PolyStateΠ y = PolyStateΠ (x  y)

instance (Functor ς)  Functor (PolyStateΠ s ς) where
  map f = PolyStateΠ  map (mapFst f)  runPolyStateΠ

isoToPolyStateΠMorph  (ς (a,s)  ς (b,s))  (PolyStateΠ s ς a  PolyStateΠ s ς b)
isoToPolyStateΠMorph f = PolyStateΠ  f  runPolyStateΠ

isoFromPolyStateΠMorph  (PolyStateΠ s ς a  PolyStateΠ s ς b)  (ς (a,s)  ς (b,s))
isoFromPolyStateΠMorph f = runPolyStateΠ  f  PolyStateΠ

newtype NondetJoinΠ ς a = NondetJoinΠ { runNondetJoinΠ  ς (𝒫ᵇ a) }
makePrettyUnion ''NondetJoinΠ

isoNondetJoinΠ  NondetJoinΠ ς a  ς (𝒫ᵇ a)
isoNondetJoinΠ = Iso runNondetJoinΠ NondetJoinΠ

instance (OrdFunctorial POrd ς,Ord a)  POrd (NondetJoinΠ ς a) where
  () =
    with (ordFunctorial  W (POrd (ς (𝒫ᵇ a)))) $
    () `on` runNondetJoinΠ
instance (OrdFunctorial POrd ς)  OrdPolymorphic POrd (NondetJoinΠ ς) where ordPolymorphic = W
instance (Functorial JoinLattice ς)  Bot (NondetJoinΠ ς a) where
  bot =
    with (functorial  W (JoinLattice (ς (𝒫ᵇ a)))) $
    NondetJoinΠ bot
instance (Functorial JoinLattice ς)  Join (NondetJoinΠ ς a) where
  NondetJoinΠ x  NondetJoinΠ y =
    with (functorial  W (JoinLattice (ς (𝒫ᵇ a)))) $
    NondetJoinΠ $ x  y
instance (Functorial JoinLattice ς)  JoinLattice (NondetJoinΠ ς a)
instance (Functorial JoinLattice ς)  Polymorphic JoinLattice (NondetJoinΠ ς) where polymorphic = W
instance (Difference (ς (𝒫ᵇ a)))  Difference (NondetJoinΠ ς a) where
  NondetJoinΠ x  NondetJoinΠ y = NondetJoinΠ $ x  y

isoToNondetJoinΠMorph  (ς (𝒫ᵇ a)  ς (𝒫ᵇ b))  (NondetJoinΠ ς a  NondetJoinΠ ς b)
isoToNondetJoinΠMorph f = NondetJoinΠ  f  runNondetJoinΠ

isoFromNondetJoinΠMorph  (NondetJoinΠ ς a  NondetJoinΠ ς b)  (ς (𝒫ᵇ a)  ς (𝒫ᵇ b))
isoFromNondetJoinΠMorph f = runNondetJoinΠ  f  NondetJoinΠ

newtype FlowJoinΠ s ς a = FlowJoinΠ { runFlowJoinΠ  ς (a  s) }
makePrettyUnion ''FlowJoinΠ

instance (OrdFunctorial POrd ς,POrd s,JoinLattice s,Ord s,Ord a)  POrd (FlowJoinΠ s ς a) where
  () =
    with (ordFunctorial  W (POrd (ς (a  s)))) $
    () `on` runFlowJoinΠ
instance (OrdFunctorial POrd ς,POrd s,JoinLattice s,Ord s)  OrdPolymorphic POrd (FlowJoinΠ s ς) where ordPolymorphic = W
instance (Functorial JoinLattice ς)  Bot (FlowJoinΠ s ς a) where
  bot =
    with (functorial  W (JoinLattice (ς (a  s)))) $
    FlowJoinΠ bot
instance (Functorial JoinLattice ς)  Join (FlowJoinΠ s ς a) where
  FlowJoinΠ x  FlowJoinΠ y =
    with (functorial  W (JoinLattice (ς (a  s)))) $
    FlowJoinΠ $ x  y
instance (Functorial JoinLattice ς)  JoinLattice (FlowJoinΠ s ς a)
instance (Functorial JoinLattice ς)  Polymorphic JoinLattice (FlowJoinΠ s ς) where polymorphic = W
instance (Difference (ς (a  s)))  Difference (FlowJoinΠ s ς a) where
  FlowJoinΠ x  FlowJoinΠ y = FlowJoinΠ $ x  y

isoToFlowJoinΠMorph  (ς (a  s)  ς (b  s))  (FlowJoinΠ s ς a  FlowJoinΠ s ς b)
isoToFlowJoinΠMorph f = FlowJoinΠ  f  runFlowJoinΠ

isoFromFlowJoinΠMorph  (FlowJoinΠ s ς a  FlowJoinΠ s ς b)  (ς (a  s)  ς (b  s))
isoFromFlowJoinΠMorph f = runFlowJoinΠ  f  FlowJoinΠ

-- ## Injections

newtype StateI s ι a = StateI { runStateI  ι (a,s) }
isoStateI  StateI s ι a  ι (a,s)
isoStateI = Iso runStateI StateI

type NondetJoinI ι = ι
type FlowJoinI = StateI

-- ## Galois Transformers Instances

instance 
  (GaloisTransformer ς m) 
   
  GaloisTransformer (StateΠ s ς) (StateT s m) where
    αGT   a b. (StateΠ s ς a  StateΠ s ς b)  (a  StateT s m b)
    αGT (isoFromStateΠMorph  f) = isoToStateTMorph $ αGT f
    γGT   a b. (a  StateT s m b)  (StateΠ s ς a  StateΠ s ς b)
    γGT (isoFromStateTMorph  f) = isoToStateΠMorph $ γGT f

instance 
  (GaloisTransformer ς m) 
   
  GaloisTransformer (PolyStateΠ s ς) (PolyStateT s m) where
    αGT   a b. (PolyStateΠ s ς a  PolyStateΠ s ς b)  (a  PolyStateT s m b)
    αGT (isoFromPolyStateΠMorph  f) = isoToPolyStateTMorph $ αGT f
    γGT   a b. (a  PolyStateT s m b)  (PolyStateΠ s ς a  PolyStateΠ s ς b)
    γGT (isoFromPolyStateTMorph  f) = isoToPolyStateΠMorph $ γGT f

instance 
  (GaloisTransformer ς m,Functorial JoinLattice m) 
   
  GaloisTransformer (NondetJoinΠ ς) (NondetJoinT m) where
    αGT   a b. (NondetJoinΠ ς a  NondetJoinΠ ς b)  (a  NondetJoinT m b)
    αGT (isoFromNondetJoinΠMorph  f) = isoToNondetJoinTMorph $ αGT f  return
    γGT   a b. (a  NondetJoinT m b)  (NondetJoinΠ ς a  NondetJoinΠ ς b)
    γGT (isoFromNondetJoinTMorph  f) = 
      with (functorial  W (JoinLattice (m (𝒫ᵇ b)))) $
      isoToNondetJoinΠMorph $ γGT $ joins  map f

instance 
  (GaloisTransformer ς m,Functorial JoinLattice m,JoinLattice s) 
   
  GaloisTransformer (FlowJoinΠ s ς) (FlowJoinT s m) where
    αGT   a b. (FlowJoinΠ s ς a  FlowJoinΠ s ς b)  (a  FlowJoinT s m b)
    αGT (isoFromFlowJoinΠMorph  f) = isoToFlowJoinTMorph $ αGT f  lazyDictJoin  singleFold
    γGT   a b. (a  FlowJoinT s m b)  (FlowJoinΠ s ς a  FlowJoinΠ s ς b)
    γGT (isoFromFlowJoinTMorph  f) = 
      with (functorial  W (JoinLattice (m (b  s)))) $
      isoToFlowJoinΠMorph $ γGT $ \ xss  joins $ map f $ list xss

isoαGT  (GaloisTransformer ς₁ m₁)  ς₂  ς₁  m₂  m₁   a b. (ς₂ a  ς₂ b)  (a  m₂ b)
isoαGT isomorphicς isomorphicm f = isoFrom2 isomorphicm  αGT (isoTo2 isomorphicς  f  isoFrom2 isomorphicς)

isoγGT  (GaloisTransformer ς₁ m₁)  ς₂  ς₁  m₂  m₁   a b. (a  m₂ b)  (ς₂ a  ς₂ b)
isoγGT isomorphicς isomorphicm f = isoFrom2 isomorphicς  γGT (isoTo2 isomorphicm  f)  isoTo2 isomorphicς

-- ## Injection Instances

instance (Inject ι ς)  Inject (StateI s ι) (StateΠ s ς) where
  inject   a. StateI s ι a  StateΠ s ς a
  inject (StateI ιas) = StateΠ $ inject ιas

instance (Inject ι ς)  Inject (StateI s ι) (PolyStateΠ s ς) where
  inject   a. StateI s ι a  PolyStateΠ s ς a
  inject (StateI ιas) = PolyStateΠ $ inject ιas

instance (Inject ι ς,Functor ς)  Inject ι (NondetJoinΠ ς) where
  inject   a. ι a  NondetJoinΠ ς a
  inject aI = NondetJoinΠ $ single ^$ inject aI

instance (Inject ι ς,Functor ς)  Inject (StateI s ι) (FlowJoinΠ s ς) where
  inject   a. StateI s ι a  FlowJoinΠ s ς a
  inject (StateI ιas) = FlowJoinΠ $ map single $ inject ιas

isoInject  (Inject ι₁ ς₁)  ι₂  ι₁  ς₂  ς₁   a. ι₂ a  ς₂ a
isoInject isomorphicι isomorphicς = isoFrom2 isomorphicς  inject  isoTo2 isomorphicι