------------------------------------------------------------------------
-- Syntax of pure type systems (PTS)
------------------------------------------------------------------------

module Pts.Syntax where

open import Data.Fin using (Fin)
open import Data.Fin.Substitution
open import Data.Fin.Substitution.Lemmas
open import Data.Fin.Substitution.ExtraLemmas
open import Data.Fin.Substitution.Typed
open import Data.Star using (Star; ε; _◅_)
open import Data.Nat using (ℕ; suc; _+_)
open import Data.Vec using (lookup)
open import Relation.Binary.PropositionalEquality as PropEq
open PropEq.≡-Reasoning

-- All submodules are parametrized by a given set of sorts.
module _ (Sort : Set) where

  ----------------------------------------------------------------------
  -- Abstract syntax of terms

  module Syntax where

    infixl 9 _·_

    -- Untyped terms with up to n free variables.
    data Term (n : ℕ) : Set where
      var  : (x : Fin n)                     → Term n  -- variable
      sort : (s : Sort)                      → Term n  -- sort
      Π    : (a : Term n) (b : Term (1 + n)) → Term n  -- dependent product
      ƛ    : (a : Term n) (b : Term (1 + n)) → Term n  -- abstraction
      _·_  : (a b : Term n)                  → Term n  -- application

  open Syntax

  ----------------------------------------------------------------------
  -- Substitutions in terms

  -- Application of generic substitutions to terms
  module SubstApp {T} (l : Lift T Term) where
    open Lift l

    infixl 8 _/_

    -- Apply a substitution to a term.
    _/_ : ∀ {m n} → Term m → Sub T m n → Term n
    var x  / σ = lift (lookup x σ)
    sort s / σ = sort s
    Π a b  / σ = Π (a / σ) (b / σ ↑)
    ƛ a b  / σ = ƛ (a / σ) (b / σ ↑)
    a · b  / σ = (a / σ) · (b / σ)

    -- Some helper lemmas about applying sequences of substitutions.
    -- To be used in PathSubstLemmas.

    open Application (record { _/_ = _/_ }) hiding (_/_)

    -- Sorts are invariant under substitution.
    sort-/✶-↑✶ : ∀ k {m n s} (σs : Subs T m n) → sort s /✶ σs ↑✶ k ≡ sort s
    sort-/✶-↑✶ k ε        = refl
    sort-/✶-↑✶ k (σ ◅ σs) = cong₂ _/_ (sort-/✶-↑✶ k σs) refl

    -- Substitutions in dependent product types are compositional.
    Π-/✶-↑✶ : ∀ k {m n a b} (σs : Subs T m n) →
                  (Π a b) /✶ σs ↑✶ k ≡  Π (a /✶ σs ↑✶ k) (b /✶ σs ↑✶ suc k)
    Π-/✶-↑✶ k ε        = refl
    Π-/✶-↑✶ k (σ ◅ σs) = cong₂ _/_ (Π-/✶-↑✶ k σs) refl

    -- Substitutions in abstractions are compositional.
    ƛ-/✶-↑✶ : ∀ k {m n a b} (σs : Subs T m n) →
                  (ƛ a b) /✶ σs ↑✶ k ≡  ƛ (a /✶ σs ↑✶ k) (b /✶ σs ↑✶ suc k)
    ƛ-/✶-↑✶ k ε        = refl
    ƛ-/✶-↑✶ k (σ ◅ σs) = cong₂ _/_ (ƛ-/✶-↑✶ k σs) refl

    -- Substitutions in applications are compositional.
    ·-/✶-↑✶ : ∀ k {m n a b} (σs : Subs T m n) →
                  (a · b) /✶ σs ↑✶ k ≡ (a /✶ σs ↑✶ k) · (b /✶ σs ↑✶ k)
    ·-/✶-↑✶ k ε        = refl
    ·-/✶-↑✶ k (σ ◅ σs) = cong₂ _/_ (·-/✶-↑✶ k σs) refl

  -- Substitutions in terms and associated lemmas.
  module Substitution where

    -- Term substitutions.
    termSubst : TermSubst Term
    termSubst = record { var = var; app = SubstApp._/_ }

    -- Lemmas relating application of sequences of generic substitutions
    -- lifted to any number of additional variables.
    --
    -- Using these generic lemmas, we can instantiate the record
    -- Data.Fin.Substitution.Lemmas.TermLemmas below, which gives access
    -- to a number of useful (derived) lemmas about path substitutions.
    module Lemmas {T₁ T₂} {lift₁ : Lift T₁ Term} {lift₂ : Lift T₂ Term} where
      open SubstApp
      open Lift lift₁ using () renaming (_↑✶_ to _↑✶₁_)
      open Lift lift₂ using () renaming (_↑✶_ to _↑✶₂_)
      open Application (record { _/_ = SubstApp._/_ lift₁ }) using ()
        renaming (_/✶_ to _/✶₁_)
      open Application (record { _/_ = SubstApp._/_ lift₂ }) using ()
        renaming (_/✶_ to _/✶₂_)

      -- Sequences of (lifted) T₁ and T₂-substitutions are equivalent
      -- when applied to terms if they are equivalent when applied to
      -- variables.
      /✶-↑✶ : ∀ {m n} (σs₁ : Subs T₁ m n) (σs₂ : Subs T₂ m n) →
                (∀ k x → var x /✶₁ σs₁ ↑✶₁ k ≡ var x /✶₂ σs₂ ↑✶₂ k) →
                 ∀ k t → t     /✶₁ σs₁ ↑✶₁ k ≡ t     /✶₂ σs₂ ↑✶₂ k
      /✶-↑✶ σs₁ σs₂ hyp k (var x)  = hyp k x
      /✶-↑✶ σs₁ σs₂ hyp k (sort s) = begin
        sort s /✶₁ σs₁ ↑✶₁ k   ≡⟨ sort-/✶-↑✶ _ k σs₁ ⟩
        sort s                 ≡⟨ sym (sort-/✶-↑✶ _ k σs₂) ⟩
        sort s /✶₂ σs₂ ↑✶₂ k   ∎
      /✶-↑✶ σs₁ σs₂ hyp k (Π a b) = begin
          (Π a b) /✶₁ σs₁ ↑✶₁ k
        ≡⟨ Π-/✶-↑✶ _ k σs₁ ⟩
          Π (a /✶₁ σs₁ ↑✶₁ k) (b /✶₁ σs₁ ↑✶₁ suc k)
        ≡⟨ cong₂ Π (/✶-↑✶ σs₁ σs₂ hyp k a) (/✶-↑✶ σs₁ σs₂ hyp (suc k) b) ⟩
          Π (a /✶₂ σs₂ ↑✶₂ k) (b /✶₂ σs₂ ↑✶₂ suc k)
        ≡⟨ sym (Π-/✶-↑✶ _ k σs₂) ⟩
          (Π a b) /✶₂ σs₂ ↑✶₂ k
        ∎
      /✶-↑✶ σs₁ σs₂ hyp k (ƛ a b) = begin
          (ƛ a b) /✶₁ σs₁ ↑✶₁ k
        ≡⟨ ƛ-/✶-↑✶ _ k σs₁ ⟩
          ƛ (a /✶₁ σs₁ ↑✶₁ k) (b /✶₁ σs₁ ↑✶₁ suc k)
        ≡⟨ cong₂ ƛ (/✶-↑✶ σs₁ σs₂ hyp k a) (/✶-↑✶ σs₁ σs₂ hyp (suc k) b) ⟩
          ƛ (a /✶₂ σs₂ ↑✶₂ k) (b /✶₂ σs₂ ↑✶₂ suc k)
        ≡⟨ sym (ƛ-/✶-↑✶ _ k σs₂) ⟩
          (ƛ a b) /✶₂ σs₂ ↑✶₂ k
        ∎
      /✶-↑✶ σs₁ σs₂ hyp k (a · b) = begin
          (a · b) /✶₁ σs₁ ↑✶₁ k
        ≡⟨ ·-/✶-↑✶ _ k σs₁ ⟩
          (a /✶₁ σs₁ ↑✶₁ k) · (b /✶₁ σs₁ ↑✶₁ k)
        ≡⟨ cong₂ _·_ (/✶-↑✶ σs₁ σs₂ hyp k a) (/✶-↑✶ σs₁ σs₂ hyp k b) ⟩
          (a /✶₂ σs₂ ↑✶₂ k) · (b /✶₂ σs₂ ↑✶₂ k)
        ≡⟨ sym (·-/✶-↑✶ _ k σs₂) ⟩
          (a · b) /✶₂ σs₂ ↑✶₂ k
        ∎

    -- By instantiating TermLemmas, we get access to a number of useful
    -- (derived) lemmas about path substitutions.
    termLemmas : TermLemmas Term
    termLemmas = record
      { termSubst = termSubst
      ; app-var   = refl
      ; /✶-↑✶     = Lemmas./✶-↑✶
      }

    open TermLemmas termLemmas public hiding (var; termSubst)

    -- By instantiating TermLikeLemmas, we get access to a number of
    -- useful (derived) lemmas about variable substitutions (renamings).
    private
      termLikeLemmas : TermLikeLemmas Term Term
      termLikeLemmas = record
        { app        = SubstApp._/_
        ; termLemmas = termLemmas
        ; /✶-↑✶₁     = Lemmas./✶-↑✶
        ; /✶-↑✶₂     = Lemmas./✶-↑✶
        }

    open TermLikeLemmas termLikeLemmas public
      using (varLiftAppLemmas; varLiftSubLemmas; _/Var_)

    infix 10 _[_]

    -- Shorthand for single-variable term substitutions.
    _[_] : ∀ {n} → Term (1 + n) → Term n → Term n
    a [ b ] = a / sub b

  ----------------------------------------------------------------------
  -- Concrete typing contexts over terms.

  module Ctx where
    open Substitution using (weaken)
    open Context      Term public hiding (module Ctx)

    weakenOps : WeakenOps
    weakenOps = record { weaken = weaken }

    open WeakenOps weakenOps public hiding (weaken)


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

open import LogicalFormulae
open import Semirings.Definition

module Semirings.Solver {a : _} {A : Set a} {Zero One : A} {_+_ : A → A → A} {_*_ : A → A → A} (S : Semiring Zero One _+_ _*_) (comm : (a b : A) → a * b ≡ b * a) where

open Semiring S

-- You can see how to use the solver in LectureNotes/NumbersAndSets/Lecture1.agda.

data Expr : Set a where
  const : A → Expr
  zero : Expr
  succ : Expr → Expr
  plus : Expr → Expr → Expr
  times : Expr → Expr → Expr

-- TODO: do this in distinct phases. (Work out what those phases are.)
-- Expose those phases to the user so that they can choose which tactics to use.
-- Phases:
--  * In any expression which is all sums, extract all consts out to the front
--  * In any expression which is all sums, extract all the succs to the front

eval : Expr → A
eval (const k) = k
eval zero = Zero
eval (succ e) = One + (eval e)
eval (plus x y) = (eval x) + (eval y)
eval (times x y) = (eval x) * (eval y)

simplePlus : Expr → Expr → Expr
simplePlus (const x) (const y) = plus (const x) (const y)
simplePlus (const x) zero = const x
simplePlus (const x) (succ b) = succ (plus (const x) b)
simplePlus (const x) (plus a b) = plus (const x) (plus a b)
simplePlus (const x) (times a b) = plus (const x) (times a b)
simplePlus zero b = b
simplePlus (succ a) (const x) = succ (plus (const x) a)
simplePlus (succ a) zero = succ a
simplePlus (succ a) (succ b) = succ (succ (plus a b))
simplePlus (succ a) (plus b c) = succ (plus a (plus b c))
simplePlus (succ a) (times b c) = succ (plus a (times b c))
simplePlus (plus a b) (const x) = plus (const x) (plus a b)
simplePlus (plus a b) zero = plus a b
simplePlus (plus a b) (succ c) = succ (plus a (plus b c))
simplePlus (plus a b) (plus c d) = plus a (plus b (plus c d))
simplePlus (plus a b) (times c d) = plus a (plus b (times c d))
simplePlus (times a b) (const x) = plus (const x) (times a b)
simplePlus (times a b) zero = times a b
simplePlus (times a b) (succ c) = succ (plus (times a b) c)
simplePlus (times a b) (plus c d) = plus (times a b) (plus c d)
simplePlus (times a b) (times c d) = plus (times a b) (times c d)

private
  lemma : (a b c : A) → a + (b + c) ≡ b + (a + c)
  lemma a b c rewrite commutative a (b + c) | equalityCommutative (+Associative b c a) = applyEquality (b +_) (commutative _ _)

simplePlusIsPlus : (a b : Expr) → eval (simplePlus a b) ≡ eval (plus a b)
simplePlusIsPlus (const x) (const x₁) = refl
simplePlusIsPlus (const x) zero rewrite Semiring.sumZeroRight S x = refl
simplePlusIsPlus (const x) (succ b) = lemma One x (eval b)
simplePlusIsPlus (const x) (plus b c) = refl
simplePlusIsPlus (const x) (times b c) = refl
simplePlusIsPlus zero b = equalityCommutative (sumZeroLeft (eval b))
simplePlusIsPlus (succ a) (const x) rewrite lemma One x (eval a) = commutative x _
simplePlusIsPlus (succ a) zero = equalityCommutative (sumZeroRight _)
simplePlusIsPlus (succ a) (succ b) rewrite lemma (One + eval a) One (eval b) = applyEquality (One +_) (+Associative One _ _)
simplePlusIsPlus (succ a) (plus b c) = +Associative _ _ _
simplePlusIsPlus (succ a) (times b c) = +Associative _ _ _
simplePlusIsPlus (plus a b) (const x) = commutative x _
simplePlusIsPlus (plus a b) zero = equalityCommutative (sumZeroRight _)
simplePlusIsPlus (plus a b) (succ c) rewrite lemma (eval a + eval b) One (eval c) = applyEquality (One +_) (+Associative _ _ _)
simplePlusIsPlus (plus a b) (plus c d) = +Associative (eval a) _ _
simplePlusIsPlus (plus a b) (times c d) = +Associative _ _ _
simplePlusIsPlus (times a b) (const x) = commutative x _
simplePlusIsPlus (times a b) zero = equalityCommutative (sumZeroRight _)
simplePlusIsPlus (times a b) (succ c) rewrite lemma One (eval a * eval b) (eval c) = refl
simplePlusIsPlus (times a b) (plus c d) = refl
simplePlusIsPlus (times a b) (times c d) = refl

