{-# OPTIONS --without-K #-}
-- Constructions on top of exploration functions

open import Level.NP
open import Type hiding (★)
open import Type.Identities
open import Function.NP
open import Function.Extensionality
open import Algebra.FunctionProperties.NP
open import Data.Two.Base
open import Data.Indexed
open import Data.Nat.NP hiding (_⊔_)
open import Data.Nat.Properties
open import Data.Fin using (Fin) renaming (zero to fzero)
open import Data.Maybe.NP
open import Algebra
open import Data.Product.NP renaming (map to ×-map) hiding (first)
open import Data.Sum.NP renaming (map to ⊎-map)
open import Data.Zero using (𝟘)
open import Data.One using (𝟙)
open import Data.Tree.Binary
import Data.List as List
open List using (List; _++_)
open import Relation.Nullary.Decidable
open import Relation.Nullary.NP
open import Relation.Binary
open import Relation.Binary.Sum using (_⊎-cong_)
open import Relation.Binary.Product.Pointwise using (_×-cong_)
import Function.Related as FR
import Relation.Binary.PropositionalEquality.NP as ≡
open import HoTT
open Equivalences
open ≡ using (_≡_)

open import Explore.Core
open import Explore.Properties
import Explore.Monad as EM

module Explore.Explorable where

module _ {m a} {A : ★ a} where
  open EM {a} m
  gfilter-explore : ∀ {B} → (A →? B) → Explore m A → Explore m B
  gfilter-explore f eᴬ = eᴬ >>= λ x → maybe (λ η → point-explore η) empty-explore (f x)

  filter-explore : (A → 𝟚) → Explore m A → Explore m A
  filter-explore p = gfilter-explore λ x → [0: nothing 1: just x ] (p x)

  -- monoidal exploration: explore A with a monoid M
  explore-monoid : ∀ {ℓ} → Explore m A → ExploreMon m ℓ A
  explore-monoid eᴬ M = eᴬ ε _·_ where open Mon M renaming (_∙_ to _·_)

  explore-endo : Explore m A → Explore m A
  explore-endo eᴬ ε op f = eᴬ id _∘′_ (op ∘ f) ε

  explore-endo-monoid : ∀ {ℓ} → Explore m A → ExploreMon m ℓ A
  explore-endo-monoid = explore-monoid ∘ explore-endo

  explore-backward : Explore m A → Explore m A
  explore-backward eᴬ ε _∙_ f = eᴬ ε (flip _∙_) f

  -- explore-backward ∘ explore-backward = id
  -- (m : a comm monoid) → explore-backward m = explore m

private
  module FindForward {a} {A : ★ a} (explore : Explore a A) where
    find? : Find? A
    find? = explore nothing (M?._∣_ _)

    first : Maybe A
    first = find? just

    findKey : FindKey A
    findKey pred = find? (λ x → [0: nothing 1: just x ] (pred x))

module ExplorePlug {ℓ a} {A : ★ a} where
  record ExploreIndKit p (P : Explore ℓ A → ★ p) : ★ (a ⊔ ₛ ℓ ⊔ p) where
    constructor mk
    field
      Pε : P empty-explore
      P∙ : ∀ {e₀ e₁ : Explore ℓ A} → P e₀ → P e₁ → P (merge-explore e₀ e₁)
      Pf : ∀ x → P (point-explore x)

  _$kit_ : ∀ {p} {P : Explore ℓ A → ★ p} {e : Explore ℓ A}
           → ExploreInd p e → ExploreIndKit p P → P e
  _$kit_ {P = P} ind (mk Pε P∙ Pf) = ind P Pε P∙ Pf

  _,-kit_ : ∀ {p} {P : Explore ℓ A → ★ p}{Q : Explore ℓ A → ★ p}
            → ExploreIndKit p P → ExploreIndKit p Q → ExploreIndKit p (P ×° Q)
  Pk ,-kit Qk = mk (Pε Pk , Pε Qk)
                   (λ x y → P∙ Pk (fst x) (fst y) , P∙ Qk (snd x) (snd y))
                   (λ x → Pf Pk x , Pf Qk x)
               where open ExploreIndKit

  ExploreInd-Extra : ∀ p → Explore ℓ A → ★ _
  ExploreInd-Extra p exp =
    ∀ (Q     : Explore ℓ A → ★ p)
      (Q-kit : ExploreIndKit p Q)
      (P     : Explore ℓ A → ★ p)
      (Pε    : P empty-explore)
      (P∙    : ∀ {e₀ e₁ : Explore ℓ A} → Q e₀ → Q e₁ → P e₀ → P e₁
               → P (merge-explore e₀ e₁))
      (Pf    : ∀ x → P (point-explore x))
    → P exp

  to-extra : ∀ {p} {e : Explore ℓ A} → ExploreInd p e → ExploreInd-Extra p e
  to-extra e-ind Q Q-kit P Pε P∙ Pf =
   snd (e-ind (Q ×° P)
           (Qε , Pε)
           (λ { (a , b) (c , d) → Q∙ a c , P∙ a c b d })
           (λ x → Qf x , Pf x))
   where open ExploreIndKit Q-kit renaming (Pε to Qε; P∙ to Q∙; Pf to Qf)

  ExplorePlug : ∀ {m} (M : Monoid ℓ m) (e : Explore _ A) → ★ _
  ExplorePlug M e = ∀ f x → e∘ ε _∙_ f ∙ x ≈ e∘ x _∙_ f
     where open Mon M
           e∘ = explore-endo e

  plugKit : ∀ {m} (M : Monoid ℓ m) → ExploreIndKit _ (ExplorePlug M)
  plugKit M = mk (λ _ → fst identity)
                 (λ Ps Ps' f x →
                    trans (∙-cong (! Ps _ _) refl)
                          (trans (assoc _ _ _)
                                 (trans (∙-cong refl (Ps' _ x)) (Ps _ _))))
                 (λ x f _ → ∙-cong (snd identity (f x)) refl)
       where open Mon M

module FromExplore
    {a} {A : ★ a}
    (explore : ∀ {ℓ} → Explore ℓ A) where

  module _ {ℓ} where
    with-monoid : ∀ {m} → ExploreMon ℓ m A
    with-monoid = explore-monoid explore

    with∘ : Explore ℓ A
    with∘ = explore-endo explore

    with-endo-monoid : ∀ {m} → ExploreMon ℓ m A
    with-endo-monoid = explore-endo-monoid explore

    backward : Explore ℓ A
    backward = explore-backward explore

    gfilter : ∀ {B} → (A →? B) → Explore ℓ B
    gfilter f = gfilter-explore f explore

    filter : (A → 𝟚) → Explore ℓ A
    filter p = filter-explore p explore

  sum : Sum A
  sum = explore 0 _+_

  Card : ℕ
  Card = sum (const 1)

  count : Count A
  count f = sum (𝟚▹ℕ ∘ f)

  product : (A → ℕ) → ℕ
  product = explore 1 _*_

  big-∧ big-∨ big-xor : (A → 𝟚) → 𝟚

  big-∧ = explore 1₂ _∧_
  and   = big-∧
  all   = big-∧

  big-∨ = explore 0₂ _∨_
  or    = big-∨
  any   = big-∨

  big-xor = explore 0₂ _xor_

  big-lift∧ big-lift∨ : Level → (A → 𝟚) → 𝟚
  big-lift∧ ℓ f = lower (explore {ℓ} (lift 1₂) (lift-op₂ _∧_) (lift ∘ f))
  big-lift∨ ℓ f = lower (explore {ℓ} (lift 0₂) (lift-op₂ _∨_) (lift ∘ f))

  bin-tree : BinTree A
  bin-tree = explore empty fork leaf

  list : List A
  list = explore List.[] _++_ List.[_]

  module FindBackward = FindForward backward

  findLast? : Find? A
  findLast? = FindBackward.find?

  last : Maybe A
  last = FindBackward.first

  findLastKey : FindKey A
  findLastKey = FindBackward.findKey

  open FindForward explore public

module FromLookup
    {a} {A : ★ a}
    {explore : ∀ {ℓ} → Explore ℓ A}
    (lookup : ∀ {ℓ} → Lookup {ℓ} explore)
    where

  module CheckDec! {ℓ}{P : A → ★ ℓ}(decP : ∀ x → Dec (P x)) where
    CheckDec! : ★ _
    CheckDec! = explore (Lift 𝟙) _×_ λ x → ✓ ⌊ decP x ⌋

    checkDec! : {p✓ : CheckDec!} → ∀ x → P x
    checkDec! {p✓} x = toWitness (lookup p✓ x)

module FromExploreInd
    {a} {A : ★ a}
    {explore : ∀ {ℓ} → Explore ℓ A}
    (explore-ind : ∀ {p ℓ} → ExploreInd {ℓ} p explore)
    where

  open FromExplore explore public

  module _ {ℓ p} where
    explore-mon-ext : ExploreMonExt {ℓ} p explore
    explore-mon-ext m {f} {g} f≈°g = explore-ind (λ s → s _ _ f ≈ s _ _ g) refl ∙-cong f≈°g
      where open Mon m

    explore-mono : ExploreMono {ℓ} p explore
    explore-mono _⊆_ z⊆ _∙-mono_ {f} {g} f⊆°g =
      explore-ind (λ e → e _ _ f ⊆ e _ _ g) z⊆ _∙-mono_ f⊆°g

    open ExplorePlug {ℓ} {a} {A}

    explore∘-plug : (M : Monoid ℓ ℓ) → ExplorePlug M explore
    explore∘-plug M = explore-ind $kit plugKit M

    module _ (M : Monoid ℓ ℓ)
             (open Mon M)
             (f : A → C)
             where
        explore-endo-monoid-spec′ : ∀ z → explore ε _∙_ f ∙ z ≈ explore-endo explore z _∙_ f
        explore-endo-monoid-spec′ = explore-ind (λ e → ∀ z → e ε _∙_ f ∙ z ≈ explore-endo e z _∙_ f)
                                                (fst identity) (λ P₀ P₁ z → trans (assoc _ _ _) (trans (∙-cong refl (P₁ z)) (P₀ _))) (λ _ _ → refl)

        explore-endo-monoid-spec : with-monoid M f ≈ with-endo-monoid M f
        explore-endo-monoid-spec = trans (! snd identity _) (explore-endo-monoid-spec′ ε)

    explore∘-ind : ∀ (M : Monoid ℓ ℓ) → BigOpMonInd ℓ M (with-endo-monoid M)
    explore∘-ind M P Pε P∙ Pf P≈ =
      snd (explore-ind (λ e → ExplorePlug M e × P (λ f → e id _∘′_ (_∙_ ∘ f) ε))
                 (const (fst identity) , Pε)
                 (λ {e} {s'} Ps Ps' → ExploreIndKit.P∙ (plugKit M) {e} {s'} (fst Ps) (fst Ps')
                                    , P≈ (λ f → fst Ps f _) (P∙ (snd Ps) (snd Ps')))
                 (λ x → ExploreIndKit.Pf (plugKit M) x
                      , P≈ (λ f → ! snd identity _) (Pf x)))
      where open Mon M

    explore-swap : ∀ {b} → ExploreSwap {ℓ} p explore {b}
    explore-swap mon {eᴮ} eᴮ-ε pf f =
      explore-ind (λ e → e _ _ (eᴮ ∘ f) ≈ eᴮ (e _ _ ∘ flip f))
                  (! eᴮ-ε)
                  (λ p q → trans (∙-cong p q) (! pf _ _))
                  (λ _ → refl)
      where open Mon mon

    explore-ε : Exploreε {ℓ} p explore
    explore-ε M = explore-ind (λ e → e ε _ (const ε) ≈ ε)
                              refl
                              (λ x≈ε y≈ε → trans (∙-cong x≈ε y≈ε) (fst identity ε))
                              (λ _ → refl)
      where open Mon M

    explore-hom : ExploreHom {ℓ} p explore
    explore-hom cm f g = explore-ind (λ e → e _ _ (f ∙° g) ≈ e _ _ f ∙ e _ _ g)
                                     (! fst identity ε)
                                     (λ p₀ p₁ → trans (∙-cong p₀ p₁) (∙-interchange _ _ _ _))
                                     (λ _ → refl)
      where open CMon cm

    explore-linˡ : ExploreLinˡ {ℓ} p explore
    explore-linˡ m _◎_ f k ide dist = explore-ind (λ e → e ε _∙_ (λ x → k ◎ f x) ≈ k ◎ e ε _∙_ f) (! ide) (λ x x₁ → trans (∙-cong x x₁) (! dist k _ _)) (λ x → refl)
      where open Mon m

    explore-linʳ : ExploreLinʳ {ℓ} p explore
    explore-linʳ m _◎_ f k ide dist = explore-ind (λ e → e ε _∙_ (λ x → f x ◎ k) ≈ e ε _∙_ f ◎ k) (! ide) (λ x x₁ → trans (∙-cong x x₁) (! dist k _ _)) (λ x → refl)
      where open Mon m

    module ProductMonoid
        {M : ★₀} (εₘ : M) (_⊕ₘ_ : Op₂ M)
        {N : ★₀} (εₙ : N) (_⊕ₙ_ : Op₂ N)
        where
        ε = (εₘ , εₙ)
        _⊕_ : Op₂ (M × N)
        (xₘ , xₙ) ⊕ (yₘ , yₙ) = (xₘ ⊕ₘ yₘ , xₙ ⊕ₙ yₙ)

        explore-product-monoid :
          ∀ fₘ fₙ → explore ε _⊕_ < fₘ , fₙ > ≡ (explore εₘ _⊕ₘ_ fₘ , explore εₙ _⊕ₙ_ fₙ)
        explore-product-monoid fₘ fₙ =
          explore-ind (λ e → e ε _⊕_ < fₘ , fₙ > ≡ (e εₘ _⊕ₘ_ fₘ , e εₙ _⊕ₙ_ fₙ)) ≡.refl (≡.ap₂ _⊕_) (λ _ → ≡.refl)
  {-
  empty-explore:
    ε ≡ (εₘ , εₙ) ✓
  point-explore (x , y):
    < fₘ , fₙ > (x , y) ≡ (fₘ x , fₙ y) ✓
  merge-explore e₀ e₁:
    e₀ ε _⊕_ < fₘ , fₙ > ⊕ e₁ ε _⊕_ < fₘ , fₙ >
    ≡
    (e₀ εₘ _⊕ₘ_ fₘ , e₀ εₙ _⊕ₙ_ fₙ) ⊕ (e₁ εₘ _⊕ₘ_ fₘ , e₁ εₙ _⊕ₙ_ fₙ)
    ≡
    (e₀ εₘ _⊕ₘ_ fₘ ⊕ e₁ εₘ _⊕ₘ_ fₘ , e₀ εₙ _⊕ₙ_ fₙ ⊕ e₁ εₙ _⊕ₙ_ fₙ)
  -}

  module _ {ℓ} where
    reify : Reify {ℓ} explore
    reify = explore-ind (λ eᴬ → Πᵉ eᴬ _) _ _,_

    unfocus : Unfocus {ℓ} explore
    unfocus = explore-ind Unfocus (λ{ (lift ()) }) (λ P Q → [ P , Q ]) (λ η → _,_ η)

    module _ {ℓᵣ aᵣ} {Aᵣ : A → A → ★ aᵣ}
             (Aᵣ-refl : Reflexive Aᵣ) where
      ⟦explore⟧ : ⟦Explore⟧ ℓᵣ Aᵣ (explore {ℓ}) (explore {ℓ})
      ⟦explore⟧ Mᵣ zᵣ ∙ᵣ fᵣ = explore-ind (λ e → Mᵣ (e _ _ _) (e _ _ _)) zᵣ (λ η → ∙ᵣ η) (λ η → fᵣ Aᵣ-refl)

    explore-ext : ExploreExt {ℓ} explore
    explore-ext ε op = explore-ind (λ e → e _ _ _ ≡ e _ _ _) ≡.refl (≡.ap₂ op)

  module LiftHom
       {m p}
       {S T : ★ m}
       (_≈_ : T → T → ★ p)
       (≈-refl : Reflexive _≈_)
       (≈-trans : Transitive _≈_)
       (zero : S)
       (_+_  : Op₂ S)
       (one  : T)
       (_*_  : Op₂ T)
       (≈-cong-* : _*_ Preserves₂ _≈_ ⟶ _≈_ ⟶ _≈_)
       (f   : S → T)
       (g   : A → S)
       (hom-0-1 : f zero ≈ one)
       (hom-+-* : ∀ {x y} → (f (x + y)) ≈ (f x * f y))
       where

        lift-hom : f (explore zero _+_ g) ≈ explore one _*_ (f ∘ g)
        lift-hom = explore-ind (λ e → f (e zero _+_ g) ≈ e one _*_ (f ∘ g))
                               hom-0-1
                               (λ p q → ≈-trans hom-+-* (≈-cong-* p q))
                               (λ _ → ≈-refl)

  module _ {ℓ} {P : A → ★_ ℓ} where
    open LiftHom {S = ★_ ℓ} {★_ ℓ} (λ A B → B → A) id _∘′_
                 (Lift 𝟘) _⊎_ (Lift 𝟙) _×_
                 (λ f g → ×-map f g) Dec P (const (no (λ{ (lift ()) })))
                 (uncurry Dec-⊎)
                 public renaming (lift-hom to lift-Dec)

  module FromFocus {p} (focus : Focus {p} explore) where
    Dec-Σ : ∀ {P} → Π A (Dec ∘ P) → Dec (Σ A P)
    Dec-Σ = map-Dec unfocus focus ∘ lift-Dec ∘ reify

  lift-hom-≡ :
      ∀ {m} {S T : ★ m}
        (zero : S)
        (_+_  : Op₂ S)
        (one  : T)
        (_*_  : Op₂ T)
        (f    : S → T)
        (g    : A → S)
        (hom-0-1 : f zero ≡ one)
        (hom-+-* : ∀ {x y} → f (x + y) ≡ f x * f y)
      → f (explore zero _+_ g) ≡ explore one _*_ (f ∘ g)
  lift-hom-≡ z _+_ o _*_ = LiftHom.lift-hom _≡_ ≡.refl ≡.trans z _+_ o _*_ (≡.ap₂ _*_)

  -- Since so far S and T should have the same level, we get this mess of resizing
  -- There is a later version based on ⟦explore⟧.
  module _ (f : A → 𝟚) {{_ : UA}} where
    lift-✓all-Πᵉ : ✓ (big-lift∧ ₁ f) ≡ Πᵉ explore (✓ ∘ f)
    lift-✓all-Πᵉ = lift-hom-≡ (lift 1₂) (lift-op₂ _∧_) (Lift 𝟙) _×_ (✓ ∘ lower) (lift ∘ f) (≡.! Lift≡id) (✓-∧-× _ _)

  module _ (f : A → 𝟚) where
    lift-✓any↔Σᵉ : ✓ (big-lift∨ ₁ f) ↔ Σᵉ explore (✓ ∘ f)
    lift-✓any↔Σᵉ = LiftHom.lift-hom _↔_ (id , id) (zip (flip _∘′_) _∘′_)
                     (lift 0₂) (lift-op₂ _∨_) (Lift 𝟘) _⊎_
                     (zip ⊎-map ⊎-map) (✓ ∘ lower) (lift ∘ f)
                     ((λ()) , λ{(lift())}) (✓∨-⊎ , ⊎-✓∨)

  sum-ind : SumInd sum
  sum-ind P P0 P+ Pf = explore-ind (λ e → P (e 0 _+_)) P0 P+ Pf

  sum-ext : SumExt sum
  sum-ext = explore-ext 0 _+_

  sum-zero : SumZero sum
  sum-zero = explore-ε ℕ+.monoid

  sum-hom : SumHom sum
  sum-hom = explore-hom ℕ°.+-commutativeMonoid

  sum-mono : SumMono sum
  sum-mono = explore-mono _≤_ z≤n _+-mono_

  sum-swap' : SumSwap sum
  sum-swap' {sumᴮ = sᴮ} sᴮ-0 hom f =
    sum-ind (λ s → s (sᴮ ∘ f) ≡ sᴮ (s ∘ flip f))
            (! sᴮ-0)
            (λ p q → (ap₂ _+_ p q) ∙ (! hom _ _)) (λ _ → refl)
    where open ≡

  sum-lin : SumLin sum
  sum-lin f zero    = sum-zero
  sum-lin f (suc k) = ≡.trans (sum-hom f (λ x → k * f x)) (≡.ap₂ _+_ (≡.refl {x = sum f}) (sum-lin f k))

  sum-const : SumConst sum
  sum-const x = sum-ext (λ _ → ! snd ℕ°.*-identity x) ∙ sum-lin (const 1) x ∙ ℕ°.*-comm x Card
    where open ≡

  exploreStableUnder→sumStableUnder : ∀ {p} → StableUnder explore p → SumStableUnder sum p
  exploreStableUnder→sumStableUnder SU-p = SU-p 0 _+_

  count-ext : CountExt count
  count-ext f≗g = sum-ext (≡.cong 𝟚▹ℕ ∘ f≗g)

  sumStableUnder→countStableUnder : ∀ {p} → SumStableUnder sum p → CountStableUnder count p
  sumStableUnder→countStableUnder sumSU-p f = sumSU-p (𝟚▹ℕ ∘ f)

  diff-list = with-endo-monoid (List.monoid A) List.[_]

  {-
  list≡diff-list : list ≡ diff-list
  list≡diff-list = {!explore-endo-monoid-spec (List.monoid A) List.[_]!}
  -}

  lift-sum : ∀ ℓ → Sum A
  lift-sum ℓ f = lower {₀} {ℓ} (explore (lift 0) (lift-op₂ _+_) (lift ∘ f))

  Fin-lower-sum≡Σᵉ-Fin : ∀ {{_ : UA}}(f : A → ℕ) → Fin (lift-sum _ f) ≡ Σᵉ explore (Fin ∘ f)
  Fin-lower-sum≡Σᵉ-Fin f = lift-hom-≡ (lift 0) (lift-op₂ _+_) (Lift 𝟘) _⊎_ (Fin ∘ lower) (lift ∘ f) (Fin0≡𝟘 ∙ ! Lift≡id) (! Fin-⊎-+)
    where open ≡

module FromTwoExploreInd
    {a} {A : ★ a}
    {eᴬ : ∀ {ℓ} → Explore ℓ A}
    (eᴬ-ind : ∀ {p ℓ} → ExploreInd {ℓ} p eᴬ)
    {b} {B : ★ b}
    {eᴮ : ∀ {ℓ} → Explore ℓ B}
    (eᴮ-ind : ∀ {p ℓ} → ExploreInd {ℓ} p eᴮ)
    where

    module A = FromExploreInd eᴬ-ind
    module B = FromExploreInd eᴮ-ind

    module _ {c ℓ}(cm : CommutativeMonoid c ℓ) where
        open CMon cm

        opᴬ = eᴬ ε _∙_
        opᴮ = eᴮ ε _∙_

        -- TODO use lift-hom
        explore-swap' : ∀ f → opᴬ (opᴮ ∘ f) ≈ opᴮ (opᴬ ∘ flip f)
        explore-swap' = A.explore-swap m (B.explore-ε m) (B.explore-hom cm)

    sum-swap : ∀ f → A.sum (B.sum ∘ f) ≡ B.sum (A.sum ∘ flip f)
    sum-swap = explore-swap' ℕ°.+-commutativeMonoid

module FromTwoAdequate-sum
  {{_ : UA}}{{_ : FunExt}}
  {A}{B}
  {sumᴬ : Sum A}{sumᴮ : Sum B}
  (open Adequacy _≡_)
  (sumᴬ-adq : Adequate-sum sumᴬ)
  (sumᴮ-adq : Adequate-sum sumᴮ) where

  open ≡
  sumStableUnder : (p : A ≃ B)(f : B → ℕ)
                 → sumᴬ (f ∘ ·→ p) ≡ sumᴮ f
  sumStableUnder p f = Fin-injective (sumᴬ-adq (f ∘ ·→ p)
                                      ∙ Σ-fst≃ p _
                                      ∙ ! sumᴮ-adq f)

  sumStableUnder′ : (p : A ≃ B)(f : A → ℕ)
                 → sumᴬ f ≡ sumᴮ (f ∘ <– p)
  sumStableUnder′ p f = Fin-injective (sumᴬ-adq f
                                      ∙ Σ-fst≃′ p _
                                      ∙ ! sumᴮ-adq (f ∘ <– p))

module FromAdequate-sum
  {A}
  {sum : Sum A}
  (open Adequacy _≡_)
  (sum-adq : Adequate-sum sum)
  {{_ : UA}}{{_ : FunExt}}
  where

  open FromTwoAdequate-sum sum-adq sum-adq public
  open ≡

  sum-ext : SumExt sum
  sum-ext = ap sum ∘ λ=

  private
    count : Count A
    count f = sum (𝟚▹ℕ ∘ f)

  private
    module M {p q : A → 𝟚}(same-count : count p ≡ count q) where
      private

        P = λ x → p x ≡ 1₂
        Q = λ x → q x ≡ 1₂
        ¬P = λ x → p x ≡ 0₂
        ¬Q = λ x → q x ≡ 0₂

        π : Σ A P ≡ Σ A Q
        π = ! Σ=′ _ (count-≡ p)
            ∙ ! (sum-adq (𝟚▹ℕ ∘ p))
            ∙ ap Fin same-count
            ∙ sum-adq (𝟚▹ℕ ∘ q)
            ∙ Σ=′ _ (count-≡ q)

        lem1 : ∀ px qx → 𝟚▹ℕ qx ≡ (𝟚▹ℕ (px ∧ qx)) + 𝟚▹ℕ (not px) * 𝟚▹ℕ qx
        lem1 1₂ 1₂ = ≡.refl
        lem1 1₂ 0₂ = ≡.refl
        lem1 0₂ 1₂ = ≡.refl
        lem1 0₂ 0₂ = ≡.refl

        lem2 : ∀ px qx → 𝟚▹ℕ px ≡ (𝟚▹ℕ (px ∧ qx)) + 𝟚▹ℕ px * 𝟚▹ℕ (not qx)
        lem2 1₂ 1₂ = ≡.refl
        lem2 1₂ 0₂ = ≡.refl
        lem2 0₂ 1₂ = ≡.refl
        lem2 0₂ 0₂ = ≡.refl

        lemma1 : ∀ px qx → (qx ≡ 1₂) ≡ (Fin (𝟚▹ℕ (px ∧ qx)) ⊎ (px ≡ 0₂ × qx ≡ 1₂))
        lemma1 px qx = ! Fin-≡-≡1₂ qx
                     ∙ ap Fin (lem1 px qx)
                     ∙ ! Fin-⊎-+
                     ∙ ⊎= refl (! Fin-×-* ∙ ×= (Fin-≡-≡0₂ px) (Fin-≡-≡1₂ qx))

        lemma2 : ∀ px qx → (Fin (𝟚▹ℕ (px ∧ qx)) ⊎ (px ≡ 1₂ × qx ≡ 0₂)) ≡ (px ≡ 1₂)
        lemma2 px qx = ! ⊎= refl (! Fin-×-* ∙ ×= (Fin-≡-≡1₂ px) (Fin-≡-≡0₂ qx)) ∙ Fin-⊎-+ ∙ ap Fin (! lem2 px qx) ∙ Fin-≡-≡1₂ px

        π' : (Fin (sum (λ x → 𝟚▹ℕ (p x ∧ q x))) ⊎ Σ A (λ x →  P x × ¬Q x))
           ≡ (Fin (sum (λ x → 𝟚▹ℕ (p x ∧ q x))) ⊎ Σ A (λ x → ¬P x ×  Q x))
        π' = ⊎= (sum-adq (λ x → 𝟚▹ℕ (p x ∧ q x))) refl
           ∙ ! Σ⊎-split
           ∙ Σ=′ _ (λ x → lemma2 (p x) (q x))
           ∙ π
           ∙ Σ=′ _ (λ x → lemma1 (p x) (q x))
           ∙ Σ⊎-split
           ∙ ! ⊎= (sum-adq (λ x → 𝟚▹ℕ (p x ∧ q x))) refl

        π'' : Σ A (P ×° ¬Q) ≡ Σ A (¬P ×° Q)
        π'' = Fin⊎-injective (sum (λ x → 𝟚▹ℕ (p x ∧ q x))) π'

      open EquivalentSubsets π'' public

  same-count→iso : ∀{p q : A → 𝟚}(same-count : count p ≡ count q) → p ≡ q ∘ M.π {p} {q} same-count
  same-count→iso {p} {q} sc = M.prop {p} {q} sc

module From⟦Explore⟧
    {-a-} {A : ★₀ {- a-}}
    {explore   : ∀ {ℓ} → Explore ℓ A}
    (⟦explore⟧ : ∀ {ℓ₀ ℓ₁} ℓᵣ → ⟦Explore⟧ {ℓ₀} {ℓ₁} ℓᵣ _≡_ explore explore)
    {{_ : UA}}
    where
  open FromExplore explore

  module AlsoInFromExploreInd
          {ℓ}(M : Monoid ℓ ℓ)
             (open Mon M)
             (f : A → C)
             where
        explore-endo-monoid-spec′ : ∀ z → explore ε _∙_ f ∙ z ≈ explore-endo explore z _∙_ f
        explore-endo-monoid-spec′ = ⟦explore⟧ ₀ {C} {C → C}
                                              (λ r s → ∀ z → r ∙ z ≈ s z)
                                              (fst identity)
                                              (λ P₀ P₁ z → trans (assoc _ _ _) (trans (∙-cong refl (P₁ z)) (P₀ _)))
                                              (λ xᵣ _ → ∙-cong (reflexive (≡.ap f xᵣ)) refl)

        explore-endo-monoid-spec : with-monoid M f ≈ with-endo-monoid M f
        explore-endo-monoid-spec = trans (! snd identity _) (explore-endo-monoid-spec′ ε)

  open ≡
  module _ (f : A → ℕ) where
    sum⇒Σᵉ : Fin (explore 0 _+_ f) ≡ explore (Lift 𝟘) _⊎_ (Fin ∘ f)
    sum⇒Σᵉ = ⟦explore⟧ {₀} {₁} ₁
                (λ n X → Fin n ≡ X)
                (Fin0≡𝟘 ∙ ! Lift≡id)
                (λ p q → ! Fin-⊎-+ ∙ ⊎= p q)
                (ap (Fin ∘ f))

    product⇒Πᵉ : Fin (explore 1 _*_ f) ≡ explore (Lift 𝟙) _×_ (Fin ∘ f)
    product⇒Πᵉ = ⟦explore⟧ {₀} {₁} ₁
                    (λ n X → Fin n ≡ X)
                    (Fin1≡𝟙 ∙ ! Lift≡id)
                    (λ p q → ! Fin-×-* ∙ ×= p q)
                    (ap (Fin ∘ f))

  module _ (f : A → 𝟚) where
    ✓all-Πᵉ : ✓ (all f) ≡ Πᵉ explore (✓ ∘ f)
    ✓all-Πᵉ = ⟦explore⟧ {₀} {₁} ₁
                (λ b X → ✓ b ≡ X)
                (! Lift≡id)
                (λ p q → ✓-∧-× _ _ ∙ ×= p q)
                (ap (✓ ∘ f))

    ✓any→Σᵉ : ✓ (any f) → Σᵉ explore (✓ ∘ f)
    ✓any→Σᵉ p = ⟦explore⟧ {₀} {ₛ ₀} ₁
                         (λ b (X : ★₀) → Lift (✓ b) → X)
                         (λ x → lift (lower x))
                         (λ { {0₂} {x₁} xᵣ {y₀} {y₁} yᵣ zᵣ → inr (yᵣ zᵣ)
                            ; {1₂} {x₁} xᵣ {y₀} {y₁} yᵣ zᵣ → inl (xᵣ _) })
                         (λ xᵣ x → tr (✓ ∘ f) xᵣ (lower x)) (lift p)

  module FromAdequate-Σᵉ
           (adequate-Σᵉ : ∀ {ℓ} → Adequate-Σ {ℓ} (Σᵉ explore))
          where
    open Adequacy

    adequate-sum : Adequate-sum _≡_ sum
    adequate-sum f = sum⇒Σᵉ f ∙ adequate-Σᵉ (Fin ∘ f)

    open FromAdequate-sum adequate-sum public

    adequate-any : Adequate-any -→- any
    adequate-any f e = coe (adequate-Σᵉ (✓ ∘ f)) (✓any→Σᵉ f e)

  module FromAdequate-Πᵉ
           (adequate-Πᵉ : ∀ {ℓ} → Adequate-Π {ℓ} (Πᵉ explore))
          where
    open Adequacy

    adequate-product : Adequate-product _≡_ product
    adequate-product f = product⇒Πᵉ f ∙ adequate-Πᵉ (Fin ∘ f)

    adequate-all : Adequate-all _≡_ all
    adequate-all f = ✓all-Πᵉ f ∙ adequate-Πᵉ _

    check! : (f : A → 𝟚) {pf : ✓ (all f)} → (∀ x → ✓ (f x))
    check! f {pf} = coe (adequate-all f) pf

{-
module ExplorableRecord where
    record Explorable A : ★₁ where
      constructor mk
      field
        explore     : Explore₀ A
        explore-ind : ExploreInd₀ explore

      open FromExploreInd explore-ind
      field
        adequate-sum     : Adequate-sum sum
    --  adequate-product : AdequateProduct product

      open FromExploreInd explore-ind public

    open Explorable public

    ExploreForFun : ★₀ → ★₁
    ExploreForFun A = ∀ {X} → Explorable X → Explorable (A → X)

    record Funable A : ★₂ where
      constructor _,_
      field
        explorable : Explorable A
        negative   : ExploreForFun A

    module DistFun {A} (μA : Explorable A)
                       (μA→ : ExploreForFun A)
                       {B} (μB : Explorable B){X}
                       (_≈_ : X → X → ★ ₀)
                       (0′  : X)
                       (_+_ : X → X → X)
                       (_*_ : X → X → X) where

      Σᴮ = explore μB 0′ _+_
      Π' = explore μA 0′ _*_
      Σ' = explore (μA→ μB) 0′ _+_

      DistFun = ∀ f → Π' (Σᴮ ∘ f) ≈ Σ' (Π' ∘ _ˢ_ f)

    DistFun : ∀ {A} → Explorable A → ExploreForFun A → ★₁
    DistFun μA μA→ = ∀ {B} (μB : Explorable B) c → let open CMon {₀}{₀} c in
                       ∀ _*_ → Zero _≈_ ε _*_ → _DistributesOver_ _≈_ _*_ _∙_ → _*_ Preserves₂ _≈_ ⟶ _≈_ ⟶ _≈_
                       → DistFun.DistFun μA μA→ μB _≈_ ε _∙_ _*_

    DistFunable : ∀ {A} → Funable A → ★₁
    DistFunable (μA , μA→) = DistFun μA μA→

    module _ {{_ : UA}}{{_ : FunExt}} where
        μ-iso : ∀ {A B} → (A ≃ B) → Explorable A → Explorable B
        μ-iso {A}{B} A≃B μA = mk (EM.map _ A→B (explore μA)) (EM.map-ind _ A→B (explore-ind μA)) ade
          where
            open ≡
            A→B = –> A≃B
            ade = λ f → adequate-sum μA (f ∘ A→B) ∙ Σ-fst≃ A≃B _

        -- I guess this could be more general
        μ-iso-preserve : ∀ {A B} (A≃B : A ≃ B) f (μA : Explorable A) → sum μA f ≡ sum (μ-iso A≃B μA) (f ∘ <– A≃B)
        μ-iso-preserve A≃B f μA = sum-ext μA (λ x → ap f (! (<–-inv-l A≃B x)))
          where open ≡

          {-
        μLift : ∀ {A} → Explorable A → Explorable (Lift A)
        μLift = μ-iso {!(! Lift↔id)!}
          where open ≡
          -}
-- -}
-- -}
-- -}
-- -}


open import MJ.Types as Types
import MJ.Classtable.Core as Core

module MJ.Semantics.Environments {c}(Ct : Core.Classtable c) where

open import Prelude
open import Data.List
open import Data.List.Membership.Propositional
open import Data.List.Relation.Unary.All as All
open import Data.List.Relation.Unary.Any

open import MJ.LexicalScope c

{-
We equip MJ with mutable lexical environments.
We could choose to model this directly, moving the environment from
the Reader part of the evaluation monad to the State part.

Instead we choose to keep our environments immutable and model
mutability of the values in it by an indirection via the mutable store.
This greatly simplifies the treatment of environments in the interpreter
and keeps the representation lightweight, even though we support block scopes.
-}
Env : ∀ (Γ : Ctx)(W : World c) → Set
Env Γ W = All (λ a → vty a ∈ W) Γ

_⊕_ : ∀ {Γ W a} → Env Γ W → (vty a) ∈ W → Env (Γ +local a) W
_⊕_ E v = v ∷ E

open import Data.List.Any
getvar : ∀ {Γ W a} → Var Γ a → Env Γ W → vty a ∈ W
getvar px E = All.lookup E px


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

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

module Data.List.Relation.Unary.AllPairs.Properties where

open import Data.List hiding (any)
open import Data.List.Relation.Unary.All as All using (All; []; _∷_)
import Data.List.Relation.Unary.All.Properties as All
open import Data.List.Relation.Unary.AllPairs as AllPairs using (AllPairs; []; _∷_)
open import Data.Bool.Base using (true; false)
open import Data.Fin.Base using (Fin)
open import Data.Fin.Properties using (suc-injective)
open import Data.Nat.Base using (zero; suc; _<_; z≤n; s≤s)
open import Data.Nat.Properties using (≤-refl; ≤-step)
open import Function using (_∘_; flip)
open import Relation.Binary using (Rel; DecSetoid)
open import Relation.Binary.PropositionalEquality using (_≢_)
open import Relation.Unary using (Pred; Decidable)
open import Relation.Nullary using (does)

------------------------------------------------------------------------
-- Introduction (⁺) and elimination (⁻) rules for list operations
------------------------------------------------------------------------
-- map

module _ {a b ℓ} {A : Set a} {B : Set b} {R : Rel A ℓ} {f : B → A} where

  map⁺ : ∀ {xs} → AllPairs (λ x y → R (f x) (f y)) xs →
         AllPairs R (map f xs)
  map⁺ []           = []
  map⁺ (x∉xs ∷ xs!) = All.map⁺ x∉xs ∷ map⁺ xs!

------------------------------------------------------------------------
-- ++

module _ {a ℓ} {A : Set a} {R : Rel A ℓ} where

  ++⁺ : ∀ {xs ys} → AllPairs R xs → AllPairs R ys →
        All (λ x → All (R x) ys) xs → AllPairs R (xs ++ ys)
  ++⁺ []         Rys _              = Rys
  ++⁺ (px ∷ Rxs) Rys (Rxys ∷ Rxsys) = All.++⁺ px Rxys ∷ ++⁺ Rxs Rys Rxsys

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

module _ {a ℓ} {A : Set a} {R : Rel A ℓ} where

  concat⁺ : ∀ {xss} → All (AllPairs R) xss →
            AllPairs (λ xs ys → All (λ x → All (R x) ys) xs) xss →
            AllPairs R (concat xss)
  concat⁺ []           []              = []
  concat⁺ (pxs ∷ pxss) (Rxsxss ∷ Rxss) = ++⁺ pxs (concat⁺ pxss Rxss)
    (All.map All.concat⁺ (All.All-swap Rxsxss))

------------------------------------------------------------------------
-- take and drop

module _ {a ℓ} {A : Set a} {R : Rel A ℓ} where

  take⁺ : ∀ {xs} n → AllPairs R xs → AllPairs R (take n xs)
  take⁺ zero    pxs        = []
  take⁺ (suc n) []         = []
  take⁺ (suc n) (px ∷ pxs) = All.take⁺ n px ∷ take⁺ n pxs

  drop⁺ : ∀ {xs} n → AllPairs R xs → AllPairs R (drop n xs)
  drop⁺ zero    pxs       = pxs
  drop⁺ (suc n) []        = []
  drop⁺ (suc n) (_ ∷ pxs) = drop⁺ n pxs

------------------------------------------------------------------------
-- applyUpTo

module _ {a ℓ} {A : Set a} {R : Rel A ℓ} where

  applyUpTo⁺₁ : ∀ f n → (∀ {i j} → i < j → j < n → R (f i) (f j)) → AllPairs R (applyUpTo f n)
  applyUpTo⁺₁ f zero    Rf = []
  applyUpTo⁺₁ f (suc n) Rf =
    All.applyUpTo⁺₁ _ n (Rf (s≤s z≤n) ∘ s≤s) ∷
    applyUpTo⁺₁ _ n (λ i≤j j<n → Rf (s≤s i≤j) (s≤s j<n))

  applyUpTo⁺₂ : ∀ f n → (∀ i j → R (f i) (f j)) → AllPairs R (applyUpTo f n)
  applyUpTo⁺₂ f n Rf = applyUpTo⁺₁ f n (λ _ _ → Rf _ _)

------------------------------------------------------------------------
-- applyDownFrom

module _ {a ℓ} {A : Set a} {R : Rel A ℓ} where

  applyDownFrom⁺₁ : ∀ f n → (∀ {i j} → j < i → i < n → R (f i) (f j)) →
                    AllPairs R (applyDownFrom f n)
  applyDownFrom⁺₁ f zero    Rf = []
  applyDownFrom⁺₁ f (suc n) Rf =
    All.applyDownFrom⁺₁ _ n (flip Rf ≤-refl)  ∷
    applyDownFrom⁺₁ f n (λ j<i i<n → Rf j<i (≤-step i<n))

  applyDownFrom⁺₂ : ∀ f n → (∀ i j → R (f i) (f j)) → AllPairs R (applyDownFrom f n)
  applyDownFrom⁺₂ f n Rf = applyDownFrom⁺₁ f n (λ _ _ → Rf _ _)

------------------------------------------------------------------------
-- tabulate

module _ {a ℓ} {A : Set a} {R : Rel A ℓ} where

  tabulate⁺ : ∀ {n} {f : Fin n → A} → (∀ {i j} → i ≢ j → R (f i) (f j)) →
              AllPairs R (tabulate f)
  tabulate⁺ {zero}  fᵢ~fⱼ = []
  tabulate⁺ {suc n} fᵢ~fⱼ =
    All.tabulate⁺ (λ j → fᵢ~fⱼ λ()) ∷
    tabulate⁺ (fᵢ~fⱼ ∘ (_∘ suc-injective))

------------------------------------------------------------------------
-- filter

module _ {a ℓ p} {A : Set a} {R : Rel A ℓ}
         {P : Pred A p} (P? : Decidable P) where

  filter⁺ : ∀ {xs} → AllPairs R xs → AllPairs R (filter P? xs)
  filter⁺ {_}      []           = []
  filter⁺ {x ∷ xs} (x∉xs ∷ xs!) with does (P? x)
  ... | false = filter⁺ xs!
  ... | true  = All.filter⁺ P? x∉xs ∷ filter⁺ xs!


-- Basic intuitionistic modal logic S4, without ∨, ⊥, or ◇.
-- Gentzen-style formalisation of syntax, after Bierman-de Paiva.
-- Simple terms.

module BasicIS4.Syntax.Gentzen where

open import BasicIS4.Syntax.Common public

-- Derivations.

mutual
  infix 3 _⊢_
  data _⊢_ (Γ : Cx Ty) : Ty → Set where
    var      : ∀ {A}   → A ∈ Γ → Γ ⊢ A
    lam      : ∀ {A B} → Γ , A ⊢ B → Γ ⊢ A ▻ B
    app      : ∀ {A B} → Γ ⊢ A ▻ B → Γ ⊢ A → Γ ⊢ B
    multibox : ∀ {A Δ} → Γ ⊢⋆ □⋆ Δ → □⋆ Δ ⊢ A → Γ ⊢ □ A
    down     : ∀ {A}   → Γ ⊢ □ A → Γ ⊢ A
    pair     : ∀ {A B} → Γ ⊢ A → Γ ⊢ B → Γ ⊢ A ∧ B
    fst      : ∀ {A B} → Γ ⊢ A ∧ B → Γ ⊢ A
    snd      : ∀ {A B} → Γ ⊢ A ∧ B → Γ ⊢ B
    unit     : Γ ⊢ ⊤

  infix 3 _⊢⋆_
  _⊢⋆_ : Cx Ty → Cx Ty → Set
  Γ ⊢⋆ ∅     = 𝟙
  Γ ⊢⋆ Ξ , A = Γ ⊢⋆ Ξ × Γ ⊢ A

-- Monotonicity with respect to context inclusion.

mutual
  mono⊢ : ∀ {A Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢ A → Γ′ ⊢ A
  mono⊢ η (var i)         = var (mono∈ η i)
  mono⊢ η (lam t)         = lam (mono⊢ (keep η) t)
  mono⊢ η (app t u)       = app (mono⊢ η t) (mono⊢ η u)
  mono⊢ η (multibox ts u) = multibox (mono⊢⋆ η ts) u
  mono⊢ η (down t)        = down (mono⊢ η t)
  mono⊢ η (pair t u)      = pair (mono⊢ η t) (mono⊢ η u)
  mono⊢ η (fst t)         = fst (mono⊢ η t)
  mono⊢ η (snd t)         = snd (mono⊢ η t)
  mono⊢ η unit            = unit

  mono⊢⋆ : ∀ {Ξ Γ Γ′} → Γ ⊆ Γ′ → Γ ⊢⋆ Ξ → Γ′ ⊢⋆ Ξ
  mono⊢⋆ {∅}     η ∙        = ∙
  mono⊢⋆ {Ξ , A} η (ts , t) = mono⊢⋆ η ts , mono⊢ η t

-- Shorthand for variables.

v₀ : ∀ {A Γ} → Γ , A ⊢ A
v₀ = var i₀

v₁ : ∀ {A B Γ} → Γ , A , B ⊢ A
v₁ = var i₁

v₂ : ∀ {A B C Γ} → Γ , A , B , C ⊢ A
v₂ = var i₂

-- Reflexivity.

refl⊢⋆ : ∀ {Γ} → Γ ⊢⋆ Γ
refl⊢⋆ {∅}     = ∙
refl⊢⋆ {Γ , A} = mono⊢⋆ weak⊆ refl⊢⋆ , v₀

-- Deduction theorem is built-in.

lam⋆ : ∀ {Ξ Γ A} → Γ ⧺ Ξ ⊢ A → Γ ⊢ Ξ ▻⋯▻ A
lam⋆ {∅}     = I
lam⋆ {Ξ , B} = lam⋆ {Ξ} ∘ lam

lam⋆₀ : ∀ {Γ A} → Γ ⊢ A → ∅ ⊢ Γ ▻⋯▻ A
lam⋆₀ {∅}     = I
lam⋆₀ {Γ , B} = lam⋆₀ ∘ lam

-- Detachment theorem.

det : ∀ {A B Γ} → Γ ⊢ A ▻ B → Γ , A ⊢ B
det t = app (mono⊢ weak⊆ t) v₀

det⋆ : ∀ {Ξ Γ A} → Γ ⊢ Ξ ▻⋯▻ A → Γ ⧺ Ξ ⊢ A
det⋆ {∅}     = I
det⋆ {Ξ , B} = det ∘ det⋆ {Ξ}

det⋆₀ : ∀ {Γ A} → ∅ ⊢ Γ ▻⋯▻ A → Γ ⊢ A
det⋆₀ {∅}     = I
det⋆₀ {Γ , B} = det ∘ det⋆₀

-- Cut and multicut.

cut : ∀ {A B Γ} → Γ ⊢ A → Γ , A ⊢ B → Γ ⊢ B
cut t u = app (lam u) t

multicut : ∀ {Ξ A Γ} → Γ ⊢⋆ Ξ → Ξ ⊢ A → Γ ⊢ A
multicut {∅}     ∙        u = mono⊢ bot⊆ u
multicut {Ξ , B} (ts , t) u = app (multicut ts (lam u)) t

-- Transitivity.

trans⊢⋆ : ∀ {Γ″ Γ′ Γ} → Γ ⊢⋆ Γ′ → Γ′ ⊢⋆ Γ″ → Γ ⊢⋆ Γ″
trans⊢⋆ {∅}      ts ∙        = ∙
trans⊢⋆ {Γ″ , A} ts (us , u) = trans⊢⋆ ts us , multicut ts u

-- Contraction.

ccont : ∀ {A B Γ} → Γ ⊢ (A ▻ A ▻ B) ▻ A ▻ B
ccont = lam (lam (app (app v₁ v₀) v₀))

cont : ∀ {A B Γ} → Γ , A , A ⊢ B → Γ , A ⊢ B
cont t = det (app ccont (lam (lam t)))

-- Exchange, or Schönfinkel’s C combinator.

cexch : ∀ {A B C Γ} → Γ ⊢ (A ▻ B ▻ C) ▻ B ▻ A ▻ C
cexch = lam (lam (lam (app (app v₂ v₀) v₁)))

exch : ∀ {A B C Γ} → Γ , A , B ⊢ C → Γ , B , A ⊢ C
exch t = det (det (app cexch (lam (lam t))))

-- Composition, or Schönfinkel’s B combinator.

ccomp : ∀ {A B C Γ} → Γ ⊢ (B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
ccomp = lam (lam (lam (app v₂ (app v₁ v₀))))

comp : ∀ {A B C Γ} → Γ , B ⊢ C → Γ , A ⊢ B → Γ , A ⊢ C
comp t u = det (app (app ccomp (lam t)) (lam u))

-- Useful theorems in functional form.

dist : ∀ {A B Γ} → Γ ⊢ □ (A ▻ B) → Γ ⊢ □ A → Γ ⊢ □ B
dist t u = multibox ((∙ , t) , u) (app (down v₁) (down v₀))

up : ∀ {A Γ} → Γ ⊢ □ A → Γ ⊢ □ □ A
up t = multibox (∙ , t) v₀

distup : ∀ {A B Γ} → Γ ⊢ □ (□ A ▻ B) → Γ ⊢ □ A → Γ ⊢ □ B
distup t u = dist t (up u)

box : ∀ {A Γ} → ∅ ⊢ A → Γ ⊢ □ A
box t = multibox ∙ t

unbox : ∀ {A C Γ} → Γ ⊢ □ A → Γ , □ A ⊢ C → Γ ⊢ C
unbox t u = app (lam u) t

-- Useful theorems in combinatory form.

ci : ∀ {A Γ} → Γ ⊢ A ▻ A
ci = lam v₀

ck : ∀ {A B Γ} → Γ ⊢ A ▻ B ▻ A
ck = lam (lam v₁)

cs : ∀ {A B C Γ} → Γ ⊢ (A ▻ B ▻ C) ▻ (A ▻ B) ▻ A ▻ C
cs = lam (lam (lam (app (app v₂ v₀) (app v₁ v₀))))

cdist : ∀ {A B Γ} → Γ ⊢ □ (A ▻ B) ▻ □ A ▻ □ B
cdist = lam (lam (dist v₁ v₀))

cup : ∀ {A Γ} → Γ ⊢ □ A ▻ □ □ A
cup = lam (up v₀)

cdown : ∀ {A Γ} → Γ ⊢ □ A ▻ A
cdown = lam (down v₀)

cdistup : ∀ {A B Γ} → Γ ⊢ □ (□ A ▻ B) ▻ □ A ▻ □ B
cdistup = lam (lam (dist v₁ (up v₀)))

cunbox : ∀ {A C Γ} → Γ ⊢ □ A ▻ (□ A ▻ C) ▻ C
cunbox = lam (lam (app v₀ v₁))

cpair : ∀ {A B Γ} → Γ ⊢ A ▻ B ▻ A ∧ B
cpair = lam (lam (pair v₁ v₀))

cfst : ∀ {A B Γ} → Γ ⊢ A ∧ B ▻ A
cfst = lam (fst v₀)

csnd : ∀ {A B Γ} → Γ ⊢ A ∧ B ▻ B
csnd = lam (snd v₀)

-- Internalisation, or lifting, and additional theorems.

lift : ∀ {Γ A} → Γ ⊢ A → □⋆ Γ ⊢ □ A
lift {∅}     t = box t
lift {Γ , B} t = det (app cdist (lift (lam t)))

hypup : ∀ {A B Γ} → Γ ⊢ A ▻ B → Γ ⊢ □ A ▻ B
hypup t = lam (app (mono⊢ weak⊆ t) (down v₀))

hypdown : ∀ {A B Γ} → Γ ⊢ □ □ A ▻ B → Γ ⊢ □ A ▻ B
hypdown t = lam (app (mono⊢ weak⊆ t) (up v₀))

cxup : ∀ {Γ A} → Γ ⊢ A → □⋆ Γ ⊢ A
cxup {∅}     t = t
cxup {Γ , B} t = det (hypup (cxup (lam t)))

cxdown : ∀ {Γ A} → □⋆ □⋆ Γ ⊢ A → □⋆ Γ ⊢ A
cxdown {∅}     t = t
cxdown {Γ , B} t = det (hypdown (cxdown (lam t)))

box⋆ : ∀ {Ξ Γ} → ∅ ⊢⋆ Ξ → Γ ⊢⋆ □⋆ Ξ
box⋆ {∅}     ∙        = ∙
box⋆ {Ξ , A} (ts , t) = box⋆ ts , box t

lift⋆ : ∀ {Ξ Γ} → Γ ⊢⋆ Ξ → □⋆ Γ ⊢⋆ □⋆ Ξ
lift⋆ {∅}     ∙        = ∙
lift⋆ {Ξ , A} (ts , t) = lift⋆ ts , lift t

up⋆ : ∀ {Ξ Γ} → Γ ⊢⋆ □⋆ Ξ → Γ ⊢⋆ □⋆ □⋆ Ξ
up⋆ {∅}     ∙        = ∙
up⋆ {Ξ , A} (ts , t) = up⋆ ts , up t

down⋆ : ∀ {Ξ Γ} → Γ ⊢⋆ □⋆ Ξ → Γ ⊢⋆ Ξ
down⋆ {∅}     ∙        = ∙
down⋆ {Ξ , A} (ts , t) = down⋆ ts , down t

dist′ : ∀ {A B Γ} → Γ ⊢ □ (A ▻ B) → Γ ⊢ □ A ▻ □ B
dist′ t = lam (dist (mono⊢ weak⊆ t) v₀)

mpair : ∀ {A B Γ} → Γ ⊢ □ A → Γ ⊢ □ B → Γ ⊢ □ (A ∧ B)
mpair t u = multibox ((∙ , t) , u) (pair (down v₁) (down v₀))

mfst : ∀ {A B Γ} → Γ ⊢ □ (A ∧ B) → Γ ⊢ □ A
mfst t = multibox (∙ , t) (fst (down v₀))

msnd : ∀ {A B Γ} → Γ ⊢ □ (A ∧ B) → Γ ⊢ □ B
msnd t = multibox (∙ , t) (snd (down v₀))

-- Closure under context concatenation.

concat : ∀ {A B Γ} Γ′ → Γ , A ⊢ B → Γ′ ⊢ A → Γ ⧺ Γ′ ⊢ B
concat Γ′ t u = app (mono⊢ (weak⊆⧺₁ Γ′) (lam t)) (mono⊢ weak⊆⧺₂ u)

-- Substitution.

mutual
  [_≔_]_ : ∀ {A B Γ} → (i : A ∈ Γ) → Γ ∖ i ⊢ A → Γ ⊢ B → Γ ∖ i ⊢ B
  [ i ≔ s ] var j         with i ≟∈ j
  [ i ≔ s ] var .i        | same   = s
  [ i ≔ s ] var ._        | diff j = var j
  [ i ≔ s ] lam t         = lam ([ pop i ≔ mono⊢ weak⊆ s ] t)
  [ i ≔ s ] app t u       = app ([ i ≔ s ] t) ([ i ≔ s ] u)
  [ i ≔ s ] multibox ts u = multibox ([ i ≔ s ]⋆ ts) u
  [ i ≔ s ] down t        = down ([ i ≔ s ] t)
  [ i ≔ s ] pair t u      = pair ([ i ≔ s ] t) ([ i ≔ s ] u)
  [ i ≔ s ] fst t         = fst ([ i ≔ s ] t)
  [ i ≔ s ] snd t         = snd ([ i ≔ s ] t)
  [ i ≔ s ] unit          = unit

  [_≔_]⋆_ : ∀ {Ξ A Γ} → (i : A ∈ Γ) → Γ ∖ i ⊢ A → Γ ⊢⋆ Ξ → Γ ∖ i ⊢⋆ Ξ
  [_≔_]⋆_ {∅}     i s ∙        = ∙
  [_≔_]⋆_ {Ξ , B} i s (ts , t) = [ i ≔ s ]⋆ ts , [ i ≔ s ] t

-- Convertibility.

data _⋙_ {Γ : Cx Ty} : ∀ {A} → Γ ⊢ A → Γ ⊢ A → Set where
  refl⋙      : ∀ {A} → {t : Γ ⊢ A}
                      → t ⋙ t

  trans⋙     : ∀ {A} → {t t′ t″ : Γ ⊢ A}
                      → t ⋙ t′ → t′ ⋙ t″
                      → t ⋙ t″

  sym⋙       : ∀ {A} → {t t′ : Γ ⊢ A}
                      → t ⋙ t′
                      → t′ ⋙ t

  conglam⋙   : ∀ {A B} → {t t′ : Γ , A ⊢ B}
                        → t ⋙ t′
                        → lam t ⋙ lam t′

  congapp⋙   : ∀ {A B} → {t t′ : Γ ⊢ A ▻ B} → {u u′ : Γ ⊢ A}
                        → t ⋙ t′ → u ⋙ u′
                        → app t u ⋙ app t′ u′

  -- TODO: What about multibox?
  congdown⋙  : ∀ {A} → {t t′ : Γ ⊢ □ A}
                      → t ⋙ t′
                      → down t ⋙ down t′

  congpair⋙  : ∀ {A B} → {t t′ : Γ ⊢ A} → {u u′ : Γ ⊢ B}
                        → t ⋙ t′ → u ⋙ u′
                        → pair t u ⋙ pair t′ u′

  congfst⋙   : ∀ {A B} → {t t′ : Γ ⊢ A ∧ B}
                        → t ⋙ t′
                        → fst t ⋙ fst t′

  congsnd⋙   : ∀ {A B} → {t t′ : Γ ⊢ A ∧ B}
                        → t ⋙ t′
                        → snd t ⋙ snd t′

  beta▻⋙     : ∀ {A B} → {t : Γ , A ⊢ B} → {u : Γ ⊢ A}
                        → app (lam t) u ⋙ ([ top ≔ u ] t)

  eta▻⋙      : ∀ {A B} → {t : Γ ⊢ A ▻ B}
                        → t ⋙ lam (app (mono⊢ weak⊆ t) v₀)

  -- TODO: What about beta and eta for □?
  beta∧₁⋙    : ∀ {A B} → {t : Γ ⊢ A} → {u : Γ ⊢ B}
                        → fst (pair t u) ⋙ t

  beta∧₂⋙    : ∀ {A B} → {t : Γ ⊢ A} → {u : Γ ⊢ B}
                        → snd (pair t u) ⋙ u

  eta∧⋙      : ∀ {A B} → {t : Γ ⊢ A ∧ B}
                        → t ⋙ pair (fst t) (snd t)

  eta⊤⋙     : ∀ {t : Γ ⊢ ⊤} → t ⋙ unit


open import SOAS.Metatheory.Syntax

-- Second-order equational logic library
module SOAS.Metatheory.SecondOrder.Equality {T : Set} (Syn : Syntax {T}) where

open import SOAS.Common
open import SOAS.Families.Core {T}
open import SOAS.Context
open import SOAS.Variable

open import SOAS.Metatheory.FreeMonoid Syn

open import SOAS.Metatheory.SecondOrder.Metasubstitution Syn
open import SOAS.Metatheory.SecondOrder.Unit Syn

open import SOAS.ContextMaps.CategoryOfRenamings {T}
open import SOAS.Families.Build

open import SOAS.Metatheory Syn
open Syntax Syn
open Theory

private
  variable
    α β γ : T
    Γ Δ Π : Ctx
    𝔐 𝔑 : MCtx

-- Equational reasoning system extended from an axiom relation on terms
module EqLogic (_▹_⊢_≋ₐ_ : ∀ 𝔐 Γ {α} → (𝔐 ▷ 𝕋) α Γ → (𝔐 ▷ 𝕋) α Γ → Set) where

  -- Equality between terms: smallest equivalence relation closed under the axioms
  -- and parametrised metasubstitution
  data _▹_⊢_≋_ : (𝔐 : MCtx){α : T}(Γ : Ctx) → (𝔐 ▷ 𝕋) α Γ → (𝔐 ▷ 𝕋) α Γ → Set₁ where
    ax  : {t s : (𝔐 ▷ 𝕋) α Γ} → 𝔐 ▹ Γ ⊢ t ≋ₐ s →  𝔐 ▹ Γ ⊢ t ≋ s
    eq  : {t s : (𝔐 ▷ 𝕋) α Γ} → t ≡ s → 𝔐 ▹ Γ ⊢ t ≋ s
    sy  : {t s : (𝔐 ▷ 𝕋) α Γ} → 𝔐 ▹ Γ ⊢ t ≋ s → 𝔐 ▹ Γ ⊢ s ≋ t
    tr  : {t s u : (𝔐 ▷ 𝕋) α Γ} → 𝔐 ▹ Γ ⊢ t ≋ s → 𝔐 ▹ Γ ⊢ s ≋ u → 𝔐 ▹ Γ ⊢ t ≋ u
    □ms : {t s : (𝔐 ▷ 𝕋) α Γ}
        → 𝔐 ▹ Γ ⊢ t ≋ s
        → (ρ : Γ ↝ Δ)
        → (ζ ξ : MSub Δ 𝔐 𝔑)
        → (∀{τ Π}(𝔪 : Π ⊩ τ ∈ 𝔐) → 𝔑 ▹ (Π ∔ Δ) ⊢ (ix≀ ζ 𝔪) ≋ (ix≀ ξ 𝔪))
        → 𝔑 ▹ Δ ⊢ (□msub≀ t ρ ζ) ≋ (□msub≀ s ρ ξ)

  -- Reflexivity of ≋ lifted from refl
  rf : {t : (𝔐 ▷ 𝕋) α Γ} → 𝔐 ▹ Γ ⊢ t ≋ t
  rf = eq refl

  infix 1 _▹_⊢_≋_

  -- Helpers for equational reasoning
  module ≋-Reasoning where

    _≋⟨_⟩_ : (t : (𝔐 ▷ 𝕋) α Γ) {s u : (𝔐 ▷ 𝕋) α Γ}
           → 𝔐 ▹ Γ ⊢ t ≋ s
           → 𝔐 ▹ Γ ⊢ s ≋ u
           → 𝔐 ▹ Γ ⊢ t ≋ u
    t ≋⟨ t≋s ⟩ s≋u = tr {t = t} t≋s s≋u
    _≋⟨_⟩ₛ_ : (t : (𝔐 ▷ 𝕋) α Γ) {s u : (𝔐 ▷ 𝕋) α Γ}
           → 𝔐 ▹ Γ ⊢ s ≋ t
           → 𝔐 ▹ Γ ⊢ s ≋ u
           → 𝔐 ▹ Γ ⊢ t ≋ u
    t ≋⟨ s≋t ⟩ₛ s≋u = tr {t = t} (sy s≋t) s≋u

    _≡⟨_⟩_ : (t : (𝔐 ▷ 𝕋) α Γ){s : (𝔐 ▷ 𝕋) α Γ} {u : (𝔐 ▷ 𝕋) α Γ}
           → t ≡ s
           → 𝔐 ▹ Γ ⊢ s ≋ u
           → 𝔐 ▹ Γ ⊢ t ≋ u
    t ≡⟨ t≡s ⟩ s≋u = t ≋⟨ eq t≡s ⟩ s≋u

    _≡⟨⟩_ : (t : (𝔐 ▷ 𝕋) α Γ) {s : (𝔐 ▷ 𝕋) α Γ}
           → 𝔐 ▹ Γ ⊢ t ≋ s
           → 𝔐 ▹ Γ ⊢ t ≋ s
    t ≡⟨⟩ t≋s = t ≡⟨ refl ⟩ t≋s

    begin_ : {t s : (𝔐 ▷ 𝕋) α Γ} → 𝔐 ▹ Γ ⊢ t ≋ s → 𝔐 ▹ Γ ⊢ t ≋ s
    begin t≋s = t≋s

    _∎ : (t : (𝔐 ▷ 𝕋) α Γ) → 𝔐 ▹ Γ ⊢ t ≋ t
    t ∎ = rf

    infix  1 begin_
    infixr 2 _≋⟨_⟩_
    infixr 2 _≋⟨_⟩ₛ_
    infixr 2 _≡⟨_⟩_
    infixr 2 _≡⟨⟩_
    infix  3 _∎

  -- Rewrite two sides of ≋ with ≡
  rw : {t t′ s s′ : (𝔐 ▷ 𝕋) α Γ}
     → t ≡ t′ → s ≡ s′ → 𝔐 ▹ Γ ⊢ t ≋ s → 𝔐 ▹ Γ ⊢ t′ ≋ s′
  rw {t = t}{t′}{s}{s′} t≡t′ s≡s′ t≋s =
    begin t′ ≡⟨ sym t≡t′ ⟩ t ≋⟨ t≋s ⟩ s ≡⟨ s≡s′ ⟩ s′ ∎
      where open ≋-Reasoning

  -- Closure under base metasubstitution
  ms : {t s : (𝔐 ▷ 𝕋) α Γ}
     → 𝔐 ▹ Γ ⊢ t ≋ s
     → (ζ ξ : MSub Γ 𝔐 𝔑)
     → (∀{τ Π}(𝔪 : Π ⊩ τ ∈ 𝔐) → 𝔑 ▹ (Π ∔ Γ) ⊢ (ix≀ ζ 𝔪) ≋ (ix≀ ξ 𝔪))
     → 𝔑 ▹ Γ ⊢ msub≀ t ζ ≋ msub≀ s ξ
  ms {t = t}{s} t≋s ζ ξ ζ≋ξ = rw (□msub-id t (ix≀ ζ)) (□msub-id s (ix≀ ξ)) (□ms t≋s id ζ ξ ζ≋ξ)

  -- Metasubstitution of same mapping applied to two equivalent terms
  ms-eq : {t s : (𝔐 ▷ 𝕋) α Γ}
     → 𝔐 ▹ Γ ⊢ t ≋ s
     → (ζ : MSub Γ 𝔐 𝔑)
     → 𝔑 ▹ Γ ⊢ msub≀ t ζ ≋ msub≀ s ζ
  ms-eq {t = t}{s} t≋s ζ = ms t≋s ζ ζ (λ 𝔪 → rf)

  -- Closure under renaming
  ren≋ : {t s : (𝔐 ▷ 𝕋) α Γ}
         → 𝔐 ▹ Γ ⊢ t ≋ s
         → (ρ : Γ ↝ Δ)
         → 𝔐 ▹ Δ ⊢ 𝕣𝕖𝕟 ∥ 𝔐 ∥ t ρ ≋ 𝕣𝕖𝕟 ∥ 𝔐 ∥ s ρ
  ren≋ {𝔐}{α}{Γ}{Δ}{t = t}{s} t≋s ρ =
    begin
        (𝕣𝕖𝕟 ∥ 𝔐 ∥ t ρ)
    ≡⟨ sym (□msub-runit t ρ) ⟩
        □msub t ρ ms-unit
    ≡⟨ sym (cong (□msub t ρ) (iext (dext (id≀≈ms-unit Δ)))) ⟩
        □msub≀ t ρ (id≀ Δ)
    ≋⟨ □ms t≋s ρ (id≀ Δ) (id≀ Δ) (λ 𝔪 → rf) ⟩
        □msub≀ s ρ (id≀ Δ)
    ≡⟨ cong (□msub s ρ) (iext (dext (id≀≈ms-unit Δ))) ⟩
        □msub s ρ ms-unit
    ≡⟨ □msub-runit s ρ ⟩
        (𝕣𝕖𝕟 ∥ 𝔐 ∥ s ρ)
    ∎ where open ≋-Reasoning

  -- Lemma to apply equality to terms attached to the end of a metasubstitution
  ▹-eq : {Π′ : Ctx}{s u : (𝔑 ▷ 𝕋) β (Π′ ∔ Γ)}
       → 𝔑 ▹ (Π′ ∔ Γ) ⊢ s ≋ u → (ζ ξ : MSub Γ 𝔐 𝔑)
       → (∀{τ Π}(𝔪 : Π ⊩ τ ∈ 𝔐) → 𝔑 ▹ (Π ∔ Γ) ⊢ (ix≀ ζ 𝔪) ≋ (ix≀ ξ 𝔪))
       → (𝔪 : Π ⊩ α ∈ (𝔐 ⁅ Π′ ⊩ₙ β ⁆))
       → 𝔑 ▹ Π ∔ Γ ⊢ ix≀ (ζ ▹ s) 𝔪 ≋ ix≀ (ξ ▹ u) 𝔪
  ▹-eq e ◦ ◦ ζ≋ξ ↓ = e
  ▹-eq e (x ◃ ζ) (y ◃ ξ) ζ≋ξ ↓ = ζ≋ξ ↓
  ▹-eq e (x ◃ ζ) (y ◃ ξ) ζ≋ξ (↑ 𝔪) = ▹-eq e ζ ξ (λ 𝔫 → ζ≋ξ (↑ 𝔫)) 𝔪

  -- Congruence: metasubstitution of equivalent terms into a term extended with
  -- a new metavariable
  cong≋ : (t : (𝔐 ⁅ Π ⊩ₙ β ⁆ ▷ 𝕋) α Γ)
      → {s u : (𝔐 ▷ 𝕋) β (Π ∔ Γ)}
      → 𝔐 ▹ (Π ∔ Γ) ⊢ s ≋ u
      → 𝔐 ▹ Γ ⊢ instₑ t s ≋ instₑ t u
  cong≋ t {s} {u} s≋u = ms rf (id≀ _ ▹ s) (id≀ _ ▹ u) (▹-eq s≋u (id≀ _) (id≀ _) λ _ → rf)

  -- Double congruence
  cong₂≋ : {Π₁ Π₂ : Ctx}{β₁ β₂ : T}
        (t : ((𝔐 ⁅ Π₁ ⊩ₙ β₁ ⁆) ⁅ Π₂ ⊩ₙ β₂ ⁆ ▷ 𝕋) α Γ)
      → {s₁ u₁ : (𝔐 ▷ 𝕋) β₁ (Π₁ ∔ Γ)}
      → {s₂ u₂ : (𝔐 ▷ 𝕋) β₂ (Π₂ ∔ Γ)}
      → 𝔐 ▹ (Π₁ ∔ Γ) ⊢ s₁ ≋ u₁
      → 𝔐 ▹ (Π₂ ∔ Γ) ⊢ s₂ ≋ u₂
      → 𝔐 ▹ Γ ⊢ instₑ₂ t s₁ s₂ ≋ instₑ₂ t u₁ u₂
  cong₂≋ t {s₁}{u₁}{s₂}{u₂} s≋u₁ s≋u₂ =
    ms rf ((id≀ _ ▹ s₁) ▹ s₂) ((id≀ _ ▹ u₁) ▹ u₂)
    (▹-eq s≋u₂ (id≀ _ ▹ s₁) (id≀ _ ▹ u₁) (▹-eq s≋u₁ (id≀ _) (id≀ _) (λ - → rf)))

  -- Syntactic sugar
  cong[_]inside_ : {s u : (𝔐 ▷ 𝕋) β (Π ∔ Γ)}
      → 𝔐 ▹ (Π ∔ Γ) ⊢ s ≋ u
      → (t : (𝔐 ⁅ Π ⊩ₙ β ⁆ ▷ 𝕋) α Γ)
      → 𝔐 ▹ Γ ⊢ instₑ t s ≋ instₑ t u
  cong[ s≋u ]inside t = cong≋ t s≋u
  infix 05 cong[_]inside_
  cong₂[_][_]inside_ : {Π₁ Π₂ : Ctx}{β₁ β₂ : T}
      → {s₁ u₁ : (𝔐 ▷ 𝕋) β₁ (Π₁ ∔ Γ)}
      → {s₂ u₂ : (𝔐 ▷ 𝕋) β₂ (Π₂ ∔ Γ)}
      → 𝔐 ▹ (Π₁ ∔ Γ) ⊢ s₁ ≋ u₁
      → 𝔐 ▹ (Π₂ ∔ Γ) ⊢ s₂ ≋ u₂
      → (t : ((𝔐 ⁅ Π₁ ⊩ₙ β₁ ⁆) ⁅ Π₂ ⊩ₙ β₂ ⁆ ▷ 𝕋) α Γ)
      → 𝔐 ▹ Γ ⊢ instₑ₂ t s₁ s₂ ≋ instₑ₂ t u₁ u₂
  cong₂[ s≋u₁ ][ s≋u₂ ]inside t = cong₂≋ t s≋u₁ s≋u₂
  infix 05 cong₂[_][_]inside_

  -- Linear metasubstitution
  ○ms : {t s : (𝔐 ▷ 𝕋) α Γ}
     → 𝔐 ▹ Γ ⊢ t ≋ s
     → (ζ ξ : MSub Δ 𝔐 𝔑)
     → (∀{τ Π}(𝔪 : Π ⊩ τ ∈ 𝔐) → 𝔑 ▹ (Π ∔ Δ) ⊢ (ix≀ ζ 𝔪) ≋ (ix≀ ξ 𝔪))
     → 𝔑 ▹ Γ ∔ Δ ⊢ ○msub≀ t ζ ≋ ○msub≀ s ξ
  ○ms {Γ = Γ}{𝔑 = 𝔑}{t = t}{s} t≋s ζ ξ ζ≋ξ = □ms t≋s (inl Γ) (inr≀ Γ ζ) (inr≀ Γ ξ) (λ {τ}{Π} 𝔪 → begin
        ix≀ (inr≀ Γ ζ) 𝔪
    ≡⟨ ix-inr≀ ζ 𝔪 ⟩
        (𝕣𝕖𝕟 ∥ 𝔑 ∥ (ix≀ ζ 𝔪) (Π ∔∣ inr Γ))
    ≋⟨ ren≋ (ζ≋ξ 𝔪) (Π ∔∣ inr Γ) ⟩
        (𝕣𝕖𝕟 ∥ 𝔑 ∥ (ix≀ ξ 𝔪) (Π ∔∣ inr Γ))
    ≡⟨ sym (ix-inr≀ ξ 𝔪) ⟩
        ix≀ (inr≀ Γ ξ) 𝔪
    ∎) where open ≋-Reasoning

  -- Linear metasubstitution of same mapping applied to two equivalent terms
  ○ms-eq : {t s : (𝔐 ▷ 𝕋) α Π}
     → 𝔐 ▹ Π ⊢ t ≋ s
     → (ζ : MSub Γ 𝔐 𝔑)
     → 𝔑 ▹ Π ∔ Γ ⊢ ○msub≀ t ζ ≋ ○msub≀ s ζ
  ○ms-eq {t = t}{s} t≋s ζ = ○ms t≋s ζ ζ (λ 𝔪 → rf)

  -- Application of an axiom as a rewrite rule, and syntactic sugar
  ax≋ : {t s : (𝔐 ▷ 𝕋) α Π}
      → 𝔐 ▹ Π ⊢ t ≋ₐ s → (ζ : MSub Γ 𝔐 𝔑)
      → 𝔑 ▹ Π ∔ Γ ⊢ ○msub≀ t ζ ≋ ○msub≀ s ζ
  ax≋ a ζ = ○ms-eq (ax a) ζ

  ax_with《_ : {t s : (𝔐 ▷ 𝕋) α Π}
      → 𝔐 ▹ Π ⊢ t ≋ₐ s → (ζ : MSub Γ 𝔐 𝔑)
      → 𝔑 ▹ Π ∔ Γ ⊢ ○msub≀ t ζ ≋ ○msub≀ s ζ
  ax_with《_ = ax≋

  infix 15 ax_with《_

  -- Application of an equivalence as a rewrite rule, and syntactic sugar
  thm_with《_ : {t s : (𝔐 ▷ 𝕋) α Π}
     → 𝔐 ▹ Π ⊢ t ≋ s
     → (ζ : MSub Γ 𝔐 𝔑)
     → 𝔑 ▹ Π ∔ Γ ⊢ ○msub≀ t ζ ≋ ○msub≀ s ζ
  thm_with《_ = ○ms-eq
  infix 15 thm_with《_

  -- Application of theorem with no metavariables
  thm : {t s : (⁅⁆ ▷ 𝕋) α Π}
      → ⁅⁆ ▹ Π ⊢ t ≋ s
      → 𝔐 ▹ Π ∔ Γ ⊢ ○msub≀ t ◦ ≋ ○msub≀ s ◦
  thm e = thm e with《 ◦


module FStream.Clocked where

open import Library
open import FStream.Core

mutual
  record ClSF {i : Size} {ℓ₁ ℓ₂} {Cl : Set ℓ₂}  (C : Container ℓ₁) (cl : FStream C Cl) (A B : Set ℓ₂) : Set (ℓ₁ ⊔ ℓ₂) where
    inductive
    field
      inFCl : A → ⟦ C ⟧ (ClSF' {i} C cl A B)
  record ClSF' {i : Size} {ℓ₁ ℓ₂} {Cl : Set ℓ₂}  (C : Container ℓ₁) (cl : FStream C Cl) (A B : Set ℓ₂) : Set (ℓ₁ ⊔ ℓ₂) where
    coinductive
    field
      head : B
      tail : {j : Size< i} → ClSF {j} C cl A B
open FStream public
open FStream' public

_>>>_ : ∀ {i : Size} {ℓ₁ ℓ₂} {a b c Cl : Set ℓ₂} {C : Container ℓ₁} {cl : FStream C Cl} → ClSF {i} C cl a b → ClSF {i} C cl b c → ClSF {i} C cl a c
_>>>_ = {!!}

-- runClock : ∀ {i : Size} {ℓ₁ ℓ₂} {Cl B : Set ℓ₂} {C : Container ℓ₁} → (cl : FStream C Cl) → ClSF {i} C cl A B → FStream {i} C A -> FStream {i} C B
runClock : ∀ {i : Size} {ℓ₁ ℓ₂} {A B Cl : Set ℓ₂} {C : Container ℓ₁} → (cl : FStream C Cl) → ClSF {i} C cl A B → FStream {i} C A -> FStream {i} C B
runClock = {!!}


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

open import Base

module Algebra.FreeGroupAsReducedWords {i} (A : Set i) (eq : has-dec-eq A) where

A-is-set : is-set A
A-is-set = dec-eq-is-set eq

data word : Set i where
  ε : word
  _∷_ : A → word → word
  _′∷_ : A → word → word

is-reduced : word → Set i
is-reduced ε = unit
is-reduced (x ∷ ε) = unit
is-reduced (x ∷ (y ∷ w)) = is-reduced (y ∷ w)
is-reduced (x ∷ (y ′∷ w)) = (x ≢ y) × is-reduced (y ′∷ w)
is-reduced (x ′∷ ε) = unit
is-reduced (x ′∷ (y ∷ w)) = (x ≢ y) × is-reduced (y ∷ w)
is-reduced (x ′∷ (y ′∷ w)) = is-reduced (y ′∷ w)

is-reduced-is-prop : (w : word) → is-prop (is-reduced w)
is-reduced-is-prop ε = unit-is-prop
is-reduced-is-prop (x ∷ ε) = unit-is-prop
is-reduced-is-prop (x ∷ (y ∷ w)) = is-reduced-is-prop (y ∷ w)
is-reduced-is-prop (x ∷ (y ′∷ w)) =
  ×-is-truncated _ (Π-is-truncated _ (λ _ → λ ())) (is-reduced-is-prop (y ′∷ w))
is-reduced-is-prop (x ′∷ ε) = unit-is-prop
is-reduced-is-prop (x ′∷ (y ∷ w)) =
  ×-is-truncated _ (Π-is-truncated _ (λ _ → λ ())) (is-reduced-is-prop (y ∷ w))
is-reduced-is-prop (x ′∷ (y ′∷ w)) = is-reduced-is-prop (y ′∷ w)

reduced-word : Set i
reduced-word = Σ word is-reduced

word-total-path : {x y : A} (p : x ≡ y) {v w : word} (q : v ≡ w)
  → (x ∷ v ≡ y ∷ w)
word-total-path refl refl = refl

word'-total-path : {x y : A} (p : x ≡ y) {v w : word} (q : v ≡ w)
  → (x ′∷ v ≡ y ′∷ w)
word'-total-path refl refl = refl

-- The following six functions prove things like if [x ∷ v ≡ y ∷ w],
-- then [x ≡ y].
-- This is not as easy as it sounds, you cannot directly induct on the equality
-- (because [x ∷ v] is not a general element of type word), so you have to
-- extract the head, but it’s not always possible…

word-comp-path-type : (v w : word) → Set i
word-comp-path-type ε ε = unit
word-comp-path-type ε (y ∷ w) = ⊥
word-comp-path-type ε (y ′∷ w) = ⊥
word-comp-path-type (x ∷ v) ε = ⊥
word-comp-path-type (x ∷ v) (y ∷ w) = (x ≡ y) × (v ≡ w)
word-comp-path-type (x ∷ v) (y ′∷ w) = ⊥
word-comp-path-type (x ′∷ v) ε = ⊥
word-comp-path-type (x ′∷ v) (y ∷ w) = ⊥
word-comp-path-type (x ′∷ v) (y ′∷ w) = (x ≡ y) × (v ≡ w)

word-comp-path : {v w : word} (p : v ≡ w) → word-comp-path-type v w
word-comp-path {v = ε}      refl = tt
word-comp-path {v = x  ∷ v} refl = (refl , refl)
word-comp-path {v = x ′∷ v} refl = (refl , refl)

word-base-path : {x y : A} {v w : word} (p : x ∷ v ≡ y ∷ w) → x ≡ y
word-base-path p = π₁ (word-comp-path p)

word-fiber-path : {x y : A} {v w : word} (p : x ∷ v ≡ y ∷ w) → v ≡ w
word-fiber-path p = π₂ (word-comp-path p)

word'-base-path : {x y : A} {v w : word} (p : x ′∷ v ≡ y ′∷ w) → x ≡ y
word'-base-path p = π₁ (word-comp-path p)

word'-fiber-path : {x y : A} {v w : word} (p : x ′∷ v ≡ y ′∷ w) → v ≡ w
word'-fiber-path p = π₂ (word-comp-path p)

-- This one goes to Set and is used to prove that the constructors of [word] are
-- disjoint
word-cst-dis : (v w : word) → Set
word-cst-dis ε ε = unit
word-cst-dis ε (y ∷ w) = ⊥
word-cst-dis ε (y ′∷ w) = ⊥
word-cst-dis (x ∷ v) ε = ⊥
word-cst-dis (x ∷ v) (y ∷ w) = unit
word-cst-dis (x ∷ v) (y ′∷ w) = ⊥
word-cst-dis (x ′∷ v) ε = ⊥
word-cst-dis (x ′∷ v) (y ∷ w) = ⊥
word-cst-dis (x ′∷ v) (y ′∷ w) = unit

word-has-dec-eq : has-dec-eq word
word-has-dec-eq ε ε = inl refl
word-has-dec-eq ε (x ∷ w) = inr (λ p → transport (word-cst-dis ε) p tt)
word-has-dec-eq ε (x ′∷ w) = inr (λ p → transport (word-cst-dis ε) p tt)
word-has-dec-eq (x ∷ v) ε = inr (λ p → transport (word-cst-dis (x ∷ v)) p tt)
word-has-dec-eq (x ∷ v) (y ∷ w) with (eq x y)
word-has-dec-eq (x ∷ v) (y ∷ w) | inl x≡y with (word-has-dec-eq v w)
word-has-dec-eq (x ∷ v) (y ∷ w) | inl x≡y | inl v≡w =
  inl (word-total-path x≡y v≡w)
word-has-dec-eq (x ∷ v) (y ∷ w) | inl x≡y | inr v≢w =
  inr (λ p → v≢w (word-fiber-path p))
word-has-dec-eq (x ∷ v) (y ∷ w) | inr x≢y = inr (λ p → x≢y (word-base-path p))
word-has-dec-eq (x ∷ v) (y ′∷ w) =
  inr (λ p → transport (word-cst-dis (x ∷ v)) p tt)
word-has-dec-eq (x ′∷ v) ε = inr (λ p → transport (word-cst-dis (x ′∷ v)) p tt)
word-has-dec-eq (x ′∷ v) (y ∷ w) =
  inr (λ p → transport (word-cst-dis (x ′∷ v)) p tt)
word-has-dec-eq (x ′∷ v) (y ′∷ w) with (eq x y)
word-has-dec-eq (x ′∷ v) (y ′∷ w) | inl x≡y with (word-has-dec-eq v w)
word-has-dec-eq (x ′∷ v) (y ′∷ w) | inl x≡y | inl v≡w =
  inl (word'-total-path x≡y v≡w)
word-has-dec-eq (x ′∷ v) (y ′∷ w) | inl x≡y | inr v≢w =
  inr (λ p → v≢w (word'-fiber-path p))
word-has-dec-eq (x ′∷ v) (y ′∷ w) | inr x≢y =
  inr (λ p → x≢y (word'-base-path p))

word-is-set : is-set word
word-is-set = dec-eq-is-set word-has-dec-eq

abstract
  reduced-is-set : is-set reduced-word
  reduced-is-set =
    subtype-truncated-S-is-truncated-S _ word-is-set is-reduced-is-prop

tail-is-reduced : (x : A) (w : word) (r : is-reduced (x ∷ w)) → is-reduced w
tail-is-reduced x ε red = tt
tail-is-reduced x (y ∷ w) red = red
tail-is-reduced x (y ′∷ w) red = π₂ red

tail'-is-reduced : (x : A) (w : word) (r : is-reduced (x ′∷ w)) → is-reduced w
tail'-is-reduced x ε red = tt
tail'-is-reduced x (y ∷ w) red = π₂ red
tail'-is-reduced x (y ′∷ w) red = red

import Algebra.FreeGroup as F
open F A
import Algebra.FreeGroupProps as Fp
open Fp A

reduced-to-freegroup : reduced-word → freegroup
reduced-to-freegroup (ε , _) = e
reduced-to-freegroup ((x ∷ w) , r) =
  x · reduced-to-freegroup (w , tail-is-reduced x w r)
reduced-to-freegroup ((x ′∷ w) , r) =
  x ⁻¹· reduced-to-freegroup (w , tail'-is-reduced x w r)

mul-reduce : A → reduced-word → reduced-word
mul-reduce x (ε , red) = ((x ∷ ε) , tt)
mul-reduce x ((y ∷ w) , red) = ((x ∷ (y ∷ w)) , red)
mul-reduce x ((y ′∷ w) , red) with (eq x y)
mul-reduce x ((y ′∷ w) , red) | inl equal = (w , tail'-is-reduced y w red)
mul-reduce x ((y ′∷ w) , red) | inr different =
  ((x ∷ (y ′∷ w)) , (different , red))

mul'-reduce : A → reduced-word → reduced-word
mul'-reduce x (ε , red) = ((x ′∷ ε) , tt)
mul'-reduce x ((y ∷ w) , red) with (eq x y)
mul'-reduce x ((y ∷ w) , red) | inl equal = (w , tail-is-reduced y w red)
mul'-reduce x ((y ∷ w) , red) | inr different =
  ((x ′∷ (y ∷ w)) , (different , red))
mul'-reduce x ((y ′∷ w) , red) = (x ′∷ (y ′∷ w)) , red

abstract
  mul-mul'-reduce : (x : A) (w : reduced-word)
    → mul-reduce x (mul'-reduce x w) ≡ w
  mul-mul'-reduce x (ε , red) with (eq x x)
  mul-mul'-reduce x (ε , red) | inl obvious = refl
  mul-mul'-reduce x (ε , red) | inr absurd = abort-nondep (absurd refl)
  mul-mul'-reduce x ((y ∷ w) , red) with (eq x y)
  mul-mul'-reduce x ((y ∷ ε) , red) | inl equal = ap _ equal
  mul-mul'-reduce x ((y ∷ (z ∷ w)) , red) | inl equal = ap _ equal
  mul-mul'-reduce x ((y ∷ (z ′∷ w)) , red) | inl equal with (eq x z)
  mul-mul'-reduce x ((y ∷ (z ′∷ w)) , red) | inl equal | inl absurd =
    abort-nondep (π₁ red (! equal ∘ absurd))
  mul-mul'-reduce x ((y ∷ (z ′∷ w)) , red) | inl equal | inr obvious =
    Σ-eq (ap _ equal) (π₁ (is-reduced-is-prop (y ∷ (z ′∷ w)) _ _))
  mul-mul'-reduce x ((y ∷ w) , red) | inr different with (eq x x)
  mul-mul'-reduce x ((y ∷ w) , red) | inr different | inl obvious = refl
  mul-mul'-reduce x ((y ∷ w) , red) | inr different | inr absurd =
    abort-nondep (absurd refl)
  mul-mul'-reduce x ((y ′∷ w) , red) with (eq x x)
  mul-mul'-reduce x ((y ′∷ w) , red) | inl obvious = refl
  mul-mul'-reduce x ((y ′∷ w) , red) | inr absurd =
    abort-nondep (absurd refl)

abstract
  mul'-mul-reduce : (x : A) (w : reduced-word)
    → mul'-reduce x (mul-reduce x w) ≡ w
  mul'-mul-reduce x (ε , red) with (eq x x)
  mul'-mul-reduce x (ε , red) | inl obvious = refl
  mul'-mul-reduce x (ε , red) | inr absurd = abort-nondep (absurd refl)
  mul'-mul-reduce x ((y ′∷ w) , red) with (eq x y)
  mul'-mul-reduce x ((y ′∷ ε) , red) | inl equal = ap _ equal
  mul'-mul-reduce x ((y ′∷ (z ′∷ w)) , red) | inl equal = ap _ equal
  mul'-mul-reduce x ((y ′∷ (z ∷ w)) , red) | inl equal with (eq x z)
  mul'-mul-reduce x ((y ′∷ (z ∷ w)) , red) | inl equal | inl absurd =
    abort-nondep (π₁ red (! equal ∘ absurd))
  mul'-mul-reduce x ((y ′∷ (z ∷ w)) , red) | inl equal | inr obvious =
    Σ-eq (ap _ equal) (π₁ (is-reduced-is-prop (y ′∷ (z ∷ w)) _ _))
  mul'-mul-reduce x ((y ′∷ w) , red) | inr different with (eq x x)
  mul'-mul-reduce x ((y ′∷ w) , red) | inr different | inl obvious = refl
  mul'-mul-reduce x ((y ′∷ w) , red) | inr different | inr absurd =
    abort-nondep (absurd refl)
  mul'-mul-reduce x ((y ∷ w) , red) with (eq x x)
  mul'-mul-reduce x ((y ∷ w) , red) | inl obvious = refl
  mul'-mul-reduce x ((y ∷ w) , red) | inr absurd =
    abort-nondep (absurd refl)

freegroup-to-reduced : freegroup → reduced-word
freegroup-to-reduced = freegroup-rec-nondep reduced-word
  (ε , tt)
  mul-reduce
  mul'-reduce
  mul-mul'-reduce
  mul'-mul-reduce
  reduced-is-set

abstract
  mul-reduce-reduced : (x : A) (w : word) (red : is-reduced (x ∷ w))
    → mul-reduce x (w , tail-is-reduced x w red) ≡ ((x ∷ w) , red)
  mul-reduce-reduced x ε red = refl
  mul-reduce-reduced x (y ∷ w) red = refl
  mul-reduce-reduced x (y ′∷ w) red with (eq x y)
  mul-reduce-reduced x (y ′∷ w) red | inl absurd = abort-nondep (π₁ red absurd)
  mul-reduce-reduced x (y ′∷ w) red | inr obvious =
    Σ-eq refl (π₁ (is-reduced-is-prop (x ∷ (y ′∷ w)) _ _))

abstract
  mul'-reduce-reduced : (x : A) (w : word) (red : is-reduced (x ′∷ w))
    → mul'-reduce x (w , tail'-is-reduced x w red) ≡ ((x ′∷ w) , red)
  mul'-reduce-reduced x ε red = refl
  mul'-reduce-reduced x (y ∷ w) red with (eq x y)
  mul'-reduce-reduced x (y ∷ w) red | inl absurd = abort-nondep (π₁ red absurd)
  mul'-reduce-reduced x (y ∷ w) red | inr obvious =
    Σ-eq refl (π₁ (is-reduced-is-prop (x ′∷ (y ∷ w)) _ _))
  mul'-reduce-reduced x (y ′∷ w) red = refl

inv₁ : (w : reduced-word) → freegroup-to-reduced (reduced-to-freegroup w) ≡ w
inv₁ (ε , red) = refl
inv₁ ((x ∷ w) , red) = ap (mul-reduce x) (inv₁ (w , tail-is-reduced x w red))
                       ∘ mul-reduce-reduced x w red
inv₁ ((x ′∷ w) , red) =
  ap (mul'-reduce x) (inv₁ (w , tail'-is-reduced x w red))
  ∘ mul'-reduce-reduced x w red

reduced-to-freegroup-mul-reduce : (x : A) (v : reduced-word)
  → reduced-to-freegroup (mul-reduce x v) ≡ x · (reduced-to-freegroup v)
reduced-to-freegroup-mul-reduce x (ε , red) = refl
reduced-to-freegroup-mul-reduce x ((y ∷ v) , red) = refl
reduced-to-freegroup-mul-reduce x ((y ′∷ v) , red) with (eq x y)
reduced-to-freegroup-mul-reduce x ((.x ′∷ v) , red) | inl refl =
  ! (right-inverse-· x (reduced-to-freegroup (v , tail'-is-reduced x v red)))
reduced-to-freegroup-mul-reduce x ((y ′∷ v) , red) | inr different = refl

reduced-to-freegroup-mul'-reduce : (x : A) (v : reduced-word)
  → reduced-to-freegroup (mul'-reduce x v) ≡ x ⁻¹· (reduced-to-freegroup v)
reduced-to-freegroup-mul'-reduce x (ε , red) = refl
reduced-to-freegroup-mul'-reduce x ((y ∷ v) , red) with (eq x y)
reduced-to-freegroup-mul'-reduce x ((.x ∷ v) , red) | inl refl =
  ! (left-inverse-· x (reduced-to-freegroup (v , tail-is-reduced x v red)))
reduced-to-freegroup-mul'-reduce x ((y ∷ v) , red) | inr different = refl
reduced-to-freegroup-mul'-reduce x ((y ′∷ v) , red) = refl

inv₂ : (a : freegroup) → reduced-to-freegroup (freegroup-to-reduced a) ≡ a
inv₂ = freegroup-rec _
  refl
  (λ x u p → reduced-to-freegroup-mul-reduce x (freegroup-to-reduced u)
             ∘ ap (λ t → x · t) {y = u} p)
  (λ x u p → reduced-to-freegroup-mul'-reduce x (freegroup-to-reduced u)
             ∘ ap (λ t → x ⁻¹· t) {y = u} p)
  (λ x u t → π₁ (freegroup-is-set _ _ _ _))
  (λ x u t → π₁ (freegroup-is-set _ _ _ _))
  (λ u → truncated-is-truncated-S _ (freegroup-is-set _ _))

freegroup-equiv-reduced : freegroup ≃ reduced-word
freegroup-equiv-reduced =
  (freegroup-to-reduced , iso-is-eq _ reduced-to-freegroup inv₁ inv₂)


module Impure.LFRef.Eval where

open import Prelude
open import Data.Fin using (fromℕ≤)
open import Data.List hiding ([_])
open import Data.List.All
open import Data.List.Any
open import Data.Vec hiding (map; _∷ʳ_)
open import Data.Maybe hiding (All; Any)
open import Extensions.List as L

open import Impure.LFRef.Syntax hiding (subst)
open import Impure.LFRef.Welltyped

-- machine configuration: expression to reduce and a store
Config : Set
Config = Exp 0 × Store

!load : ∀ {i} → (μ : Store) → i < length μ → Term 0
!load {i = i} [] ()
!load {i = zero} (x ∷ μ) (s≤s p) = proj₁ x
!load {i = suc i} (x ∷ μ) (s≤s p) = !load μ p

!store : ∀ {i e} → (μ : Store) → i < length μ → Val e → Store
!store [] () v
!store {i = zero} (x ∷ μ) (s≤s p) v = (, v) ∷ μ
!store {i = suc i} (x ∷ μ) (s≤s p) v = (, v) ∷ (!store μ p v)

!call : ∀ {n m} → Exp m → (l : List (Term n)) → length l ≡ m → Exp n
!call e ts p = e exp/ subst (Vec _) p (fromList ts)

-- small steps for expressions
infix 1 _⊢_≻_
data _⊢_≻_ (𝕊 : Sig) : (t t' : Config) → Set where

  funapp-β : ∀ {fn ts μ φ} →
             (Sig.funs 𝕊) L.[ fn ]= φ →
             (p : length ts ≡ Fun.m φ) →
             -------------------------
             𝕊 ⊢ fn ·★ ts , μ ≻ (!call (Fun.body φ) ts p) , μ

  ref-val : ∀ {t μ} →
            (v : Val t) →
            ----------------------------------------------------
            𝕊 ⊢ ref (tm t) , μ ≻ (tm (loc (length μ))) , (μ ∷ʳ (, v))

  ≔-val : ∀ {i e μ} →
          (p : i < length μ) →
          (v : Val e) →
          --------------------------------------------
          𝕊 ⊢ (tm (loc i)) ≔ (tm e) , μ ≻ (tm unit) , (μ L.[ fromℕ≤ p ]≔ (, v))

  !-val : ∀ {i μ} →
          (p : i < length μ) →
          -----------------------------------------
          𝕊 ⊢ ! (tm (loc i)) , μ ≻ tm (!load μ p) , μ

  ref-clos : ∀ {e e' μ μ'} →
             𝕊 ⊢ e , μ ≻ e' , μ' →
             ---------------------------
             𝕊 ⊢ ref e , μ ≻ ref e' , μ'

  !-clos   : ∀ {e e' μ μ'} →
             𝕊 ⊢ e , μ ≻ e' , μ' →
             -----------------------
             𝕊 ⊢ ! e , μ ≻ ! e' , μ'

  ≔-clos₁  : ∀ {x x' e μ μ'} →
             𝕊 ⊢ x , μ ≻ x' , μ' →
             --------------------------
             𝕊 ⊢ x ≔ e , μ ≻ x' ≔ e , μ'

  ≔-clos₂  : ∀ {x e e' μ μ'} →
             ExpVal x →
             𝕊 ⊢ e , μ ≻ e' , μ' →
             --------------------------
             𝕊 ⊢ x ≔ e , μ ≻ x ≔ e' , μ'

infix 1 _⊢_≻ₛ_
data _⊢_≻ₛ_ (𝕊 : Sig) : (t t' : SeqExp 0 × Store) → Set where

  -- reductions
  lett-β  : ∀ {t e μ} →
            ----------------------------------------------
            𝕊 ⊢ (lett (tm t) e) , μ ≻ₛ (e seq/ (sub t)) , μ

  -- contextual closure
  ret-clos  : ∀ {e μ e' μ'} →
              𝕊 ⊢ e , μ ≻ e' , μ' →
              -------------------------------------
              𝕊 ⊢ (ret e) , μ ≻ₛ (ret e') , μ'

  lett-clos : ∀ {x e x' μ μ'} →
              𝕊 ⊢ x , μ ≻ x' , μ' →
              -------------------------------------
              𝕊 ⊢ (lett x e) , μ ≻ₛ (lett x' e) , μ'

-- reflexive-transitive closure of ≻
open import Data.Star
infix 1 _⊢_≻⋆_
_⊢_≻⋆_ : (Sig) → (c c' : SeqExp 0 × Store) → Set
𝕊 ⊢ c ≻⋆ c' = Star (_⊢_≻ₛ_ 𝕊) c c'


------------------------------------------------------------------------------
-- Properties related with the forest type
------------------------------------------------------------------------------

{-# OPTIONS --exact-split              #-}
{-# OPTIONS --no-sized-types           #-}
{-# OPTIONS --no-universe-polymorphism #-}
{-# OPTIONS --without-K                #-}

module FOTC.Program.Mirror.Forest.PropertiesATP where

open import Common.FOL.Relation.Binary.EqReasoning

open import FOTC.Base
open import FOTC.Base.List
open import FOTC.Base.List.PropertiesATP using ( ∷-rightCong )
open import FOTC.Data.List
open import FOTC.Data.List.PropertiesATP
  using ( ++-leftIdentity
        ; mapCong₂
        ; ++-leftCong
        ; reverse-[x]≡[x]
        )
open import FOTC.Program.Mirror.Forest.TotalityATP
open import FOTC.Program.Mirror.Type

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

++-rightIdentity : ∀ {xs} → Forest xs → xs ++ [] ≡ xs
++-rightIdentity fnil = ++-leftIdentity []
++-rightIdentity (fcons {x} {xs} Tx Fxs) =
  prf (++-rightIdentity Fxs)
  where postulate prf : xs ++ [] ≡ xs → (x ∷ xs) ++ [] ≡ x ∷ xs
        {-# ATP prove prf #-}

++-assoc : ∀ {xs} → Forest xs → ∀ ys zs → (xs ++ ys) ++ zs ≡ xs ++ (ys ++ zs)
++-assoc fnil ys zs = prf
  where postulate prf : ([] ++ ys) ++ zs ≡ [] ++ ys ++ zs
        {-# ATP prove prf #-}

++-assoc (fcons {x} {xs} Tx Fxs) ys zs = prf (++-assoc Fxs ys zs)
  where postulate prf : (xs ++ ys) ++ zs ≡ xs ++ ys ++ zs →
                        ((x ∷ xs) ++ ys) ++ zs ≡ (x ∷ xs) ++ ys ++ zs
        {-# ATP prove prf #-}

-- We don't use an automatic proof, because it is necessary to erase a
-- proof term which we don't know how to erase.
map-++ : ∀ f {xs} → (∀ {x} → Tree x → Tree (f · x)) →
         Forest xs →
         ∀ ys → map f (xs ++ ys) ≡ map f xs ++ map f ys
map-++ f h fnil ys =
  map f ([] ++ ys)
    ≡⟨ mapCong₂ (++-leftIdentity ys) ⟩
  map f ys
    ≡⟨ sym (++-leftIdentity (map f ys)) ⟩
  [] ++ map f ys
     ≡⟨ ++-leftCong (sym (map-[] f)) ⟩
  map f [] ++ map f ys ∎

map-++ f h (fcons {x} {xs} Tx Fxs) ys =
  map f ((x ∷ xs) ++ ys)
    ≡⟨ mapCong₂ (++-∷ x xs ys) ⟩
  map f (x ∷ xs ++ ys)
    ≡⟨ map-∷ f x (xs ++ ys) ⟩
  f · x ∷ map f (xs ++ ys)
    ≡⟨ ∷-rightCong (map-++ f h Fxs ys) ⟩
  f · x ∷ (map f xs ++ map f ys)
    ≡⟨ sym (++-∷ (f · x) (map f xs) (map f ys)) ⟩
  (f · x ∷ map f xs) ++ map f ys
     ≡⟨ ++-leftCong (sym (map-∷ f x xs)) ⟩
  map f (x ∷ xs) ++ map f ys ∎

rev-++ : ∀ {xs} → Forest xs → ∀ ys → rev xs ys ≡ rev xs [] ++ ys
rev-++ fnil ys = prf
  where postulate prf : rev [] ys ≡ rev [] [] ++ ys
        {-# ATP prove prf #-}

rev-++ (fcons {x} {xs} Tx Fxs) ys =
  prf (rev-++ Fxs (x ∷ ys))
      (rev-++ Fxs (x ∷ []))
  where postulate prf : rev xs (x ∷ ys) ≡ rev xs [] ++ x ∷ ys →
                        rev xs (x ∷ []) ≡ rev xs [] ++ x ∷ [] →
                        rev (x ∷ xs) ys ≡ rev (x ∷ xs) [] ++ ys
        {-# ATP prove prf ++-assoc rev-Forest #-}

reverse-++ : ∀ {xs ys} → Forest xs → Forest ys →
             reverse (xs ++ ys) ≡ reverse ys ++ reverse xs
reverse-++ {ys = ys} fnil Fys = prf
  where postulate prf : reverse ([] ++ ys) ≡ reverse ys ++ reverse []
        {-# ATP prove prf ++-rightIdentity reverse-Forest #-}

reverse-++ (fcons {x} {xs} Tx Fxs) fnil = prf
  where
  postulate prf : reverse ((x ∷ xs) ++ []) ≡ reverse [] ++ reverse (x ∷ xs)
  {-# ATP prove prf ++-rightIdentity #-}

reverse-++ (fcons {x} {xs} Tx Fxs) (fcons {y} {ys} Ty Fys) =
  prf (reverse-++ Fxs (fcons Ty Fys))
  where
  postulate prf : reverse (xs ++ y ∷ ys) ≡ reverse (y ∷ ys) ++
                                           reverse xs →
                  reverse ((x ∷ xs) ++ y ∷ ys) ≡ reverse (y ∷ ys) ++
                                                 reverse (x ∷ xs)
  {-# ATP prove prf reverse-Forest ++-Forest rev-++ ++-assoc #-}

reverse-∷ : ∀ {x ys} → Tree x → Forest ys →
            reverse (x ∷ ys) ≡ reverse ys ++ (x ∷ [])
reverse-∷ {x} Tx fnil = prf
  where postulate prf : reverse (x ∷ []) ≡ reverse [] ++ x ∷ []
        {-# ATP prove prf #-}

reverse-∷ {x} Tx (fcons {y} {ys} Ty Fys) = prf
  where postulate prf : reverse (x ∷ y ∷ ys) ≡ reverse (y ∷ ys) ++ x ∷ []
        {-# ATP prove prf reverse-[x]≡[x] reverse-++ #-}


module Data.Collection where

open import Data.Collection.Core public
open import Data.Collection.Equivalence
open import Data.Collection.Inclusion
open import Relation.Nullary

--------------------------------------------------------------------------------
--  Singleton
--------------------------------------------------------------------------------

singleton : String → Collection
singleton x = x ∷ []

--------------------------------------------------------------------------------
--  Delete
--------------------------------------------------------------------------------

delete : String → Collection → Collection
delete x [] = []
delete x (a ∷ A) with x ≟ a
delete x (a ∷ A) | yes p =     delete x A -- keep deleting, because there might be many of them
delete x (a ∷ A) | no ¬p = a ∷ delete x A

--------------------------------------------------------------------------------
--  Union
--------------------------------------------------------------------------------

union : Collection → Collection → Collection
union []      B = B
union (a ∷ A) B with a ∈? B
union (a ∷ A) B | yes p = union A B
union (a ∷ A) B | no ¬p = a ∷ union A B

--
-- open import Data.List
-- open import Data.String hiding (setoid)
-- open import Data.List using ([]; _∷_) public
-- open import Data.Product
-- open import Data.Sum renaming (map to mapSum)
-- -- open import Data.Bool using (Bool; true; false; T; not)
-- open import Level renaming (zero to lvl0)
-- open import Function using (_∘_; id; flip; _on_)
-- open import Function.Equivalence using (_⇔_; Equivalence; equivalence)
--
-- open import Function.Equality using (_⟨$⟩_) renaming (cong to cong≈)
-- open import Relation.Nullary
-- open import Relation.Unary
-- open import Relation.Nullary.Negation
-- open import Relation.Nullary.Decidable renaming (map to mapDec; map′ to mapDec′)
-- open import Relation.Binary hiding (_⇒_)
-- open import Relation.Binary.PropositionalEquality
-- -- open ≡-Reasoning
--
--
-- -- I know this notation is a bit confusing
-- _⊈_ : ∀ {a ℓ₁ ℓ₂} {A : Set a} → Pred A ℓ₁ → Pred A ℓ₂ → Set _
-- P ⊈ Q = ∀ {x} → x ∉ P → x ∉ Q
--
--
-- ⊆-IsPreorder : IsPreorder _≋_ _⊆_
-- ⊆-IsPreorder = record
--     { isEquivalence = {!   !}
--     ; reflexive = {!   !}
--     ; trans = {!   !}
--     }
--
--
-- []-empty : Empty c[ [] ]
-- []-empty = λ x → λ ()
--
-- here-respects-≡ : ∀ {A} → (λ x → x ∈ c[ A ]) Respects _≡_
-- here-respects-≡ refl = id
--
-- there-respects-≡ : ∀ {x A} → (λ a → x ∈ c[ a ∷ A ]) Respects _≡_
-- there-respects-≡ refl = id
--
-- ∈-respects-≡ : ∀ {x} → (λ P → x ∈ c[ P ]) Respects _≡_
-- ∈-respects-≡ refl = id
--
-- there-if-not-here : ∀ {x a A} → x ≢ a → x ∈ c[ a ∷ A ] → x ∈ c[ A ]
-- there-if-not-here x≢a here          = contradiction refl x≢a
-- there-if-not-here x≢a (there x∈a∷A) = x∈a∷A
--
-- here-if-not-there : ∀ {x a A} → x ∉ c[ A ] → x ∈ c[ a ∷ A ] → x ≡ a
-- here-if-not-there x∉A here = refl
-- here-if-not-there x∉A (there x∈A) = contradiction x∈A x∉A

--
-- map-¬∷ : ∀ {x a A B} → (x ∉ c[ A ] → x ∉ c[ B ]) → x ≢ a → x ∉ c[ a ∷ A ] → x ∉ c[ a ∷ B ]
-- map-¬∷ f x≢x x∉a∷A here = contradiction refl x≢x
-- map-¬∷ f x≢a x∉a∷A (there x∈B) = f (x∉a∷A ∘ there) x∈B
--
-- still-not-there : ∀ {x y} A → x ≢ y → x ∉ c[ A ] → x ∉ c[ y ∷ A ]
-- still-not-there [] x≢y x∉[y] here = x≢y refl
-- still-not-there [] x≢y x∉[y] (there ())
-- still-not-there (a ∷ A) x≢y x∉a∷A here = x≢y refl
-- still-not-there (a ∷ A) x≢y x∉a∷A (there x∈a∷A) = x∉a∷A x∈a∷A
--
-- --------------------------------------------------------------------------------
-- --  Union
-- --------------------------------------------------------------------------------
--
-- union : Collection → Collection → Collection
-- union []      B = B
-- union (a ∷ A) B with a ∈? B
-- union (a ∷ A) B | yes p = union A B
-- union (a ∷ A) B | no ¬p = a ∷ union A B
--
-- in-right-union : ∀ A B → c[ B ] ⊆ c[ union A B ]
-- in-right-union []      B x∈B = x∈B
-- in-right-union (a ∷ A) B x∈B with a ∈? B
-- in-right-union (a ∷ A) B x∈B | yes p = in-right-union A B x∈B
-- in-right-union (a ∷ A) B x∈B | no ¬p = there (in-right-union A B x∈B)
--
-- in-left-union : ∀ A B → c[ A ] ⊆ c[ union A B ]
-- in-left-union []      B ()
-- in-left-union (a ∷ A) B x∈A         with a ∈? B
-- in-left-union (a ∷ A) B here        | yes p = in-right-union A B p
-- in-left-union (a ∷ A) B (there x∈A) | yes p = in-left-union A B x∈A
-- in-left-union (a ∷ A) B here        | no ¬p = here
-- in-left-union (a ∷ A) B (there x∈A) | no ¬p = there (in-left-union A B x∈A)
--
-- ∪-left-identity : ∀ A → c[ [] ] ∪ c[ A ] ≋ c[ A ]
-- ∪-left-identity A = equivalence to inj₂
--     where
--             to : c[ [] ] ∪ c[ A ] ⊆ c[ A ]
--             to (inj₁ ())
--             to (inj₂ ∈A) = ∈A
--
-- ∪-right-identity : ∀ A → c[ A ] ∪ c[ [] ] ≋ c[ A ]
-- ∪-right-identity A = equivalence to inj₁
--     where
--             to : c[ A ] ∪ c[ [] ] ⊆ c[ A ]
--             to (inj₁ ∈A) = ∈A
--             to (inj₂ ())
--
-- in-either : ∀ A B → c[ union A B ] ⊆ c[ A ] ∪ c[ B ]
-- in-either []      B x∈A∪B         = inj₂ x∈A∪B
-- in-either (a ∷ A) B x∈A∪B         with a ∈? B
-- in-either (a ∷ A) B x∈A∪B         | yes p = mapSum there id (in-either A B x∈A∪B)
-- in-either (a ∷ A) B here          | no ¬p = inj₁ here
-- in-either (a ∷ A) B (there x∈A∪B) | no ¬p = mapSum there id (in-either A B x∈A∪B)
--
-- ∪-union : ∀ A B → c[ A ] ∪ c[ B ] ≋ c[ union A B ]
-- ∪-union A B = equivalence to (in-either A B)
--     where   to : ∀ {x} → x ∈ c[ A ] ∪ c[ B ] → x ∈ c[ union A B ]
--             to (inj₁ ∈A) = in-left-union A B ∈A
--             to (inj₂ ∈B) = in-right-union A B ∈B
--
-- -- map-⊆-union : ∀ {A B C} → c[ A ] ⊆ c[ B ] → c[ B ] ⊆ c[ C ] → c[  ] ⊆ c[ union C D ]
-- -- map-⊆-union f g ∈union = {!   !}
--
--
-- union-branch-1 : ∀ {x a} A B → a ∈ c[ B ] → x ∈ c[ union (a ∷ A) B ] → x ∈ c[ union A B ]
-- union-branch-1 {x} {a} A B a∈B x∈union with a ∈? B
-- union-branch-1 A B a∈B x∈union | yes p = x∈union
-- union-branch-1 A B a∈B x∈union | no ¬p = contradiction a∈B ¬p
--
-- there-left-union-coherence : ∀ {x} {a} A B → x ∈ c[ a ∷ A ] → x ∈c a ∷ union A B
-- there-left-union-coherence A B here          = here
-- there-left-union-coherence A B (there x∈a∷A) = there (in-left-union A B x∈a∷A)
--
--
-- in-neither : ∀ {x} A B → x ∉c union A B → x ∉ c[ A ] × x ∉ c[ B ]
-- in-neither []      B x∉A∪B = (λ ()) , x∉A∪B
-- in-neither (a ∷ A) B x∉A∪B with a ∈? B
-- in-neither (a ∷ A) B x∉A∪B | yes a∈B = (contraposition (union-branch-1 A B a∈B ∘ in-left-union (a ∷ A) B) x∉A∪B) , (contraposition (in-right-union A B) x∉A∪B)
-- in-neither (a ∷ A) B x∉A∪B | no a∉B = (contraposition (there-left-union-coherence A B) x∉A∪B) , contraposition (there ∘ in-right-union A B) x∉A∪B
--
--
-- delete : String → Collection → Collection
-- delete x [] = []
-- delete x (a ∷ A) with x ≟ a
-- delete x (a ∷ A) | yes p =     delete x A -- keep deleting, because there might be many of them
-- delete x (a ∷ A) | no ¬p = a ∷ delete x A
--
-- ∉-after-deleted : ∀ x A → x ∉ c[ delete x A ]
-- ∉-after-deleted x [] ()
-- ∉-after-deleted x (a ∷ A) with x ≟ a
-- ∉-after-deleted x (a ∷ A) | yes p = ∉-after-deleted x A
-- ∉-after-deleted x (a ∷ A) | no ¬p = still-not-there (delete x A) ¬p (∉-after-deleted x A)
--
--
-- still-∈-after-deleted : ∀ {x} y A → x ≢ y → x ∈ c[ A ] → x ∈ c[ delete y A ]
-- still-∈-after-deleted y []       x≢y ()
-- still-∈-after-deleted y (a  ∷ A) x≢y x∈A with y ≟ a
-- still-∈-after-deleted y (.y ∷ A) x≢y x∈A | yes refl = still-∈-after-deleted y A x≢y (there-if-not-here x≢y x∈A)
-- still-∈-after-deleted y (a  ∷ A) x≢y x∈A | no ¬p    = ∷-⊆-monotone {!   !} {! x∈A  !}
-- -- still-∈-after-deleted y (a  ∷ A) x≢y x∈A | no ¬p    = ∷-⊆-monotone (still-∈-after-deleted y A x≢y) x∈A
--
-- -- still-∉-after-deleted : ∀ {x} y A → x ≢ y → x ∉ c[ A ] → x ∉ c[ delete y A ]
-- -- still-∉-after-deleted y []       x≢y x∉A = x∉A
-- -- still-∉-after-deleted y (a  ∷ A) x≢y x∉A with y ≟ a
-- -- still-∉-after-deleted y (.y ∷ A) x≢y x∉A | yes refl = still-∉-after-deleted y A x≢y (x∉A ∘ there)
-- -- still-∉-after-deleted {x} y (a  ∷ A) x≢y x∉A | (no ¬p) with x ≟ a
-- -- still-∉-after-deleted y (x ∷ A) x≢y x∉A | no ¬p | yes refl = contradiction here x∉A
-- -- still-∉-after-deleted y (a ∷ A) x≢y x∉A | no ¬p | no ¬q = map-¬∷ (still-∉-after-deleted y A x≢y) ¬q x∉A
-- --
-- -- still-∉-after-recovered : ∀ {x} y A → x ≢ y → x ∉c delete y A → x ∉ c[ A ]
-- -- still-∉-after-recovered     y []      x≢y x∉deleted ()
-- -- still-∉-after-recovered     y (a  ∷ A) x≢y x∉deleted x∈a∷A with y ≟ a
-- -- still-∉-after-recovered     y (.y ∷ A) x≢y x∉deleted x∈a∷A | yes refl = still-∉-after-recovered y A x≢y x∉deleted (there-if-not-here x≢y x∈a∷A)
-- -- still-∉-after-recovered {x} y (a  ∷ A) x≢y x∉deleted x∈a∷A | no ¬p with x ≟ a
-- -- still-∉-after-recovered     y (x  ∷ A) x≢y x∉deleted x∈a∷A | no ¬p | yes refl = contradiction here x∉deleted
-- -- still-∉-after-recovered     y (a  ∷ A) x≢y x∉deleted x∈a∷A | no ¬p | no ¬q = x∉deleted (∷-⊆-monotone (still-∈-after-deleted y A x≢y) x∈a∷A)
--
-- singleton : String → Collection
-- singleton x = x ∷ []
--
-- singleton-≡ : ∀ {x y} → x ∈ c[ singleton y ] → x ≡ y
-- singleton-≡ here = refl
-- singleton-≡ (there ())


-- Copyright:  (c) 2016 Ertugrul Söylemez
-- License:    BSD3
-- Maintainer: Ertugrul Söylemez <esz@posteo.de>

module Data.Nat.Core where

open import Agda.Builtin.Nat public
  renaming (Nat to ℕ)
  using (suc; zero)
  renaming (_+_ to _+ℕ_; _*_ to _*ℕ_)
open import Classes
open import Core

instance
  ℕ-Number : Number ℕ
  ℕ-Number =
    record {
      Constraint = λ _ → ⊤;
      fromNat = λ n → n
    }

  ℕ-Plus : Plus ℕ
  ℕ-Plus = record { _+_ = _+ℕ_ }

  ℕ-Times : Times ℕ
  ℕ-Times = record { _*_ = _*ℕ_ }

  ℕ,≡ : Equiv ℕ
  ℕ,≡ = PropEq ℕ

  module ℕ,≡ = Equiv ℕ,≡

data _≤_ : ℕ → ℕ → Set where
  instance 0≤s : ∀ {y} → 0 ≤ y
  instance s≤s : ∀ {x y} → x ≤ y → suc x ≤ suc y

infix 4 _≤_

ℕ,≤ : TotalOrder ℕ
ℕ,≤ =
  record {
    partialOrder = record {
      Eq = PropEq ℕ;
      _≤_ = _≤_;
      antisym = antisym;
      refl' = refl';
      trans = trans
    };
    total = total
  }

  where
  antisym : {x y : ℕ} → x ≤ y → y ≤ x → x ≡ y
  antisym 0≤s 0≤s = ≡-refl
  antisym (s≤s p) (s≤s q) = cong suc (antisym p q)

  refl' : {x y : ℕ} → x ≡ y → x ≤ y
  refl' {zero} ≡-refl = 0≤s
  refl' {suc x} ≡-refl = s≤s (refl' ≡-refl)

  trans : {x y z : ℕ} → x ≤ y → y ≤ z → x ≤ z
  trans 0≤s q = 0≤s
  trans (s≤s p) (s≤s q) = s≤s (trans p q)

  total : (x y : ℕ) → Either (x ≤ y) (y ≤ x)
  total zero y = Left 0≤s
  total (suc x) zero = Right 0≤s
  total (suc x) (suc y) with total x y
  total (suc x) (suc y) | Left p = Left (s≤s p)
  total (suc x) (suc y) | Right p = Right (s≤s p)

module ℕ,≤ = TotalOrder ℕ,≤

_≤?_ : ∀ x y → Decision (x ≤ y)
zero ≤? y = yes 0≤s
suc x ≤? zero = no (λ ())
suc x ≤? suc y with x ≤? y
suc x ≤? suc y | yes p = yes (s≤s p)
suc x ≤? suc y | no np = no (λ { (s≤s p) → np p })

_multiple-of_ : ℕ → ℕ → Set
x multiple-of y = ∃ (λ k → k * y ≡ x)

module Props where
  open Equiv (PropEq ℕ)

  private
    _!+!_ : ∀ {x1 x2 y1 y2 : ℕ} → x1 ≡ x2 → y1 ≡ y2 → x1 + y1 ≡ x2 + y2
    _!+!_ = cong2 _+_
    R = λ x → refl {x = x}

    infixl 6 _!+!_

  +-assoc : ∀ x y z → (x + y) + z ≈ x + (y + z)
  +-assoc zero y z = refl
  +-assoc (suc x) y z = cong suc (+-assoc x y z)

  +-left-id : ∀ x → 0 + x ≈ x
  +-left-id _ = refl

  +-right-id : ∀ x → x + 0 ≈ x
  +-right-id zero = refl
  +-right-id (suc x) = cong suc (+-right-id x)

  +-comm : ∀ x y → x + y ≈ y + x
  +-comm zero y = sym (+-right-id y)
  +-comm (suc x) y =
    begin
      suc x + y ≈[ cong suc (+-comm x y) ]
      suc y + x ≈[ sym (one-comm y x) ]
      y + suc x
    qed

    where
    one-comm : ∀ x y → x + suc y ≈ suc x + y
    one-comm zero _ = refl
    one-comm (suc x) y = cong suc (one-comm x y)

  +-suc-assoc : ∀ x y → x + suc y ≈ suc (x + y)
  +-suc-assoc x y =
    begin
      x + suc y   ≈[ sym (+-assoc x 1 y) ]
      (x + 1) + y ≈[ +-comm x 1 !+! R y ]
      suc (x + y)
    qed

  *-+-left-dist : ∀ x a b → x * (a + b) ≈ x * a + x * b
  *-+-left-dist zero a b = refl
  *-+-left-dist (suc x) a b =
    begin
      a + b + x * (a + b)       ≈[ R (a + b) !+! *-+-left-dist x a b ]
      a + b + (x * a + x * b)   ≈[ +-assoc a b _ ]
      a + (b + (x * a + x * b)) ≈[
      cong (a +_) $
        b + (x * a + x * b) ≈[ sym (+-assoc b (x * a) (x * b)) ]
        b + x * a + x * b   ≈[ +-comm b _ !+! R (x * b) ]
        x * a + b + x * b   ≈[ +-assoc (x * a) _ _ ]
        x * a + (b + x * b)
      qed ]
      a + (x * a + (b + x * b)) ≈[ sym (+-assoc a _ _) ]
      a + x * a + (b + x * b)
    qed

  *-+-right-dist : ∀ a b x → (a + b) * x ≈ a * x + b * x
  *-+-right-dist zero b x = refl
  *-+-right-dist (suc a) b x =
    begin
      x + (a + b) * x     ≈[ R x !+! *-+-right-dist a b x ]
      x + (a * x + b * x) ≈[ sym (+-assoc x (a * x) (b * x)) ]
      (x + a * x) + b * x
    qed

  *-assoc : ∀ x y z → (x * y) * z ≈ x * (y * z)
  *-assoc zero y z = refl
  *-assoc (suc x) y z =
    begin
      (y + x * y) * z     ≈[ *-+-right-dist y (x * y) z ]
      y * z + (x * y) * z ≈[ R (y * z) !+! *-assoc x y z ]
      y * z + x * (y * z)
    qed

  *-left-id : ∀ x → 1 * x ≈ x
  *-left-id = +-right-id

  *-right-id : ∀ x → x * 1 ≈ x
  *-right-id zero = refl
  *-right-id (suc x) = cong suc (*-right-id x)

  *-right-zero : ∀ x → x * 0 ≈ 0
  *-right-zero zero = refl
  *-right-zero (suc x) = *-right-zero x

  *-comm : ∀ x y → x * y ≈ y * x
  *-comm zero y = sym (*-right-zero y)
  *-comm (suc x) zero = *-comm x zero
  *-comm (suc x) (suc y) =
    cong suc $
    begin
      y + x * suc y       ≈[ R y !+! (*-+-left-dist x 1 y) ]
      y + (x * 1 + x * y) ≈[ R y !+! (*-right-id x !+! R (x * y)) ]
      y + (x + x * y)     ≈[ sym (+-assoc y x (x * y)) ]
      y + x + x * y       ≈[ +-comm y x !+! *-comm x y ]
      x + y + y * x       ≈[ +-assoc x y (y * x) ]
      x + (y + y * x)     ≈[ R x !+! (sym (*-right-id y) !+! R (y * x)) ]
      x + (y * 1 + y * x) ≈[ R x !+! sym (*-+-left-dist y 1 x) ]
      x + y * suc x
    qed

  suc-inj : ∀ {x y} → suc x ≈ suc y → x ≈ y
  suc-inj ≡-refl = refl

  sum-mult : ∀ {x1 x2 y} → x1 multiple-of y → x2 multiple-of y → (x1 + x2) multiple-of y
  sum-mult {x1} {x2} {y} (k1 , p1) (k2 , p2) =
    k1 + k2
    because:
      (k1 + k2) * y   ≈[ *-+-right-dist k1 k2 y ]
      k1 * y + k2 * y ≈[ p1 !+! p2 ]
      x1 + x2
    qed


------------------------------------------------------------------------
-- The Agda standard library
--
-- Many properties which hold for _∼_ also hold for _∼_ on f
------------------------------------------------------------------------

open import Relation.Binary

module Relation.Binary.On where

open import Function
open import Data.Product

module _ {a b} {A : Set a} {B : Set b} (f : B → A) where

  implies : ∀ {ℓ₁ ℓ₂} (≈ : Rel A ℓ₁) (∼ : Rel A ℓ₂) →
            ≈ ⇒ ∼ → (≈ on f) ⇒ (∼ on f)
  implies _ _ impl = impl

  reflexive : ∀ {ℓ} (∼ : Rel A ℓ) → Reflexive ∼ → Reflexive (∼ on f)
  reflexive _ refl = refl

  irreflexive : ∀ {ℓ₁ ℓ₂} (≈ : Rel A ℓ₁) (∼ : Rel A ℓ₂) →
                Irreflexive ≈ ∼ → Irreflexive (≈ on f) (∼ on f)
  irreflexive _ _ irrefl = irrefl

  symmetric : ∀ {ℓ} (∼ : Rel A ℓ) → Symmetric ∼ → Symmetric (∼ on f)
  symmetric _ sym = sym

  transitive : ∀ {ℓ} (∼ : Rel A ℓ) → Transitive ∼ → Transitive (∼ on f)
  transitive _ trans = trans

  antisymmetric : ∀ {ℓ₁ ℓ₂} (≈ : Rel A ℓ₁) (≤ : Rel A ℓ₂) →
                  Antisymmetric ≈ ≤ → Antisymmetric (≈ on f) (≤ on f)
  antisymmetric _ _ antisym = antisym

  asymmetric : ∀ {ℓ} (< : Rel A ℓ) → Asymmetric < → Asymmetric (< on f)
  asymmetric _ asym = asym

  respects : ∀ {ℓ p} (∼ : Rel A ℓ) (P : A → Set p) →
             P Respects ∼ → (P ∘ f) Respects (∼ on f)
  respects _ _ resp = resp

  respects₂ : ∀ {ℓ₁ ℓ₂} (∼₁ : Rel A ℓ₁) (∼₂ : Rel A ℓ₂) →
              ∼₁ Respects₂ ∼₂ → (∼₁ on f) Respects₂ (∼₂ on f)
  respects₂ _ _ (resp₁ , resp₂) =
    ((λ {_} {_} {_} → resp₁) , λ {_} {_} {_} → resp₂)

  decidable : ∀ {ℓ} (∼ : Rel A ℓ) → Decidable ∼ → Decidable (∼ on f)
  decidable _ dec = λ x y → dec (f x) (f y)

  total : ∀ {ℓ} (∼ : Rel A ℓ) → Total ∼ → Total (∼ on f)
  total _ tot = λ x y → tot (f x) (f y)

  trichotomous : ∀ {ℓ₁ ℓ₂} (≈ : Rel A ℓ₁) (< : Rel A ℓ₂) →
                 Trichotomous ≈ < → Trichotomous (≈ on f) (< on f)
  trichotomous _ _ compare = λ x y → compare (f x) (f y)

  isEquivalence : ∀ {ℓ} {≈ : Rel A ℓ} →
                  IsEquivalence ≈ → IsEquivalence (≈ on f)
  isEquivalence {≈ = ≈} eq = record
    { refl  = reflexive  ≈ Eq.refl
    ; sym   = symmetric  ≈ Eq.sym
    ; trans = transitive ≈ Eq.trans
    }
    where module Eq = IsEquivalence eq

  isPreorder : ∀ {ℓ₁ ℓ₂} {≈ : Rel A ℓ₁} {∼ : Rel A ℓ₂} →
               IsPreorder ≈ ∼ → IsPreorder (≈ on f) (∼ on f)
  isPreorder {≈ = ≈} {∼} pre = record
    { isEquivalence = isEquivalence Pre.isEquivalence
    ; reflexive     = implies ≈ ∼ Pre.reflexive
    ; trans         = transitive ∼ Pre.trans
    }
    where module Pre = IsPreorder pre

  isDecEquivalence : ∀ {ℓ} {≈ : Rel A ℓ} →
                     IsDecEquivalence ≈ → IsDecEquivalence (≈ on f)
  isDecEquivalence {≈ = ≈} dec = record
    { isEquivalence = isEquivalence Dec.isEquivalence
    ; _≟_           = decidable ≈ Dec._≟_
    }
    where module Dec = IsDecEquivalence dec

  isPartialOrder : ∀ {ℓ₁ ℓ₂} {≈ : Rel A ℓ₁} {≤ : Rel A ℓ₂} →
                   IsPartialOrder ≈ ≤ →
                   IsPartialOrder (≈ on f) (≤ on f)
  isPartialOrder {≈ = ≈} {≤} po = record
    { isPreorder = isPreorder Po.isPreorder
    ; antisym    = antisymmetric ≈ ≤ Po.antisym
    }
    where module Po = IsPartialOrder po

  isDecPartialOrder : ∀ {ℓ₁ ℓ₂} {≈ : Rel A ℓ₁} {≤ : Rel A ℓ₂} →
                      IsDecPartialOrder ≈ ≤ →
                      IsDecPartialOrder (≈ on f) (≤ on f)
  isDecPartialOrder dpo = record
    { isPartialOrder = isPartialOrder DPO.isPartialOrder
    ; _≟_            = decidable _ DPO._≟_
    ; _≤?_           = decidable _ DPO._≤?_
    }
    where module DPO = IsDecPartialOrder dpo

  isStrictPartialOrder : ∀ {ℓ₁ ℓ₂} {≈ : Rel A ℓ₁} {< : Rel A ℓ₂} →
                         IsStrictPartialOrder ≈ < →
                         IsStrictPartialOrder (≈ on f) (< on f)
  isStrictPartialOrder {≈ = ≈} {<} spo = record
    { isEquivalence = isEquivalence Spo.isEquivalence
    ; irrefl        = irreflexive ≈ < Spo.irrefl
    ; trans         = transitive < Spo.trans
    ; <-resp-≈      = respects₂ < ≈ Spo.<-resp-≈
    }
    where module Spo = IsStrictPartialOrder spo

  isTotalOrder : ∀ {ℓ₁ ℓ₂} {≈ : Rel A ℓ₁} {≤ : Rel A ℓ₂} →
                 IsTotalOrder ≈ ≤ →
                 IsTotalOrder (≈ on f) (≤ on f)
  isTotalOrder {≈ = ≈} {≤} to = record
    { isPartialOrder = isPartialOrder To.isPartialOrder
    ; total          = total ≤ To.total
    }
    where module To = IsTotalOrder to

  isDecTotalOrder : ∀ {ℓ₁ ℓ₂} {≈ : Rel A ℓ₁} {≤ : Rel A ℓ₂} →
                    IsDecTotalOrder ≈ ≤ →
                    IsDecTotalOrder (≈ on f) (≤ on f)
  isDecTotalOrder {≈ = ≈} {≤} dec = record
    { isTotalOrder = isTotalOrder Dec.isTotalOrder
    ; _≟_          = decidable ≈ Dec._≟_
    ; _≤?_         = decidable ≤ Dec._≤?_
    }
    where module Dec = IsDecTotalOrder dec

  isStrictTotalOrder : ∀ {ℓ₁ ℓ₂} {≈ : Rel A ℓ₁} {< : Rel A ℓ₂} →
                       IsStrictTotalOrder ≈ < →
                       IsStrictTotalOrder (≈ on f) (< on f)
  isStrictTotalOrder {≈ = ≈} {<} sto = record
    { isEquivalence = isEquivalence Sto.isEquivalence
    ; trans         = transitive < Sto.trans
    ; compare       = trichotomous ≈ < Sto.compare
    ; <-resp-≈      = respects₂ < ≈ Sto.<-resp-≈
    }
    where module Sto = IsStrictTotalOrder sto

preorder : ∀ {p₁ p₂ p₃ b} {B : Set b} (P : Preorder p₁ p₂ p₃) →
           (B → Preorder.Carrier P) → Preorder _ _ _
preorder P f = record
  { isPreorder = isPreorder f (Preorder.isPreorder P)
  }

setoid : ∀ {s₁ s₂ b} {B : Set b} (S : Setoid s₁ s₂) →
         (B → Setoid.Carrier S) → Setoid _ _
setoid S f = record
  { isEquivalence = isEquivalence f (Setoid.isEquivalence S)
  }

decSetoid : ∀ {d₁ d₂ b} {B : Set b} (D : DecSetoid d₁ d₂) →
            (B → DecSetoid.Carrier D) → DecSetoid _ _
decSetoid D f = record
  { isDecEquivalence = isDecEquivalence f (DecSetoid.isDecEquivalence D)
  }

poset : ∀ {p₁ p₂ p₃ b} {B : Set b} (P : Poset p₁ p₂ p₃) →
        (B → Poset.Carrier P) → Poset _ _ _
poset P f = record
  { isPartialOrder = isPartialOrder f (Poset.isPartialOrder P)
  }

decPoset : ∀ {d₁ d₂ d₃ b} {B : Set b} (D : DecPoset d₁ d₂ d₃) →
           (B → DecPoset.Carrier D) → DecPoset _ _ _
decPoset D f = record
  { isDecPartialOrder =
      isDecPartialOrder f (DecPoset.isDecPartialOrder D)
  }

strictPartialOrder :
  ∀ {s₁ s₂ s₃ b} {B : Set b} (S : StrictPartialOrder s₁ s₂ s₃) →
  (B → StrictPartialOrder.Carrier S) → StrictPartialOrder _ _ _
strictPartialOrder S f = record
  { isStrictPartialOrder =
      isStrictPartialOrder f (StrictPartialOrder.isStrictPartialOrder S)
  }

totalOrder : ∀ {t₁ t₂ t₃ b} {B : Set b} (T : TotalOrder t₁ t₂ t₃) →
             (B → TotalOrder.Carrier T) → TotalOrder _ _ _
totalOrder T f = record
  { isTotalOrder = isTotalOrder f (TotalOrder.isTotalOrder T)
  }

decTotalOrder :
  ∀ {d₁ d₂ d₃ b} {B : Set b} (D : DecTotalOrder d₁ d₂ d₃) →
  (B → DecTotalOrder.Carrier D) → DecTotalOrder _ _ _
decTotalOrder D f = record
  { isDecTotalOrder = isDecTotalOrder f (DecTotalOrder.isDecTotalOrder D)
  }

strictTotalOrder :
  ∀ {s₁ s₂ s₃ b} {B : Set b} (S : StrictTotalOrder s₁ s₂ s₃) →
  (B → StrictTotalOrder.Carrier S) → StrictTotalOrder _ _ _
strictTotalOrder S f = record
  { isStrictTotalOrder =
      isStrictTotalOrder f (StrictTotalOrder.isStrictTotalOrder S)
  }


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

open import HoTT
open import cohomology.Exactness
open import cohomology.FunctionOver
open import cohomology.ProductRepr
open import cohomology.Theory
open import cohomology.WedgeCofiber

{- For the cohomology group of a suspension ΣX, the group inverse has the
 - explicit form Cⁿ(flip-susp) : Cⁿ(ΣX) → Cⁿ(ΣX).
 -}

module cohomology.InverseInSusp {i} (CT : CohomologyTheory i)
  (n : ℤ) {X : Ptd i} where

open CohomologyTheory CT
open import cohomology.Functor CT
open import cohomology.BaseIndependence CT
open import cohomology.Wedge CT

private
  module CW = CWedge n (⊙Susp X) (⊙Susp X)

  module Subtract = SuspensionRec (fst X) {C = fst (⊙Susp X ⊙∨ ⊙Susp X)}
    (winl (south _))
    (winr (south _))
    (λ x → ap winl (! (merid _ x)) ∙ wglue ∙ ap winr (merid _ x))

  subtract = Subtract.f

  ⊙subtract : fst (⊙Susp X ⊙→ ⊙Susp X ⊙∨ ⊙Susp X)
  ⊙subtract = (subtract , ! (ap winl (merid _ (snd X))))

  projl-subtract : ∀ σ → projl _ _ (subtract σ) == flip-susp σ
  projl-subtract = Suspension-elim (fst X) idp idp $
    ↓-='-from-square ∘ vert-degen-square ∘ λ x →
      ap-∘ (projl _ _) subtract (merid _ x)
      ∙ ap (ap (projl _ _)) (Subtract.glue-β x)
      ∙ ap-∙ (projl _ _) (ap winl (! (merid _ x))) (wglue ∙ ap winr (merid _ x))
      ∙ ((∘-ap (projl _ _) winl (! (merid _ x))
          ∙ ap-idf _)
         ∙2 (ap-∙ (projl _ _) wglue (ap winr (merid _ x))
             ∙ (Projl.glue-β _ _
                ∙2 (∘-ap (projl _ _) winr (merid _ x) ∙ ap-cst _ _))))
      ∙ ∙-unit-r _
      ∙ ! (FlipSusp.glue-β x)

  projr-subtract : ∀ σ → projr _ _ (subtract σ) == σ
  projr-subtract = Suspension-elim (fst X) idp idp $
    ↓-∘=idf-in (projr _ _) subtract ∘ λ x →
      ap (ap (projr _ _)) (Subtract.glue-β x)
      ∙ ap-∙ (projr _ _) (ap winl (! (merid _ x))) (wglue ∙ ap winr (merid _ x))
      ∙ ((∘-ap (projr _ _) winl (! (merid _ x)) ∙ ap-cst _ _)
         ∙2 (ap-∙ (projr _ _) wglue (ap winr (merid _ x))
             ∙ (Projr.glue-β _ _
                ∙2 (∘-ap (projr _ _) winr (merid _ x) ∙ ap-idf _))))

  fold-subtract : ∀ σ → fold (subtract σ) == south _
  fold-subtract = Suspension-elim (fst X) idp idp $
    ↓-app=cst-in ∘ ! ∘ λ x →
      ∙-unit-r _
      ∙ ap-∘ fold subtract (merid _ x)
      ∙ ap (ap fold) (Subtract.glue-β x)
      ∙ ap-∙ fold (ap winl (! (merid _ x))) (wglue ∙ ap winr (merid _ x))
      ∙ ((∘-ap fold winl (! (merid _ x)) ∙ ap-idf _)
         ∙2 (ap-∙ fold wglue (ap winr (merid _ x))
             ∙ (Fold.glue-β
                ∙2 (∘-ap fold winr (merid _ x) ∙ ap-idf _))))
      ∙ !-inv-l (merid _ x)

  cancel :
    ×ᴳ-sum-hom (C-abelian n _) (CF-hom n (⊙flip-susp X)) (idhom _) ∘ᴳ ×ᴳ-diag
    == cst-hom
  cancel =
    ap2 (λ φ ψ → ×ᴳ-sum-hom (C-abelian n _) φ ψ ∘ᴳ ×ᴳ-diag)
        (! (CF-λ= n projl-subtract))
        (! (CF-ident n) ∙ ! (CF-λ= n projr-subtract))
    ∙ transport (λ {(G , φ , ψ) → φ ∘ᴳ ψ == cst-hom})
        (pair= (CW.path) $ ↓-×-in
          (CW.wedge-in-over ⊙subtract)
          (CW.⊙wedge-rec-over (⊙idf _) (⊙idf _)
           ▹ ap2 ×ᴳ-hom-in (CF-ident n) (CF-ident n)))
        (! (CF-comp n ⊙fold ⊙subtract)
         ∙ CF-λ= n (λ σ → fold-subtract σ ∙ ! (merid _ (snd X)))
         ∙ CF-cst n)

C-flip-susp-is-inv :
  CF-hom n (⊙flip-susp X) == inv-hom (C n (⊙Susp X)) (C-abelian _ _)
C-flip-susp-is-inv = hom= _ _ $ λ= $ λ g →
  ! (group-inv-unique-l (C n (⊙Susp X)) _ g (app= (ap GroupHom.f cancel) g))


------------------------------------------------------------------------
-- Termination
------------------------------------------------------------------------

{-# OPTIONS --sized-types #-}

open import Prelude

module Delay-monad.Termination {a} {A : Type a} where

open import Equality.Propositional
open import Logical-equivalence using (_⇔_)
open import Prelude.Size

open import Bijection equality-with-J using (_↔_)
open import Double-negation equality-with-J
open import Monad equality-with-J

open import Delay-monad
open import Delay-monad.Bisimilarity hiding (_∎; step-≡ˡ)

-- Termination predicates.

Terminates : Size → Delay A ∞ → A → Type a
Terminates i x y = [ i ] now y ≈ x

infix 4 _⇓_

_⇓_ : Delay A ∞ → A → Type a
_⇓_ = Terminates ∞

-- Terminates i is pointwise isomorphic to Terminates ∞.
--
-- Note that Terminates carves out an "inductive fragment" of [_]_≈_:
-- the only "coinductive" constructor, later, does not target
-- Terminates.

Terminates↔⇓ : ∀ {i x y} → Terminates i x y ↔ x ⇓ y
Terminates↔⇓ = record
  { surjection = record
    { logical-equivalence = record
      { to   = to
      ; from = from _
      }
    ; right-inverse-of = to∘from
    }
  ; left-inverse-of = from∘to
  }
  where
  to : ∀ {i x y} → Terminates i x y → x ⇓ y
  to now        = now
  to (laterʳ p) = laterʳ (to p)

  from : ∀ i {x y} → x ⇓ y → Terminates i x y
  from _ now        = now
  from _ (laterʳ p) = laterʳ (from _ p)

  to∘from : ∀ {i x y} (p : x ⇓ y) → to (from i p) ≡ p
  to∘from now        = refl
  to∘from (laterʳ p) = cong laterʳ (to∘from p)

  from∘to : ∀ {i x y} (p : Terminates i x y) → from i (to p) ≡ p
  from∘to now        = refl
  from∘to (laterʳ p) = cong laterʳ (from∘to p)

-- The termination relation respects weak bisimilarity.
--
-- This function cannot be made size-preserving in its second argument
-- (unless A is uninhabited), see Delay-monad.Bisimilarity.Negative.

⇓-respects-≈ : ∀ {i x y z} →
               Terminates i x z → x ≈ y → Terminates i y z
⇓-respects-≈ = transitive-≈-now

-- If a computation does not terminate, then it is weakly bisimilar
-- to never.

¬⇓→⇑ : ∀ {i} x → ¬ (∃ λ y → x ⇓ y) → [ i ] never ≈ x
¬⇓→⇑ (now   x) ¬⇓ = ⊥-elim (¬⇓ (_ , now))
¬⇓→⇑ (later x) ¬⇓ = later λ { .force → ¬⇓→⇑ _ (¬⇓ ∘ Σ-map id laterʳ) }

-- In the double-negation monad every computation is weakly
-- bisimilar to either never or now something.

¬¬[⇑⊎⇓] : ∀ x → ¬¬ (never ≈ x ⊎ ∃ λ y → x ⇓ y)
¬¬[⇑⊎⇓] x = [ inj₂ , inj₁ ∘ ¬⇓→⇑ _ ] ⟨$⟩ excluded-middle

-- If A is a set, then the termination predicate is propositional.

Terminates-propositional :
  Is-set A → ∀ {i x y} → Is-proposition (Terminates i x y)
Terminates-propositional A-set {i} = λ p q → irr p q refl
  where
  irr :
    ∀ {x y y′}
    (p : [ i ] now y  ≈ x)
    (q : [ i ] now y′ ≈ x)
    (y≡y′ : y ≡ y′) →
    subst (([ i ]_≈ x) ∘ now) y≡y′ p ≡ q
  irr         (laterʳ p) (laterʳ q) refl = cong laterʳ (irr p q refl)
  irr {y = y} now        now        y≡y  =
    subst (([ i ]_≈ now y) ∘ now) y≡y  now  ≡⟨ cong (λ eq → subst (([ i ]_≈ now y) ∘ now) eq _) (A-set y≡y refl) ⟩
    subst (([ i ]_≈ now y) ∘ now) refl now  ≡⟨⟩
    now                                     ∎

-- If x terminates with y and z, then y is equal to z.

termination-value-unique :
  ∀ {i x y z} → Terminates i x y → Terminates i x z → y ≡ z
termination-value-unique now        now        = refl
termination-value-unique (laterʳ p) (laterʳ q) =
  termination-value-unique p q

-- If A is a set, then ∃ λ y → x ⇓ y is propositional.

∃-Terminates-propositional :
  Is-set A → ∀ {i x} → Is-proposition (∃ (Terminates i x))
∃-Terminates-propositional A-set (y₁ , x⇓y₁) (y₂ , x⇓y₂) =
  Σ-≡,≡→≡
    (termination-value-unique x⇓y₁ x⇓y₂)
    (Terminates-propositional A-set _ _)


{-# OPTIONS --rewriting #-}

data _≡_ {ℓ} {A : Set ℓ} (x : A) : A → Set ℓ where
  instance refl : x ≡ x

{-# BUILTIN REWRITE _≡_ #-}

postulate
  admit : ∀ {ℓ} {A : Set ℓ} → A
  X : Set

postulate
  Wrap : Set → Set

  wrap : {A : Set} → A → Wrap A

  rec : (A : Set) (P : Set) → (A → P) → Wrap A → P

  Rec : (A : Set) (P : Set₁) → (A → P) → Wrap A → P

  Rec-β : {A : Set} (P : Set₁) → ∀ f → (a : A) → Rec A P f (wrap a) ≡ f a

  {-# REWRITE Rec-β #-} -- bug disappears without this

record Σ {ℓ} (A : Set ℓ) (B : A → Set) : Set ℓ where
  constructor _,_
  field
    fst : A
    snd : B fst

open Σ public

-- bug disappears if Comp or isFib is not wrapped in a record

record Comp (A : X → Set) : Set where
  field
    comp : ∀ s → A s

open Comp public

record isFib {Γ : Set} (A : Γ → Set) : Set where
  field
    lift : (p : X → Γ) → Comp (λ x → A (p x))

open isFib public

compSys : (ψ : X → Set)
  (A : Σ X (λ x → Wrap (ψ x)) → Set)
  (u : ∀ s → Wrap (ψ s)) (s : X)
  → A (s , u s)
compSys ψ A u s =
  rec (∀ i → ψ i) (A (s , u s))
    (λ v →
      subst (λ s → wrap (v s)) (λ s → u s) admit
      .lift (λ s → s) .comp s)
    admit
  where
  subst : (w w' : ∀ i → Wrap (ψ i)) → isFib (λ s → A (s , w s)) → isFib (λ s → A (s , w' s))
  subst = admit

-- bug disappears if ×id is inlined
×id : {A A' : Set} {B : A' → Set} (f : A → A') → Σ A (λ x → B (f x)) → Σ A' B
×id f (a , b) = (f a , b)

fib : (ψ : X → Set) (A : Σ X (λ x → Wrap (ψ x)) → Set) → isFib A
fib ψ A .lift p .comp =
  compSys (λ x → ψ (p x .fst)) (λ xu → A (×id (λ x → p x .fst) xu)) (λ x → p x .snd)

-- bug seems to disappear if the underscore in (Σ Set _) is filled in
template : (ψ : X → Set) → X → Σ (Σ X (λ x → Wrap (ψ x)) → Set) isFib
template ψ n =
  (_ , fib ψ (λ b → Rec (ψ (b .fst)) (Σ Set _) (λ _ → admit) (b .snd) .fst))

eq : {B : Set₁} (f : X → B) {x y : X} → f x ≡ f y
eq = admit

templateEq : (ψ : X → Set) (n₀ : X) → template ψ n₀ ≡ template ψ n₀
templateEq ψ n₀ = eq (template ψ)