Library UniMath.CategoryTheory.Hyperdoctrines.FirstOrderHyperdoctrine

Require Import UniMath.Foundations.All.
Require Import UniMath.MoreFoundations.All.
Require Import UniMath.CategoryTheory.Core.Prelude.
Require Import UniMath.CategoryTheory.DisplayedCats.Core.
Require Import UniMath.CategoryTheory.DisplayedCats.Isos.
Require Import UniMath.CategoryTheory.DisplayedCats.Univalence.
Require Import UniMath.CategoryTheory.DisplayedCats.Functors.
Require Import UniMath.CategoryTheory.DisplayedCats.Fibrations.
Require Import UniMath.CategoryTheory.DisplayedCats.Fiber.
Require Import UniMath.CategoryTheory.DisplayedCats.Projection.
Require Import UniMath.CategoryTheory.DisplayedCats.Fiberwise.FiberwiseTerminal.
Require Import UniMath.CategoryTheory.DisplayedCats.Fiberwise.FiberwiseInitial.
Require Import UniMath.CategoryTheory.DisplayedCats.Fiberwise.FiberwiseProducts.
Require Import UniMath.CategoryTheory.DisplayedCats.Fiberwise.FiberwiseCoproducts.
Require Import UniMath.CategoryTheory.DisplayedCats.Fiberwise.FiberwiseCartesianClosed.
Require Import UniMath.CategoryTheory.DisplayedCats.Fiberwise.DependentProducts.
Require Import UniMath.CategoryTheory.DisplayedCats.Fiberwise.DependentSums.
Require Import UniMath.CategoryTheory.Hyperdoctrines.Hyperdoctrine.
Require Import UniMath.CategoryTheory.Limits.Initial.
Require Import UniMath.CategoryTheory.Limits.Terminal.
Require Import UniMath.CategoryTheory.Limits.BinProducts.
Require Import UniMath.CategoryTheory.Limits.BinCoproducts.
Require Import UniMath.CategoryTheory.Limits.Pullbacks.
Require Import UniMath.CategoryTheory.Limits.Preservation.

Require Import UniMath.Tactics.Simplify.
Require Import UniMath.Tactics.Utilities.
Require Import Ltac2.Ltac2.
Require Import Ltac2.Notations.

Local Open Scope cat.
Local Open Scope hd.

Set Default Show proof.
Mode "Classic".

1. First-order hyperdoctrines


Definition first_order_hyperdoctrine
  : UU
  := (H : hyperdoctrine),
     fiberwise_terminal (hyperdoctrine_cleaving H)
     ×
     fiberwise_initial (hyperdoctrine_cleaving H)
     ×
      (P : fiberwise_binproducts (hyperdoctrine_cleaving H)),
     fiberwise_bincoproducts (hyperdoctrine_cleaving H)
     ×
     fiberwise_exponentials P
     ×
     has_dependent_products (hyperdoctrine_cleaving H)
     ×
     has_dependent_sums (hyperdoctrine_cleaving H).

Coercion first_order_hyperdoctrine_to_hyperdoctrine
         (H : first_order_hyperdoctrine)
  : hyperdoctrine.
Show proof.
  exact (pr1 H).

Coercion first_order_hyperdoctrine_to_preorder_hyperdoctrine
         (H : first_order_hyperdoctrine)
  : first_order_preorder_hyperdoctrine.
Show proof.
  exact (_
         ,,
         pr12 H
         ,,
         pr122 H
         ,,
         pr1 (pr222 H)
         ,,
         pr12 (pr222 H)
         ,,
         pr122 (pr222 H)
         ,,
         pr1 (pr222 (pr222 H))
         ,,
         pr2 (pr222 (pr222 H))).

Definition univalent_first_order_hyperdoctrine
  : UU
  := (H : univalent_hyperdoctrine),
     fiberwise_terminal (hyperdoctrine_cleaving H)
     ×
     fiberwise_initial (hyperdoctrine_cleaving H)
     ×
      (P : fiberwise_binproducts (hyperdoctrine_cleaving H)),
     fiberwise_bincoproducts (hyperdoctrine_cleaving H)
     ×
     fiberwise_exponentials P
     ×
     has_dependent_products (hyperdoctrine_cleaving H)
     ×
     has_dependent_sums (hyperdoctrine_cleaving H).

Coercion univalent_first_order_hyperdoctrine_to_hyperdoctrine
         (H : univalent_first_order_hyperdoctrine)
  : univalent_hyperdoctrine.
Show proof.
  exact (pr1 H).

Coercion univalent_first_order_hyperdoctrine_to_first_order
         (H : univalent_first_order_hyperdoctrine)
  : first_order_hyperdoctrine.
Show proof.
  exact (_
         ,,
         pr12 H
         ,,
         pr122 H
         ,,
         pr1 (pr222 H)
         ,,
         pr12 (pr222 H)
         ,,
         pr122 (pr222 H)
         ,,
         pr1 (pr222 (pr222 H))
         ,,
         pr2 (pr222 (pr222 H))).

Definition make_first_order_preorder_hyperdoctrine
           (H : preorder_hyperdoctrine)
           (TH : fiberwise_terminal (hyperdoctrine_cleaving H))
           (IH : fiberwise_initial (hyperdoctrine_cleaving H))
           (PH : fiberwise_binproducts (hyperdoctrine_cleaving H))
           (CH : fiberwise_bincoproducts (hyperdoctrine_cleaving H))
           (IMPH : fiberwise_exponentials PH)
           (DPH : has_dependent_products (hyperdoctrine_cleaving H))
           (DSH : has_dependent_sums (hyperdoctrine_cleaving H))
  : first_order_preorder_hyperdoctrine
  := H
     ,,
     TH
     ,,
     IH
     ,,
     PH
     ,,
     CH
     ,,
     IMPH
     ,,
     DPH
     ,,
     DSH.

Definition make_first_order_hyperdoctrine
           (H : hyperdoctrine)
           (TH : fiberwise_terminal (hyperdoctrine_cleaving H))
           (IH : fiberwise_initial (hyperdoctrine_cleaving H))
           (PH : fiberwise_binproducts (hyperdoctrine_cleaving H))
           (CH : fiberwise_bincoproducts (hyperdoctrine_cleaving H))
           (IMPH : fiberwise_exponentials PH)
           (DPH : has_dependent_products (hyperdoctrine_cleaving H))
           (DSH : has_dependent_sums (hyperdoctrine_cleaving H))
  : first_order_hyperdoctrine
  := H
     ,,
     TH
     ,,
     IH
     ,,
     PH
     ,,
     CH
     ,,
     IMPH
     ,,
     DPH
     ,,
     DSH.

Definition make_univalent_first_order_hyperdoctrine
           (H : univalent_hyperdoctrine)
           (TH : fiberwise_terminal (hyperdoctrine_cleaving H))
           (IH : fiberwise_initial (hyperdoctrine_cleaving H))
           (PH : fiberwise_binproducts (hyperdoctrine_cleaving H))
           (CH : fiberwise_bincoproducts (hyperdoctrine_cleaving H))
           (IMPH : fiberwise_exponentials PH)
           (DPH : has_dependent_products (hyperdoctrine_cleaving H))
           (DSH : has_dependent_sums (hyperdoctrine_cleaving H))
  : univalent_first_order_hyperdoctrine
  := H
     ,,
     TH
     ,,
     IH
     ,,
     PH
     ,,
     CH
     ,,
     IMPH
     ,,
     DPH
     ,,
     DSH.

2. The truth formula

Definition first_order_hyperdoctrine_truth
           {H : first_order_hyperdoctrine}
           {Γ : ty H}
  : form Γ
  := terminal_obj_in_fib (pr12 H) Γ.

Notation "'⊤'" := first_order_hyperdoctrine_truth : hyperdoctrine.

Proposition truth_intro
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            (Δ : form Γ)
  : Δ .
Show proof.
  exact (TerminalArrow (terminal_in_fib (pr12 H) Γ) Δ).