simplify : Expr → Expr
simplify (const k) = const k
simplify zero = zero
simplify (succ e) = succ (simplify e)
simplify (plus (const k) (const x)) = simplePlus (const k) (const x)
simplify (plus (const k) zero) = simplify (const k)
simplify (plus (const k) (succ expr)) = succ (simplify (plus (const k) expr))
simplify (plus (const k) (plus x y)) = simplePlus (const k) (simplify (plus x y))
simplify (plus (const k) (times x y)) = simplePlus (const k) (simplify (times x y))
simplify (plus zero expr2) = simplify expr2
simplify (plus (succ expr1) expr2) = succ (simplify (plus expr1 expr2))
simplify (plus (plus expr1 expr3) zero) = simplify (plus expr1 expr3)
simplify (plus (plus expr1 expr3) (const k)) = simplePlus (const k) (simplify (plus expr1 expr3))
simplify (plus (plus expr1 expr3) (succ expr2)) = succ (simplePlus (simplify (plus expr1 expr3)) (simplify expr2))
simplify (plus (plus expr1 expr3) (plus expr2 expr4)) = simplePlus (simplify (plus expr1 expr3)) (simplify (plus expr2 expr4))
simplify (plus (plus expr1 expr3) (times expr2 expr4)) = simplePlus (simplify (plus expr1 expr3)) (simplify (times expr2 expr4))
simplify (plus (times expr1 expr3) (const k)) = simplePlus (const k) (simplify (times expr1 expr3))
simplify (plus (times expr1 expr3) zero) = times (simplify expr1) (simplify expr3)
simplify (plus (times expr1 expr3) (succ expr2)) = succ (simplePlus (simplify (times expr1 expr3)) (simplify expr2))
simplify (plus (times expr1 expr3) (plus expr2 expr4)) = simplePlus (simplify (times expr1 expr3)) (simplify (plus expr2 expr4))
simplify (plus (times expr1 expr3) (times expr2 expr4)) = simplePlus (simplify (times expr1 expr3)) (simplify (times expr2 expr4))
simplify (times (const k) (const x)) = times (const k) (const x)
simplify (times (const k) zero) = zero
simplify (times (const k) (succ zero)) = const k
simplify (times (const k) (succ (const y))) = simplePlus (const k) (times (const k) (const y))
simplify (times (const k) (succ (plus a b))) = simplePlus (const k) (simplify (times (const k) (plus a b)))
simplify (times (const k) (succ (times a b))) = simplePlus (const k) (simplify (times (const k) (times a b)))
simplify (times (const k) (succ (succ x))) = simplePlus (const k) (simplify (times (const k) (succ x)))
simplify (times (const k) (plus expr2 expr3)) = simplePlus (simplify (times (const k) (expr2))) (simplify (times (const k) expr3))
simplify (times (const k) (times expr2 expr3)) = times (const k) (simplify (times expr2 expr3))
simplify (times zero expr2) = zero
simplify (times (succ zero) expr2) = simplify expr2
simplify (times (succ (succ expr1)) expr2) = simplePlus (simplify expr2) (simplify (times (succ expr1) expr2))
simplify (times (succ (plus expr1 expr3)) (const k)) = times (const k) (simplify (succ (plus expr1 expr3)))
simplify (times (succ (plus expr1 expr3)) zero) = zero
simplify (times (succ (plus expr1 expr3)) (succ expr2)) = succ (simplePlus (simplify (times expr1 expr2)) (simplePlus (simplify (times expr3 expr2)) (simplePlus expr2 (simplify (plus expr1 expr3)))))
simplify (times (succ (plus expr1 expr3)) (plus expr2 expr4)) = times (simplify (succ (plus expr1 expr3))) (simplify (plus expr2 expr4))
simplify (times (succ (plus expr1 expr3)) (times expr2 expr4)) = times (simplify (succ (plus expr1 expr3))) (simplify (times expr2 expr4))
simplify (times (succ (const k)) (const x)) = simplePlus (const x) (times (const k) (const x))
simplify (times (succ (const k)) zero) = zero
simplify (times (succ (const k)) (succ (const x))) = succ (simplePlus (const k) (simplePlus (const x) (simplify (times (const k) (const x)))))
simplify (times (succ (const k)) (succ zero)) = succ (const k)
simplify (times (succ (const k)) (succ (succ expr2))) = succ (simplePlus (const k) (simplePlus (simplify (succ expr2)) (simplify (times (const k) (succ (expr2))))))
simplify (times (succ (const k)) (succ (plus expr2 expr3))) = succ (simplePlus (const k) (simplePlus (simplify (plus expr2 expr3)) (simplify (times (const k) (plus expr2 expr3)))))
simplify (times (succ (const k)) (succ (times expr2 expr3))) = succ (simplePlus (const k) (simplePlus (simplify (times expr2 expr3)) (simplify (times (const k) (times expr2 expr3)))))
simplify (times (succ (const k)) (plus expr2 expr3)) = simplePlus (simplify (plus expr2 expr3)) (simplify (times (const k) (plus expr2 expr3)))
simplify (times (succ (const k)) (times expr2 expr3)) = simplePlus (simplify (times expr2 expr3)) (simplify (times (const k) (times expr2 expr3)))
simplify (times (succ (times expr1 expr3)) (const x)) = times (const x) (simplify (succ (times expr1 expr3)))
simplify (times (succ (times expr1 expr3)) zero) = zero
simplify (times (succ (times expr1 expr3)) (succ expr2)) = succ (simplePlus (simplify expr2) (plus (simplify (times expr1 expr3)) (simplify (times (times expr1 expr3) expr2))))
simplify (times (succ (times expr1 expr3)) (plus expr2 expr4)) = simplePlus (simplify (plus expr2 expr4)) (simplify (times (times expr1 expr3) (plus expr2 expr4)))
simplify (times (succ (times expr1 expr3)) (times expr2 expr4)) = simplePlus (simplify (times expr2 expr4)) (simplify (times (times expr1 expr3) (times expr2 expr4)))
simplify (times (plus expr1 expr3) zero) = zero
simplify (times (plus expr1 expr3) (succ expr2)) = simplePlus (simplify (plus expr1 expr3)) (simplify (times (plus expr1 expr3) expr2))
simplify (times (plus expr1 expr3) (const x)) = times (const x) (simplify (plus expr1 expr3))
simplify (times (plus expr1 expr3) (plus expr2 expr4)) = simplePlus (simplify (times expr1 expr2)) (simplePlus (simplify (times expr3 expr2)) (simplePlus (simplify (times expr1 expr4)) (simplify (times expr3 expr4))))
simplify (times (plus expr1 expr3) (times expr2 expr4)) = simplePlus (simplify (times expr1 (times expr2 expr4))) (simplify (times expr3 (times expr2 expr4)))
simplify (times (times expr1 expr3) zero) = zero
simplify (times (times expr1 expr3) (succ expr2)) = simplePlus (simplify (times expr1 expr3)) (simplify (times (times expr1 expr3) expr2))
simplify (times (times expr1 expr3) (plus expr2 expr4)) = simplePlus (simplify (times (times expr1 expr3) expr2)) (simplify (times (times expr1 expr3) expr4))
simplify (times (times expr1 expr3) (times expr2 expr4)) = times (simplify expr1) (simplify (times expr3 (times expr2 expr4)))
simplify (times (times expr1 expr3) (const x)) = times (simplify (times expr1 expr3)) (const x)

equalPlus : {a b c d : A} → (a ≡ c) → (b ≡ d) → (a + b) ≡ (c + d)
equalPlus refl refl = refl

equalTimes : {a b c d : A} → (a ≡ c) → (b ≡ d) → (a * b) ≡ (c * d)
equalTimes refl refl = refl

otherDist : {a b c : A} → (a * b) + (c * b) ≡ (a + c) * b
otherDist = transitivity (equalPlus (comm _ _) (comm _ _)) (transitivity (equalityCommutative (+DistributesOver* _ _ _)) (comm _ _))

simplifyPreserves : (a : Expr) → eval (simplify a) ≡ eval a
simplifyPreserves (const x) = refl
simplifyPreserves zero = refl
simplifyPreserves (succ a) = applyEquality (One +_) (simplifyPreserves a)
simplifyPreserves (plus (const x) (const y)) = refl
simplifyPreserves (plus (const x) zero) = equalityCommutative (sumZeroRight x)
simplifyPreserves (plus (const x) (succ b)) = transitivity (transitivity (applyEquality (One +_) (transitivity (simplifyPreserves (plus (const x) b)) (commutative x (eval b)))) (+Associative One (eval b) x)) (commutative (One + eval b) x)
simplifyPreserves (plus (const x) (plus b c)) = transitivity (simplePlusIsPlus (const x) (simplify (plus b c))) (applyEquality (x +_) (simplifyPreserves (plus b c)))
simplifyPreserves (plus (const x) (times b c)) = transitivity (simplePlusIsPlus (const x) (simplify (times b c))) (applyEquality (x +_) (simplifyPreserves (times b c)))
simplifyPreserves (plus zero a) = transitivity (simplifyPreserves a) (equalityCommutative (sumZeroLeft (eval a)))
simplifyPreserves (plus (succ a) b) = transitivity (applyEquality (One +_) (simplifyPreserves (plus a b))) (+Associative One (eval a) (eval b))
simplifyPreserves (plus (plus a b) (const x)) = transitivity (simplePlusIsPlus (const x) (simplify (plus a b))) (transitivity (commutative x (eval (simplify (plus a b)))) (applyEquality (_+ x) (simplifyPreserves (plus a b))))
simplifyPreserves (plus (plus a b) zero) = transitivity (simplifyPreserves (plus a b)) (equalityCommutative (sumZeroRight _))
simplifyPreserves (plus (plus a b) (succ c)) = transitivity (applyEquality (One +_) (simplePlusIsPlus (simplify (plus a b)) (simplify c))) (transitivity (+Associative One _ _) (transitivity (applyEquality (_+ eval (simplify c)) (commutative One _)) (transitivity (equalityCommutative (+Associative _ _ _)) (transitivity (applyEquality (_+ (One + eval (simplify c))) (simplifyPreserves (plus a b))) (applyEquality (λ i → (eval a + eval b) + (One + i)) (simplifyPreserves c))))))
simplifyPreserves (plus (plus a b) (plus c d)) = transitivity (simplePlusIsPlus (simplify (plus a b)) (simplify (plus c d))) (transitivity (applyEquality (_+ eval (simplify (plus c d))) (simplifyPreserves (plus a b))) (applyEquality ((eval a + eval b) +_) (simplifyPreserves (plus c d))))
simplifyPreserves (plus (plus a b) (times c d)) = transitivity (simplePlusIsPlus (simplify (plus a b)) (simplify (times c d))) (transitivity (applyEquality (_+ eval (simplify (times c d))) (simplifyPreserves (plus a b))) (applyEquality ((eval a + eval b) +_) (simplifyPreserves (times c d))))
simplifyPreserves (plus (times a b) (const x)) = transitivity (simplePlusIsPlus (const x) (simplify (times a b))) (transitivity (commutative x _) (applyEquality (_+ x) (simplifyPreserves (times a b))))
simplifyPreserves (plus (times a b) zero) = transitivity (transitivity (applyEquality (_* eval (simplify b)) (simplifyPreserves a)) (applyEquality ((eval a) *_) (simplifyPreserves b))) (equalityCommutative (sumZeroRight _))
simplifyPreserves (plus (times a b) (succ c)) = transitivity (transitivity (applyEquality (One +_) (transitivity (simplePlusIsPlus (simplify (times a b)) (simplify c)) (transitivity (transitivity (applyEquality (eval (simplify (times a b)) +_) (simplifyPreserves c)) (applyEquality (_+ eval c) (simplifyPreserves (times a b)))) (commutative _ (eval c))))) (+Associative One (eval c) _)) (commutative _ (eval a * eval b))
simplifyPreserves (plus (times a b) (plus c d)) = transitivity (simplePlusIsPlus (simplify (times a b)) (simplify (plus c d))) (transitivity (applyEquality (_+ eval (simplify (plus c d))) (simplifyPreserves (times a b))) (applyEquality ((eval a * eval b) +_) (simplifyPreserves (plus c d))))
simplifyPreserves (plus (times a b) (times c d)) = transitivity (simplePlusIsPlus (simplify (times a b)) (simplify (times c d))) (transitivity (applyEquality (_+ eval (simplify (times c d))) (simplifyPreserves (times a b))) (applyEquality ((eval a * eval b) +_) (simplifyPreserves (times c d))))
simplifyPreserves (times (const x) (const y)) = refl
simplifyPreserves (times (const x) zero) = equalityCommutative (productZeroRight x)
simplifyPreserves (times (const x) (succ (const y))) = transitivity (applyEquality (_+ (x * y)) (equalityCommutative (productOneRight _))) (equalityCommutative (+DistributesOver* x One y))
simplifyPreserves (times (const x) (succ zero)) = equalityCommutative (transitivity (applyEquality (x *_) (sumZeroRight One)) (productOneRight x))
simplifyPreserves (times (const x) (succ (succ b))) = transitivity (simplePlusIsPlus (const x) (simplify (times (const x) (succ b)))) (transitivity (equalPlus (equalityCommutative (productOneRight _)) (simplifyPreserves (times (const x) (succ b)))) (equalityCommutative (+DistributesOver* x One _)))
simplifyPreserves (times (const x) (succ (plus b c))) = transitivity (simplePlusIsPlus (const x) (simplePlus (simplify (times (const x) b)) (simplify (times (const x) c)))) (transitivity (equalPlus (equalityCommutative (productOneRight _)) (transitivity (simplePlusIsPlus (simplify (times (const x) b)) (simplify (times (const x) c))) (transitivity (equalPlus (simplifyPreserves (times (const x) b)) (simplifyPreserves (times (const x) c))) (equalityCommutative (+DistributesOver* x (eval b) (eval c)))))) (equalityCommutative (+DistributesOver* x One _)))
simplifyPreserves (times (const x) (succ (times b c))) = transitivity (equalPlus (equalityCommutative (productOneRight x)) (applyEquality (x *_) (simplifyPreserves (times b c)))) (equalityCommutative (+DistributesOver* x One _))
simplifyPreserves (times (const x) (plus b c)) = transitivity (simplePlusIsPlus (simplify (times (const x) b)) (simplify (times (const x) c))) (transitivity (transitivity (applyEquality (eval (simplify (times (const x) b)) +_) (simplifyPreserves (times (const x) c))) (applyEquality (_+ (x * eval c)) (simplifyPreserves (times (const x) b)))) (equalityCommutative (+DistributesOver* x (eval b) (eval c))))
simplifyPreserves (times (const x) (times b c)) = applyEquality (x *_) (simplifyPreserves (times b c))
simplifyPreserves (times zero b) = equalityCommutative (productZeroLeft (eval b))
simplifyPreserves (times (succ (const x)) (const y)) = transitivity (transitivity (equalPlus (equalityCommutative (productOneRight y)) (comm x y)) (equalityCommutative (+DistributesOver* y One x))) (comm y _)
simplifyPreserves (times (succ (const x)) zero) = equalityCommutative (productZeroRight _)
simplifyPreserves (times (succ (const x)) (succ (const y))) = transitivity (transitivity (transitivity (+Associative One x _) (applyEquality ((One + x) +_) (transitivity (equalPlus (equalityCommutative (productOneRight y)) (comm x y)) (equalityCommutative (+DistributesOver* y One x))))) (equalPlus (equalityCommutative (productOneRight (One + x))) (comm _ _))) (equalityCommutative (+DistributesOver* _ _ _))
simplifyPreserves (times (succ (const x)) (succ zero)) = equalityCommutative (transitivity (applyEquality ((One + x) *_) (sumZeroRight One)) (productOneRight _))
simplifyPreserves (times (succ (const x)) (succ (succ b))) = transitivity (applyEquality (One +_) (simplePlusIsPlus (const x) (simplePlus (succ (simplify b)) (simplify (times (const x) (succ b)))))) (transitivity (applyEquality (λ i → One + (x + i)) (simplePlusIsPlus (succ (simplify b)) (simplify (times (const x) (succ b))))) (transitivity (applyEquality (λ i → One + (x + ((One + i) + eval (simplify (times (const x) (succ b)))))) (simplifyPreserves b)) (transitivity (applyEquality (λ i → One + (x + ((One + eval b) + i))) (simplifyPreserves (times (const x) (succ b)))) (transitivity (transitivity (+Associative One x _) (equalPlus (equalityCommutative (productOneRight _)) (transitivity (transitivity (transitivity (applyEquality ((One + eval b) +_) (+DistributesOver* x One (eval b))) (transitivity (transitivity (equalityCommutative (+Associative One (eval b) _)) (transitivity (applyEquality (One +_) (transitivity (+Associative (eval b) _ _) (transitivity (equalPlus (transitivity (commutative _ _) (equalPlus (productOneRight x) (equalityCommutative (productOneRight _)))) (comm x _)) (equalityCommutative (+Associative x _ _))))) (+Associative One x _))) (applyEquality ((One + x) +_) (equalityCommutative (+DistributesOver* (eval b) One x))))) (equalPlus (equalityCommutative (productOneRight _)) (comm (eval b) _))) (equalityCommutative (+DistributesOver* (One + x) One _))))) (equalityCommutative (+DistributesOver* (One + x) One _))))))
simplifyPreserves (times (succ (const x)) (succ (plus b c))) = transitivity (transitivity (transitivity (applyEquality (One +_) (simplePlusIsPlus (const x) (simplePlus (simplify (plus b c)) (simplePlus (simplify (times (const x) b)) (simplify (times (const x) c)))))) (transitivity (+Associative One x _) (applyEquality ((One + x) +_) (transitivity (simplePlusIsPlus (simplify (plus b c)) (simplePlus (simplify (times (const x) b)) (simplify (times (const x) c)))) (transitivity (equalPlus (simplifyPreserves (plus b c)) (simplePlusIsPlus (simplify (times (const x) b)) (simplify (times (const x) c)))) (transitivity (transitivity (transitivity (transitivity (equalityCommutative (+Associative _ _ _ )) (transitivity (applyEquality (eval b +_) (transitivity (applyEquality (eval c +_) (equalPlus (simplifyPreserves (times (const x) b)) (simplifyPreserves (times (const x) c)))) (transitivity (+Associative (eval c) _ _) (transitivity (applyEquality (_+ (x * eval c)) (commutative _ _)) (equalityCommutative (+Associative _ _ _)))))) (+Associative _ _ _))) (equalPlus (equalPlus (equalityCommutative (productOneRight (eval b))) (comm _ _)) (equalPlus (equalityCommutative (productOneRight (eval c))) (comm x _)))) (equalPlus (equalityCommutative (+DistributesOver* (eval b) One x)) (equalityCommutative (+DistributesOver* (eval c) One x)))) (equalPlus (comm _ _) (comm _ _)))))))) (equalPlus (equalityCommutative (productOneRight _)) (equalityCommutative (+DistributesOver* (One + x) _ _)))) (equalityCommutative (+DistributesOver* (One + x) _ _))
simplifyPreserves (times (succ (const x)) (succ (times b c))) = transitivity (transitivity (applyEquality (One +_) (simplePlusIsPlus (const x) (simplePlus (simplify (times b c)) (times (const x) (simplify (times b c)))))) (transitivity (+Associative One x _) (equalPlus (equalityCommutative (productOneRight _)) (transitivity (simplePlusIsPlus (simplify (times b c)) (times (const x) (simplify (times b c)))) (transitivity (transitivity (equalPlus (transitivity (simplifyPreserves (times b c)) (equalityCommutative (productOneRight _))) (transitivity (comm _ _) (applyEquality (_* x) (simplifyPreserves (times b c))))) (equalityCommutative (+DistributesOver* _ One x))) (comm _ _)))))) (equalityCommutative (+DistributesOver* _ _ _))
simplifyPreserves (times (succ (const x)) (plus b c)) = transitivity (simplePlusIsPlus (simplify (plus b c)) (simplePlus (simplify (times (const x) b)) (simplify (times (const x) c)))) (transitivity (transitivity (equalPlus (transitivity (simplifyPreserves (plus b c)) (equalityCommutative (productOneRight _))) (transitivity (simplePlusIsPlus (simplify (times (const x) b)) (simplify (times (const x) c))) (transitivity (transitivity (equalPlus (simplifyPreserves (times (const x) b)) (simplifyPreserves (times (const x) c))) (equalityCommutative (+DistributesOver* x _ _))) (comm x _)))) (equalityCommutative (+DistributesOver* _ One x))) (comm _ _))
simplifyPreserves (times (succ (const x)) (times b c)) = transitivity (simplePlusIsPlus (simplify (times b c)) (times (const x) (simplify (times b c)))) (transitivity (transitivity (equalPlus (transitivity (simplifyPreserves (times b c)) (equalityCommutative (productOneRight _))) (transitivity (comm x _) (applyEquality (_* x) (simplifyPreserves (times b c))))) (equalityCommutative (+DistributesOver* _ One x))) (comm _ _))
simplifyPreserves (times (succ zero) b) = transitivity (simplifyPreserves b) (transitivity (equalityCommutative (productOneLeft (eval b))) (applyEquality (_* eval b) (equalityCommutative (sumZeroRight One))))
simplifyPreserves (times (succ (succ a)) b) = transitivity (simplePlusIsPlus (simplify b) (simplify (times (succ a) b))) (transitivity (equalPlus (simplifyPreserves b) (simplifyPreserves (times (succ a) b))) (transitivity (transitivity (equalPlus (equalityCommutative (productOneRight _)) (comm _ _)) (equalityCommutative (+DistributesOver* (eval b) One _))) (comm (eval b) _)))
simplifyPreserves (times (succ (plus a b)) (const x)) = transitivity (comm x _) (applyEquality (λ i → (One + i) * x) (simplifyPreserves (plus a b)))
simplifyPreserves (times (succ (plus a b)) zero) = equalityCommutative (productZeroRight _)
simplifyPreserves (times (succ (plus a b)) (succ c)) = transitivity (transitivity (transitivity (applyEquality (One +_) (transitivity (simplePlusIsPlus (simplify (times a c)) (simplePlus (simplify (times b c)) (simplePlus c (simplify (plus a b))))) (transitivity (transitivity (equalPlus (simplifyPreserves (times a c)) (simplePlusIsPlus (simplify (times b c)) (simplePlus c (simplify (plus a b))))) (transitivity (+Associative _ _ _) (transitivity (transitivity (equalPlus (transitivity (equalPlus (comm _ _) (transitivity (simplifyPreserves (times b c)) (comm _ _))) (equalityCommutative (+DistributesOver* _ _ _))) (transitivity (simplePlusIsPlus c (simplify (plus a b))) (transitivity (commutative _ _) (equalPlus (simplifyPreserves (plus a b)) (equalityCommutative (productOneRight _)))))) (commutative _ _)) (equalityCommutative (+Associative _ _ _))))) (applyEquality ((eval a + eval b) +_) (equalityCommutative (+DistributesOver* _ One _)))))) (+Associative One _ _)) (equalPlus (equalityCommutative (productOneRight _)) (comm _ _))) (equalityCommutative (+DistributesOver* _ _ _))
simplifyPreserves (times (succ (plus a b)) (plus c d)) = equalTimes (applyEquality (One +_) (simplifyPreserves (plus a b))) (simplifyPreserves (plus c d))
simplifyPreserves (times (succ (plus a b)) (times c d)) = equalTimes (applyEquality (One +_) (simplifyPreserves (plus a b))) (simplifyPreserves (times c d))
simplifyPreserves (times (succ (times a b)) (const x)) = transitivity (comm x _) (applyEquality (λ i → (One + i) * x) (simplifyPreserves (times a b)))
simplifyPreserves (times (succ (times a b)) zero) = equalityCommutative (productZeroRight _)
simplifyPreserves (times (succ (times a b)) (succ c)) = transitivity (transitivity (transitivity (applyEquality (One +_) (transitivity (simplePlusIsPlus (simplify c) (plus (simplify (times a b)) (simplify (times (times a b) c)))) (transitivity (transitivity (transitivity (equalPlus (transitivity (simplifyPreserves c) (equalityCommutative (productOneRight _))) (transitivity (commutative _ _) (equalPlus (transitivity (simplifyPreserves (times (times a b) c)) (comm _ _)) (simplifyPreserves (times a b))))) (+Associative _ _ _)) (applyEquality (_+ (eval a * eval b)) (equalityCommutative (+DistributesOver* _ One _)))) (commutative _ _)))) (+Associative One _ _)) (equalPlus (equalityCommutative (productOneRight _)) (comm _ _))) (equalityCommutative (+DistributesOver* _ One _))
simplifyPreserves (times (succ (times a b)) (plus c d)) = transitivity (simplePlusIsPlus (simplify (plus c d)) (simplePlus (simplify (times (times a b) c)) (simplify (times (times a b) d)))) (transitivity (equalPlus (simplifyPreserves (plus c d)) (simplePlusIsPlus (simplify (times (times a b) c)) (simplify (times (times a b) d)))) (transitivity (transitivity (equalPlus (equalityCommutative (productOneRight _)) (transitivity (transitivity (equalPlus (simplifyPreserves (times (times a b) c)) (simplifyPreserves (times (times a b) d))) (equalityCommutative (+DistributesOver* _ _ _))) (comm _ _))) (equalityCommutative (+DistributesOver* _ One _))) (comm _ _)))
simplifyPreserves (times (succ (times a b)) (times c d)) = transitivity (simplePlusIsPlus (simplify (times c d)) (times (simplify a) (simplify (times b (times c d))))) (transitivity (equalPlus (simplifyPreserves (times c d)) (equalTimes (simplifyPreserves a) (simplifyPreserves (times b (times c d))))) (transitivity (transitivity (equalPlus (equalityCommutative (productOneRight _)) (transitivity (*Associative (eval a) _ _) (comm _ _))) (equalityCommutative (+DistributesOver* _ One _))) (comm _ _)))
simplifyPreserves (times (plus a b) (const x)) = transitivity (comm x _) (applyEquality (_* x) (simplifyPreserves (plus a b)))
simplifyPreserves (times (plus a b) zero) = equalityCommutative (productZeroRight _)
simplifyPreserves (times (plus a b) (succ c)) = transitivity (simplePlusIsPlus (simplify (plus a b)) (simplify (times (plus a b) c))) (transitivity (applyEquality ((eval (simplify (plus a b))) +_) (simplifyPreserves (times (plus a b) c))) (transitivity (applyEquality (_+ ((eval a + eval b) * eval c)) (simplifyPreserves (plus a b))) (transitivity (equalPlus (equalityCommutative (productOneRight _)) refl) (equalityCommutative (+DistributesOver* _ One (eval c))))))
simplifyPreserves (times (plus a b) (plus c d)) = transitivity (simplePlusIsPlus (simplify (times a c)) (simplePlus (simplify (times b c)) (simplePlus (simplify (times a d)) (simplify (times b d))))) (transitivity (transitivity (transitivity (transitivity (equalPlus (transitivity (simplifyPreserves (times a c)) (comm _ _)) (transitivity (simplePlusIsPlus (simplify (times b c)) (simplePlus (simplify (times a d)) (simplify (times b d)))) (equalPlus (transitivity (simplifyPreserves (times b c)) (comm _ _)) (transitivity (simplePlusIsPlus (simplify (times a d)) (simplify (times b d))) (equalPlus (transitivity (simplifyPreserves (times a d)) (comm _ _)) (transitivity (simplifyPreserves (times b d)) (comm _ _))))))) (+Associative _ _ _)) (equalPlus (equalityCommutative (+DistributesOver* (eval c) (eval a) _)) (equalityCommutative (+DistributesOver* (eval d) (eval a) _)))) (equalPlus (comm (eval c) _) (comm (eval d) _))) (equalityCommutative (+DistributesOver* _ (eval c) (eval d))))
simplifyPreserves (times (plus a b) (times c d)) = transitivity (simplePlusIsPlus (simplify (times a (times c d))) (simplify (times b (times c d)))) (transitivity (equalPlus (simplifyPreserves (times a (times c d))) (simplifyPreserves (times b (times c d)))) (transitivity (equalPlus (comm (eval a) _) (comm (eval b) _)) (transitivity (equalityCommutative (+DistributesOver* _ (eval a) (eval b))) (comm _ _))))
simplifyPreserves (times (times a b) (const x)) = applyEquality (_* x) (simplifyPreserves (times a b))
simplifyPreserves (times (times a b) zero) = equalityCommutative (productZeroRight _)
simplifyPreserves (times (times a b) (succ c)) = transitivity (simplePlusIsPlus (simplify (times a b)) (simplify (times (times a b) c))) (transitivity (equalPlus (simplifyPreserves (times a b)) (simplifyPreserves (times (times a b) c))) (transitivity (applyEquality (_+ ((eval a * eval b) * eval c)) (equalityCommutative (productOneRight _))) (equalityCommutative (+DistributesOver* _ One (eval c)))))
simplifyPreserves (times (times a b) (plus c d)) = transitivity (simplePlusIsPlus (simplify (times (times a b) c)) (simplify (times (times a b) d))) (transitivity (equalPlus (simplifyPreserves (times (times a b) c)) (simplifyPreserves (times (times a b) d))) (equalityCommutative (+DistributesOver* _ _ _)))
simplifyPreserves (times (times a b) (times c d)) = transitivity (equalTimes (simplifyPreserves a) (simplifyPreserves (times b (times c d)))) (*Associative (eval a) (eval b) _)

