module Oscar.Data.AList where

open import Oscar.Data.Equality
open import Oscar.Data.Nat

open import Oscar.Level

module _ {a} (A : Nat → Set a) where

  data AList (n : Nat) : Nat → Set a where
    [] : AList n n
    _∷_ : ∀ {m} → A m → AList n m → AList n (suc m)

open import Oscar.Category

module Category' {a} {A : Nat → Set a} where

  _⊸_ = AList A

  infix 4 _≋_
  _≋_ : ∀ {m n} → m ⊸ n → m ⊸ n → Set a
  _≋_ = _≡_

  infixr 9 _∙_
  _∙_ : ∀ {m n} → m ⊸ n → ∀ {l} → l ⊸ m → l ⊸ n
  [] ∙ ρ = ρ
  (x ∷ σ) ∙ ρ = x ∷ (σ ∙ ρ)

  ∙-associativity : ∀ {k l} (f : k ⊸ l) {m} (g : l ⊸ m) {n} (h : m ⊸ n) → (h ∙ g) ∙ f ≋ h ∙ g ∙ f
  ∙-associativity f g [] = refl
  ∙-associativity f g (x ∷ h) = cong (x ∷_) (∙-associativity f g h)

  ε : ∀ {n} → n ⊸ n
  ε = []

  ∙-left-identity : ∀ {m n} (σ : m ⊸ n) → ε ∙ σ ≋ σ
  ∙-left-identity _ = refl

  ∙-right-identity : ∀ {m n} (σ : m ⊸ n) → σ ∙ ε ≋ σ
  ∙-right-identity [] = refl
  ∙-right-identity (x ∷ σ) = cong (x ∷_) (∙-right-identity σ)

  semigroupoidAList : Semigroupoid (AList A) (λ {x y} → ≡-setoid (AList A x y))
  Semigroupoid._∙_ semigroupoidAList = _∙_
  Semigroupoid.∙-extensionality semigroupoidAList refl refl = refl
  Semigroupoid.∙-associativity semigroupoidAList = ∙-associativity

  categoryAList : Category semigroupoidAList
  Category.ε categoryAList = ε
  Category.ε-left-identity categoryAList = ∙-left-identity _
  Category.ε-right-identity categoryAList = ∙-right-identity _

--   ∙-associativity σ _ [] = refl
--   ∙-associativity (τ asnoc t / x) ρ σ = cong (λ s → s asnoc t / x) (∙-associativity τ ρ σ)

-- -- semigroupoid : ∀ {a} (A : Nat → Set a) → Semigroupoid a a ∅̂
-- -- Semigroupoid._∙_ = semigroupoid

-- -- -- record Semigroupoid
-- -- --   {𝔬} {𝔒 :

-- -- -- -- module Oscar.Data.AList {𝔣} (FunctionName : Set 𝔣) where

-- -- -- -- open import Oscar.Data.Fin
-- -- -- -- open import Oscar.Data.Nat
-- -- -- -- open import Oscar.Data.Product
-- -- -- -- open import Oscar.Data.Term FunctionName
-- -- -- -- open import Oscar.Data.IList

-- -- -- -- pattern anil = []
-- -- -- -- pattern _asnoc_/_ σ t' x = (t' , x) ∷ σ

-- -- -- -- AList : Nat → Nat → Set 𝔣
-- -- -- -- AList m n = IList (λ m → Term m × Fin (suc m)) n m

-- -- -- -- -- data AList' (n : Nat) : Nat -> Set 𝔣 where
-- -- -- -- --   anil : AList' n n
-- -- -- -- --   _asnoc_/_ : ∀ {m} (σ : AList' n m) (t' : Term m) (x : Fin (suc m))
-- -- -- -- --                -> AList' n (suc m)

-- -- -- -- -- open import Oscar.Function
-- -- -- -- -- --AList = flip AList'