Proposition truth_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ : ty H}
            (s : tm Γ₁ Γ₂)
  : [ s ] = .
Show proof.
  use (isotoid_disp _ (idpath _)).
  - apply is_univalent_disp_hyperdoctrine.
  - use z_iso_disp_from_z_iso_fiber.
    apply (preserves_terminal_to_z_iso _ (pr212 H _ _ s) _ _).

3. The falsity formula

Definition first_order_hyperdoctrine_false
           {H : first_order_hyperdoctrine}
           {Γ : ty H}
  : form Γ
  := initial_obj_in_fib (pr122 H) Γ.

Notation "'⊥'" := first_order_hyperdoctrine_false : hyperdoctrine.

Proposition false_elim
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            (Δ φ : form Γ)
            (p : Δ )
  : Δ φ.
Show proof.
  use (hyperdoctrine_cut p).
  exact (InitialArrow (initial_in_fib (pr122 H) Γ) φ).

Proposition false_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ : ty H}
            (s : Γ₁ --> Γ₂)
  : [ s ] = .
Show proof.
  use (isotoid_disp _ (idpath _)).
  - apply is_univalent_disp_hyperdoctrine.
  - use z_iso_disp_from_z_iso_fiber.
    apply (preserves_initial_to_z_iso _ (pr2 (pr122 H) _ _ s) _ _).

4. Conjunction

Definition first_order_hyperdoctrine_conj
           {H : first_order_hyperdoctrine}
           {Γ : ty H}
           (φ ψ : form Γ)
  : form Γ
  := BinProductObject _ (binprod_in_fib (pr1 (pr222 H)) φ ψ).

Notation "φ ∧ ψ" := (first_order_hyperdoctrine_conj φ ψ) : hyperdoctrine.

Proposition conj_intro
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ)
            (q : Δ ψ)
  : Δ φ ψ.
Show proof.
  exact (BinProductArrow _ (binprod_in_fib (pr1 (pr222 H)) φ ψ) p q).

Proposition conj_elim_left
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ ψ)
  : Δ φ.
Show proof.
  use (hyperdoctrine_cut p).
  apply (BinProductPr1 _ (binprod_in_fib (pr1 (pr222 H)) φ ψ)).

Proposition conj_elim_right
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ ψ)
  : Δ ψ.
Show proof.
  use (hyperdoctrine_cut p).
  apply (BinProductPr2 _ (binprod_in_fib (pr1 (pr222 H)) φ ψ)).

Proposition conj_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ : ty H}
            (s : Γ₁ --> Γ₂)
            (φ ψ : form Γ₂)
  : (φ ψ) [ s ] = (φ [ s ] ψ [ s ]).
Show proof.

5. Weakening of hypotheses

The presence of conjunction allows us to add assumptions to the formula context. We can derive the proper weaking rules for that.
Proposition weaken_left
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ₁ φ : form Γ}
            (p : Δ₁ φ)
            (Δ₂ : form Γ)
  : Δ₁ Δ₂ φ.
Show proof.
  use (hyperdoctrine_cut _ p).
  apply (BinProductPr1 _ (binprod_in_fib (pr1 (pr222 H)) Δ₁ Δ₂)).

Proposition weaken_right
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ₂ φ : form Γ}
            (p : Δ₂ φ)
            (Δ₁ : form Γ)
  : Δ₁ Δ₂ φ.
Show proof.
  use (hyperdoctrine_cut _ p).
  apply (BinProductPr2 _ (binprod_in_fib (pr1 (pr222 H)) Δ₁ Δ₂)).

Proposition weaken_cut
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ)
            (q : Δ φ ψ)
  : Δ ψ.
Show proof.
  refine (hyperdoctrine_cut _ q).
  use (BinProductArrow _ (binprod_in_fib _ Δ φ)).
  - apply hyperdoctrine_hyp.
  - exact p.

Proposition weaken_to_empty
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ : form Γ}
            (p : φ)
  : Δ φ.
Show proof.
  refine (hyperdoctrine_cut _ p).
  use truth_intro.

Proposition hyp_sym
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ Δ' φ : form Γ}
            (p : Δ Δ' φ)
  : Δ' Δ φ.
Show proof.
  refine (hyperdoctrine_cut _ p).
  use conj_intro.
  - use weaken_right.
    apply hyperdoctrine_hyp.
  - use weaken_left.
    apply hyperdoctrine_hyp.

Proposition hyp_ltrans
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ Δ' Δ'' φ : form Γ}
            (p : Δ (Δ' Δ'') φ)
  : (Δ Δ') Δ'' φ.
Show proof.
  refine (hyperdoctrine_cut _ p).
  use conj_intro.
  - do 2 use weaken_left.
    apply hyperdoctrine_hyp.
  - use conj_intro.
    + use weaken_left.
      use weaken_right.
      apply hyperdoctrine_hyp.
    + use weaken_right.
      apply hyperdoctrine_hyp.

Proposition hyp_rtrans
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ Δ' Δ'' φ : form Γ}
            (p : (Δ Δ') Δ'' φ)
  : Δ (Δ' Δ'') φ.
Show proof.
  refine (hyperdoctrine_cut _ p).
  use conj_intro.
  - use conj_intro.
    + use weaken_left.
      apply hyperdoctrine_hyp.
    + use weaken_right.
      use weaken_left.
      apply hyperdoctrine_hyp.
  - do 2 use weaken_right.
    apply hyperdoctrine_hyp.

Proposition conj_assoc
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            (φ φ φ : form Γ)
  : ((φ φ) φ) = (φ (φ φ)).
Show proof.
  use hyperdoctrine_formula_eq.
  - apply hyp_ltrans.
    apply hyperdoctrine_hyp.
  - apply hyp_rtrans.
    apply hyperdoctrine_hyp.

6. Disjunction

Definition first_order_hyperdoctrine_disj
           {H : first_order_hyperdoctrine}
           {Γ : ty H}
           (φ ψ : form Γ)
  : form Γ
  := BinCoproductObject (bincoprod_in_fib (pr12 (pr222 H)) φ ψ).

Notation "φ ∨ ψ" := (first_order_hyperdoctrine_disj φ ψ) : hyperdoctrine.

Proposition disj_intro_left
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ)
  : Δ φ ψ.
Show proof.
  use (hyperdoctrine_cut p).
  apply (BinCoproductIn1 (bincoprod_in_fib (pr12 (pr222 H)) φ ψ)).

Proposition disj_intro_right
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ ψ)
  : Δ φ ψ.
Show proof.
  use (hyperdoctrine_cut p).
  apply (BinCoproductIn2 (bincoprod_in_fib (pr12 (pr222 H)) φ ψ)).

Proposition distributivity
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            (φ ψ χ : form Γ)
  : φ (ψ χ) (φ ψ) (φ χ).
Show proof.
  exact (pr1 (distributivity_fiberwise_exponentials
                (pr12 (pr222 H))
                (pr122 (pr222 H))
                φ ψ χ)).

Proposition disj_elim
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ χ : form Γ}
            (p : Δ φ ψ)
            (q : Δ φ χ)
            (r : Δ ψ χ)
  : Δ χ.
Show proof.
  refine (hyperdoctrine_cut
            _
            (BinCoproductArrow (bincoprod_in_fib (pr12 (pr222 H)) (Δ φ) (Δ ψ)) q r)).
  use (weaken_cut p).
  apply distributivity.

Proposition disj_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ : ty H}
            (s : Γ₁ --> Γ₂)
            (φ ψ : form Γ₂)
  : (φ ψ) [ s ] = (φ [ s ] ψ [ s ]).
Show proof.

7. Implication

Definition first_order_hyperdoctrine_impl
           {H : first_order_hyperdoctrine}
           {Γ : ty H}
           (φ ψ : form Γ)
  : form Γ
  := exp_in_fib (pr122 (pr222 H)) φ ψ.

Notation "φ ⇒ ψ" := (first_order_hyperdoctrine_impl φ ψ) : hyperdoctrine.

Proposition impl_intro
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ ψ)
  : Δ φ ψ.
Show proof.
  refine (lam_in_fib (pr122 (pr222 H)) _).
  use (hyperdoctrine_cut _ p).
  apply hyp_sym.
  apply hyperdoctrine_hyp.

Proposition impl_elim
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ)
            (q : Δ φ ψ)
  : Δ ψ.
Show proof.
  use (hyperdoctrine_cut _ (eval_in_fib (pr122 (pr222 H)) φ ψ)).
  use conj_intro.
  - exact p.
  - exact q.

Proposition impl_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ : ty H}
            (s : Γ₁ --> Γ₂)
            (φ ψ : form Γ₂)
  : (φ ψ) [ s ] = (φ [ s ] ψ [ s ]).