from_to_by_ : (x y : Expr) (pr : eval (simplify x) ≡ eval (simplify y)) → eval x ≡ eval y
from a to b by pr = transitivity (equalityCommutative (simplifyPreserves a)) (transitivity pr (simplifyPreserves b))


------------------------------------------------------------------------------
-- The Booleans
------------------------------------------------------------------------------

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

module FOTC.Data.Bool where

open import FOTC.Base
open import FOTC.Data.Bool.Type public

infixr 6 _&&_

------------------------------------------------------------------------------
-- Basic functions

-- The conjunction.
_&&_  : D → D → D
a && b = if a then b else false
{-# ATP definition _&&_ #-}

-- The negation.
not : D → D
not b = if b then false else true
{-# ATP definition not #-}


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

module FunExt where

open import Base

module _ {i j} {A : Type i} {B : A → Type j} where

  happly : (f g : (x : A) → B x) → (f == g) → ((x : A) → f x == g x)
  happly f g = ind== D d where
    D : (f g : (x : A) → B x) → f == g → Type _
    D f g _ = (x : A) → f x == g x

    d : (f : (x : A) → B x) → D f f idp
    d f = λ x → idp

  postulate
    fun-ext : (f g : (x : A) → B x) → is-equiv (happly f g)


module Everything where

-- basic utilities
open import Library
open import Isomorphism

-- basic category theory
open import Categories
open import Categories.Sets
open import Categories.Families
open import Categories.Initial
open import Categories.Terminal
open import Categories.CoProducts
open import Categories.PushOuts
open import Categories.Setoids -- should be replaced by standard libary def

open import Functors
open import Functors.Fin
open import Functors.FullyFaithful

open import Naturals

-- basic examples
open import Monoids
open import FunctorCat

-- ordinary monads
open import Monads
open import Monads.MonadMorphs
open import Adjunctions
open import Adjunctions.Adj2Mon
open import Monads.Kleisli
open import Monads.Kleisli.Functors
open import Monads.Kleisli.Adjunction
open import Monads.EM
open import Monads.EM.Functors
open import Monads.EM.Adjunction
open import Monads.CatofAdj
open import Monads.CatofAdj.InitAdj
open import Monads.CatofAdj.TermAdjObj
open import Monads.CatofAdj.TermAdjHom
open import Monads.CatofAdj.TermAdjUniq
open import Monads.CatofAdj.TermAdj

-- relative monads
open import RMonads
open import RMonads.RMonadMorphs
open import RAdjunctions
open import RAdjunctions.RAdj2RMon
open import RMonads.REM
open import RMonads.REM.Functors
open import RMonads.REM.Adjunction
open import RMonads.RKleisli
open import RMonads.RKleisli.Functors
open import RMonads.RKleisli.Adjunction
open import RMonads.Restriction
open import RMonads.SpecialCase
open import RMonads.CatofRAdj
open import RMonads.CatofRAdj.InitRAdj
open import RMonads.CatofRAdj.TermRAdjObj
open import RMonads.CatofRAdj.TermRAdjHom
open import RMonads.CatofRAdj.TermRAdj
open import RMonads.Modules

-- rmonad examples
open import WellScopedTerms
open import WellScopedTermsModel
open import WellTypedTerms
open import WellTypedTermsModel

open import Lawvere


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

open import lib.Basics
open import lib.NType2
open import lib.PathGroupoid

open import lib.types.Bool
open import lib.types.IteratedSuspension
open import lib.types.Lift
open import lib.types.LoopSpace
open import lib.types.Nat
open import lib.types.Paths
open import lib.types.Pi
open import lib.types.Pointed
open import lib.types.Sigma
open import lib.types.Suspension
open import lib.types.TLevel
open import lib.types.Unit

open SuspensionRec public using () renaming (f to Susp-rec)

open import nicolai.pseudotruncations.Preliminary-definitions
open import nicolai.pseudotruncations.Liblemmas

module nicolai.pseudotruncations.pointed-O-Sphere where

{- helper file for some technical things -}

module _ {i} where

  bool = fst (⊙Sphere {i} O)
  tt₀ : bool
  tt₀ = snd (⊙Sphere {i} O)
  ff₀ : bool
  ff₀ = lift false

module bool-neutral
           {i j}
           (P : bool {i} → Type j)
           (p₀ : P tt₀) where

  bool-pair : (P tt₀ × P ff₀) ≃ ((b : bool) → P b)
  bool-pair = equiv (λ tf → (λ { (lift true) → fst tf ; (lift false) → snd tf }))
                    (λ f → (f tt₀ , f ff₀))
                    (λ f → λ= (λ { (lift true) → idp ; (lift false) → idp }))
                    (λ tf → idp)

  pair-red : Σ (P tt₀ × P ff₀) (λ tf → fst tf == p₀) → P ff₀
  pair-red ((pt , pf) , q) = pf

  sing-exp : P ff₀ → Σ (P tt₀ × P ff₀) (λ tf → fst tf == p₀)
  sing-exp pf = ((p₀ , pf) , idp)

  red-exp : (pf : P ff₀) → pair-red (sing-exp pf) == pf
  red-exp _ = idp

  exp-red : (x : Σ (P tt₀ × P ff₀) (λ tf → fst tf == p₀)) → sing-exp (pair-red x) == x
  exp-red ((.p₀ , pf) , idp) = idp
  {- I tried to do it with pattern matching, but this does not introduce
     a dot [.] before [p₀]. Thus, is fails. Is this a bug? -}

  pair-one-determined : Σ (P tt₀ × P ff₀) (λ tf → fst tf == p₀) ≃ P ff₀
  pair-one-determined = equiv pair-red sing-exp red-exp exp-red

  {- This reduction result is (on paper) trivial, but for our formalization
     quite powerful! -}
  reduction : (Σ ((b : bool) → P b) λ g → g tt₀ == p₀)   ≃   P ff₀
  reduction = (Σ ((b : bool) → P b) λ g → g tt₀ == p₀)
                 ≃⟨ equiv-Σ-fst {A = P tt₀ × P ff₀}
                                {B = (b : bool) → P b}
                                (λ g → g tt₀ == p₀)
                                {h = fst bool-pair}
                                (snd bool-pair) ⁻¹ ⟩
               Σ (P tt₀ × P ff₀) ((λ g → g tt₀ == p₀) ∘ fst bool-pair)
                 ≃⟨ ide _ ⟩
               Σ (P tt₀ × P ff₀) (λ tf → fst tf == p₀)
                 ≃⟨ pair-one-determined ⟩
               P ff₀
                 ≃∎


{-# OPTIONS --cubical --no-import-sorts --safe #-}

open import Cubical.Core.Everything
open import Cubical.Foundations.HLevels

module Cubical.Algebra.Magma.Construct.Left {ℓ} (Aˢ : hSet ℓ) where

open import Cubical.Foundations.Prelude
open import Cubical.Algebra.Magma

private
  A = ⟨ Aˢ ⟩
  isSetA = Aˢ .snd

_◂_ : Op₂ A
x ◂ y = x

------------------------------------------------------------------------
-- Properties

◂-zeroˡ : ∀ x → LeftZero x _◂_
◂-zeroˡ _ _ = refl

◂-identityʳ : ∀ x → RightIdentity x _◂_
◂-identityʳ _ _ = refl

------------------------------------------------------------------------
-- Magma definition

Left-isMagma : IsMagma A _◂_
Left-isMagma = record { is-set = isSetA }

LeftMagma : Magma ℓ
LeftMagma = record { isMagma = Left-isMagma }


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

open import Categories.Category using () renaming (Category to Setoid-Category)
open import Categories.Category.Monoidal

module Categories.Category.Construction.EnrichedFunctors
  {o ℓ e} {V : Setoid-Category o ℓ e} (M : Monoidal V) where

-- The (enriched) functor category for a given pair of V-enriched categories

open import Level
open import Data.Product using (_,_; uncurry′)

open import Categories.Enriched.Category M
open import Categories.Enriched.Functor M renaming (id to idF)
open import Categories.Enriched.NaturalTransformation M renaming (id to idNT)
open import Categories.Functor.Bifunctor using (Bifunctor)

open NaturalTransformation using (_[_])

EnrichedFunctors : ∀ {c d} (C : Category c) (D : Category d) →
                   Setoid-Category (ℓ ⊔ e ⊔ c ⊔ d) (ℓ ⊔ e ⊔ c) (e ⊔ c)
EnrichedFunctors C D = record
  { Obj = Functor C D
  ; _⇒_ = NaturalTransformation
  ; _≈_ = λ α β → ∀ {X} → α [ X ] ≈ β [ X ]
  ; id  = idNT
  ; _∘_ = _∘ᵥ_
  ; assoc     = assoc
  ; sym-assoc = sym-assoc
  ; identityˡ = identityˡ
  ; identityʳ = identityʳ
  ; identity² = identity²
  ; equiv     = record
    { refl  = Equiv.refl
    ; sym   = λ α≈β → Equiv.sym α≈β
    ; trans = λ α≈β β≈γ → Equiv.trans α≈β β≈γ
    }
  ; ∘-resp-≈ = λ α₁≈β₁ α₂≈β₂ → ∘-resp-≈ α₁≈β₁ α₂≈β₂
  }
  where open Underlying D

-- Horizontal composition of natural transformations (aka the Godement
-- product) induces a composition functor over functor categories.
--
-- Note that all the equational reasoning happens in the underlying
-- (ordinary) categories!

⊚ : ∀ {c d e} {C : Category c} {D : Category d} {E : Category e} →
    Bifunctor (EnrichedFunctors D E) (EnrichedFunctors C D) (EnrichedFunctors C E)
⊚ {_} {_} {_} {_} {D} {E} = record
  { F₀ = uncurry′ _∘F_
  ; F₁ = uncurry′ _∘ₕ_
  ; identity = λ{ {F , G} {X} →
    begin
      (F $₁ D.id) ∘ E.id   ≈⟨ identityʳ ⟩
      F $₁ D.id            ≈⟨ identity F ⟩
      E.id                 ∎ }
  ; homomorphism = λ{ {_ , F₂} {G₁ , G₂} {H₁ , _} {α₁ , α₂} {β₁ , β₂} {X} →
    begin
      H₁ $₁ (β₂ [ X ] D.∘ α₂ [ X ]) ∘ β₁ [ F₂ $₀ X ] ∘ α₁ [ F₂ $₀ X ]
    ≈⟨ homomorphism H₁ ⟩∘⟨refl ⟩
      (H₁ $₁ β₂ [ X ] ∘ H₁ $₁ α₂ [ X ]) ∘ β₁ [ F₂ $₀ X ] ∘ α₁ [ F₂ $₀ X ]
    ≈⟨ ⟺ assoc ○ ∘-resp-≈ˡ assoc ⟩
      (H₁ $₁ β₂ [ X ] ∘ (H₁ $₁ α₂ [ X ] ∘ β₁ [ F₂ $₀ X ])) ∘ α₁ [ F₂ $₀ X ]
    ≈˘⟨ (refl⟩∘⟨ commute β₁ (α₂ [ X ])) ⟩∘⟨refl ⟩
      (H₁ $₁ β₂ [ X ] ∘ (β₁ [ G₂ $₀ X ] ∘ G₁ $₁ α₂ [ X ])) ∘ α₁ [ F₂ $₀ X ]
    ≈˘⟨ ⟺ assoc ○ ∘-resp-≈ˡ assoc ⟩
      (H₁ $₁ β₂ [ X ] ∘ β₁ [ G₂ $₀ X ]) ∘ G₁ $₁ α₂ [ X ] ∘ α₁ [ F₂ $₀ X ]
    ∎ }
  ; F-resp-≈ = λ{ {_} {F , _} (eq₁ , eq₂) → ∘-resp-≈ (F-resp-≈ F eq₂) eq₁ }
  }
  where
    module D = Underlying D
    module E = Underlying E
    open E hiding (id)
    open HomReasoning
    open UnderlyingFunctor hiding (F₀; F₁)
    open UnderlyingNT

    -- Aliases used to shorten some proof expressions

    infixr 14 _$₀_ _$₁_
    _$₀_ = UnderlyingFunctor.F₀
    _$₁_ = UnderlyingFunctor.F₁


-- Combinators for logical reasoning
{-# OPTIONS --without-K --safe --exact-split #-}

module Constructive.Combinators where

-- agda-stdlib
open import Data.Empty
open import Data.Sum as Sum
open import Data.Product as Prod
open import Function.Base
open import Relation.Nullary using (¬_; Dec; yes; no)
open import Relation.Nullary.Decidable using (⌊_⌋)
import Relation.Unary as U
open import Relation.Binary.PropositionalEquality

-- agda-misc
open import Constructive.Common

---------------------------------------------------------------------------
-- Combinators
---------------------------------------------------------------------------

module _ {a b c} {A : Set a} {B : Set b} {C : Set c} where
  →-distrib-⊎-× : ((A ⊎ B) → C) → (A → C) × (B → C)
  →-distrib-⊎-× f = f ∘ inj₁ , f ∘ inj₂

  →-undistrib-⊎-× : (A → C) × (B → C) → (A ⊎ B) → C
  →-undistrib-⊎-× (f , g) (inj₁ x) = f x
  →-undistrib-⊎-× (f , g) (inj₂ y) = g y

  →-undistrib-⊎-×-flip : (A ⊎ B) → (A → C) × (B → C) → C
  →-undistrib-⊎-×-flip = flip →-undistrib-⊎-×

  →-undistrib-×-⊎ : (A → C) ⊎ (B → C) → (A × B) → C
  →-undistrib-×-⊎ (inj₁ f) (x , y) = f x
  →-undistrib-×-⊎ (inj₂ g) (x , y) = g y

  →-undistrib-×-⊎-flip : (A × B) → (A → C) ⊎ (B → C) → C
  →-undistrib-×-⊎-flip = flip →-undistrib-×-⊎

-- contradiction
contradiction : ∀ {a w} {A : Set a} {WhatEver : Set w} → A → ¬ A → WhatEver
contradiction x ¬x = ⊥-elim (¬x x)

-- sum and product
module _ {a b} {A : Set a} {B : Set b} where
  A⊎B→¬A→B : A ⊎ B → ¬ A → B
  A⊎B→¬A→B (inj₁ x) ¬A = contradiction x ¬A
  A⊎B→¬A→B (inj₂ y) ¬A = y

  A⊎B→¬B→A : A ⊎ B → ¬ B → A
  A⊎B→¬B→A (inj₁ x) ¬B = x
  A⊎B→¬B→A (inj₂ y) ¬B = contradiction y ¬B

  ¬A⊎B→A→B : ¬ A ⊎ B → A → B
  ¬A⊎B→A→B (inj₁ ¬A) x = contradiction x ¬A
  ¬A⊎B→A→B (inj₂ y)  _ = y

  [A→B]→¬[A×¬B] : (A → B) → ¬ (A × ¬ B)
  [A→B]→¬[A×¬B] f (x , y) = y (f x)

  A×B→¬[A→¬B] : A × B → ¬ (A → ¬ B)
  A×B→¬[A→¬B] (x , y) f = f x y

  -- De Morgan's laws
  ¬[A⊎B]→¬A×¬B : ¬ (A ⊎ B) → ¬ A × ¬ B
  ¬[A⊎B]→¬A×¬B = →-distrib-⊎-×

  ¬A×¬B→¬[A⊎B] : ¬ A × ¬ B → ¬ (A ⊎ B)
  ¬A×¬B→¬[A⊎B] = →-undistrib-⊎-×

  A⊎B→¬[¬A×¬B] : A ⊎ B → ¬ (¬ A × ¬ B)
  A⊎B→¬[¬A×¬B] = →-undistrib-⊎-×-flip

  ¬A⊎¬B→¬[A×B] : ¬ A ⊎ ¬ B → ¬ (A × B)
  ¬A⊎¬B→¬[A×B] = →-undistrib-×-⊎

  A×B→¬[¬A⊎¬B] : A × B → ¬ (¬ A ⊎ ¬ B)
  A×B→¬[¬A⊎¬B] = →-undistrib-×-⊎-flip

  -- Double negated DEM₃
  ¬[A×B]→¬¬[¬A⊎¬B] : ¬ (A × B) → ¬ ¬ (¬ A ⊎ ¬ B)
  ¬[A×B]→¬¬[¬A⊎¬B] ¬[A×B] ¬[¬A⊎¬B] =
    ¬[¬A⊎¬B] (inj₁ λ x → contradiction (inj₂ (λ y → ¬[A×B] (x , y))) ¬[¬A⊎¬B])

  dec⊎⇒¬[A×B]→¬A⊎¬B : Dec⊎ A → Dec⊎ B → ¬ (A × B) → ¬ A ⊎ ¬ B
  dec⊎⇒¬[A×B]→¬A⊎¬B (inj₁ x)  (inj₁ y)  ¬[A×B] = contradiction (x , y) ¬[A×B]
  dec⊎⇒¬[A×B]→¬A⊎¬B (inj₁ x)  (inj₂ ¬y) ¬[A×B] = inj₂ ¬y
  dec⊎⇒¬[A×B]→¬A⊎¬B (inj₂ ¬x) _         ¬[A×B] = inj₁ ¬x

  join : (A → A → B) → A → B
  join f x = f x x

-- properties of negation
module _ {a} {A : Set a} where
  [A→¬A]→¬A : (A → ¬ A) → ¬ A
  [A→¬A]→¬A = join

  [¬A→A]→¬¬A : (¬ A → A) → ¬ ¬ A
  [¬A→A]→¬¬A ¬A→A ¬A = ¬A (¬A→A ¬A)

  -- Law of noncontradiction (LNC)
  ¬[A×¬A] : ¬ (A × ¬ A)
  ¬[A×¬A] = uncurry (flip _$_)

module _ {a b} {A : Set a} {B : Set b} where
  ¬[A→B]→¬B : ¬ (A → B) → ¬ B
  ¬[A→B]→¬B ¬[A→B] y = ¬[A→B] (const y)

  ¬[A→B]→¬[A→¬¬B] : ¬ (A → B) → ¬ (A → ¬ ¬ B)
  ¬[A→B]→¬[A→¬¬B] ¬[A→B] A→¬¬B = ¬[A→B] λ x → ⊥-elim $ A→¬¬B x (¬[A→B]→¬B ¬[A→B])

  ¬[A→B]→B→A : ¬ (A → B) → B → A
  ¬[A→B]→B→A ¬[A→B] y = contradiction (λ _ → y) ¬[A→B]

  [[A→B]→A]→¬A→A : ((A → B) → A) → ¬ A → A
  [[A→B]→A]→¬A→A [A→B]→A ¬A = [A→B]→A (⊥-elim ∘′ ¬A)

  [[A→B]→A]→¬¬A : ((A → B) → A) → ¬ ¬ A
  [[A→B]→A]→¬¬A [A→B]→A ¬A = ¬A ([[A→B]→A]→¬A→A [A→B]→A ¬A)

  [[[A→B]→A]→A]→¬B→¬¬A→A : (((A → B) → A) → A) → ¬ B → ¬ ¬ A → A
  [[[A→B]→A]→A]→¬B→¬¬A→A [[A→B]→A]→A ¬B ¬¬A =
    [[A→B]→A]→A λ A→B → contradiction (flip _∘′_ A→B ¬B) ¬¬A

module _ {a b} {A : Set a} {B : Set b} where
  contraposition : (A → B) → ¬ B → ¬ A
  contraposition = flip _∘′_

  -- variant of contraposition
  [A→¬¬B]→¬B→¬A : (A → ¬ ¬ B) → ¬ B → ¬ A
  [A→¬¬B]→¬B→¬A f ¬B x = (f x) ¬B

  [¬A→¬B]→¬¬[B→A] : (¬ A → ¬ B) → ¬ ¬ (B → A)
  [¬A→¬B]→¬¬[B→A] ¬A→¬B ¬[B→A] = ¬[B→A] λ y → ⊥-elim $ ¬A→¬B (¬[A→B]→¬B ¬[B→A]) y

  [A→¬B]→¬¬A→¬B : (A → ¬ B) → ¬ ¬ A → ¬ B
  [A→¬B]→¬¬A→¬B A→¬B ¬¬A y = ¬¬A λ x → A→¬B x y

module _ {a} {A : Set a} where
  -- introduction for double negation
  DN-intro : A → ¬ ¬ A
  DN-intro = flip _$_

  -- triple negation to negation
  TN-to-N : ¬ ¬ ¬ A → ¬ A
  TN-to-N = contraposition DN-intro

  -- Double negation of excluded middle
  DN-Dec⊎ : ¬ ¬ Dec⊎ A
  DN-Dec⊎ = λ f → (f ∘ inj₂) (f ∘ inj₁)

-- eliminator for ⊥
⊥-stable : ¬ ¬ ⊥ → ⊥
⊥-stable f = f id

-- Double negation is monad
module _ {a} {A : Set a} where
  DN-join : ¬ ¬ ¬ ¬ A → ¬ ¬ A
  DN-join = TN-to-N

module _ {a b} {A : Set a} {B : Set b} where
  DN-map : (A → B) → ¬ ¬ A → ¬ ¬ B
  DN-map = contraposition ∘′ contraposition

module _ {a b} {A : Set a} {B : Set b} where
  DN-bind : (A → ¬ ¬ B) → ¬ ¬ A → ¬ ¬ B
  DN-bind f = DN-join ∘′ DN-map f

  DN-bind⁻¹ : (¬ ¬ A → ¬ ¬ B) → A → ¬ ¬ B
  DN-bind⁻¹ f = f ∘′ DN-intro

module _ {a b} {A : Set a} {B : Set b} where
  DN-ap : ¬ ¬ (A → B) → ¬ ¬ A → ¬ ¬ B
  DN-ap ff fx = DN-bind (λ f → DN-map f fx) ff

  DN-ap⁻¹ : (¬ ¬ A → ¬ ¬ B) → ¬ ¬ (A → B)
  DN-ap⁻¹ f ¬[A→B] = ¬[A→B]→¬[A→¬¬B] ¬[A→B] (DN-bind⁻¹ f)

-- distributive properties
  DN-distrib-× : ¬ ¬ (A × B) → ¬ ¬ A × ¬ ¬ B
  DN-distrib-× ¬¬A×B = DN-map proj₁ ¬¬A×B , DN-map proj₂ ¬¬A×B

  DN-undistrib-× : ¬ ¬ A × ¬ ¬ B → ¬ ¬ (A × B)
  DN-undistrib-× = [A→¬¬B]→¬B→¬A ¬[A×B]→¬¬[¬A⊎¬B] ∘′ ¬A×¬B→¬[A⊎B]

  DN-undistrib-⊎ : ¬ ¬ A ⊎ ¬ ¬ B → ¬ ¬ (A ⊎ B)
  DN-undistrib-⊎ = Sum.[ DN-map inj₁ , DN-map inj₂ ]

  stable-undistrib-× : Stable A × Stable B → Stable (A × B)
  stable-undistrib-× (A-stable , B-stable) ¬¬[A×B] =
    Prod.map A-stable B-stable $ DN-distrib-× ¬¬[A×B]

module _ {a b c} {A : Set a} {B : Set b} {C : Set c} where
  ip-⊎-DN : (A → (B ⊎ C)) → ¬ ¬ ((A → B) ⊎ (A → C))
  ip-⊎-DN f =
    DN-map Sum.[ (Sum.map const const ∘ f) , (λ ¬A → inj₁ λ x → ⊥-elim (¬A x)) ]
          DN-Dec⊎

DN-ip : ∀ {p q r} {P : Set p} {Q : Set q} {R : Q → Set r} →
        Q → (P → Σ Q R) → ¬ ¬ (Σ Q λ x → (P → R x))
DN-ip q f =
  DN-map Sum.[ (λ x → Prod.map₂ const (f x)) ,
               (λ ¬P → q , λ P → ⊥-elim $ ¬P P) ] DN-Dec⊎

-- Properties of Dec⊎
module _ {a} {A : Set a} where
  dec⊎⇒dec : Dec⊎ A → Dec A
  dec⊎⇒dec (inj₁ x) = yes x
  dec⊎⇒dec (inj₂ y) = no y

  dec⇒dec⊎ : Dec A → Dec⊎ A
  dec⇒dec⊎ (yes p) = inj₁ p
  dec⇒dec⊎ (no ¬p) = inj₂ ¬p

  ¬-dec⊎ : Dec⊎ A → Dec⊎ (¬ A)
  ¬-dec⊎ (inj₁ x) = inj₂ (DN-intro x)
  ¬-dec⊎ (inj₂ y) = inj₁ y

module _ {a b} {A : Set a} {B : Set b} where
  dec⊎-map : (A → B) → (B → A) → Dec⊎ A → Dec⊎ B
  dec⊎-map f g dec⊎ = Sum.map f (contraposition g) dec⊎

  dec⊎-⊎ : Dec⊎ A → Dec⊎ B → Dec⊎ (A ⊎ B)
  dec⊎-⊎ (inj₁ x)  _         = inj₁ (inj₁ x)
  dec⊎-⊎ (inj₂ ¬x) (inj₁ y)  = inj₁ (inj₂ y)
  dec⊎-⊎ (inj₂ ¬x) (inj₂ ¬y) = inj₂ (¬A×¬B→¬[A⊎B] (¬x , ¬y))

  dec⊎-× : Dec⊎ A → Dec⊎ B → Dec⊎ (A × B)
  dec⊎-× (inj₁ x)  (inj₁ y)  = inj₁ (x , y)
  dec⊎-× (inj₁ x)  (inj₂ ¬y) = inj₂ (¬y ∘ proj₂)
  dec⊎-× (inj₂ ¬x) _         = inj₂ (¬x ∘ proj₁)

-- Properties of Stable
module _ {a} {A : Set a} where
  dec⇒stable : Dec A → Stable A
  dec⇒stable (yes p) ¬¬A = p
  dec⇒stable (no ¬p) ¬¬A = ⊥-elim (¬¬A ¬p)

  ¬-stable : Stable (¬ A)
  ¬-stable = TN-to-N

  dec⊎⇒stable : Dec⊎ A → Stable A
  dec⊎⇒stable dec⊎ = dec⇒stable (dec⊎⇒dec dec⊎)

module _ {a p} {A : Set a} {P : A → Set p} where
  DecU⇒stable : DecU P → ∀ x → Stable (P x)
  DecU⇒stable P? x = dec⊎⇒stable (P? x)

  -- Properties of DecU
  ¬-DecU : DecU P → DecU (λ x → ¬ (P x))
  ¬-DecU P? x = ¬-dec⊎ (P? x)

  DecU⇒decidable : DecU P → U.Decidable P
  DecU⇒decidable P? x = dec⊎⇒dec (P? x)

  decidable⇒DecU : U.Decidable P → DecU P
  decidable⇒DecU P? x = dec⇒dec⊎ (P? x)

DecU-map : ∀ {a b p} {A : Set a} {B : Set b} {P : A → Set p} →
  (f : B → A) → DecU P → DecU (λ x → P (f x))
DecU-map f P? x = dec⊎-map id id (P? (f x))

module _ {a p q} {A : Set a} {P : A → Set p} {Q : A → Set q} where
  DecU-⊎ : DecU P → DecU Q → DecU (λ x → P x ⊎ Q x)
  DecU-⊎ P? Q? x = dec⊎-⊎ (P? x) (Q? x)

  DecU-× : DecU P → DecU Q → DecU (λ x → P x × Q x)
  DecU-× P? Q? x = dec⊎-× (P? x) (Q? x)

-- Quantifier
module _ {a p} {A : Set a} {P : A → Set p} where
  ∃P→¬∀¬P : ∃ P → ¬ (∀ x → ¬ (P x))
  ∃P→¬∀¬P = flip uncurry

  ∀P→¬∃¬P : (∀ x → P x) → ¬ ∃ λ x → ¬ (P x)
  ∀P→¬∃¬P f (x , ¬Px) = ¬Px (f x)

  ¬∃P→∀¬P : ¬ ∃ P → ∀ x → ¬ (P x)
  ¬∃P→∀¬P = curry

  ∀¬P→¬∃P : (∀ x → ¬ (P x)) → ¬ ∃ P
  ∀¬P→¬∃P = uncurry

  ∃¬P→¬∀P : ∃ (λ x → ¬ (P x)) → ¬ (∀ x → P x)
  ∃¬P→¬∀P (x , ¬Px) ∀P = ¬Px (∀P x)

  ¬∀¬P→¬¬∃P : ¬ (∀ x → ¬ P x) → ¬ ¬ ∃ P
  ¬∀¬P→¬¬∃P ¬∀¬P = contraposition ¬∃P→∀¬P ¬∀¬P

  ¬¬∃P→¬∀¬P : ¬ ¬ ∃ P → ¬ (∀ x → ¬ P x)
  ¬¬∃P→¬∀¬P ¬¬∃P = contraposition ∀¬P→¬∃P ¬¬∃P

  ¬¬∀P→¬∃¬P : ¬ ¬ (∀ x → P x) → ¬ ∃ λ x → ¬ (P x)
  ¬¬∀P→¬∃¬P ¬¬∀P = contraposition ∃¬P→¬∀P ¬¬∀P

  ¬¬∃P<=>¬∀¬P : ¬ ¬ ∃ P <=> ¬ (∀ x → ¬ P x)
  ¬¬∃P<=>¬∀¬P = mk<=> ¬¬∃P→¬∀¬P ¬∀¬P→¬¬∃P

  -- remove?
  ∀¬¬P→¬∃¬P : (∀ x → ¬ ¬ P x) → ¬ ∃ λ x → ¬ (P x)
  ∀¬¬P→¬∃¬P = uncurry

  -- converse of DNS
  ¬¬∀P→∀¬¬P : ¬ ¬ (∀ x → P x) → ∀ x → ¬ ¬ P x
  ¬¬∀P→∀¬¬P f x = DN-map (λ ∀P → ∀P x) f

  ∃¬¬P→¬¬∃P  : (∃ λ x → ¬ ¬ P x) → ¬ ¬ ∃ λ x → P x
  ∃¬¬P→¬¬∃P (x , ¬¬Px) = DN-map (λ Px → x , Px) ¬¬Px

  ¬¬∃¬P→¬∀P : ¬ ¬ ∃ (λ x → ¬ (P x)) → ¬ (∀ x → P x)
  ¬¬∃¬P→¬∀P = contraposition ∀P→¬∃¬P

  ¬∃¬P→∀¬¬P : ¬ ∃ (λ x → ¬ P x) → ∀ x → ¬ ¬ P x
  ¬∃¬P→∀¬¬P = curry

  ∀P→∀¬¬P : (∀ x → P x) → ∀ x → ¬ ¬ P x
  ∀P→∀¬¬P ∀P x = DN-intro (∀P x)

  ∃P→∃¬¬P : ∃ P → ∃ λ x → ¬ ¬ P x
  ∃P→∃¬¬P (x , Px) = x , DN-intro Px

module _ {a p q} {A : Set a} {P : A → Set p} {Q : A → Set q} where
  [∀¬P→∀¬Q]→¬¬[∃Q→∃P] : ((∀ x → ¬ P x) → (∀ x → ¬ Q x)) → ¬ ¬ (∃ Q → ∃ P)
  [∀¬P→∀¬Q]→¬¬[∃Q→∃P] ∀¬P→∀¬Q =
    DN-ap⁻¹ (¬∀¬P→¬¬∃P ∘ contraposition ∀¬P→∀¬Q ∘ ¬¬∃P→¬∀¬P)

-- Quantifier rearrangement for stable predicate
module _ {a p} {A : Set a} {P : A → Set p} (P-stable : ∀ x → Stable (P x)) where
  P-stable⇒∃¬¬P→∃P : ∃ (λ x → ¬ ¬ P x) → ∃ P
  P-stable⇒∃¬¬P→∃P (x , ¬¬Px) = x , P-stable x ¬¬Px

  P-stable⇒∀¬¬P→∀P : (∀ x → ¬ ¬ P x) → ∀ x → P x
  P-stable⇒∀¬¬P→∀P ∀¬¬P x = P-stable x (∀¬¬P x)

  P-stable⇒¬¬∀P→∀P : ¬ ¬ (∀ x → P x) → ∀ x → P x
  P-stable⇒¬¬∀P→∀P = P-stable⇒∀¬¬P→∀P ∘′ ¬¬∀P→∀¬¬P

  P-stable⇒¬∃¬P→∀P : ¬ ∃ (λ x → ¬ P x) → ∀ x → P x
  P-stable⇒¬∃¬P→∀P ¬∃¬P = P-stable⇒∀¬¬P→∀P (¬∃¬P→∀¬¬P ¬∃¬P)

  P-stable⇒¬∀P→¬¬∃¬P : ¬ (∀ x → P x) → ¬ ¬ ∃ (λ x → ¬ (P x))
  P-stable⇒¬∀P→¬¬∃¬P ¬∀P = contraposition P-stable⇒¬∃¬P→∀P ¬∀P

module _ {a p} {A : Set a} {P : A → Set p} (P? : DecU P) where
  P?⇒∃¬¬P→∃P : ∃ (λ x → ¬ ¬ P x) → ∃ P
  P?⇒∃¬¬P→∃P = P-stable⇒∃¬¬P→∃P (DecU⇒stable P?)

  P?⇒∀¬¬P→∀P : (∀ x → ¬ ¬ P x) → ∀ x → P x
  P?⇒∀¬¬P→∀P = P-stable⇒∀¬¬P→∀P (DecU⇒stable P?)

  P?⇒¬¬∀P→∀P : ¬ ¬ (∀ x → P x) → ∀ x → P x
  P?⇒¬¬∀P→∀P = P-stable⇒¬¬∀P→∀P (DecU⇒stable P?)

  P?⇒¬∃¬P→∀P : ¬ ∃ (λ x → ¬ P x) → ∀ x → P x
  P?⇒¬∃¬P→∀P = P-stable⇒¬∃¬P→∀P (DecU⇒stable P?)

  P?⇒¬∀P→¬¬∃¬P : ¬ (∀ x → P x) → ¬ ¬ ∃ (λ x → ¬ P x)
  P?⇒¬∀P→¬¬∃¬P = P-stable⇒¬∀P→¬¬∃¬P (DecU⇒stable P?)

  -- call/cc
  P?⇒[¬∀P→∀P]→∀P : (¬ (∀ x → P x) → ∀ x → P x) → ∀ x → P x
  P?⇒[¬∀P→∀P]→∀P ¬∀P→∀P = P?⇒¬¬∀P→∀P λ ¬∀P → ¬∀P (¬∀P→∀P ¬∀P)

  P?⇒[∃¬P→∀P]→∀P : (∃ (λ x → ¬ P x) → ∀ x → P x) → ∀ x → P x
  P?⇒[∃¬P→∀P]→∀P ∃¬P→∀P =
    P?⇒¬¬∀P→∀P λ ¬∀P → P?⇒¬∀P→¬¬∃¬P ¬∀P λ ∃¬P → ¬∀P (∃¬P→∀P ∃¬P)

  -- [∀¬P→¬∀Q]→¬∃¬Q→¬¬∃P

module _ {a p q} {A : Set a} {P : A → Set p} {Q : A → Set q} where
  P?⇒[∃¬P→∃¬Q]→∀Q→∀P : DecU P → (∃ (λ x → ¬ P x) → ∃ (λ x → ¬ Q x)) →
                       (∀ x → Q x) → ∀ x → P x
  P?⇒[∃¬P→∃¬Q]→∀Q→∀P P? ∃¬P→∃¬Q =
    P?⇒¬∃¬P→∀P P? ∘ contraposition ∃¬P→∃¬Q ∘ ∀P→¬∃¬P

  P?⇒[∃Q→∀P]→¬∀¬Q→∀P : DecU P → (∃ Q → ∀ x → P x) → ¬ (∀ x → ¬ Q x) → ∀ x → P x
  P?⇒[∃Q→∀P]→¬∀¬Q→∀P P? ∃Q→∀P ¬∀¬Q = P?⇒¬¬∀P→∀P P? (DN-map ∃Q→∀P (¬∀¬P→¬¬∃P ¬∀¬Q))

  ¬[¬∀P⊎¬∀Q]→∀P×∀Q : DecU P → DecU Q → ¬ (¬ (∀ x → P x) ⊎ ¬ (∀ x → Q x)) →
                     (∀ x → P x) × (∀ x → Q x)
  ¬[¬∀P⊎¬∀Q]→∀P×∀Q P? Q? ¬[¬∀P⊎¬∀Q] =
    Prod.map (P?⇒¬¬∀P→∀P P?) (P?⇒¬¬∀P→∀P Q?) (¬[A⊎B]→¬A×¬B ¬[¬∀P⊎¬∀Q])

module _ {a p q} {A : Set a} {P : A → Set p} {Q : A → Set q} where
  ∃-undistrib-⊎ : ∃ P ⊎ ∃ Q → ∃ (λ x → P x ⊎ Q x)
  ∃-undistrib-⊎ (inj₁ (x , Px)) = x , inj₁ Px
  ∃-undistrib-⊎ (inj₂ (x , Qx)) = x , inj₂ Qx

  ∃-distrib-⊎ : ∃ (λ x → P x ⊎ Q x) → ∃ P ⊎ ∃ Q
  ∃-distrib-⊎ (x , inj₁ Px) = inj₁ (x , Px)
  ∃-distrib-⊎ (x , inj₂ Qx) = inj₂ (x , Qx)

  ∃-distrib-× : ∃ (λ x → P x × Q x) → ∃ P × ∃ Q
  ∃-distrib-× (x , Px , Qx) = (x , Px) , (x , Qx)

  ∀-undistrib-× : (∀ x → P x) × (∀ x → Q x) → ∀ x → P x × Q x
  ∀-undistrib-× (∀P , ∀Q) x = ∀P x , ∀Q x

  ∀-distrib-× : (∀ x → P x × Q x) → (∀ x → P x) × (∀ x → Q x)
  ∀-distrib-× ∀x→Px×Qx = proj₁ ∘ ∀x→Px×Qx , proj₂ ∘ ∀x→Px×Qx

  ∀-undistrib-⊎ : (∀ x → P x) ⊎ (∀ x → Q x) → ∀ x → P x ⊎ Q x
  ∀-undistrib-⊎ (inj₁ ∀P) x = inj₁ (∀P x)
  ∀-undistrib-⊎ (inj₂ ∀Q) x = inj₂ (∀Q x)

  ¬[¬∃P×¬∃Q]→¬¬∃x→Px⊎Qx : ¬ (¬ ∃ P × ¬ ∃ Q) → ¬ ¬ ∃ λ x → P x ⊎ Q x
  ¬[¬∃P×¬∃Q]→¬¬∃x→Px⊎Qx = DN-map ∃-undistrib-⊎ ∘′ contraposition ¬[A⊎B]→¬A×¬B

  [¬¬∃x→Px⊎Qx]→¬[¬∃P×¬∃Q] : (¬ ¬ ∃ λ x → P x ⊎ Q x) → ¬ (¬ ∃ P × ¬ ∃ Q)
  [¬¬∃x→Px⊎Qx]→¬[¬∃P×¬∃Q] = contraposition ¬A×¬B→¬[A⊎B] ∘′ DN-map ∃-distrib-⊎

  ¬∀¬P×¬∀¬Q→¬¬[∃P×∃Q] : ¬ (∀ x → ¬ P x) × ¬ (∀ x → ¬ Q x) → ¬ ¬ (∃ P × ∃ Q)
  ¬∀¬P×¬∀¬Q→¬¬[∃P×∃Q] = DN-undistrib-× ∘′ Prod.map ¬∀¬P→¬¬∃P ¬∀¬P→¬¬∃P

  ¬¬[∃P×∃Q]→¬∀¬P×¬∀¬Q : ¬ ¬ (∃ P × ∃ Q) → ¬ (∀ x → ¬ P x) × ¬ (∀ x → ¬ Q x)
  ¬¬[∃P×∃Q]→¬∀¬P×¬∀¬Q = Prod.map ¬¬∃P→¬∀¬P ¬¬∃P→¬∀¬P ∘′ DN-distrib-×

  [∀x→Px→Qx]→∀P→∀Q : (∀ x → P x → Q x) → (∀ x → P x) → ∀ x → Q x
  [∀x→Px→Qx]→∀P→∀Q f g x = f x (g x)


-- Andreas, 2012-03-15, example by Nisse
-- {-# OPTIONS --show-implicit -v tc.meta:20 #-}
module Issue585-17 where

data List (A : Set) : Set where
  []  : List A
  _∷_ : A → List A → List A

data ″ℕ″ : Set where
  suc : ″ℕ″ → ″ℕ″

replicate : {A : Set} → ″ℕ″ → A → List A
replicate (suc n) x = x ∷ replicate n x

data P (A B : Set) : List B → Set where
  p : (x : B) → P A B (x ∷ [])

data Q {A B xs} : List B → P A B xs → Set where
  q : ∀ {x xs p} → Q xs p → Q (x ∷ xs) p

foo : {A B : Set} (x : B) (n : ″ℕ″) → Q (replicate n x) (p {A = A} x)
foo x n = bar x n
  where
  bar : {B : Set} (x : B) (n : ″ℕ″) → Q (replicate n x) (p x)
  bar x (suc n) = q (bar x n)


------------------------------------------------------------------------
-- The Agda standard library
--
-- Convenient syntax for "equational reasoning" using a partial order
------------------------------------------------------------------------

open import Relation.Binary

module Relation.Binary.PartialOrderReasoning
         {p₁ p₂ p₃} (P : Poset p₁ p₂ p₃) where

open Poset P
import Relation.Binary.PreorderReasoning as PreR
open PreR preorder public renaming (_∼⟨_⟩_ to _≤⟨_⟩_)


{-# OPTIONS --without-K #-}
module hott.truncation.elim where

open import sum
open import equality
open import function
open import hott.equivalence
open import hott.level
open import hott.loop
open import hott.truncation.core
open import hott.truncation.equality
open import hott.univalence
open import sets.nat
open import sets.unit

is-null : ∀ {i j}{X : Set i}{Y : Set j} → Y → (X → Y) → Set _
is-null y f = ∀ x → y ≡ f x

is-null-level : ∀ {i j}{X : Set i}{Y : Set j} → h 2 Y
              → (y : Y)(f : X → Y) → h 1 (is-null y f)
is-null-level hY y f = Π-level λ x → hY y (f x)

compose-is-null : ∀ {i j k}{X : Set i}{Y : Set j}{Z : Set k}
                → (y : Y)(f : X → Y)(g : Y → Z)
                → is-null y f
                → is-null (g y) (g ∘ f)
compose-is-null y f g c y' = ap g (c y')

Null : ∀ {i j} → ℕ → Set i → Set j → Set _
Null n X Y = Σ (X → Y) λ f → (x : X) → is-null (refl' n (f x)) (mapΩ n f)

-- connected components

Conn : ∀ {i}{X : Set i}(n : ℕ) → Trunc n X → Set _
Conn {X = X} n c = Σ X λ x → [ x ] ≡ c

map-conn : ∀ {i j}{X : Set i}{Y : Set j}(n : ℕ)
         → (c : Trunc n X)
         → (X → Y)
         → (Conn n c → Y)
map-conn n c f (x , p) = f x

conn-decomp : ∀ {i}{X : Set i}(n : ℕ)
            → Σ (Trunc n X) (Conn n) ≅ X
conn-decomp {X = X} n = total-iso [_]

conn-connected : ∀ {i}{X : Set i}(n : ℕ)(c : Trunc n X)
               → contr (Trunc n (Conn n c))
conn-connected {X = X} n c = φ c , lem c
  where
    φ' : (x : X) → Trunc n (Conn n [ x ])
    φ'  x = [ x , refl ]

    φ : (c : Trunc n X) → Trunc n (Conn n c)
    φ = Trunc-dep-elim n (λ c → Trunc n (Conn n c)) (λ _ → Trunc-level n) φ'

    lem' : (c : Trunc n X)(a : Conn n c) → φ c ≡ [ a ]
    lem' .([ x ]) (x , refl) = Trunc-dep-elim-β n
      (λ c → Trunc n (Conn n c))
      (λ _ → Trunc-level n) φ' x

    lem : (c : Trunc n X)(a : Trunc n (Conn n c)) → φ c ≡ a
    lem c = Trunc-dep-elim n (λ a → φ c ≡ a) (λ a → h↑ (Trunc-level n) (φ c) a) (lem' c)

-- main result

module CM {i j} n (X : Set i)(Y : Set j)
          (hY : h (n + 2) Y) where
  cm' : Y → Null n X Y
  cm' y = (λ _ → y) , (λ x p → sym (mapΩ-const n y x p))

  cm : (Trunc (suc n) X → Y) → Null n X Y
  cm f = (λ x → f [ x ]) , λ x
       → subst₂ (is-null) (mapΩ-refl n f) (eq x)
               (compose-is-null (refl' n [ x ]) (mapΩ n [_]) (mapΩ n f)
                 (λ p → h1⇒prop (Ω-level n hT) _ _))
     where
       f' : X → Y
       f' x = f [ x ]

       eq' : (x : X)(p : Ω n x) → mapΩ n f (mapΩ n [_] p) ≡ mapΩ n f' p
       eq' x p = mapΩ-hom n [_] f p

       eq : (x : X) → mapΩ n f ∘ mapΩ n [_] ≡ mapΩ n f'
       eq x = funext (eq' x)

       hT : h (n + 1) (Trunc (suc n) X)
       hT = subst (λ m → h m (Trunc (suc n) X)) (+-commutativity 1 n) (Trunc-level (suc n))

Trunc-elim' : ∀ {i j} n (X : Set i)(Y : Set j) → h (n + 2) Y
            → (Trunc (suc n) X → Y) ≅ Null n X Y

null-connected : ∀ {i j} n {X : Set i}{Y : Set j}
               → contr (Trunc (suc n) X)
               → h (n + 2) Y
               → (f : X → Y)
               → ((x : X) → is-null (refl' n (f x)) (mapΩ n f {x}))
               → (x : X) → is-null (f x) f
null-connected zero hX hY f c x₀ x₁ = c x₀ x₁
null-connected (suc n) {X} hX hY f c x₀ x₁
  = φ (trunc-equality (suc n) (h1⇒prop (h↑ hX) _ _))
  where
    ap-null : (x y : X)(p : x ≡ y) → is-null (refl' n (ap f p)) (mapΩ n (ap f) {p})
    ap-null x .x refl = c x

    φ : Trunc (suc n) (x₀ ≡ x₁) → f x₀ ≡ f x₁
    φ = invert (Trunc-elim' n (x₀ ≡ x₁) (f x₀ ≡ f x₁) (hY _ _))
        ( ap f , ap-null x₀ x₁ )

Trunc-elim-connected : ∀ {i j} n (X : Set i)(Y : Set j)
                     → contr (Trunc (suc n) X)
                     → h (n + 2) Y
                     → Y ≅ Null n X Y
Trunc-elim-connected n X Y hX hY = ≈⇒≅ (cm' , cm-equiv' (proj₁ hX))
  where
    open CM n X Y hY

    module _ (x₀ : X) where
      g : Null n X Y → Y
      g (f , c) = f x₀

      α : (y : Y) → g (cm' y) ≡ y
      α y = refl

      β : (x : Null n X Y) → cm' (g x) ≡ x
      β (f , c) = unapΣ ( (funext λ x → null-connected n hX hY f c _ _)
                        , h1⇒prop (Π-level λ x → is-null-level (Ω-level n hY) _ _) _ _)

      f-iso : Y ≅ Null n X Y
      f-iso = iso cm' g α β

    cm-equiv : X → weak-equiv cm'
    cm-equiv x₀ = proj₂ (≅⇒≈ (f-iso x₀))

    cm-equiv' : Trunc (suc n) X → weak-equiv cm'
    cm-equiv' = invert (Trunc-elim-iso (suc n) X (weak-equiv cm') (h! (weak-equiv-h1 cm'))) cm-equiv

abstract
  Trunc-elim'₀ : ∀ {i j} n (X : Set i)(Y : Set j) → h (n + 2) Y
              → (Trunc (suc n) X → Y) ≅ Null n X Y
  Trunc-elim'₀ n X Y hY = begin
      (Trunc (suc n) X → Y)
    ≅⟨ ( Π-ap-iso refl≅ λ c
       → Trunc-elim-connected n _ _ (conn-connected (suc n) c) hY) ⟩
      ((c : Trunc (suc n) X) → Null n (Conn (suc n) c) Y)
    ≅⟨ ΠΣ-swap-iso ⟩
      ( Σ ((c : Trunc (suc n) X) → Conn (suc n) c → Y) λ f
      → ((c : Trunc (suc n) X) → (x : Conn (suc n) c) → is-null _ (mapΩ n (f c))) )
    ≅⟨ sym≅ ( Σ-ap-iso (curry-iso (λ _ _ → Y)) λ f
            → curry-iso λ c x → is-null _ (mapΩ n (λ x' → f (c , x')))  ) ⟩
      ( Σ ((Σ (Trunc (suc n) X) λ c → Conn (suc n) c) → Y) λ f
      → (((x' : (Σ (Trunc (suc n) X) λ c → Conn (suc n) c))
           → is-null _ (mapΩ n (λ x → f (proj₁ x' , x))))) )
    ≅⟨ ( Σ-ap-iso' (→-ap-iso (conn-decomp (suc n)) refl≅) λ f
       → Π-ap-iso (conn-decomp (suc n)) λ x → refl≅ ) ⟩
      ( Σ (X → Y) λ f
      → (((x : X) → is-null _ (mapΩ n (λ x → f (proj₁ x)) {x , refl}))) )
    ≅⟨ ( Σ-ap-iso refl≅ λ f → Π-ap-iso refl≅ λ x → lem f x ) ⟩
      Null n X Y
    ∎
    where
      open ≅-Reasoning

      lem : (f : X → Y) (x : X)
          → is-null (refl' n (f x)) (mapΩ n (map-conn (suc n) [ x ] f) {x , refl})
          ≅ is-null (refl' n (f x)) (mapΩ n f {x})
      lem f x = ≡⇒≅ (ap (is-null (refl' n (f x))) (funext hom)) ·≅ sym≅ is-const-equiv
        where
          π : Conn (suc n) [ x ] → X
          π = proj₁

          hom : (p : Ω n (x , refl))
              → mapΩ n (map-conn (suc n) [ x ] f) p
              ≡ mapΩ n f (mapΩ n proj₁ p)
          hom p = sym (mapΩ-hom n proj₁ f p)

          φ : Ω n {Conn (suc n) [ x ]} (x , refl) ≅ Ω n x
          φ = loop-sum n λ a → Trunc-level (suc n) _ _

          is-const-equiv : is-null {X = Ω n x} (refl' n (f x)) (mapΩ n f {x})
                         ≅ is-null {X = Ω n {Conn (suc n) [ x ]} (x , refl)}
                                   (refl' n (f x))
                                   (mapΩ n f ∘ mapΩ n proj₁)
          is-const-equiv = Π-ap-iso (sym≅ φ) λ p
                         → refl≅

  Trunc-elim'-β₀ : ∀ {i j n}{X : Set i}{Y : Set j} (hY : h (n + 2) Y)
                → (f : Trunc (suc n) X → Y)
                → proj₁ (apply (Trunc-elim'₀ n X Y hY) f) ≡ (λ x → f [ x ])
  Trunc-elim'-β₀ hY f = refl

  Trunc-elim'-β : ∀ {i j n}{X : Set i}{Y : Set j} (hY : h (n + 2) Y)
                → (f : Trunc (suc n) X → Y)
                → apply (Trunc-elim'₀ n X Y hY) f ≡ CM.cm n X Y hY f
  Trunc-elim'-β {n = n} hY f = unapΣ
    ( Trunc-elim'-β₀ hY f
    , h1⇒prop (Π-level λ x → is-null-level (Ω-level n hY) _ _) _ _ )

Trunc-elim' n X Y hY = ≈⇒≅ (cm , cm-we)
  where
    open CM n X Y hY

    cm-eq : apply (Trunc-elim'₀ n X Y hY) ≡ cm
    cm-eq = funext (Trunc-elim'-β hY)

    cm-we : weak-equiv cm
    cm-we = subst weak-equiv cm-eq (proj₂ (≅⇒≈ (Trunc-elim'₀ n X Y hY)))

-- example

Repr : ∀ {i} → Set i → Set _
Repr {i} A = Σ (Trunc 2 A → Set i) λ hom
       → (a : A) → hom [ a ] ≅ (a ≡ a)

Braiding : ∀ {i} → Set i → Set _
Braiding A = {a : A}(p q : a ≡ a) → p · q ≡ q · p

braiding-repr : ∀ {i}{A : Set i} → h 3 A → Braiding A → Repr A
braiding-repr {i}{A} hA γ = (λ c → proj₁ (hom c)) , λ a
                          → ≡⇒≅ (ap proj₁ (hom-β a))
  where
    eq : A → Set _
    eq a = (a ≡ a)

    hom' : A → Type _ 2
    hom' a = eq a , hA a a

    type-eq-iso : ∀ {n}{X Y : Type i n}
                → (X ≡ Y) ≅ (proj₁ X ≈ proj₁ Y)
    type-eq-iso {n}{X}{Y} = begin
        (X ≡ Y)
      ≅⟨ sym≅ Σ-split-iso ⟩
        (Σ (proj₁ X ≡ proj₁ Y) λ p → subst (h n) p (proj₂ X) ≡ proj₂ Y)
      ≅⟨ (Σ-ap-iso refl≅ λ p → contr-⊤-iso (hn-h1 n _ _ _)) ⟩
        ((proj₁ X ≡ proj₁ Y) × ⊤)
      ≅⟨ ×-right-unit ⟩
        (proj₁ X ≡ proj₁ Y)
      ≅⟨ uni-iso ⟩
        (proj₁ X ≈ proj₁ Y)
      ∎
      where open ≅-Reasoning

    type-eq : ∀ {n}{X : Type i n}{p : X ≡ X}
            → ((x : proj₁ X) → coerce (ap proj₁ p) x ≡ x)
            → p ≡ refl
    type-eq f = invert≅ (iso≡ type-eq-iso) (unapΣ
              ( funext f , h1⇒prop (weak-equiv-h1 _) _ _))

    ap-eq : {a b : A}(p : a ≡ b)(q : a ≡ a) → coerce (ap proj₁ (ap hom' p)) q ≡ sym p · q · p
    ap-eq refl q = sym (left-unit q)

    hom-null : (a : A) → is-null refl (mapΩ 1 hom' {a})
    hom-null a p = sym (type-eq λ q → ap-eq p q
                                    · ap (λ z → z · p) (γ (sym p) q)
                                    · associativity q (sym p) p
                                    · ap (λ z → q · z) (right-inverse p)
                                    · left-unit q)

    hom-iso : (Trunc 2 A → Type i 2) ≅ Null 1 A (Type i 2)
    hom-iso = Trunc-elim' 1 A (Type i 2) type-level

    hom : Trunc 2 A → Type _ 2
    hom = invert hom-iso (hom' , hom-null)

    hom-β : (a : A) → hom [ a ] ≡ hom' a
    hom-β a = funext-inv (ap proj₁ (_≅_.iso₂ hom-iso (hom' , hom-null))) a


module Data.Num.Sandbox.WF where

open import Data.Nat
-- open import Relation.Binary
--
-- open DecTotalOrder decTotalOrder
--   using (trans)
--
-- data Acc (n : ℕ) : Set where
--     acc : (∀ m → m < n → Acc m) → Acc n
--
-- go0 : (m : ℕ) → m < 0 → Acc m
-- go0 m ()
--
-- <-wf : ∀ n → Acc n
-- <-wf n = acc (go n)
--     where   go : (n m : ℕ) → m < n → Acc m
--             go zero m ()
--             go (suc n₁) zero m<n = acc (λ _ → λ ())
--             go (suc n₁) (suc m) (s≤s m<n₁) = acc (λ m₁ m₁<m+1 → go n₁ m₁ (trans m₁<m+1 m<n₁))

pp : ℕ → ℕ
pp zero = zero
pp (suc zero) = zero
pp (suc (suc n)) = n
--
-- f : ℕ → ℕ
-- f n = go n (<-wf n)
--     where
--         go : ∀ n → Acc n → ℕ
--         go zero    _ = zero
--         go (suc n) (acc a) = go (pp (suc n)) (a (pp (suc n)) {!   !})

open import Induction.Nat

half : ℕ → ℕ
half = <-rec _ go
    where   go : (x : ℕ) → ((y : ℕ) → suc y ≤′ x → ℕ) → ℕ
            go zero acc = zero
            go (suc zero) acc = zero
            go (suc (suc n)) acc = suc (acc n (≤′-step ≤′-refl))


module ConstructorHeadedDivergenceIn2-2-10 where

data ⊤ : Set where
  tt : ⊤

data ℕ : Set where
  zero : ℕ
  suc : ℕ → ℕ

data _×_ (A B : Set) : Set where
  _,_ : A → B → A × B

{- Brandon Moore reports (July 2011)
In 2.2.10 the following code seems to cause typechecking
to diverge.
-}

f : ℕ → Set
f zero = ⊤
f (suc n) = ℕ × f n

enum : (n : ℕ) → f n
enum zero = tt
enum (suc n) = n , enum n

n : ℕ
n = _

test : f n
test = enum (suc n)
{-
This typechecks quickly if the definition
of test is changed to

test = enum n

I think the problem is that the body has type ℕ × f n,
and unifying it with the expected type f n invokes the
constructor-headed function specialization to resolve
n to suc n', and the process repeats.

Brandon
-}

-- Andreas, 2011-07-28 This bug is not reproducible in the darcs
-- version.
--
-- This file should fail with unresolved metas.


{-# OPTIONS --safe #-}
module Cubical.Data.Int.MoreInts.DiffInt.Properties where

open import Cubical.Foundations.HLevels

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Univalence

open import Cubical.Data.Int.MoreInts.DiffInt.Base
open import Cubical.Data.Nat as ℕ renaming (_+_ to _+ℕ_ ; _·_ to _*ℕ_)
open import Cubical.Data.Sigma
open import Cubical.Data.Bool

open import Cubical.Relation.Binary.Base
open import Cubical.Relation.Nullary

open import Cubical.HITs.SetQuotients as SetQuotients
open import Cubical.HITs.PropositionalTruncation as PropositionalTruncation
open import Cubical.Foundations.Isomorphism

open BinaryRelation

relIsEquiv : isEquivRel rel
relIsEquiv = equivRel {A = ℕ × ℕ} relIsRefl relIsSym relIsTrans
  where
    relIsRefl : isRefl rel
    relIsRefl (a0 , a1) = refl

    relIsSym : isSym rel
    relIsSym (a0 , a1) (b0 , b1) p = sym p

    relIsTrans : isTrans rel
    relIsTrans (a0 , a1) (b0 , b1) (c0 , c1) p0 p1 =
      inj-m+ {m = (b0 +ℕ b1)} ((b0 +ℕ b1) +ℕ (a0 +ℕ c1) ≡⟨ ℕ.+-assoc (b0 +ℕ b1) a0 c1  ⟩
            ((b0 +ℕ b1) +ℕ a0) +ℕ c1 ≡[ i ]⟨ ℕ.+-comm b0 b1 i +ℕ a0   +ℕ c1 ⟩
            ((b1 +ℕ b0) +ℕ a0) +ℕ c1 ≡[ i ]⟨ ℕ.+-comm (b1 +ℕ b0) a0 i +ℕ c1 ⟩
            (a0 +ℕ (b1 +ℕ b0)) +ℕ c1 ≡[ i ]⟨ ℕ.+-assoc a0 b1 b0 i    +ℕ c1 ⟩
            (a0 +ℕ b1) +ℕ b0 +ℕ c1 ≡⟨ sym (ℕ.+-assoc (a0 +ℕ b1) b0 c1) ⟩
            (a0 +ℕ b1) +ℕ (b0 +ℕ c1) ≡⟨ cong (λ p → p . fst +ℕ p .snd) (transport ΣPath≡PathΣ (p0 , p1))⟩
            (b0 +ℕ a1) +ℕ (c0 +ℕ b1) ≡⟨ sym (ℕ.+-assoc b0 a1 (c0 +ℕ b1))⟩
            b0 +ℕ (a1 +ℕ (c0 +ℕ b1)) ≡[ i ]⟨ b0 +ℕ (a1 +ℕ ℕ.+-comm c0 b1 i) ⟩
            b0 +ℕ (a1 +ℕ (b1 +ℕ c0)) ≡[ i ]⟨ b0 +ℕ ℕ.+-comm a1 (b1 +ℕ c0) i ⟩
            b0 +ℕ ((b1 +ℕ c0) +ℕ a1) ≡[ i ]⟨ b0 +ℕ ℕ.+-assoc b1 c0 a1 (~ i) ⟩
            b0 +ℕ (b1 +ℕ (c0 +ℕ a1)) ≡⟨ ℕ.+-assoc b0 b1 (c0 +ℕ a1)⟩
            (b0 +ℕ b1) +ℕ (c0 +ℕ a1) ∎ )

relIsProp : BinaryRelation.isPropValued rel
relIsProp a b x y = isSetℕ _ _ _ _

discreteℤ : Discrete ℤ
discreteℤ = discreteSetQuotients (discreteΣ discreteℕ λ _ → discreteℕ) relIsProp relIsEquiv (λ _ _ → discreteℕ _ _)

private
  _ : Dec→Bool (discreteℤ [ (3 , 5) ] [ (4 , 6) ]) ≡ true
  _ = refl

  _ : Dec→Bool (discreteℤ [ (3 , 5) ] [ (4 , 7) ]) ≡ false
  _ = refl

  _+ℕ'_ : (ℕ × ℕ) → (ℕ × ℕ) → (ℕ × ℕ)
  (n₁ , n₂) +ℕ' (m₁ , m₂) = (n₁ +ℕ m₁ , n₂ +ℕ m₂)

  [_-ℕ'_] : ℕ → ℕ → ℤ
  [ x -ℕ' y ] = [ x , y ]

  ℤ-cancelˡ : ∀ {a b} (c : ℕ) → [ (c ℕ.+ a) -ℕ' (c +ℕ b) ] ≡ [ a -ℕ' b ]
  ℤ-cancelˡ {a} {b} c = eq/ _ _ (tmp a b c)
    where tmp : ∀ a b c → rel (c +ℕ a , c +ℕ b) (a , b)
          tmp a b c =
            c +ℕ a +ℕ b
              ≡⟨ cong (λ x → x +ℕ b) (ℕ.+-comm c a) ⟩
            a +ℕ c +ℕ b
              ≡⟨ sym(ℕ.+-assoc a c b) ⟩
            a +ℕ (c +ℕ b) ∎

  ℤ-cancelʳ : ∀ {a b} (c : ℕ) → [ a +ℕ c -ℕ' b +ℕ c ] ≡ [ a -ℕ' b ]
  ℤ-cancelʳ {a} {b} c = eq/ _ _ (tmp a b c)
    where tmp : ∀ a b c → rel (a +ℕ c , b +ℕ c) (a , b)
          tmp a b c =
            a +ℕ c +ℕ b
              ≡⟨ sym(ℕ.+-assoc a c b) ⟩
            a +ℕ (c +ℕ b)
              ≡⟨ cong (λ x → a +ℕ x) (ℕ.+-comm c b) ⟩
            a +ℕ (b +ℕ c) ∎

  -- right congruence
  +-right-congruence : ∀ x y z → rel y z → rel (x +ℕ' y) (x +ℕ' z)
  +-right-congruence x y z  p =
    (fst x +ℕ fst y) +ℕ (snd x +ℕ snd z)
      ≡⟨ cong (λ x' → (fst x +ℕ fst y) +ℕ x') (ℕ.+-comm (snd x) (snd z)) ⟩
    (fst x +ℕ fst y) +ℕ (snd z +ℕ snd x)
      ≡⟨ ℕ.+-assoc (fst x +ℕ fst y) (snd z) (snd x) ⟩
    fst x +ℕ fst y +ℕ snd z +ℕ snd x
      ≡⟨ cong (λ x' → x' +ℕ snd x) (sym (ℕ.+-assoc (fst x) (fst y) (snd z))) ⟩
    fst x +ℕ (fst y +ℕ snd z) +ℕ snd x
      ≡⟨ cong (λ x' → fst x +ℕ x' +ℕ snd x) p ⟩
    fst x +ℕ (fst z +ℕ snd y) +ℕ snd x
      ≡⟨ cong (λ x' → x' +ℕ snd x) (ℕ.+-assoc (fst x) (fst z) (snd y)) ⟩
    fst x +ℕ fst z +ℕ snd y +ℕ snd x
      ≡⟨ sym (ℕ.+-assoc (fst x +ℕ fst z) (snd y) (snd x)) ⟩
    fst x +ℕ fst z +ℕ (snd y +ℕ snd x)
      ≡⟨ cong (λ x' → fst x +ℕ fst z +ℕ x') (ℕ.+-comm (snd y) (snd x)) ⟩
    fst x +ℕ fst z +ℕ (snd x +ℕ snd y) ∎

  +-left-congruence : ∀ x y z → rel x y → rel (x +ℕ' z) (y +ℕ' z)
  +-left-congruence x y z p =
    (fst x +ℕ fst z) +ℕ (snd y +ℕ snd z)
      ≡⟨ cong (λ x' → x' +ℕ (snd y +ℕ snd z)) (ℕ.+-comm (fst x) (fst z)) ⟩
    (fst z +ℕ fst x) +ℕ (snd y +ℕ snd z)
      ≡⟨ ℕ.+-assoc (fst z +ℕ fst x) (snd y) (snd z) ⟩
    fst z +ℕ fst x +ℕ snd y +ℕ snd z
      ≡⟨ cong (λ x' → x' +ℕ snd z) (sym (ℕ.+-assoc (fst z) (fst x) (snd y))) ⟩
    fst z +ℕ (fst x +ℕ snd y) +ℕ snd z
      ≡⟨ cong (λ x' → fst z +ℕ x' +ℕ snd z) p ⟩
    fst z +ℕ (fst y +ℕ snd x) +ℕ snd z
      ≡⟨ cong (λ x' → x' +ℕ snd z) (ℕ.+-assoc (fst z) (fst y) (snd x)) ⟩
    fst z +ℕ fst y +ℕ snd x +ℕ snd z
      ≡⟨ sym (ℕ.+-assoc (fst z +ℕ fst y) (snd x) (snd z)) ⟩
    fst z +ℕ fst y +ℕ (snd x +ℕ snd z)
      ≡⟨ cong (λ x' → x' +ℕ (snd x +ℕ snd z)) (ℕ.+-comm (fst z) (fst y)) ⟩
    fst y +ℕ fst z +ℕ (snd x +ℕ snd z) ∎

ℤ-isSet : isSet ℤ
ℤ-isSet m n p q = squash/ m n p q

_+_ : ℤ → ℤ → ℤ
_+_ = SetQuotients.rec2 ℤ-isSet (λ x y → [ x +ℕ' y ]) feql feqr
    where
      feql : (a b c : ℕ × ℕ) (r : rel a b) → [ a +ℕ' c ] ≡ [ b +ℕ' c ]
      feql a b c r = eq/ (a +ℕ' c) (b +ℕ' c) (+-left-congruence a b c r)
      feqr : (a b c : ℕ × ℕ) (r : rel b c) → [ a +ℕ' b ] ≡ [ a +ℕ' c ]
      feqr a b c r = eq/ (a +ℕ' b) (a +ℕ' c) (+-right-congruence a b c r)

private
  zero-identityˡ-lem : (a : ℕ)(b : ℤ) → (Σ (ℕ × ℕ) ( λ (c , d) → [ (c , d) ]  ≡ b )) → [ a , a ] + b ≡ b
  zero-identityˡ-lem a b ( (c , d) , p ) =
    [ a , a ] + b           ≡⟨ cong (λ x → [ a , a ] + x) (sym p) ⟩
    [ a , a ] + [ c , d ]   ≡⟨ ℤ-cancelˡ a ⟩
    [ c , d ]               ≡⟨ p ⟩
    b ∎

  zero-identityʳ-lem : (a : ℕ)(b : ℤ) → (Σ (ℕ × ℕ) ( λ (c , d) → [ (c , d) ]  ≡ b )) → b + [ a , a ] ≡ b
  zero-identityʳ-lem a b ( (c , d) , p ) =
    b + [ a , a ]           ≡⟨ cong (λ x → x + [ a , a ]) (sym(p)) ⟩
    [ c , d ] + [ a , a ]   ≡⟨ ℤ-cancelʳ a ⟩
    [ c , d ]               ≡⟨ p ⟩
    b ∎

zero-identityˡ : (a : ℕ) (b : ℤ) → [ a , a ] + b ≡ b
zero-identityˡ a b = PropositionalTruncation.rec (lem2 a b) (zero-identityˡ-lem a b) ([]surjective b)
                     where
                       lem2 : (a : ℕ) (b : ℤ) → isProp ([ a , a ] + b ≡ b)
                       lem2 a b = ℤ-isSet ([ a , a ] + b) b

zero-identityʳ : (a : ℕ)(b : ℤ) → b + [ a , a ] ≡ b
zero-identityʳ a b = PropositionalTruncation.rec (lem2 a b) (zero-identityʳ-lem a b) ([]surjective b)
                     where
                       lem2 : (a : ℕ) (b : ℤ) → isProp (b + [ a , a ] ≡ b)
                       lem2 a b = ℤ-isSet (b + [ a , a ]) b

-ℤ_  : ℤ → ℤ
-ℤ [ a ] = [ snd a , fst a ]
-ℤ eq/ a b r i = eq/ (snd a , fst a) (snd b , fst b) (tmp a b r) i
                  where
                    tmp : ∀ a b → fst a +ℕ snd b ≡ fst b +ℕ snd a → snd a +ℕ fst b ≡ snd b +ℕ fst a
                    tmp a b r =
                      snd a +ℕ fst b ≡⟨ ℕ.+-comm (snd a) (fst b) ⟩
                      fst b +ℕ snd a ≡⟨ sym r ⟩
                      fst a +ℕ snd b ≡⟨ ℕ.+-comm (fst a) (snd b) ⟩
                      snd b +ℕ fst a ∎
-ℤ squash/ a a₁ p q i i₁ = squash/ (-ℤ a) (-ℤ a₁) (cong (λ x → -ℤ x) p) (cong (λ x → -ℤ x) q) i i₁

private
  -ℤ-invʳ-lem : (b : ℤ) → (Σ (ℕ × ℕ) ( λ (c , d) → [ (c , d) ]  ≡ b )) → b + (-ℤ b) ≡ [ 0 , 0 ]
  -ℤ-invʳ-lem b ( (c , d) , p ) =
    b + (-ℤ b)
      ≡⟨ cong (λ x → x + (-ℤ b)) (sym p) ⟩
    [ c , d ] + (-ℤ b)
      ≡⟨ cong (λ x → [ c , d ] + (-ℤ x)) (sym p) ⟩
    [ c +ℕ d , d +ℕ c ]
      ≡⟨ cong (λ x → [ c +ℕ d , x ]) (ℕ.+-comm d c) ⟩
    [ c +ℕ d , c +ℕ d ]
      ≡⟨ ℤ-cancelˡ c ⟩
    [ d -ℕ' d ]
      ≡⟨ sym(ℤ-cancelʳ 0) ⟩
    [ d +ℕ 0 -ℕ' d +ℕ 0 ]
      ≡⟨ ℤ-cancelˡ d ⟩
    [ 0 , 0 ] ∎

  -ℤ-invˡ-lem : (b : ℤ) → (Σ (ℕ × ℕ) ( λ (c , d) → [ (c , d) ]  ≡ b )) → (-ℤ b) + b ≡ [ 0 , 0 ]
  -ℤ-invˡ-lem b ( (c , d) , p ) =
    (-ℤ b) + b
      ≡⟨ cong (λ x → (-ℤ b) + x) (sym p) ⟩
    (-ℤ b) + [ c , d ]
      ≡⟨ cong (λ x → (-ℤ x) + [ c , d ]) (sym p) ⟩
    [ d +ℕ c , c +ℕ d ]
      ≡⟨ cong (λ x → [ x , c +ℕ d ]) (ℕ.+-comm d c) ⟩
    [ c +ℕ d , c +ℕ d ]
      ≡⟨ ℤ-cancelˡ c ⟩
    [ d -ℕ' d ]
      ≡⟨ sym(ℤ-cancelʳ 0) ⟩
    [ d +ℕ 0 -ℕ' d +ℕ 0 ]
      ≡⟨ ℤ-cancelˡ d ⟩
    [ 0 , 0 ] ∎

_-_ : ℤ → ℤ → ℤ
a - b = a + (-ℤ b)

-ℤ-invʳ : (b : ℤ) → b + (-ℤ b) ≡ [ 0 , 0 ]
-ℤ-invʳ b = PropositionalTruncation.rec (lem2 b) (-ℤ-invʳ-lem b) ([]surjective b)
             where
               lem2 : (b : ℤ) → isProp (b + (-ℤ b) ≡ [ 0 , 0 ])
               lem2 b = ℤ-isSet (b + (-ℤ b)) [ 0 , 0 ]

-ℤ-invˡ : (b : ℤ) → (-ℤ b) + b ≡ [ 0 , 0 ]
-ℤ-invˡ b = PropositionalTruncation.rec (lem2 b) (-ℤ-invˡ-lem b) ([]surjective b)
             where
               lem2 : (b : ℤ) → isProp ((-ℤ b) + b ≡ [ 0 , 0 ])
               lem2 b = ℤ-isSet ((-ℤ b) + b) [ 0 , 0 ]

+ℤ-assoc : ∀ x y z → x + (y + z) ≡ (x + y) + z
+ℤ-assoc = elimProp3 (λ _ _ _ → ℤ-isSet _ _)
    (λ { (a , b) (c , d) (e , f) i → [ ℕ.+-assoc a c e i -ℕ' ℕ.+-assoc b d f i ] })

+ℤ-comm : ∀ x y → x + y ≡ y + x
+ℤ-comm = elimProp2 (λ _ _ → ℤ-isSet _ _)
         λ ( a , b ) ( c , d ) i → [ ℕ.+-comm a c i -ℕ'  ℕ.+-comm b d i ]


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

open import Categories.Category

module Categories.Diagram.Pullback.Properties {o ℓ e} (C : Category o ℓ e) where

open import Function using (_$_)

open import Categories.Category.BinaryProducts C
open import Categories.Category.Cartesian C
open import Categories.Diagram.Pullback C
open import Categories.Diagram.Equalizer C hiding (up-to-iso)
open import Categories.Object.Product C hiding (up-to-iso)
open import Categories.Object.Terminal C hiding (up-to-iso)
open import Categories.Morphism C
open import Categories.Morphism.Reasoning C
open import Categories.Category.Complete.Finitely using (FinitelyComplete)
open import Data.Product using (∃; _,_)

private
  open Category C
  variable
    X Y Z : Obj
    f g h i : X ⇒ Y
open HomReasoning
open Equiv

-- pullbacks of a monomorphism along itself give us the identity arrow.
pullback-self-mono : Mono f → IsPullback id id f f
pullback-self-mono mono = record
  { commute = refl
  ; universal = λ {X} {h₁} {h₂} eq → h₁
  ; unique = λ id∘i≈h₁ _ → ⟺ identityˡ ○ id∘i≈h₁
  ; p₁∘universal≈h₁ = identityˡ
  ; p₂∘universal≈h₂ = λ {X} {h₁} {h₂} {eq} → identityˡ ○ mono h₁ h₂ eq
  }

-- pullback from a terminal object is the same as a product
module _ (t : Terminal) where
  open Terminal t

  pullback-⊤⇒product : Pullback (! {X}) (! {Y}) → Product X Y
  pullback-⊤⇒product p = record
    { A×B      = P
    ; π₁       = p₁
    ; π₂       = p₂
    ; ⟨_,_⟩    = λ f g → universal (!-unique₂ {f = ! ∘ f} {g = ! ∘ g})
    ; project₁ = p₁∘universal≈h₁
    ; project₂ = p₂∘universal≈h₂
    ; unique   = λ eq eq′ → ⟺ (unique eq eq′)
    }
    where open Pullback p

  product⇒pullback-⊤ : Product X Y → Pullback (! {X}) (! {Y})
  product⇒pullback-⊤ p = record
    { p₁              = π₁
    ; p₂              = π₂
    ; isPullback = record
      { commute         = !-unique₂
      ; universal       = λ {_ f g} _ → ⟨ f , g ⟩
      ; unique          = λ eq eq′ → ⟺ (unique eq eq′)
      ; p₁∘universal≈h₁ = project₁
      ; p₂∘universal≈h₂ = project₂
      }
    }
    where open Product p

-- pullbacks respect _≈_
module _ (p : Pullback f g) where
  open Pullback p

  pullback-resp-≈ : f ≈ h → g ≈ i → Pullback h i
  pullback-resp-≈ eq eq′ = record
    { p₁              = p₁
    ; p₂              = p₂
    ; isPullback = record
      { commute         = ∘-resp-≈ˡ (⟺ eq) ○ commute ○ ∘-resp-≈ˡ eq′
      ; universal       = λ eq″ → universal (∘-resp-≈ˡ eq ○ eq″ ○ ∘-resp-≈ˡ (⟺ eq′))
      ; unique          = unique
      ; p₁∘universal≈h₁ = p₁∘universal≈h₁
      ; p₂∘universal≈h₂ = p₂∘universal≈h₂
      }
    }

-- Some facts about pulling back along identity
module _ (p : Pullback id f) where
  open Pullback p

  -- This is a more subtle way of saying that 'p₂ ≈ id', without involving heterogenous equality.
  pullback-identity : universal id-comm-sym ∘ p₂ ≈ id
  pullback-identity = begin
    universal Basic.id-comm-sym ∘ p₂ ≈⟨ unique ( pullˡ p₁∘universal≈h₁ ) (pullˡ p₂∘universal≈h₂)  ⟩
    universal eq                     ≈⟨ universal-resp-≈ (⟺ commute ○ identityˡ) identityˡ ⟩
    universal commute                ≈˘⟨ Pullback.id-unique p ⟩
    id ∎
    where
      eq : id ∘ f ∘ p₂ ≈ f ∘ id ∘ p₂
      eq = begin
        (id ∘ f ∘ p₂) ≈⟨ elimˡ Equiv.refl ⟩
        (f ∘ p₂)      ≈˘⟨ refl⟩∘⟨ identityˡ ⟩
        (f ∘ id ∘ p₂) ∎

-- pullbacks in Cartesian categories create equalizers
module _ (pullbacks : ∀ {X Y Z} (f : X ⇒ Z) (g : Y ⇒ Z) → Pullback f g)
         (cartesian : Cartesian) where
  open Cartesian cartesian
  open BinaryProducts products using (⟨_,_⟩; π₁; π₂; ⟨⟩-cong₂; ⟨⟩∘; project₁; project₂)

  pullback×cartesian⇒equalizer : Equalizer f g
  pullback×cartesian⇒equalizer {f = f} {g = g} = record
    { arr       = p.p₁
    ; isEqualizer = record
      { equality  = equality
      ; equalize  = λ {_ h} eq → p.universal $ begin
        ⟨ f , g ⟩ ∘ h               ≈⟨ ⟨⟩∘ ⟩
        ⟨ f ∘ h , g ∘ h ⟩           ≈˘⟨ ⟨⟩-cong₂ identityˡ (identityˡ ○ eq) ⟩
        ⟨ id ∘ f ∘ h , id ∘ f ∘ h ⟩ ≈˘⟨ ⟨⟩∘ ⟩
        ⟨ id , id ⟩ ∘ f ∘ h         ∎
      ; universal = ⟺ p.p₁∘universal≈h₁
      ; unique    = λ eq → p.unique (⟺ eq)
                                    (⟺ (pullˡ eq′) ○ ⟺ (∘-resp-≈ʳ eq))
      }
    }
    where p : Pullback ⟨ f , g ⟩ ⟨ id , id ⟩
          p = pullbacks _ _
          module p = Pullback p
          eq : ⟨ f ∘ p.p₁ , g ∘ p.p₁ ⟩ ≈ ⟨ p.p₂ , p.p₂ ⟩
          eq = begin
            ⟨ f ∘ p.p₁ , g ∘ p.p₁ ⟩      ≈˘⟨ ⟨⟩∘ ⟩
            ⟨ f , g ⟩ ∘ p.p₁             ≈⟨ p.commute ⟩
            ⟨ id , id ⟩ ∘ p.p₂           ≈⟨ ⟨⟩∘ ⟩
            ⟨ id ∘ p.p₂ , id ∘ p.p₂ ⟩    ≈⟨ ⟨⟩-cong₂ identityˡ identityˡ ⟩
            ⟨ p.p₂ , p.p₂ ⟩              ∎
          eq′ : f ∘ p.p₁ ≈  p.p₂
          eq′ = begin
            f ∘ p.p₁                     ≈˘⟨ project₁ ⟩
            π₁ ∘ ⟨ f ∘ p.p₁ , g ∘ p.p₁ ⟩ ≈⟨ refl⟩∘⟨ eq ⟩
            π₁ ∘ ⟨ p.p₂ , p.p₂ ⟩         ≈⟨ project₁ ⟩
            p.p₂                         ∎
          equality : f ∘ p.p₁ ≈ g ∘ p.p₁
          equality = begin
            f ∘ p.p₁                     ≈⟨ eq′ ⟩
            p.p₂                         ≈˘⟨ project₂ ⟩
            π₂ ∘ ⟨ p.p₂ , p.p₂ ⟩         ≈˘⟨ refl⟩∘⟨ eq ⟩
            π₂ ∘ ⟨ f ∘ p.p₁ , g ∘ p.p₁ ⟩ ≈⟨ project₂ ⟩
            g ∘ p.p₁                     ∎

-- all pullbacks and a terminal object make a category finitely complete
pullback-⊤⇒FinitelyComplete : (∀ {X Y Z} (f : X ⇒ Z) (g : Y ⇒ Z) → Pullback f g) → Terminal → FinitelyComplete C
pullback-⊤⇒FinitelyComplete pullbacks ⊤ = record
  { cartesian = cartesian
  ; equalizer = λ _ _ → pullback×cartesian⇒equalizer pullbacks cartesian
  }
    where
      open Category hiding (Obj)
      open Pullback
      open Terminal ⊤ hiding (⊤)

      _×_ : (A B : Obj) → Pullback (IsTerminal.! ⊤-is-terminal) (IsTerminal.! ⊤-is-terminal)
      A × B = pullbacks (IsTerminal.! ⊤-is-terminal) (IsTerminal.! ⊤-is-terminal)

      cartesian = record
        { terminal = ⊤
        ; products = record
            { product = λ {A B} → record
                { A×B = P {A}{_}{B} (A × B)
                ; π₁ = p₁ (A × B)
                ; π₂ = p₂ (A × B)
                ; ⟨_,_⟩ =  λ _ _ → universal (A × B) (!-unique₂)
                ; project₁ = p₁∘universal≈h₁ (A × B)
                ; project₂ = p₂∘universal≈h₂ (A × B)
                ; unique = λ eq₁ eq₂ → Equiv.sym C (unique (A × B) eq₁ eq₂)
                }
            }
        }

-- extra properties of "up-to-iso"
module IsoPb {X Y Z} {f : X ⇒ Z} {g : Y ⇒ Z} (pull₀ pull₁ : Pullback f g) where
  open Pullback using (P; p₁; p₂; p₁∘universal≈h₁; p₂∘universal≈h₂; commute; universal)

  P₀≅P₁ : P pull₀ ≅ P pull₁
  P₀≅P₁ = up-to-iso pull₀ pull₁

  P₀⇒P₁ : P pull₀ ⇒ P pull₁
  P₀⇒P₁ = _≅_.from P₀≅P₁

  p₁-≈ : p₁ pull₁ ∘ P₀⇒P₁ ≈ p₁ pull₀
  p₁-≈ = p₁∘universal≈h₁ pull₁ {eq = commute pull₀}

  p₂-≈ : p₂ pull₁ ∘ P₀⇒P₁ ≈ p₂ pull₀
  p₂-≈ = p₂∘universal≈h₂ pull₁ {eq = commute pull₀}


------------------------------------------------------------------------
-- Preimages
------------------------------------------------------------------------

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

-- Partly based on Voevodsky's work on so-called univalent
-- foundations.

open import Equality

module Preimage
  {reflexive} (eq : ∀ {a p} → Equality-with-J a p reflexive) where

open import Bijection eq as Bijection hiding (id; _∘_)
open Derived-definitions-and-properties eq
open import H-level eq as H-level
open import Injection eq hiding (id; _∘_)
open import Logical-equivalence using (module _⇔_)
open import Prelude
open import Surjection eq hiding (id; _∘_)

-- The preimage of y under f is denoted by f ⁻¹ y.

infix 5 _⁻¹_

_⁻¹_ : ∀ {a b} {A : Type a} {B : Type b} → (A → B) → B → Type (a ⊔ b)
f ⁻¹ y = ∃ λ x → f x ≡ y

-- Preimages under the identity function are contractible. (Note that
-- Singleton x is equal to id ⁻¹ x.)

id⁻¹-contractible : ∀ {a} {A : Type a} (y : A) →
                    Contractible (id ⁻¹ y)
id⁻¹-contractible = singleton-contractible

-- _⁻¹_ respects extensional equality of functions.

respects-extensional-equality :
  ∀ {a b} {A : Type a} {B : Type b} {f g : A → B} {y} →
  (∀ x → f x ≡ g x) → (f ⁻¹ y) ↔ (g ⁻¹ y)
respects-extensional-equality {f = f} {g} {y} f≡g = record
  { surjection = record
    { logical-equivalence = record
      { to   = to′
      ; from = from′
      }
    ; right-inverse-of = right-inverse-of
    }
  ; left-inverse-of = left-inverse-of
  }
  where
  to′ : f ⁻¹ y → g ⁻¹ y
  to′ (x , fx≡y) = x , (
    g x  ≡⟨ sym $ f≡g x ⟩
    f x  ≡⟨ fx≡y ⟩∎
    y    ∎)

  from′ : g ⁻¹ y → f ⁻¹ y
  from′ (x , gx≡y) = x , (
    f x  ≡⟨ f≡g x ⟩
    g x  ≡⟨ gx≡y ⟩∎
    y    ∎)

  abstract
    right-inverse-of : ∀ p → to′ (from′ p) ≡ p
    right-inverse-of = λ g⁻¹y → cong (_,_ (proj₁ g⁻¹y)) (
      let p = f≡g (proj₁ g⁻¹y); q = proj₂ g⁻¹y in
      trans (sym p) (trans p q)  ≡⟨ sym $ trans-assoc _ _ _ ⟩
      trans (trans (sym p) p) q  ≡⟨ cong (λ p → trans p q) (trans-symˡ _) ⟩
      trans (refl _) q           ≡⟨ trans-reflˡ _ ⟩∎
      q                          ∎)

    left-inverse-of : ∀ p → from′ (to′ p) ≡ p
    left-inverse-of = λ f⁻¹y → cong (_,_ (proj₁ f⁻¹y))
      let p = f≡g (proj₁ f⁻¹y); q = proj₂ f⁻¹y in
      trans p (trans (sym p) q)  ≡⟨ sym $ trans-assoc _ _ _ ⟩
      trans (trans p (sym p)) q  ≡⟨ cong (λ p → trans p q) (trans-symʳ _) ⟩
      trans (refl _) q           ≡⟨ trans-reflˡ _ ⟩∎
      q                          ∎

-- Split surjections can be lifted to preimages.

lift-surjection :
  ∀ {a b} {A : Type a} {B : Type b} (A↠B : A ↠ B) → let open _↠_ A↠B in
  ∀ {y} → (from ∘ to ⁻¹ y) ↠ (from ⁻¹ y)
lift-surjection {A = A} {B} A↠B {y} = record
  { logical-equivalence = record
    { to   = drop-∘
    ; from = add-∘
    }
  ; right-inverse-of = right-inv
  }
  where
  open _↠_ A↠B

  -- Given a preimage under (f ∘ g) a preimage under f can be
  -- constructed.

  drop-∘ : (from ∘ to) ⁻¹ y → from ⁻¹ y
  drop-∘ = Σ-map to id

  -- If f is a left inverse of g then the other direction also
  -- holds.

  abstract
    add-∘-lemma : ∀ {x} → from x ≡ y → from (to (from x)) ≡ y
    add-∘-lemma {x} from-x≡y =
      from (to (from x))  ≡⟨ cong from (right-inverse-of x) ⟩
      from x              ≡⟨ from-x≡y ⟩∎
      y                   ∎

  add-∘ : from ⁻¹ y → (from ∘ to) ⁻¹ y
  add-∘ (x , from-x≡y) = (from x , add-∘-lemma from-x≡y)

  abstract

    -- add-∘ is a right inverse of drop-∘.

    right-inv : (from⁻¹y : from ⁻¹ y) → drop-∘ (add-∘ from⁻¹y) ≡ from⁻¹y
    right-inv (x , from-x≡y) =
        (to (from x) , trans (cong from (right-inverse-of x)) from-x≡y)  ≡⟨ sym $ lemma (right-inverse-of x) from-x≡y ⟩∎
        (x           , from-x≡y)                                         ∎
      where
      lemma : ∀ {x y z} {f : B → A}
              (y≡x : y ≡ x) (p : f x ≡ z) →
              _≡_ {A = f ⁻¹ z} (x , p) (y , trans (cong f y≡x) p)
      lemma {z = z} {f} = elim
        (λ {y x} y≡x → (p : f x ≡ z) →
           _≡_ {A = f ⁻¹ z} (x , p) (y , trans (cong f y≡x) p))
        (λ x p → cong (_,_ x) (
           p                          ≡⟨ sym $ trans-reflˡ _ ⟩
           trans (refl (f x)) p       ≡⟨ cong (λ q → trans q p) (sym (cong-refl f)) ⟩∎
           trans (cong f (refl x)) p  ∎))

-- A consequence of the lemmas above is that preimages under a
-- bijection are contractible.

bijection⁻¹-contractible :
  ∀ {a b} {A : Type a} {B : Type b} (A↔B : A ↔ B) → let open _↔_ A↔B in
  ∀ y → Contractible (to ⁻¹ y)
bijection⁻¹-contractible A↔B =
  H-level.respects-surjection surj 0 ∘ id⁻¹-contractible
  where
  open _↔_ (Bijection.inverse A↔B)

  surj : ∀ {y} → id ⁻¹ y ↠ from ⁻¹ y
  surj {y} =
    id ⁻¹ y         ↠⟨ _↔_.surjection $
                         respects-extensional-equality (sym ∘ left-inverse-of) ⟩
    from ∘ to ⁻¹ y  ↠⟨ lift-surjection surjection ⟩□
    from ⁻¹ y       □

abstract

  -- Preimages under an injection into a set are propositional.

  injection⁻¹-propositional :
    ∀ {a b} {A : Type a} {B : Type b} (A↣B : A ↣ B) →
    let open _↣_ A↣B in
    Is-set B →
    ∀ y → Is-proposition (to ⁻¹ y)
  injection⁻¹-propositional A↣B B-set y (x₁ , tox₁≡y) (x₂ , tox₂≡y) =
    Σ-≡,≡→≡ (injective (to x₁  ≡⟨ tox₁≡y ⟩
                        y      ≡⟨ sym tox₂≡y ⟩∎
                        to x₂  ∎))
            (subst (λ x → to x ≡ y)
                   (injective (trans tox₁≡y (sym tox₂≡y)))
                   tox₁≡y                                   ≡⟨ B-set _ _ ⟩∎
             tox₂≡y                                         ∎)
    where
    open _↣_ A↣B


record _×_ (A B : Set) : Set where
  no-eta-equality
  constructor _,_
  field
    fst : A
    snd : B

open _×_

swap : ∀ {A B} → A × B → B × A
swap (x , y) = y , x

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

-- This should fail since we don't have eta for _×_.
fails : ∀ {A B} (p : A × B) → swap p ≡ (snd p , fst p)
fails p = refl