{- Formal verification of authenticated append-only skiplists in Agda, version 1.0.

   Copyright (c) 2020 Oracle and/or its affiliates.
   Licensed under the Universal Permissive License v 1.0 as shown at https://opensource.oracle.com/licenses/upl
-}
open import Data.Unit.NonEta
open import Data.Empty
open import Data.Sum
open import Data.Product
open import Data.Product.Properties
open import Data.Fin hiding (_<_; _≤_)
open import Data.Fin.Properties using () renaming (_≟_ to _≟Fin_)
open import Data.Nat renaming (_≟_ to _≟ℕ_; _≤?_ to _≤?ℕ_)
open import Data.Nat.Properties
open import Data.List renaming (map to List-map)
open import Data.List.Properties using (∷-injective; length-map)
open import Data.List.Relation.Unary.Any renaming (map to Any-map)
open import Data.List.Relation.Unary.All renaming (lookup to All-lookup; map to All-map)
open import Data.List.Relation.Unary.All.Properties hiding (All-map)
open import Data.List.Relation.Unary.Any.Properties renaming (map⁺ to Any-map⁺)
open import Data.List.Relation.Binary.Pointwise using (decidable-≡)
open import Data.Bool hiding (_<_; _≤_)
open import Data.Maybe renaming (map to Maybe-map)
open import Function

open import Relation.Binary.PropositionalEquality
open import Relation.Binary.Core
open import Relation.Nullary

open import AAOSL.Lemmas
open import AAOSL.Abstract.Hash
open import AAOSL.Abstract.DepRel

-- This module defines the DepRel type, which represents the class of AAOSLs we
-- consider, and proves properties about any DepRel.