Show proof.
  use (isotoid_disp _ (idpath _)).
  - apply is_univalent_disp_hyperdoctrine.
  - use z_iso_disp_from_z_iso_fiber.
    exact (_ ,, preserves_exponentials_in_fib (pr122 (pr222 H)) s φ ψ).

Proposition impl_id
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            (φ : form Γ)
  : φ φ.
Show proof.
  use impl_intro.
  use weaken_right.
  apply hyperdoctrine_hyp.

8. Universal quantification

Definition first_order_hyperdoctrine_forall
           {H : first_order_hyperdoctrine}
           {Γ A : ty H}
           (φ : form (Γ ×h A))
  : form Γ
  := dep_prod (pr1 (pr222 (pr222 H))) (π (identity (Γ ×h A))) φ.

Notation "'∀h' φ" := (first_order_hyperdoctrine_forall φ) (at level 10)
    : hyperdoctrine.

Proposition forall_intro
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ : form Γ}
            {φ : form (Γ ×h A)}
            (p : Δ [ π (tm_var _) ] φ)
  : Δ h φ.
Show proof.
  use (hyperdoctrine_cut
         (dep_prod_unit (pr1 (pr222 (pr222 H))) (π (identity (Γ ×h A))) Δ)).
  use dep_prod_mor.
  cbn.
  exact p.

Proposition forall_elim
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ : form Γ}
            {φ : form (Γ ×h A)}
            (p : Δ h φ)
            (t : tm Γ A)
  : Δ φ [ tm_var _ , t ].
Show proof.
  use (hyperdoctrine_cut p).
  assert ((h φ)[ π (tm_var (Γ ×h A)) ] φ) as r.
  {
    exact (dep_prod_counit (pr1 (pr222 (pr222 H))) (π (identity (Γ ×h A))) φ).
  }
  pose (hyperdoctrine_proof_subst tm_var Γ , t r) as r'.
  rewrite hyperdoctrine_comp_subst in r'.
  rewrite hyperdoctrine_pair_comp_pr1 in r'.
  rewrite hyperdoctrine_id_subst in r'.
  exact r'.

Proposition quantifier_subst_pb_eq
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ : ty H}
            (A : ty H)
            (s : tm Γ₁ Γ₂)
  : s [ π (tm_var (Γ₁ ×h A)) ]tm
    =
    (π (tm_var _)) [ s [ π (tm_var _) ]tm , π (tm_var _) ]tm.
Show proof.
  rewrite hyperdoctrine_pair_comp_pr1.
  apply idpath.

Definition quantifier_subst_pb
           {H : first_order_hyperdoctrine}
           {Γ₁ Γ₂ : ty H}
           (A : ty H)
           (s : tm Γ₁ Γ₂)
  : isPullback (!(quantifier_subst_pb_eq A s)).
Show proof.
  intros Γ' t t' p.
  use iscontraprop1.
  - abstract
      (use invproofirrelevance ;
       intros ζ₁ ζ₂ ;
       use subtypePath ; [ intro ; apply isapropdirprod ; apply homset_property | ] ;
       refine (hyperdoctrine_pair_eta _ @ _ @ !(hyperdoctrine_pair_eta _)) ;
       pose (pr22 ζ₁) as q ;
       rewrite hyperdoctrine_pr1_comp in q ;
       rewrite id_right in q ;
       rewrite q ; clear q ;
       pose (pr22 ζ₂) as q ;
       rewrite hyperdoctrine_pr1_comp in q ;
       rewrite id_right in q ;
       rewrite q ; clear q ;
       apply maponpaths ;
       pose (maponpaths (λ z, π z) (pr12 ζ₁)) as q ; cbn in q ;
       rewrite (hyperdoctrine_pair_comp (H := H)) in q ;
       unfold tm_subst in q ;
       rewrite !assoc in q ;
       rewrite (hyperdoctrine_pr1_comp (H := H)) in q ;
       rewrite hyperdoctrine_pr2_comp in q ;
       rewrite !id_right in q ;
       rewrite hyperdoctrine_pair_pr2 in q ;
       rewrite q ;
       clear q ;
       pose (maponpaths (λ z, π z) (pr12 ζ₂)) as q ; cbn in q ;
       rewrite (hyperdoctrine_pair_comp (H := H)) in q ;
       unfold tm_subst in q ;
       rewrite !assoc in q ;
       rewrite (hyperdoctrine_pr1_comp (H := H)) in q ;
       rewrite hyperdoctrine_pr2_comp in q ;
       rewrite !id_right in q ;
       rewrite hyperdoctrine_pair_pr2 in q ;
       rewrite q ;
       clear q ;
       apply idpath).
  - refine ( t' , t · π (tm_var _) ,, _ ,, _).
    + abstract
        (rewrite hyperdoctrine_pair_comp ;
         unfold tm_subst ;
         rewrite !assoc ;
         rewrite hyperdoctrine_pair_comp_pr1' ;
         rewrite hyperdoctrine_pair_comp_pr2' ;
         rewrite <- p ;
         rewrite hyperdoctrine_pr1_comp ;
         rewrite hyperdoctrine_pr2_comp ;
         rewrite !id_right ;
         rewrite <- hyperdoctrine_pair_eta ;
         apply idpath).
    + abstract
        (rewrite hyperdoctrine_pair_comp_pr1' ;
         apply idpath).

Proposition forall_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ A : ty H}
            (s : tm Γ₁ Γ₂)
            (φ : form (Γ₂ ×h A))
  : (h φ) [ s ]
    =
    (h (φ [ s [ π (tm_var _) ]tm , π (tm_var _) ])).
Show proof.
  pose (pr21 (pr222 (pr222 H)) _ _ _ _ _ _ _ _ _ (quantifier_subst_pb A s) φ) as p.
  pose (f := (_ ,, p) : z_iso _ _).
  use hyperdoctrine_formula_eq.
  - apply f.
  - exact (inv_from_z_iso f).

9. Existential quantification

Definition first_order_hyperdoctrine_exists
           {H : first_order_hyperdoctrine}
           {Γ A : ty H}
           (φ : form (Γ ×h A))
  : form Γ
  := dep_sum (pr2 (pr222 (pr222 H))) (π (identity (Γ ×h A))) φ.

Notation "'∃h' φ" := (first_order_hyperdoctrine_exists φ) (at level 10)
    : hyperdoctrine.

Proposition exists_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ A : ty H}
            (s : tm Γ₁ Γ₂)
            (φ : form (Γ₂ ×h A))
  : (h φ) [ s ]
    =
    h (φ [ s [ π (tm_var _) ]tm , π (tm_var _) ]).
Show proof.
  pose (pr22 (pr222 (pr222 H)) _ _ _ _ _ _ _ _ _ (quantifier_subst_pb A s) φ) as p.
  pose (f := (_ ,, p) : z_iso _ _).
  use hyperdoctrine_formula_eq.
  - exact (inv_from_z_iso f).
  - apply f.

Proposition exists_intro
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ : form Γ}
            {φ : form (Γ ×h A)}
            {t : tm Γ A}
            (p : Δ φ [ tm_var _ , t ])
  : Δ h φ.
