-- Definition and properties of relational fold.

module Relation.Fold where

open import Prelude.Category.Isomorphism
open import Prelude.Function
open import Prelude.Function.Fam
open import Description
open import Relation
open import Relation.CompChain

open import Function using (id; _∘_)
open import Data.Unit using (; tt)
open import Data.Product using (Σ; Σ-syntax; _,_; proj₁; proj₂; _×_)
open import Data.List using (List; []; _∷_)
open import Relation.Binary using (module Setoid)
import Relation.Binary.EqReasoning as EqReasoning
import Relation.Binary.PreorderReasoning as PreorderReasoning
open import Relation.Binary.PropositionalEquality using (_≡_; refl; cong; cong₂; subst)


mutual

  foldR' : {I : Set} {D : Desc I} {X : I  Set}  ( D X  X)   i  μ D i ↝⁻ X i
  foldR' {I} {D} {X} R i (con ds) = mapFoldR D (Desc.comp D i) R ds >>= (R !!) i

  mapFoldR : {I : Set} (D : Desc I) (E : RDesc I) {X : I  Set}  ( D X  X)   E  (μ D) ↝⁻  E  X
  mapFoldR D (ṿ is)   R ds       = mapFoldR-Ṗ D R is ds
  mapFoldR D (σ S E)  R (s , ds) = map℘ (_,_ s) (mapFoldR D (E s) R ds)

  mapFoldR-Ṗ : {I : Set} (D : Desc I) {X : I  Set}  ( D X  X)  (is : List I)   is (μ D) ↝⁻  is X
  mapFoldR-Ṗ D R []       _        = any
  mapFoldR-Ṗ D R (i  is) (d , ds) = map℘₂ _,_ (foldR' R i d) (mapFoldR-Ṗ D R is ds)

foldR : {I : Set} {D : Desc I} {X : I  Set}  ( D X  X)  μ D  X
foldR R = wrap (foldR' R)

foldR'-fun-computation : {I : Set} {D : Desc I} {X : I  Set} (f :  D X  X)   {i} (d : μ D i)  foldR' (fun f) i d (fold f d)
foldR'-fun-computation {I} {D} {X} f =
  induction D  i d  foldR' (fun f) i d (fold f d))  i ds ihs  mapFold D (Desc.comp D i) f ds , aux (Desc.comp D i) ds ihs , refl)
  where
    aux : (D' : RDesc I) (ds :  D'  (μ D))  All D'  i d  foldR' (fun f) i d (fold f d)) ds 
          mapFoldR D D' (fun f) ds (mapFold D D' f ds)
    aux (ṿ [])       _        _          = tt
    aux (ṿ (i  is)) (d , ds) (ih , ihs) = fold f d , ih , mapFold-Ṗ D f is ds , aux (ṿ is) ds ihs , refl
    aux (σ S D')     (s , ds) ihs        = mapFold D (D' s) f ds , aux (D' s) ds ihs , refl

foldR'-fun-unique : {I : Set} {D : Desc I} {X : I  Set} (f :  D X  X)   {i} (d : μ D i) (x : X i)  foldR' (fun f) i d x  fold f d  x
foldR'-fun-unique {I} {D} {X} f =
  induction D  i d  (x : X i)  foldR' (fun f) i d x  fold f d  x)
               { i ds ihs .(f xs) (xs , mf , refl)  cong f (aux (Desc.comp D i) ds ihs xs mf) })
  where
    aux : (D' : RDesc I) (ds :  D'  (μ D))  All D'  i d  (x : X i)  foldR' (fun f) i d x  fold f d  x) ds 
          (xs :  D'  X)  mapFoldR D D' (fun f) ds xs  mapFold D D' f ds  xs
    aux (ṿ [])       _        _          _         _                         = refl
    aux (ṿ (i  is)) (d , ds) (ih , ihs) (x , xs)  (._ , r , ._ , rs , refl) = cong₂ _,_ (ih x r) (aux (ṿ is) ds ihs xs rs)
    aux (σ S D')     (s , ds) ihs        (.s , xs) (.xs , eqs , refl)        = cong (_,_ s) (aux (D' s) ds ihs xs eqs)

fun-preserves-fold : {I : Set} (D : Desc I) {X : I  Set} (f :  D X  X)  fun (fold f)  foldR {D = D} (fun f)
fun-preserves-fold D f = wrap  i  wrap λ { d ._ refl  foldR'-fun-computation f d }) , wrap  i  wrap (foldR'-fun-unique f))

