open import Relation.Binary.PropositionalEquality as PE

module Examples.NestedTypes where

postulate ext :  {}  Extensionality  

open import Data.Product as P
open import Data.Sum as S
open import Function
open import Function.Equality hiding (id; _∘_)
open import Function.Related.TypeIsomorphisms
open import Function.Inverse as FI renaming (id to ↔refl; _∘_ to ↔trans; sym to ↔sym)

open import Relation.Binary.Sum
open import Relation.Binary.Product.Pointwise
open import Relation.Binary.Sigma.Pointwise as SR hiding (refl; )


import Prelude
open Prelude Set  A B  B -> A)  c  id)  f g  g  f)

import Prelude.Isos
open Prelude.Isos Set  A B  B -> A)  c  id)  f g  g  f)
                       f  refl)
                       f  refl)
                       f g h  refl)

import Uniform.posIR
open Uniform.posIR Set  A B  B -> A)
                    c  id)  f g  g  f)

import Uniform.Constructions
open Uniform.Constructions Set  A B  B -> A)  c  id)  f g  g  f)

import Uniform.Constructions.Product
open Uniform.Constructions.Product Set  A B  B -> A)
                                    c  id)  f g  g  f)

import Uniform.Constructions.Coproduct
open Uniform.Constructions.Coproduct Set  A B  B -> A)
                                      c  id)  f g  g  f)

open import Uniform.Constructions.Pi

import Uniform.Shape
open Uniform.Shape Set  A B  B -> A)  c  id)  f g  g  f)


open import Data.Container as Cont hiding (⟪_⟫; _⇒_)
open import Data.Container.Combinator as C hiding (id; _∘_; _⊎_; _×_)

Cont : Set1
Cont = Container _

-- Containers are functors, so they preserve ↔
-- This is easier to prove than the general case, since
-- containers satisfy the functor laws definitionally
⟦_⟧-preserves-↔ : (C : Cont) -> {X Y : Set} -> X  Y ->
                    Cont.⟦ C  X  Cont.⟦ C  Y
 (S  P) ⟧-preserves-↔  q
  = record { to = record { _⟨$⟩_ = Cont.map (_⟨$⟩_ (Inverse.to q))
                         ; cong = PE.cong (Cont.map (_⟨$⟩_ (Inverse.to q)))
                         }
           ; from = record { _⟨$⟩_ = Cont.map (_⟨$⟩_ (Inverse.from q))
                           ; cong = PE.cong (Cont.map (_⟨$⟩_ (Inverse.from q)))
                           }
           ; inverse-of
               = record {
                   left-inverse-of
                     = λ x  Eq⇒≡ (refl ,  p  Inverse.left-inverse-of q _))
                 ; right-inverse-of
                     = λ x  Eq⇒≡ (refl ,  p  Inverse.right-inverse-of q _))
                 }
           }
      where open Inverse
            open Function.Equality.Π
            Eq⇒≡ :  {c} {C : Container c} {X : Set c} {xs ys :  C  X} 
                    Eq _≡_ xs ys  xs  ys
            Eq⇒≡ {xs = s , f} {ys = .s , f′} (refl , f≈f′) =
              PE.cong (_,_ s) (ext f≈f′)

data Nest : Set1 where
  Id : Nest
  K : Cont -> Nest
  _⊞_ _⊠_ _⊛_ : Nest -> Nest -> Nest

infixl 6 _⊞_
infixl 7 _⊠_
infixr 9 _⊛_

⟪_⟫ : Nest -> (Set -> Set) -> (Set -> Set)
   Id   H   = H
  K C   H   = Cont.⟦ C 
 F  G  H X =  F  H X   G  H X
 F  G  H X =  F  H X ×  G  H X
 F  G  H   =  F  H   G  H