Show proof.
  use (hyperdoctrine_cut p).
  assert (h φ) [ π (tm_var (Γ ×h A)) ]) as r.
  {
    exact (dep_sum_unit (pr2 (pr222 (pr222 H))) (π (identity (Γ ×h A))) φ).
  }
  pose (hyperdoctrine_proof_subst tm_var Γ , t r) as r'.
  rewrite hyperdoctrine_comp_subst in r'.
  rewrite hyperdoctrine_pair_comp_pr1 in r'.
  rewrite hyperdoctrine_id_subst in r'.
  exact r'.

Proposition exists_elim_empty
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ ψ : form Γ}
            {φ : form (Γ ×h A)}
            (p : Δ h φ)
            (q : φ ψ [ π (tm_var (Γ ×h A)) ])
  : Δ ψ.
Show proof.
  assert (h (ψ [ π (tm_var (Γ ×h A)) ]) ψ) as r.
  {
    exact (dep_sum_counit (pr2 (pr222 (pr222 H))) (π (identity (Γ ×h A))) ψ).
  }
  use (hyperdoctrine_cut _ r).
  use (hyperdoctrine_cut p).
  use dep_sum_mor.
  exact q.

Proposition frobenius_reciprocity
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            (φ : form (Γ ×h A))
            (Δ : form Γ)
  : Δ (h φ) (h (Δ [ π (tm_var (Γ ×h A)) ] φ)).
Show proof.
  enough (h φ Δ h (Δ [ π (tm_var (Γ ×h A)) ] φ)) as r₁.
  {
    assert (Δ h φ Δ h (Δ [ π (tm_var (Γ ×h A)) ] φ)) as r₂.
    {
      use weaken_right.
      exact r₁.
    }
    refine (impl_elim _ r₂).
    use weaken_left.
    apply hyperdoctrine_hyp.
  }
  use (exists_elim_empty (hyperdoctrine_hyp _)).
  rewrite impl_subst.
  use impl_intro.
  rewrite exists_subst.
  use exists_intro.
  - exact (π (tm_var _)).
  - rewrite hyperdoctrine_comp_subst.
    rewrite hyperdoctrine_pair_subst.
    rewrite tm_subst_comp.
    rewrite hyperdoctrine_pair_comp_pr1.
    rewrite hyperdoctrine_pair_comp_pr2.
    rewrite tm_subst_var.
    rewrite conj_subst.
    rewrite hyperdoctrine_comp_subst.
    rewrite hyperdoctrine_pair_comp_pr1.
    rewrite <- hyperdoctrine_pair_eta.
    rewrite hyperdoctrine_id_subst.
    use hyp_sym.
    apply hyperdoctrine_hyp.

Proposition exists_elim
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ ψ : form Γ}
            {φ : form (Γ ×h A)}
            (p : Δ h φ)
            (q : Δ [ π (tm_var (Γ ×h A)) ] φ ψ [ π (tm_var (Γ ×h A)) ])
  : Δ ψ.
Show proof.
  assert (h (ψ [ π (tm_var (Γ ×h A)) ]) ψ) as r.
  {
    exact (dep_sum_counit (pr2 (pr222 (pr222 H))) (π (identity (Γ ×h A))) ψ).
  }
  use (hyperdoctrine_cut _ r).
  use (weaken_cut p).
  use (hyperdoctrine_cut (frobenius_reciprocity _ _)).
  use dep_sum_mor.
  exact q.

10. Equality

Definition first_order_hyperdoctrine_equal
           {H : first_order_hyperdoctrine}
           {Γ A : ty H}
           (t₁ t₂ : tm Γ A)
  : form Γ
  := (dep_sum (pr2 (pr222 (pr222 H))) (Δ_{A}) ) [ t₁ , t₂ ].

Notation "t₁ ≡ t₂" := (first_order_hyperdoctrine_equal t₁ t₂)
    : hyperdoctrine.

Proposition equal_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ A : ty H}
            (s : Γ₁ --> Γ₂)
            (t₁ t₂ : tm Γ₂ A)
  : (t₁ t₂) [ s ] = (t₁ [ s ]tm t₂ [ s ]tm).
Show proof.

Proposition hyperdoctrine_refl'
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            (t : tm Γ A)
  : t t.
Show proof.
  assert ( (dep_sum (pr2 (pr222 (pr222 H))) (Δ_{A}) ) [ Δ_{A} ]) as p.
  {
    exact (dep_sum_unit (pr2 (pr222 (pr222 H))) (Δ_{A}) ).
  }
  pose (hyperdoctrine_proof_subst t p) as q.
  rewrite truth_subst in q.
  rewrite hyperdoctrine_comp_subst in q.
  rewrite hyperdoctrine_diag_subst in q.
  exact q.

Proposition hyperdoctrine_refl
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            (Δ : form Γ)
            (t : tm Γ A)
  : Δ t t.
Show proof.

Proposition hyperdoctrine_refl_eq
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            (Δ : form Γ)
            {t₁ t₂ : tm Γ A}
            (p : t₁ = t₂)
  : Δ t₁ t₂.
Show proof.
  induction p.
  apply hyperdoctrine_refl.

Proposition hyperdoctrine_eq_elim_help
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            (φ : form (A ×h A))
            (p : φ [ Δ_{A} ])
            (t₁ t₂ : tm Γ A)
  : t₁ t₂ φ [ t₁ , t₂ ].