foldR-α-lemma : {I : Set} {D : Desc I} {X : I  Set} (R : X  μ D)  foldR α  R  R
foldR-α-lemma {I} {D} {X} R =
  begin
    foldR α  R
      ≃⟨ Setoid.sym setoid (•-cong-r R (fun-preserves-fold D con)) 
    fun (fold con)  R
      ≃⟨ •-cong-r R (fun-cong  _  reflection D)) 
    fun id  R
      ≃⟨ idR-l R 
    R
  
  where setoid = ≃-Setoid X (μ D)
        open EqReasoning setoid renaming (_≈⟨_⟩_ to _≃⟨_⟩_; _∎ to _□)

foldR-least : {I : Set} (D : Desc I) {X : I  Set} (R :  D X  X) (S : μ D  X)  R   D S  α º  S  foldR R  S
foldR-least {I} D {X} R S prefix-point =
  wrap λ _  wrap (induction D  i d  (x : X i)  foldR' R i d x  (S !!) i d x)
                                { i ds ihs x (xs , rs , r) 
                                    modus-ponens-⊆ prefix-point i (con ds) x (xs , (ds , refl , aux (Desc.comp D i) ds ihs xs rs) , r)}))
  where
    aux : (D' : RDesc I) (ds :  D'  (μ D)) (ihs : All D'  i d  (x : X i)  foldR' R i d x  (S !!) i d x) ds)
          (xs :  D'  X) (rs : mapFoldR D D' R ds xs)  mapR D' S ds xs
    aux (ṿ [])       _        _          _         _                         = tt
    aux (ṿ (i  is)) (d , ds) (ih , ihs) (x , xs)  (._ , r , ._ , rs , refl) = x , ih x r , xs , aux (ṿ is) ds ihs xs rs , refl
    aux (σ T D')     (t , ds) ihs        (.t , xs) (.xs , rs , refl)         = xs , aux (D' t) ds ihs xs rs , refl