⟪_⟫-mor : (N : Nest) ->
              {F G : Set -> Set} ->
              (Gmor : {X Y : Set} -> (f : X -> Y) -> G X -> G Y) ->
              (η : (X : Set) -> F X -> G X) ->
              (X Y : Set) -> (h : X -> Y) ->  N  F X ->  N  G Y
 Id ⟫-mor Gmor η X Y h = Gmor h  η X
 K C ⟫-mor Gmor η X Y h (s , f) = s , h  f
 N  N' ⟫-mor Gmor η X Y h
 = S.map ( N ⟫-mor Gmor η X Y h) ( N' ⟫-mor Gmor η X Y h)
 N  N' ⟫-mor Gmor η X Y h
 = P.map ( N ⟫-mor Gmor η X Y h) ( N' ⟫-mor Gmor η X Y h)
 N  N' ⟫-mor Gmor η X Y z
 =  N ⟫-mor Gmor η ( N'  _ X) ( N'  _ Y)
                                    ( N' ⟫-mor Gmor η X Y z)


⟪_⟫Cont : Nest -> Cont -> Cont
   Id  ⟫Cont H = H
  K C  ⟫Cont H = C
 F  G ⟫Cont H =  F ⟫Cont H C.⊎  G ⟫Cont H
 F  G ⟫Cont H =  F ⟫Cont H C.×  G ⟫Cont H
 F  G ⟫Cont H =  F ⟫Cont H C.∘  G ⟫Cont H

⟪_⟫-restricts-to-Cont : (N : Nest) -> (C : Cont) ->
                         N  (Cont.⟦ C ) ↔̇ Cont.⟦  N ⟫Cont C 
 Id ⟫-restricts-to-Cont C = ↔refl
 K D ⟫-restricts-to-Cont C = ↔refl
 F  G ⟫-restricts-to-Cont C
   = ↔trans (FI.sym (C.Sum.correct ( F ⟫Cont C) ( G ⟫Cont C)))
             ( F ⟫-restricts-to-Cont C ⊎-↔  G ⟫-restricts-to-Cont C)
 F  G ⟫-restricts-to-Cont C
   = ↔trans (FI.sym (C.Product.correct ext ( F ⟫Cont C) ( G ⟫Cont C)))
             ( F ⟫-restricts-to-Cont C ×-↔  G ⟫-restricts-to-Cont C)
 F  G ⟫-restricts-to-Cont C {X}
   = ↔trans (FI.sym (C.Composition.correct ( F ⟫Cont C) ( G ⟫Cont C)))
       (↔trans ( ( F ⟫Cont C) ⟧-preserves-↔ ( G ⟫-restricts-to-Cont C))
                ( F ⟫-restricts-to-Cont C))


-- ⟦ N • γ ⟧UIR C ↔FamC (⟪ N ⟫Cont C) C.∘ (⟦ γ ⟧UIR C)
_•_ :  Nest -> UIR+ -> UIR+
Id  γ = δ₁ ((λ X  proj₂ X _)  IRwkσ (IRwkδ γ))
                f  ⇒-mor _ _ f _ _ (refl-shape (IRwkσ (IRwkδ γ)))
                              (HomcMor _ _ (HomcMor _ _ (idIR' γ))))
(K (S  P))  γ = σ  _  S) ((λ X  P (proj₂ X))  IRwkσ (IRwkσ γ))
(N  N')  γ = (N  γ) +UIR (N'  γ)
(N  N')  γ =  (N  γ) ×IR[  Ps P's'  Ps  P's') , S.map ] (N'  γ)
(N  N')  γ = N  (N'  γ)



⟪_⟫IR : Nest -> UIR+
 Id ⟫IR = δ₁ (ι  { (_ , X)  X _}))  f  ι→ι f)
 K (S  P) ⟫IR = σ  _  S) (ι  { (_ , s)  P s })) 
 F  G ⟫IR =  F ⟫IR +UIR  G ⟫IR
 F  G ⟫IR =  F ⟫IR ×IR[ Ps P's'  Ps  P's') , S.map ]  G ⟫IR
 F  G ⟫IR = F   G ⟫IR