Show proof.
  pose (dep_sum_counit (pr2 (pr222 (pr222 H))) (Δ_{A}) φ) as r.
  pose (hyperdoctrine_proof_subst t₁ , t₂ r) as r'.
  use (hyperdoctrine_cut _ r').
  unfold first_order_hyperdoctrine_equal.
  use hyperdoctrine_proof_subst.
  use dep_sum_mor.
  exact p.

Proposition hyperdoctrine_eq_elim_help_con'
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            (φ : form ((A ×h A) ×h Γ))
            (p : φ [ Δ_{A} [ π (tm_var _) ]tm , π (tm_var _) ])
            (t₁ t₂ : tm Γ A)
  : t₁ t₂ φ [ t₁ , t₂ , tm_var _ ].
Show proof.
  assert ( (h φ) [ Δ_{ A } ]) as q.
  {
    rewrite forall_subst.
    use forall_intro.
    rewrite truth_subst.
    rewrite hyperdoctrine_diag_subst.
    rewrite hyperdoctrine_diag_subst in p.
    exact p.
  }
  refine (hyperdoctrine_cut (hyperdoctrine_eq_elim_help (h φ) q t₁ t₂) _).
  rewrite forall_subst.
  use (hyperdoctrine_cut (forall_elim (hyperdoctrine_hyp _) (tm_var _))).
  rewrite hyperdoctrine_comp_subst.
  rewrite hyperdoctrine_pair_subst.
  rewrite tm_subst_comp.
  rewrite hyperdoctrine_pair_comp_pr1.
  rewrite hyperdoctrine_pair_comp_pr2.
  rewrite tm_subst_var.
  apply hyperdoctrine_hyp.

Definition hyperdoctrine_eq_elim_help_con_sub
           {H : first_order_hyperdoctrine}
           (Γ A : ty H)
  : tm ((A ×h A) ×h Γ) (Γ ×h (A ×h A)).
Show proof.
  refine _ , _ , _ .
  - exact (π (tm_var _)).
  - exact ((π (tm_var _)) [ π (tm_var _) ]tm).
  - exact ((π (tm_var _)) [ π (tm_var _) ]tm).

Proposition hyperdoctrine_eq_elim_help_con
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            (φ : form (Γ ×h (A ×h A)))
            (p : φ [ π (tm_var _) , Δ_{A} [ π (tm_var _) ]tm ])
            (t₁ t₂ : tm Γ A)
  : t₁ t₂ φ [ tm_var _ , t₁ , t₂ ].
Show proof.
  pose (s := hyperdoctrine_eq_elim_help_con_sub Γ A).
  assert ( φ [s] [ Δ_{ A } [ π (tm_var _) ]tm , π (tm_var _) ])
    as q.
  {
    unfold s, hyperdoctrine_eq_elim_help_con_sub.
    rewrite hyperdoctrine_comp_subst.
    rewrite hyperdoctrine_diag_subst.
    rewrite !hyperdoctrine_pair_subst.
    rewrite hyperdoctrine_pair_comp_pr2.
    rewrite !tm_subst_comp.
    rewrite !hyperdoctrine_pair_comp_pr1.
    rewrite hyperdoctrine_pair_comp_pr2.
    rewrite hyperdoctrine_diag_subst in p.
    pose (hyperdoctrine_proof_subst π (tm_var _) , π (tm_var _) p) as p'.
    rewrite truth_subst in p'.
    refine (hyperdoctrine_cut p' _).
    rewrite hyperdoctrine_comp_subst.
    rewrite !hyperdoctrine_pair_subst.
    rewrite hyperdoctrine_pair_comp_pr2.
    rewrite !hyperdoctrine_pair_comp_pr1.
    apply hyperdoctrine_hyp.
  }
  use (hyperdoctrine_cut (hyperdoctrine_eq_elim_help_con'[ s ]) q t₁ t₂)).
  unfold s, hyperdoctrine_eq_elim_help_con_sub.
  rewrite hyperdoctrine_comp_subst.
  rewrite !hyperdoctrine_pair_subst.
  rewrite hyperdoctrine_pair_comp_pr2.
  rewrite !tm_subst_comp.
  rewrite !hyperdoctrine_pair_comp_pr1.
  rewrite hyperdoctrine_pair_comp_pr2.
  apply hyperdoctrine_hyp.

Proposition hyperdoctrine_eq_elim
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ : form Γ}
            (φ : form (Γ ×h A))
            {t₁ t₂ : tm Γ A}
            (p : Δ t₁ t₂)
            (q : Δ φ [ tm_var _ , t₁ ])
  : Δ φ [ tm_var _ , t₂ ].
Show proof.
  pose[ π (tm_var _) , (π (tm_var _)) [ π (tm_var _) ]tm ]
        
        φ [ π (tm_var _) , (π (tm_var _)) [ π (tm_var _) ]tm ])
    as ψ.
  assert ( ψ [ π (tm_var (Γ ×h A)), Δ_{ A } [ π (tm_var (Γ ×h A)) ]tm ])
    as r.
  {
    unfold ψ.
    rewrite impl_subst.
    rewrite !hyperdoctrine_comp_subst.
    rewrite !hyperdoctrine_pair_subst.
    rewrite !tm_subst_comp.
    rewrite hyperdoctrine_pair_comp_pr1.
    rewrite hyperdoctrine_pair_comp_pr2.
    rewrite <- !tm_subst_comp.
    unfold hyperdoctrine_diag.
    rewrite hyperdoctrine_pair_comp_pr1.
    rewrite hyperdoctrine_pair_comp_pr2.
    rewrite !var_tm_subst.
    apply impl_id.
  }
  pose proof (hyperdoctrine_eq_elim_help_con ψ r t₁ t₂) as r'.
  unfold ψ in r'.
  rewrite impl_subst in r'.
  rewrite !hyperdoctrine_comp_subst in r'.
  rewrite !hyperdoctrine_pair_subst in r'.
  rewrite !tm_subst_comp in r'.
  rewrite hyperdoctrine_pair_comp_pr1 in r'.
  rewrite hyperdoctrine_pair_comp_pr2 in r'.
  rewrite hyperdoctrine_pair_comp_pr1 in r'.
  rewrite hyperdoctrine_pair_comp_pr2 in r'.
  use (impl_elim q).
  use (hyperdoctrine_cut p).
  exact r'.

11. Derived rules for equality

Proposition hyperdoctrine_eq_sym
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ : form Γ}
            {t₁ t₂ : tm Γ A}
            (p : Δ t₁ t₂)
  : Δ t₂ t₁.
Show proof.
  pose (φ := (π (tm_var _) t₁ [ π (tm_var _) ]tm) : form (Γ ×h A)).
  assert (Δ φ [ tm_var Γ , t₁ ]) as q.
  {
    unfold φ.
    rewrite equal_subst.
    rewrite !tm_subst_comp.
    rewrite hyperdoctrine_pair_comp_pr1.
    rewrite tm_subst_var.
    rewrite hyperdoctrine_pair_comp_pr2.
    apply hyperdoctrine_refl.
  }
  pose (hyperdoctrine_eq_elim φ p q) as r.
  unfold φ in r.
  rewrite equal_subst in r.
  rewrite !tm_subst_comp in r.
  rewrite hyperdoctrine_pair_comp_pr1 in r.
  rewrite tm_subst_var in r.
  rewrite hyperdoctrine_pair_comp_pr2 in r.
  exact r.

Proposition hyperdoctrine_eq_trans
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ : form Γ}
            {t₁ t₂ t₃ : tm Γ A}
            (p : Δ t₁ t₂)
            (p' : Δ t₂ t₃)
  : Δ t₁ t₃.
Show proof.
  pose (φ := (π (tm_var _) t₃ [ π (tm_var _) ]tm) : form (Γ ×h A)).
  assert (Δ φ [ tm_var Γ , t₂ ]) as q.
  {
    unfold φ.
    rewrite equal_subst.
    rewrite !tm_subst_comp.
    rewrite hyperdoctrine_pair_comp_pr1.
    rewrite tm_subst_var.
    rewrite hyperdoctrine_pair_comp_pr2.
    exact p'.
  }
  pose (hyperdoctrine_eq_elim φ (hyperdoctrine_eq_sym p) q) as r.
  unfold φ in r.
  rewrite equal_subst in r.
  rewrite !tm_subst_comp in r.
  rewrite hyperdoctrine_pair_comp_pr1 in r.
  rewrite tm_subst_var in r.
  rewrite hyperdoctrine_pair_comp_pr2 in r.
  exact r.

Proposition hyperdoctrine_eq_transportf
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ : form Γ}
            {t₁ t₂ : tm Γ A}
            (φ : form A)
            (p : Δ t₁ t₂)
            (q : Δ φ [ t₁ ])
  : Δ φ [ t₂ ].
Show proof.
  assert (Δ t₁ t₂ φ [ t₁ ]) as r.
  {
    exact (conj_intro p q).
  }
  refine (hyperdoctrine_cut r _).
  pose (hyperdoctrine_eq_elim
          (φ [ π (tm_var _) ])
          (weaken_left (hyperdoctrine_hyp _) _)
          (weaken_right (hyperdoctrine_hyp _) (t₁ t₂)))
    as h.
  rewrite !hyperdoctrine_comp_subst in h.
  rewrite !hyperdoctrine_pr2_subst in h.
  rewrite !var_tm_subst in h.
  rewrite !hyperdoctrine_pair_pr2 in h.
  exact h.

Proposition hyperdoctrine_eq_transportb
            {H : first_order_hyperdoctrine}
            {Γ A : ty H}
            {Δ : form Γ}
            {t₁ t₂ : tm Γ A}
            (φ : form A)
            (p : Δ t₁ t₂)
            (q : Δ φ [ t₂ ])
  : Δ φ [ t₁ ].
Show proof.
  use (hyperdoctrine_eq_transportf _ _ q).
  use hyperdoctrine_eq_sym.
  exact p.

Proposition hyperdoctrine_unit_eq_prf
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            (t : tm Γ 𝟙)
            (Δ : form Γ)
  : Δ t !!.
Show proof.