module AAOSL.Abstract.Advancement
   -- A Hash function maps a bytestring into a hash.
   (hash    : ByteString → Hash)
   -- And is collision resistant
   (hash-cr : ∀{x y} → hash x ≡ hash y → Collision hash x y ⊎ x ≡ y)

   -- Indexes can be encoded in an injective way
   (encodeI     : ℕ → ByteString)
   (encodeI-inj : (m n : ℕ) → encodeI m ≡ encodeI n → m ≡ n)

   (dep  : DepRel)
 where

  -- Brings the DepRel names into scope instantiated
  -- for the module parameters in question.
  open DepRel dep

  hop-prog : ∀{m}(h : HopFrom m) → hop-tgt h ≢ m
  hop-prog = <⇒≢ ∘ hop-<

  hop-≤ : ∀{m}(h : HopFrom m) → hop-tgt h ≤ m
  hop-≤ = <⇒≤ ∘ hop-<

  depsof : ℕ → List ℕ
  depsof 0       = []
  depsof (suc i) = List-map hop-tgt (nats (lvlof (suc i)))

  depsof-ne : ∀ m → depsof m ≡ [] → m ≡ 0
  depsof-ne 0       hyp = refl
  depsof-ne (suc m) hyp =
    let le0 = trans (sym (length-map hop-tgt (nats (lvlof (suc m)))) )
                    (cong length hyp)
        le-nz = nats-length (lvlof-s m)
     in ⊥-elim (1≤0-⊥ (subst (1 ≤_) le0 le-nz))

  hopFromZ-⊥ : (h : HopFrom 0) → ⊥
  hopFromZ-⊥ h = fin0-⊥ (subst Fin lvlof-z h)
    where fin0-⊥ : Fin 0 → ⊥
          fin0-⊥ ()

  hop-tgt-is-dep : {m : ℕ}(h : HopFrom m)
                 → hop-tgt h ∈ depsof m
  hop-tgt-is-dep {0}     h = ⊥-elim (hopFromZ-⊥ h)
  hop-tgt-is-dep {suc m} h = Any-map⁺ (Any-map (cong hop-tgt) (nats-correct h))

  -- Two simple but useful lemmas made to work over our abstract
  -- index type.
  ≤-≢-mon : ∀{i j tgt} → tgt ≢ j → tgt ≤ j → i ≤ tgt → i ≢ j
  ≤-≢-mon tgt≢j tgt≤j i≤tgt i≡j
    = tgt≢j (sym (≤-antisym (subst (_≤ _) i≡j i≤tgt) tgt≤j))

  ≟ℕ-refl : (s : ℕ) → (s ≟ℕ s) ≡ yes refl
  ≟ℕ-refl s with s ≟ℕ s
  ...| no abs   = ⊥-elim (abs refl)
  ...| yes refl = refl

  ⊥-prop : (a b : ⊥) → a ≡ b
  ⊥-prop () ()

  ≟ℕ-neg : (m n : ℕ) → (p : m ≢ n) → (m ≟ℕ n) ≡ no p
  ≟ℕ-neg m n p with m ≟ℕ n
  ...| yes imp = ⊥-elim (p imp)
  ...| no  r   = cong no (fun-ext (λ x → ⊥-prop (r x) (p x)))

  HopFrom≢0 : ∀{j}(h : HopFrom j) → j ≢ 0
  HopFrom≢0 {j} h refl = hopFromZ-⊥ h

  open WithCryptoHash hash hash-cr

  -- This function is total, even though we won't always know a hash for every
  -- index.  That's ok, we only use the hashes of the relevant indexes.
  View : Set
  View = ℕ → Hash

  -- An inhabitant of 'Agree s t ixs' is a proof that the views s and t
  -- agree on the hash of every index i ∈ ixs.
  Agree : View → View → List ℕ → Set
  Agree v u = All (λ s → v s ≡ u s)

  -- Returns the list of hashes that the authenticator
  -- at the given index depends on.
  deps-hash : ℕ → View → List Hash
  deps-hash s tbl = List-map tbl (depsof s)

  -- TODO-2: Make auth, auth-inj-1 and auth-inj-2 module parameters
  -- TODO-1: Make sure the names are consistent with the paper

  --------------------------
  -- Defining authenticators

  -- Authenticators will depend on all p-auths of the
  -- dependencies of a node.
  auth : (s : ℕ) → Hash → View → Hash
  auth s h tbl = hash-concat (hash (encodeI s) ∷ h ∷ deps-hash s tbl)

  -- We will be using two separate injectivity functions. One is
  -- for the hash of the data in a node, which can't be the
  -- initial node!
  auth-inj-1 : {j : ℕ}{h₁ h₂ : Hash}{t₁ t₂ : View}
             → j ≢ 0
             → auth j h₁ t₁ ≡ auth j h₂ t₂
             → HashBroke ⊎ h₁ ≡ h₂
  auth-inj-1 {j} {h₁} {h₂} {t₁} {t₂} j≢s₀ hip
    with hash-concat-inj { hash (encodeI j) ∷ h₁ ∷ deps-hash j t₁ }
                         { hash (encodeI j) ∷ h₂ ∷ deps-hash j t₂ } hip
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ r  = inj₂ (proj₁ (∷-injective (proj₂ (∷-injective r))))

  -- The second one does induction on the list of dependencies.
  auth-inj-2 : {i : ℕ}{h : Hash}(t₁ t₂ : View)
             → auth i h t₁ ≡ auth i h t₂
             → HashBroke ⊎ Agree t₁ t₂ (depsof i)
  auth-inj-2 {i} {h} t₁ t₂ hip
    with hash-concat-inj { hash (encodeI i) ∷ h ∷ deps-hash i t₁ }
                         { hash (encodeI i) ∷ h ∷ deps-hash i t₂ } hip
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ r  = inj₂ (auth-inj-2-aux t₁ t₂ (depsof i) (proj₂ (∷-injective (proj₂ (∷-injective r)))))
    where
      auth-inj-2-aux : (t₁ t₂ : View)(l : List ℕ)
                     → List-map t₁ l ≡ List-map t₂ l
                     → Agree t₁ t₂ l
      auth-inj-2-aux t1 t2 [] hyp = []
      auth-inj-2-aux t1 t2 (x ∷ l) hyp = proj₁ (∷-injective hyp) ∷ auth-inj-2-aux t1 t2 l (proj₂ (∷-injective hyp))

  ------------------------
  ------------------------
  -- Advancement Proofs --
  ------------------------
  ------------------------

  -- Finally, advancement proofs in their simple variant
  data AdvPath : ℕ → ℕ → Set where
    AdvDone  : ∀{i} → AdvPath i i
    AdvThere : ∀{j i}
             → Hash -- datum digest
             → (h : HopFrom j)
             → AdvPath (hop-tgt h) i
             → AdvPath j i

  -- Override a view with a hash for a specific index.
  _∪₁_ : View → ℕ × Hash → View
  _∪₁_ tbl (s , h) s'
     with s ≟ℕ s'
  ...| yes _ = h
  ...| no  _ = tbl s'

  -- The rebuild function is, essentially, a transformer over
  -- the current view of the skiplog. It is PARAMOUNT to return
  -- a new 'view' of the world, as we can see in rebuild-⊕ lemma.
  -- Otherwise, it becomes seriously intricate to express
  -- that rebuilding the hash of index j "depends on" the rebuilt
  -- hashes of j's dependencies.
  rebuild : ∀{i j} → AdvPath j i → View → View
  rebuild {i} AdvDone tbl = tbl
  rebuild (AdvThere {j = j} x h prf) tbl
    = let tbl' = rebuild prf tbl
       in tbl' ∪₁ (j , auth j x tbl')

  lemma1 : ∀{j i} → AdvPath j i → i ≤ j
  lemma1 AdvDone              = ≤-refl
  lemma1 {j} (AdvThere x h a) = ≤-trans (lemma1 a) (hop-≤ h)

  rebuild-tgt-lemma : ∀{j i}(a : AdvPath j i){t : View}
                    → rebuild a t i ≡ t i
  rebuild-tgt-lemma AdvDone = refl
  rebuild-tgt-lemma {j} {i} (AdvThere x h a)
    rewrite ≟ℕ-neg j i (<⇒≢ (≤-<-trans (lemma1 a) (hop-< h)) ∘ sym)
          = rebuild-tgt-lemma a

  lemma2 : ∀{i}(a : AdvPath i i) → a ≡ AdvDone
  lemma2 AdvDone = refl
  lemma2 (AdvThere x h a)
    = ⊥-elim (hop-prog h (sym (≤-antisym (lemma1 a) (hop-≤ h))))

  -- Lemma3 states that if a hop exists, then it is not from
  -- s₀. This is necessary to eliminate some nasty cases.
  lemma3 : ∀{j i} → (h : HopFrom j) → AdvPath (hop-tgt h) i → j ≢ 0
  lemma3 h a = HopFrom≢0 h

  ----------------------
  -- Proof Splitting  --
  ----------------------

  _⊕_ : ∀{j k i} → AdvPath j k → AdvPath k i → AdvPath j i
  AdvDone          ⊕ rest = rest
  (AdvThere d h a) ⊕ rest = AdvThere d h (a ⊕ rest)

  ⊕-id-r : ∀{j i}(a : AdvPath j i) → a ⊕ AdvDone ≡ a
  ⊕-id-r AdvDone = refl
  ⊕-id-r (AdvThere x h a) = cong (AdvThere x h) (⊕-id-r a)

  -- A value of type 'i ∈AP a' illustrates index i as a dependency
  -- of 'a'.
  data _∈AP_ (i₀ : ℕ) : {j i : ℕ} → AdvPath j i → Set where
    hereTgtDone  : i₀ ∈AP (AdvDone {i₀})

    hereTgtThere : ∀{i}{d : Hash}{hop : HopFrom i₀}{a : AdvPath (hop-tgt hop) i}
                 → i₀ ∈AP (AdvThere d hop a)

    step         : ∀{i j}{d : Hash}{hop : HopFrom j}{a : AdvPath (hop-tgt hop) i}
                 → i₀ ≢ j
                 → i₀ ∈AP a
                 → i₀ ∈AP (AdvThere d hop a)

  ∈AP-src : ∀{j i}{a : AdvPath j i}
          → j ∈AP a
  ∈AP-src {a = AdvDone}        = hereTgtDone
  ∈AP-src {a = AdvThere x h a} = hereTgtThere

  ∈AP-tgt : ∀{j i}{a : AdvPath j i}
          → i ∈AP a
  ∈AP-tgt {a = AdvDone}        = hereTgtDone
  ∈AP-tgt {a = AdvThere x h a} = step (<⇒≢ (≤-<-trans (lemma1 a) (hop-< h))) ∈AP-tgt

  ∈AP-≤ : ∀{j i}{a : AdvPath j i}
               → {i0 : ℕ} → i0 ∈AP a
               → i0 ≤ j
  ∈AP-≤ hereTgtDone  = ≤-refl
  ∈AP-≤ hereTgtThere = ≤-refl
  ∈AP-≤ (step _ hyp) = ≤-trans (∈AP-≤ hyp) (hop-≤ _)

  ∈AP-≥ : ∀{j i}{a : AdvPath j i}
        → {i0 : ℕ} → i0 ∈AP a
        → i ≤ i0
  ∈AP-≥ hereTgtDone = ≤-refl
  ∈AP-≥ {a = a} hereTgtThere = lemma1 a
  ∈AP-≥ (step _ hyp) = ∈AP-≥ hyp

  rebuild-⊕' : ∀{j k i}
             → {t : View}
             → (a₁ : AdvPath j k)
             → (a₂ : AdvPath k i)
             → ∀{l} → l ∈AP a₂
             → rebuild (a₁ ⊕ a₂) t l ≡ rebuild a₂ t l
  rebuild-⊕' AdvDone a₂ hyp = refl
  rebuild-⊕' {j} (AdvThere x h a₁) a₂ {l} hyp
    with j ≟ℕ l
  ...| yes nope = ⊥-elim (≤-≢-mon (hop-prog h) (hop-≤ h)
                          (≤-trans (∈AP-≤ hyp) (lemma1 a₁)) (sym nope))
  ...| no ok    = rebuild-⊕' a₁ a₂ hyp

  ∈AP-cut : ∀{j k i}
          → (a : AdvPath j i)
          → k ∈AP a
          → Σ (AdvPath j k × AdvPath k i)
              (λ { (x , y) → a ≡ x ⊕ y })
  ∈AP-cut AdvDone hereTgtDone
    = (AdvDone , AdvDone) , refl
  ∈AP-cut (AdvThere d h a) hereTgtThere
    = (AdvDone , AdvThere d h a) , refl
  ∈AP-cut (AdvThere d h a) (step x prf)
    with ∈AP-cut a prf
  ...| xy , ind = (AdvThere d h (proj₁ xy) , proj₂ xy)
                , cong (AdvThere d h) ind

  ∈AP-cut₁ : ∀{j k i}
           → (a : AdvPath j i)
           → k ∈AP a
           → AdvPath k i
  ∈AP-cut₁ a prf = proj₂ (proj₁ (∈AP-cut a prf))

  ∈AP-∈-cut
    : ∀{j k i}
    → (a : AdvPath j i)
    → (prf : k ∈AP a)
    → ∀{m} → m ∈AP a → m ≤ k
    → m ∈AP (∈AP-cut₁ a prf)
  ∈AP-∈-cut AdvDone hereTgtDone m∈ap hyp = m∈ap
  ∈AP-∈-cut (AdvThere _ _ _) hereTgtThere m∈ap hyp = m∈ap
  ∈AP-∈-cut (AdvThere d h a) (step x prf) hereTgtThere hyp
    = ⊥-elim (<⇒≱ (hop-< h) (≤-trans hyp (∈AP-≤ prf)))
  ∈AP-∈-cut (AdvThere d h a) (step x prf) (step x₁ m∈ap) hyp
    = ∈AP-∈-cut a prf m∈ap hyp

  ∈AP-cut₁-rebuild
    : ∀{j k i}
    → (a : AdvPath j i)
    → (prf : k ∈AP a)
    → {s : ℕ} → (s ∈AP (∈AP-cut₁ a prf))
    → ∀{t} → rebuild a t s ≡ rebuild (∈AP-cut₁ a prf) t s
  ∈AP-cut₁-rebuild a prf s∈cut {t}
    with ∈AP-cut a prf
  ...| (x , y) , refl = rebuild-⊕' x y s∈cut

  rebuild-⊕ : ∀{j k i}
            → {t : View}
            → (a₁ : AdvPath j k)
            → (a₂ : AdvPath k i)
            → rebuild (a₁ ⊕ a₂) t k ≡ rebuild a₂ t k
  rebuild-⊕ {k = k} a₁ AdvDone = rebuild-⊕' a₁ AdvDone {k} hereTgtDone
  rebuild-⊕ {k = k} {i = i} a₁ (AdvThere .{k} d h p) = rebuild-⊕' a₁ (AdvThere d h p) hereTgtThere

  ∈AP-⊕ : ∀{j i₁ k i₂ i}
        → {e  : AdvPath j k}{a₁ : AdvPath k i₁}
        → {a₂ : AdvPath k i₂}
        → i ∈AP (e ⊕ a₁)
        → i ∈AP a₂
        → i ∈AP a₁
  ∈AP-⊕ {e = AdvDone}        hyp1 hyp2 = hyp1
  ∈AP-⊕ {e = AdvThere x h e} hereTgtThere   hyp2
    with ≤-antisym (∈AP-≤ hyp2) (lemma1 (AdvThere x h e))
  ...| refl = ⊥-elim (hop-prog h (≤-antisym (hop-≤ h) (lemma1 e)))
  ∈AP-⊕ {e = AdvThere x h e} (step x₁ hyp1) hyp2 = ∈AP-⊕ hyp1 hyp2

  ∈AP-point' : ∀{j k i}
             → {a₁ : AdvPath j k}
             → {a₂ : AdvPath k i}
             → {m : ℕ} → m ∈AP a₁ → m ∈AP a₂
             → m ≡ k
  ∈AP-point' hereTgtDone h2 = refl
  ∈AP-point' {a₁ = AdvThere d h a₁} {a₂} hereTgtThere h2
    = ⊥-elim (≤-≢-mon (≤-≢-mon (hop-prog h) (hop-≤ h) (lemma1 a₁) ∘ sym)
                      (∈AP-≤ h2)
                      (lemma1 (AdvThere d h a₁)) refl)
  ∈AP-point' (step x h1) h2 = ∈AP-point' h1 h2

  ∈AP-point'' : ∀{j₁ j₂ i₁ i₂}
              → {a₁ : AdvPath j₁ i₁}
              → {a₂ : AdvPath j₂ i₂}
              → j₂ < i₁
              → {i : ℕ} → i ∈AP a₁ → i ∈AP a₂
              → ⊥
  ∈AP-point'' j<i hereTgtDone h2
    = (<⇒≢ j<i) (≤-antisym (<⇒≤ j<i) (∈AP-≤ h2))
  ∈AP-point'' {a₁ = a₁} j<i hereTgtThere h2
    = (<⇒≢ j<i) (≤-antisym (<⇒≤ j<i) (≤-trans (lemma1 a₁) (∈AP-≤ h2)))
  ∈AP-point'' j<i (step x h1) h2 = ∈AP-point'' j<i h1 h2

  ∈AP-point : ∀{j₁ j₂ i₁ i₂}
            → {a₁ : AdvPath j₁ i₁}
            → {a₂ : AdvPath j₂ i₂}
            → j₂ ≤ i₁
            → {i : ℕ} → i ∈AP a₁ → i ∈AP a₂
            → j₂ ≡ i₁ × i ≡ j₂
  ∈AP-point {j₂ = j₂} {i₁ = i₁} j₂≤i₁ h1 h2
    with j₂ ≟ℕ i₁
  ...| no abs = ⊥-elim (∈AP-point'' (≤∧≢⇒< j₂≤i₁ abs) h1 h2)
  ...| yes refl = refl , ∈AP-point' h1 h2

  ∈AP-AdvDone-≡ : ∀{i j}
                → i ∈AP (AdvDone {j})
                → i ≡ j
  ∈AP-AdvDone-≡ hereTgtDone = refl

  -- It is important that we can split proofs. Here, we know that 'a'
  -- and the guide are proofs that come from jump from the same source, j.
  split-⊕ : ∀{J j i}
           → {H : HopFrom J}
           → {h : HopFrom j}
           → j ≤ J
           → hop-tgt H < hop-tgt h
           → i ≤ hop-tgt H
           → (a : AdvPath (hop-tgt h) i)
           → Σ (AdvPath (hop-tgt h) (hop-tgt H) × AdvPath (hop-tgt H) i)
               (λ { (x , y) → a ≡ x ⊕ y })
  split-⊕ j≤J H<h i≤H AdvDone
    = ⊥-elim (≤-≢-mon (<⇒≢ H<h) (<⇒≤ H<h) i≤H refl)
  split-⊕ {i = i} {H} {h} j≤J H<h i≤H (AdvThere d h' a)
    with hop-tgt h' ≟ℕ hop-tgt H
  ...| yes sameHop = (AdvThere d h' (subst (AdvPath (hop-tgt h')) sameHop AdvDone)
                     , subst (λ P → AdvPath P i) sameHop a)
                     , aux sameHop a
     where
      aux : ∀{i j k}{h : HopFrom j} → (stop : hop-tgt h ≡ k)
          → (a : AdvPath (hop-tgt h) i)
          → AdvThere d h a
            ≡ (AdvThere d h (subst (AdvPath (hop-tgt h)) stop AdvDone)
               ⊕ subst (λ P → AdvPath P i) stop a)
      aux refl a = refl
  ...| no  diffHop
    with ≤-total (hop-tgt h') (hop-tgt H)
  ...| inj₁ crossover with hops-nested-or-nonoverlapping (≤∧≢⇒< crossover diffHop)
                                                         H<h
  ...| abs = ⊥-elim (≤-≢-mon (mmm (hop-≤ h) (hop-prog h) j≤J) (≤-trans (hop-≤ h) j≤J) abs refl)
     where mmm : ∀{h j J} → h ≤ j → h ≢ j → j ≤ J → h ≢ J
           mmm h≤j h≢j j≤J refl = h≢j (≤-antisym h≤j j≤J)
  split-⊕ {i = i} {H} {h} j≤J H<h i≤H (AdvThere d h' a)
     | no diffHop
     | inj₂ go
    with split-⊕ {H = H} (≤-trans (hop-≤ h) j≤J) (≤∧≢⇒< go (diffHop ∘ sym)) i≤H a
  ...| ((x , y) , prf) = (AdvThere d h' x , y) , cong (AdvThere d h') prf

  ---------------------
  -- Evolutionary CR --
  ---------------------

  -- The type 'AgreeOnCommon t₁ t₂ a₁ a₂', or 'AOC' for short, is inhabited if
  -- and only if the advancement proofs a₁ and a₂ agree on the hash they rebuild
  -- for every index that is visited by both. Moreover, the views must also
  -- agree on the dependencies of said indexes.
  data AOC (t₁ t₂ : View)
      : ∀{i₁ i₂ j} → AdvPath j i₁ → AdvPath j i₂ → Set where
    PDoneDone : ∀{i} → t₁ i ≡ t₂ i → AOC t₁ t₂ {i} {i} AdvDone AdvDone

    --                        h
    --             ⌜⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⁻⌝
    --             |                     |
    --             |         ⌜⁻⁻⁻ a₁ ⁻⁻⁻⁻|
    --             |         |           |
    --   i₂  ⋯  hop-tgt h  ≤ i₁          j
    PDoneL : ∀{j i₁ i₂}{d}{h : HopFrom j}
           → (a₁ : AdvPath j i₁)
           → (a₂ : AdvPath (hop-tgt h) i₂)
           → hop-tgt h ≤ i₁
           → rebuild a₁ t₁ j ≡ rebuild (AdvThere d h a₂) t₂ j
           → AOC t₁ t₂ a₁ (AdvThere d h a₂)

    PDoneR : ∀{j i₁ i₂}{d}{h : HopFrom j}
           → (a₁ : AdvPath (hop-tgt h) i₁)
           → (a₂ : AdvPath j i₂)
           → hop-tgt h ≤ i₂
           → rebuild (AdvThere d h a₁) t₁ j ≡ rebuild a₂ t₂ j
           → AOC t₁ t₂ (AdvThere d h a₁) a₂

    PCong : ∀{j i₁ i₂}{d}{h : HopFrom j}
          → i₁ ≤ i₂
          → (a₁ : AdvPath (hop-tgt h) i₁)
          → (a₂ : AdvPath (hop-tgt h) i₂)
          → Agree (rebuild a₁ t₁) (rebuild a₂ t₂) (depsof j)
          → AOC t₁ t₂ a₁ a₂
          → AOC t₁ t₂ (AdvThere d h a₁)
                      (AdvThere d h a₂)

    PMeetR : ∀{j i₁ i₂ d}{h₁ h₂ : HopFrom j}
           → (e  : AdvPath (hop-tgt h₁) (hop-tgt h₂))
           → (a₁ : AdvPath (hop-tgt h₂) i₁)
           → (a₂ : AdvPath (hop-tgt h₂) i₂)
           → hop-tgt h₂ < hop-tgt h₁
           → Agree (rebuild (e ⊕ a₁) t₁) (rebuild a₂ t₂) (depsof j)
           → AOC t₁ t₂ a₁ a₂
           → AOC t₁ t₂ (AdvThere d h₁ (e ⊕ a₁))
                       (AdvThere d h₂ a₂)

    PMeetL : ∀{j i₁ i₂ d}{h₁ h₂ : HopFrom j}
           → (e  : AdvPath (hop-tgt h₂) (hop-tgt h₁))
           → (a₁ : AdvPath (hop-tgt h₁) i₁)
           → (a₂ : AdvPath (hop-tgt h₁) i₂)
           → hop-tgt h₁ < hop-tgt h₂
           → Agree (rebuild a₁ t₁) (rebuild (e ⊕ a₂) t₂) (depsof j)
           → AOC t₁ t₂ a₁ a₂
           → AOC t₁ t₂ (AdvThere d h₁ a₁)
                       (AdvThere d h₂ (e ⊕ a₂))

  -- We use the 'TERMINATING' pragma because we use a recursive call on
  -- an argument that Agda can't infer being structurally smaller.
  -- TODO-1: provide clear and detailed explanation of this, which argument, why smaller?
  -- An interesting longer-term TODO-2 would be to use 'Sized Types' to inform
  -- the typechecker of this fact, or perhaps a custom-made moral equivalent.
  {-# TERMINATING #-}
  aoc : ∀{i₁ i₂ j}
      → i₁ ≤ i₂ -- wlog
      → (t₁ t₂ : View)(a₁ : AdvPath j i₁)(a₂ : AdvPath j i₂)
      → rebuild a₁ t₁ j ≡ rebuild a₂ t₂ j
      → HashBroke ⊎ AOC t₁ t₂ a₁ a₂
  aoc _ t1 t2 AdvDone AdvDone hip
    = inj₂ (PDoneDone hip)
  aoc _ t1 t2 AdvDone (AdvThere x h a2) hip
    = inj₂ (PDoneL AdvDone a2 (hop-≤ h) hip)
  aoc _ t1 t2 (AdvThere x h a1) AdvDone hip
    = inj₂ (PDoneR a1 AdvDone (hop-≤ h) hip)
  aoc {i₁} {i₂} {j} k t₁ t₂ (AdvThere d₁ h₁ a₁) (AdvThere d₂ h₂ a₂) hip
    with ≤-total i₂ (hop-tgt h₁)
  ...| inj₂ h₁≤i₂ = inj₂ (PDoneR a₁ (AdvThere d₂ h₂ a₂) h₁≤i₂ hip)
  ...| inj₁ go
    rewrite ≟ℕ-refl j
    with auth-inj-1 {j} {d₁} {d₂} (lemma3 h₁ a₁) hip
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ refl
    with auth-inj-2 {j} {d₁} (rebuild a₁ t₁) (rebuild a₂ t₂) hip
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ agree
    with h₁ ≟Hop h₂
  ...| yes refl
    with witness (hop-tgt-is-dep h₁) agree
  ...| wit rewrite (≟ℕ-refl (hop-tgt h₁))
    with aoc k t₁ t₂ a₁ a₂ wit
  ...| inj₁ hb  = inj₁ hb
  ...| inj₂ rec = inj₂ (PCong k a₁ a₂ agree rec)
  aoc {i₁} {i₂} {j} k t₁ t₂ (AdvThere d₁ h₁ a₁) (AdvThere d₂ h₂ a₂) hip
     | inj₁ go
     | inj₂ refl
     | inj₂ agree
     | no diffHop
    with ≤-total (hop-tgt h₁) (hop-tgt h₂)
  ...| inj₁ h₁<h₂ with split-⊕ ≤-refl (≤∧≢⇒< h₁<h₂ (diffHop ∘ hop-tgt-inj)) go a₂
  ...| ((x , y) , refl)
    with aoc k t₁ t₂ a₁ y (trans (witness (hop-tgt-is-dep h₁) agree) (rebuild-⊕ x y))
  ...| inj₁ hb  = inj₁ hb
  ...| inj₂ res = inj₂ (PMeetL x a₁ y (≤∧≢⇒< h₁<h₂ (diffHop ∘ hop-tgt-inj)) agree res)
  aoc {j = j} k t₁ t₂ (AdvThere d₁ h₁ a₁) (AdvThere d₂ h₂ a₂) hip
     | inj₁ go
     | inj₂ refl
     | inj₂ agree
     | no diffHop
     | inj₂ h₂<h₁ with split-⊕ ≤-refl (≤∧≢⇒< h₂<h₁ (diffHop ∘ hop-tgt-inj ∘ sym))
                         (≤-trans k (lemma1 a₂)) a₁
  ...| ((x , y) , refl)
    with aoc k t₁ t₂ y a₂ (trans (sym (rebuild-⊕ x y))
                                 (witness (hop-tgt-is-dep h₂) agree))
  ...| inj₁ hb  = inj₁ hb
  ...| inj₂ res = inj₂ (PMeetR x y a₂ (≤∧≢⇒< h₂<h₁ (diffHop ∘ hop-tgt-inj ∘ sym)) agree res)

  -- TODO: rename AGREEONCOMMON for consistency with paper, or comment to make connection
  aoc-correct : ∀{j i₁ i₂}{a₁ : AdvPath j i₁}{a₂ : AdvPath j i₂}
              → {t₁ t₂ : View}
              → AOC t₁ t₂ a₁ a₂
              → {i : ℕ} → i ∈AP a₁ → i ∈AP a₂
              → HashBroke ⊎ rebuild a₁ t₁ i ≡ rebuild a₂ t₂ i
  aoc-correct (PDoneDone x) hereTgtDone hereTgtDone = inj₂ x
  aoc-correct (PDoneL a₁ a₂ x x₁) hyp1 hereTgtThere = inj₂ x₁
  aoc-correct {j} {t₁ = t₁} {t₂} (PDoneL {d = d} {h} a₁ a₂ x x₁) {i} hyp1 (step x₂ hyp2)
    with j ≟ℕ i
  ...| yes nope = ⊥-elim (x₂ (sym nope))
  ...| no  ok
    with a₁
  ...| AdvDone rewrite ∈AP-AdvDone-≡ hyp1 = ⊥-elim (x₂ refl)
  ...| AdvThere d₁ h₁ a₁'
    with ∈AP-point x hyp1 hyp2
  ...| refl , refl
    with j ≟ℕ hop-tgt h
  ...| yes nope = ⊥-elim (x₂ (sym nope))
  ...| no ok' rewrite ≟ℕ-refl j
    with auth-inj-1 {h₁ = d₁} {d} (HopFrom≢0 h) x₁
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ refl
    with auth-inj-2 {j} {d₁} (rebuild a₁' t₁) (rebuild a₂ t₂) x₁
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ r  = inj₂ (witness (hop-tgt-is-dep h) r)
  aoc-correct (PDoneR a₁ a₂ x x₁) hereTgtThere hyp2 = inj₂ x₁
  aoc-correct {j} {t₁ = t₁} {t₂} (PDoneR {d = d} {h} a₁ a₂ x x₁) {i} (step x₂ hyp1) hyp2
    with j ≟ℕ i
  ...| yes nope = ⊥-elim (x₂ (sym nope))
  ...| no  ok
    with a₂
  ...| AdvDone rewrite ∈AP-AdvDone-≡ hyp2 = ⊥-elim (x₂ refl)
  ...| AdvThere d₂ h₂ a₂'
    with ∈AP-point x hyp2 hyp1
  ...| refl , refl
    with j ≟ℕ hop-tgt h
  ...| yes nope = ⊥-elim (x₂ (sym nope))
  ...| no ok' rewrite ≟ℕ-refl j
    with auth-inj-1 {h₁ = d₂} {d} (HopFrom≢0 h) (sym x₁)
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ refl
    with auth-inj-2 {j} {d₂} (rebuild a₁ t₁) (rebuild a₂' t₂) x₁
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ r  = inj₂ (witness (hop-tgt-is-dep h) r)
  aoc-correct {j} {t₁ = t₁} {t₂} (PCong x a₁ a₂ x₁ aoc₁) hereTgtThere hereTgtThere
    rewrite ≟ℕ-refl j
          | List-map-≡-All (rebuild a₁ t₁) (rebuild a₂ t₂) (depsof j) x₁
          = inj₂ refl
  aoc-correct (PCong x a₁ a₂ x₁ aoc₁) hereTgtThere (step x₂ hyp2)
    = ⊥-elim (x₂ refl)
  aoc-correct (PCong x a₁ a₂ x₁ aoc₁) (step x₂ hyp1) hereTgtThere
    = ⊥-elim (x₂ refl)
  aoc-correct {j} (PCong x a₁ a₂ x₁ aoc₁) {i} (step x₂ hyp1) (step x₃ hyp2)
    with j ≟ℕ i
  ...| yes nope = ⊥-elim (x₂ (sym nope))
  ...| no  ok   = aoc-correct aoc₁ hyp1 hyp2
  aoc-correct {j} {t₁ = t₁} {t₂} (PMeetR e a₁ a₂ x₁ x₂ aoc₁) hereTgtThere hereTgtThere
    rewrite ≟ℕ-refl j
          | List-map-≡-All (rebuild (e ⊕ a₁) t₁) (rebuild a₂ t₂) (depsof j) x₂
          = inj₂ refl
  aoc-correct (PMeetR e a₁ a₂ x₁ x₂ aoc₁) hereTgtThere (step x₃ hyp2)
    = ⊥-elim (x₃ refl)
  aoc-correct (PMeetR e a₁ a₂ x₁ x₂ aoc₁) (step x₃ hyp1) hereTgtThere
    = ⊥-elim (x₃ refl)
  aoc-correct {j} (PMeetR e a₁ a₂ x₁ x₂ aoc₁) {i} (step x₃ hyp1) (step x₄ hyp2)
    with j ≟ℕ i
  ...| yes nope = ⊥-elim (x₃ (sym nope))
  ...| no  ok
    with aoc-correct aoc₁ (∈AP-⊕ hyp1 hyp2) hyp2
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ r  = inj₂ (trans (rebuild-⊕' e a₁ (∈AP-⊕ hyp1 hyp2)) r)

  aoc-correct {j} {t₁ = t₁} {t₂} (PMeetL e a₁ a₂ x₁ x₂ aoc₁) hereTgtThere hereTgtThere
    rewrite ≟ℕ-refl j
          | List-map-≡-All (rebuild a₁ t₁) (rebuild (e ⊕ a₂) t₂) (depsof j) x₂
          = inj₂ refl
  aoc-correct (PMeetL e a₁ a₂ x₁ x₂ aoc₁) hereTgtThere (step x₃ hyp2)
    = ⊥-elim (x₃ refl)
  aoc-correct (PMeetL e a₁ a₂ x₁ x₂ aoc₁) (step x₃ hyp1) hereTgtThere
    = ⊥-elim (x₃ refl)
  aoc-correct {j} (PMeetL e a₁ a₂ x₁ x₂ aoc₁) {i} (step x₃ hyp1) (step x₄ hyp2)
    with j ≟ℕ i
  ...| yes nope = ⊥-elim (x₃ (sym nope))
  ...| no  ok
    with aoc-correct aoc₁ hyp1 (∈AP-⊕ hyp2 hyp1)
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ r  = inj₂ (trans r (sym (rebuild-⊕' e a₂ (∈AP-⊕ hyp2 hyp1))))

  AgreeOnCommon : ∀{j i₁ i₂}
                → {t₁ t₂ : View}
                → (a₁ : AdvPath j i₁)(a₂ : AdvPath j i₂)
                → rebuild a₁ t₁ j ≡ rebuild a₂ t₂ j
                → {i : ℕ} → i ∈AP a₁ → i ∈AP a₂
                → HashBroke ⊎ rebuild a₁ t₁ i ≡ rebuild a₂ t₂ i
  AgreeOnCommon {i₁ = i₁} {i₂} {t₁} {t₂} a₁ a₂ rebuild-to-j i∈a₁ i∈a₂
     with ≤-total i₁ i₂
  ...| inj₁ i₁≤i₂ with aoc i₁≤i₂ t₁ t₂ a₁ a₂ rebuild-to-j
  ...|              inj₁ hb = inj₁ hb
  ...|              inj₂ xx = aoc-correct xx i∈a₁ i∈a₂

  AgreeOnCommon {i₁ = i₁} {i₂} {t₁} {t₂} a₁ a₂ rebuild-to-j i∈a₁ i∈a₂
     | inj₂ i₂≤i₁ with aoc i₂≤i₁ t₂ t₁ a₂ a₁ (sym rebuild-to-j)
  ...|              inj₁ hb = inj₁ hb
  ...|              inj₂ xx with aoc-correct xx i∈a₂ i∈a₁
  ...|                        inj₁ hb = inj₁ hb
  ...|                        inj₂ xx1 = inj₂ (sym xx1)

  AgreeOnCommon-∈ : ∀{j₁ j₂ i₁ i₂}
                → {t₁ t₂ : View}
                → (a₁ : AdvPath j₁ i₁)(a₂ : AdvPath j₂ i₂)
                → j₂ ∈AP a₁
                → rebuild a₁ t₁ j₂ ≡ rebuild a₂ t₂ j₂
                → {i : ℕ} → i ∈AP a₁ → i ∈AP a₂
                → HashBroke ⊎ rebuild a₁ t₁ i ≡ rebuild a₂ t₂ i
  AgreeOnCommon-∈ a₁ a₂ j2∈a1 hyp ia1 ia2
    with ∈AP-cut a₁ j2∈a1
  ...| ((a₁₁ , a₁₂) , refl)
    with AgreeOnCommon a₁₂ a₂ (trans (sym (rebuild-⊕' a₁₁ a₁₂ ∈AP-src)) hyp) (∈AP-⊕ ia1 ia2) ia2
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ res = inj₂ (trans (rebuild-⊕' a₁₁ a₁₂ (∈AP-⊕ ia1 ia2)) res)

  -----------------------
  -- Membership Proofs --
  -----------------------

  -- A membership proof for i is simply an advancement proof from j to i,
  -- a digest of the data in i and the authenticators that i depends on (which
  -- come in the view)
  MembershipProof : ℕ → ℕ → Set
  MembershipProof j i = AdvPath j i × Hash × i ≢ 0

  mbr-datum : ∀{j i} → MembershipProof j i → Hash
  mbr-datum (_ , d , _) = d

  mbr-proof : ∀{j i} → MembershipProof j i → AdvPath j i
  mbr-proof (p , _ , _) = p

  mbr-not-init : ∀{j i} → MembershipProof j i → i ≢ 0
  mbr-not-init (_ , _ , m) = m

  -- Rebuilding it is the same as rebuilding an advancement proof, but we
  -- explicitely compute the authenticator at i.
  rebuildMP : ∀{j i} → MembershipProof j i → View → View
  rebuildMP {j} {i} mbr t = rebuild (mbr-proof mbr)
                                    (t ∪₁ (i , auth i (mbr-datum mbr) t))

  semi-evo-cr : ∀{j i₁ i₂}{t₁ t₂ : View}
         → (a₁ : AdvPath j i₁)
         → (a₂ : AdvPath j i₂)
         → rebuild a₁ t₁ j ≡ rebuild a₂ t₂ j
         → ∀{s₁ s₂ tgt}{u₁ u₂ : View}
         → (m₁ : MembershipProof s₁ tgt)(m₂ : MembershipProof s₂ tgt)
         → s₁ ∈AP a₁ → s₂ ∈AP a₂
         → tgt ∈AP a₁ → tgt ∈AP a₂
         → tgt ≢ 0
         → rebuildMP m₁ u₁ s₁ ≡ rebuild a₁ t₁ s₁
         → rebuildMP m₂ u₂ s₂ ≡ rebuild a₂ t₂ s₂
         → HashBroke ⊎ (mbr-datum m₁ ≡ mbr-datum m₂)
  semi-evo-cr {t₁ = t₁} {t₂} a₁ a₂ hyp {tgt = tgt} {u₁} {u₂} m₁ m₂ s₁∈a₁ s₂∈a₂ t∈a₁ t∈a₂ t≢0 c₁ c₂
     with AgreeOnCommon (mbr-proof m₁) (∈AP-cut₁ a₁ s₁∈a₁)
                       (trans c₁ (∈AP-cut₁-rebuild a₁ s₁∈a₁ ∈AP-src {t₁}))
                       ∈AP-tgt (∈AP-∈-cut a₁ s₁∈a₁ t∈a₁ (lemma1 (mbr-proof m₁)))
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ hyp1
    with AgreeOnCommon (mbr-proof m₂) (∈AP-cut₁ a₂ s₂∈a₂)
                       (trans c₂ (∈AP-cut₁-rebuild a₂ s₂∈a₂ ∈AP-src {t₂}))
                       ∈AP-tgt (∈AP-∈-cut a₂ s₂∈a₂ t∈a₂ (lemma1 (mbr-proof m₂)))
  ...| inj₁ hb = inj₁ hb
  ...| inj₂ hyp2
    with AgreeOnCommon a₁ a₂ hyp t∈a₁ t∈a₂
  ...| inj₁ hb   = inj₁ hb
  ...| inj₂ hyp3
    rewrite sym (∈AP-cut₁-rebuild a₁ s₁∈a₁
                  (∈AP-∈-cut a₁ s₁∈a₁ t∈a₁ (lemma1 (mbr-proof m₁))) {t₁})
          | sym (∈AP-cut₁-rebuild a₂ s₂∈a₂
                  (∈AP-∈-cut a₂ s₂∈a₂ t∈a₂ (lemma1 (mbr-proof m₂))) {t₂})

    with trans hyp1 (trans hyp3 (sym hyp2))
  ...| half     with rebuild-tgt-lemma (mbr-proof m₁)
                       {u₁ ∪₁ (tgt , auth tgt (mbr-datum m₁) u₁) }
                   | rebuild-tgt-lemma (mbr-proof m₂)
                       {u₂ ∪₁ (tgt , auth tgt (mbr-datum m₂) u₂) }
  ...| l1 | l2
    rewrite ≟ℕ-refl tgt = auth-inj-1 {tgt} {mbr-datum m₁} {mbr-datum m₂} t≢0 (trans (sym l1) (trans half l2))


open import Prelude renaming (lift to finlift) hiding (id; subst)

module Implicits.Substitutions.Lemmas.LNMetaType where

open import Implicits.Syntax.LNMetaType
open import Implicits.Substitutions.LNMetaType

open import Data.Fin.Substitution
open import Data.Fin.Substitution.Lemmas
open import Data.Vec.Properties
open import Data.Star using (Star; ε; _◅_)
open import Data.Nat.Properties
open import Data.Nat as Nat
open import Extensions.Substitution
open import Relation.Binary using (module DecTotalOrder)
open DecTotalOrder decTotalOrder using () renaming (refl to ≤-refl; trans to ≤-trans)
open import Relation.Binary.HeterogeneousEquality as H using ()
module HR = H.≅-Reasoning

typeLemmas : TermLemmas MetaType
typeLemmas = record { termSubst = typeSubst; app-var = refl ; /✶-↑✶ = Lemma./✶-↑✶ }
  where
    module Lemma {T₁ T₂} {lift₁ : Lift T₁ MetaType} {lift₂ : Lift T₂ MetaType} where

      open Lifted lift₁ using () renaming (_↑✶_ to _↑✶₁_; _/✶_ to _/✶₁_)
      open Lifted lift₂ using () renaming (_↑✶_ to _↑✶₂_; _/✶_ to _/✶₂_)

      /✶-↑✶ : ∀ {m n} (σs₁ : Subs T₁ m n) (σs₂ : Subs T₂ m n) →
                          (∀ k x → (simpl (mvar x)) /✶₁ σs₁ ↑✶₁ k ≡ (simpl (mvar x)) /✶₂ σs₂ ↑✶₂ k) →
                          ∀ k t → t /✶₁ σs₁ ↑✶₁ k ≡ t /✶₂ σs₂ ↑✶₂ k
      /✶-↑✶ ρs₁ ρs₂ hyp k (simpl (mvar x)) = hyp k x
      /✶-↑✶ ρs₁ ρs₂ hyp k (simpl (tvar x)) = begin
          simpl (tvar x) /✶₁ ρs₁ ↑✶₁ k
          ≡⟨ MetaTypeApp.tvar-/✶-↑✶ _ k ρs₁ ⟩
          simpl (tvar x)
          ≡⟨ sym $ MetaTypeApp.tvar-/✶-↑✶ _ k ρs₂ ⟩
          simpl (tvar x) /✶₂ ρs₂ ↑✶₂ k ∎
      /✶-↑✶ ρs₁ ρs₂ hyp k (simpl (tc c)) = begin
          (simpl (tc c)) /✶₁ ρs₁ ↑✶₁ k
          ≡⟨ MetaTypeApp.tc-/✶-↑✶ _ k ρs₁ ⟩
          (simpl (tc c))
          ≡⟨ sym $ MetaTypeApp.tc-/✶-↑✶ _ k ρs₂ ⟩
          (simpl (tc c)) /✶₂ ρs₂ ↑✶₂ k ∎
      /✶-↑✶ ρs₁ ρs₂ hyp k (simpl (a →' b)) = begin
          (simpl (a →' b)) /✶₁ ρs₁ ↑✶₁ k
          ≡⟨ MetaTypeApp.→'-/✶-↑✶ _ k ρs₁ ⟩
          simpl ((a /✶₁ ρs₁ ↑✶₁ k) →' (b /✶₁ ρs₁ ↑✶₁ k))
          ≡⟨ cong₂ (λ a b → simpl (a →' b)) (/✶-↑✶ ρs₁ ρs₂ hyp k a) (/✶-↑✶ ρs₁ ρs₂ hyp k b) ⟩
          simpl ((a /✶₂ ρs₂ ↑✶₂ k) →' (b /✶₂ ρs₂ ↑✶₂ k))
          ≡⟨ sym (MetaTypeApp.→'-/✶-↑✶ _ k ρs₂) ⟩
          (simpl (a →' b)) /✶₂ ρs₂ ↑✶₂ k
          ∎
      /✶-↑✶ ρs₁ ρs₂ hyp k (a ⇒ b) = begin
          (a ⇒ b) /✶₁ ρs₁ ↑✶₁ k
          ≡⟨ MetaTypeApp.⇒-/✶-↑✶ _ k ρs₁ ⟩ --
          (a /✶₁ ρs₁ ↑✶₁ k) ⇒ (b /✶₁ ρs₁ ↑✶₁ k)
          ≡⟨ cong₂ _⇒_ (/✶-↑✶ ρs₁ ρs₂ hyp k a) (/✶-↑✶ ρs₁ ρs₂ hyp k b) ⟩
          (a /✶₂ ρs₂ ↑✶₂ k) ⇒ (b /✶₂ ρs₂ ↑✶₂ k)
          ≡⟨ sym (MetaTypeApp.⇒-/✶-↑✶ _ k ρs₂) ⟩
          (a ⇒ b) /✶₂ ρs₂ ↑✶₂ k
          ∎
      /✶-↑✶ ρs₁ ρs₂ hyp k (∀' a) = begin
          (∀' a) /✶₁ ρs₁ ↑✶₁ k
          ≡⟨ MetaTypeApp.∀'-/✶-↑✶ _ k ρs₁ ⟩
          ∀' (a /✶₁ ρs₁ ↑✶₁ k)
          ≡⟨ cong ∀' (/✶-↑✶ ρs₁ ρs₂ hyp k a) ⟩
          ∀' (a /✶₂ ρs₂ ↑✶₂ k)
          ≡⟨ sym (MetaTypeApp.∀'-/✶-↑✶ _ k ρs₂) ⟩
          (∀' a) /✶₂ ρs₂ ↑✶₂ k
          ∎

open TermLemmas typeLemmas public hiding (var; id; _/_; _↑⋆_; wk; weaken; sub)
open AdditionalLemmas typeLemmas public

-- The above lemma /✶-↑✶ specialized to single substitutions
/-↑⋆ : ∀ {T₁ T₂} {lift₁ : Lift T₁ MetaType} {lift₂ : Lift T₂ MetaType} →
    let open Lifted lift₁ using () renaming (_↑⋆_ to _↑⋆₁_; _/_ to _/₁_)
        open Lifted lift₂ using () renaming (_↑⋆_ to _↑⋆₂_; _/_ to _/₂_)
    in
    ∀ {n k} (ρ₁ : Sub T₁ n k) (ρ₂ : Sub T₂ n k) →
    (∀ i x → (simpl (mvar x)) /₁ ρ₁ ↑⋆₁ i ≡ (simpl (mvar x)) /₂ ρ₂ ↑⋆₂ i) →
        ∀ i a → a /₁ ρ₁ ↑⋆₁ i ≡ a /₂ ρ₂ ↑⋆₂ i
/-↑⋆ ρ₁ ρ₂ hyp i a = /✶-↑✶ (ρ₁ ◅ ε) (ρ₂ ◅ ε) hyp i a

-- weakening a simple type gives a simple type
simpl-wk : ∀ {ν} k (τ : MetaSType (k N+ ν)) → ∃ λ τ' → (simpl τ) / wk ↑⋆ k ≡ simpl τ'
simpl-wk k (tc x) = , refl
simpl-wk k (mvar n) = , var-/-wk-↑⋆ k n
simpl-wk k (tvar n) = , refl
simpl-wk k (x →' x₁) = , refl

tclosed-wk : ∀ {ν m} {a : MetaType m} → TClosed ν a → TClosed (suc ν) a
tclosed-wk (a ⇒ b) = tclosed-wk a ⇒ tclosed-wk b
tclosed-wk (∀' x) = ∀' (tclosed-wk x)
tclosed-wk (simpl x) = simpl $ tclosed-wks x
  where
    tclosed-wks : ∀ {ν m} {τ : MetaSType m} → TClosedS ν τ → TClosedS (suc ν) τ
    tclosed-wks (tvar p) = tvar (≤-steps 1 p)
    tclosed-wks mvar = mvar
    tclosed-wks (a →' b) = (tclosed-wk a) →' (tclosed-wk b)
    tclosed-wks tc = tc

-- proper substitution doesn't affect the number of tvars
tclosed-/ : ∀ {m m' n} (a : MetaType m) {σ : Sub MetaType m m'} → TClosed n a →
            (∀ i → TClosed n (lookup i σ)) → TClosed n (a / σ)
tclosed-/ (a ⇒ b) (ca ⇒ cb) f = tclosed-/ a ca f ⇒ tclosed-/ b cb f
tclosed-/ (∀' a) (∀' x) f = ∀' (tclosed-/ a x (λ p → tclosed-wk (f p)))
tclosed-/ (simpl x) (simpl y) f = tclosed-/s x y f
  where
    tclosed-/s : ∀ {m m' n} (a : MetaSType m) {σ : Sub MetaType m m'} → TClosedS n a →
                 (∀ i → TClosed n (lookup i σ)) → TClosed n (a simple/ σ)
    tclosed-/s (tvar _) (tvar p) f = simpl (tvar p)
    tclosed-/s (mvar i) _ f = f i
    tclosed-/s (a →' b) (ca →' cb) f = simpl (tclosed-/ a ca f →' tclosed-/ b cb f)
    tclosed-/s (tc c) _ _ = simpl tc

-- opening any free tvar will reduce the number of free tvars
tclosed-open : ∀ {m ν} {a : MetaType m} k → k N< (suc ν) → TClosed (suc ν) a →
               TClosed ν (open-meta k a)
tclosed-open k k<ν (a ⇒ b) = (tclosed-open k k<ν a) ⇒ (tclosed-open k k<ν b)
tclosed-open k k<ν (∀' a) = ∀' (tclosed-open (suc k) (s≤s k<ν) a)
tclosed-open k k<ν (simpl (tvar {x = x} p)) with Nat.compare x k
tclosed-open .(suc (k' N+ k)) (s≤s k<ν) (simpl (tvar p)) | less k' k =
  simpl (tvar (≤-trans (m≤m+n (suc k') k) k<ν))
tclosed-open x k<ν (simpl (tvar x₁)) | equal .x = simpl mvar
tclosed-open m k<ν (simpl (tvar (s≤s p))) | greater .m k = simpl (tvar p)
tclosed-open k k<ν (simpl mvar) = simpl mvar
tclosed-open k k<ν (simpl (x →' x₁)) = simpl (tclosed-open k k<ν x →' tclosed-open k k<ν x₁)
tclosed-open k k<ν (simpl tc) = simpl tc

open-meta-◁m₁ : ∀ {m k} (a : MetaType m) → (open-meta k a) ◁m₁ ≡ a ◁m₁
open-meta-◁m₁ (a ⇒ b) = open-meta-◁m₁ b
open-meta-◁m₁ (∀' a) = cong suc (open-meta-◁m₁ a)
open-meta-◁m₁ (simpl x) = refl

lem : ∀ {m} n k (a : MetaType m) → open-meta n (open-meta (n N+ suc k) a) H.≅
                                    open-meta (n N+ k) (open-meta n a)
lem n k (a ⇒ b) = H.cong₂ _⇒_ (lem n k a) (lem n k b)
lem n k (∀' a) = H.cong ∀' (lem _ _ a)
lem n k (simpl (tvar x)) with Nat.compare x (n N+ (suc k))
lem n k (simpl (tvar x)) | z = {!z!}
lem n k (simpl (mvar x)) = H.refl
lem n k (simpl (a →' b)) = H.cong₂ (λ u v → simpl (u →' v)) (lem n k a) (lem n k b)
lem n k (simpl (tc x)) = H.refl

open-meta-◁m : ∀ {m} k (a : MetaType m) →
               ((open-meta k a) ◁m) H.≅ open-meta k (a ◁m)
open-meta-◁m k (a ⇒ b) = open-meta-◁m k b
open-meta-◁m k (∀' a) = HR.begin
  open-meta zero ((open-meta (suc k)) a ◁m)
    HR.≅⟨  {!!} ⟩
  open-meta zero (open-meta (suc k) (a ◁m))
    HR.≅⟨ lem zero k (a ◁m) ⟩
  open-meta k (open-meta zero (a ◁m)) HR.∎
open-meta-◁m k (simpl x) = H.refl

open import Implicits.Syntax.Type
open import Implicits.Substitutions.Type as TS using ()

lem₁ : ∀ {ν} k (a : Type (k N+ suc ν)) (b : Type ν) →
                (to-meta (a TS./ (TS.sub b) TS.↑⋆ k)) ≡
                  (open-meta k (to-meta a) / sub (to-meta b))
lem₁ k (simpl (tc x)) b = refl
lem₁ k (simpl (tvar n)) b with Nat.compare (toℕ n) k
lem₁ k (simpl (tvar n)) b | z = {!!}
lem₁ k (simpl (a →' b)) c = cong₂ (λ u v → simpl (u →' v)) (lem₁ k a c) (lem₁ k b c)
lem₁ k (a ⇒ b) c = cong₂ _⇒_ (lem₁ k a c) (lem₁ k b c)
lem₁ k (∀' a) b = cong ∀' (lem₁ (suc k) a b)


module Semantics where

open import Syntax public

-- Kripke models.

record Model : Set₁ where
  infix 3 _⊩ᵅ_
  field
    World     : Set
    _≤_       : World → World → Set
    refl≤     : ∀ {w} → w ≤ w
    trans≤    : ∀ {w w′ w″} → w ≤ w′ → w′ ≤ w″ → w ≤ w″
    idtrans≤  : ∀ {w w′} → (p : w ≤ w′) → trans≤ refl≤ p ≡ p
    _⊩ᵅ_     : World → Atom → Set
    mono⊩ᵅ   : ∀ {P w w′} → w ≤ w′ → w ⊩ᵅ P → w′ ⊩ᵅ P
    idmono⊩ᵅ : ∀ {P w} → (s : w ⊩ᵅ P) → mono⊩ᵅ {P} refl≤ s ≡ s

open Model {{…}} public

-- Forcing in a particular world.

module _ {{_ : Model}} where
  infix 3 _⊩_
  _⊩_ : World → Type → Set
  w ⊩ α P    = w ⊩ᵅ P
  w ⊩ A ⇒ B = ∀ {w′} → w ≤ w′ → w′ ⊩ A → w′ ⊩ B
  w ⊩ A ⩕ B  = w ⊩ A ∧ w ⊩ B
  w ⊩ ⫪     = ⊤

  infix 3 _⊩⋆_
  _⊩⋆_ : World → Stack Type → Set
  w ⊩⋆ ∅     = ⊤
  w ⊩⋆ Ξ , A = w ⊩⋆ Ξ ∧ w ⊩ A

-- Function extensionality.

postulate
  funext  : ∀ {X : Set} {Y : X → Set} {f g : ∀ x → Y x} →
            (∀ x → f x ≡ g x) →
            (λ x → f x) ≡ (λ x → g x)

  ⟨funext⟩ : ∀ {X : Set} {Y : X → Set} {f g : ∀ {x} → Y x} →
             (∀ {x} → f {x} ≡ g {x}) →
             (λ {x} → f {x}) ≡ (λ {x} → g {x})

extfun : ∀ {X : Set} {Y : X → Set} {f g : ∀ x → Y x} →
         (λ x → f x) ≡ (λ x → g x) →
         (∀ x → f x ≡ g x)
extfun refl = λ x → refl

⟨extfun⟩ : ∀ {X : Set} {Y : X → Set} {f g : ∀ {x} → Y x} →
           (λ {x} → f {x}) ≡ (λ {x} → g {x}) →
           (∀ {x} → f {x} ≡ g {x})
⟨extfun⟩ refl = refl

-- Monotonicity of forcing with respect to constructive accessibility.

module _ {{_ : Model}} where
  mono⊩ : ∀ {A w w′} → w ≤ w′ → w ⊩ A → w′ ⊩ A
  mono⊩ {α P}    p s = mono⊩ᵅ p s
  mono⊩ {A ⇒ B} p s = λ p′ → s (trans≤ p p′)
  mono⊩ {A ⩕ B}  p s = mono⊩ {A} p (π₁ s) , mono⊩ {B} p (π₂ s)
  mono⊩ {⫪}     p s = ∙

  mono⊩⋆ : ∀ {Ξ w w′} → w ≤ w′ → w ⊩⋆ Ξ → w′ ⊩⋆ Ξ
  mono⊩⋆ {∅}     p ∙       = ∙
  mono⊩⋆ {Ξ , A} p (σ , s) = mono⊩⋆ {Ξ} p σ , mono⊩ {A} p s

-- TODO: Naming things.

module _ {{_ : Model}} where
  idmono⊩ : ∀ {A w} → (s : w ⊩ A) → mono⊩ {A} refl≤ s ≡ s
  idmono⊩ {α P}    s = idmono⊩ᵅ s
  idmono⊩ {A ⇒ B} s = ⟨funext⟩ λ {w′} → funext λ p → cong s (idtrans≤ p)
  idmono⊩ {A ⩕ B}  s = cong² _,_ (idmono⊩ {A} (π₁ s)) (idmono⊩ {B} (π₂ s))
  idmono⊩ {⫪}     s = refl

  idmono⊩⋆ : ∀ {Ξ w} → (γ : w ⊩⋆ Ξ) → mono⊩⋆ refl≤ γ ≡ γ
  idmono⊩⋆ {∅}     ∙       = refl
  idmono⊩⋆ {Ξ , A} (σ , s) = cong² _,_ (idmono⊩⋆ σ) (idmono⊩ {A} s)

-- TODO: Naming things.

module _ {{_ : Model}} where
  _⟪_⊫_⟫ : World → Context → Type → Set
  w ⟪ Γ ⊫ A ⟫ = w ⊩⋆ Γ → w ⊩ A

-- Evaluation equipment.

module _ {{_ : Model}} where
  ⟪var⟫ : ∀ {A Γ w} → A ∈ Γ → w ⟪ Γ ⊫ A ⟫
  ⟪var⟫ top     = λ { (γ , s) → s }
  ⟪var⟫ (pop i) = λ { (γ , s) → ⟪var⟫ i γ }

  ⟪lam⟫ : ∀ {A B Γ w} → (∀ {w′} → w′ ⟪ Γ , A ⊫ B ⟫) → w ⟪ Γ ⊫ A ⇒ B ⟫
  ⟪lam⟫ ⟪d⟫ = λ γ p s → ⟪d⟫ (mono⊩⋆ p γ , s)

  ⟪app⟫ : ∀ {A B Γ w} → w ⟪ Γ ⊫ A ⇒ B ⟫ → w ⟪ Γ ⊫ A ⟫ → w ⟪ Γ ⊫ B ⟫
  ⟪app⟫ ⟪d⟫ ⟪e⟫ = λ γ → ⟪d⟫ γ refl≤ (⟪e⟫ γ)

  ⟪pair⟫ : ∀ {A B Γ w} → w ⟪ Γ ⊫ A ⟫ → w ⟪ Γ ⊫ B ⟫ → w ⟪ Γ ⊫ A ⩕ B ⟫
  ⟪pair⟫ ⟪d⟫ ⟪e⟫ = λ γ → ⟪d⟫ γ , ⟪e⟫ γ

  ⟪fst⟫ : ∀ {A B Γ w} → w ⟪ Γ ⊫ A ⩕ B ⟫ → w ⟪ Γ ⊫ A ⟫
  ⟪fst⟫ ⟪d⟫ = λ γ → π₁ (⟪d⟫ γ)

  ⟪snd⟫ : ∀ {A B Γ w} → w ⟪ Γ ⊫ A ⩕ B ⟫ → w ⟪ Γ ⊫ B ⟫
  ⟪snd⟫ ⟪d⟫ = λ γ → π₂ (⟪d⟫ γ)

  ⟪unit⟫ : ∀ {Γ w} → w ⟪ Γ ⊫ ⫪ ⟫
  ⟪unit⟫ = λ γ → ∙

-- Shorthand for variables.

module _ {{_ : Model}} where
  ⟪v₀⟫ : ∀ {A Γ w} → w ⟪ Γ , A ⊫ A ⟫
  ⟪v₀⟫ {A} = ⟪var⟫ {A} i₀

-- Forcing in all worlds, or semantic entailment.

module _ {{_ : Model}} where
  infix 3 _⊨_
  _⊨_ : Context → Type → Set
  Γ ⊨ A = ∀ {w} → w ⟪ Γ ⊫ A ⟫

-- Evaluation, or soundness of the semantics with respect to the syntax.

module _ {{_ : Model}} where
  eval : ∀ {A Γ} → Γ ⊢ A → Γ ⊨ A
  eval (var i)            = ⟪var⟫ i
  eval (lam {A} {B} d)    = ⟪lam⟫ {A} {B} (eval d)
  eval (app {A} {B} d e)  = ⟪app⟫ {A} {B} (eval d) (eval e)
  eval (pair {A} {B} d e) = ⟪pair⟫ {A} {B} (eval d) (eval e)
  eval (fst {A} {B} d)    = ⟪fst⟫ {A} {B} (eval d)
  eval (snd {A} {B} d)    = ⟪snd⟫ {A} {B} (eval d)
  eval unit               = ⟪unit⟫

-- The canonical model.

private
  instance
    canon : Model
    canon = record
      { World     = Context
      ; _≤_       = _⊆_
      ; refl≤     = refl⊆
      ; trans≤    = trans⊆
      ; idtrans≤  = idtrans⊆
      ; _⊩ᵅ_     = λ Γ P → Γ ⊢ⁿᵉ α P
      ; mono⊩ᵅ   = mono⊢ⁿᵉ
      ; idmono⊩ᵅ = idmono⊢ⁿᵉ
      }

-- Soundness and completeness of the canonical model with respect to the syntax.

mutual
  evalᶜ : ∀ {A Γ} → Γ ⊢ⁿᵉ A → Γ ⊩ A
  evalᶜ {α P}    d = d
  evalᶜ {A ⇒ B} d = λ p e → evalᶜ (appⁿᵉ (mono⊢ⁿᵉ p d) (quotᶜ e))
  evalᶜ {A ⩕ B}  d = evalᶜ (fstⁿᵉ d) , evalᶜ (sndⁿᵉ d)
  evalᶜ {⫪}     d = ∙

  quotᶜ : ∀ {A Γ} → Γ ⊩ A → Γ ⊢ⁿᶠ A
  quotᶜ {α P}    s = neⁿᶠ s
  quotᶜ {A ⇒ B} s = lamⁿᶠ (quotᶜ (s weak⊆ (evalᶜ {A} v₀ⁿᵉ)))
  quotᶜ {A ⩕ B}  s = pairⁿᶠ (quotᶜ (π₁ s)) (quotᶜ (π₂ s))
  quotᶜ {⫪}     s = unitⁿᶠ

-- Reflexivity of simultaneous forcing.

refl⊩⋆ : ∀ {Γ} → Γ ⊩⋆ Γ
refl⊩⋆ {∅}     = ∙
refl⊩⋆ {Γ , A} = mono⊩⋆ weak⊆ refl⊩⋆ , evalᶜ {A} v₀ⁿᵉ

-- Quotation, or completeness of the semantics with respect to the syntax.

quot : ∀ {A Γ} → Γ ⊨ A → Γ ⊢ⁿᶠ A
quot s = quotᶜ (s refl⊩⋆)

-- Normalisation by evaluation.

nbe : ∀ {A Γ} → Γ ⊢ A → Γ ⊢ⁿᶠ A
nbe = quot ∘ eval


{-# OPTIONS --safe #-}

module Definition.Conversion.Reduction where

open import Definition.Untyped
open import Definition.Typed
open import Definition.Typed.Properties
open import Definition.Conversion

-- Weak head expansion of algorithmic equality of types.
reductionConv↑ : ∀ {A A′ B B′ r Γ}
               → Γ ⊢ A ⇒* A′ ^ r
               → Γ ⊢ B ⇒* B′ ^ r
               → Whnf A′
               → Whnf B′
               → Γ ⊢ A′ [conv↑] B′ ^ r
               → Γ ⊢ A  [conv↑] B ^ r
reductionConv↑ x x₁ x₂ x₃ ([↑] A″ B″ D D′ whnfA′ whnfB′ A′<>B′)
              rewrite whnfRed* D x₂ | whnfRed* D′ x₃ =
  [↑] A″ B″ x x₁ whnfA′ whnfB′ A′<>B′

-- Weak head expansion of algorithmic equality of terms.
reductionConv↑Term : ∀ {t t′ u u′ A B Γ l}
                   → Γ ⊢ A ⇒* B ^ [ ! , l ]
                   → Γ ⊢ t ⇒* t′ ∷ B ^ l
                   → Γ ⊢ u ⇒* u′ ∷ B ^ l
                   → Whnf B
                   → Whnf t′
                   → Whnf u′
                   → Γ ⊢ t′ [conv↑] u′ ∷ B ^ l
                   → Γ ⊢ t  [conv↑] u  ∷ A ^ l
reductionConv↑Term x x₁ x₂ x₃ x₄ x₅
                   ([↑]ₜ B₁ t″ u″ D d d′ whnfB whnft′ whnfu′ t<>u)
                   rewrite whnfRed* D x₃ | whnfRed*Term d x₄ | whnfRed*Term d′ x₅ =
  [↑]ₜ B₁ t″ u″ x x₁ x₂ whnfB whnft′ whnfu′ t<>u


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.GradedRing.Instances.TrivialGradedRing where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv

open import Cubical.Data.Unit
open import Cubical.Data.Nat using (ℕ ; zero ; suc ; +-assoc ; +-zero)
open import Cubical.Data.Sigma

open import Cubical.Algebra.Monoid
open import Cubical.Algebra.Monoid.Instances.Nat
open import Cubical.Algebra.AbGroup
open import Cubical.Algebra.AbGroup.Instances.Unit
open import Cubical.Algebra.DirectSum.DirectSumHIT.Base
open import Cubical.Algebra.Ring
open import Cubical.Algebra.GradedRing.Base
open import Cubical.Algebra.GradedRing.DirectSumHIT

private variable
  ℓ : Level

open Iso
open GradedRing-⊕HIT-index
open GradedRing-⊕HIT-⋆

module _
  (ARing@(A , Astr) : Ring ℓ)
  where

  open RingStr Astr
  open RingTheory ARing

  private

    G : ℕ → Type ℓ
    G zero = A
    G (suc k) = Unit*

    Gstr : (k : ℕ) → AbGroupStr (G k)
    Gstr zero = snd (Ring→AbGroup ARing)
    Gstr (suc k) = snd UnitAbGroup

    ⋆ : {k l : ℕ} → G k → G l → G (k Cubical.Data.Nat.+ l)
    ⋆ {zero} {zero} x y = x · y
    ⋆ {zero} {suc l} x y = tt*
    ⋆ {suc k} {l} x y = tt*

    0-⋆ : {k l : ℕ} (b : G l) → ⋆ (AbGroupStr.0g (Gstr k)) b ≡ AbGroupStr.0g (Gstr (k Cubical.Data.Nat.+ l))
    0-⋆ {zero} {zero} b = 0LeftAnnihilates _
    0-⋆ {zero} {suc l} b = refl
    0-⋆ {suc k} {l} b = refl

    ⋆-0 : {k l : ℕ} (a : G k) → ⋆ a (AbGroupStr.0g (Gstr l)) ≡ AbGroupStr.0g (Gstr (k Cubical.Data.Nat.+ l))
    ⋆-0 {zero} {zero} a = 0RightAnnihilates _
    ⋆-0 {zero} {suc l} a = refl
    ⋆-0 {suc k} {l} a = refl

    ⋆Assoc : {k l m : ℕ} (a : G k) (b : G l) (c : G m) →
             _≡_ {A = Σ[ k ∈ ℕ ] G k}
             (k Cubical.Data.Nat.+ (l Cubical.Data.Nat.+ m) , ⋆ a (⋆ b c))
             (k Cubical.Data.Nat.+ l Cubical.Data.Nat.+ m , ⋆ (⋆ a b) c)
    ⋆Assoc {zero} {zero} {zero} a b c =  ΣPathTransport→PathΣ _ _ (+-assoc _ _ _ , transportRefl _ ∙ ·Assoc _ _ _)
    ⋆Assoc {zero} {zero} {suc m} a b c = ΣPathTransport→PathΣ _ _ (+-assoc _ _ _ , transportRefl _)
    ⋆Assoc {zero} {suc l} {m} a b c =  ΣPathTransport→PathΣ _ _ (+-assoc _ _ _ , transportRefl _)
    ⋆Assoc {suc k} {l} {m} a b c = ΣPathTransport→PathΣ _ _ (+-assoc _ _ _ , transportRefl _)

    ⋆IdR : {k : ℕ} (a : G k) → _≡_ {A = Σ[ k ∈ ℕ ] G k} (k Cubical.Data.Nat.+ 0 , ⋆ a 1r) (k , a)
    ⋆IdR {zero} a = ΣPathTransport→PathΣ _ _ (refl , (transportRefl _ ∙ ·IdR _))
    ⋆IdR {suc k} a = ΣPathTransport→PathΣ _ _ ((+-zero _) , (transportRefl _))

    ⋆IdL : {l : ℕ} (b : G l) → _≡_ {A = Σ[ k ∈ ℕ ] G k} (l , ⋆ 1r b) (l , b)
    ⋆IdL {zero} b = ΣPathTransport→PathΣ _ _ (refl , (transportRefl _ ∙ ·IdL _))
    ⋆IdL {suc l} b = ΣPathTransport→PathΣ _ _ (refl , (transportRefl _))

    ⋆DistR+ : {k l : ℕ} (a : G k) (b c : G l) →
              ⋆ a (Gstr l .AbGroupStr._+_ b c) ≡ Gstr (k Cubical.Data.Nat.+ l) .AbGroupStr._+_ (⋆ a b) (⋆ a c)
    ⋆DistR+ {zero} {zero} a b c = ·DistR+ _ _ _
    ⋆DistR+ {zero} {suc l} a b c = refl
    ⋆DistR+ {suc k} {l} a b c = refl

    ⋆DistL+ : {k l : ℕ} (a b : G k) (c : G l) →
              ⋆ (Gstr k .AbGroupStr._+_ a b) c ≡ Gstr (k Cubical.Data.Nat.+ l) .AbGroupStr._+_ (⋆ a c) (⋆ b c)
    ⋆DistL+ {zero} {zero} a b c = ·DistL+ _ _ _
    ⋆DistL+ {zero} {suc l} a b c = refl
    ⋆DistL+ {suc k} {l} a b c = refl

  trivialGradedRing : GradedRing ℓ-zero ℓ
  trivialGradedRing = makeGradedRing ARing NatMonoid
                      G Gstr 1r ⋆  0-⋆ ⋆-0
                      ⋆Assoc ⋆IdR ⋆IdL ⋆DistR+ ⋆DistL+
                      equivRing
    where
    equivRing : RingEquiv ARing (⊕HITgradedRing-Ring
                                 NatMonoid G Gstr 1r ⋆ 0-⋆ ⋆-0 ⋆Assoc ⋆IdR ⋆IdL ⋆DistR+ ⋆DistL+)
    fst equivRing = isoToEquiv is
      where
      is : Iso A (⊕HIT ℕ G Gstr)
      fun is a = base 0 a
      inv is = DS-Rec-Set.f _ _ _ _ is-set
               0r (λ {zero a → a ; (suc k) a → 0r }) _+_ +Assoc +IdR +Comm
               (λ { zero → refl ; (suc k) → refl}) (λ {zero a b → refl ; (suc n) a b → +IdR _})
      rightInv is = DS-Ind-Prop.f _ _ _ _ (λ _ → trunc _ _)
                    (base-neutral _)
                    (λ { zero a → refl ; (suc k) a → base-neutral _ ∙ sym (base-neutral _)} )
                    λ {U V} ind-U ind-V → sym (base-add _ _ _) ∙ cong₂ _add_ ind-U ind-V
      leftInv is = λ _ → refl
    snd equivRing = makeIsRingHom
                    -- issue agda have trouble infering the Idx, G, Gstr
                    {R = ARing}
                    {S = ⊕HITgradedRing-Ring NatMonoid G Gstr 1r ⋆ 0-⋆ ⋆-0 ⋆Assoc ⋆IdR ⋆IdL ⋆DistR+ ⋆DistL+}
                    {f = λ a → base 0 a}
                    refl (λ _ _ → sym (base-add _ _ _)) λ _ _ → refl


------------------------------------------------------------------------
-- Monotone functions
------------------------------------------------------------------------

{-# OPTIONS --erased-cubical --safe #-}

module Partiality-algebra.Monotone where

open import Equality.Propositional.Cubical
open import Prelude hiding (⊥)

open import Bijection equality-with-J using (_↔_)
import Equivalence equality-with-J as Eq
open import Function-universe equality-with-J hiding (id; _∘_)
open import H-level.Closure equality-with-J

open import Partiality-algebra as PA hiding (id; _∘_)
import Partiality-algebra.Properties as PAP

-- Definition of monotone functions.

record [_⟶_]⊑
         {a₁ p₁ q₁} {A₁ : Type a₁} (P₁ : Partiality-algebra p₁ q₁ A₁)
         {a₂ p₂ q₂} {A₂ : Type a₂} (P₂ : Partiality-algebra p₂ q₂ A₂) :
         Type (p₁ ⊔ p₂ ⊔ q₁ ⊔ q₂) where
  private
    module P₁ = Partiality-algebra P₁
    module P₂ = Partiality-algebra P₂

  field
    function : P₁.T → P₂.T
    monotone : ∀ {x y} → x P₁.⊑ y → function x P₂.⊑ function y

open [_⟶_]⊑

-- Identity.

id⊑ : ∀ {a p q} {A : Type a} {P : Partiality-algebra p q A} →
      [ P ⟶ P ]⊑
function id⊑ = id
monotone id⊑ = id

-- Composition.

infixr 40 _∘⊑_

_∘⊑_ :
  ∀ {a₁ p₁ q₁} {A₁ : Type a₁} {P₁ : Partiality-algebra p₁ q₁ A₁}
    {a₂ p₂ q₂} {A₂ : Type a₂} {P₂ : Partiality-algebra p₂ q₂ A₂}
    {a₃ p₃ q₃} {A₃ : Type a₃} {P₃ : Partiality-algebra p₃ q₃ A₃} →
  [ P₂ ⟶ P₃ ]⊑ → [ P₁ ⟶ P₂ ]⊑ → [ P₁ ⟶ P₃ ]⊑
function (f ∘⊑ g) = function f ∘ function g
monotone (f ∘⊑ g) = monotone f ∘ monotone g

-- Equality characterisation lemma for monotone functions.

equality-characterisation-monotone :
  ∀ {a₁ p₁ q₁} {A₁ : Type a₁} {P₁ : Partiality-algebra p₁ q₁ A₁}
    {a₂ p₂ q₂} {A₂ : Type a₂} {P₂ : Partiality-algebra p₂ q₂ A₂}
    {f g : [ P₁ ⟶ P₂ ]⊑} →
  (∀ x → function f x ≡ function g x) ↔ f ≡ g
equality-characterisation-monotone {P₁ = P₁} {P₂ = P₂} {f} {g} =
  (∀ x → function f x ≡ function g x)                                   ↔⟨ Eq.extensionality-isomorphism ext ⟩

  function f ≡ function g                                               ↝⟨ ignore-propositional-component
                                                                             (implicit-Π-closure ext 1 λ _ →
                                                                              implicit-Π-closure ext 1 λ _ →
                                                                              Π-closure          ext 1 λ _ →
                                                                              P₂.⊑-propositional) ⟩
  _↔_.to rearrange f ≡ _↔_.to rearrange g                               ↔⟨ Eq.≃-≡ (Eq.↔⇒≃ rearrange) ⟩□

  f ≡ g                                                                 □
  where
  module P₁ = Partiality-algebra P₁
  module P₂ = Partiality-algebra P₂

  rearrange :
    [ P₁ ⟶ P₂ ]⊑
      ↔
    ∃ λ (h : P₁.T → P₂.T) →
      ∀ {x y} → x P₁.⊑ y → h x P₂.⊑ h y
  rearrange = record
    { surjection = record
      { logical-equivalence = record
        { to   = λ f → function f , monotone f
        ; from = uncurry λ f m → record { function = f; monotone = m }
        }
      ; right-inverse-of = λ _ → refl
      }
    ; left-inverse-of = λ _ → refl
    }
    where
    open Partiality-algebra

-- Composition is associative.

∘⊑-assoc :
  ∀ {a₁ p₁ q₁} {A₁ : Type a₁} {P₁ : Partiality-algebra p₁ q₁ A₁}
    {a₂ p₂ q₂} {A₂ : Type a₂} {P₂ : Partiality-algebra p₂ q₂ A₂}
    {a₃ p₃ q₃} {A₃ : Type a₃} {P₃ : Partiality-algebra p₃ q₃ A₃}
    {a₄ p₄ q₄} {A₄ : Type a₄} {P₄ : Partiality-algebra p₄ q₄ A₄}
  (f : [ P₃ ⟶ P₄ ]⊑) (g : [ P₂ ⟶ P₃ ]⊑) {h : [ P₁ ⟶ P₂ ]⊑} →
  f ∘⊑ (g ∘⊑ h) ≡ (f ∘⊑ g) ∘⊑ h
∘⊑-assoc _ _ =
  _↔_.to equality-characterisation-monotone λ _ → refl

module _
  {a₁ p₁ q₁} {A₁ : Type a₁} {P₁ : Partiality-algebra p₁ q₁ A₁}
  {a₂ p₂ q₂} {A₂ : Type a₂} {P₂ : Partiality-algebra p₂ q₂ A₂} where

  private
    module P₁   = Partiality-algebra P₁
    module P₂   = Partiality-algebra P₂
    module PAP₁ = PAP P₁
    module PAP₂ = PAP P₂

  -- If a monotone function is applied to an increasing sequence,
  -- then the result is another increasing sequence.

  [_$_]-inc :
    [ P₁ ⟶ P₂ ]⊑ → P₁.Increasing-sequence → P₂.Increasing-sequence
  [ f $ s ]-inc = (λ n → function f (s P₁.[ n ]))
                , (λ n → monotone f (P₁.increasing s n))

  -- A lemma relating monotone functions and least upper bounds.

  ⨆$⊑$⨆ : (f : [ P₁ ⟶ P₂ ]⊑) →
          ∀ s → P₂.⨆ [ f $ s ]-inc P₂.⊑ function f (P₁.⨆ s)
  ⨆$⊑$⨆ f s = P₂.least-upper-bound _ _ λ n →

    [ f $ s ]-inc P₂.[ n ]  PAP₂.⊑⟨ monotone f (

        s P₁.[ n ]                    PAP₁.⊑⟨ P₁.upper-bound _ _ ⟩■
        P₁.⨆ s                        ■) ⟩■

    function f (P₁.⨆ s)     ■


-- Raw terms, weakening (renaming) and substitution.

{-# OPTIONS --without-K --safe #-}

module Definition.Untyped where

open import Tools.Fin
open import Tools.Nat
open import Tools.Product
open import Tools.List
import Tools.PropositionalEquality as PE

infixl 30 _∙_
infix 30 Π_▹_
infixr 22 _▹▹_
infix 30 Σ_▹_
infixr 22 _××_
infix 30 ⟦_⟧_▹_
infixl 30 _ₛ•ₛ_ _•ₛ_ _ₛ•_
infix 25 _[_]
infix 25 _[_]↑

-- Typing contexts (length indexed snoc-lists, isomorphic to lists).
-- Terms added to the context are well scoped in the sense that it cannot
-- contain more unbound variables than can be looked up in the context.

data Con (A : Nat → Set) : Nat → Set where
  ε   :                             Con A 0        -- Empty context.
  _∙_ : {n : Nat} → Con A n → A n → Con A (1+ n)   -- Context extension.

private
  variable
    n m ℓ : Nat

-- Representation of sub terms using a list of binding levels

data GenTs (A : Nat → Set) : Nat → List Nat → Set where
  []  : {n : Nat} → GenTs A n []
  _∷_ : {n b : Nat} {bs : List Nat} (t : A (b + n)) (ts : GenTs A n bs) → GenTs A n (b ∷ bs)

-- Kinds are indexed on the number of expected sub terms
-- and the number of new variables bound by each sub term

data Kind : (ns : List Nat) → Set where
  Ukind : Kind []

  Pikind  : Kind (0 ∷ 1 ∷ [])
  Lamkind : Kind (1 ∷ [])
  Appkind : Kind (0 ∷ 0 ∷ [])

  Sigmakind : Kind (0 ∷ 1 ∷ [])
  Prodkind  : Kind (0 ∷ 0 ∷ [])
  Fstkind   : Kind (0 ∷ [])
  Sndkind   : Kind (0 ∷ [])

  Natkind    : Kind []
  Zerokind   : Kind []
  Suckind    : Kind (0 ∷ [])
  Natreckind : Kind (1 ∷ 0 ∷ 0 ∷ 0 ∷ [])

  Unitkind : Kind []
  Starkind : Kind []

  Emptykind    : Kind []
  Emptyreckind : Kind (0 ∷ 0 ∷ [])

-- Terms are indexed by its number of unbound variables and are either:
-- de Bruijn style variables or
-- generic terms, formed by their kind and sub terms

data Term (n : Nat) : Set where
  var : (x : Fin n) → Term n
  gen : {bs : List Nat} (k : Kind bs) (c : GenTs Term n bs) → Term n

private
  variable
    A F H t u v : Term n
    B E G       : Term (1+ n)

-- The Grammar of our language.

-- We represent the expressions of our language as de Bruijn terms.
-- Variables are natural numbers interpreted as de Bruijn indices.
-- Π, lam, and natrec are binders.

-- Type constructors.
U      : Term n                      -- Universe.
U = gen Ukind []

Π_▹_ : (A : Term n) (B : Term (1+ n)) → Term n  -- Dependent function type (B is a binder).
Π A ▹ B = gen Pikind (A ∷ B ∷ [])

Σ_▹_ : (A : Term n) (B : Term (1+ n)) → Term n  -- Dependent sum type (B is a binder).
Σ A ▹ B = gen Sigmakind (A ∷ B ∷ [])

ℕ      : Term n                      -- Type of natural numbers.
ℕ = gen Natkind []

Empty : Term n                       -- Empty type
Empty = gen Emptykind []

Unit  : Term n                       -- Unit type
Unit = gen Unitkind []

lam    : (t : Term (1+ n)) → Term n  -- Function abstraction (binder).
lam t = gen Lamkind (t ∷ [])

_∘_    : (t u : Term n) → Term n     -- Application.
t ∘ u = gen Appkind (t ∷ u ∷ [])

prod : (t u : Term n) → Term n       -- Dependent products
prod t u = gen Prodkind (t ∷ u ∷ [])

fst : (t : Term n) → Term n          -- First projection
fst t = gen Fstkind (t ∷ [])

snd : (t : Term n) → Term n          -- Second projection
snd t = gen Sndkind (t ∷ [])

-- Introduction and elimination of natural numbers.
zero   : Term n                      -- Natural number zero.
zero = gen Zerokind []

suc    : (t : Term n)       → Term n -- Successor.
suc t = gen Suckind (t ∷ [])

natrec : (A : Term (1+ n)) (t u v : Term n) → Term n  -- Natural number recursor (A is a binder).
natrec A t u v = gen Natreckind (A ∷ t ∷ u ∷ v ∷ [])

star : Term n                        -- Unit element
star = gen Starkind []

Emptyrec : (A e : Term n) → Term n   -- Empty type recursor
Emptyrec A e = gen Emptyreckind (A ∷ e ∷ [])

-- Binding types

data BindingType : Set where
  BΠ : BindingType
  BΣ : BindingType

⟦_⟧_▹_ : BindingType → Term n → Term (1+ n) → Term n
⟦ BΠ ⟧ F ▹ G = Π F ▹ G
⟦ BΣ ⟧ F ▹ G = Σ F ▹ G

-- Injectivity of term constructors w.r.t. propositional equality.

-- If  W F G = W H E  then  F = H  and  G = E.

B-PE-injectivity : ∀ W → ⟦ W ⟧ F ▹ G PE.≡ ⟦ W ⟧ H ▹ E → F PE.≡ H × G PE.≡ E
B-PE-injectivity BΠ PE.refl = PE.refl , PE.refl
B-PE-injectivity BΣ PE.refl = PE.refl , PE.refl

-- If  suc n = suc m  then  n = m.

suc-PE-injectivity : suc t PE.≡ suc u → t PE.≡ u
suc-PE-injectivity PE.refl = PE.refl

-- Neutral terms.

-- A term is neutral if it has a variable in head position.
-- The variable blocks reduction of such terms.

data Neutral : Term n → Set where
  var       : (x : Fin n) → Neutral (var x)
  ∘ₙ        : Neutral t   → Neutral (t ∘ u)
  fstₙ      : Neutral t   → Neutral (fst t)
  sndₙ      : Neutral t   → Neutral (snd t)
  natrecₙ   : Neutral v   → Neutral (natrec G t u v)
  Emptyrecₙ : Neutral t   → Neutral (Emptyrec A t)

-- Weak head normal forms (whnfs).

-- These are the (lazy) values of our language.

data Whnf {n : Nat} : Term n → Set where

  -- Type constructors are whnfs.
  Uₙ     : Whnf U
  Πₙ     : Whnf (Π A ▹ B)
  Σₙ     : Whnf (Σ A ▹ B)
  ℕₙ     : Whnf ℕ
  Unitₙ  : Whnf Unit
  Emptyₙ : Whnf Empty

  -- Introductions are whnfs.
  lamₙ  : Whnf (lam t)
  zeroₙ : Whnf zero
  sucₙ  : Whnf (suc t)
  starₙ : Whnf star
  prodₙ : Whnf (prod t u)

  -- Neutrals are whnfs.
  ne    : Neutral t → Whnf t

-- Whnf inequalities.

-- Different whnfs are trivially distinguished by propositional equality.
-- (The following statements are sometimes called "no-confusion theorems".)

U≢ne : Neutral A → U PE.≢ A
U≢ne () PE.refl

ℕ≢ne : Neutral A → ℕ PE.≢ A
ℕ≢ne () PE.refl

Empty≢ne : Neutral A → Empty PE.≢ A
Empty≢ne () PE.refl

Unit≢ne : Neutral A → Unit PE.≢ A
Unit≢ne () PE.refl

B≢ne : ∀ W → Neutral A → ⟦ W ⟧ F ▹ G PE.≢ A
B≢ne BΠ () PE.refl
B≢ne BΣ () PE.refl

U≢B : ∀ W → U PE.≢ ⟦ W ⟧ F ▹ G
U≢B BΠ ()
U≢B BΣ ()

ℕ≢B : ∀ W → ℕ PE.≢ ⟦ W ⟧ F ▹ G
ℕ≢B BΠ ()
ℕ≢B BΣ ()

Empty≢B : ∀ W → Empty PE.≢ ⟦ W ⟧ F ▹ G
Empty≢B BΠ ()
Empty≢B BΣ ()

Unit≢B : ∀ W → Unit PE.≢ ⟦ W ⟧ F ▹ G
Unit≢B BΠ ()
Unit≢B BΣ ()

zero≢ne : Neutral t → zero PE.≢ t
zero≢ne () PE.refl

suc≢ne : Neutral t → suc u PE.≢ t
suc≢ne () PE.refl

-- Several views on whnfs (note: not recursive).

-- A whnf of type ℕ is either zero, suc t, or neutral.

data Natural {n : Nat} : Term n → Set where
  zeroₙ :             Natural zero
  sucₙ  :             Natural (suc t)
  ne    : Neutral t → Natural t

-- A (small) type in whnf is either Π A B, Σ A B, ℕ, Empty, Unit or neutral.
-- Large types could also be U.

data Type {n : Nat} : Term n → Set where
  Πₙ     :             Type (Π A ▹ B)
  Σₙ     :             Type (Σ A ▹ B)
  ℕₙ     :             Type ℕ
  Emptyₙ :             Type Empty
  Unitₙ  :             Type Unit
  ne     : Neutral t → Type t

⟦_⟧-type : ∀ (W : BindingType) → Type (⟦ W ⟧ F ▹ G)
⟦ BΠ ⟧-type = Πₙ
⟦ BΣ ⟧-type = Σₙ

-- A whnf of type Π A ▹ B is either lam t or neutral.

data Function {n : Nat} : Term n → Set where
  lamₙ : Function (lam t)
  ne   : Neutral t → Function t

-- A whnf of type Σ A ▹ B is either prod t u or neutral.

data Product {n : Nat} : Term n → Set where
  prodₙ : Product (prod t u)
  ne    : Neutral t → Product t

-- These views classify only whnfs.
-- Natural, Type, Function and Product are a subsets of Whnf.

naturalWhnf : Natural t → Whnf t
naturalWhnf sucₙ   = sucₙ
naturalWhnf zeroₙ  = zeroₙ
naturalWhnf (ne x) = ne x

typeWhnf : Type A → Whnf A
typeWhnf Πₙ     = Πₙ
typeWhnf Σₙ     = Σₙ
typeWhnf ℕₙ     = ℕₙ
typeWhnf Emptyₙ = Emptyₙ
typeWhnf Unitₙ  = Unitₙ
typeWhnf (ne x) = ne x

functionWhnf : Function t → Whnf t
functionWhnf lamₙ   = lamₙ
functionWhnf (ne x) = ne x

productWhnf : Product t → Whnf t
productWhnf prodₙ  = prodₙ
productWhnf (ne x) = ne x

⟦_⟧ₙ : (W : BindingType) → Whnf (⟦ W ⟧ F ▹ G)
⟦_⟧ₙ BΠ = Πₙ
⟦_⟧ₙ BΣ = Σₙ

------------------------------------------------------------------------
-- Weakening

-- In the following we define untyped weakenings η : Wk.
-- The typed form could be written η : Γ ≤ Δ with the intention
-- that η transport a term t living in context Δ to a context Γ
-- that can bind additional variables (which cannot appear in t).
-- Thus, if Δ ⊢ t : A and η : Γ ≤ Δ then Γ ⊢ wk η t : wk η A.
--
-- Even though Γ is "larger" than Δ we write Γ ≤ Δ to be conformant
-- with subtyping A ≤ B.  With subtyping, relation Γ ≤ Δ could be defined as
-- ``for all x ∈ dom(Δ) have Γ(x) ≤ Δ(x)'' (in the sense of subtyping)
-- and this would be the natural extension of weakenings.

data Wk : Nat → Nat → Set where
  id    : {n : Nat} → Wk n n                      -- η : Γ ≤ Γ.
  step  : {n m : Nat} → Wk m n → Wk (1+ m) n      -- If η : Γ ≤ Δ then step η : Γ∙A ≤ Δ.
  lift  : {n m : Nat} → Wk m n → Wk (1+ m) (1+ n) -- If η : Γ ≤ Δ then lift η : Γ∙A ≤ Δ∙A.

-- Composition of weakening.
-- If η : Γ ≤ Δ and η′ : Δ ≤ Φ then η • η′ : Γ ≤ Φ.

infixl 30 _•_

_•_                :  {l m n : Nat} → Wk l m → Wk m n → Wk l n
id      • η′       =  η′
step η  • η′       =  step  (η • η′)
lift η  • id       =  lift  η
lift η  • step η′  =  step  (η • η′)
lift η  • lift η′  =  lift  (η • η′)

liftn : {k m : Nat} → Wk k m → (n : Nat) → Wk (n + k) (n + m)
liftn ρ Nat.zero = ρ
liftn ρ (1+ n)   = lift (liftn ρ n)

-- Weakening of variables.
-- If η : Γ ≤ Δ and x ∈ dom(Δ) then wkVar η x ∈ dom(Γ).

wkVar : {m n : Nat} (ρ : Wk m n) (x : Fin n) → Fin m
wkVar id x            = x
wkVar (step ρ) x      = (wkVar ρ x) +1
wkVar (lift ρ) x0     = x0
wkVar (lift ρ) (x +1) = (wkVar ρ x) +1

  -- Weakening of terms.
  -- If η : Γ ≤ Δ and Δ ⊢ t : A then Γ ⊢ wk η t : wk η A.

mutual
  wkGen : {m n : Nat} {bs : List Nat} (ρ : Wk m n) (c : GenTs Term n bs) → GenTs Term m bs
  wkGen ρ []                = []
  wkGen ρ (_∷_ {b = b} t c) = (wk (liftn ρ b) t) ∷ (wkGen ρ c)

  wk : {m n : Nat} (ρ : Wk m n) (t : Term n) → Term m
  wk ρ (var x)   = var (wkVar ρ x)
  wk ρ (gen k c) = gen k (wkGen ρ c)

-- Adding one variable to the context requires wk1.
-- If Γ ⊢ t : B then Γ∙A ⊢ wk1 t : wk1 B.

wk1 : Term n → Term (1+ n)
wk1 = wk (step id)

-- Weakening of a neutral term.

wkNeutral : ∀ ρ → Neutral t → Neutral {n} (wk ρ t)
wkNeutral ρ (var n)       = var (wkVar ρ n)
wkNeutral ρ (∘ₙ n)        = ∘ₙ (wkNeutral ρ n)
wkNeutral ρ (fstₙ n)      = fstₙ (wkNeutral ρ n)
wkNeutral ρ (sndₙ n)      = sndₙ (wkNeutral ρ n)
wkNeutral ρ (natrecₙ n)   = natrecₙ (wkNeutral ρ n)
wkNeutral ρ (Emptyrecₙ e) = Emptyrecₙ (wkNeutral ρ e)

-- Weakening can be applied to our whnf views.

wkNatural : ∀ ρ → Natural t → Natural {n} (wk ρ t)
wkNatural ρ sucₙ   = sucₙ
wkNatural ρ zeroₙ  = zeroₙ
wkNatural ρ (ne x) = ne (wkNeutral ρ x)

wkType : ∀ ρ → Type t → Type {n} (wk ρ t)
wkType ρ Πₙ     = Πₙ
wkType ρ Σₙ     = Σₙ
wkType ρ ℕₙ     = ℕₙ
wkType ρ Emptyₙ = Emptyₙ
wkType ρ Unitₙ  = Unitₙ
wkType ρ (ne x) = ne (wkNeutral ρ x)

wkFunction : ∀ ρ → Function t → Function {n} (wk ρ t)
wkFunction ρ lamₙ   = lamₙ
wkFunction ρ (ne x) = ne (wkNeutral ρ x)

wkProduct : ∀ ρ → Product t → Product {n} (wk ρ t)
wkProduct ρ prodₙ  = prodₙ
wkProduct ρ (ne x) = ne (wkNeutral ρ x)

wkWhnf : ∀ ρ → Whnf t → Whnf {n} (wk ρ t)
wkWhnf ρ Uₙ      = Uₙ
wkWhnf ρ Πₙ      = Πₙ
wkWhnf ρ Σₙ      = Σₙ
wkWhnf ρ ℕₙ      = ℕₙ
wkWhnf ρ Emptyₙ  = Emptyₙ
wkWhnf ρ Unitₙ   = Unitₙ
wkWhnf ρ lamₙ    = lamₙ
wkWhnf ρ prodₙ   = prodₙ
wkWhnf ρ zeroₙ   = zeroₙ
wkWhnf ρ sucₙ    = sucₙ
wkWhnf ρ starₙ   = starₙ
wkWhnf ρ (ne x)  = ne (wkNeutral ρ x)

-- Non-dependent version of Π.

_▹▹_ : Term n → Term n → Term n
A ▹▹ B = Π A ▹ wk1 B

-- Non-dependent products.

_××_ : Term n → Term n → Term n
A ×× B = Σ A ▹ wk1 B

------------------------------------------------------------------------
-- Substitution

-- The substitution operation  subst σ t  replaces the free de Bruijn indices
-- of term t by chosen terms as specified by σ.

-- The substitution σ itself is a map from natural numbers to terms.

Subst : Nat → Nat → Set
Subst m n = Fin n → Term m

-- Given closed contexts ⊢ Γ and ⊢ Δ,
-- substitutions may be typed via Γ ⊢ σ : Δ meaning that
-- Γ ⊢ σ(x) : (subst σ Δ)(x) for all x ∈ dom(Δ).
--
-- The substitution operation is then typed as follows:
-- If Γ ⊢ σ : Δ and Δ ⊢ t : A, then Γ ⊢ subst σ t : subst σ A.
--
-- Although substitutions are untyped, typing helps us
-- to understand the operation on substitutions.

-- We may view σ as the infinite stream σ 0, σ 1, ...

-- Extract the substitution of the first variable.
--
-- If Γ ⊢ σ : Δ∙A  then Γ ⊢ head σ : subst σ A.

head : Subst m (1+ n) → Term m
head σ = σ x0

-- Remove the first variable instance of a substitution
-- and shift the rest to accommodate.
--
-- If Γ ⊢ σ : Δ∙A then Γ ⊢ tail σ : Δ.

tail : Subst m (1+ n) → Subst m n
tail σ x = σ (x +1)

-- Substitution of a variable.
--
-- If Γ ⊢ σ : Δ then Γ ⊢ substVar σ x : (subst σ Δ)(x).

substVar : (σ : Subst m n) (x : Fin n) → Term m
substVar σ x = σ x

-- Identity substitution.
-- Replaces each variable by itself.
--
-- Γ ⊢ idSubst : Γ.

idSubst : Subst n n
idSubst = var

-- Weaken a substitution by one.
--
-- If Γ ⊢ σ : Δ then Γ∙A ⊢ wk1Subst σ : Δ.

wk1Subst : Subst m n → Subst (1+ m) n
wk1Subst σ x = wk1 (σ x)

-- Lift a substitution.
--
-- If Γ ⊢ σ : Δ then Γ∙A ⊢ liftSubst σ : Δ∙A.

liftSubst : (σ : Subst m n) → Subst (1+ m) (1+ n)
liftSubst σ x0     = var x0
liftSubst σ (x +1) = wk1Subst σ x

liftSubstn : {k m : Nat} → Subst k m → (n : Nat) → Subst (n + k) (n + m)
liftSubstn σ Nat.zero = σ
liftSubstn σ (1+ n)   = liftSubst (liftSubstn σ n)

-- Transform a weakening into a substitution.
--
-- If ρ : Γ ≤ Δ then Γ ⊢ toSubst ρ : Δ.

toSubst :  Wk m n → Subst m n
toSubst pr x = var (wkVar pr x)

-- Apply a substitution to a term.
--
-- If Γ ⊢ σ : Δ and Δ ⊢ t : A then Γ ⊢ subst σ t : subst σ A.

mutual
  substGen : {bs : List Nat} (σ : Subst m n) (g : GenTs Term n bs) → GenTs Term m bs
  substGen σ  []      = []
  substGen σ (_∷_ {b = b} t ts) = subst (liftSubstn σ b) t ∷ (substGen σ ts)

  subst : (σ : Subst m n) (t : Term n) → Term m
  subst σ (var x)   = substVar σ x
  subst σ (gen x c) = gen x (substGen σ c)

-- Extend a substitution by adding a term as
-- the first variable substitution and shift the rest.
--
-- If Γ ⊢ σ : Δ and Γ ⊢ t : subst σ A then Γ ⊢ consSubst σ t : Δ∙A.

consSubst : Subst m n → Term m → Subst m (1+ n)
consSubst σ t  x0    = t
consSubst σ t (x +1) = σ x

-- Singleton substitution.
--
-- If Γ ⊢ t : A then Γ ⊢ sgSubst t : Γ∙A.

sgSubst : Term n → Subst n (1+ n)
sgSubst = consSubst idSubst

-- Compose two substitutions.
--
-- If Γ ⊢ σ : Δ and Δ ⊢ σ′ : Φ then Γ ⊢ σ ₛ•ₛ σ′ : Φ.

_ₛ•ₛ_ : Subst ℓ m → Subst m n → Subst ℓ n
_ₛ•ₛ_ σ σ′ x = subst σ (σ′ x)

-- Composition of weakening and substitution.
--
--  If ρ : Γ ≤ Δ and Δ ⊢ σ : Φ then Γ ⊢ ρ •ₛ σ : Φ.

_•ₛ_ : Wk ℓ m → Subst m n → Subst ℓ n
_•ₛ_ ρ σ x = wk ρ (σ x)

--  If Γ ⊢ σ : Δ and ρ : Δ ≤ Φ then Γ ⊢ σ ₛ• ρ : Φ.

_ₛ•_ : Subst ℓ m → Wk m n → Subst ℓ n
_ₛ•_ σ ρ x = σ (wkVar ρ x)

-- Substitute the first variable of a term with an other term.
--
-- If Γ∙A ⊢ t : B and Γ ⊢ s : A then Γ ⊢ t[s] : B[s].

_[_] : (t : Term (1+ n)) (s : Term n) → Term n
t [ s ] = subst (sgSubst s) t

-- Substitute the first variable of a term with an other term,
-- but let the two terms share the same context.
--
-- If Γ∙A ⊢ t : B and Γ∙A ⊢ s : A then Γ∙A ⊢ t[s]↑ : B[s]↑.

_[_]↑ : (t : Term (1+ n)) (s : Term (1+ n)) → Term (1+ n)
t [ s ]↑ = subst (consSubst (wk1Subst idSubst) s) t

B-subst : (σ : Subst m n) (W : BindingType) (F : Term n) (G : Term (1+ n))
        → subst σ (⟦ W ⟧ F ▹ G) PE.≡ ⟦ W ⟧ (subst σ F) ▹ (subst (liftSubst σ) G)
B-subst σ BΠ F G = PE.refl
B-subst σ BΣ F G = PE.refl


module UniDB.Examples.Lc where

open import UniDB

data Tm (γ : Dom) : Set where
  var : (i : Ix γ) → Tm γ
  abs : (t : Tm (suc γ)) → Tm γ
  app : (t₁ t₂ : Tm γ) → Tm γ

instance
  iVrTm : Vr Tm
  vr     {{iVrTm}}      = var
  vr-inj {{iVrTm}} refl = refl

  iApTm : Ap Tm Tm
  ap {{iApTm}} ξ (var i)    = lk ξ i
  ap {{iApTm}} ξ (abs t)    = abs (ap {Tm} (ξ ↑₁) t )
  ap {{iApTm}} ξ (app t t₁) = app (ap {Tm} ξ t) (ap {Tm} ξ t₁)

  iApVrTm : ApVr Tm
  ap-vr {{iApVrTm}} ξ i = refl

  iApIdmTm : ApIdm Tm Tm
  ap-idm {{iApIdmTm}} {Ξ} (var i)    = lk-idm {Tm} {Ξ} i
  ap-idm {{iApIdmTm}} {Ξ} (abs t)    = cong abs (begin
    ap {Tm} (idm {Ξ} _ ↑₁) t  ≡⟨ cong₂ (ap {Tm}) (idm-↑₁ {Ξ}) (refl {x = t}) ⟩
    ap {Tm} (idm {Ξ} _) t     ≡⟨ ap-idm {Tm} t ⟩
    t                         ∎)
  ap-idm {{iApIdmTm}} {Ξ} (app t t₁) =
    cong₂ app (ap-idm {Tm} t) (ap-idm {Tm} t₁)

  iApPairTm : ApPair Tm Tm
  ap-pair {{iApPairTm}} ξ ζ (var i)     = refl
  ap-pair {{iApPairTm}} ξ ζ (abs x)     = cong abs (ap-pair {Tm} (ξ ↑₁) (ζ ↑₁) x)
  ap-pair {{iApPairTm}} ξ ζ (app x₁ x₂) = cong₂ app (ap-pair {Tm} ξ ζ x₁) (ap-pair {Tm} ξ ζ x₂)

  iApRelTm : ApRel Tm Tm
  ap-rel≅ {{iApRelTm}} hyp (var i)     = lk≃ (≅-to-≃ hyp) i
  ap-rel≅ {{iApRelTm}} hyp (abs t)     = cong abs (ap-rel≅ {Tm} {Tm} (≅-↑₁ hyp) t)
  ap-rel≅ {{iApRelTm}} hyp (app t₁ t₂) = cong₂ app (ap-rel≅ {Tm} {Tm} hyp t₁) (ap-rel≅ {Tm} {Tm} hyp t₂)

postulate
  Ρ : MOR
  instance
    iIdmΡ : Idm Ρ
    iWkmΡ : Wkm Ρ
    iUpΡ : Up Ρ
    iUpIdmΡ : UpIdm Ρ
    iCompΡ : Comp Ρ
    iWkmHomΡ : WkmHom Ρ
    iLkΡ : {T : STX} {{vrT : Vr T}} → Lk T Ρ
    iLkIdmΡ : {T : STX} {{vrT : Vr T}} → LkIdm T Ρ
    iLkWkmΡ : {T : STX} {{vrT : Vr T}} → LkWkm T Ρ
    iLkCompAp : {T : STX} {{vrT : Vr T}} {{apTT : Ap T T}} {{apVrT : ApVr T}} → LkCompAp T Ρ
    iLkUpΡ : {T : STX} {{vrT : Vr T}} {{wkT : Wk T}} → LkUp T Ρ
    iLkRenΡ : {T : STX} {{vrT : Vr T}} → LkRen T Ρ

instance
  iWkTm : Wk Tm
  wk₁ {{iWkTm}} = ap {Tm} (wkm {Ρ} 1)
  wk {{iWkTm}} δ t = ap {Tm} (wkm {Ρ} δ) t
  wk-zero {{iWkTm}} t = begin
    ap {Tm} (wkm {Ρ} 0) t ≡⟨ ap-rel≅ {Tm} (Ix≅-to-≅ {Tm} (≃-to-≅` (≃-↑ {Ix} (record { lk≃ = λ i → begin
        lk (wkm {Ρ} 0) i ≡⟨ lk-wkm {Ix} {Ρ} 0 i ⟩
        i                ≡⟨ sym (lk-idm {Ix} {Ρ} i) ⟩
        lk (idm {Ρ} _) i ∎
      })))) t ⟩
    ap {Tm} (idm {Ρ} _) t ≡⟨ ap-idm {Tm} {Tm} {Ρ} t ⟩
    t                     ∎
  wk-suc {{iWkTm}} δ t = begin
    ap {Tm} (wkm {Ρ} (suc δ)) t                 ≡⟨ cong (λ ζ → ap {Tm} {Tm} {Ρ} ζ t) (wkm-suc {Ρ} δ) ⟩
    ap {Tm} (wkm {Ρ} δ ⊙ wkm {Ρ} 1) t          ≡⟨ ap-rel≅ {Tm} (Ix≅-to-≅ {Tm} {Ρ} {Pair Ρ Ρ} (≃-to-≅`
      (≃-↑ {Ix} {Ρ} {Pair Ρ Ρ} {ξ = wkm {Ρ} δ ⊙ wkm {Ρ} 1} {wkm {Ρ} δ ⊗ wkm {Ρ} 1} (record { lk≃ =
        lk-⊙-ap {Ix} {Ρ} (wkm {Ρ} δ) (wkm {Ρ} 1)  })))) t ⟩
    ap {Tm} (wkm {Ρ} δ ⊗ wkm {Ρ} 1) t          ≡⟨ ap-pair {Tm} (wkm {Ρ} δ) (wkm {Ρ} 1) t ⟩
    ap {Tm} (wkm {Ρ} 1) (ap {Tm} (wkm {Ρ} δ) t) ∎

instance
  iWkVrTm : WkVr Tm
  wk₁-vr {{iWkVrTm}} i = lk-wkm {Tm} {Ρ} 1 i
  wk-vr {{iWkVrTm}} δ i = lk-wkm {Tm} {Ρ} δ i

  iApWkmWkTm : ApWkmWk Tm Tm
  ap-wkm-wk₁ {{iApWkmWkTm}} {Ξ} = ap-wkm-rel Tm Tm Ξ Ρ 1
  ap-wkm-wk {{iApWkmWkTm}} {Ξ} = ap-wkm-rel Tm Tm Ξ Ρ

  -- iApCompTm : ApComp Tm Tm
  -- ap-⊙ {{iApCompTm}} {Ξ} ξ₁ ξ₂ (var i) = lk-⊙-ap {Tm} {Ξ} ξ₁ ξ₂ i
  -- ap-⊙ {{iApCompTm}} {Ξ} ξ₁ ξ₂ (abs t) rewrite ⊙-↑₁ {Ξ} ξ₁ ξ₂ =
  --   cong abs (ap-⊙ {Tm} (ξ₁ ↑₁) (ξ₂ ↑₁) t)
  -- ap-⊙ {{iApCompTm}} {Ξ} ξ₁ ξ₂ (app t₁ t₂) =
  --   cong₂ app (ap-⊙ {Tm} {Tm} {Ξ} ξ₁ ξ₂ t₁) (ap-⊙ {Tm} {Tm} {Ξ} ξ₁ ξ₂ t₂)

  iApCompTm : ApComp Tm Tm
  iApCompTm = iApComp Tm Tm

  iApWkTm : ApWk Tm Tm
  iApWkTm = iApWk Tm Tm

{-}

--------------------------------------------------------------------------------

subTm : {X : STX} {{apTmX : Ap Tm X}} {γ : Dom} (s : Tm γ) (x : X (suc γ)) → X γ
subTm {X} s x = ap Tm X (Sub Tm) (beta Tm (Sub Tm) s) x

data Eval {γ : Dom} : Tm γ → Tm γ → Set where
  e-app₁ : {t₁ t₁' t₂ : Tm γ} → Eval t₁ t₁' → Eval (app t₁ t₂) (app t₁' t₂)
  e-app₂ : {t₁ t₂ t₂' : Tm γ} → Eval t₂ t₂' → Eval (app t₁ t₂) (app t₁ t₂')
  e-abs  : {t t' : Tm (suc γ)} → Eval t t' → Eval (abs t) (abs t')
  e-beta : {s : Tm γ} {t : Tm (suc γ)} → Eval (app (abs t) s) (subTm s t)

--------------------------------------------------------------------------------
-}


------------------------------------------------------------------------
-- The Agda standard library
--
-- Properties related to All
------------------------------------------------------------------------

{-# OPTIONS --without-K --safe #-}

module Data.Vec.Relation.Unary.All.Properties where

open import Data.List using ([]; _∷_)
open import Data.List.Relation.Unary.All as List using ([]; _∷_)
open import Data.Product as Prod using (_×_; _,_; uncurry; uncurry′)
open import Data.Vec as Vec
open import Data.Vec.Relation.Unary.All as All using (All; []; _∷_)
open import Function using (_∘_; id)
open import Function.Inverse using (_↔_; inverse)
open import Relation.Unary using (Pred) renaming (_⊆_ to _⋐_)
open import Relation.Binary.PropositionalEquality
  using (_≡_; refl; cong; cong₂; →-to-⟶)

------------------------------------------------------------------------
-- map

module _ {a b p} {A : Set a} {B : Set b} {P : Pred B p} {f : A → B} where

  map⁺ : ∀ {n} {xs : Vec A n} → All (P ∘ f) xs → All P (map f xs)
  map⁺ []         = []
  map⁺ (px ∷ pxs) = px ∷ map⁺ pxs

  map⁻ : ∀ {n} {xs : Vec A n} → All P (map f xs) → All (P ∘ f) xs
  map⁻ {xs = []}    []       = []
  map⁻ {xs = _ ∷ _} (px ∷ pxs) = px ∷ map⁻ pxs

-- A variant of All.map

module _ {a b p q} {A : Set a} {B : Set b} {f : A → B}
         {P : Pred A p} {Q : Pred B q} where

  gmap : ∀ {n} → P ⋐ Q ∘ f → All P {n} ⋐ All Q {n} ∘ map f
  gmap g = map⁺ ∘ All.map g

------------------------------------------------------------------------
-- _++_

module _ {a n p} {A : Set a} {P : Pred A p} where

  ++⁺ : ∀ {m} {xs : Vec A m} {ys : Vec A n} →
        All P xs → All P ys → All P (xs ++ ys)
  ++⁺ []         pys = pys
  ++⁺ (px ∷ pxs) pys = px ∷ ++⁺ pxs pys

  ++ˡ⁻ : ∀ {m} (xs : Vec A m) {ys : Vec A n} →
         All P (xs ++ ys) → All P xs
  ++ˡ⁻ []       _          = []
  ++ˡ⁻ (x ∷ xs) (px ∷ pxs) = px ∷ ++ˡ⁻ xs pxs

  ++ʳ⁻ : ∀ {m} (xs : Vec A m) {ys : Vec A n} →
         All P (xs ++ ys) → All P ys
  ++ʳ⁻ []       pys        = pys
  ++ʳ⁻ (x ∷ xs) (px ∷ pxs) = ++ʳ⁻ xs pxs

  ++⁻ : ∀ {m} (xs : Vec A m) {ys : Vec A n} →
        All P (xs ++ ys) → All P xs × All P ys
  ++⁻ []       p          = [] , p
  ++⁻ (x ∷ xs) (px ∷ pxs) = Prod.map₁ (px ∷_) (++⁻ _ pxs)

  ++⁺∘++⁻ : ∀ {m} (xs : Vec A m) {ys : Vec A n} →
            (p : All P (xs ++ ys)) →
            uncurry′ ++⁺ (++⁻ xs p) ≡ p
  ++⁺∘++⁻ []       p          = refl
  ++⁺∘++⁻ (x ∷ xs) (px ∷ pxs) = cong (px ∷_) (++⁺∘++⁻ xs pxs)

  ++⁻∘++⁺ : ∀ {m} {xs : Vec A m} {ys : Vec A n} →
            (p : All P xs × All P ys) →
            ++⁻ xs (uncurry ++⁺ p) ≡ p
  ++⁻∘++⁺ ([]       , pys) = refl
  ++⁻∘++⁺ (px ∷ pxs , pys) rewrite ++⁻∘++⁺ (pxs , pys) = refl

  ++↔ : ∀ {m} {xs : Vec A m} {ys : Vec A n} →
        (All P xs × All P ys) ↔ All P (xs ++ ys)
  ++↔ {xs = xs} = inverse (uncurry ++⁺) (++⁻ xs) ++⁻∘++⁺ (++⁺∘++⁻ xs)

------------------------------------------------------------------------
-- concat

module _ {a m p} {A : Set a} {P : Pred A p} where

  concat⁺ : ∀ {n} {xss : Vec (Vec A m) n} →
            All (All P) xss → All P (concat xss)
  concat⁺ []           = []
  concat⁺ (pxs ∷ pxss) = ++⁺ pxs (concat⁺ pxss)

  concat⁻ : ∀ {n} (xss : Vec (Vec A m) n) →
            All P (concat xss) → All (All P) xss
  concat⁻ []         []   = []
  concat⁻ (xs ∷ xss) pxss = ++ˡ⁻ xs pxss ∷ concat⁻ xss (++ʳ⁻ xs pxss)

------------------------------------------------------------------------
-- toList

module _ {a p} {A : Set a} {P : A → Set p} where

  toList⁺ : ∀ {n} {xs : Vec A n} → All P xs → List.All P (toList xs)
  toList⁺ []         = []
  toList⁺ (px ∷ pxs) = px ∷ toList⁺ pxs

  toList⁻ : ∀ {n} {xs : Vec A n} → List.All P (toList xs) → All P xs
  toList⁻ {xs = []}     []         = []
  toList⁻ {xs = x ∷ xs} (px ∷ pxs) = px ∷ toList⁻ pxs

------------------------------------------------------------------------
-- fromList

module _ {a p} {A : Set a} {P : A → Set p} where

  fromList⁺ : ∀ {xs} → List.All P xs → All P (fromList xs)
  fromList⁺ []         = []
  fromList⁺ (px ∷ pxs) = px ∷ fromList⁺ pxs

  fromList⁻ : ∀ {xs} → All P (fromList xs) → List.All P xs
  fromList⁻ {[]}     []         = []
  fromList⁻ {x ∷ xs} (px ∷ pxs) = px ∷ (fromList⁻ pxs)

------------------------------------------------------------------------
-- DEPRECATED NAMES
------------------------------------------------------------------------
-- Please use the new names as continuing support for the old names is
-- not guaranteed.

-- Version 0.16

All-map     = map⁺
{-# WARNING_ON_USAGE All-map
"Warning: All-map was deprecated in v0.16.
Please use map⁺ instead."
#-}
map-All     = map⁻
{-# WARNING_ON_USAGE map-All
"Warning: map-All was deprecated in v0.16.
Please use map⁻ instead."
#-}
All-++⁺     = ++⁺
{-# WARNING_ON_USAGE All-++⁺
"Warning: All-++⁺ was deprecated in v0.16.
Please use ++⁺ instead."
#-}
All-++ˡ⁻    = ++ˡ⁻
{-# WARNING_ON_USAGE All-++ˡ⁻
"Warning: All-++ˡ⁻ was deprecated in v0.16.
Please use ++ˡ⁻ instead."
#-}
All-++ʳ⁻    = ++ʳ⁻
{-# WARNING_ON_USAGE All-++ʳ⁻
"Warning: All-++ʳ⁻ was deprecated in v0.16.
Please use ++ʳ⁻ instead."
#-}
All-++⁻     = ++⁻
{-# WARNING_ON_USAGE All-++⁻
"Warning: All-++⁻ was deprecated in v0.16.
Please use ++⁻ instead."
#-}
All-++⁺∘++⁻ = ++⁺∘++⁻
{-# WARNING_ON_USAGE All-++⁺∘++⁻
"Warning: All-++⁺∘++⁻ was deprecated in v0.16.
Please use ++⁺∘++⁻ instead."
#-}
All-++⁻∘++⁺ = ++⁻∘++⁺
{-# WARNING_ON_USAGE All-++⁻∘++⁺
"Warning: All-++⁻∘++⁺ was deprecated in v0.16.
Please use ++⁻∘++⁺ instead."
#-}
All-concat⁺ = concat⁺
{-# WARNING_ON_USAGE All-concat⁺
"Warning: All-concat⁺ was deprecated in v0.16.
Please use concat⁺ instead."
#-}
All-concat⁻ = concat⁻
{-# WARNING_ON_USAGE All-concat⁻
"Warning: All-concat⁻ was deprecated in v0.16.
Please use concat⁻ instead."
#-}


module Logic.Convenience {ℓ} where

import      Lvl
open import Data.Tuple as Tuple using (_⨯_ ; _,_)
open import Functional
open import Logic.Propositional{ℓ}
open import Type

record [⇒]-proof (Proof : Stmt → Stmt → Type{ℓ}) : Type{Lvl.𝐒(ℓ)} where
  infixl 10 _⇒_
  infixr 10 _⇐_

  field
    _⇒_ : ∀{X Y : Stmt} → X → Proof(X)(Y) → Y

  _⇐_ : ∀{X Y : Stmt} → Proof(X)(Y) → X → Y
  _⇐_ = swap(_⇒_)
open [⇒]-proof ⦃...⦄ public

instance
  [⇒]-proof-[→] : [⇒]-proof (X ↦ Y ↦ (X → Y))
  _⇒_ ⦃ [⇒]-proof-[→] ⦄ = apply

instance
  [⇒]-proof-[↔] : [⇒]-proof (X ↦ Y ↦ (X ↔ Y))
  _⇒_ ⦃ [⇒]-proof-[↔] ⦄ = swap(Tuple.right)

instance
  [⇒]-proof-unrelated : [⇒]-proof (X ↦ Y ↦ Y)
  _⇒_ ⦃ [⇒]-proof-unrelated ⦄ = swap(const)

-- TODO: ⇔ probably means using transitivity


{-# OPTIONS --cubical-compatible #-}

open import Agda.Builtin.Equality

subst :
  {A : Set} {@0 x y : A}
  (@0 P : A → Set) → x ≡ y → P x → P y
subst P refl p = p


{-# OPTIONS --without-K --rewriting #-}

{-
Imports everything that is not imported by something else.
This is not supposed to be used anywhere, this is just a simple way to
do `make all'

This file is intentionally named index.agda so that
Agda will generate index.html.
-}

module index where

{- some group theory results -}
import groups.ReducedWord
import groups.ProductRepr
import groups.CoefficientExtensionality

{- homotopy groups of circles -}
import homotopy.LoopSpaceCircle
import homotopy.PinSn
import homotopy.HopfJunior
import homotopy.Hopf

{- cohomology -}
import cohomology.EMModel
import cohomology.Sigma
import cohomology.Coproduct
import cohomology.Torus
-- import cohomology.MayerVietorisExact -- FIXME

{- prop * prop is still a prop -}
import homotopy.PropJoinProp

{- a space with preassigned homotopy groups -}
import homotopy.SpaceFromGroups

{- pushout 3x3 lemma -}
{- These takes lots of time and memory to check. -}
-- import homotopy.3x3.Commutes -- commented out because this does not run on travis.
-- import homotopy.JoinAssoc3x3 -- commented out because this does not run on travis.

{- covering spaces -}
import homotopy.GroupSetsRepresentCovers
import homotopy.AnyUniversalCoverIsPathSet
import homotopy.PathSetIsInitalCover

{- van kampen -}
import homotopy.VanKampen

{- blakers massey -}
import homotopy.BlakersMassey

{- cw complexes -}
import cw.CW
import cw.examples.Examples
-- cellular cohomology groups
import cw.cohomology.CellularChainComplex
-- Eilenberg-Steenred cohomology groups rephrased
import cw.cohomology.ReconstructedCohomologyGroups
-- isomorphisms between the cochains the heads
import cw.cohomology.ReconstructedCochainsIsoCellularCochains

-- There are some unported theorems

-- import Spaces.IntervalProps
-- import Algebra.F2NotCommutative
-- import Spaces.LoopSpaceDecidableWedgeCircles
-- import Homotopy.PullbackIsPullback
-- import Homotopy.PushoutIsPushout
-- import Homotopy.Truncation
-- import Sets.QuotientUP


------------------------------------------------------------------------
-- The parser data type
------------------------------------------------------------------------

-- This hybrid variant is coinductive /and/ includes !_.

module RecursiveDescent.Hybrid.Type where

open import Data.Bool
open import Data.Product.Record

open import RecursiveDescent.Index

-- A type for parsers which can be implemented using recursive
-- descent. The types used ensure that the implemented backends are
-- structurally recursive.

-- The parsers are indexed on a type of nonterminals.

codata Parser (tok : Set) (nt : ParserType) : ParserType₁ where
  !_     :  forall {e c r}
         -> nt (e , c) r -> Parser tok nt (e , step c) r
  symbol :  Parser tok nt (false , leaf) tok
  return :  forall {r} -> r -> Parser tok nt (true , leaf) r
  fail   :  forall {r} -> Parser tok nt (false , leaf) r
  _?>>=_ :  forall {c₁ e₂ c₂ r₁ r₂}
         -> Parser tok nt (true , c₁) r₁
         -> (r₁ -> Parser tok nt (e₂ , c₂) r₂)
         -> Parser tok nt (e₂ , node c₁ c₂) r₂
  _!>>=_ :  forall {c₁ r₁ r₂} {i₂ : r₁ -> Index}
         -> Parser tok nt (false , c₁) r₁
         -> ((x : r₁) -> Parser tok nt (i₂ x) r₂)
         -> Parser tok nt (false , step c₁) r₂
  alt    :  forall e₁ e₂ {c₁ c₂ r}
         -> Parser tok nt (e₁      , c₁)         r
         -> Parser tok nt (e₂      , c₂)         r
         -> Parser tok nt (e₁ ∨ e₂ , node c₁ c₂) r

-- Grammars.

Grammar : Set -> ParserType -> Set1
Grammar tok nt = forall {i r} -> nt i r -> Parser tok nt i r