foldR-greatest : {I : Set} (D : Desc I) {X : I  Set} (R :  D X  X) (S : μ D  X)  S  R   D S  α º  S  foldR R
foldR-greatest {I} D {X} R S postfix-point = wrap λ _  wrap (induction D  i d  (x : X i)  (S !!) i d x  foldR' R i d x) alg)
  where
    aux : (D' : RDesc I) (ds :  D'  (μ D))  All D'  i d  (x : X i)  (S !!) i d x  foldR' R i d x) ds 
          (xs :  D'  X)  mapR D' S ds xs  mapFoldR D D' R ds xs
    aux (ṿ [])       _        _          _         _                         = tt
    aux (ṿ (i  is)) (d , ds) (ih , ihs) (x , xs)  (._ , s , ._ , ss , refl) = x , ih x s , xs , aux (ṿ is) ds ihs xs ss , refl
    aux (σ T D')     (t , ds) ihs        (.t , xs) (.xs , ss , refl)         = xs , aux (D' t) ds ihs xs ss , refl
    alg : (i : I) (ds :  D (μ D) i)  All (Desc.comp D i)  i d  (x : X i)  (S !!) i d x  foldR' R i d x) ds 
          (x : X i)  (S !!) i (con ds) x  foldR' R i (con {D = D} ds) x
    alg i ds ihs x s with modus-ponens-⊆ postfix-point i (con ds) x s
    alg i ds ihs x s | xs , (.ds , refl , ss) , r = xs , aux (Desc.comp D i) ds ihs xs ss , r

foldR-computation-⊆ : {I : Set} (D : Desc I) {X : I  Set} (R :  D X  X)  foldR {D = D} R  α  R   D (foldR R)
foldR-computation-⊆ {I} D {X} R = wrap λ i  wrap λ { ds x (.(con ds) , refl , xs , rs , r)  xs , aux (Desc.comp D i) ds xs rs , r }
  where
    aux : (D' : RDesc I) (ds :  D'  (μ D)) (xs :  D'  X)  mapFoldR D D' R ds xs  mapR D' (foldR R) ds xs
    aux (ṿ [])       _        _         _                         = tt
    aux (ṿ (i  is)) (d , ds) (x , xs)  (._ , r , ._ , rs , refl) = x , r , xs , aux (ṿ is) ds xs rs , refl
    aux (σ S D')     (s , ds) (.s , xs) (.xs , rs , refl)         = xs , aux (D' s) ds xs rs , refl

foldR-computation-⊇ : {I : Set} (D : Desc I) {X : I  Set} (R :  D X  X)  foldR {D = D} R  α  R   D (foldR R)
foldR-computation-⊇ {I} D {X} R = wrap λ i  wrap λ { ds x (xs , rs , r)  con ds , refl , xs , aux (Desc.comp D i) ds xs rs , r }
  where
    aux : (D' : RDesc I) (ds :  D'  (μ D)) (xs :  D'  X)  mapR D' (foldR R) ds xs  mapFoldR D D' R ds xs
    aux (ṿ [])       _        _         _                         = tt
    aux (ṿ (i  is)) (d , ds) (x , xs)  (._ , r , ._ , rs , refl) = x , r , xs , aux (ṿ is) ds xs rs , refl
    aux (σ S D')     (s , ds) (.s , xs) (.xs , rs , refl)         = xs , aux (D' s) ds xs rs , refl

foldR-computation : {I : Set} (D : Desc I) {X : I  Set} (R :  D X  X)  foldR {D = D} R  α  R   D (foldR R)
foldR-computation D R = foldR-computation-⊆ D R , foldR-computation-⊇ D R

foldR-computation' : {I : Set} (D : Desc I) {X : I  Set} (R :  D X  X)  foldR {D = D} R  R   D (foldR R)  α º
foldR-computation' D {X} R =
  begin
    foldR R
      ≃⟨ Setoid.sym setoid (idR-r (foldR R)) 
    foldR R  idR
      ≃⟨ Setoid.sym setoid (•-cong-l (foldR R) (iso-idR  i  Setoid.sym (IsoSetoid Fun) (μ-iso D i)))) 
    foldR R  α  α º
      ≃⟨ ≃-chain-r (foldR R  (α )) (R  ( D (foldR R) )) (foldR-computation D R) 
    R   D (foldR R)  α º
  
  where setoid = ≃-Setoid (μ D) X
        open EqReasoning setoid renaming (_≈⟨_⟩_ to _≃⟨_⟩_; _∎ to _□)

foldR-fusion-⊇ :
  {I : Set} (D : Desc I) {X Y : I  Set} (R : X  Y) (S :  D X  X) (T :  D Y  Y)  R  S  T   D R  R  foldR {D = D} S  foldR T
foldR-fusion-⊇ D {X} {Y} R S T fusion-condition =
  foldR-least D T (R  foldR S)
    (begin
       R  foldR S
         ⊇⟨ •-monotonic-l R (proj₁ (idR-r (foldR S))) 
       R  foldR S  idR
         ⊇⟨ ⊇-chain-l (R  foldR S ) (proj₁ (iso-idR  i  Relation.Binary.Setoid.sym (IsoSetoid Fun) (μ-iso D i)))) 
       R  foldR S  α  α º
         ⊇⟨ ⊇-chain (R ) (foldR S  α ) (S   D (foldR S) ) (proj₂ (foldR-computation D S)) 
       R  S   D (foldR S)  α º
         ⊇⟨ ⊇-chain-r (R  S ) (T   D R ) fusion-condition 
       T   D R   D (foldR S)  α º
         ⊇⟨ ⊇-chain (T ) ( D R   D (foldR S) ) ( D (R  foldR S) ) (proj₁ (Ṙ-preserves-comp D R (foldR S))) 
       T   D (R  foldR S)  α º
     )
  where open PreorderReasoning (⊇-Preorder (μ D) Y) renaming (_∼⟨_⟩_ to _⊇⟨_⟩_; _∎ to _□)

foldR-fusion-⊆ :
  {I : Set} (D : Desc I) {X Y : I  Set} (R : X  Y) (S :  D X  X) (T :  D Y  Y)  R  S  T   D R  R  foldR {D = D} S  foldR T
foldR-fusion-⊆ D {X} {Y} R S T fusion-condition =
  foldR-greatest D T (R  foldR S)
    (begin
       R  foldR S
         ⊆⟨ •-monotonic-l R (proj₂ (idR-r (foldR S))) 
       R  foldR S  idR
         ⊆⟨ ⊆-chain-l (R  foldR S ) (proj₂ (iso-idR  i  Relation.Binary.Setoid.sym (IsoSetoid Fun) (μ-iso D i)))) 
       R  foldR S  α  α º
         ⊆⟨ ⊆-chain (R ) (foldR S  α ) (S   D (foldR S) ) (proj₁ (foldR-computation D S)) 
       R  S   D (foldR S)  α º
         ⊆⟨ ⊆-chain-r (R  S ) (T   D R ) fusion-condition 
       T   D R   D (foldR S)  α º
         ⊆⟨ ⊆-chain (T ) ( D R   D (foldR S) ) ( D (R  foldR S) ) (proj₂ (Ṙ-preserves-comp D R (foldR S))) 
       T   D (R  foldR S)  α º
     )
  where open PreorderReasoning (⊆-Preorder (μ D) Y) renaming (_∼⟨_⟩_ to _⊆⟨_⟩_; _∎ to _□)

foldR-fusion :
  {I : Set} (D : Desc I) {X Y : I  Set} (R : X  Y) (S :  D X  X) (T :  D Y  Y)  R  S  T   D R  R  foldR {D = D} S  foldR T
foldR-fusion D R S T (fusion-condition-⊆ , fusion-condition-⊇) =
  foldR-fusion-⊆ D R S T fusion-condition-⊆ , foldR-fusion-⊇ D R S T fusion-condition-⊇