Proposition hyperdoctrine_unit_tm_eq
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            (t t' : tm Γ 𝟙)
            (Δ : form Γ)
  : Δ t t'.
Show proof.

Proposition hyperdoctrine_eq_pr1
            {H : first_order_hyperdoctrine}
            {Γ A B : ty H}
            {Δ : form Γ}
            {t t' : tm Γ (A ×h B)}
            (p : Δ t t')
  : Δ π t π t'.
Show proof.
  pose (φ := ((π t) [ π (tm_var _) ]tm π (tm_var (Γ ×h A ×h B)))) : form (Γ ×h A ×h B)).
  assert (Δ φ [ tm_var Γ , t ]) as r.
  {
    unfold φ.
    rewrite equal_subst.
    rewrite !tm_subst_comp.
    rewrite !hyperdoctrine_pr1_subst.
    rewrite var_tm_subst.
    rewrite hyperdoctrine_pair_pr1.
    rewrite tm_subst_var.
    rewrite !hyperdoctrine_pr2_subst.
    rewrite var_tm_subst.
    rewrite hyperdoctrine_pair_pr2.
    apply hyperdoctrine_refl.
  }
  pose (hyperdoctrine_eq_elim φ p r) as r'.
  unfold φ in r'.
  rewrite equal_subst in r'.
  rewrite !tm_subst_comp in r'.
  rewrite !hyperdoctrine_pr1_subst in r'.
  rewrite var_tm_subst in r'.
  rewrite hyperdoctrine_pair_pr1 in r'.
  rewrite tm_subst_var in r'.
  rewrite !hyperdoctrine_pr2_subst in r'.
  rewrite var_tm_subst in r'.
  rewrite hyperdoctrine_pair_pr2 in r'.
  exact r'.

Proposition hyperdoctrine_eq_pr2
            {H : first_order_hyperdoctrine}
            {Γ A B : ty H}
            {Δ : form Γ}
            {t t' : tm Γ (A ×h B)}
            (p : Δ t t')
  : Δ π t π t'.
Show proof.
  pose (φ := ((π t) [ π (tm_var _) ]tm π (tm_var (Γ ×h A ×h B)))) : form (Γ ×h A ×h B)).
  assert (Δ φ [ tm_var Γ , t ]) as r.
  {
    unfold φ.
    rewrite equal_subst.
    rewrite !tm_subst_comp.
    rewrite !hyperdoctrine_pr1_subst.
    rewrite var_tm_subst.
    rewrite hyperdoctrine_pair_pr1.
    rewrite tm_subst_var.
    rewrite !hyperdoctrine_pr2_subst.
    rewrite var_tm_subst.
    rewrite hyperdoctrine_pair_pr2.
    apply hyperdoctrine_refl.
  }
  pose (hyperdoctrine_eq_elim φ p r) as r'.
  unfold φ in r'.
  rewrite equal_subst in r'.
  rewrite !tm_subst_comp in r'.
  rewrite !hyperdoctrine_pr1_subst in r'.
  rewrite var_tm_subst in r'.
  rewrite hyperdoctrine_pair_pr1 in r'.
  rewrite tm_subst_var in r'.
  rewrite !hyperdoctrine_pr2_subst in r'.
  rewrite var_tm_subst in r'.
  rewrite hyperdoctrine_pair_pr2 in r'.
  exact r'.

Proposition hyperdoctrine_eq_pair_left
            {H : first_order_hyperdoctrine}
            {Γ A B : ty H}
            {Δ : form Γ}
            {s₁ s₂ : tm Γ A}
            (p : Δ s₁ s₂)
            (t : tm Γ B)
  : Δ s₁ , t s₂ , t .
Show proof.
  pose (φ := ( s₁ [ π (tm_var _) ]tm , t [ π (tm_var _) ]tm
              
               π (tm_var _) , t [ π (tm_var _) ]tm )
          : form (Γ ×h A)).
  assert (Δ φ [ tm_var Γ , s₁ ]) as r.
  {
    unfold φ.
    rewrite equal_subst.
    rewrite !hyperdoctrine_pair_subst.
    rewrite !tm_subst_comp.
    rewrite hyperdoctrine_pr1_subst.
    rewrite hyperdoctrine_pr2_subst.
    rewrite !var_tm_subst.
    rewrite hyperdoctrine_pair_pr1.
    rewrite hyperdoctrine_pair_pr2.
    rewrite !tm_subst_var.
    apply hyperdoctrine_refl.
  }
  pose (hyperdoctrine_eq_elim φ p r) as r'.
  unfold φ in r'.
  rewrite equal_subst in r'.
  rewrite !hyperdoctrine_pair_subst in r'.
  rewrite !tm_subst_comp in r'.
  rewrite hyperdoctrine_pr1_subst in r'.
  rewrite hyperdoctrine_pr2_subst in r'.
  rewrite !var_tm_subst in r'.
  rewrite hyperdoctrine_pair_pr1 in r'.
  rewrite hyperdoctrine_pair_pr2 in r'.
  rewrite !tm_subst_var in r'.
  exact r'.

Proposition hyperdoctrine_eq_pair_right
            {H : first_order_hyperdoctrine}
            {Γ A B : ty H}
            {Δ : form Γ}
            (s : tm Γ A)
            {t₁ t₂ : tm Γ B}
            (p : Δ t₁ t₂)
  : Δ s , t₁ s , t₂ .
Show proof.
  pose (φ := ( s [ π (tm_var _) ]tm , t₁ [ π (tm_var _) ]tm
              
               s [ π (tm_var _) ]tm , π (tm_var _) )
          : form (Γ ×h B)).
  assert (Δ φ [ tm_var Γ , t₁ ]) as r.
  {
    unfold φ.
    rewrite equal_subst.
    rewrite !hyperdoctrine_pair_subst.
    rewrite !tm_subst_comp.
    rewrite hyperdoctrine_pr1_subst.
    rewrite hyperdoctrine_pr2_subst.
    rewrite !var_tm_subst.
    rewrite hyperdoctrine_pair_pr1.
    rewrite hyperdoctrine_pair_pr2.
    rewrite !tm_subst_var.
    apply hyperdoctrine_refl.
  }
  pose (hyperdoctrine_eq_elim φ p r) as r'.
  unfold φ in r'.
  rewrite equal_subst in r'.
  rewrite !hyperdoctrine_pair_subst in r'.
  rewrite !tm_subst_comp in r'.
  rewrite hyperdoctrine_pr1_subst in r'.
  rewrite hyperdoctrine_pr2_subst in r'.
  rewrite !var_tm_subst in r'.
  rewrite hyperdoctrine_pair_pr1 in r'.
  rewrite hyperdoctrine_pair_pr2 in r'.
  rewrite !tm_subst_var in r'.
  exact r'.

Proposition hyperdoctrine_eq_pair_eq
            {H : first_order_hyperdoctrine}
            {Γ A B : ty H}
            {Δ : form Γ}
            {s₁ s₂ : tm Γ A}
            (p : Δ s₁ s₂)
            {t₁ t₂ : tm Γ B}
            (q : Δ t₁ t₂)
  : Δ s₁ , t₁ s₂ , t₂ .
Show proof.
  exact (hyperdoctrine_eq_trans
           (hyperdoctrine_eq_pair_left p _)
           (hyperdoctrine_eq_pair_right _ q)).

Proposition hyperdoctrine_eq_prod_eq
            {H : first_order_hyperdoctrine}
            {Γ A B : ty H}
            {Δ : form Γ}
            {t₁ t₂ : tm Γ (A ×h B)}
            (p : Δ π t₁ π t₂)
            (q : Δ π t₁ π t₂)
  : Δ t₁ t₂.
Show proof.
  rewrite (hyperdoctrine_pair_eta t₁).
  rewrite (hyperdoctrine_pair_eta t₂).
  use hyperdoctrine_eq_pair_eq.
  - exact p.
  - exact q.

Proposition hyperdoctrine_subst_eq
            {H : first_order_hyperdoctrine}
            {Γ Γ' B : ty H}
            {Δ : form _}
            {s₁ s₂ : tm Γ Γ'}
            (p : Δ s₁ s₂)
            (t : tm Γ' B)
  : Δ t [ s₁ ]tm t [ s₂ ]tm.
Show proof.
  pose (φ := t [ s₁ [ π (tm_var _) ]tm ]tm t [ π (tm_var _) ]tm).
  assert (Δ φ [ tm_var Γ, s₁ ]) as q.
  {
    unfold φ.
    rewrite equal_subst.
    rewrite !tm_subst_comp.
    rewrite hyperdoctrine_pr1_subst.
    rewrite hyperdoctrine_pr2_subst.
    rewrite var_tm_subst.
    rewrite hyperdoctrine_pair_pr1.
    rewrite hyperdoctrine_pair_pr2.
    rewrite tm_subst_var.
    apply hyperdoctrine_refl.
  }
  pose (r := hyperdoctrine_eq_elim φ p q).
  unfold φ in r.
  rewrite equal_subst in r.
  rewrite !tm_subst_comp in r.
  rewrite hyperdoctrine_pr1_subst in r.
  rewrite hyperdoctrine_pr2_subst in r.
  rewrite var_tm_subst in r.
  rewrite hyperdoctrine_pair_pr1 in r.
  rewrite hyperdoctrine_pair_pr2 in r.
  rewrite tm_subst_var in r.
  exact r.

12. Derived connectives

12.1. Bi-implication

Definition first_order_hyperdoctrine_iff
           {H : first_order_hyperdoctrine}
           {Γ : ty H}
           (φ ψ : form Γ)
  : form Γ
  := (φ ψ) (ψ φ).

Notation "φ ⇔ ψ" := (first_order_hyperdoctrine_iff φ ψ) : hyperdoctrine.

Proposition iff_intro
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ ψ)
            (q : Δ ψ φ)
  : Δ φ ψ.
Show proof.
  use conj_intro.
  - use impl_intro.
    exact p.
  - use impl_intro.
    exact q.

Proposition iff_elim_left
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ ψ)
            (q : Δ φ)
  : Δ ψ.
Show proof.
  use (impl_elim q).
  exact (conj_elim_left p).

Proposition iff_elim_right
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ ψ)
            (q : Δ ψ)
  : Δ φ.
Show proof.
  use (impl_elim q).
  exact (conj_elim_right p).

Proposition iff_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ : ty H}
            (s : tm Γ₁ Γ₂)
            (φ ψ : form Γ₂)
  : ((φ ψ) [ s ])
    =
    (φ [ s ] ψ [ s ]).
Show proof.
  unfold first_order_hyperdoctrine_iff.
  rewrite conj_subst.
  rewrite !impl_subst.
  apply idpath.

Proposition iff_refl
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            (Δ φ : form Γ)
  : Δ φ φ.
Show proof.
  use iff_intro.
  - use weaken_right.
    apply hyperdoctrine_hyp.
  - use weaken_right.
    apply hyperdoctrine_hyp.

Proposition iff_sym
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ ψ)
  : Δ ψ φ.
Show proof.
  use iff_intro.
  - use (iff_elim_right (weaken_left p _)).
    use weaken_right.
    apply hyperdoctrine_hyp.
  - use (iff_elim_left (weaken_left p _)).
    use weaken_right.
    apply hyperdoctrine_hyp.

Proposition iff_trans
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ χ : form Γ}
            (p : Δ φ ψ)
            (q : Δ ψ χ)
  : Δ φ χ.
