{-# OPTIONS --without-K  #-}
open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Data.List using (List; []; _∷_)
open import Data.Nat using (; zero; suc)
open import Relation.Binary.PropositionalEquality 
    using (_≡_; refl; sym; cong; cong₂; cong-app)
open import ScopedTuple
open import Sig
open import Var

module Structures where


{-

  A Shiftable thing of type V may contain variable occurences (de
  Bruijn indices) that need to be shifted up by one when they go
  underneath a variable binder. Thus, the operations on Shiftable
  things are:

  1. ⇑ v        to shift all the variables in v up by one.
  2. var→val x  to inject the variable x into a V.
  
  Also, we require that incrementing a variable x and then injecting it into
  V is equal to injecting it into V and then shifting the result.

  var→val (suc x) ≡ ⇑ (var→val x)

  Examples of Shiftable things:

     * Variables (shifting increments the variable, injecting is the identity)
     * Terms (shifting applies a renaming to the term, injecting wraps
              the variable in the term constructors for variables)
     * Maybe ℕ (shifting does nothing; variables get mapped to `nothing`)

-}
record Shiftable {} (V : Set ) : Set  where
  field  : V  V
        var→val : Var  V
        var→val-suc-shift : ∀{x}  var→val (suc x)   (var→val x)
open Shiftable {{...}} public

instance
  Var-is-Shiftable : Shiftable Var
  Var-is-Shiftable = record { var→val = λ x  x ;  = suc
                            ; var→val-suc-shift = λ {x}  refl }


record Composable {} (V₁ V₂ V₃ : Set ){{_ : Shiftable V₁}} : Set  where
   field ⌈_⌉ : (Var  V₂)  V₁  V₃
         val₂₃ : V₂  V₃
         ⌈⌉-var→val :  σ x   σ  (var→val x)  val₂₃ (σ x)
open Composable {{...}} public

instance
    Var³-Composable : Composable Var Var Var
    Var³-Composable = record { ⌈_⌉ = λ f x  f x ; val₂₃ = λ x  x
                     ; ⌈⌉-var→val = λ σ x  refl }

infixr 5 _⨟_

_⨟_ : ∀{}{V₁ V₂ V₃ : Set } {{_ : Shiftable V₁}} {{_ : Composable V₁ V₂ V₃}}
      (Var  V₁)  (Var  V₂)  (Var  V₃)
(σ₁  σ₂) x =  σ₂  (σ₁ x)

record Equiv {} (V₁ : Set )(V₂ : Set ) : Set (lsuc ) where
  field _≈_ : V₁  V₂  Set 
open Equiv {{...}} public

instance
  ≡-Var-is-Equiv : Equiv Var Var
  ≡-Var-is-Equiv = record { _≈_ = _≡_ }

record EquivRel {} (V : Set ) {{_ : Equiv {} V V}} : Set  where
  field ≈-refl :  (v : V)  v  v
        ≈-sym :  {u v : V}  u  v  v  u
        ≈-trans :  {u v w : V}  u  v  v  w  u  w
open EquivRel {{...}} public

infixr 2 _≈⟨⟩_ _≈⟨_⟩_
infix  3 _≈∎

_≈⟨⟩_ :  {}{A : Set } {{_ : Equiv{} A A}} {{_ : EquivRel A}}
    (x : A) {y : A} 
   x  y
    -----
   x  y
x ≈⟨⟩ refl  =  ≈-refl x

_≈⟨_⟩_ :  {}{A : Set } {{_ : Equiv{} A A}} {{_ : EquivRel A}}
    (x : A) {y z : A}
   x  y    y  z
    ---------------
   x  z
x ≈⟨ x≈y  y≈z  =  ≈-trans x≈y y≈z

_≈∎ :  {}{A : Set } {{_ : Equiv{} A A}} {{_ : EquivRel A}}
    (x : A)
    -------
   x  x
x ≈∎  =  ≈-refl x

instance
  ≡-Var-is-EquivRel : EquivRel Var
  ≡-Var-is-EquivRel = record { ≈-refl = λ v  refl
      ; ≈-sym = λ { {u}{v} refl  refl }
      ; ≈-trans = λ { {u}{v}{w} refl refl  refl } }

record Relatable {} (V₁ : Set ) (V₂ : Set )
    {{_ : Shiftable V₁}} {{_ : Shiftable V₂}} : Set (lsuc ) where
    field {{eq}} : Equiv {} V₁ V₂
          var→val≈ :  x  (var→val{V = V₁} x)  (var→val{V = V₂} x)
          shift≈ : ∀{v₁ : V₁}{v₂ : V₂}  v₁  v₂  ( v₁)  ( v₂)
open Relatable {{...}} public

record ShiftId {} (V : Set ) {{_ : Equiv{} V V}} {{S : Shiftable V}} 
    : Set  where
    field shift-id :  x  (var→val{V = V} x)  ( (var→val x))
open ShiftId {{...}} public

{-
Bind : {ℓᵛ ℓᶜ : Level} → Set ℓᵛ → Set ℓᶜ → Sig → Set (ℓᵛ ⊔ ℓᶜ)
Bind {ℓᵛ}{ℓᶜ} V C ■ = Lift ℓᵛ C
Bind V C (ν b) = V → Bind V C b
Bind V C (∁ b) = Bind V C b
-}
Bind : { : Level}  Set   Set   Sig  Set 
Bind V C  = C
Bind V C (ν b) = V  Bind V C b
Bind V C ( b) = Bind V C b

{-
 Equivalence of Bind's based on equivalence of V's and C's.
 -}

_⩳_  :  {} {V₁ V₂ : Set } {C₁ C₂ : Set }
     {{EqV : Equiv{} V₁ V₂}} {{EqC : Equiv{} C₁ C₂}}
    (Bind V₁ C₁)  (Bind V₂ C₂)
_⩳_ {}{b = } c₁ c₂ = c₁  c₂
_⩳_ {V₁ = V₁}{V₂}{C₁}{C₂}{{R}}{b = ν b} r₁ r₂ =
  ∀{v₁ : V₁}{v₂ : V₂}  v₁  v₂  _⩳_ {b = b} (r₁ v₁) (r₂ v₂)
_⩳_ {b =  b} r₁ r₂ = _⩳_ {b = b} r₁ r₂ 

module WithOpSig (Op : Set) (sig : Op  List Sig) where

  record Foldable { : Level}(V : Set )(C : Set ) : Set 
      where
    field ret : V  C
          fold-op : (op : Op)  Tuple {} (sig op) (Bind V C)  C
  open Foldable {{...}} public

  record Similar {} (V₁ : Set )(V₂ : Set )
    (C₁ : Set )(C₂ : Set )
    {{SV1 : Shiftable V₁}} {{SV2 : Shiftable V₂}}
    {{F1 : Foldable V₁ C₁}} {{F2 : Foldable V₂ C₂}}
    {{EqC : Equiv{} C₁ C₂}}
        : Set (lsuc ) where
    field {{rel}} : Relatable V₁ V₂
    field ret≈ : ∀{v₁ : V₁}{v₂ : V₂}  v₁  v₂  (Foldable.ret F1 v₁)  (ret v₂)
    field op⩳ : ∀{op}{rs₁ : Tuple (sig op) (Bind V₁ C₁)}
                     {rs₂ : Tuple (sig op) (Bind V₂ C₂)}
               zip  {b}  _⩳_{V₁ = V₁}{V₂}{C₁}{C₂}{b}) {bs = sig op} rs₁ rs₂
               fold-op op rs₁  fold-op op rs₂
  open Similar {{...}} public

  record SyntacticFold { : Level}(V : Set )(C : Set )
    : Set (lsuc ) where
    field {{V-shiftable}} : Shiftable V
          {{foldable}} : Foldable V C
          fvᵛ : V  Var  Set
          fvᶜ : C  Var  Set
          fv-ret :  (v : V)  fvᶜ (ret v)  fvᵛ v
          fv-var→val :  (x y : Var)  fvᵛ (var→val x) y  (x  y)
          fv-shift :  (v : V) (y : Var)  fvᵛ ( v) (suc y)  fvᵛ v y          
  open SyntacticFold {{...}} public

{------------------------------------------------------------------------------}

postulate
  extensionality : ∀{ℓ₁ ℓ₂} {A : Set ℓ₁ }{B : Set ℓ₂} {f g : A  B}
     (∀ (x : A)  f x  g x)
      -----------------------
     f  g