-- -- -- -- -- -- data AList : Nat -> Nat -> Set 𝔣 where
-- -- -- -- -- --   anil : ∀ {n} -> AList n n
-- -- -- -- -- --   _asnoc_/_ : ∀ {m n} (σ : AList m n) (t' : Term m) (x : Fin (suc m))
-- -- -- -- -- --                -> AList (suc m) n

-- -- -- -- -- -- -- open import Relation.Binary.PropositionalEquality renaming ([_] to [[_]])
-- -- -- -- -- -- -- open ≡-Reasoning

-- -- -- -- -- -- -- step-prop : ∀ {m n} (s t : Term (suc m)) (σ : AList m n) r z ->
-- -- -- -- -- -- --           (Unifies s t [-◇ sub (σ asnoc r / z) ]) ⇔ (Unifies ((r for z) ◃ s) ((r for z) ◃ t) [-◇ sub σ ])
-- -- -- -- -- -- -- step-prop s t σ r z f = to , from
-- -- -- -- -- -- --   where
-- -- -- -- -- -- --     lemma1 : ∀ t -> (f ◇ sub σ) ◃ ((r for z) ◃ t) ≡ (f ◇ (sub σ ◇ (r for z))) ◃ t
-- -- -- -- -- -- --     lemma1 t = bind-assoc f (sub σ) (r for z) t
-- -- -- -- -- -- --     to = λ a → begin
-- -- -- -- -- -- --                  (f ◇ sub σ) ◃ ((r for z) ◃ s) ≡⟨ lemma1 s ⟩
-- -- -- -- -- -- --                  (f ◇ (sub σ ◇ (r for z))) ◃ s ≡⟨ a ⟩
-- -- -- -- -- -- --                  (f ◇ (sub σ ◇ (r for z))) ◃ t ≡⟨ sym (lemma1 t) ⟩
-- -- -- -- -- -- --                  (f ◇ sub σ) ◃ ((r for z) ◃ t) ∎
-- -- -- -- -- -- --     from = λ a → begin
-- -- -- -- -- -- --                    (f ◇ (sub σ ◇ (r for z))) ◃ s ≡⟨ sym (lemma1 s) ⟩
-- -- -- -- -- -- --                    (f ◇ sub σ) ◃ ((r for z) ◃ s) ≡⟨ a ⟩
-- -- -- -- -- -- --                    (f ◇ sub σ) ◃ ((r for z) ◃ t) ≡⟨ lemma1 t ⟩
-- -- -- -- -- -- --                    (f ◇ (sub σ ◇ (r for z))) ◃ t ∎

-- -- -- -- -- -- -- record ⋆amgu (T : ℕ → Set) : Set where
-- -- -- -- -- -- --   field amgu : ∀ {m} (s t : T m) (acc : ∃ (AList m)) -> Maybe (∃ (AList m))

-- -- -- -- -- -- -- open ⋆amgu ⦃ … ⦄ public

-- -- -- -- -- -- -- open import Category.Monad using (RawMonad)
-- -- -- -- -- -- -- import Level
-- -- -- -- -- -- -- open RawMonad (Data.Maybe.monad {Level.zero})

-- -- -- -- -- -- -- open import Relation.Binary using (IsDecEquivalence)
-- -- -- -- -- -- -- open import Relation.Nullary using (Dec; yes; no)
-- -- -- -- -- -- -- open import Data.Nat using (ℕ; _≟_)
-- -- -- -- -- -- -- open import Data.Product using () renaming (Σ to Σ₁)
-- -- -- -- -- -- -- open Σ₁ renaming (proj₁ to π₁; proj₂ to π₂)
-- -- -- -- -- -- -- open import Data.Product using (Σ)
-- -- -- -- -- -- -- open Σ
-- -- -- -- -- -- -- open import Data.Sum
-- -- -- -- -- -- -- open import Function

-- -- -- -- -- -- -- NothingForkLeft : ∀ {m l} (s1 t1 : Term m) (ρ : AList m l) (s2 t2 : Term m) →
-- -- -- -- -- -- --                Nothing⋆ $ Unifies⋆ s1 t1 [-◇⋆ sub ρ ] →
-- -- -- -- -- -- --                Nothing⋆ $ Unifies⋆ (s1 fork s2) (t1 fork t2) [-◇⋆ sub ρ ]
-- -- -- -- -- -- -- NothingForkLeft s1 t1 ρ s2 t2 nounify = No[Q◇ρ]→No[P◇ρ] No[Q◇ρ]
-- -- -- -- -- -- --   where
-- -- -- -- -- -- --     P = Unifies⋆ (s1 fork s2) (t1 fork t2)
-- -- -- -- -- -- --     Q = (Unifies⋆ s1 t1 ∧⋆ Unifies⋆ s2 t2)
-- -- -- -- -- -- --     Q⇔P : Q ⇔⋆ P
-- -- -- -- -- -- --     Q⇔P = switch⋆ P Q (Properties.fact1' {_} {s1} {s2} {t1} {t2})
-- -- -- -- -- -- --     No[Q◇ρ]→No[P◇ρ] : Nothing⋆ (Q [-◇⋆ sub ρ ]) -> Nothing⋆ (P [-◇⋆ sub ρ ])
-- -- -- -- -- -- --     No[Q◇ρ]→No[P◇ρ] = Properties.fact2⋆ (Q [-◇⋆ sub ρ ]) (P [-◇⋆ sub ρ ]) (Properties.fact5⋆ Q P (sub ρ) Q⇔P)
-- -- -- -- -- -- --     No[Q◇ρ] : Nothing⋆ (Q [-◇⋆ sub ρ ])
-- -- -- -- -- -- --     No[Q◇ρ] = failure-propagation.first⋆ (sub ρ) (Unifies⋆ s1 t1) (Unifies⋆ s2 t2) nounify

-- -- -- -- -- -- -- NothingForkRight : ∀ {m l n} (σ : AList l n)
-- -- -- -- -- -- --                   (s1 s2 : Term m)
-- -- -- -- -- -- --                   (t1 t2 : Term m) →
-- -- -- -- -- -- --                   (ρ : AList m l) →
-- -- -- -- -- -- --                   Max⋆ (Unifies⋆ s1 t1 [-◇⋆ sub ρ ]) $ sub σ →
-- -- -- -- -- -- --                   Nothing⋆ $ Unifies⋆ s2 t2 [-◇⋆ sub (σ ++ ρ) ] →
-- -- -- -- -- -- --                   Nothing⋆ $ Unifies⋆ (s1 fork s2) (t1 fork t2) [-◇⋆ sub ρ ]
-- -- -- -- -- -- -- NothingForkRight σ s1 s2 t1 t2 ρ a nounify = No[Q◇ρ]→No[P◇ρ]⋆ No[Q◇ρ]⋆
-- -- -- -- -- -- --     where
-- -- -- -- -- -- --     P⋆ = Unifies⋆ (s1 fork s2) (t1 fork t2)
-- -- -- -- -- -- --     Q⋆ = (Unifies⋆ s1 t1 ∧⋆ Unifies⋆ s2 t2)
-- -- -- -- -- -- --     Q⇔P⋆ : Q⋆ ⇔⋆ P⋆
-- -- -- -- -- -- --     Q⇔P⋆ = switch⋆ P⋆ Q⋆ (Properties.fact1'⋆ {_} {s1} {s2} {t1} {t2})
-- -- -- -- -- -- --     No[Q◇ρ]→No[P◇ρ]⋆ : Nothing⋆ (Q⋆ [-◇⋆ sub ρ ]) -> Nothing⋆ (P⋆ [-◇⋆ sub ρ ])
-- -- -- -- -- -- --     No[Q◇ρ]→No[P◇ρ]⋆ = Properties.fact2⋆ (Q⋆ [-◇⋆ sub ρ ]) (P⋆ [-◇⋆ sub ρ ]) (Properties.fact5⋆ Q⋆ P⋆ (sub ρ) Q⇔P⋆)
-- -- -- -- -- -- --     No[Q◇ρ]⋆ : Nothing⋆ (Q⋆ [-◇⋆ sub ρ ])
-- -- -- -- -- -- --     No[Q◇ρ]⋆ = failure-propagation.second⋆ (sub ρ) (sub σ) (Unifies⋆ s1 t1) (Unifies s2 t2) a
-- -- -- -- -- -- --        (λ f → nounify f ∘ π₂ (Unifies s2 t2) (cong (f ◃_) ∘ sym ∘ SubList.fact1 σ ρ))

-- -- -- -- -- -- -- MaxFork : ∀ {m l n n1} (s1 s2 t1 t2 : Term m)
-- -- -- -- -- -- --                        (ρ : AList m l) (σ : AList l n) →
-- -- -- -- -- -- --                      Max⋆ (Unifies⋆ s1 t1 [-◇⋆ sub ρ ]) $ sub σ →
-- -- -- -- -- -- --                      (σ1 : AList n n1) →
-- -- -- -- -- -- --                      Max⋆ (Unifies⋆ s2 t2 [-◇⋆ sub (σ ++ ρ) ]) $ sub σ1 →
-- -- -- -- -- -- --                      Max⋆ (Unifies⋆ (s1 fork s2) (t1 fork t2) [-◇⋆ sub ρ ]) $ sub (σ1 ++ σ)
-- -- -- -- -- -- -- MaxFork s1 s2 t1 t2 ρ σ a σ1 b = Max[P∧Q◇ρ][σ1++σ]
-- -- -- -- -- -- --     where
-- -- -- -- -- -- --       P = Unifies s1 t1
-- -- -- -- -- -- --       Q = Unifies s2 t2
-- -- -- -- -- -- --       P∧Q = P ∧ Q
-- -- -- -- -- -- --       C = Unifies (s1 fork s2) (t1 fork t2)
-- -- -- -- -- -- --       Max[C◇ρ]⇔Max[P∧Q◇ρ] : Max (C [-◇ sub ρ ]) ⇔ Max (P∧Q [-◇ sub ρ ])
-- -- -- -- -- -- --       Max[C◇ρ]⇔Max[P∧Q◇ρ] = Max.fact (C [-◇ sub ρ ]) (P∧Q [-◇ sub ρ ]) (Properties.fact5 C P∧Q (sub ρ)
-- -- -- -- -- -- --                       (Properties.fact1' {_} {s1} {s2} {t1} {t2}))
-- -- -- -- -- -- --       Max[Q◇σ++ρ]⇔Max[Q◇σ◇ρ] : Max (Q [-◇ sub (σ ++ ρ)]) ⇔ Max (Q [-◇ sub σ ◇ sub ρ ])
-- -- -- -- -- -- --       Max[Q◇σ++ρ]⇔Max[Q◇σ◇ρ] = Max.fact (Q [-◇ sub (σ ++ ρ)]) (Q [-◇ sub σ ◇ sub ρ ]) (Properties.fact6 Q (SubList.fact1 σ ρ))
-- -- -- -- -- -- --       Max[P∧Q◇ρ][σ1++σ] : π₁ (Max (C [-◇ sub ρ ])) (sub (σ1 ++ σ))
-- -- -- -- -- -- --       Max[P∧Q◇ρ][σ1++σ] = π₂ (Max (C [-◇ sub ρ ])) (≐-sym (SubList.fact1 σ1 σ))
-- -- -- -- -- -- --                (proj₂ (Max[C◇ρ]⇔Max[P∧Q◇ρ] (sub σ1 ◇ sub σ))
-- -- -- -- -- -- --                       (optimist (sub ρ) (sub σ) (sub σ1) P Q (DClosed.fact1 s1 t1) a (proj₁ (Max[Q◇σ++ρ]⇔Max[Q◇σ◇ρ] (sub σ1)) b)))

-- -- -- -- -- -- -- NothingVecHead : ∀ {m l} (t₁ t₂ : Term m) (ρ : AList m l) {N} (ts₁ ts₂ : Vec (Term m) N) →
-- -- -- -- -- -- --                Nothing⋆ $ Unifies⋆ t₁ t₂ [-◇⋆ sub ρ ] →
-- -- -- -- -- -- --                Nothing⋆ $ Unifies⋆V (t₁ ∷ ts₁) (t₂ ∷ ts₂) [-◇⋆ sub ρ ]
-- -- -- -- -- -- -- NothingVecHead t₁ t₂ ρ ts₁ ts₂ nounify = No[Q◇ρ]→No[P◇ρ] No[Q◇ρ]
-- -- -- -- -- -- --   where
-- -- -- -- -- -- --     P = Unifies⋆V (t₁ ∷ ts₁) (t₂ ∷ ts₂)
-- -- -- -- -- -- --     Q = (Unifies⋆ t₁ t₂ ∧⋆ Unifies⋆V ts₁ ts₂)
-- -- -- -- -- -- --     Q⇔P : Q ⇔⋆ P
-- -- -- -- -- -- --     Q⇔P = switch⋆ P Q (Properties.fact1'V {_} {t₁} {t₂} {_} {ts₁} {ts₂})
-- -- -- -- -- -- --     No[Q◇ρ]→No[P◇ρ] : Nothing⋆ (Q [-◇⋆ sub ρ ]) -> Nothing⋆ (P [-◇⋆ sub ρ ])
-- -- -- -- -- -- --     No[Q◇ρ]→No[P◇ρ] = Properties.fact2⋆ (Q [-◇⋆ sub ρ ]) (P [-◇⋆ sub ρ ]) (Properties.fact5⋆ Q P (sub ρ) Q⇔P)
-- -- -- -- -- -- --     No[Q◇ρ] : Nothing⋆ (Q [-◇⋆ sub ρ ])
-- -- -- -- -- -- --     No[Q◇ρ] = failure-propagation.first⋆ (sub ρ) (Unifies⋆ t₁ t₂) (Unifies⋆V ts₁ ts₂) nounify

-- -- -- -- -- -- -- NothingVecTail : ∀ {m l n} (σ : AList l n)
-- -- -- -- -- -- --                   (t₁ t₂ : Term m)
-- -- -- -- -- -- --                   {N}
-- -- -- -- -- -- --                   (ts₁ ts₂ : Vec (Term m) N) →
-- -- -- -- -- -- --                   (ρ : AList m l) →
-- -- -- -- -- -- --                   Max⋆ (Unifies⋆ t₁ t₂ [-◇⋆ sub ρ ]) $ sub σ →
-- -- -- -- -- -- --                   Nothing⋆ $ Unifies⋆V ts₁ ts₂ [-◇⋆ sub (σ ++ ρ) ] →
-- -- -- -- -- -- --                   Nothing⋆ $ Unifies⋆V (t₁ ∷ ts₁) (t₂ ∷ ts₂) [-◇⋆ sub ρ ]
-- -- -- -- -- -- -- NothingVecTail σ t₁ t₂ ts₁ ts₂ ρ a nounify = No[Q◇ρ]→No[P◇ρ]⋆ No[Q◇ρ]⋆
-- -- -- -- -- -- --     where
-- -- -- -- -- -- --     P⋆ = Unifies⋆V (t₁ ∷ ts₁) (t₂ ∷ ts₂)
-- -- -- -- -- -- --     Q⋆ = (Unifies⋆ t₁ t₂ ∧⋆ Unifies⋆V ts₁ ts₂)
-- -- -- -- -- -- --     Q⇔P⋆ : Q⋆ ⇔⋆ P⋆
-- -- -- -- -- -- --     Q⇔P⋆ = switch⋆ P⋆ Q⋆ (Properties.fact1'V {_} {t₁} {t₂} {_} {ts₁} {ts₂})
-- -- -- -- -- -- -- --    Q⇔P⋆ = switch⋆ P⋆ Q⋆ (Properties.fact1'⋆ {_} {s1} {s2} {t1} {t2})
-- -- -- -- -- -- --     No[Q◇ρ]→No[P◇ρ]⋆ : Nothing⋆ (Q⋆ [-◇⋆ sub ρ ]) -> Nothing⋆ (P⋆ [-◇⋆ sub ρ ])
-- -- -- -- -- -- --     No[Q◇ρ]→No[P◇ρ]⋆ = Properties.fact2⋆ (Q⋆ [-◇⋆ sub ρ ]) (P⋆ [-◇⋆ sub ρ ]) (Properties.fact5⋆ Q⋆ P⋆ (sub ρ) Q⇔P⋆)
-- -- -- -- -- -- --     No[Q◇ρ]⋆ : Nothing⋆ (Q⋆ [-◇⋆ sub ρ ])
-- -- -- -- -- -- --     No[Q◇ρ]⋆ = failure-propagation.second⋆ (sub ρ) (sub σ) (Unifies⋆ t₁ t₂) (UnifiesV ts₁ ts₂) a
-- -- -- -- -- -- --        (λ f → nounify f ∘ π₂ (UnifiesV ts₁ ts₂) (cong (f ◃_) ∘ sym ∘ SubList.fact1 σ ρ))

-- -- -- -- -- -- -- MaxVec : ∀ {m l n n1} (t₁ t₂ : Term m) {N} (ts₁ ts₂ : Vec (Term m) N)
-- -- -- -- -- -- --                        (ρ : AList m l) (σ : AList l n) →
-- -- -- -- -- -- --                      Max⋆ (Unifies⋆ t₁ t₂ [-◇⋆ sub ρ ]) $ sub σ →
-- -- -- -- -- -- --                      (σ1 : AList n n1) →
-- -- -- -- -- -- --                      Max⋆ (Unifies⋆V ts₁ ts₂ [-◇⋆ sub (σ ++ ρ) ]) $ sub σ1 →
-- -- -- -- -- -- --                      Max⋆ (Unifies⋆V (t₁ ∷ ts₁) (t₂ ∷ ts₂) [-◇⋆ sub ρ ]) $ sub (σ1 ++ σ)
-- -- -- -- -- -- -- MaxVec t₁ t₂ ts₁ ts₂ ρ σ a σ1 b = Max[P∧Q◇ρ][σ1++σ]
-- -- -- -- -- -- --     where
-- -- -- -- -- -- --       P = Unifies t₁ t₂
-- -- -- -- -- -- --       Q = UnifiesV ts₁ ts₂
-- -- -- -- -- -- --       P∧Q = P ∧ Q
-- -- -- -- -- -- --       C = UnifiesV (t₁ ∷ ts₁) (t₂ ∷ ts₂)
-- -- -- -- -- -- --       Max[C◇ρ]⇔Max[P∧Q◇ρ] : Max (C [-◇ sub ρ ]) ⇔ Max (P∧Q [-◇ sub ρ ])
-- -- -- -- -- -- --       Max[C◇ρ]⇔Max[P∧Q◇ρ] = Max.fact (C [-◇ sub ρ ]) (P∧Q [-◇ sub ρ ]) (Properties.fact5 C P∧Q (sub ρ)
-- -- -- -- -- -- --                       (Properties.fact1'V {_} {t₁} {t₂} {_} {ts₁} {ts₂}))
-- -- -- -- -- -- --       Max[Q◇σ++ρ]⇔Max[Q◇σ◇ρ] : Max (Q [-◇ sub (σ ++ ρ)]) ⇔ Max (Q [-◇ sub σ ◇ sub ρ ])
-- -- -- -- -- -- --       Max[Q◇σ++ρ]⇔Max[Q◇σ◇ρ] = Max.fact (Q [-◇ sub (σ ++ ρ)]) (Q [-◇ sub σ ◇ sub ρ ]) (Properties.fact6 Q (SubList.fact1 σ ρ))
-- -- -- -- -- -- --       Max[P∧Q◇ρ][σ1++σ] : π₁ (Max (C [-◇ sub ρ ])) (sub (σ1 ++ σ))
-- -- -- -- -- -- --       Max[P∧Q◇ρ][σ1++σ] = π₂ (Max (C [-◇ sub ρ ])) (≐-sym (SubList.fact1 σ1 σ))
-- -- -- -- -- -- --                (proj₂ (Max[C◇ρ]⇔Max[P∧Q◇ρ] (sub σ1 ◇ sub σ))
-- -- -- -- -- -- --                       (optimist (sub ρ) (sub σ) (sub σ1) P Q (DClosed.fact1 t₁ t₂) a (proj₁ (Max[Q◇σ++ρ]⇔Max[Q◇σ◇ρ] (sub σ1)) b)))

-- -- -- -- -- -- -- NothingFor→NothingComposition : ∀ {m l} (s t : Term (suc m)) (ρ : AList m l)
-- -- -- -- -- -- --              (r : Term m) (z : Fin (suc m)) →
-- -- -- -- -- -- --            Nothing⋆ (Unifies⋆ ((r for z) ◃ s) ((r for z) ◃ t) [-◇⋆ sub ρ ]) →
-- -- -- -- -- -- --            Nothing⋆ (Unifies⋆ s t [-◇⋆ sub (ρ asnoc r / z) ])
-- -- -- -- -- -- -- NothingFor→NothingComposition s t ρ r z nounify = NoQ→NoP nounify
-- -- -- -- -- -- --       where
-- -- -- -- -- -- --         P = Unifies s t [-◇ sub (ρ asnoc r / z) ]
-- -- -- -- -- -- --         Q = Unifies ((r for z) ◃ s) ((r for z) ◃ t) [-◇ sub ρ ]
-- -- -- -- -- -- --         NoQ→NoP : Nothing Q → Nothing P
-- -- -- -- -- -- --         NoQ→NoP = Properties.fact2 Q P (switch P Q (step-prop s t ρ r z))

-- -- -- -- -- -- -- MaxFor→MaxComposition : ∀ {m l n} (s t : Term (suc m)) (ρ : AList m l)
-- -- -- -- -- -- --               (r : Term m) (z : Fin (suc m)) (σ : AList l n) →
-- -- -- -- -- -- --             Max⋆ (Unifies⋆ ((r for z) ◃ s) ((r for z) ◃ t) [-◇⋆ sub ρ ]) $ sub σ →
-- -- -- -- -- -- --             Max⋆ (Unifies⋆ s t [-◇⋆ sub (ρ asnoc r / z) ]) $ sub σ
-- -- -- -- -- -- -- MaxFor→MaxComposition s t ρ r z σ a = proj₂ (MaxP⇔MaxQ (sub σ)) a
-- -- -- -- -- -- --       where
-- -- -- -- -- -- --         P = Unifies s t [-◇ sub (ρ asnoc r / z) ]
-- -- -- -- -- -- --         Q = Unifies ((r for z) ◃ s) ((r for z) ◃ t) [-◇ sub ρ ]
-- -- -- -- -- -- --         MaxP⇔MaxQ : Max P ⇔ Max Q
-- -- -- -- -- -- --         MaxP⇔MaxQ = Max.fact P Q (step-prop s t ρ r z)

-- -- -- -- -- -- -- module _ ⦃ isDecEquivalenceA : IsDecEquivalence (_≡_ {A = FunctionName}) ⦄ where

-- -- -- -- -- -- --   open IsDecEquivalence isDecEquivalenceA using () renaming (_≟_ to _≟F_)

-- -- -- -- -- -- --   mutual

-- -- -- -- -- -- --     instance ⋆amguTerm : ⋆amgu Term
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm leaf leaf acc = just acc
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm leaf (function _ _) acc = nothing
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm leaf (s' fork t') acc = nothing
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm (s' fork t') leaf acc = nothing
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm (s' fork t') (function _ _) acc = nothing
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm (s1 fork s2) (t1 fork t2) acc =
-- -- -- -- -- -- --                       amgu s2 t2 =<< amgu s1 t1 acc
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm (function fn₁ ts₁) leaf acc = nothing
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm (function fn₁ {n₁} ts₁) (function fn₂ {n₂} ts₂) acc
-- -- -- -- -- -- --      with fn₁ ≟F fn₂
-- -- -- -- -- -- --     … | no _ = nothing
-- -- -- -- -- -- --     … | yes _ with n₁ ≟ n₂
-- -- -- -- -- -- --     … | no _ = nothing
-- -- -- -- -- -- --     … | yes refl = amgu ts₁ ts₂ acc
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm (function fn₁ ts₁) (_ fork _) acc = nothing
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm (i x) (i y) (m , anil) = just (flexFlex x y)
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm (i x) t     (m , anil) = flexRigid x t
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm t     (i x) (m , anil) = flexRigid x t
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguTerm s     t  (n , σ asnoc r / z) =
-- -- -- -- -- -- --              (λ σ -> σ ∃asnoc r / z) <$>
-- -- -- -- -- -- --                amgu ((r for z) ◃ s) ((r for z) ◃ t) (n , σ)

-- -- -- -- -- -- --     instance ⋆amguVecTerm : ∀ {N} → ⋆amgu (flip Vec N ∘ Term)
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguVecTerm [] [] acc = just acc
-- -- -- -- -- -- --     ⋆amgu.amgu ⋆amguVecTerm (t₁ ∷ t₁s) (t₂ ∷ t₂s) acc = amgu t₁s t₂s =<< amgu t₁ t₂ acc

-- -- -- -- -- -- --   mgu : ∀ {m} -> (s t : Term m) -> Maybe (∃ (AList m))
-- -- -- -- -- -- --   mgu {m} s t = amgu s t (m , anil)

-- -- -- -- -- -- --   mutual

-- -- -- -- -- -- --     -- We use a view so that we need to handle fewer cases in the main proof
-- -- -- -- -- -- --     data AmguT : {m : ℕ} -> (s t : Term m) -> ∃ (AList m) -> Maybe (∃ (AList m)) -> Set where
-- -- -- -- -- -- --       Flip : ∀ {m s t acc} -> amgu t s acc ≡ amgu s t acc ->
-- -- -- -- -- -- --         AmguT {m} t s acc (amgu t s acc) -> AmguT         s            t            acc        (amgu s t acc)
-- -- -- -- -- -- --       leaf-leaf : ∀ {m acc} ->             AmguT {m}     leaf         leaf         acc        (just acc)
-- -- -- -- -- -- --       fn-name   : ∀ {m fn₁ fn₂ N₁ N₂ acc} {ts₁ : Vec (Term m) N₁} {ts₂ : Vec (Term m) N₂} →
-- -- -- -- -- -- --                     fn₁ ≢ fn₂ →
-- -- -- -- -- -- --                                            AmguT {m}     (function fn₁ ts₁)
-- -- -- -- -- -- --                                                                      (function fn₂ ts₂)
-- -- -- -- -- -- --                                                                                acc        nothing
-- -- -- -- -- -- --       fn-size   : ∀ {m fn₁ fn₂ N₁ N₂ acc} {ts₁ : Vec (Term m) N₁} {ts₂ : Vec (Term m) N₂} →
-- -- -- -- -- -- --                     N₁ ≢ N₂ →
-- -- -- -- -- -- --                                            AmguT {m}     (function fn₁ ts₁)
-- -- -- -- -- -- --                                                                      (function fn₂ ts₂)
-- -- -- -- -- -- --                                                                                acc        nothing
-- -- -- -- -- -- --       fn-fn     : ∀ {m fn N acc} {ts₁ ts₂ : Vec (Term m) N} →
-- -- -- -- -- -- --                                            AmguT {m}     (function fn ts₁)
-- -- -- -- -- -- --                                                                      (function fn ts₂)
-- -- -- -- -- -- --                                                                                acc        (amgu ts₁ ts₂ acc)
-- -- -- -- -- -- --       leaf-fork : ∀ {m s t acc} ->         AmguT {m}     leaf         (s fork t)   acc        nothing
-- -- -- -- -- -- --       leaf-fn   : ∀ {m fn N} {ts : Vec (Term _) N} {acc} ->       AmguT {m}     leaf         (function fn ts)   acc        nothing
-- -- -- -- -- -- --       fork-fn   : ∀ {m s t fn N} {ts : Vec (Term _) N} {acc} ->   AmguT {m}     (s fork t)   (function fn ts)   acc        nothing
-- -- -- -- -- -- --       fork-fork : ∀ {m s1 s2 t1 t2 acc} -> AmguT {m}     (s1 fork s2) (t1 fork t2) acc        (amgu s2 t2 =<< amgu s1 t1 acc)
-- -- -- -- -- -- --       var-var   : ∀ {m x y} ->             AmguT         (i x)        (i y)        (m , anil) (just (flexFlex x y))
-- -- -- -- -- -- --       var-t : ∀ {m x t} -> i x ≢ t ->      AmguT         (i x)        t            (m , anil) (flexRigid x t)
-- -- -- -- -- -- --       s-t : ∀{m s t n σ r z} ->            AmguT {suc m} s            t            (n , σ asnoc r / z) ((λ σ -> σ ∃asnoc r / z) <$>
-- -- -- -- -- -- --                                                                                amgu ((r for z) ◃ s) ((r for z) ◃ t) (n , σ))

-- -- -- -- -- -- --     data AmguTV : {m : ℕ} -> ∀ {N} (ss ts : Vec (Term m) N) -> ∃ (AList m) -> Maybe (∃ (AList m)) -> Set where
-- -- -- -- -- -- --       fn0-fn0   : ∀ {m acc} →
-- -- -- -- -- -- --                                            AmguTV {m}     ([])
-- -- -- -- -- -- --                                                                      ([])
-- -- -- -- -- -- --                                                                                acc        (just acc)

-- -- -- -- -- -- --       fns-fns   : ∀ {m N acc} {t₁ t₂ : Term m} {ts₁ ts₂ : Vec (Term m) N} →
-- -- -- -- -- -- --                                            AmguTV {m}     ((t₁ ∷ ts₁))
-- -- -- -- -- -- --                                                                      ((t₂ ∷ ts₂))
-- -- -- -- -- -- --                                                                                acc        (amgu ts₁ ts₂ =<< amgu t₁ t₂ acc)

-- -- -- -- -- -- --   view : ∀ {m : ℕ} -> (s t : Term m) -> (acc : ∃ (AList m)) -> AmguT s t acc (amgu s t acc)
-- -- -- -- -- -- --   view leaf         leaf         acc        = leaf-leaf
-- -- -- -- -- -- --   view leaf         (s fork t)   acc        = leaf-fork
-- -- -- -- -- -- --   view (s fork t)   leaf         acc        = Flip refl leaf-fork
-- -- -- -- -- -- --   view (s1 fork s2) (t1 fork t2) acc        = fork-fork
-- -- -- -- -- -- --   view (function fn₁ {n₁} ts₁) (function fn₂ {n₂} ts₂) acc  with
-- -- -- -- -- -- --    fn₁ ≟F fn₂
-- -- -- -- -- -- --   … | no neq = fn-name neq
-- -- -- -- -- -- --   … | yes refl with n₁ ≟ n₂
-- -- -- -- -- -- --   … | yes refl = fn-fn
-- -- -- -- -- -- --   … | no neq = fn-size neq
-- -- -- -- -- -- --   view leaf         (function fn ts) acc    = leaf-fn
-- -- -- -- -- -- --   view (function fn ts) leaf         acc    = Flip refl leaf-fn
-- -- -- -- -- -- --   view (function fn ts) (_ fork _)   acc    = Flip refl fork-fn
-- -- -- -- -- -- --   view (s fork t)   (function fn ts) acc    = fork-fn
-- -- -- -- -- -- --   view (i x)        (i y)        (m , anil) = var-var
-- -- -- -- -- -- --   view (i x)        leaf         (m , anil) = var-t (λ ())
-- -- -- -- -- -- --   view (i x)        (s fork t)   (m , anil) = var-t (λ ())
-- -- -- -- -- -- --   view (i x)        (function fn ts)   (m , anil) = var-t (λ ())
-- -- -- -- -- -- --   view leaf         (i x)        (m , anil) = Flip refl (var-t (λ ()))
-- -- -- -- -- -- --   view (s fork t)   (i x)        (m , anil) = Flip refl (var-t (λ ()))
-- -- -- -- -- -- --   view (function fn ts) (i x)    (m , anil) = Flip refl (var-t (λ ()))
-- -- -- -- -- -- --   view (i x)        (i x')       (n , σ asnoc r / z) = s-t
-- -- -- -- -- -- --   view (i x)        leaf         (n , σ asnoc r / z) = s-t
-- -- -- -- -- -- --   view (i x)        (s fork t)   (n , σ asnoc r / z) = s-t
-- -- -- -- -- -- --   view leaf         (i x)        (n , σ asnoc r / z) = s-t
-- -- -- -- -- -- --   view (s fork t)   (i x)        (n , σ asnoc r / z) = s-t
-- -- -- -- -- -- --   view (function fn ts)   (i x)        (n , σ asnoc r / z) = s-t
-- -- -- -- -- -- --   view (i x) (function fn ts)          (n , σ asnoc r / z) = s-t

-- -- -- -- -- -- --   viewV : ∀ {m : ℕ} {N} -> (ss ts : Vec (Term m) N) -> (acc : ∃ (AList m)) -> AmguTV ss ts acc (amgu ss ts acc)
-- -- -- -- -- -- --   viewV []         []         acc        = fn0-fn0
-- -- -- -- -- -- --   viewV (t ∷ ts₁) (t₂ ∷ ts₂) acc         = fns-fns

-- -- -- -- -- -- --   amgu-Correctness : {m : ℕ} -> (s t : Term m) -> ∃ (AList m) -> Set
-- -- -- -- -- -- --   amgu-Correctness s t (l , ρ) =
-- -- -- -- -- -- --       (∃ λ n → ∃ λ σ → π₁ (Max (Unifies s t [-◇ sub ρ ])) (sub σ) × amgu s t (l , ρ) ≡ just (n , σ ++ ρ ))
-- -- -- -- -- -- --     ⊎ (Nothing ((Unifies s t) [-◇ sub ρ ]) ×  amgu s t (l , ρ) ≡ nothing)

-- -- -- -- -- -- --   amgu-Correctness⋆ : {m : ℕ} -> (s t : Term m) -> ∃ (AList m) -> Set
-- -- -- -- -- -- --   amgu-Correctness⋆ s t (l , ρ) =
-- -- -- -- -- -- --       (∃ λ n → ∃ λ σ → π₁ (Max (Unifies s t [-◇ sub ρ ])) (sub σ) × amgu s t (l , ρ) ≡ just (n , σ ++ ρ ))
-- -- -- -- -- -- --     ⊎ (Nothing ((Unifies s t) [-◇ sub ρ ]) ×  amgu s t (l , ρ) ≡ nothing)

-- -- -- -- -- -- --   amgu-Ccomm : ∀ {m} s t acc -> amgu {m = m} s t acc ≡ amgu t s acc -> amgu-Correctness s t acc -> amgu-Correctness t s acc
-- -- -- -- -- -- --   amgu-Ccomm s t (l , ρ) st≡ts = lemma
-- -- -- -- -- -- --     where
-- -- -- -- -- -- --       Unst = (Unifies s t) [-◇ sub ρ ]
-- -- -- -- -- -- --       Unts = (Unifies t s) [-◇ sub ρ ]

-- -- -- -- -- -- --       Unst⇔Unts : ((Unifies s t) [-◇ sub ρ ]) ⇔ ((Unifies t s) [-◇ sub ρ ])
-- -- -- -- -- -- --       Unst⇔Unts = Properties.fact5 (Unifies s t) (Unifies t s) (sub ρ) (Properties.fact1 {_} {s} {t})

-- -- -- -- -- -- --       lemma : amgu-Correctness s t (l , ρ) -> amgu-Correctness t s (l , ρ)
-- -- -- -- -- -- --       lemma (inj₁ (n , σ , MaxUnst , amgu≡just)) =
-- -- -- -- -- -- --         inj₁ (n , σ , proj₁ (Max.fact Unst Unts Unst⇔Unts (sub σ)) MaxUnst , trans (sym st≡ts) amgu≡just)
-- -- -- -- -- -- --       lemma (inj₂ (NoUnst , amgu≡nothing)) =
-- -- -- -- -- -- --         inj₂ ((λ {_} → Properties.fact2 Unst Unts Unst⇔Unts NoUnst) , trans (sym st≡ts) amgu≡nothing)

-- -- -- -- -- -- --   amgu-Ccomm⋆ : ∀ {m} s t acc -> amgu {m = m} s t acc ≡ amgu t s acc -> amgu-Correctness⋆ s t acc -> amgu-Correctness⋆ t s acc
-- -- -- -- -- -- --   amgu-Ccomm⋆ s t (l , ρ) st≡ts = lemma
-- -- -- -- -- -- --     where
-- -- -- -- -- -- --       Unst = (Unifies s t) [-◇ sub ρ ]
-- -- -- -- -- -- --       Unts = (Unifies t s) [-◇ sub ρ ]

-- -- -- -- -- -- --       Unst⇔Unts : ((Unifies s t) [-◇ sub ρ ]) ⇔ ((Unifies t s) [-◇ sub ρ ])
-- -- -- -- -- -- --       Unst⇔Unts = Properties.fact5 (Unifies s t) (Unifies t s) (sub ρ) (Properties.fact1 {_} {s} {t})

-- -- -- -- -- -- --       lemma : amgu-Correctness s t (l , ρ) -> amgu-Correctness t s (l , ρ)
-- -- -- -- -- -- --       lemma (inj₁ (n , σ , MaxUnst , amgu≡just)) =
-- -- -- -- -- -- --         inj₁ (n , σ , proj₁ (Max.fact Unst Unts Unst⇔Unts (sub σ)) MaxUnst , trans (sym st≡ts) amgu≡just)
-- -- -- -- -- -- --       lemma (inj₂ (NoUnst , amgu≡nothing)) =
-- -- -- -- -- -- --         inj₂ ((λ {_} → Properties.fact2 Unst Unts Unst⇔Unts NoUnst) , trans (sym st≡ts) amgu≡nothing)

-- -- -- -- -- -- --   mutual

-- -- -- -- -- -- --     amguV-c : ∀ {m N} {ss ts : Vec (Term m) N} {l ρ} -> AmguTV ss ts (l , ρ) (amgu ss ts (l , ρ)) ->
-- -- -- -- -- -- --                 (∃ λ n → ∃ λ σ → Max⋆ (Unifies⋆V ss ts [-◇⋆ sub ρ ]) (sub σ) × amgu {m = m} ss ts (l , ρ) ≡ just (n , σ ++ ρ ))
-- -- -- -- -- -- --               ⊎ (Nothing⋆ (Unifies⋆V ss ts [-◇⋆ sub ρ ])                     × amgu {m = m} ss ts (l , ρ) ≡ nothing)
-- -- -- -- -- -- --     amguV-c {m} {N} {ss} {ts} {l} {ρ} amg with amgu ss ts (l , ρ)
-- -- -- -- -- -- --     amguV-c {m} {0} {.[]} {.[]} {l} {ρ} fn0-fn0 | .(just (l , ρ)) = inj₁ (_ , anil , trivial-problemV {_} {_} {_} {[]} {sub ρ} , cong (just ∘ _,_ l) (sym (SubList.anil-id-l ρ)))
-- -- -- -- -- -- --     amguV-c {m} {.(suc _)} {(t₁ ∷ ts₁)} {(t₂ ∷ ts₂)} {l} {ρ} fns-fns | _  with amgu t₁ t₂ (l , ρ)  | amgu-c $ view t₁ t₂ (l , ρ)
-- -- -- -- -- -- --     amguV-c {m} {.(suc _)} {(t₁ ∷ ts₁)} {(t₂ ∷ ts₂)} {l} {ρ} fns-fns | _ | am | inj₂ (nounify , refl) = inj₂ ((λ {_} → NothingVecHead t₁ t₂ ρ _ _ nounify) , refl)
-- -- -- -- -- -- --     amguV-c {m} {.(suc _)} {(t₁ ∷ ts₁)} {(t₂ ∷ ts₂)} {l} {ρ} fns-fns | _ | am | inj₁ (n , σ , a , refl)
-- -- -- -- -- -- --      with amgu ts₁ ts₂ (n , σ ++ ρ) | amguV-c (viewV (ts₁) (ts₂) (n , (σ ++ ρ)))
-- -- -- -- -- -- --     … | _ | inj₂ (nounify , refl) = inj₂ ((λ {_} → NothingVecTail σ t₁ t₂ _ _ ρ a nounify) , refl)
-- -- -- -- -- -- --     … | _ | inj₁ (n1 , σ1 , a1 , refl) = inj₁ (n1 , σ1 ++ σ , MaxVec t₁ t₂ _ _ ρ σ a σ1 a1 , cong (λ σ -> just (n1 , σ)) (++-assoc σ1 σ ρ))

-- -- -- -- -- -- --     amgu-c : ∀ {m s t l ρ} -> AmguT s t (l , ρ) (amgu s t (l , ρ)) ->
-- -- -- -- -- -- --                (∃ λ n → ∃ λ σ → Max⋆ (Unifies⋆ s t [-◇⋆ sub ρ ]) (sub σ) × amgu {m = m} s t (l , ρ) ≡ just (n , σ ++ ρ ))
-- -- -- -- -- -- --              ⊎ (Nothing⋆ (Unifies⋆ s t [-◇⋆ sub ρ ])                     × amgu {m = m} s t (l , ρ) ≡ nothing)
-- -- -- -- -- -- --     amgu-c {m} {s} {t} {l} {ρ} amg with amgu s t (l , ρ)
-- -- -- -- -- -- --     amgu-c {l = l} {ρ} leaf-leaf | ._
-- -- -- -- -- -- --       = inj₁ (l , anil , trivial-problem {_} {_} {leaf} {sub ρ} , cong (λ x -> just (l , x)) (sym (SubList.anil-id-l ρ)) )
-- -- -- -- -- -- --     amgu-c (fn-name neq) | _ = inj₂ ((λ f x → neq (Term-function-inj-FunctionName x)) , refl)
-- -- -- -- -- -- --     amgu-c (fn-size neq) | _ = inj₂ ((λ f x → neq (Term-function-inj-VecSize x)) , refl)
-- -- -- -- -- -- --     amgu-c {s = function fn ts₁} {t = function .fn ts₂} {l = l} {ρ = ρ} fn-fn | _ with amgu ts₁ ts₂ | amguV-c (viewV ts₁ ts₂ (l , ρ))
-- -- -- -- -- -- --     … | _ | inj₂ (nounify , refl!) rewrite refl! = inj₂ ((λ {_} f feq → nounify f (Term-function-inj-Vector feq)) , refl)
-- -- -- -- -- -- --     … | _ | inj₁ (n , σ , (b , c) , refl!) rewrite refl! = inj₁ (_ , _ , ((cong (function fn) b) , (λ {_} f' feq → c f' (Term-function-inj-Vector feq))) , refl )
-- -- -- -- -- -- --     amgu-c leaf-fork | .nothing = inj₂ ((λ _ () ) , refl)
-- -- -- -- -- -- --     amgu-c leaf-fn | _ = inj₂ ((λ _ () ) , refl)
-- -- -- -- -- -- --     amgu-c fork-fn | _ = inj₂ ((λ _ () ) , refl)
-- -- -- -- -- -- --     amgu-c {m} {s1 fork s2} {t1 fork t2} {l} {ρ} fork-fork | ._
-- -- -- -- -- -- --      with amgu s1 t1 (l , ρ)  | amgu-c $ view s1 t1 (l , ρ)
-- -- -- -- -- -- --     … | .nothing             | inj₂ (nounify , refl) = inj₂ ((λ {_} -> NothingForkLeft s1 t1 ρ s2 t2 nounify) , refl)
-- -- -- -- -- -- --     … | .(just (n , σ ++ ρ)) | inj₁ (n , σ , a , refl)
-- -- -- -- -- -- --      with amgu s2 t2 (n , σ ++ ρ) | amgu-c (view s2 t2 (n , (σ ++ ρ)))
-- -- -- -- -- -- --     … | .nothing                | inj₂ (nounify , refl) = inj₂ ( (λ {_} -> NothingForkRight σ s1 s2 t1 t2 ρ a nounify) , refl)
-- -- -- -- -- -- --     … | .(just (n1 , σ1 ++ (σ ++ ρ))) | inj₁ (n1 , σ1 , b , refl)
-- -- -- -- -- -- --         = inj₁ (n1 , σ1 ++ σ , MaxFork s1 s2 t1 t2 ρ σ a σ1 b , cong (λ σ -> just (n1 , σ)) (++-assoc σ1 σ ρ))
-- -- -- -- -- -- --     amgu-c {suc l} {i x} {i y} (var-var) | .(just (flexFlex x y))
-- -- -- -- -- -- --      with thick? x y | Thick.fact1 x y (thick? x y) refl
-- -- -- -- -- -- --     …  | .(just y') | inj₂ (y' , thinxy'≡y , refl )
-- -- -- -- -- -- --       = inj₁ (l , anil asnoc i y' / x , var-elim-i-≡ x (i y) (sym (cong i thinxy'≡y)) , refl )
-- -- -- -- -- -- --     …  | .nothing | inj₁ ( x≡y , refl ) rewrite sym x≡y
-- -- -- -- -- -- --       = inj₁ (suc l , anil , trivial-problem {_} {_} {i x} {sub anil} , refl)
-- -- -- -- -- -- --     amgu-c {suc l} {i x} {t} (var-t ix≢t) | .(flexRigid x t)
-- -- -- -- -- -- --      with check x t | check-prop x t
-- -- -- -- -- -- --     … | .nothing  | inj₂ ( ps , r , refl) = inj₂ ( (λ {_} -> NothingStep x t ix≢t ps r )  , refl)
-- -- -- -- -- -- --     … | .(just t') | inj₁ (t' , r , refl) = inj₁ ( l , anil asnoc t' / x , var-elim-i-≡ x t r , refl )
-- -- -- -- -- -- --     amgu-c {suc m} {s} {t} {l} {ρ asnoc r / z} s-t
-- -- -- -- -- -- --       | .((λ x' → x' ∃asnoc r / z) <$>
-- -- -- -- -- -- --           (amgu ((r for z) ◃ s) ((r for z) ◃ t) (l , ρ)))
-- -- -- -- -- -- --      with amgu-c (view ((r for z) ◃ s) ((r for z) ◃ t) (l , ρ))
-- -- -- -- -- -- --     … | inj₂ (nounify , ra) = inj₂ ( (λ {_} -> NothingFor→NothingComposition s t ρ r z nounify) , cong (_<$>_ (λ x' → x' ∃asnoc r / z)) ra )
-- -- -- -- -- -- --     … | inj₁ (n , σ , a , ra)  = inj₁ (n , σ , MaxFor→MaxComposition s t ρ r z σ a , cong (_<$>_ (λ x' → x' ∃asnoc r / z)) ra)
-- -- -- -- -- -- --     amgu-c {m} {s} {t} {l} {ρ} (Flip amguts≡amgust amguts) | ._ = amgu-Ccomm⋆ t s (l , ρ) amguts≡amgust (amgu-c amguts)
-- -- -- -- -- -- --     amgu-c {zero} {i ()} _ | _

-- -- -- -- -- -- --   mgu-c : ∀ {m} (s t : Term m) ->
-- -- -- -- -- -- --              (∃ λ n → ∃ λ σ → (Max⋆ (Unifies⋆ s t)) (sub σ) × mgu s t ≡ just (n , σ))
-- -- -- -- -- -- --            ⊎ (Nothing⋆ (Unifies⋆ s t)                       × mgu s t ≡ nothing)
-- -- -- -- -- -- --   mgu-c {m} s t = amgu-c (view s t (m , anil))

-- -- -- -- -- -- --   unify : ∀ {m} (s t : Term m) ->
-- -- -- -- -- -- --              (∃ λ n → ∃ λ (σ : AList m n) → Max⋆ (Unifies⋆ s t) $ sub σ)
-- -- -- -- -- -- --              ⊎ Nothing⋆ (Unifies⋆ s t)
-- -- -- -- -- -- --   unify {m} s t with amgu-c (view s t (m , anil))
-- -- -- -- -- -- --   unify {m} s₁ t | inj₁ (proj₃ , proj₄ , proj₅ , proj₆) = inj₁ (proj₃ , proj₄ , proj₅)
-- -- -- -- -- -- --   unify {m} s t | inj₂ (proj₃ , _) = inj₂ proj₃

-- -- -- -- -- -- --   unifyV : ∀ {m N} (s t : Vec (Term m) N) ->
-- -- -- -- -- -- --              (∃ λ n → ∃ λ (σ : AList m n) → Max⋆ (Unifies⋆V s t) $ sub σ)
-- -- -- -- -- -- --              ⊎ Nothing⋆ (Unifies⋆V s t)
-- -- -- -- -- -- --   unifyV {m} {N} s t with amguV-c (viewV s t (m , anil))
-- -- -- -- -- -- --   … | inj₁ (proj₃ , proj₄ , proj₅ , proj₆) = inj₁ (proj₃ , proj₄ , proj₅)
-- -- -- -- -- -- --   … | inj₂ (proj₃ , _) = inj₂ proj₃

-- -- -- -- -- -- --   open import Oscar.Data.Permutation

-- -- -- -- -- -- --   unifyWith : ∀ {m N} (p q : Term m) (X Y : Vec (Term m) N) →
-- -- -- -- -- -- --               (∃ λ X* → X* ≡ordering' X × ∃ λ n → ∃ λ (σ : AList m n) → Max⋆ (Unifies⋆V (p ∷ X*) (q ∷ Y)) $ sub σ)
-- -- -- -- -- -- --               ⊎
-- -- -- -- -- -- --               (∀ X* → X* ≡ordering' X → Nothing⋆ (Unifies⋆V (p ∷ X*) (q ∷ Y)))
-- -- -- -- -- -- --   unifyWith p q X Y = {!!}


{-# OPTIONS --type-in-type #-}
module functors where
open import prelude

record Category {O : Set} (𝒞[_,_] : O → O → Set) : Set where
  constructor 𝒾:_▸:_𝒾▸:_▸𝒾:
  infixl 8 _▸_
  field
    𝒾 : ∀ {x} → 𝒞[ x , x ]
    _▸_ : ∀ {x y z} → 𝒞[ x , y ] → 𝒞[ y , z ] → 𝒞[ x , z ]
    𝒾▸ : ∀ {x y} (f : 𝒞[ x , y ]) → (𝒾 ▸ f) ≡ f
    ▸𝒾 : ∀ {x y} (f : 𝒞[ x , y ]) → (f ▸ 𝒾) ≡ f
open Category ⦃...⦄ public

record Functor {A : Set} {𝒜[_,_] : A → A → Set}
               {B : Set} {ℬ[_,_] : B → B → Set}
               (f : A → B) : Set where
  constructor φ:_𝒾:_▸:
  field
    ⦃ cat₁ ⦄ : Category {A} 𝒜[_,_]
    ⦃ cat₂ ⦄ : Category {B} ℬ[_,_]
    φ : ∀ {a b} → 𝒜[ a , b ] → ℬ[ f a , f b ]
    functor-id : ∀ {a} → φ {a} 𝒾 ≡ 𝒾
    functor-comp : ∀ {A B C} → (f : 𝒜[ A , B ]) (g : 𝒜[ B , C ])
                 → φ (f ▸ g) ≡ φ f ▸ φ g
open Functor ⦃...⦄ public

record Nat {A : Set} {𝒜[_,_] : A → A → Set}
           {B : Set} {ℬ[_,_] : B → B → Set}
           {F : A → B} {G : A → B}
           (α : (x : A) → ℬ[ F x , G x ]) : Set where
  field
    overlap ⦃ fun₁ ⦄ : Functor {A} {𝒜[_,_]} {B} {ℬ[_,_]} F
    overlap ⦃ fun₂ ⦄ : Functor {A} {𝒜[_,_]} {B} {ℬ[_,_]} G
    nat : ∀ {x y : A} (f : 𝒜[ x , y ]) → let _▹_ = _▸_ ⦃ cat₂ ⦄
                                          in φ f ▹ α y ≡ α x ▹ φ f

module _ {A : Set} {𝒜[_,_] : A → A → Set}  where
    instance
        id-functor : ⦃ Category 𝒜[_,_] ⦄ → Functor id
        id-functor = φ: id
                     𝒾: refl
                     ▸: λ f g → refl
    record Monad (f : A → A) : Set where
      field
        ⦃ functor ⦄ : Functor {_} {𝒜[_,_]} {_} {𝒜[_,_]} f
        η : ∀ {a} → 𝒜[ a , f a ]
        μ : ∀ {a} → 𝒜[ f (f a) , f a ]
    open Monad ⦃...⦄ public

{-
    record Lift[_⇒_] (pre post : (Set → Set) → Set) (t : (Set → Set) → Set → Set) : Set where
      field
        ⦃ lifted ⦄ : ∀ {f} → post (t f)
        lift : {f : Set → Set} {a : Set} → ⦃ pre f ⦄ → f a → t f a
    open Lift[_⇒_] ⦃...⦄ public
-}

Cat[_,_] : Set → Set → Set
Cat[ a , b ] = a → b

instance
  Cat-cat : Category Cat[_,_]
  Cat-cat = record { 𝒾 = id ; _▸_ = λ f g → g ∘ f ; 𝒾▸ = λ _ → refl; ▸𝒾 = λ _ → refl}
instance
  ≡-cat : ∀ {A} → Category {A} (λ a b → a ≡ b)
  𝒾 {{≡-cat}} = refl
  _▸_ {{≡-cat}} = trans
  𝒾▸ {{≡-cat}} _ = refl
  ▸𝒾 {{≡-cat}} x≡y rewrite x≡y = refl
instance
    ×-cat : ∀ {A 𝒜[_,_]} ⦃ 𝒜 : Category {A} 𝒜[_,_] ⦄
              {B ℬ[_,_]} ⦃ ℬ : Category {B} ℬ[_,_] ⦄
          → Category {A × B} (λ (a , b) (a′ , b′) → 𝒜[ a , a′ ] × ℬ[ b , b′ ])
    𝒾 {{×-cat}} = 𝒾 , 𝒾
    _▸_ {{×-cat}} (f , f′ ) ( g , g′ ) = f ▸ g , f′ ▸ g′
    𝒾▸ {{×-cat}} (f , g) = 𝒾▸ f ×⁼ 𝒾▸ g
    ▸𝒾 {{×-cat}} (f , g) = ▸𝒾 f ×⁼ ▸𝒾 g

data _ᵒᵖ {A : Set} (𝒜 : A → A → Set) : A → A → Set where
  _ᵗ : ∀ {y x} → 𝒜(y )( x) → (𝒜 ᵒᵖ)(x )( y)

instance
    op-cat  : {A : Set} {𝒜[_,_] : A → A → Set} ⦃ 𝒜 : Category {A} 𝒜[_,_] ⦄
            → Category (𝒜[_,_] ᵒᵖ)
    𝒾 {{op-cat}}  = 𝒾 ᵗ
    _▸_ {{op-cat}} (f ᵗ) (g ᵗ) = (g ▸ f) ᵗ
    𝒾▸ {{op-cat}} (f ᵗ) = _ᵗ ⟨$⟩ ▸𝒾 f
    ▸𝒾 {{op-cat}} (f ᵗ) = _ᵗ ⟨$⟩ 𝒾▸ f


{-

Defines different notions of morphisms and properties of morphisms of
groups:

- GroupHom (homomorphisms)
- GroupEquiv (equivs which are homomorphisms)
- GroupIso (isos which are homomorphisms)
- Image
- Kernel
- Surjective
- Injective
- Mono
- BijectionIso (surjective + injective)

-}
{-# OPTIONS --safe #-}
module Cubical.Algebra.Group.Morphisms where

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

open import Cubical.Algebra.Group.Base
open import Cubical.Algebra.Group.DirProd
open import Cubical.Data.Sigma

open import Cubical.Reflection.RecordEquiv

private
  variable
    ℓ ℓ' ℓ'' ℓ''' : Level

record IsGroupHom {A : Type ℓ} {B : Type ℓ'}
  (M : GroupStr A) (f : A → B) (N : GroupStr B)
  : Type (ℓ-max ℓ ℓ')
  where

  -- Shorter qualified names
  private
    module M = GroupStr M
    module N = GroupStr N

  field
    pres· : (x y : A) → f (x M.· y) ≡ f x N.· f y
    pres1 : f M.1g ≡ N.1g
    presinv : (x : A) → f (M.inv x) ≡ N.inv (f x)

unquoteDecl IsGroupHomIsoΣ = declareRecordIsoΣ IsGroupHomIsoΣ (quote IsGroupHom)

GroupHom : (G : Group ℓ) (H : Group ℓ') → Type (ℓ-max ℓ ℓ')
GroupHom G H = Σ[ f ∈ (G .fst → H .fst) ] IsGroupHom (G .snd) f (H .snd)

GroupIso : (G : Group ℓ) (H : Group ℓ') → Type (ℓ-max ℓ ℓ')
GroupIso G H = Σ[ e ∈ Iso (G .fst) (H .fst) ] IsGroupHom (G .snd) (e .Iso.fun) (H .snd)

IsGroupEquiv : {A : Type ℓ} {B : Type ℓ'}
  (M : GroupStr A) (e : A ≃ B) (N : GroupStr B) → Type (ℓ-max ℓ ℓ')
IsGroupEquiv M e N = IsGroupHom M (e .fst) N

GroupEquiv : (G : Group ℓ) (H : Group ℓ') → Type (ℓ-max ℓ ℓ')
GroupEquiv G H = Σ[ e ∈ (G .fst ≃ H .fst) ] IsGroupEquiv (G .snd) e (H .snd)

groupEquivFun : {G : Group ℓ} {H : Group ℓ'} → GroupEquiv G H → G .fst → H .fst
groupEquivFun e = e .fst .fst

-- Image, kernel, surjective, injective, and bijections

open IsGroupHom
open GroupStr

private
  variable
    G H : Group ℓ

isInIm : GroupHom G H → ⟨ H ⟩ → Type _
isInIm {G = G} ϕ h = ∃[ g ∈ ⟨ G ⟩ ] ϕ .fst g ≡ h

isInKer : GroupHom G H → ⟨ G ⟩ → Type _
isInKer {H = H} ϕ g = ϕ .fst g ≡ 1g (snd H)

Ker : GroupHom G H → Type _
Ker {G = G} ϕ = Σ[ x ∈ ⟨ G ⟩ ] isInKer ϕ x

Im : GroupHom G H → Type _
Im {H = H} ϕ = Σ[ x ∈ ⟨ H ⟩ ] isInIm ϕ x

isSurjective : GroupHom G H → Type _
isSurjective {H = H} ϕ = (x : ⟨ H ⟩) → isInIm ϕ x

isInjective : GroupHom G H → Type _
isInjective {G = G} ϕ = (x : ⟨ G ⟩) → isInKer ϕ x → x ≡ 1g (snd G)

isMono : GroupHom G H → Type _
isMono {G = G} f = {x y : ⟨ G ⟩} → f .fst x ≡ f .fst y → x ≡ y

-- Group bijections
record BijectionIso (G : Group ℓ) (H : Group ℓ') : Type (ℓ-max ℓ ℓ') where

  constructor bijIso

  field
    fun : GroupHom G H
    inj : isInjective fun
    surj : isSurjective fun


{-# OPTIONS --without-K --exact-split #-}

module 18-circle where

import 17-groups
open 17-groups public

{- Section 11.1 The induction principle of the circle -}

free-loops :
  { l1 : Level} (X : UU l1) → UU l1
free-loops X = Σ X (λ x → Id x x)

base-free-loop :
  { l1 : Level} {X : UU l1} → free-loops X → X
base-free-loop = pr1

loop-free-loop :
  { l1 : Level} {X : UU l1} (l : free-loops X) →
  Id (base-free-loop l) (base-free-loop l)
loop-free-loop = pr2

-- Now we characterize the identity types of free loops

Eq-free-loops :
  { l1 : Level} {X : UU l1} (l l' : free-loops X) → UU l1
Eq-free-loops (pair x l) l' =
  Σ (Id x (pr1 l')) (λ p → Id (l ∙ p) (p ∙ (pr2 l')))

reflexive-Eq-free-loops :
  { l1 : Level} {X : UU l1} (l : free-loops X) → Eq-free-loops l l
reflexive-Eq-free-loops (pair x l) = pair refl right-unit

Eq-free-loops-eq :
  { l1 : Level} {X : UU l1} (l l' : free-loops X) →
  Id l l' → Eq-free-loops l l'
Eq-free-loops-eq l .l refl = reflexive-Eq-free-loops l

abstract
  is-contr-total-Eq-free-loops :
    { l1 : Level} {X : UU l1} (l : free-loops X) →
    is-contr (Σ (free-loops X) (Eq-free-loops l))
  is-contr-total-Eq-free-loops (pair x l) =
    is-contr-total-Eq-structure
      ( λ x l' p → Id (l ∙ p) (p ∙ l'))
      ( is-contr-total-path x)
      ( pair x refl)
      ( is-contr-is-equiv'
        ( Σ (Id x x) (λ l' → Id l l'))
        ( tot (λ l' α → right-unit ∙ α))
        ( is-equiv-tot-is-fiberwise-equiv
          ( λ l' → is-equiv-concat right-unit l'))
        ( is-contr-total-path l))

abstract
  is-equiv-Eq-free-loops-eq :
    { l1 : Level} {X : UU l1} (l l' : free-loops X) →
    is-equiv (Eq-free-loops-eq l l')
  is-equiv-Eq-free-loops-eq l =
    fundamental-theorem-id l
      ( reflexive-Eq-free-loops l)
      ( is-contr-total-Eq-free-loops l)
      ( Eq-free-loops-eq l)

{- We introduce dependent free loops, which are used in the induction principle
   of the circle. -}

dependent-free-loops :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) (P : X → UU l2) → UU l2
dependent-free-loops l P =
  Σ ( P (base-free-loop l))
    ( λ p₀ → Id (tr P (loop-free-loop l) p₀) p₀)

Eq-dependent-free-loops :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) (P : X → UU l2) →
  ( p p' : dependent-free-loops l P) → UU l2
Eq-dependent-free-loops (pair x l) P (pair y p) p' =
  Σ ( Id y (pr1 p'))
    ( λ q → Id (p ∙ q) ((ap (tr P l) q) ∙ (pr2 p')))

reflexive-Eq-dependent-free-loops :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) (P : X → UU l2) →
  ( p : dependent-free-loops l P) → Eq-dependent-free-loops l P p p
reflexive-Eq-dependent-free-loops (pair x l) P (pair y p) =
  pair refl right-unit

Eq-dependent-free-loops-eq :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) (P : X → UU l2) →
  ( p p' : dependent-free-loops l P) →
  Id p p' → Eq-dependent-free-loops l P p p'
Eq-dependent-free-loops-eq l P p .p refl =
  reflexive-Eq-dependent-free-loops l P p

abstract
  is-contr-total-Eq-dependent-free-loops :
    { l1 l2 : Level} {X : UU l1} (l : free-loops X) (P : X → UU l2) →
    ( p : dependent-free-loops l P) →
    is-contr (Σ (dependent-free-loops l P) (Eq-dependent-free-loops l P p))
  is-contr-total-Eq-dependent-free-loops (pair x l) P (pair y p) =
    is-contr-total-Eq-structure
      ( λ y' p' q → Id (p ∙ q) ((ap (tr P l) q) ∙ p'))
      ( is-contr-total-path y)
      ( pair y refl)
      ( is-contr-is-equiv'
        ( Σ (Id (tr P l y) y) (λ p' → Id p p'))
        ( tot (λ p' α → right-unit ∙ α))
        ( is-equiv-tot-is-fiberwise-equiv
          ( λ p' → is-equiv-concat right-unit p'))
        ( is-contr-total-path p))

abstract
  is-equiv-Eq-dependent-free-loops-eq :
    { l1 l2 : Level} {X : UU l1} (l : free-loops X) (P : X → UU l2)
    ( p p' : dependent-free-loops l P) →
    is-equiv (Eq-dependent-free-loops-eq l P p p')
  is-equiv-Eq-dependent-free-loops-eq l P p =
    fundamental-theorem-id p
      ( reflexive-Eq-dependent-free-loops l P p)
      ( is-contr-total-Eq-dependent-free-loops l P p)
      ( Eq-dependent-free-loops-eq l P p)

eq-Eq-dependent-free-loops :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) (P : X → UU l2)
  ( p p' : dependent-free-loops l P) →
  Eq-dependent-free-loops l P p p' → Id p p'
eq-Eq-dependent-free-loops l P p p' =
  inv-is-equiv (is-equiv-Eq-dependent-free-loops-eq l P p p')

{- We now define the induction principle of the circle. -}

ev-free-loop' :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) (P : X → UU l2) →
  ( (x : X) → P x) → dependent-free-loops l P
ev-free-loop' (pair x₀ p) P f = pair (f x₀) (apd f p)

induction-principle-circle :
  { l1 : Level} (l2 : Level) {X : UU l1} (l : free-loops X) →
  UU ((lsuc l2) ⊔ l1)
induction-principle-circle l2 {X} l =
  (P : X → UU l2) → sec (ev-free-loop' l P)

{- Section 11.2 The universal property of the circle -}

{- We first state the universal property of the circle -}

ev-free-loop :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) (Y : UU l2) →
  ( X → Y) → free-loops Y
ev-free-loop l Y f = pair (f (pr1 l)) (ap f (pr2 l))

universal-property-circle :
  { l1 : Level} (l2 : Level) {X : UU l1} (l : free-loops X) → UU _
universal-property-circle l2 l =
  ( Y : UU l2) → is-equiv (ev-free-loop l Y)

{- A fairly straightforward proof of the universal property of the circle
   factors through the dependent universal property of the circle. -}

dependent-universal-property-circle :
  { l1 : Level} (l2 : Level) {X : UU l1} (l : free-loops X) →
  UU ((lsuc l2) ⊔ l1)
dependent-universal-property-circle l2 {X} l =
  ( P : X → UU l2) → is-equiv (ev-free-loop' l P)

{- We first prove that the dependent universal property of the circle follows
   from the induction principle of the circle. To show this, we have to show
   that the section of ev-free-loop' is also a retraction. This construction
   is also by the induction principle of the circle, but it requires (a minimal
   amount of) preparations. -}

Eq-subst :
  { l1 l2 : Level} {X : UU l1} {P : X → UU l2} (f g : (x : X) → P x) →
  X → UU _
Eq-subst f g x = Id (f x) (g x)

tr-Eq-subst :
  { l1 l2 : Level} {X : UU l1} {P : X → UU l2} (f g : (x : X) → P x)
  { x y : X} (p : Id x y) (q : Id (f x) (g x)) (r : Id (f y) (g y))→
  ( Id ((apd f p) ∙ r) ((ap (tr P p) q) ∙ (apd g p))) →
  ( Id (tr (Eq-subst f g) p q) r)
tr-Eq-subst f g refl q .((ap id q) ∙ refl) refl =
  inv (right-unit ∙ (ap-id q))

dependent-free-loops-htpy :
  {l1 l2 : Level} {X : UU l1} {l : free-loops X} {P : X → UU l2}
  {f g : (x : X) → P x} →
  ( Eq-dependent-free-loops l P (ev-free-loop' l P f) (ev-free-loop' l P g)) →
  ( dependent-free-loops l (λ x → Id (f x) (g x)))
dependent-free-loops-htpy {l = (pair x l)} (pair p q) =
  pair p (tr-Eq-subst _ _ l p p q)

isretr-ind-circle :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) →
  ( ind-circle : induction-principle-circle l2 l) (P : X → UU l2) →
  ( (pr1 (ind-circle P)) ∘ (ev-free-loop' l P)) ~ id
isretr-ind-circle l ind-circle P f =
  eq-htpy
    ( pr1
      ( ind-circle
        ( λ t → Id (pr1 (ind-circle P) (ev-free-loop' l P f) t) (f t)))
      ( dependent-free-loops-htpy
        ( Eq-dependent-free-loops-eq l P _ _
          ( pr2 (ind-circle P) (ev-free-loop' l P f)))))

abstract
  dependent-universal-property-induction-principle-circle :
    { l1 l2 : Level} {X : UU l1} (l : free-loops X) →
    induction-principle-circle l2 l → dependent-universal-property-circle l2 l
  dependent-universal-property-induction-principle-circle l ind-circle P =
    is-equiv-has-inverse
      ( pr1 (ind-circle P))
      ( pr2 (ind-circle P))
      ( isretr-ind-circle l ind-circle P)

{- We use the dependent universal property to derive a uniqeness property of
   dependent functions on the circle. -}

dependent-uniqueness-circle :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) →
  dependent-universal-property-circle l2 l →
  { P : X → UU l2} (k : dependent-free-loops l P) →
  is-contr
    ( Σ ( (x : X) → P x)
        ( λ h → Eq-dependent-free-loops l P (ev-free-loop' l P h) k))
dependent-uniqueness-circle l dup-circle {P} k =
  is-contr-is-equiv'
    ( fib (ev-free-loop' l P) k)
    ( tot (λ h → Eq-dependent-free-loops-eq l P (ev-free-loop' l P h) k))
    ( is-equiv-tot-is-fiberwise-equiv
      (λ h → is-equiv-Eq-dependent-free-loops-eq l P (ev-free-loop' l P h) k))
    ( is-contr-map-is-equiv (dup-circle P) k)

{- Now that we have established the dependent universal property, we can
   reduce the (non-dependent) universal property to the dependent case. We do
   so by constructing a commuting triangle relating ev-free-loop to
   ev-free-loop' via a comparison equivalence. -}

tr-const :
  {i j : Level} {A : UU i} {B : UU j} {x y : A} (p : Id x y) (b : B) →
  Id (tr (λ (a : A) → B) p b) b
tr-const refl b = refl

apd-const :
  {i j : Level} {A : UU i} {B : UU j} (f : A → B) {x y : A}
  (p : Id x y) → Id (apd f p) ((tr-const p (f x)) ∙ (ap f p))
apd-const f refl = refl

comparison-free-loops :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) (Y : UU l2) →
  free-loops Y → dependent-free-loops l (λ x → Y)
comparison-free-loops l Y =
  tot (λ y l' → (tr-const (pr2 l) y) ∙ l')

abstract
  is-equiv-comparison-free-loops :
    { l1 l2 : Level} {X : UU l1} (l : free-loops X) (Y : UU l2) →
    is-equiv (comparison-free-loops l Y)
  is-equiv-comparison-free-loops l Y =
    is-equiv-tot-is-fiberwise-equiv
      ( λ y → is-equiv-concat (tr-const (pr2 l) y) y)

triangle-comparison-free-loops :
  { l1 l2 : Level} {X : UU l1} (l : free-loops X) (Y : UU l2) →
  ( (comparison-free-loops l Y) ∘ (ev-free-loop l Y)) ~
  ( ev-free-loop' l (λ x → Y))
triangle-comparison-free-loops (pair x l) Y f =
  eq-Eq-dependent-free-loops
    ( pair x l)
    ( λ x → Y)
    ( comparison-free-loops (pair x l) Y (ev-free-loop (pair x l) Y f))
    ( ev-free-loop' (pair x l) (λ x → Y) f)
    ( pair refl (right-unit ∙ (inv (apd-const f l))))

abstract
  universal-property-dependent-universal-property-circle :
    { l1 l2 : Level} {X : UU l1} (l : free-loops X) →
    ( dependent-universal-property-circle l2 l) →
    ( universal-property-circle l2 l)
  universal-property-dependent-universal-property-circle l dup-circle Y =
    is-equiv-right-factor
      ( ev-free-loop' l (λ x → Y))
      ( comparison-free-loops l Y)
      ( ev-free-loop l Y)
      ( htpy-inv (triangle-comparison-free-loops l Y))
      ( is-equiv-comparison-free-loops l Y)
      ( dup-circle (λ x → Y))

{- Now we get the universal property of the circle from the induction principle
   of the circle by composing the earlier two proofs. -}

abstract
  universal-property-induction-principle-circle :
    { l1 l2 : Level} {X : UU l1} (l : free-loops X) →
    induction-principle-circle l2 l → universal-property-circle l2 l
  universal-property-induction-principle-circle l =
    ( universal-property-dependent-universal-property-circle l) ∘
    ( dependent-universal-property-induction-principle-circle l)

unique-mapping-property-circle :
  { l1 : Level} (l2 : Level) {X : UU l1} (l : free-loops X) →
  UU (l1 ⊔ (lsuc l2))
unique-mapping-property-circle l2 {X} l =
  ( Y : UU l2) (l' : free-loops Y) →
    is-contr (Σ (X → Y) (λ f → Eq-free-loops (ev-free-loop l Y f) l'))

abstract
  unique-mapping-property-universal-property-circle :
    { l1 l2 : Level} {X : UU l1} (l : free-loops X) →
    universal-property-circle l2 l →
    unique-mapping-property-circle l2 l
  unique-mapping-property-universal-property-circle l up-circle Y l' =
    is-contr-is-equiv'
      ( fib (ev-free-loop l Y) l')
      ( tot (λ f → Eq-free-loops-eq (ev-free-loop l Y f) l'))
      ( is-equiv-tot-is-fiberwise-equiv
        ( λ f → is-equiv-Eq-free-loops-eq (ev-free-loop l Y f) l'))
      ( is-contr-map-is-equiv (up-circle Y) l')

{- We assume that we have a circle. -}

postulate 𝕊¹ : UU lzero

postulate base-𝕊¹ : 𝕊¹

postulate loop-𝕊¹ : Id base-𝕊¹ base-𝕊¹

free-loop-𝕊¹ : free-loops 𝕊¹
free-loop-𝕊¹ = pair base-𝕊¹ loop-𝕊¹

postulate ind-𝕊¹ : {l : Level} → induction-principle-circle l free-loop-𝕊¹

dependent-universal-property-𝕊¹ :
  {l : Level} → dependent-universal-property-circle l free-loop-𝕊¹
dependent-universal-property-𝕊¹ =
  dependent-universal-property-induction-principle-circle free-loop-𝕊¹ ind-𝕊¹

dependent-uniqueness-𝕊¹ :
  {l : Level} {P : 𝕊¹ → UU l} (k : dependent-free-loops free-loop-𝕊¹ P) →
  is-contr (Σ ((x : 𝕊¹) → P x) (λ h → Eq-dependent-free-loops free-loop-𝕊¹ P (ev-free-loop' free-loop-𝕊¹ P h) k))
dependent-uniqueness-𝕊¹ {l} {P} k =
  dependent-uniqueness-circle free-loop-𝕊¹ dependent-universal-property-𝕊¹ k

universal-property-𝕊¹ :
  {l : Level} → universal-property-circle l free-loop-𝕊¹
universal-property-𝕊¹ =
  universal-property-dependent-universal-property-circle
    free-loop-𝕊¹
    dependent-universal-property-𝕊¹

-- Section 14.3 Multiplication on the circle

{- Exercises -}

-- Exercise 11.1

{- The dependent universal property of the circle (and hence also the induction
   principle of the circle, implies that the circle is connected in the sense
   that for any family of propositions parametrized by the circle, if the
   proposition at the base holds, then it holds for any x : circle. -}

abstract
  is-connected-circle' :
    { l1 l2 : Level} {X : UU l1} (l : free-loops X) →
    ( dup-circle : dependent-universal-property-circle l2 l)
    ( P : X → UU l2) (is-prop-P : (x : X) → is-prop (P x)) →
    P (base-free-loop l) → (x : X) → P x
  is-connected-circle' l dup-circle P is-prop-P p =
    inv-is-equiv
      ( dup-circle P)
      ( pair p (center (is-prop-P _ (tr P (pr2 l) p) p)))


{-# OPTIONS --cubical #-}
open import Agda.Builtin.Cubical.Path

data PushOut {A B C : Set} (f : C → A) (g : C → B) : Set where
  inl : A → PushOut f g
  inr : B → PushOut f g
  push : ∀ c → inl (f c) ≡ inr (g c)
  squash : ∀ a b → a ≡ b

data ∥_∥₀ (A : Set) : Set where
  ∣_∣    : A → ∥ A ∥₀
  squash : ∀ (x y : ∥ A ∥₀) → (p q : x ≡ y) → p ≡ q

data Segment : Set where
  start end : Segment
  line      : start ≡ end

-- prop truncation
data ∥_∥ (A : Set) : Set where
  ∣_∣ : A → ∥ A ∥
  squash : ∀ (x y : ∥ A ∥) → x ≡ y

elim : ∀ {A : Set} (P : Set) → ((x y : P) → x ≡ y) → (A → P) → ∥ A ∥ → P
elim P Pprop f ∣ x ∣          = f x
elim P Pprop f (squash x y i) = Pprop (elim P Pprop f x) (elim P Pprop f y) i


{-# OPTIONS --safe --warning=error --without-K --guardedness #-}

open import Setoids.Setoids
open import Rings.Definition
open import Rings.Lemmas
open import Rings.Orders.Partial.Definition
open import Rings.Orders.Total.Definition
open import Groups.Definition
open import Groups.Lemmas
open import Fields.Fields
open import Sets.EquivalenceRelations
open import Sequences
open import Setoids.Orders.Partial.Definition
open import Setoids.Orders.Total.Definition
open import Functions.Definition
open import LogicalFormulae
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Numbers.Naturals.Order.Lemmas

module Fields.CauchyCompletion.Multiplication {m n o : _} {A : Set m} {S : Setoid {m} {n} A} {_+_ : A → A → A} {_*_ : A → A → A} {_<_ : Rel {m} {o} A} {pOrder : SetoidPartialOrder S _<_} {R : Ring S _+_ _*_} {pRing : PartiallyOrderedRing R pOrder} (order : TotallyOrderedRing pRing) (F : Field R) where

open Setoid S
open SetoidTotalOrder (TotallyOrderedRing.total order)
open SetoidPartialOrder pOrder
open Equivalence eq
open PartiallyOrderedRing pRing
open Ring R
open Group additiveGroup
open Field F
open import Fields.Orders.Lemmas {F = F} record { oRing = order }
open import Fields.Orders.Total.Lemmas {F = F} record { oRing = order }

open import Fields.Lemmas F
open import Rings.Orders.Partial.Lemmas pRing
open import Rings.Orders.Total.Lemmas order
open import Rings.Orders.Total.Cauchy order
open import Rings.Orders.Total.AbsoluteValue order
open import Fields.CauchyCompletion.Definition order F
open import Fields.CauchyCompletion.Setoid order F
open import Fields.CauchyCompletion.Approximation order F

0!=1 : {e : A} → (0G < e) → 0R ∼ 1R → False
0!=1 {e} 0<e 0=1 = irreflexive (<WellDefined (Equivalence.reflexive eq) (oneZeroImpliesAllZero R 0=1) 0<e)

private
  littleLemma : {a b c d : A} → ((a * b) + inverse (c * d)) ∼ ((a * (b + inverse d)) + (d * (a + inverse c)))
  littleLemma {a} {b} {c} {d} = Equivalence.transitive eq (Equivalence.transitive eq (+WellDefined (Equivalence.reflexive eq) (Equivalence.transitive eq (Equivalence.transitive eq (Equivalence.symmetric eq identLeft) (+WellDefined (Equivalence.symmetric eq (Equivalence.transitive eq (+WellDefined (Equivalence.transitive eq (ringMinusExtracts R) (inverseWellDefined additiveGroup *Commutative)) (Equivalence.reflexive eq)) (invLeft {d * a}))) (Equivalence.transitive eq (Equivalence.symmetric eq (ringMinusExtracts' R)) *Commutative))) (Equivalence.symmetric eq +Associative))) (+Associative)) (Equivalence.symmetric eq (+WellDefined (*DistributesOver+) (*DistributesOver+)))

  timesConverges : (a b : CauchyCompletion) → cauchy (apply _*_ (CauchyCompletion.elts a) (CauchyCompletion.elts b))
  timesConverges record { elts = a ; converges = aConv } record { elts = b ; converges = bConv } e 0<e with boundModulus record { elts = a ; converges = aConv }
  ... | aBound , (Na , prABound) with boundModulus record { elts = b ; converges = bConv }
  ... | bBound , (Nb , prBBound) = N , ans
    where
      boundBoth : A
      boundBoth = aBound + bBound
      abstract
        ab<bb : aBound < boundBoth
        ab<bb = <WellDefined identLeft groupIsAbelian (orderRespectsAddition {0R} {bBound} (greaterThanAbsImpliesGreaterThan0 (prBBound (succ Nb) (le 0 refl))) aBound)
        bb<bb : bBound < boundBoth
        bb<bb = <WellDefined identLeft (Equivalence.reflexive eq) (orderRespectsAddition {0R} {aBound} (greaterThanAbsImpliesGreaterThan0 (prABound (succ Na) (le 0 refl))) bBound)
        0<boundBoth : 0R < boundBoth
        0<boundBoth = <WellDefined identLeft (Equivalence.reflexive eq) (ringAddInequalities (greaterThanAbsImpliesGreaterThan0 (prABound (succ Na) (le 0 refl))) (greaterThanAbsImpliesGreaterThan0 (prBBound (succ Nb) (le 0 refl))))
      abstract
        1/boundBoothAndPr : Sg A λ i → i * (aBound + bBound) ∼ 1R
        1/boundBoothAndPr = allInvertible boundBoth λ pr → irreflexive (<WellDefined (Equivalence.reflexive eq) pr 0<boundBoth)
        1/boundBooth : A
        1/boundBooth with 1/boundBoothAndPr
        ... | a , _ = a
        1/boundBoothPr : 1/boundBooth * (aBound + bBound) ∼ 1R
        1/boundBoothPr with 1/boundBoothAndPr
        ... | _ , pr = pr
        0<1/boundBooth : 0G < 1/boundBooth
        0<1/boundBooth = inversePositiveIsPositive 1/boundBoothPr 0<boundBoth
      abstract
        miniEAndPr : Sg A (λ i → (i + i) ∼ (e * 1/boundBooth))
        miniEAndPr = halve charNot2 (e * 1/boundBooth)
        miniE : A
        miniE with miniEAndPr
        ... | a , _ = a
        miniEPr : (miniE + miniE) ∼ (e * 1/boundBooth)
        miniEPr with miniEAndPr
        ... | _ , pr = pr
        0<miniE : 0R < miniE
        0<miniE = halvePositive miniE (<WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq miniEPr) (orderRespectsMultiplication 0<e 0<1/boundBooth))
      abstract
        reallyNAAndPr : Sg ℕ (λ N → {m n : ℕ} → N <N m → N <N n → abs (index a m + inverse (index a n)) < miniE)
        reallyNAAndPr = aConv miniE 0<miniE
        reallyNa : ℕ
        reallyNa with reallyNAAndPr
        ... | a , _ = a
        reallyNaPr : {m n : ℕ} → reallyNa <N m → reallyNa <N n → abs (index a m + inverse (index a n)) < miniE
        reallyNaPr with reallyNAAndPr
        ... | _ , pr = pr
        reallyNBAndPr : Sg ℕ (λ N → {m n : ℕ} → N <N m → N <N n → abs (index b m + inverse (index b n)) < miniE)
        reallyNBAndPr = bConv miniE 0<miniE
        reallyNb : ℕ
        reallyNb with reallyNBAndPr
        ... | a , _ = a
        reallyNbPr : {m n : ℕ} → reallyNb <N m → reallyNb <N n → abs (index b m + inverse (index b n)) < miniE
        reallyNbPr with reallyNBAndPr
        ... | _ , pr = pr
      N : ℕ
      N = (Na +N (Nb +N (reallyNa +N reallyNb)))
      ans : {m : ℕ} {n : ℕ} → N <N m → N <N n → abs (index (apply _*_ a b) m + inverse (index (apply _*_ a b) n)) < e
      ans {m} {n} N<m N<n rewrite indexAndApply a b _*_ {m} | indexAndApply a b _*_ {n} = ans'''
        where
          Na<m : Na <N m
          Na<m = inequalityShrinkLeft N<m
          Nb<n : Nb <N n
          Nb<n = inequalityShrinkLeft (inequalityShrinkRight {Na} N<n)
          abstract
            sum : {m n : ℕ} → (reallyNa +N reallyNb) <N m → (reallyNa +N reallyNb) <N n → (boundBoth * ((abs (index b m + inverse (index b n))) + (abs (index a m + inverse (index a n))))) < e
            sum {m} {n} <m <n = <WellDefined *Commutative (Equivalence.transitive eq (*WellDefined miniEPr (Equivalence.reflexive eq)) (Equivalence.transitive eq (Equivalence.symmetric eq *Associative) (Equivalence.transitive eq (Equivalence.transitive eq (*WellDefined (Equivalence.reflexive eq) 1/boundBoothPr) *Commutative) (identIsIdent)))) (ringCanMultiplyByPositive {c = boundBoth} 0<boundBoth (ringAddInequalities (reallyNbPr {m} {n} (inequalityShrinkRight <m) (inequalityShrinkRight <n)) (reallyNaPr {m} {n} (inequalityShrinkLeft <m) (inequalityShrinkLeft <n))))
          q : ((boundBoth * (abs (index b m + inverse (index b n)))) + (boundBoth * (abs (index a m + inverse (index a n))))) < e
          q = <WellDefined *DistributesOver+ (Equivalence.reflexive eq) (sum {m} {n} (inequalityShrinkRight {Nb} (inequalityShrinkRight {Na} N<m)) (inequalityShrinkRight {Nb} (inequalityShrinkRight {Na} N<n)))
          p : ((abs (index a m) * abs (index b m + inverse (index b n))) + (abs (index b n) * abs (index a m + inverse (index a n)))) < e
          p with totality 0R (index b m + inverse (index b n))
          p | inl (inl 0<bm-bn) with totality 0R (index a m + inverse (index a n))
          p | inl (inl 0<bm-bn) | inl (inl 0<am-an) = SetoidPartialOrder.<Transitive pOrder (ringAddInequalities (<WellDefined (Equivalence.reflexive eq) (*WellDefined (Equivalence.reflexive eq) (greaterZeroImpliesEqualAbs 0<bm-bn)) (ringCanMultiplyByPositive 0<bm-bn (SetoidPartialOrder.<Transitive pOrder (prABound m Na<m) ab<bb))) (<WellDefined (Equivalence.reflexive eq) (*WellDefined (Equivalence.reflexive eq) (greaterZeroImpliesEqualAbs 0<am-an)) (ringCanMultiplyByPositive 0<am-an (SetoidPartialOrder.<Transitive pOrder (prBBound n Nb<n) bb<bb)))) q
          p | inl (inl 0<bm-bn) | inl (inr am-an<0) = SetoidPartialOrder.<Transitive pOrder (ringAddInequalities (<WellDefined (*WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq (greaterZeroImpliesEqualAbs 0<bm-bn))) (Equivalence.reflexive eq) (ringCanMultiplyByPositive (<WellDefined (Equivalence.reflexive eq) (greaterZeroImpliesEqualAbs 0<bm-bn) 0<bm-bn) (SetoidPartialOrder.<Transitive pOrder (prABound m Na<m) ab<bb))) (<WellDefined (*WellDefined (Equivalence.reflexive eq) (lessZeroImpliesEqualNegAbs am-an<0)) (Equivalence.reflexive eq) (ringCanMultiplyByPositive (<WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq (lessZeroImpliesEqualNegAbs am-an<0)) (lemm2 _ am-an<0)) (SetoidPartialOrder.<Transitive pOrder (prBBound n Nb<n) bb<bb)))) q
          p | inl (inl 0<bm-bn) | inr 0=am-an = <WellDefined (+WellDefined (Equivalence.reflexive eq) (*WellDefined (Equivalence.reflexive eq) 0=am-an)) (Equivalence.reflexive eq) (<WellDefined (+WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq timesZero)) (Equivalence.reflexive eq) (<WellDefined (Equivalence.symmetric eq identRight) (Equivalence.reflexive eq) (SetoidPartialOrder.<Transitive pOrder (<WellDefined (Equivalence.reflexive eq) (+WellDefined (Equivalence.reflexive eq) (*WellDefined (Equivalence.reflexive eq) (Equivalence.transitive eq (Equivalence.symmetric eq absZeroIsZero) (absWellDefined _ _ 0=am-an)))) (<WellDefined (Equivalence.reflexive eq) (+WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq timesZero)) (<WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq identRight) (<WellDefined (*WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq (greaterZeroImpliesEqualAbs 0<bm-bn))) (Equivalence.reflexive eq) (ringCanMultiplyByPositive (<WellDefined (Equivalence.reflexive eq) (greaterZeroImpliesEqualAbs 0<bm-bn) 0<bm-bn) (SetoidPartialOrder.<Transitive pOrder (prABound m Na<m) ab<bb)))))) q)))
          p | inl (inr bm-bn<0) = SetoidPartialOrder.<Transitive pOrder ans'' q
            where
              bar : ((abs (index a m)) * (inverse (index b m + inverse (index b n)))) < (boundBoth * abs (index b m + inverse (index b n)))
              bar = <WellDefined (*WellDefined (Equivalence.reflexive eq) (lessZeroImpliesEqualNegAbs bm-bn<0)) (Equivalence.reflexive eq) (ringCanMultiplyByPositive (<WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq (lessZeroImpliesEqualNegAbs bm-bn<0)) (lemm2 _ bm-bn<0)) (SetoidPartialOrder.<Transitive pOrder (prABound m Na<m) (ab<bb)))
              foo : (((abs (index b n)) * (abs (index a m + inverse (index a n)))) < (boundBoth * abs (index a m + inverse (index a n)))) || (((abs (index b n)) * (abs (index a m + inverse (index a n)))) ∼ (boundBoth * abs (index a m + inverse (index a n))))
              foo with totality 0R (index a m + inverse (index a n))
              foo | inl (inl 0<am-an) = inl (ringCanMultiplyByPositive 0<am-an (SetoidPartialOrder.<Transitive pOrder (prBBound n Nb<n) bb<bb))
              foo | inl (inr am-an<0) = inl (ringCanMultiplyByPositive (lemm2 _ am-an<0) (SetoidPartialOrder.<Transitive pOrder (prBBound n Nb<n) bb<bb))
              foo | inr 0=am-an = inr (Equivalence.transitive eq (*WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq 0=am-an)) (Equivalence.transitive eq (Equivalence.transitive eq timesZero (Equivalence.symmetric eq timesZero)) (*WellDefined (Equivalence.reflexive eq) 0=am-an)))
              ans'' : _
              ans'' with foo
              ... | inl pr = ringAddInequalities bar pr
              ... | inr pr = <WellDefined (Equivalence.reflexive eq) (+WellDefined (Equivalence.reflexive eq) pr) (orderRespectsAddition bar ((abs (index b n)) * (abs (index a m + inverse (index a n)))))
          p | inr 0=bm-bn = ans''
            where
              bar : (boundBoth * abs (index b m + inverse (index b n))) ∼ 0R
              bar = Equivalence.transitive eq (*WellDefined (Equivalence.reflexive eq) (Equivalence.transitive eq (absWellDefined _ _ (Equivalence.symmetric eq 0=bm-bn)) (absZeroIsZero))) timesZero
              bar' : (abs (index a m) * (index b m + inverse (index b n))) ∼ 0R
              bar' = Equivalence.transitive eq (*WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq 0=bm-bn)) timesZero
              foo : (((abs (index b n)) * (abs (index a m + inverse (index a n)))) < (boundBoth * abs (index a m + inverse (index a n)))) || (0R ∼ (index a m + inverse (index a n)))
              foo with totality 0R (index a m + inverse (index a n))
              foo | inl (inl 0<am-an) = inl (SetoidPartialOrder.<Transitive pOrder (ringCanMultiplyByPositive 0<am-an (prBBound n Nb<n)) (ringCanMultiplyByPositive 0<am-an bb<bb))
              foo | inl (inr am-an<0) = inl (SetoidPartialOrder.<Transitive pOrder (ringCanMultiplyByPositive (lemm2 _ am-an<0) (prBBound n Nb<n)) (ringCanMultiplyByPositive (lemm2 _ am-an<0) bb<bb))
              foo | inr 0=am-an = inr 0=am-an
              ans'' : _
              ans'' with foo
              ... | inl pr = SetoidPartialOrder.<Transitive pOrder (<WellDefined groupIsAbelian groupIsAbelian (<WellDefined (+WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq bar')) (+WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq bar)) (orderRespectsAddition pr 0R))) q
              ... | inr pr = <WellDefined (Equivalence.transitive eq (Equivalence.symmetric eq identRight) (+WellDefined (Equivalence.symmetric eq bar') (Equivalence.symmetric eq (Equivalence.transitive eq (*WellDefined (Equivalence.reflexive eq) (Equivalence.transitive eq (absWellDefined _ _ (Equivalence.symmetric eq pr)) absZeroIsZero)) timesZero)))) (Equivalence.reflexive eq) 0<e
          ans''' : abs ((index a m * index b m) + inverse (index a n * index b n)) < e
          ans''' with triangleInequality (index a m * (index b m + inverse (index b n))) (index b n * (index a m + inverse (index a n)))
          ... | inl less = <WellDefined (Equivalence.symmetric eq (absWellDefined ((index a m * index b m) + (inverse (index a n * index b n))) (((index a m) * (index b m + (inverse (index b n)))) + ((index b n) * (index a m + inverse (index a n)))) littleLemma)) (Equivalence.reflexive eq) (SetoidPartialOrder.<Transitive pOrder less (<WellDefined (+WellDefined (Equivalence.symmetric eq (absRespectsTimes (index a m) _)) (Equivalence.symmetric eq (absRespectsTimes (index b n) _))) (Equivalence.reflexive eq) p))
          ... | inr equal = <WellDefined {_ + _} {abs _} {e} {e} (symmetric (transitive (transitive (absWellDefined ((index a m * index b m) + (inverse (index a n * index b n))) (((index a m) * (index b m + (inverse (index b n)))) + ((index b n) * (index a m + inverse (index a n)))) littleLemma) equal) (+WellDefined (absRespectsTimes (index a m) _) (absRespectsTimes (index b n) _)))) reflexive p

_*C_ : CauchyCompletion → CauchyCompletion → CauchyCompletion
CauchyCompletion.elts (record { elts = a ; converges = aConv } *C record { elts = b ; converges = bConv }) = apply _*_ a b
CauchyCompletion.converges (a *C b) = timesConverges a b

*CCommutative : {a b : CauchyCompletion} → Setoid._∼_ cauchyCompletionSetoid (a *C b) (b *C a)
*CCommutative {a} {b} ε 0<e = 0 , ans
  where
    foo : {x y : A} → (x * y) + inverse (y * x) ∼ 0G
    foo = Equivalence.transitive eq (+WellDefined (Equivalence.reflexive eq) (inverseWellDefined additiveGroup *Commutative)) invRight
    ans : {m : ℕ} → 0 <N m → abs (index (apply _+_ (CauchyCompletion.elts (a *C b)) (map inverse (CauchyCompletion.elts (b *C a)))) m) < ε
    ans {m} 0<m rewrite indexAndApply (apply _*_ (CauchyCompletion.elts a) (CauchyCompletion.elts b)) (map inverse (apply _*_ (CauchyCompletion.elts b) (CauchyCompletion.elts a))) _+_ {m} | indexAndApply (CauchyCompletion.elts a) (CauchyCompletion.elts b) _*_ {m} | equalityCommutative (mapAndIndex (apply _*_ (CauchyCompletion.elts b) (CauchyCompletion.elts a)) inverse m) | indexAndApply (CauchyCompletion.elts b) (CauchyCompletion.elts a) _*_ {m} = <WellDefined (Equivalence.symmetric eq (Equivalence.transitive eq (absWellDefined _ _ foo) (identityOfIndiscernablesRight _∼_ (Equivalence.reflexive eq) absZero))) (Equivalence.reflexive eq) 0<e

private

  multiplicationWellDefinedLeft' : (0!=1 : 0R ∼ 1R → False) (a b c : CauchyCompletion) → Setoid._∼_ cauchyCompletionSetoid a b → Setoid._∼_ cauchyCompletionSetoid (a *C c) (b *C c)
  multiplicationWellDefinedLeft' 0!=1 a b c a=b ε 0<e = N , ans
    where
      abstract
        cBoundAndPr : Sg A (λ b → Sg ℕ (λ N → (m : ℕ) → (N <N m) → (abs (index (CauchyCompletion.elts c) m)) < b))
        cBoundAndPr = boundModulus c
        cBound : A
        cBound with cBoundAndPr
        ... | a , _ = a
        cBoundN : ℕ
        cBoundN with cBoundAndPr
        ... | _ , (N , _) = N
        cBoundPr : (m : ℕ) → (cBoundN <N m) → (abs (index (CauchyCompletion.elts c) m)) < cBound
        cBoundPr with cBoundAndPr
        ... | _ , (_ , pr) = pr
      0<cBound : 0G < cBound
      0<cBound with totality 0G cBound
      0<cBound | inl (inl 0<cBound) = 0<cBound
      0<cBound | inl (inr cBound<0) = exFalso (absNonnegative (SetoidPartialOrder.<Transitive pOrder (cBoundPr (succ cBoundN) (le 0 refl)) cBound<0))
      0<cBound | inr 0=cBound = exFalso (absNonnegative (<WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq 0=cBound) (cBoundPr (succ cBoundN) (le 0 refl))))
      e/c : A
      e/c with allInvertible cBound (λ pr → irreflexive (<WellDefined (Equivalence.reflexive eq) pr 0<cBound))
      ... | (1/c , _) = ε * 1/c
      e/cPr : e/c * cBound ∼ ε
      e/cPr with allInvertible cBound (λ pr → irreflexive (<WellDefined (Equivalence.reflexive eq) pr 0<cBound))
      ... | (1/c , pr) = Equivalence.transitive eq (Equivalence.transitive eq (Equivalence.transitive eq (Equivalence.symmetric eq *Associative) (*WellDefined (Equivalence.reflexive eq) pr)) *Commutative) (identIsIdent)
      0<e/c : 0G < e/c
      0<e/c = ringCanCancelPositive {0G} {e/c} 0<cBound (<WellDefined (Equivalence.symmetric eq (Equivalence.transitive eq *Commutative timesZero)) (Equivalence.symmetric eq e/cPr) 0<e)
      abBound : ℕ
      abBound with a=b e/c 0<e/c
      ... | Na=b , _ = Na=b
      abPr : {m : ℕ} → (abBound <N m) → abs (index (apply _+_ (CauchyCompletion.elts a) (map inverse (CauchyCompletion.elts b))) m) < e/c
      abPr with a=b e/c 0<e/c
      ... | Na=b , pr = pr
      N : ℕ
      N = abBound +N cBoundN
      cBounded : (m : ℕ) → (N <N m) → abs (index (CauchyCompletion.elts c) m) < cBound
      cBounded m N<m = cBoundPr m (inequalityShrinkRight N<m)
      a-bSmall : (m : ℕ) → N <N m → abs ((index (CauchyCompletion.elts a) m) + inverse (index (CauchyCompletion.elts b) m)) < e/c
      a-bSmall m N<m = <WellDefined (absWellDefined _ _ (transitive (identityOfIndiscernablesLeft _∼_ reflexive (equalityCommutative (indexAndApply (CauchyCompletion.elts a) (map inverse (CauchyCompletion.elts b)) _+_ {m}))) (+WellDefined reflexive (identityOfIndiscernablesLeft _∼_ reflexive (mapAndIndex (CauchyCompletion.elts b) inverse m))))) reflexive (abPr {m} (inequalityShrinkLeft N<m))
      ans : {m : ℕ} → N <N m → abs (index (apply _+_ (apply _*_ (CauchyCompletion.elts a) (CauchyCompletion.elts c)) (map inverse (apply _*_ (CauchyCompletion.elts b) (CauchyCompletion.elts c)))) m) < ε
      ans {m} N<m = <WellDefined (absWellDefined _ _ (transitive (+WellDefined (identityOfIndiscernablesLeft _∼_ reflexive (indexAndApply _ _ _*_ {m})) (transitive (transitive (ringMinusExtracts' R) (inverseWellDefined additiveGroup (identityOfIndiscernablesRight _∼_ reflexive (equalityCommutative (indexAndApply _ _ _*_ {m}))))) (identityOfIndiscernablesLeft _∼_ reflexive (equalityCommutative (mapAndIndex _ inverse m))))) (identityOfIndiscernablesLeft _∼_ reflexive (indexAndApply _ _ _+_ {m})))) reflexive (<WellDefined (absWellDefined ((index (CauchyCompletion.elts a) m + inverse (index (CauchyCompletion.elts b) m)) * index (CauchyCompletion.elts c) m) _ (Equivalence.transitive eq (Equivalence.transitive eq *Commutative *DistributesOver+) (+WellDefined *Commutative *Commutative))) (Equivalence.reflexive eq) (<WellDefined (Equivalence.symmetric eq (absRespectsTimes _ _)) (Equivalence.reflexive eq) (<WellDefined (Equivalence.reflexive eq) e/cPr (ans' (index (CauchyCompletion.elts a) m) (index (CauchyCompletion.elts b) m) (index (CauchyCompletion.elts c) m) (a-bSmall m N<m) (cBounded m N<m)))))
        where
          ans' : (a b c : A) → abs (a + inverse b) < e/c → abs c < cBound → (abs (a + inverse b) * abs c) < (e/c * cBound)
          ans' a b c a-b<e/c c<bound with totality 0R c
          ans' a b c a-b<e/c c<bound | inl (inl 0<c) with totality 0G (a + inverse b)
          ans' a b c a-b<e/c c<bound | inl (inl 0<c) | inl (inl 0<a-b) = ringMultiplyPositives 0<a-b 0<c a-b<e/c c<bound
          ans' a b c a-b<e/c c<bound | inl (inl 0<c) | inl (inr a-b<0) = ringMultiplyPositives (lemm2 (a + inverse b) a-b<0) 0<c a-b<e/c c<bound
          ans' a b c a-b<e/c c<bound | inl (inl 0<c) | inr 0=a-b = <WellDefined (Equivalence.symmetric eq (Equivalence.transitive eq (*WellDefined (Equivalence.symmetric eq 0=a-b) (Equivalence.reflexive eq)) (Equivalence.transitive eq *Commutative timesZero))) (Equivalence.reflexive eq) (orderRespectsMultiplication 0<e/c 0<cBound)
          ans' a b c a-b<e/c c<bound | inl (inr c<0) with totality 0G (a + inverse b)
          ans' a b c a-b<e/c c<bound | inl (inr c<0) | inl (inl 0<a-b) = ringMultiplyPositives 0<a-b (lemm2 c c<0) a-b<e/c c<bound
          ans' a b c a-b<e/c c<bound | inl (inr c<0) | inl (inr a-b<0) = ringMultiplyPositives (lemm2 (a + inverse b) a-b<0) (lemm2 c c<0) a-b<e/c c<bound
          ans' a b c a-b<e/c c<bound | inl (inr c<0) | inr 0=a-b = <WellDefined (Equivalence.symmetric eq (Equivalence.transitive eq (*WellDefined (Equivalence.symmetric eq 0=a-b) (Equivalence.reflexive eq)) (Equivalence.transitive eq *Commutative timesZero))) (Equivalence.reflexive eq) (orderRespectsMultiplication 0<e/c 0<cBound)
          ans' a b c a-b<e/c c<bound | inr 0=c = <WellDefined (Equivalence.symmetric eq (Equivalence.transitive eq (*WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq 0=c)) timesZero)) (Equivalence.reflexive eq) (orderRespectsMultiplication 0<e/c 0<cBound)

  multiplicationWellDefinedLeft : (a b c : CauchyCompletion) → Setoid._∼_ cauchyCompletionSetoid a b → Setoid._∼_ cauchyCompletionSetoid (a *C c) (b *C c)
  multiplicationWellDefinedLeft with totality 0R 1R
  ... | inl (inl 0<1') = multiplicationWellDefinedLeft' (λ pr → irreflexive {0G} (<WellDefined (Equivalence.reflexive eq) (Equivalence.symmetric eq pr) 0<1'))
  ... | inl (inr 1<0) = multiplicationWellDefinedLeft' (λ pr → irreflexive {0G} (<WellDefined (Equivalence.symmetric eq pr) (Equivalence.reflexive eq) 1<0))
  ... | inr (0=1) = λ a b c a=b → Equivalence.transitive (Setoid.eq cauchyCompletionSetoid) {a *C c} {injection 0G} {b *C c} (Equivalence.symmetric (Setoid.eq cauchyCompletionSetoid) {injection 0G} {a *C c} (trivialIfInputTrivial 0=1 (a *C c))) (trivialIfInputTrivial 0=1 (b *C c))

  multiplicationPreservedLeft : {a b : A} {c : CauchyCompletion} → (a ∼ b) → Setoid._∼_ cauchyCompletionSetoid (injection a *C c) (injection b *C c)
  multiplicationPreservedLeft {a} {b} {c} a=b = multiplicationWellDefinedLeft (injection a) (injection b) c (injectionPreservesSetoid a b a=b)

  multiplicationPreservedRight : {a b : A} {c : CauchyCompletion} → (a ∼ b) → Setoid._∼_ cauchyCompletionSetoid (c *C injection a) (c *C injection b)
  multiplicationPreservedRight {a} {b} {c} a=b = Equivalence.transitive (Setoid.eq cauchyCompletionSetoid) {c *C injection a} {injection a *C c} {c *C injection b} (*CCommutative {c} {injection a}) (Equivalence.transitive (Setoid.eq cauchyCompletionSetoid) {injection a *C c} {injection b *C c} {c *C injection b} (multiplicationPreservedLeft {a} {b} {c} a=b) (*CCommutative {injection b} {c}))

multiplicationPreserved : {a b c d : A} → (a ∼ b) → (c ∼ d) → Setoid._∼_ cauchyCompletionSetoid (injection a *C injection c) (injection b *C injection d)
multiplicationPreserved {a} {b} {c} {d} a=b c=d = Equivalence.transitive (Setoid.eq cauchyCompletionSetoid) {injection a *C injection c} {injection a *C injection d} {injection b *C injection d} (multiplicationPreservedRight {c} {d} {injection a} c=d) (multiplicationPreservedLeft {a} {b} {injection d} a=b)

private
  multiplicationWellDefinedRight : (a b c : CauchyCompletion) → Setoid._∼_ cauchyCompletionSetoid b c → Setoid._∼_ cauchyCompletionSetoid (a *C b) (a *C c)
  multiplicationWellDefinedRight a b c b=c = Equivalence.transitive (Setoid.eq cauchyCompletionSetoid) {a *C b} {b *C a} {a *C c} (*CCommutative {a} {b}) (Equivalence.transitive (Setoid.eq cauchyCompletionSetoid) {b *C a} {c *C a} {a *C c} (multiplicationWellDefinedLeft b c a b=c) (*CCommutative {c} {a}))

multiplicationWellDefined : {a b c d : CauchyCompletion} → Setoid._∼_ cauchyCompletionSetoid a b → Setoid._∼_ cauchyCompletionSetoid c d → Setoid._∼_ cauchyCompletionSetoid (a *C c) (b *C d)
multiplicationWellDefined {a} {b} {c} {d} a=b c=d = Equivalence.transitive (Setoid.eq cauchyCompletionSetoid) {a *C c} {a *C d} {b *C d} (multiplicationWellDefinedRight a c d c=d) (multiplicationWellDefinedLeft a b d a=b)


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

module TNT.Base where

open import Data.Nat
open import Data.Nat.DivMod
open import Data.Nat.Divisibility
open import Data.Nat.Properties
open import Agda.Builtin.Equality
open import Relation.Nullary.Decidable
open import Data.Empty

-- defination of congruence modulo

_≡_⟨mod_⟩ : ℕ → ℕ → (n : ℕ) → {≢0 : False (n ≟ 0)} → Set
(a ≡ b ⟨mod 0 ⟩){}
a ≡ b ⟨mod suc n ⟩ = a % suc n ≡ b % suc n

-- commutative

≡-comm : ∀ {ℓ} {A : Set ℓ} {a b : A} → a ≡ b → b ≡ a
≡-comm refl = refl

≡-mod-comm : ∀ {a b n : ℕ} → a ≡ b ⟨mod suc n ⟩ → b ≡ a ⟨mod suc n ⟩
≡-mod-comm refl₁ = ≡-comm refl₁

-- divisibility

a≡b⟨modn⟩⇒a%n-b%n≡0 : ∀ {a b n : ℕ} → a ≡ b ⟨mod suc n ⟩ → ∣ a % suc n - b % suc n ∣ ≡ 0
a≡b⟨modn⟩⇒a%n-b%n≡0 refl₁ = n≡m⇒∣n-m∣≡0 refl₁

a%n-b%n≡0⇒⟨a-b⟩%n≡0 : ∀ {a b n : ℕ} → ∣ a % suc n - b % suc n ∣ ≡ 0 → ∣ a - b ∣ % suc n ≡ 0
a%n-b%n≡0⇒⟨a-b⟩%n≡0 = {!!}

≡-mod-div : ∀ {a b n : ℕ} → a ≡ b ⟨mod suc n ⟩ → suc n ∣ ∣ a - b ∣
≡-mod-div {a} {b} {n} refl₁ = m%n≡0⇒n∣m ∣ a - b ∣ n (a%n-b%n≡0⇒⟨a-b⟩%n≡0 {a} {b} {n} (a≡b⟨modn⟩⇒a%n-b%n≡0 {a} {b} {n} refl₁))

-- transitivity

≡-trans : ∀ {ℓ} {A : Set ℓ} {a b c : A} → a ≡ b → b ≡ c → a ≡ c
≡-trans refl refl = refl

≡-mod-trans : ∀ {a b c n : ℕ} → a ≡ b ⟨mod suc n ⟩ → b ≡ c ⟨mod suc n ⟩ → a ≡ c ⟨mod suc n ⟩
≡-mod-trans refl₁ refl₂ = ≡-trans refl₁ refl₂


{-# OPTIONS --cubical --safe #-}
module Cubical.Foundations.Pointed.Properties where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Pointed.Base
open import Cubical.Data.Prod

Π∙ : ∀ {ℓ ℓ'} (A : Type ℓ) (B∙ : A → Pointed ℓ') → Pointed (ℓ-max ℓ ℓ')
Π∙ A B∙ = (∀ a → typ (B∙ a)) , (λ a → pt (B∙ a))

Σ∙ : ∀ {ℓ ℓ'} (A∙ : Pointed ℓ) (B∙ : typ A∙ → Pointed ℓ') → Pointed (ℓ-max ℓ ℓ')
Σ∙ A∙ B∙ = (Σ[ a ∈ typ A∙ ] typ (B∙ a)) , (pt A∙ , pt (B∙ (pt A∙)))

_×∙_ : ∀ {ℓ ℓ'} (A∙ : Pointed ℓ) (B∙ : Pointed ℓ') → Pointed (ℓ-max ℓ ℓ')
A∙ ×∙ B∙ = ((typ A∙) × (typ B∙)) , (pt A∙ , pt B∙)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Greatest common divisor
------------------------------------------------------------------------

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

module Data.Nat.GCD where

open import Data.Nat
open import Data.Nat.Divisibility
open import Data.Nat.GCD.Lemmas
open import Data.Nat.Properties using (+-suc)
open import Data.Product
open import Function
open import Induction
open import Induction.Nat using (<′-Rec; <′-recBuilder)
open import Induction.Lexicographic
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as P using (_≡_; subst)
open import Relation.Nullary using (Dec; yes; no)

------------------------------------------------------------------------
-- Greatest common divisor

module GCD where

  -- Specification of the greatest common divisor (gcd) of two natural
  -- numbers.

  record GCD (m n gcd : ℕ) : Set where
    constructor is
    field
      -- The gcd is a common divisor.
      commonDivisor : gcd ∣ m × gcd ∣ n

      -- All common divisors divide the gcd, i.e. the gcd is the
      -- greatest common divisor according to the partial order _∣_.
      greatest : ∀ {d} → d ∣ m × d ∣ n → d ∣ gcd

  open GCD public

  -- The gcd is unique.

  unique : ∀ {d₁ d₂ m n} → GCD m n d₁ → GCD m n d₂ → d₁ ≡ d₂
  unique d₁ d₂ = ∣-antisym (GCD.greatest d₂ (GCD.commonDivisor d₁))
                           (GCD.greatest d₁ (GCD.commonDivisor d₂))

  -- The gcd relation is "symmetric".

  sym : ∀ {d m n} → GCD m n d → GCD n m d
  sym g = is (swap $ GCD.commonDivisor g) (GCD.greatest g ∘ swap)

  -- The gcd relation is "reflexive".

  refl : ∀ {n} → GCD n n n
  refl = is (∣-refl , ∣-refl) proj₁

  -- The GCD of 0 and n is n.

  base : ∀ {n} → GCD 0 n n
  base {n} = is (n ∣0 , ∣-refl) proj₂

  -- If d is the gcd of n and k, then it is also the gcd of n and
  -- n + k.

  step : ∀ {n k d} → GCD n k d → GCD n (n + k) d
  step g with GCD.commonDivisor g
  step {n} {k} {d} g | (d₁ , d₂) = is (d₁ , ∣m∣n⇒∣m+n d₁ d₂) greatest′
    where
    greatest′ : ∀ {d′} → d′ ∣ n × d′ ∣ n + k → d′ ∣ d
    greatest′ (d₁ , d₂) = GCD.greatest g (d₁ , ∣m+n∣m⇒∣n d₂ d₁)

open GCD public using (GCD) hiding (module GCD)

------------------------------------------------------------------------
-- Calculating the gcd

-- The calculation also proves Bézout's lemma.

module Bézout where

  module Identity where

    -- If m and n have greatest common divisor d, then one of the
    -- following two equations is satisfied, for some numbers x and y.
    -- The proof is "lemma" below (Bézout's lemma).
    --
    -- (If this identity was stated using integers instead of natural
    -- numbers, then it would not be necessary to have two equations.)

    data Identity (d m n : ℕ) : Set where
      +- : (x y : ℕ) (eq : d + y * n ≡ x * m) → Identity d m n
      -+ : (x y : ℕ) (eq : d + x * m ≡ y * n) → Identity d m n

    -- Various properties about Identity.

    sym : ∀ {d} → Symmetric (Identity d)
    sym (+- x y eq) = -+ y x eq
    sym (-+ x y eq) = +- y x eq

    refl : ∀ {d} → Identity d d d
    refl = -+ 0 1 P.refl

    base : ∀ {d} → Identity d 0 d
    base = -+ 0 1 P.refl

    private
      infixl 7 _⊕_

      _⊕_ : ℕ → ℕ → ℕ
      m ⊕ n = 1 + m + n

    step : ∀ {d n k} → Identity d n k → Identity d n (n + k)
    step {d}     (+-  x  y       eq) with compare x y
    step {d}     (+- .x .x       eq) | equal x     = +- (2 * x)     x       (lem₂ d x   eq)
    step {d}     (+- .x .(x ⊕ i) eq) | less x i    = +- (2 * x ⊕ i) (x ⊕ i) (lem₃ d x   eq)
    step {d} {n} (+- .(y ⊕ i) .y eq) | greater y i = +- (2 * y ⊕ i) y       (lem₄ d y n eq)
    step {d}     (-+  x  y       eq) with compare x y
    step {d}     (-+ .x .x       eq) | equal x     = -+ (2 * x)     x       (lem₅ d x   eq)
    step {d}     (-+ .x .(x ⊕ i) eq) | less x i    = -+ (2 * x ⊕ i) (x ⊕ i) (lem₆ d x   eq)
    step {d} {n} (-+ .(y ⊕ i) .y eq) | greater y i = -+ (2 * y ⊕ i) y       (lem₇ d y n eq)

  open Identity public using (Identity; +-; -+) hiding (module Identity)

  module Lemma where

    -- This type packs up the gcd, the proof that it is a gcd, and the
    -- proof that it satisfies Bézout's identity.

    data Lemma (m n : ℕ) : Set where
      result : (d : ℕ) (g : GCD m n d) (b : Identity d m n) → Lemma m n

    -- Various properties about Lemma.

    sym : Symmetric Lemma
    sym (result d g b) = result d (GCD.sym g) (Identity.sym b)

    base : ∀ d → Lemma 0 d
    base d = result d GCD.base Identity.base

    refl : ∀ d → Lemma d d
    refl d = result d GCD.refl Identity.refl

    stepˡ : ∀ {n k} → Lemma n (suc k) → Lemma n (suc (n + k))
    stepˡ {n} {k} (result d g b) =
      subst (Lemma n) (+-suc n k) $
        result d (GCD.step g) (Identity.step b)

    stepʳ : ∀ {n k} → Lemma (suc k) n → Lemma (suc (n + k)) n
    stepʳ = sym ∘ stepˡ ∘ sym

  open Lemma public using (Lemma; result) hiding (module Lemma)

  -- Bézout's lemma proved using some variant of the extended
  -- Euclidean algorithm.

  lemma : (m n : ℕ) → Lemma m n
  lemma m n = build [ <′-recBuilder ⊗ <′-recBuilder ] P gcd (m , n)
    where
    P : ℕ × ℕ → Set
    P (m , n) = Lemma m n

    gcd : ∀ p → (<′-Rec ⊗ <′-Rec) P p → P p
    gcd (zero  , n                 ) rec = Lemma.base n
    gcd (suc m , zero              ) rec = Lemma.sym (Lemma.base (suc m))
    gcd (suc m , suc n             ) rec with compare m n
    gcd (suc m , suc .m            ) rec | equal .m     = Lemma.refl (suc m)
    gcd (suc m , suc .(suc (m + k))) rec | less .m k    =
                      -- "gcd (suc m) (suc k)"
      Lemma.stepˡ $ proj₁ rec (suc k) (lem₁ k m)
    gcd (suc .(suc (n + k)) , suc n) rec | greater .n k =
                      -- "gcd (suc k) (suc n)"
      Lemma.stepʳ $ proj₂ rec (suc k) (lem₁ k n) (suc n)

  -- Bézout's identity can be recovered from the GCD.

  identity : ∀ {m n d} → GCD m n d → Identity d m n
  identity {m} {n} g with lemma m n
  ... | result d g′ b with GCD.unique g g′
  ...   | P.refl = b

-- Calculates the gcd of the arguments.

gcd : (m n : ℕ) → ∃ λ d → GCD m n d
gcd m n with Bézout.lemma m n
... | Bézout.result d g _ = (d , g)

-- gcd as a proposition is decidable

gcd? : (m n d : ℕ) → Dec (GCD m n d)
gcd? m n d with gcd m n
... | d′ , p with d′ ≟ d
...   | no ¬g = no (¬g ∘ GCD.unique p)
...   | yes g = yes (subst (GCD m n) g p)


-- Transport properties

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

module Constructive.Axiom.Properties.Transport where

-- agda-stdlib
open import Level renaming (suc to lsuc; zero to lzero)
open import Data.Sum as Sum
open import Data.Product as Prod
open import Relation.Binary.PropositionalEquality
open import Function.Base
import Function.LeftInverse as LInv -- TODO use new packages
import Function.Equality as Eq

-- agda-misc
open import Constructive.Common
open import Constructive.Combinators
open import Constructive.Axiom
open import Constructive.Axiom.Properties.Base

module Transport
  {a b p} {A : Set a} {B : Set b}
  (f : A → B) (g : B → A) (inv : ∀ x → f (g x) ≡ x)
  where
  private
    ∃Pf→∃P : {P : B → Set p} → ∃ (λ x → P (f x)) → ∃ P
    ∃Pf→∃P (x , Pfx) = (f x , Pfx)

    ∃P→∃Pf : {P : B → Set p} → ∃ P → ∃ (λ x → P (f x))
    ∃P→∃Pf {P = P} (x , Px) = g x , subst P (sym (inv x)) Px

    ∃Qg→∃Q : {Q : A → Set p} → ∃ (λ x → Q (g x)) → ∃ Q
    ∃Qg→∃Q (x , Qgx) = g x , Qgx

  lpo-transport : LPO A p → LPO B p
  lpo-transport lpo P? =
    Sum.map ∃Pf→∃P (contraposition ∃P→∃Pf) $ lpo (DecU-map f P?)

  llpo-transport : LLPO A p → LLPO B p
  llpo-transport llpo P? Q? w =
    Sum.map (contraposition ∃P→∃Pf) (contraposition ∃P→∃Pf) $
            llpo (DecU-map f P?) (DecU-map f Q?)
                 (contraposition (Prod.map ∃Pf→∃P ∃Pf→∃P) w)

  wlpo-Alt-transport : WLPO-Alt A p → WLPO-Alt B p
  wlpo-Alt-transport wlpo-Alt P? =
    Sum.map (contraposition ∃P→∃Pf) (DN-map ∃Pf→∃P) $ wlpo-Alt (DecU-map f P?)

  wlpo-transport : WLPO A p → WLPO B p
  wlpo-transport = wlpo-Alt⇒wlpo ∘′ wlpo-Alt-transport ∘′ wlpo⇒wlpo-Alt

  mr-transport : MR A p → MR B p
  mr-transport mr P? ¬¬∃P =
    ∃Pf→∃P $ mr (DecU-map f P?) (DN-map ∃P→∃Pf ¬¬∃P)

  mp-transport : MP A p → MP B p
  mp-transport = mr⇒mp ∘′ mr-transport ∘′ mp⇒mr

  wmp-transport : WMP A p → WMP B p
  wmp-transport wmp {P = P} P? hyp =
    ∃Pf→∃P $ wmp (DecU-map f P?)
    λ Q? → Sum.map (DN-map ∃Qg→∃Q) (DN-map λ {(x , Px , ¬Qgx) →
      g x , (subst P (sym (inv x)) Px) , ¬Qgx }) $ hyp (DecU-map g Q?)

  mp⊎-transport : MP⊎ A p → MP⊎ B p
  mp⊎-transport mp⊎ P? Q? w =
    Sum.map (DN-map ∃Pf→∃P) (DN-map ∃Pf→∃P) $
            mp⊎ (DecU-map f P?) (DecU-map f Q?)
                (contraposition (Prod.map (contraposition ∃P→∃Pf)
                                          (contraposition ∃P→∃Pf)) w)

open Transport public

module TransportByLeftInverse
  {a b p} {A : Set a} {B : Set b} (linv : B LInv.↞ A) =
  Transport {p = p} (LInv.LeftInverse.from linv Eq.⟨$⟩_)
    (LInv.LeftInverse.to linv Eq.⟨$⟩_) (LInv.LeftInverse.left-inverse-of linv)


{-# OPTIONS --cubical #-}

module Type.Cubical.SubtypeSet where

open import Function.Axioms
open import Functional
open import Logic.Predicate as PTLogic using () renaming ([∃]-intro to intro)
import      Lvl
open import Structure.Function.Domain using (intro ; Inverseₗ ; Inverseᵣ)
open import Structure.Relator.Properties
open import Structure.Type.Identity
open import Type.Cubical.Equiv
import      Type.Cubical.Logic as Logic
open import Type.Cubical.Path.Equality
open import Type.Cubical.Univalence
open import Type.Cubical
open import Type.Properties.MereProposition
open import Type.Properties.Singleton.Proofs
open import Type

private variable ℓ ℓ₁ ℓ₂ ℓₑ : Lvl.Level
private variable T A B P Q : Type{ℓ}

{-
module _ {P Q : T → Type} ⦃ prop-P : ∀{x} → MereProposition{ℓ}(P(x)) ⦄ ⦃ prop-Q : ∀{x} → MereProposition{ℓ}(Q(x)) ⦄ where
  prop-set-extensionalityₗ : (P ≡ Q) ← (∀{x} → P(x) ↔ Q(x))
  prop-set-extensionalityₗ pq = functionExtensionalityOn P Q (propositional-extensionalityₗ pq)
-}

--data Prop{ℓ} : Type{Lvl.𝐒(ℓ)} where
--  intro : (T : Type{ℓ}) → ⦃ MereProposition(T) ⦄ → Prop
Prop = \{ℓ} → PTLogic.∃{Obj = Type{ℓ}} (T ↦ MereProposition(T))

⊤ : Prop
⊤ = intro(Logic.⊤) ⦃ prop-top ⦄

⊥ : Prop
⊥ = intro(Logic.⊥) ⦃ prop-bottom ⦄

¬_ : Prop{ℓ} → Prop
¬(intro A) = intro(Logic.¬ A) ⦃ prop-negation ⦄

_⟶_ : Prop{ℓ₁} → Prop{ℓ₂} → Prop
(intro A) ⟶ (intro B) = intro(A → B) ⦃ prop-implication ⦄

_∨_ : Prop{ℓ₁} → Prop{ℓ₂} → Prop
(intro A) ∨ (intro B) = intro(A Logic.∨ B)

_∧_ : Prop{ℓ₁} → Prop{ℓ₂} → Prop
(intro A) ∧ (intro B) = intro(A Logic.∧ B) ⦃ prop-conjunction ⦄

∃ : (T → Prop{ℓ}) → Prop
∃ P = intro(Logic.∃(PTLogic.[∃]-witness ∘ P))

-- ∀ₚ : (T → Prop{ℓ}) → Prop
-- ∀ₚ P = intro(PTLogic.∀ₗ(PTLogic.[∃]-witness ∘ P)) ⦃ {!prop-universal!} ⦄

record SubtypeSet {ℓₑ ℓ} (T : Type{ℓ}) : Type{ℓ Lvl.⊔ Lvl.𝐒(ℓₑ)} where
  constructor filter
  field _∋_ : T → Prop{ℓₑ}
open SubtypeSet using (_∋_) public

{- TODO: When Structure is generalized to arbitrary logic symbols
import      Structure.Sets.Names
open        Structure.Sets.Names.From-[∋] (_∋_) public
-}

_∈_ : T → SubtypeSet{ℓ}(T) → Prop
_∈_ = swap(_∋_)

_∉_ : T → SubtypeSet{ℓ}(T) → Prop
_∉_ = (¬_) ∘₂ (_∈_)

_∌_ : SubtypeSet{ℓ}(T) → T → Prop
_∌_ = (¬_) ∘₂ (_∋_)

∅ : SubtypeSet(T)
∅ ∋ _ = ⊥

𝐔 : SubtypeSet(T)
𝐔 ∋ _ = ⊤

∁ : SubtypeSet{ℓ}(T) → SubtypeSet(T)
(∁ A) ∋ x = A ∌ x

_∪_ : SubtypeSet{ℓ₁}(T) → SubtypeSet{ℓ₂}(T) → SubtypeSet(T)
(A ∪ B) ∋ x = (A ∋ x) ∨ (B ∋ x)

_∩_ : SubtypeSet{ℓ₁}(T) → SubtypeSet{ℓ₂}(T) → SubtypeSet(T)
(A ∩ B) ∋ x = (A ∋ x) ∧ (B ∋ x)

_∖_ : SubtypeSet{ℓ₁}(T) → SubtypeSet{ℓ₂}(T) → SubtypeSet(T)
(A ∖ B) ∋ x = (A ∋ x) ∧ (B ∌ x)

unmap : (A → B) → SubtypeSet{ℓ}(B) → SubtypeSet(A)
unmap f(A) ∋ x = A ∋ f(x)

-- map : (A → B) → SubtypeSet{ℓ}(A) → SubtypeSet(B)
-- map f(A) ∋ y = ∃(x ↦ (A ∋ x) ∧ (f(x) ≡ y))

-- TODO: Maybe SubtypeSet should require that the witness is a HSet?
-- ⊶ : (A → B) → SubtypeSet(B)
-- (⊶ f) ∋ y = ∃(x ↦ PTLogic.[∃]-intro (f(x) ≡ y) ⦃ {!!} ⦄)


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

module PiIter where

open import Level using (_⊔_) renaming (zero to l0; suc to lsuc)
open import Universe using (Universe)

open import Categories.Category using (Category)
open import Categories.Groupoid using (Groupoid)
open import Categories.Functor using (Functor)

open import Data.Empty using (⊥)
open import Data.Unit using (⊤; tt)
open import Data.Sum hiding ([_,_])
open import Data.Product
open import Relation.Binary.PropositionalEquality as P
open import Function using (flip)
open import Data.Nat using (ℕ; zero; suc)
open import Data.Nat.Properties.Simple using (+-right-identity)
open import Data.Integer using (ℤ;+_;-[1+_]) renaming (suc to ℤsuc; -_ to ℤ-; _+_ to _ℤ+_)

open import PiU using (U; ZERO; ONE; PLUS; TIMES; toℕ)
open import PiLevel0
open import PiLevel1
open import PiEquiv renaming (eval to ap; evalB to apB)
open import Equiv

infix 40 _^_

_^_ : {τ : U} → (p : τ ⟷ τ) → (k : ℤ) → (τ ⟷ τ)
p ^ (+ 0) = id⟷
p ^ (+ (suc k)) = p ◎ (p ^ (+ k))
p ^ -[1+ 0 ] = ! p
p ^ (-[1+ (suc k) ]) = (! p) ◎ (p ^ -[1+ k ])

-- useful properties of ^
-- because ^ is iterated composition of the same thing,
-- then by associativity, we can hive off compositions
-- from left or right

assoc1 : {τ : U} → {p : τ ⟷ τ} → (m : ℕ) →
  (p ◎ (p ^ (+ m))) ⇔ ((p ^ (+ m)) ◎ p)
assoc1 ℕ.zero = trans⇔ idr◎l idl◎r
assoc1 (suc m) = trans⇔ (id⇔ ⊡ assoc1 m) assoc◎l

assoc1- : {τ : U} → {p : τ ⟷ τ} → (m : ℕ) →
  ((! p) ◎ (p ^ -[1+ m ])) ⇔ ((p ^ -[1+ m ]) ◎ (! p))
assoc1- ℕ.zero = id⇔
assoc1- (suc m) = trans⇔ (id⇔ ⊡ assoc1- m) assoc◎l

-- more generally
assoc1g : {τ : U} → {p : τ ⟷ τ} → (i : ℤ) →
  (p ◎ (p ^ i)) ⇔ ((p ^ i) ◎ p)
assoc1g (+_ n) = assoc1 n
assoc1g (-[1+_] zero) = trans⇔ linv◎l rinv◎r
assoc1g (-[1+_] (suc n)) = trans⇔ assoc◎l (trans⇔ (linv◎l ⊡ id⇔) (
  trans⇔ idl◎l (2! (trans⇔ (assoc1- n ⊡ id⇔) (trans⇔ assoc◎r
  (trans⇔ (id⇔ ⊡ rinv◎l) idr◎l))))))

-- Property of ^: negating exponent is same as
-- composing in the other direction, then reversing.
^⇔! : {τ : U} → {p : τ ⟷ τ} → (k : ℤ) →
  (p ^ (ℤ- k)) ⇔ ! (p ^ k)
^⇔! (+_ ℕ.zero) = id⇔
-- need to dig deeper, as we end up negating
^⇔! (+_ (suc ℕ.zero)) = idl◎r
^⇔! (+_ (suc (suc n))) = trans⇔ (assoc1- n) (^⇔! (+ suc n) ⊡ id⇔)
^⇔! {p = p} (-[1+_] ℕ.zero) = trans⇔ idr◎l (2! !!⇔id) -- (!!⇔id {c = p})
^⇔! {p = p} (-[1+_] (suc n)) =
  trans⇔ (assoc1 (suc n)) ((^⇔! -[1+ n ]) ⊡ 2! !!⇔id) -- (!!⇔id p))

-- first match on m, n, then proof is purely PiLevel1
lower : {τ : U} {p : τ ⟷ τ} (m n : ℤ) →
  p ^ (m ℤ+ n) ⇔ ((p ^ m) ◎ (p ^ n))
lower (+_ ℕ.zero) (+_ n) = idl◎r
lower (+_ ℕ.zero) (-[1+_] n) = idl◎r
lower (+_ (suc m)) (+_ n) =
  trans⇔ (id⇔ ⊡ lower (+ m) (+ n)) assoc◎l
lower {p = p} (+_ (suc m)) (-[1+_] ℕ.zero) =
  trans⇔ idr◎r (trans⇔ (id⇔ ⊡ linv◎r) (
  trans⇔ assoc◎l (2! (assoc1 m) ⊡ id⇔)))  -- p ^ ((m + 1) -1)
lower (+_ (suc m)) (-[1+_] (suc n)) = -- p ^ ((m + 1) -(1+1+n)
  trans⇔ (lower (+ m) (-[1+ n ])) (
  trans⇔ ((trans⇔ idr◎r (id⇔ ⊡ linv◎r))  ⊡ id⇔) (
  trans⇔ assoc◎r (trans⇔ (id⇔ ⊡ assoc◎r) (
  trans⇔ assoc◎l (2! (assoc1 m) ⊡ id⇔)))))
lower (-[1+_] m) (+_ ℕ.zero) = idr◎r
lower (-[1+_] ℕ.zero) (+_ (suc n)) = 2! (trans⇔ assoc◎l (
  trans⇔ (rinv◎l ⊡ id⇔) idl◎l))
lower (-[1+_] (suc m)) (+_ (suc n)) = -- p ^ (-(1+m) + (n+1))
  trans⇔ (lower (-[1+ m ]) (+ n)) (
    trans⇔ ((trans⇔ idr◎r (id⇔ ⊡ rinv◎r))  ⊡ id⇔) (
  trans⇔ assoc◎r (trans⇔ (id⇔ ⊡ assoc◎r) (
  trans⇔ assoc◎l ((2! (assoc1- m)) ⊡ id⇔)))))
lower (-[1+_] ℕ.zero) (-[1+_] n) = id⇔
lower (-[1+_] (suc m)) (-[1+_] n) = -- p ^ (-(1+1+m) - (1+n))
  trans⇔ (id⇔ ⊡ lower (-[1+ m ]) (-[1+ n ])) assoc◎l

-- i.e. Iter is: for all i, any p' such that
-- p' ⇔ p ^ i

record Iter {τ : U} (p : τ ⟷ τ) : Set where
  constructor iter
  field
    i : ℤ
    p' : τ ⟷ τ
    p'⇔p^i : p' ⇔ (p ^ i)

-- Equality of Iter.
record _≡c_ {τ : U} {p : τ ⟷ τ} (q r : Iter p) : Set where
  constructor eqc
  field
    iter≡ : Iter.i q ≡ Iter.i r
    p⇔ : Iter.p' q ⇔ Iter.p' r

-- Iterates of id⟷
id^i≡id : {t : U} (i : ℤ) → (id⟷ ^ i) ⇔ (id⟷ {t})
id^i≡id (+_ zero) = id⇔
id^i≡id (+_ (suc n)) = trans⇔ idl◎l (id^i≡id (+ n))
id^i≡id (-[1+_] zero) = id⇔
id^i≡id (-[1+_] (suc n)) = trans⇔ idl◎l (id^i≡id -[1+ n ])


module _ where

open import Agda.Builtin.String

_&_ = primStringAppend

record Show (A : Set) : Set where
  field
    show : A → String

open Show {{...}} public

data Bin : Set where
  []  : Bin
  0:_ : Bin → Bin
  1:_ : Bin → Bin

five : Bin
five = 1: 0: 1: []

instance
  ShowBin : Show Bin
  show {{ShowBin}} [] = ""
  show {{ShowBin}} (0: b) = "0" & show b
  show {{ShowBin}} (1: b) = "1" & show b

hole : Bin → Bin
hole x = {!!}