Show proof.
  use iff_intro.
  - use (iff_elim_left (weaken_left q _)).
    use (iff_elim_left (weaken_left p _)).
    use weaken_right.
    apply hyperdoctrine_hyp.
  - use (iff_elim_right (weaken_left p _)).
    use (iff_elim_right (weaken_left q _)).
    use weaken_right.
    apply hyperdoctrine_hyp.

Proposition iff_true_true
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ)
            (q : Δ ψ)
  : Δ φ ψ.
Show proof.
  use iff_intro.
  - use weaken_left.
    exact q.
  - use weaken_left.
    exact p.

12.2. Negation

Definition first_order_hyperdoctrine_neg
           {H : first_order_hyperdoctrine}
           {Γ : ty H}
           (φ : form Γ)
  : form Γ
  := φ .

Notation "¬ φ" := (first_order_hyperdoctrine_neg φ) : hyperdoctrine.

Proposition neg_intro
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ )
  : Δ ¬ φ.
Show proof.
  use impl_intro.
  exact p.

Proposition neg_elim
            {H : first_order_hyperdoctrine}
            {Γ : ty H}
            {Δ φ ψ : form Γ}
            (p : Δ φ)
            (q : Δ ¬ φ)
  : Δ ψ.
Show proof.
  use false_elim.
  use (impl_elim p).
  exact q.

Proposition neg_subst
            {H : first_order_hyperdoctrine}
            {Γ₁ Γ₂ : ty H}
            (s : tm Γ₁ Γ₂)
            (φ : form Γ₂)
  : (¬ φ) [ s ]
    =
    ¬ (φ [ s ]).
Show proof.
  unfold first_order_hyperdoctrine_neg.
  rewrite impl_subst.
  rewrite false_subst.
  apply idpath.

13. A tactic for simplifying goals in the internal language of first-order hyperdoctrines

The tactic `hypersimplify` helps proving statements in the internal language of a hyperdoctrine. Such goals are of the shape `Δ ⊢ φ`. The tactic simplifies `Δ` and φ` by propagating the substitutions and by putting all terms that occur in either `Δ` or φ` in normal form. The tactic can print the rewrite statements that can replace it, by invoking `hypersimplify true` in Ltac2 or `hypersimplifyp` in Ltac1 (see the `Notation` commands further down).
The tactic uses identities with two different levels:
  • The rewrites with level 0 are those that express how substitution acts on formulas, and are used to propagate substitutions in `Δ` and `φ`.
  • The rewrites with level 1 are all rewrite rules on terms in the language, used to normalize all terms in `Δ` and `φ`.
In some cases, it is a bit faster to use `hypersimplify_form` to simplify the formula and delay using the full `hypersimplify` until it is necessary. The reason why this helps, is because one might have made the goal smaller and removed some unnecessary assumptions using weakening. This is demonstrated in `PERs.v` in the proof of eq_per_axioms.
The tactic can be extended with new traversals and rewrites. For example, handling of `~` is added in `PERs.v`.

Set Default Show proof.
Mode "Ltac2".

Ltac2 mutable hypertraversals () : t_traversal list := [].

Ltac2 Set hypertraversals as traversals := fun _ =>
  (make_traversal (fun () => match! goal with | [|- (_ [ ?b]tm) = _ ] => '(λ x, x [$b ]tm) end) "" " [ _ ]tm") ::
  (make_traversal (fun () => match! goal with | [|- (?a [ _]tm) = _ ] => '(λ x, $a[ x ]tm) end) "_ [" "]tm") ::
  (make_traversal (fun () => match! goal with | [|- (_ [ ?b] ) = _ ] => '(λ x, x [$b ] ) end) " " " [ _ ]" ) ::
  (make_traversal (fun () => match! goal with | [|- (?a [ _] ) = _ ] => '(λ x, $a[ x ] ) end) "_ [" "]" ) ::
  (make_traversal (fun () => match! goal with | [|- (_ ?b ) = _ ] => '(λ x, x $b ) end) "" " ∧ _" ) ::
  (make_traversal (fun () => match! goal with | [|- (?a _ ) = _ ] => '(λ x, $a x ) end) "_ ∧ " "" ) ::
  (make_traversal (fun () => match! goal with | [|- (_ ?b ) = _ ] => '(λ x, x $b ) end) "" " ∨ _" ) ::
  (make_traversal (fun () => match! goal with | [|- (?a _ ) = _ ] => '(λ x, $a x ) end) "_ ∨ " "" ) ::
  (make_traversal (fun () => match! goal with | [|- (_ ?b ) = _ ] => '(λ x, x $b ) end) "" " ⇒ _" ) ::
  (make_traversal (fun () => match! goal with | [|- (?a _ ) = _ ] => '(λ x, $a x ) end) "_ ⇒ " "" ) ::
  (make_traversal (fun () => match! goal with | [|- (_ ?b ) = _ ] => '(λ x, x $b ) end) "" " ≡ _" ) ::
  (make_traversal (fun () => match! goal with | [|- (?a _ ) = _ ] => '(λ x, $a x ) end) "_ ≡ " "" ) ::
  (make_traversal (fun () => match! goal with | [|- (_ ?b ) = _ ] => '(λ x, x $b ) end) "" " ⇔ _" ) ::
  (make_traversal (fun () => match! goal with | [|- (?a _ ) = _ ] => '(λ x, $a x ) end) "_ ⇔ " "" ) ::
  (make_traversal (fun () => match! goal with | [|- (_ ,?b ) = _ ] => '(λ x, x ,$b ) end) "⟨" " , _⟩" ) ::
  (make_traversal (fun () => match! goal with | [|- (?a, _ ) = _ ] => '(λ x, $a, x ) end) "⟨_ , " "⟩" ) ::
  (make_traversal (fun () => match! goal with | [|- (h _ ) = _ ] => '(λ x, h x ) end) "∀h " "" ) ::
  (make_traversal (fun () => match! goal with | [|- (h _ ) = _ ] => '(λ x, h x ) end) "∃h " "" ) ::
  (make_traversal (fun () => match! goal with | [|- (¬ _ ) = _ ] => '(λ x, ¬ x ) end) "¬ " "" ) ::
  (make_traversal (fun () => match! goal with | [|- (π _ ) = _ ] => '(λ x, π x ) end) "π₁ " "" ) ::
  (make_traversal (fun () => match! goal with | [|- (π _ ) = _ ] => '(λ x, π x ) end) "π₂ " "" ) ::
  traversals ().

Ltac2 mutable hyperrewrites () : (int * t_rewrite) list := [].

Ltac2 Set hyperrewrites as rewrites := fun () =>
  (0, (pn:([_]), (fun () => '(truth_subst _ )), "truth_subst _" )) ::
  (0, (pn:([_]), (fun () => '(false_subst _ )), "false_subst _" )) ::
  (0, (pn:((_ _)[_]), (fun () => '(conj_subst _ _ _ )), "conj_subst _ _ _" )) ::
  (0, (pn:((_ _)[_]), (fun () => '(disj_subst _ _ _ )), "disj_subst _ _ _" )) ::
  (0, (pn:((_ _)[_]), (fun () => '(impl_subst _ _ _ )), "impl_subst _ _ _" )) ::
  (0, (pn:((_ _)[_]), (fun () => '(iff_subst _ _ _ )), "iff_subst _ _ _" )) ::
  (0, (pn:((_ _)[_]), (fun () => '(equal_subst _ _ _ )), "equal_subst _ _ _" )) ::
  (0, (pn:((h _)[_]), (fun () => '(forall_subst _ _ )), "forall_subst _ _" )) ::
  (0, (pn:((h _)[_]), (fun () => '(exists_subst _ _ )), "exists_subst _ _" )) ::
  (0, (pn:((¬ _)[_]), (fun () => '(neg_subst _ _ )), "neg_subst _ _" )) ::
  (0, (pn:((_[_])[_]), (fun () => '(hyperdoctrine_comp_subst _ _ _)), "hyperdoctrine_comp_subst _ _ _")) ::
  (0, (pn:(_[tm_var _]), (fun () => '(hyperdoctrine_id_subst _ )), "hyperdoctrine_id_subst _" )) ::
  (1, (pn:((π _)[_]tm), (fun () => '(hyperdoctrine_pr1_subst _ _ )), "hyperdoctrine_pr1_subst _ _" )) ::
  (1, (pn:((π _)[_]tm), (fun () => '(hyperdoctrine_pr2_subst _ _ )), "hyperdoctrine_pr2_subst _ _" )) ::
  (1, (pn:(_, _[_]tm), (fun () => '(hyperdoctrine_pair_subst _ _ _)), "hyperdoctrine_pair_subst _ _ _")) ::
  (1, (pn:((tm_var _)[_]tm), (fun () => '(var_tm_subst _ )), "var_tm_subst _" )) ::
  (1, (pn:((_ [_]tm)[_]tm), (fun () => '(tm_subst_comp _ _ _ )), "tm_subst_comp _ _ _" )) ::
  (1, (pn:(_[tm_var _]tm), (fun () => '(tm_subst_var _ )), "tm_subst_var _" )) ::
  (1, (pn:(π_, _), (fun () => '(hyperdoctrine_pair_pr1 _ _ )), "hyperdoctrine_pair_pr1 _ _" )) ::
  (1, (pn:(π_, _), (fun () => '(hyperdoctrine_pair_pr2 _ _ )), "hyperdoctrine_pair_pr2 _ _" )) ::
  (1, (pn:(!![_]tm), (fun () => '(hyperdoctrine_unit_tm_subst _ )), "hyperdoctrine_unit_tm_subst _ ")) ::
  rewrites ().

Ltac2 hypertop_traversals (ltac2 : bool) (print: bool) : ((unit -> unit) * navigation) list :=
  ((fun () => match! goal with
    | [ |- _ = _ ] => refine '(!(_ @ !_))
    end), {
      left := [""];
      right := [""];
      preinpostfix := (String.concat "" ["refine "; (if ltac2 then "'" else ""); "(_ @ !maponpaths "], " ", ").");
      print := print
  }) :: ((fun () => match! goal with
    | [ |- _ = _ ] => refine '(_ @ _)
    end), {
      left := [""];
      right := [""];
      preinpostfix := (String.concat "" ["refine "; (if ltac2 then "'" else ""); "(maponpaths "], " ", " @ _).");
      print := print
  }) :: ((fun () => match! goal with
    | [ |- ?a _ ] => refine '(transportb (λ x, $a x) _ _); cbv beta
    end), {
      left := ["_ ⊢ "];
      right := [""];
      preinpostfix := (String.concat "" ["refine "; (if ltac2 then "'" else ""); "(transportb "], " ", " _).");
      print := print
  }) :: ((fun () => match! goal with
    | [ |- _ ?b ] => refine '(transportb (λ x, x $b) _ _); cbv beta
    end), {
      left := [""];
      right := [" ⊢ _"];
      preinpostfix := (String.concat "" ["refine "; (if ltac2 then "'" else ""); "(transportb "], " ", " _).");
      print := print
  }) :: [].

Ltac2 hypersimplify0 (ltac2 : bool option) (print: bool option) : int option -> unit :=
  simplify
  (List.rev (hypertraversals ()))
  (List.rev (hyperrewrites ()))
  (List.rev (hypertop_traversals (Option.default true ltac2) (Option.default false print))).

Ltac2 Notation "hypersimplify" print(opt(next)) n(opt(next)) := hypersimplify0 (Some true) print (n).

Set Default Proof Mode "Classic".

Tactic Notation "hypersimplify" := ltac2:(hypersimplify0 (Some false) (Some false) None).
Tactic Notation "hypersimplifyp" := ltac2:(hypersimplify0 (Some false) (Some true) None).
Tactic Notation "hypersimplify_form" := ltac2:(hypersimplify0 (Some false) (Some false) (Some 0)).
Tactic Notation "hypersimplifyp_form" := ltac2:(hypersimplify0 (Some false) (Some true) (Some 0)).

Ltac simplify_form_step :=
  rewrite ?truth_subst,
    ?false_subst,
    ?conj_subst,
    ?disj_subst,
    ?impl_subst,
    ?forall_subst,
    ?exists_subst,
    ?equal_subst,
    ?iff_subst,
    ?neg_subst,
    ?hyperdoctrine_comp_subst,
    ?hyperdoctrine_id_subst.

Ltac simplify_form :=
  repeat (progress simplify_form_step).

Ltac simplify_term_step :=
  rewrite ?hyperdoctrine_pr1_subst,
    ?hyperdoctrine_pr2_subst,
    ?hyperdoctrine_pair_subst,
    ?var_tm_subst,
    ?tm_subst_comp,
    ?tm_subst_var,
    ?hyperdoctrine_pair_pr1,
    ?hyperdoctrine_pair_pr2,
    ?hyperdoctrine_unit_tm_subst.

Ltac simplify_term :=
  repeat (progress simplify_term_step).

Ltac simplify := simplify_form ; simplify_term.