Library HoTT.Homotopy.Join.TriJoin
Induction and recursion principles for the triple join
Section TriJoinStructure.
Context {A B C : Type}.
Definition TriJoin := Join A (Join B C).
Definition join1 : A → TriJoin := joinl.
Definition join2 : B → TriJoin := fun b ⇒ (joinr (joinl b)).
Definition join3 : C → TriJoin := fun c ⇒ (joinr (joinr c)).
Definition join12 : ∀ a b, join1 a = join2 b := fun a b ⇒ jglue a (joinl b).
Definition join13 : ∀ a c, join1 a = join3 c := fun a c ⇒ jglue a (joinr c).
Definition join23 : ∀ b c, join2 b = join3 c := fun b c ⇒ ap joinr (jglue b c).
Definition join123 : ∀ a b c, join12 a b @ join23 b c = join13 a c := fun a b c ⇒ triangle_v a (jglue b c).
End TriJoinStructure.
Arguments TriJoin A B C : clear implicits.
Definition ap_triangle {X Y} (f : X → Y)
{a b c : X} {ab : a = b} {bc : b = c} {ac : a = c} (abc : ab @ bc = ac)
: ap f ab @ ap f bc = ap f ac
:= (ap_pp f ab bc)^ @ ap (ap f) abc.
{a b c : X} {ab : a = b} {bc : b = c} {ac : a = c} (abc : ab @ bc = ac)
: ap f ab @ ap f bc = ap f ac
:= (ap_pp f ab bc)^ @ ap (ap f) abc.
This general result abstracts away the situation where J is TriJoin A B C, a is joinl a' for some a', jr is joinr : Join B C → J, jg is fun w ⇒ jglue a' w, and p is jglue b c. By working in this generality, we can do induction on p. This also allows us to inline the proof of triangle_v.
Definition ap_trijoin_general {J W P : Type} (f : J → P)
(a : J) (jr : W → J) (jg : ∀ w, a = jr w)
{b c : W} (p : b = c)
: ap f (jg b) @ ap f (ap jr p) = ap f (jg c).
Proof.
apply ap_triangle.
induction p.
apply concat_p1.
Defined.
(a : J) (jr : W → J) (jg : ∀ w, a = jr w)
{b c : W} (p : b = c)
: ap f (jg b) @ ap f (ap jr p) = ap f (jg c).
Proof.
apply ap_triangle.
induction p.
apply concat_p1.
Defined.
Functions send the canonical triangles in triple joins to triangles.
Definition ap_trijoin {A B C P : Type} (f : TriJoin A B C → P)
(a : A) (b : B) (c : C)
: ap f (join12 a b) @ ap f (join23 b c) = ap f (join13 a c).
Proof.
nrapply ap_trijoin_general.
Defined.
Definition ap_trijoin_general_transport {J W P : Type} (f : J → P)
(a : J) (jr : W → J) (jg : ∀ w, a = jr w)
{b c : W} (p : b = c)
: ap_trijoin_general f a jr jg p
= (1 @@ ap_compose _ f _)^ @ (transport_paths_Fr _ _)^ @ apD (fun x ⇒ ap f (jg x)) p.
Proof.
induction p.
unfold ap_trijoin_general; simpl.
induction (jg b).
reflexivity.
Defined.
Definition ap_trijoin_transport {A B C P : Type} (f : TriJoin A B C → P)
(a : A) (b : B) (c : C)
: ap_trijoin f a b c
= (1 @@ ap_compose _ f _)^ @ (transport_paths_Fr _ _)^ @ apD (fun x ⇒ ap f (jglue a x)) (jglue b c).
Proof.
nrapply ap_trijoin_general_transport.
Defined.
Definition ap_trijoin_general_V {J W P : Type} (f : J → P)
(a : J) (jr : W → J) (jg : ∀ w, a = jr w)
{b c : W} (p : b = c)
: ap_trijoin_general f a jr jg p^
= (1 @@ (ap (ap f) (ap_V jr p) @ ap_V f _)) @ moveR_pV _ _ _ (ap_trijoin_general f a jr jg p)^.
Proof.
induction p.
unfold ap_trijoin_general; cbn.
by induction (jg b).
Defined.
Definition ap_trijoin_V {A B C P : Type} (f : TriJoin A B C → P)
(a : A) (b : B) (c : C)
: ap_triangle f (triangle_v a (jglue b c)^)
= (1 @@ (ap (ap f) (ap_V joinr _) @ ap_V f _)) @ moveR_pV _ _ _ (ap_trijoin f a b c)^.
Proof.
nrapply ap_trijoin_general_V.
Defined.
(a : A) (b : B) (c : C)
: ap f (join12 a b) @ ap f (join23 b c) = ap f (join13 a c).
Proof.
nrapply ap_trijoin_general.
Defined.
Definition ap_trijoin_general_transport {J W P : Type} (f : J → P)
(a : J) (jr : W → J) (jg : ∀ w, a = jr w)
{b c : W} (p : b = c)
: ap_trijoin_general f a jr jg p
= (1 @@ ap_compose _ f _)^ @ (transport_paths_Fr _ _)^ @ apD (fun x ⇒ ap f (jg x)) p.
Proof.
induction p.
unfold ap_trijoin_general; simpl.
induction (jg b).
reflexivity.
Defined.
Definition ap_trijoin_transport {A B C P : Type} (f : TriJoin A B C → P)
(a : A) (b : B) (c : C)
: ap_trijoin f a b c
= (1 @@ ap_compose _ f _)^ @ (transport_paths_Fr _ _)^ @ apD (fun x ⇒ ap f (jglue a x)) (jglue b c).
Proof.
nrapply ap_trijoin_general_transport.
Defined.
Definition ap_trijoin_general_V {J W P : Type} (f : J → P)
(a : J) (jr : W → J) (jg : ∀ w, a = jr w)
{b c : W} (p : b = c)
: ap_trijoin_general f a jr jg p^
= (1 @@ (ap (ap f) (ap_V jr p) @ ap_V f _)) @ moveR_pV _ _ _ (ap_trijoin_general f a jr jg p)^.
Proof.
induction p.
unfold ap_trijoin_general; cbn.
by induction (jg b).
Defined.
Definition ap_trijoin_V {A B C P : Type} (f : TriJoin A B C → P)
(a : A) (b : B) (c : C)
: ap_triangle f (triangle_v a (jglue b c)^)
= (1 @@ (ap (ap f) (ap_V joinr _) @ ap_V f _)) @ moveR_pV _ _ _ (ap_trijoin f a b c)^.
Proof.
nrapply ap_trijoin_general_V.
Defined.
The induction principle for the triple join
Local Definition trijoin_ind_helper {A BC : Type} (P : Join A BC → Type)
(a : A) (b c : BC) (bc : b = c)
(j1' : P (joinl a)) (j2' : P (joinr b)) (j3' : P (joinr c))
(j12' : jglue a b # j1' = j2') (j13' : jglue a c # j1' = j3') (j23' : (ap joinr bc) # j2' = j3')
(j123' : transport_pp _ (jglue a b) (ap joinr bc) j1' @ ap (transport _ (ap joinr bc)) j12' @ j23'
= transport2 _ (triangle_v a bc) _ @ j13')
: ((apD (fun x : BC ⇒ transport P (jglue a x) j1') bc)^
@ ap (transport (fun x : BC ⇒ P (joinr x)) bc) j12')
@ ((transport_compose P joinr bc j2') @ j23') = j13'.
Proof.
induction bc; simpl.
rewrite transport_pp_1 in j123'.
cbn in ×.
unfold transport; unfold transport in j123'.
rewrite ap_idmap; rewrite ap_idmap in j123'.
rewrite concat_pp_p in j123'.
apply cancelL in j123'.
rewrite 2 concat_1p.
exact j123'.
Qed.
(a : A) (b c : BC) (bc : b = c)
(j1' : P (joinl a)) (j2' : P (joinr b)) (j3' : P (joinr c))
(j12' : jglue a b # j1' = j2') (j13' : jglue a c # j1' = j3') (j23' : (ap joinr bc) # j2' = j3')
(j123' : transport_pp _ (jglue a b) (ap joinr bc) j1' @ ap (transport _ (ap joinr bc)) j12' @ j23'
= transport2 _ (triangle_v a bc) _ @ j13')
: ((apD (fun x : BC ⇒ transport P (jglue a x) j1') bc)^
@ ap (transport (fun x : BC ⇒ P (joinr x)) bc) j12')
@ ((transport_compose P joinr bc j2') @ j23') = j13'.
Proof.
induction bc; simpl.
rewrite transport_pp_1 in j123'.
cbn in ×.
unfold transport; unfold transport in j123'.
rewrite ap_idmap; rewrite ap_idmap in j123'.
rewrite concat_pp_p in j123'.
apply cancelL in j123'.
rewrite 2 concat_1p.
exact j123'.
Qed.
An induction principle for the triple join. Note that the hypotheses are phrased completely in terms of the "constructors" of TriJoin A B C.
Definition trijoin_ind (A B C : Type) (P : TriJoin A B C → Type)
(join1' : ∀ a, P (join1 a))
(join2' : ∀ b, P (join2 b))
(join3' : ∀ c, P (join3 c))
(join12' : ∀ a b, join12 a b # join1' a = join2' b)
(join13' : ∀ a c, join13 a c # join1' a = join3' c)
(join23' : ∀ b c, join23 b c # join2' b = join3' c)
(join123' : ∀ a b c, transport_pp _ (join12 a b) (join23 b c) (join1' a)
@ ap (transport _ (join23 b c)) (join12' a b) @ join23' b c
= transport2 _ (join123 a b c) _ @ join13' a c)
: ∀ x, P x.
Proof.
snrapply Join_ind.
- exact join1'.
- snrapply Join_ind.
+ exact join2'.
+ exact join3'.
+ intros b c.
lhs rapply (transport_compose P).
apply join23'.
- intro a.
snrapply Join_ind.
+ simpl. exact (join12' a).
+ simpl. exact (join13' a).
+ intros b c; cbn beta zeta.
lhs nrapply (transport_paths_FlFr_D (jglue b c)).
lhs nrapply (1 @@ _).
1: nrapply Join_ind_beta_jglue.
apply trijoin_ind_helper, join123'.
Defined.
(join1' : ∀ a, P (join1 a))
(join2' : ∀ b, P (join2 b))
(join3' : ∀ c, P (join3 c))
(join12' : ∀ a b, join12 a b # join1' a = join2' b)
(join13' : ∀ a c, join13 a c # join1' a = join3' c)
(join23' : ∀ b c, join23 b c # join2' b = join3' c)
(join123' : ∀ a b c, transport_pp _ (join12 a b) (join23 b c) (join1' a)
@ ap (transport _ (join23 b c)) (join12' a b) @ join23' b c
= transport2 _ (join123 a b c) _ @ join13' a c)
: ∀ x, P x.
Proof.
snrapply Join_ind.
- exact join1'.
- snrapply Join_ind.
+ exact join2'.
+ exact join3'.
+ intros b c.
lhs rapply (transport_compose P).
apply join23'.
- intro a.
snrapply Join_ind.
+ simpl. exact (join12' a).
+ simpl. exact (join13' a).
+ intros b c; cbn beta zeta.
lhs nrapply (transport_paths_FlFr_D (jglue b c)).
lhs nrapply (1 @@ _).
1: nrapply Join_ind_beta_jglue.
apply trijoin_ind_helper, join123'.
Defined.
The recursion principle for the triple join, and many results about it
Record TriJoinRecData {A B C P : Type} := {
j1 : A → P;
j2 : B → P;
j3 : C → P;
j12 : ∀ a b, j1 a = j2 b;
j13 : ∀ a c, j1 a = j3 c;
j23 : ∀ b c, j2 b = j3 c;
j123 : ∀ a b c, j12 a b @ j23 b c = j13 a c;
}.
Arguments TriJoinRecData : clear implicits.
Arguments Build_TriJoinRecData {A B C P}%_type_scope (j1 j2 j3 j12 j13 j23 j123)%_function_scope.
Definition trijoin_rec {A B C P : Type} (f : TriJoinRecData A B C P)
: TriJoin A B C $-> P.
Proof.
snrapply Join_rec.
- exact (j1 f).
- snrapply Join_rec.
+ exact (j2 f).
+ exact (j3 f).
+ exact (j23 f).
- intro a.
snrapply Join_ind; cbn beta.
+ exact (j12 f a).
+ exact (j13 f a).
+ intros b c.
lhs nrapply transport_paths_Fr.
exact (1 @@ Join_rec_beta_jglue _ _ _ _ _ @ j123 f a b c).
Defined.
j1 : A → P;
j2 : B → P;
j3 : C → P;
j12 : ∀ a b, j1 a = j2 b;
j13 : ∀ a c, j1 a = j3 c;
j23 : ∀ b c, j2 b = j3 c;
j123 : ∀ a b c, j12 a b @ j23 b c = j13 a c;
}.
Arguments TriJoinRecData : clear implicits.
Arguments Build_TriJoinRecData {A B C P}%_type_scope (j1 j2 j3 j12 j13 j23 j123)%_function_scope.
Definition trijoin_rec {A B C P : Type} (f : TriJoinRecData A B C P)
: TriJoin A B C $-> P.
Proof.
snrapply Join_rec.
- exact (j1 f).
- snrapply Join_rec.
+ exact (j2 f).
+ exact (j3 f).
+ exact (j23 f).
- intro a.
snrapply Join_ind; cbn beta.
+ exact (j12 f a).
+ exact (j13 f a).
+ intros b c.
lhs nrapply transport_paths_Fr.
exact (1 @@ Join_rec_beta_jglue _ _ _ _ _ @ j123 f a b c).
Defined.
Beta rules for the recursion principle.
Definition trijoin_rec_beta_join12 {A B C P : Type} (f : TriJoinRecData A B C P) (a : A) (b : B)
: ap (trijoin_rec f) (join12 a b) = j12 f a b
:= Join_rec_beta_jglue _ _ _ _ _.
Definition trijoin_rec_beta_join13 {A B C P : Type} (f : TriJoinRecData A B C P) (a : A) (c : C)
: ap (trijoin_rec f) (join13 a c) = j13 f a c
:= Join_rec_beta_jglue _ _ _ _ _.
Definition trijoin_rec_beta_join23 {A B C P : Type} (f : TriJoinRecData A B C P) (b : B) (c : C)
: ap (trijoin_rec f) (join23 b c) = j23 f b c.
Proof.
unfold trijoin_rec, join23.
lhs_V nrapply (ap_compose joinr); simpl.
apply Join_rec_beta_jglue.
Defined.
Local Lemma trijoin_rec_beta_join123_helper {A : Type} {x y z : A}
{u0 u1 : x = y} {p0 p1 r1 : y = z} {q0 s1 t1 : x = z}
(p : p0 = p1) (q : q0 = u0 @ p0) (r : p0 = r1)
(s : u1 @ r1 = s1) (t : s1 = t1) (u : u0 = u1)
: ((1 @@ p)^ @ q^) @ (((q @ (u @@ 1)) @ ((1 @@ r) @ s)) @ t)
= ((u @@ (p^ @ r)) @ s) @ t.
Proof.
induction u, t, s, r, p.
revert q0 q; by apply paths_ind_r.
Defined.
Definition trijoin_rec_beta_join123 {A B C P : Type} (f : TriJoinRecData A B C P)
(a : A) (b : B) (c : C)
: ap_trijoin (trijoin_rec f) a b c
= (trijoin_rec_beta_join12 f a b @@ trijoin_rec_beta_join23 f b c)
@ j123 f a b c @ (trijoin_rec_beta_join13 f a c)^.
Proof.
(* Expand the LHS: *)
lhs nrapply ap_trijoin_transport.
rewrite (apD_homotopic (Join_rec_beta_jglue _ _ _ _) (jglue b c)).
rewrite Join_ind_beta_jglue.
(* Change ap (transport __) _ on LHS. *)
rewrite (concat_p_pp _ (transport_paths_Fr (jglue b c) (j12 f a b)) _).
rewrite (concat_Ap (transport_paths_Fr (jglue b c))).
(* Everything that remains is pure path algebra. *)
(* trijoin_rec_beta_join23 expands to something of the form p^ @ r, so that's what is in the lemma. One can unfold it to see this, but the Qed is a bit faster without this: *)
(* unfold trijoin_rec_beta_join23. *)
(* Note that one of the aps on the LHS computes to u @@ 1, so that's what is in the lemma: *)
(* change (ap (fun q => q @ ?x) ?u) with (u @@ @idpath _ x). *)
nrapply trijoin_rec_beta_join123_helper.
Qed.
: ap (trijoin_rec f) (join12 a b) = j12 f a b
:= Join_rec_beta_jglue _ _ _ _ _.
Definition trijoin_rec_beta_join13 {A B C P : Type} (f : TriJoinRecData A B C P) (a : A) (c : C)
: ap (trijoin_rec f) (join13 a c) = j13 f a c
:= Join_rec_beta_jglue _ _ _ _ _.
Definition trijoin_rec_beta_join23 {A B C P : Type} (f : TriJoinRecData A B C P) (b : B) (c : C)
: ap (trijoin_rec f) (join23 b c) = j23 f b c.
Proof.
unfold trijoin_rec, join23.
lhs_V nrapply (ap_compose joinr); simpl.
apply Join_rec_beta_jglue.
Defined.
Local Lemma trijoin_rec_beta_join123_helper {A : Type} {x y z : A}
{u0 u1 : x = y} {p0 p1 r1 : y = z} {q0 s1 t1 : x = z}
(p : p0 = p1) (q : q0 = u0 @ p0) (r : p0 = r1)
(s : u1 @ r1 = s1) (t : s1 = t1) (u : u0 = u1)
: ((1 @@ p)^ @ q^) @ (((q @ (u @@ 1)) @ ((1 @@ r) @ s)) @ t)
= ((u @@ (p^ @ r)) @ s) @ t.
Proof.
induction u, t, s, r, p.
revert q0 q; by apply paths_ind_r.
Defined.
Definition trijoin_rec_beta_join123 {A B C P : Type} (f : TriJoinRecData A B C P)
(a : A) (b : B) (c : C)
: ap_trijoin (trijoin_rec f) a b c
= (trijoin_rec_beta_join12 f a b @@ trijoin_rec_beta_join23 f b c)
@ j123 f a b c @ (trijoin_rec_beta_join13 f a c)^.
Proof.
(* Expand the LHS: *)
lhs nrapply ap_trijoin_transport.
rewrite (apD_homotopic (Join_rec_beta_jglue _ _ _ _) (jglue b c)).
rewrite Join_ind_beta_jglue.
(* Change ap (transport __) _ on LHS. *)
rewrite (concat_p_pp _ (transport_paths_Fr (jglue b c) (j12 f a b)) _).
rewrite (concat_Ap (transport_paths_Fr (jglue b c))).
(* Everything that remains is pure path algebra. *)
(* trijoin_rec_beta_join23 expands to something of the form p^ @ r, so that's what is in the lemma. One can unfold it to see this, but the Qed is a bit faster without this: *)
(* unfold trijoin_rec_beta_join23. *)
(* Note that one of the aps on the LHS computes to u @@ 1, so that's what is in the lemma: *)
(* change (ap (fun q => q @ ?x) ?u) with (u @@ @idpath _ x). *)
nrapply trijoin_rec_beta_join123_helper.
Qed.
We're next going to define a map in the other direction. We do it via showing that TriJoinRecData is a 0-coherent 1-functor to Type. We'll later show that it is a 1-functor to 0-groupoids.
Definition trijoinrecdata_fun {A B C P Q : Type} (g : P → Q) (f : TriJoinRecData A B C P)
: TriJoinRecData A B C Q.
Proof.
snrapply Build_TriJoinRecData.
- exact (g o j1 f).
- exact (g o j2 f).
- exact (g o j3 f).
- exact (fun a b ⇒ ap g (j12 f a b)).
- exact (fun a c ⇒ ap g (j13 f a c)).
- exact (fun b c ⇒ ap g (j23 f b c)).
- intros a b c; cbn beta.
exact (ap_triangle g (j123 f a b c)).
(* The last four goals above can also be handled by the induction tactics below, but it's useful to be concrete. *)
Defined.
: TriJoinRecData A B C Q.
Proof.
snrapply Build_TriJoinRecData.
- exact (g o j1 f).
- exact (g o j2 f).
- exact (g o j3 f).
- exact (fun a b ⇒ ap g (j12 f a b)).
- exact (fun a c ⇒ ap g (j13 f a c)).
- exact (fun b c ⇒ ap g (j23 f b c)).
- intros a b c; cbn beta.
exact (ap_triangle g (j123 f a b c)).
(* The last four goals above can also be handled by the induction tactics below, but it's useful to be concrete. *)
Defined.
The triple join itself has canonical TriJoinRecData.
Definition trijoinrecdata_trijoin (A B C : Type)
: TriJoinRecData A B C (Join A (Join B C))
:= Build_TriJoinRecData join1 join2 join3 join12 join13 join23 join123.
: TriJoinRecData A B C (Join A (Join B C))
:= Build_TriJoinRecData join1 join2 join3 join12 join13 join23 join123.
Combining these gives a function going in the opposite direction to trijoin_rec.
Definition trijoin_rec_inv {A B C P : Type} (f : TriJoin A B C → P)
: TriJoinRecData A B C P
:= trijoinrecdata_fun f (trijoinrecdata_trijoin A B C).
: TriJoinRecData A B C P
:= trijoinrecdata_fun f (trijoinrecdata_trijoin A B C).
Under Funext, trijoin_rec and trijoin_rec_inv should be inverse equivalences. We'll avoid Funext and show that they are equivalences of 0-groupoids, where we choose the path structures carefully.
The type of fillers for a triangular prism with five 2d faces abc, abc', k12, k13, k23.
The graph structure on TriJoinRecData A B C P
Definition prism {P : Type}
{a b c : P} {ab : a = b} {ac : a = c} {bc : b = c} (abc : ab @ bc = ac)
{a' b' c' : P} {ab' : a' = b'} {ac' : a' = c'} {bc' : b' = c'} (abc' : ab' @ bc' = ac')
{k1 : a = a'} {k2 : b = b'} {k3 : c = c'}
(k12 : ab @ k2 = k1 @ ab') (k13 : ac @ k3 = k1 @ ac') (k23 : bc @ k3 = k2 @ bc')
:= concat_p_pp _ _ _ @ whiskerR abc k3 @ k13
= whiskerL ab k23
@ concat_p_pp _ _ _ @ whiskerR k12 bc'
@ concat_pp_p _ _ _ @ whiskerL k1 abc'.
{a b c : P} {ab : a = b} {ac : a = c} {bc : b = c} (abc : ab @ bc = ac)
{a' b' c' : P} {ab' : a' = b'} {ac' : a' = c'} {bc' : b' = c'} (abc' : ab' @ bc' = ac')
{k1 : a = a'} {k2 : b = b'} {k3 : c = c'}
(k12 : ab @ k2 = k1 @ ab') (k13 : ac @ k3 = k1 @ ac') (k23 : bc @ k3 = k2 @ bc')
:= concat_p_pp _ _ _ @ whiskerR abc k3 @ k13
= whiskerL ab k23
@ concat_p_pp _ _ _ @ whiskerR k12 bc'
@ concat_pp_p _ _ _ @ whiskerL k1 abc'.
The "identity" filler is slightly non-trivial, because the fillers for the squares, e.g. ab @ 1 = 1 @ ab, must be non-trivial.
Definition prism_id {P : Type}
{a b c : P} {ab : a = b} {ac : a = c} {bc : b = c} (abc : ab @ bc = ac)
: prism abc abc (equiv_p1_1q idpath) (equiv_p1_1q idpath) (equiv_p1_1q idpath).
Proof.
induction ab, bc, abc; simpl.
reflexivity.
Defined.
{a b c : P} {ab : a = b} {ac : a = c} {bc : b = c} (abc : ab @ bc = ac)
: prism abc abc (equiv_p1_1q idpath) (equiv_p1_1q idpath) (equiv_p1_1q idpath).
Proof.
induction ab, bc, abc; simpl.
reflexivity.
Defined.
The paths between elements of TriJoinRecData A B C P. Under Funext, this type will be equivalent to the identity type. But without Funext, this definition will be more useful.
Record TriJoinRecPath {A B C P : Type} {f g : TriJoinRecData A B C P} := {
h1 : ∀ a, j1 f a = j1 g a;
h2 : ∀ b, j2 f b = j2 g b;
h3 : ∀ c, j3 f c = j3 g c;
h12 : ∀ a b, j12 f a b @ h2 b = h1 a @ j12 g a b;
h13 : ∀ a c, j13 f a c @ h3 c = h1 a @ j13 g a c;
h23 : ∀ b c, j23 f b c @ h3 c = h2 b @ j23 g b c;
h123 : ∀ a b c, prism (j123 f a b c) (j123 g a b c) (h12 a b) (h13 a c) (h23 b c);
}.
Arguments TriJoinRecPath {A B C P} f g.
h1 : ∀ a, j1 f a = j1 g a;
h2 : ∀ b, j2 f b = j2 g b;
h3 : ∀ c, j3 f c = j3 g c;
h12 : ∀ a b, j12 f a b @ h2 b = h1 a @ j12 g a b;
h13 : ∀ a c, j13 f a c @ h3 c = h1 a @ j13 g a c;
h23 : ∀ b c, j23 f b c @ h3 c = h2 b @ j23 g b c;
h123 : ∀ a b c, prism (j123 f a b c) (j123 g a b c) (h12 a b) (h13 a c) (h23 b c);
}.
Arguments TriJoinRecPath {A B C P} f g.
We also define data for trijoin_rec that unbundles the first three components. This lets us talk about paths between two such when the first three components are definitionally equal. This is a common special case, and this set-up greatly simplifies a lot of path algebra in later proofs.
Record TriJoinRecData' {A B C P : Type} {j1' : A → P} {j2' : B → P} {j3' : C → P} := {
j12' : ∀ a b, j1' a = j2' b;
j13' : ∀ a c, j1' a = j3' c;
j23' : ∀ b c, j2' b = j3' c;
j123' : ∀ a b c, j12' a b @ j23' b c = j13' a c;
}.
Arguments TriJoinRecData' {A B C P} j1' j2' j3'.
Arguments Build_TriJoinRecData' {A B C P}%_type_scope
(j1' j2' j3' j12' j13' j23' j123')%_function_scope.
Definition prism' {P : Type} {a b c : P}
{ab : a = b} {ac : a = c} {bc : b = c} (abc : ab @ bc = ac)
{ab' : a = b} {ac' : a = c} {bc' : b = c} (abc' : ab' @ bc' = ac')
(k12 : ab = ab') (k13 : ac = ac') (k23 : bc = bc')
:= abc @ k13 = (k12 @@ k23) @ abc'.
Record TriJoinRecPath' {A B C P : Type} {j1' : A → P} {j2' : B → P} {j3' : C → P}
{f g : TriJoinRecData' j1' j2' j3'} := {
h12' : ∀ a b, j12' f a b = j12' g a b;
h13' : ∀ a c, j13' f a c = j13' g a c;
h23' : ∀ b c, j23' f b c = j23' g b c;
h123' : ∀ a b c, prism' (j123' f a b c) (j123' g a b c) (h12' a b) (h13' a c) (h23' b c);
}.
Arguments TriJoinRecPath' {A B C P} {j1' j2' j3'} f g.
j12' : ∀ a b, j1' a = j2' b;
j13' : ∀ a c, j1' a = j3' c;
j23' : ∀ b c, j2' b = j3' c;
j123' : ∀ a b c, j12' a b @ j23' b c = j13' a c;
}.
Arguments TriJoinRecData' {A B C P} j1' j2' j3'.
Arguments Build_TriJoinRecData' {A B C P}%_type_scope
(j1' j2' j3' j12' j13' j23' j123')%_function_scope.
Definition prism' {P : Type} {a b c : P}
{ab : a = b} {ac : a = c} {bc : b = c} (abc : ab @ bc = ac)
{ab' : a = b} {ac' : a = c} {bc' : b = c} (abc' : ab' @ bc' = ac')
(k12 : ab = ab') (k13 : ac = ac') (k23 : bc = bc')
:= abc @ k13 = (k12 @@ k23) @ abc'.
Record TriJoinRecPath' {A B C P : Type} {j1' : A → P} {j2' : B → P} {j3' : C → P}
{f g : TriJoinRecData' j1' j2' j3'} := {
h12' : ∀ a b, j12' f a b = j12' g a b;
h13' : ∀ a c, j13' f a c = j13' g a c;
h23' : ∀ b c, j23' f b c = j23' g b c;
h123' : ∀ a b c, prism' (j123' f a b c) (j123' g a b c) (h12' a b) (h13' a c) (h23' b c);
}.
Arguments TriJoinRecPath' {A B C P} {j1' j2' j3'} f g.
We can bundle and unbundle these types of data. For unbundling, we just handle TriJoinRecData for now.
Definition bundle_trijoinrecdata {A B C P : Type} {j1' : A → P} {j2' : B → P} {j3' : C → P}
(f : TriJoinRecData' j1' j2' j3')
: TriJoinRecData A B C P
:= Build_TriJoinRecData j1' j2' j3' (j12' f) (j13' f) (j23' f) (j123' f).
Definition unbundle_trijoinrecdata {A B C P : Type} (f : TriJoinRecData A B C P)
: TriJoinRecData' (j1 f) (j2 f) (j3 f)
:= Build_TriJoinRecData' (j1 f) (j2 f) (j3 f) (j12 f) (j13 f) (j23 f) (j123 f).
The proof by induction that is easily available to us here is what saves work in more complicated contexts.
Definition bundle_prism {P : Type} {a b c : P}
{ab : a = b} {ac : a = c} {bc : b = c} (abc : ab @ bc = ac)
{ab' : a = b} {ac' : a = c} {bc' : b = c} (abc' : ab' @ bc' = ac')
(k12 : ab = ab') (k13 : ac = ac') (k23 : bc = bc')
(k123 : prism' abc abc' k12 k13 k23)
: prism abc abc' (equiv_p1_1q k12) (equiv_p1_1q k13) (equiv_p1_1q k23).
Proof.
induction ab.
induction bc.
induction abc.
induction k12.
induction k23.
induction k13.
unfold prism' in k123.
induction (moveR_Vp _ _ _ k123); clear k123.
simpl.
reflexivity.
Defined.
Definition bundle_trijoinrecpath {A B C P : Type} {j1' : A → P} {j2' : B → P} {j3' : C → P}
{f g : TriJoinRecData' j1' j2' j3'} (h : TriJoinRecPath' f g)
: TriJoinRecPath (bundle_trijoinrecdata f) (bundle_trijoinrecdata g).
Proof.
snrapply Build_TriJoinRecPath.
1, 2, 3: reflexivity.
1, 2, 3: intros; apply equiv_p1_1q.
- apply (h12' h).
- apply (h13' h).
- apply (h23' h).
- cbn beta zeta.
intros a b c.
apply bundle_prism, (h123' h).
Defined.
{ab : a = b} {ac : a = c} {bc : b = c} (abc : ab @ bc = ac)
{ab' : a = b} {ac' : a = c} {bc' : b = c} (abc' : ab' @ bc' = ac')
(k12 : ab = ab') (k13 : ac = ac') (k23 : bc = bc')
(k123 : prism' abc abc' k12 k13 k23)
: prism abc abc' (equiv_p1_1q k12) (equiv_p1_1q k13) (equiv_p1_1q k23).
Proof.
induction ab.
induction bc.
induction abc.
induction k12.
induction k23.
induction k13.
unfold prism' in k123.
induction (moveR_Vp _ _ _ k123); clear k123.
simpl.
reflexivity.
Defined.
Definition bundle_trijoinrecpath {A B C P : Type} {j1' : A → P} {j2' : B → P} {j3' : C → P}
{f g : TriJoinRecData' j1' j2' j3'} (h : TriJoinRecPath' f g)
: TriJoinRecPath (bundle_trijoinrecdata f) (bundle_trijoinrecdata g).
Proof.
snrapply Build_TriJoinRecPath.
1, 2, 3: reflexivity.
1, 2, 3: intros; apply equiv_p1_1q.
- apply (h12' h).
- apply (h13' h).
- apply (h23' h).
- cbn beta zeta.
intros a b c.
apply bundle_prism, (h123' h).
Defined.
A tactic that helps us apply the previous result.
Ltac bundle_trijoinrecpath :=
hnf;
match goal with |- TriJoinRecPath ?F ?G ⇒
refine (bundle_trijoinrecpath (f:=unbundle_trijoinrecdata F)
(g:=unbundle_trijoinrecdata G) _) end;
snrapply Build_TriJoinRecPath'.
hnf;
match goal with |- TriJoinRecPath ?F ?G ⇒
refine (bundle_trijoinrecpath (f:=unbundle_trijoinrecdata F)
(g:=unbundle_trijoinrecdata G) _) end;
snrapply Build_TriJoinRecPath'.
Using these paths, we can restate the beta rule for trijoin_rec. The statement using TriJoinRecPath' typechecks only because trijoin_rec computes definitionally on the path constructors.
Definition trijoin_rec_beta' {A B C P : Type} (f : TriJoinRecData A B C P)
: TriJoinRecPath' (unbundle_trijoinrecdata (trijoin_rec_inv (trijoin_rec f)))
(unbundle_trijoinrecdata f).
Proof.
snrapply Build_TriJoinRecPath'; cbn.
- apply trijoin_rec_beta_join12.
- apply trijoin_rec_beta_join13.
- apply trijoin_rec_beta_join23.
- intros a b c.
unfold prism'.
apply moveR_pM.
nrapply trijoin_rec_beta_join123.
Defined.
: TriJoinRecPath' (unbundle_trijoinrecdata (trijoin_rec_inv (trijoin_rec f)))
(unbundle_trijoinrecdata f).
Proof.
snrapply Build_TriJoinRecPath'; cbn.
- apply trijoin_rec_beta_join12.
- apply trijoin_rec_beta_join13.
- apply trijoin_rec_beta_join23.
- intros a b c.
unfold prism'.
apply moveR_pM.
nrapply trijoin_rec_beta_join123.
Defined.
We can upgrade this to an unprimed path. This says that trijoin_rec_inv is split surjective.
Definition trijoin_rec_beta {A B C P : Type} (f : TriJoinRecData A B C P)
: TriJoinRecPath (trijoin_rec_inv (trijoin_rec f)) f
:= bundle_trijoinrecpath (trijoin_rec_beta' f).
: TriJoinRecPath (trijoin_rec_inv (trijoin_rec f)) f
:= bundle_trijoinrecpath (trijoin_rec_beta' f).
Definition triangle_ind {P : Type} (a : P)
(Q : ∀ (b c : P) (ab : a = b) (ac : a = c) (bc : b = c) (abc : ab @ bc = ac), Type)
(s : Q a a idpath idpath idpath idpath)
: ∀ b c ab ac bc abc, Q b c ab ac bc abc.
Proof.
intros.
induction ab.
induction bc.
induction abc.
apply s.
Defined.
(Q : ∀ (b c : P) (ab : a = b) (ac : a = c) (bc : b = c) (abc : ab @ bc = ac), Type)
(s : Q a a idpath idpath idpath idpath)
: ∀ b c ab ac bc abc, Q b c ab ac bc abc.
Proof.
intros.
induction ab.
induction bc.
induction abc.
apply s.
Defined.
This lemma handles the path algebra in the last goal of the next result.
Local Definition isinj_trijoin_rec_inv_helper {J P : Type} {f g : J → P}
{a b c : J} {ab : a = b} {ac : a = c} {bc : b = c} {abc : ab @ bc = ac}
{H1 : f a = g a} {H2 : f b = g b} {H3 : f c = g c}
{H12 : ap f ab @ H2 = H1 @ ap g ab}
{H13 : ap f ac @ H3 = H1 @ ap g ac}
{H23 : ap f bc @ H3 = H2 @ ap g bc}
(H123 : prism (ap_triangle f abc) (ap_triangle g abc) H12 H13 H23)
: (transport_pp (fun x ⇒ f x = g x) ab bc H1 @ ap (transport (fun x ⇒ f x = g x) bc)
(transport_paths_FlFr' ab H1 H2 H12)) @ transport_paths_FlFr' bc H2 H3 H23
= transport2 (fun x ⇒ f x = g x) abc H1 @ transport_paths_FlFr' ac H1 H3 H13.
Proof.
revert b c ab ac bc abc H2 H3 H12 H13 H23 H123.
nrapply triangle_ind; cbn.
unfold ap_triangle, transport_paths_FlFr', transport; cbn -[concat_pp_p].
generalize dependent (f a); intro fa; clear f.
generalize dependent (g a); intro ga; clear g a.
intros H1 H2 H3 H12 H13 H23.
rewrite ap_idmap.
revert H12; equiv_intro (equiv_1p_q1 (p:=H2) (q:=H1)) H12'; induction H12'.
revert H13; equiv_intro (equiv_1p_q1 (p:=H3) (q:=H2)) H13'; induction H13'.
induction H3.
intro H123.
unfold prism in H123.
rewrite whiskerL_1p_1 in H123.
cbn in ×.
rewrite ! concat_p1 in H123.
induction H123.
reflexivity.
Qed.
{a b c : J} {ab : a = b} {ac : a = c} {bc : b = c} {abc : ab @ bc = ac}
{H1 : f a = g a} {H2 : f b = g b} {H3 : f c = g c}
{H12 : ap f ab @ H2 = H1 @ ap g ab}
{H13 : ap f ac @ H3 = H1 @ ap g ac}
{H23 : ap f bc @ H3 = H2 @ ap g bc}
(H123 : prism (ap_triangle f abc) (ap_triangle g abc) H12 H13 H23)
: (transport_pp (fun x ⇒ f x = g x) ab bc H1 @ ap (transport (fun x ⇒ f x = g x) bc)
(transport_paths_FlFr' ab H1 H2 H12)) @ transport_paths_FlFr' bc H2 H3 H23
= transport2 (fun x ⇒ f x = g x) abc H1 @ transport_paths_FlFr' ac H1 H3 H13.
Proof.
revert b c ab ac bc abc H2 H3 H12 H13 H23 H123.
nrapply triangle_ind; cbn.
unfold ap_triangle, transport_paths_FlFr', transport; cbn -[concat_pp_p].
generalize dependent (f a); intro fa; clear f.
generalize dependent (g a); intro ga; clear g a.
intros H1 H2 H3 H12 H13 H23.
rewrite ap_idmap.
revert H12; equiv_intro (equiv_1p_q1 (p:=H2) (q:=H1)) H12'; induction H12'.
revert H13; equiv_intro (equiv_1p_q1 (p:=H3) (q:=H2)) H13'; induction H13'.
induction H3.
intro H123.
unfold prism in H123.
rewrite whiskerL_1p_1 in H123.
cbn in ×.
rewrite ! concat_p1 in H123.
induction H123.
reflexivity.
Qed.
trijoin_rec_inv is essentially injective, as a map between 0-groupoids.
Definition isinj_trijoin_rec_inv {A B C P : Type} {f g : TriJoin A B C → P}
(h : TriJoinRecPath (trijoin_rec_inv f) (trijoin_rec_inv g))
: f == g.
Proof.
snrapply trijoin_ind.
1: apply (h1 h).
1: apply (h2 h).
1: apply (h3 h).
1, 2, 3: intros; nrapply transport_paths_FlFr'.
1: apply (h12 h).
1: apply (h13 h).
1: apply (h23 h).
intros a b c; cbn beta.
apply isinj_trijoin_rec_inv_helper.
exact (h123 h a b c).
Defined.
(h : TriJoinRecPath (trijoin_rec_inv f) (trijoin_rec_inv g))
: f == g.
Proof.
snrapply trijoin_ind.
1: apply (h1 h).
1: apply (h2 h).
1: apply (h3 h).
1, 2, 3: intros; nrapply transport_paths_FlFr'.
1: apply (h12 h).
1: apply (h13 h).
1: apply (h23 h).
intros a b c; cbn beta.
apply isinj_trijoin_rec_inv_helper.
exact (h123 h a b c).
Defined.
Lemmas and tactics about triangles and prisms
Ltac generalize_some f a b c :=
let f1 := fresh in let f2 := fresh in let f3 := fresh in
let f12 := fresh in let f13 := fresh in let f23 := fresh in
let f123 := fresh in
destruct f as [f1 f2 f3 f12 f13 f23 f123]; cbn;
try generalize (f123 a b c); clear f123;
try generalize (f23 b c); clear f23;
try generalize (f13 a c); clear f13;
try generalize (f12 a b); clear f12;
try generalize (f3 c); clear f3;
try generalize (f2 b); clear f2;
try generalize (f1 a); clear f1.
(* No easy way to skip the "last" one, since we don't know which will be the last to be generalized. *)
let f1 := fresh in let f2 := fresh in let f3 := fresh in
let f12 := fresh in let f13 := fresh in let f23 := fresh in
let f123 := fresh in
destruct f as [f1 f2 f3 f12 f13 f23 f123]; cbn;
try generalize (f123 a b c); clear f123;
try generalize (f23 b c); clear f23;
try generalize (f13 a c); clear f13;
try generalize (f12 a b); clear f12;
try generalize (f3 c); clear f3;
try generalize (f2 b); clear f2;
try generalize (f1 a); clear f1.
(* No easy way to skip the "last" one, since we don't know which will be the last to be generalized. *)
Ltac triangle_ind f a b c :=
generalize_some f a b c;
intro f; (* generalize_some goes one step too far, so intro the last variable. *)
apply triangle_ind.
generalize_some f a b c;
intro f; (* generalize_some goes one step too far, so intro the last variable. *)
apply triangle_ind.
Use this with f : TriJoinRecData A B C P. Two of the arguments a, b and c should be elements of A, B and C, respectively, and the third should be a dummy value (e.g. _X_) that causes the generalize tactic to fail. It applies to goals that only depend on the components of f involving just two of A, B and C.
Ltac triangle_ind_two f a b c :=
generalize_some f a b c;
intro f; (* generalize_some goes one step too far, so intro the last variable. *)
apply paths_ind.
generalize_some f a b c;
intro f; (* generalize_some goes one step too far, so intro the last variable. *)
apply paths_ind.
The prism analog of the function triangle_ind from earlier in the file. To prove something about all prisms, it's enough to prove it for the "identity" prism. Note that we don't specialize to a prism concentrated on a single vertex, since sometimes we have to deal with a composite of two prisms.
Definition prism_ind {P : Type} (a b c : P) (ab : a = b) (ac : a = c) (bc : b = c) (abc : ab @ bc = ac)
(Q : ∀ (a' b' c' : P) (ab' : a' = b') (ac' : a' = c') (bc' : b' = c') (abc' : ab' @ bc' = ac')
(k1 : a = a') (k2 : b = b') (k3 : c = c')
(k12 : ab @ k2 = k1 @ ab') (k13 : ac @ k3 = k1 @ ac') (k23 : bc @ k3 = k2 @ bc')
(k123 : prism abc abc' k12 k13 k23), Type)
(s : Q a b c ab ac bc abc idpath idpath idpath (equiv_p1_1q idpath) (equiv_p1_1q idpath) (equiv_p1_1q idpath) (prism_id abc))
: ∀ a' b' c' ab' ac' bc' abc' k1 k2 k3 k12 k13 k23 k123, Q a' b' c' ab' ac' bc' abc' k1 k2 k3 k12 k13 k23 k123.
Proof.
intros.
induction k1, k2, k3.
revert k123.
revert k12; equiv_intro (equiv_p1_1q (p:=ab) (q:=ab')) k12'; induction k12'.
revert k13; equiv_intro (equiv_p1_1q (p:=ac) (q:=ac')) k13'; induction k13'.
revert k23; equiv_intro (equiv_p1_1q (p:=bc) (q:=bc')) k23'; induction k23'.
induction ab, bc, abc; simpl in ×.
unfold prism; simpl.
equiv_intro (equiv_concat_r (concat_1p (whiskerL 1 abc') @ whiskerL_1p_1 abc')^ idpath) k123'.
induction k123'.
simpl.
exact s.
Defined.
(Q : ∀ (a' b' c' : P) (ab' : a' = b') (ac' : a' = c') (bc' : b' = c') (abc' : ab' @ bc' = ac')
(k1 : a = a') (k2 : b = b') (k3 : c = c')
(k12 : ab @ k2 = k1 @ ab') (k13 : ac @ k3 = k1 @ ac') (k23 : bc @ k3 = k2 @ bc')
(k123 : prism abc abc' k12 k13 k23), Type)
(s : Q a b c ab ac bc abc idpath idpath idpath (equiv_p1_1q idpath) (equiv_p1_1q idpath) (equiv_p1_1q idpath) (prism_id abc))
: ∀ a' b' c' ab' ac' bc' abc' k1 k2 k3 k12 k13 k23 k123, Q a' b' c' ab' ac' bc' abc' k1 k2 k3 k12 k13 k23 k123.
Proof.
intros.
induction k1, k2, k3.
revert k123.
revert k12; equiv_intro (equiv_p1_1q (p:=ab) (q:=ab')) k12'; induction k12'.
revert k13; equiv_intro (equiv_p1_1q (p:=ac) (q:=ac')) k13'; induction k13'.
revert k23; equiv_intro (equiv_p1_1q (p:=bc) (q:=bc')) k23'; induction k23'.
induction ab, bc, abc; simpl in ×.
unfold prism; simpl.
equiv_intro (equiv_concat_r (concat_1p (whiskerL 1 abc') @ whiskerL_1p_1 abc')^ idpath) k123'.
induction k123'.
simpl.
exact s.
Defined.
Use this with f g : TriJoinRecData A B C P, h : TriJoinRecPath f g (so g is the *co*domain of h), a : A, b : B, c : C.
Use this with f g : TriJoinRecData A B C P, h : TriJoinRecPath f g (so g is the *co*domain of h). Two of the arguments a, b and c should be elements of A, B and C, respectively, and the third should be a dummy value (e.g. _X_) that causes the generalize tactic to fail. It applies to goals that only depend on the components of g and h involving just two of A, B and C. So it is dealing with one square face of the prism.
Ltac prism_ind_two g h a b c :=
generalize_some h a b c;
generalize_some g a b c;
apply square_ind. (* From Join/Core.v *)
generalize_some h a b c;
generalize_some g a b c;
apply square_ind. (* From Join/Core.v *)
Use the WildCat library to organize things
Global Instance isgraph_trijoinrecdata (A B C P : Type) : IsGraph (TriJoinRecData A B C P)
:= {| Hom := TriJoinRecPath |}.
Global Instance is01cat_trijoinrecdata (A B C P : Type) : Is01Cat (TriJoinRecData A B C P).
Proof.
apply Build_Is01Cat.
- intro f.
bundle_trijoinrecpath.
1, 2, 3: reflexivity.
intros a b c; cbn beta.
(* Can finish with: by triangle_ind f a b c. *)
unfold prism'.
cbn.
apply concat_p1_1p.
- intros f1 f2 f3 k2 k1.
snrapply Build_TriJoinRecPath; intros; cbn beta.
+ exact (h1 k1 a @ h1 k2 a).
+ exact (h2 k1 b @ h2 k2 b).
+ exact (h3 k1 c @ h3 k2 c).
+ (* Some simple path algebra works as well. *)
prism_ind_two f3 k2 a b _X_.
prism_ind_two f2 k1 a b _X_.
by triangle_ind_two f1 a b _X_.
+ prism_ind_two f3 k2 a _X_ c.
prism_ind_two f2 k1 a _X_ c.
by triangle_ind_two f1 a _X_ c.
+ prism_ind_two f3 k2 _X_ b c.
prism_ind_two f2 k1 _X_ b c.
by triangle_ind_two f1 _X_ b c.
+ cbn beta. prism_ind f3 k2 a b c.
prism_ind f2 k1 a b c.
by triangle_ind f1 a b c.
Defined.
Global Instance is0gpd_trijoinrecdata (A B C P : Type) : Is0Gpd (TriJoinRecData A B C P).
Proof.
apply Build_Is0Gpd.
intros f g h.
snrapply Build_TriJoinRecPath; intros; cbn beta.
+ exact (h1 h a)^.
+ exact (h2 h b)^.
+ exact (h3 h c)^.
+ (* Some simple path algebra works as well. *)
prism_ind_two g h a b _X_.
by triangle_ind_two f a b _X_.
+ prism_ind_two g h a _X_ c.
by triangle_ind_two f a _X_ c.
+ prism_ind_two g h _X_ b c.
by triangle_ind_two f _X_ b c.
+ prism_ind g h a b c.
by triangle_ind f a b c.
Defined.
Definition trijoinrecdata_0gpd (A B C P : Type) : ZeroGpd
:= Build_ZeroGpd (TriJoinRecData A B C P) _ _ _.
:= {| Hom := TriJoinRecPath |}.
Global Instance is01cat_trijoinrecdata (A B C P : Type) : Is01Cat (TriJoinRecData A B C P).
Proof.
apply Build_Is01Cat.
- intro f.
bundle_trijoinrecpath.
1, 2, 3: reflexivity.
intros a b c; cbn beta.
(* Can finish with: by triangle_ind f a b c. *)
unfold prism'.
cbn.
apply concat_p1_1p.
- intros f1 f2 f3 k2 k1.
snrapply Build_TriJoinRecPath; intros; cbn beta.
+ exact (h1 k1 a @ h1 k2 a).
+ exact (h2 k1 b @ h2 k2 b).
+ exact (h3 k1 c @ h3 k2 c).
+ (* Some simple path algebra works as well. *)
prism_ind_two f3 k2 a b _X_.
prism_ind_two f2 k1 a b _X_.
by triangle_ind_two f1 a b _X_.
+ prism_ind_two f3 k2 a _X_ c.
prism_ind_two f2 k1 a _X_ c.
by triangle_ind_two f1 a _X_ c.
+ prism_ind_two f3 k2 _X_ b c.
prism_ind_two f2 k1 _X_ b c.
by triangle_ind_two f1 _X_ b c.
+ cbn beta. prism_ind f3 k2 a b c.
prism_ind f2 k1 a b c.
by triangle_ind f1 a b c.
Defined.
Global Instance is0gpd_trijoinrecdata (A B C P : Type) : Is0Gpd (TriJoinRecData A B C P).
Proof.
apply Build_Is0Gpd.
intros f g h.
snrapply Build_TriJoinRecPath; intros; cbn beta.
+ exact (h1 h a)^.
+ exact (h2 h b)^.
+ exact (h3 h c)^.
+ (* Some simple path algebra works as well. *)
prism_ind_two g h a b _X_.
by triangle_ind_two f a b _X_.
+ prism_ind_two g h a _X_ c.
by triangle_ind_two f a _X_ c.
+ prism_ind_two g h _X_ b c.
by triangle_ind_two f _X_ b c.
+ prism_ind g h a b c.
by triangle_ind f a b c.
Defined.
Definition trijoinrecdata_0gpd (A B C P : Type) : ZeroGpd
:= Build_ZeroGpd (TriJoinRecData A B C P) _ _ _.
trijoinrecdata_0gpd A B C is a 1-functor from Type to ZeroGpd
Global Instance is0functor_trijoinrecdata_fun {A B C P Q : Type} (g : P → Q)
: Is0Functor (@trijoinrecdata_fun A B C P Q g).
Proof.
apply Build_Is0Functor.
intros f1 f2 h.
snrapply Build_TriJoinRecPath; intros; cbn.
1, 2, 3: apply (ap g).
1: apply (h1 h).
1: apply (h2 h).
1: apply (h3 h).
1, 2, 3: refine ((ap_pp g _ _)^ @ _ @ ap_pp g _ _); apply (ap (ap g)).
1: apply (h12 h). (* Or: prism_ind_12 f2 h a b. triangle_ind_12 f1 a b. reflexivity. *)
1: apply (h13 h).
1: apply (h23 h).
prism_ind f2 h a b c.
triangle_ind f1 a b c; cbn.
reflexivity.
Defined.
: Is0Functor (@trijoinrecdata_fun A B C P Q g).
Proof.
apply Build_Is0Functor.
intros f1 f2 h.
snrapply Build_TriJoinRecPath; intros; cbn.
1, 2, 3: apply (ap g).
1: apply (h1 h).
1: apply (h2 h).
1: apply (h3 h).
1, 2, 3: refine ((ap_pp g _ _)^ @ _ @ ap_pp g _ _); apply (ap (ap g)).
1: apply (h12 h). (* Or: prism_ind_12 f2 h a b. triangle_ind_12 f1 a b. reflexivity. *)
1: apply (h13 h).
1: apply (h23 h).
prism_ind f2 h a b c.
triangle_ind f1 a b c; cbn.
reflexivity.
Defined.
Global Instance is0functor_trijoinrecdata_0gpd (A B C : Type) : Is0Functor (trijoinrecdata_0gpd A B C).
Proof.
apply Build_Is0Functor.
intros P Q g.
snrapply Build_Morphism_0Gpd.
- exact (trijoinrecdata_fun g).
- apply is0functor_trijoinrecdata_fun.
Defined.
Proof.
apply Build_Is0Functor.
intros P Q g.
snrapply Build_Morphism_0Gpd.
- exact (trijoinrecdata_fun g).
- apply is0functor_trijoinrecdata_fun.
Defined.
Global Instance is1functor_trijoinrecdata_0gpd (A B C : Type) : Is1Functor (trijoinrecdata_0gpd A B C).
Proof.
apply Build_Is1Functor.
(* If g1 g2 : P → Q are homotopic, then the induced maps are homotopic: *)
- intros P Q g1 g2 h f; cbn in ×.
snrapply Build_TriJoinRecPath; intros; cbn.
1, 2, 3: apply h.
1, 2, 3: apply concat_Ap.
triangle_ind f a b c; cbn.
by induction (h f).
(* The identity map P → P is sent to a map homotopic to the identity. *)
- intros P f; cbn.
bundle_trijoinrecpath; intros; cbn.
1, 2, 3: apply ap_idmap.
by triangle_ind f a b c.
(* It respects composition. *)
- intros P Q R g1 g2 f; cbn.
bundle_trijoinrecpath; intros; cbn.
1, 2, 3: apply ap_compose.
by triangle_ind f a b c.
Defined.
Definition trijoinrecdata_0gpd_fun (A B C : Type) : Fun11 Type ZeroGpd
:= Build_Fun11 _ _ (trijoinrecdata_0gpd A B C).
Proof.
apply Build_Is1Functor.
(* If g1 g2 : P → Q are homotopic, then the induced maps are homotopic: *)
- intros P Q g1 g2 h f; cbn in ×.
snrapply Build_TriJoinRecPath; intros; cbn.
1, 2, 3: apply h.
1, 2, 3: apply concat_Ap.
triangle_ind f a b c; cbn.
by induction (h f).
(* The identity map P → P is sent to a map homotopic to the identity. *)
- intros P f; cbn.
bundle_trijoinrecpath; intros; cbn.
1, 2, 3: apply ap_idmap.
by triangle_ind f a b c.
(* It respects composition. *)
- intros P Q R g1 g2 f; cbn.
bundle_trijoinrecpath; intros; cbn.
1, 2, 3: apply ap_compose.
by triangle_ind f a b c.
Defined.
Definition trijoinrecdata_0gpd_fun (A B C : Type) : Fun11 Type ZeroGpd
:= Build_Fun11 _ _ (trijoinrecdata_0gpd A B C).
By the Yoneda lemma, it follows from TriJoinRecData being a 1-functor that given TriJoinRecData in J, we get a map (J → P) $-> (TriJoinRecData A B C P) of 0-groupoids which is natural in P. Below we will specialize to the case where J is TriJoin A B C with the canonical TriJoinRecData.
Definition trijoin_nattrans_recdata {A B C J : Type} (f : TriJoinRecData A B C J)
: NatTrans (opyon_0gpd J) (trijoinrecdata_0gpd_fun A B C).
Proof.
snrapply Build_NatTrans.
- rapply opyoneda_0gpd; exact f.
- exact _.
Defined.
: NatTrans (opyon_0gpd J) (trijoinrecdata_0gpd_fun A B C).
Proof.
snrapply Build_NatTrans.
- rapply opyoneda_0gpd; exact f.
- exact _.
Defined.
Thus we get a map (TriJoin A B C → P) $-> (TriJoinRecData A B C P) of 0-groupoids, natural in P. The underlying map is trijoin_rec_inv A B C P.
Definition trijoin_rec_inv_nattrans (A B C : Type)
: NatTrans (opyon_0gpd (TriJoin A B C)) (trijoinrecdata_0gpd_fun A B C)
:= trijoin_nattrans_recdata (trijoinrecdata_trijoin A B C).
: NatTrans (opyon_0gpd (TriJoin A B C)) (trijoinrecdata_0gpd_fun A B C)
:= trijoin_nattrans_recdata (trijoinrecdata_trijoin A B C).
This natural transformation is in fact a natural equivalence of 0-groupoids.
Definition trijoin_rec_inv_natequiv (A B C : Type)
: NatEquiv (opyon_0gpd (TriJoin A B C)) (trijoinrecdata_0gpd_fun A B C).
Proof.
snrapply Build_NatEquiv'.
1: apply trijoin_rec_inv_nattrans.
intro P.
apply isequiv_0gpd_issurjinj.
apply Build_IsSurjInj.
- intros f; cbn in f.
∃ (trijoin_rec f).
apply trijoin_rec_beta.
- exact (@isinj_trijoin_rec_inv A B C P).
Defined.
: NatEquiv (opyon_0gpd (TriJoin A B C)) (trijoinrecdata_0gpd_fun A B C).
Proof.
snrapply Build_NatEquiv'.
1: apply trijoin_rec_inv_nattrans.
intro P.
apply isequiv_0gpd_issurjinj.
apply Build_IsSurjInj.
- intros f; cbn in f.
∃ (trijoin_rec f).
apply trijoin_rec_beta.
- exact (@isinj_trijoin_rec_inv A B C P).
Defined.
It will be handy to name the inverse natural equivalence.
Definition trijoin_rec_natequiv (A B C : Type)
:= natequiv_inverse (trijoin_rec_inv_natequiv A B C).
:= natequiv_inverse (trijoin_rec_inv_natequiv A B C).
trijoin_rec_natequiv A B C P is an equivalence of 0-groupoids whose underlying function is definitionally trijoin_rec.
Local Definition trijoin_rec_natequiv_check (A B C P : Type)
: equiv_fun_0gpd (trijoin_rec_natequiv A B C P) = @trijoin_rec A B C P
:= idpath.
: equiv_fun_0gpd (trijoin_rec_natequiv A B C P) = @trijoin_rec A B C P
:= idpath.
Global Instance is0functor_trijoin_rec (A B C P : Type) : Is0Functor (@trijoin_rec A B C P).
Proof.
change (Is0Functor (equiv_fun_0gpd (trijoin_rec_natequiv A B C P))).
exact _.
Defined.
Proof.
change (Is0Functor (equiv_fun_0gpd (trijoin_rec_natequiv A B C P))).
exact _.
Defined.
And that trijoin_rec A B C is natural. The $== in the statement is just ==, but we use WildCat notation so that we can invert and compose these with WildCat notation.
Definition trijoin_rec_nat (A B C : Type) {P Q : Type} (g : P → Q)
(f : TriJoinRecData A B C P)
: trijoin_rec (trijoinrecdata_fun g f) $== g o trijoin_rec f.
Proof.
exact (isnat (trijoin_rec_natequiv A B C) g f).
Defined.
(f : TriJoinRecData A B C P)
: trijoin_rec (trijoinrecdata_fun g f) $== g o trijoin_rec f.
Proof.
exact (isnat (trijoin_rec_natequiv A B C) g f).
Defined.
It is also useful to record this.
Definition issect_trijoin_rec_inv {A B C P : Type} (f : TriJoin A B C → P)
: trijoin_rec (trijoin_rec_inv f) $== f
:= cate_issect (trijoin_rec_inv_natequiv A B C P) f.
: trijoin_rec (trijoin_rec_inv f) $== f
:= cate_issect (trijoin_rec_inv_natequiv A B C P) f.
This comes up a lot as well, and if you inline the proof, you get an ugly goal.
Definition moveR_trijoin_rec {A B C P : Type} {f : TriJoinRecData A B C P} {g : TriJoin A B C → P}
(p : f $== trijoin_rec_inv g)
: trijoin_rec f == g.
Proof.
exact (moveR_equiv_V_0gpd (trijoin_rec_inv_natequiv A B C P) _ _ p).
Defined.
(p : f $== trijoin_rec_inv g)
: trijoin_rec f == g.
Proof.
exact (moveR_equiv_V_0gpd (trijoin_rec_inv_natequiv A B C P) _ _ p).
Defined.
Functoriality of the triple join
Precomposition of TriJoinRecData
Definition trijoinrecdata_tricomp {A B C A' B' C' P} (k : TriJoinRecData A B C P)
(f : A' → A) (g : B' → B) (h : C' → C)
: TriJoinRecData A' B' C' P
:= {| j1 := j1 k o f; j2 := j2 k o g; j3 := j3 k o h;
j12 := fun a b ⇒ j12 k (f a) (g b);
j13 := fun a c ⇒ j13 k (f a) (h c);
j23 := fun b c ⇒ j23 k (g b) (h c);
j123 := fun a b c ⇒ j123 k (f a) (g b) (h c); |}.
(f : A' → A) (g : B' → B) (h : C' → C)
: TriJoinRecData A' B' C' P
:= {| j1 := j1 k o f; j2 := j2 k o g; j3 := j3 k o h;
j12 := fun a b ⇒ j12 k (f a) (g b);
j13 := fun a c ⇒ j13 k (f a) (h c);
j23 := fun b c ⇒ j23 k (g b) (h c);
j123 := fun a b c ⇒ j123 k (f a) (g b) (h c); |}.
Precomposition with a triple respects paths.
Definition trijoinrecdata_tricomp_0fun {A B C A' B' C' P}
{k l : TriJoinRecData A B C P} (p : k $== l)
(f : A' → A) (g : B' → B) (h : C' → C)
: trijoinrecdata_tricomp k f g h $== trijoinrecdata_tricomp l f g h.
Proof.
(* This line is not needed, but clarifies the proof. *)
unfold trijoinrecdata_tricomp; destruct p.
snrapply Build_TriJoinRecPath; intros; cbn; apply_hyp.
(* E.g., the first goal is j1 k (f a) = j1 l (f a), and this is solved by h1 p (f a). We just precompose all fields of p with f, g and h. *)
Defined.
{k l : TriJoinRecData A B C P} (p : k $== l)
(f : A' → A) (g : B' → B) (h : C' → C)
: trijoinrecdata_tricomp k f g h $== trijoinrecdata_tricomp l f g h.
Proof.
(* This line is not needed, but clarifies the proof. *)
unfold trijoinrecdata_tricomp; destruct p.
snrapply Build_TriJoinRecPath; intros; cbn; apply_hyp.
(* E.g., the first goal is j1 k (f a) = j1 l (f a), and this is solved by h1 p (f a). We just precompose all fields of p with f, g and h. *)
Defined.
Homotopies between the triple are also respected.
Definition trijoinrecdata_tricomp2 {A B C A' B' C' P} (k : TriJoinRecData A B C P)
{f f' : A' → A} {g g' : B' → B} {h h' : C' → C}
(p : f == f') (q : g == g') (r : h == h')
: trijoinrecdata_tricomp k f g h $== trijoinrecdata_tricomp k f' g' h'.
Proof.
snrapply Build_TriJoinRecPath; intros; cbn.
- apply ap, p.
- apply ap, q.
- apply ap, r.
- induction (p a), (q b); by apply equiv_p1_1q.
- induction (p a), (r c); by apply equiv_p1_1q.
- induction (q b), (r c); by apply equiv_p1_1q.
- induction (p a), (q b), (r c); apply prism_id.
Defined.
{f f' : A' → A} {g g' : B' → B} {h h' : C' → C}
(p : f == f') (q : g == g') (r : h == h')
: trijoinrecdata_tricomp k f g h $== trijoinrecdata_tricomp k f' g' h'.
Proof.
snrapply Build_TriJoinRecPath; intros; cbn.
- apply ap, p.
- apply ap, q.
- apply ap, r.
- induction (p a), (q b); by apply equiv_p1_1q.
- induction (p a), (r c); by apply equiv_p1_1q.
- induction (q b), (r c); by apply equiv_p1_1q.
- induction (p a), (q b), (r c); apply prism_id.
Defined.
Functoriality of TriJoin via functor_trijoin
Definition functor_trijoin {A B C A' B' C'} (f : A → A') (g : B → B') (h : C → C')
: TriJoin A B C → TriJoin A' B' C'
:= trijoin_rec (trijoinrecdata_tricomp (trijoinrecdata_trijoin A' B' C') f g h).
: TriJoin A B C → TriJoin A' B' C'
:= trijoin_rec (trijoinrecdata_tricomp (trijoinrecdata_trijoin A' B' C') f g h).
Definition trijoin_rec_functor_trijoin {A B C A' B' C' P} (k : TriJoinRecData A' B' C' P)
(f : A → A') (g : B → B') (h : C → C')
: trijoin_rec k o functor_trijoin f g h == trijoin_rec (trijoinrecdata_tricomp k f g h).
Proof.
(* On the LHS, we use naturality of the trijoin_rec inside functor_trijoin: *)
refine ((trijoin_rec_nat _ _ _ _ _)^$ $@ _).
refine (fmap trijoin_rec _).
(* Just to clarify to the reader what is going on: *)
change (?L $-> ?R) with (trijoinrecdata_tricomp (trijoin_rec_inv (trijoin_rec k)) f g h $-> R).
exact (trijoinrecdata_tricomp_0fun (trijoin_rec_beta k) f g h).
Defined.
(f : A → A') (g : B → B') (h : C → C')
: trijoin_rec k o functor_trijoin f g h == trijoin_rec (trijoinrecdata_tricomp k f g h).
Proof.
(* On the LHS, we use naturality of the trijoin_rec inside functor_trijoin: *)
refine ((trijoin_rec_nat _ _ _ _ _)^$ $@ _).
refine (fmap trijoin_rec _).
(* Just to clarify to the reader what is going on: *)
change (?L $-> ?R) with (trijoinrecdata_tricomp (trijoin_rec_inv (trijoin_rec k)) f g h $-> R).
exact (trijoinrecdata_tricomp_0fun (trijoin_rec_beta k) f g h).
Defined.
Now we have all of the tools to efficiently prove functoriality.
Definition functor_trijoin_compose {A B C A' B' C' A'' B'' C''}
(f : A → A') (g : B → B') (h : C → C')
(f' : A' → A'') (g' : B' → B'') (h' : C' → C'')
: functor_trijoin (f' o f) (g' o g) (h' o h) == functor_trijoin f' g' h' o functor_trijoin f g h.
Proof.
symmetry.
nrapply trijoin_rec_functor_trijoin.
Defined.
Definition functor_trijoin_idmap {A B C}
: functor_trijoin idmap idmap idmap == (idmap : TriJoin A B C → TriJoin A B C).
Proof.
apply moveR_trijoin_rec.
change (trijoinrecdata_trijoin A B C $== trijoinrecdata_fun idmap (trijoinrecdata_trijoin A B C)).
symmetry.
exact (fmap_id (trijoinrecdata_0gpd A B C) _ (trijoinrecdata_trijoin A B C)).
Defined.
Definition functor2_trijoin {A B C A' B' C'}
{f f' : A → A'} {g g' : B → B'} {h h' : C → C'}
(p : f == f') (q : g == g') (r : h == h')
: functor_trijoin f g h == functor_trijoin f' g' h'.
Proof.
unfold functor_trijoin.
rapply (fmap trijoin_rec).
apply (trijoinrecdata_tricomp2 _ p q r).
Defined.
Global Instance isequiv_functor_trijoin {A B C A' B' C'}
(f : A → A') `{!IsEquiv f}
(g : B → B') `{!IsEquiv g}
(h : C → C') `{!IsEquiv h}
: IsEquiv (functor_trijoin f g h).
Proof.
(* This proof is almost identical to the proof of isequiv_functor_join. *)
snrapply isequiv_adjointify.
- apply (functor_trijoin f^-1 g^-1 h^-1).
- etransitivity.
1: symmetry; apply functor_trijoin_compose.
etransitivity.
1: exact (functor2_trijoin (eisretr f) (eisretr g) (eisretr h)).
apply functor_trijoin_idmap.
- etransitivity.
1: symmetry; apply functor_trijoin_compose.
etransitivity.
1: exact (functor2_trijoin (eissect f) (eissect g) (eissect h)).
apply functor_trijoin_idmap.
Defined.
Definition equiv_functor_trijoin {A B C A' B' C'}
(f : A <~> A') (g : B <~> B') (h : C <~> C')
: TriJoin A B C <~> TriJoin A' B' C'
:= Build_Equiv _ _ (functor_trijoin f g h) _.
The relationship between functor_trijoin and functor_join.
Local Lemma ap_triangle_functor_join {A BC A' P} (f : A → A') (g : BC → P)
(a : A) {b c : BC} (bc : b = c) (bc' : g b = g c) (beta_jg : ap g bc = bc')
: ap_triangle (functor_join f g) (triangle_v a bc) @ functor_join_beta_jglue f g a c
= (functor_join_beta_jglue f g a b
@@ ((ap_compose joinr (functor_join f g) bc)^
@ (ap_compose g joinr bc @ ap (ap joinr) beta_jg)))
@ triangle_v (f a) bc'.
Proof.
induction bc, beta_jg; simpl.
transitivity (concat_p1 _ @ functor_join_beta_jglue f g a b).
- refine (_ @@ 1).
unfold ap_triangle.
apply moveR_Vp; symmetry.
exact (ap_pp_concat_p1 (functor_join f g) (jglue a b)).
- apply moveR_Mp; symmetry.
exact (concat_p_pp _ _ _ @ whiskerR_p1 _).
Defined.
(a : A) {b c : BC} (bc : b = c) (bc' : g b = g c) (beta_jg : ap g bc = bc')
: ap_triangle (functor_join f g) (triangle_v a bc) @ functor_join_beta_jglue f g a c
= (functor_join_beta_jglue f g a b
@@ ((ap_compose joinr (functor_join f g) bc)^
@ (ap_compose g joinr bc @ ap (ap joinr) beta_jg)))
@ triangle_v (f a) bc'.
Proof.
induction bc, beta_jg; simpl.
transitivity (concat_p1 _ @ functor_join_beta_jglue f g a b).
- refine (_ @@ 1).
unfold ap_triangle.
apply moveR_Vp; symmetry.
exact (ap_pp_concat_p1 (functor_join f g) (jglue a b)).
- apply moveR_Mp; symmetry.
exact (concat_p_pp _ _ _ @ whiskerR_p1 _).
Defined.
We'll generalize the situation a bit to keep things less verbose. join_rec g here will be functor_join g h in the next result. Maybe this extra generality will also be useful sometime?
Definition functor_join_join_rec {A B C A' P} (f : A → A') (g : JoinRecData B C P)
: functor_join f (join_rec g)
== trijoin_rec {| j1 := joinl o f; j2 := joinr o jl g; j3 := joinr o jr g;
j12 := fun a b ⇒ jglue (f a) (jl g b);
j13 := fun a c ⇒ jglue (f a) (jr g c);
j23 := fun b c ⇒ ap joinr (jg g b c);
j123 := fun a b c ⇒ triangle_v (f a) (jg g b c); |}.
Proof.
(* Recall that trijoin_rec is defined to be the inverse of trijoin_rec_inv_natequiv .... *)
refine (moveL_equiv_V_0gpd (trijoin_rec_inv_natequiv A B C _) _ _ _).
(* The next two lines aren't needed, but clarify the goal. *)
unfold trijoin_rec_inv_natequiv, equiv_fun_0gpd; simpl.
unfold trijoinrecdata_fun, trijoinrecdata_trijoin; simpl.
bundle_trijoinrecpath; intros; cbn.
- exact (functor_join_beta_jglue f _ a (joinl b)).
- exact (functor_join_beta_jglue f _ a (joinr c)).
- unfold join23.
refine ((ap_compose joinr _ _)^ @ _).
simpl.
refine (ap_compose _ joinr (jglue b c) @ _).
refine (ap (ap joinr) _).
apply join_rec_beta_jg.
- unfold prism'.
change (join123 a b c) with (triangle_v a (jglue b c)).
exact (ap_triangle_functor_join f (join_rec g) a (jglue b c) (jg g b c) (Join_rec_beta_jglue _ _ _ b c)).
Defined.
Definition functor_trijoin_as_functor_join {A B C A' B' C'}
(f : A → A') (g : B → B') (h : C → C')
: functor_join f (functor_join g h) == functor_trijoin f g h
:= functor_join_join_rec f (functor_join_recdata g h).
: functor_join f (join_rec g)
== trijoin_rec {| j1 := joinl o f; j2 := joinr o jl g; j3 := joinr o jr g;
j12 := fun a b ⇒ jglue (f a) (jl g b);
j13 := fun a c ⇒ jglue (f a) (jr g c);
j23 := fun b c ⇒ ap joinr (jg g b c);
j123 := fun a b c ⇒ triangle_v (f a) (jg g b c); |}.
Proof.
(* Recall that trijoin_rec is defined to be the inverse of trijoin_rec_inv_natequiv .... *)
refine (moveL_equiv_V_0gpd (trijoin_rec_inv_natequiv A B C _) _ _ _).
(* The next two lines aren't needed, but clarify the goal. *)
unfold trijoin_rec_inv_natequiv, equiv_fun_0gpd; simpl.
unfold trijoinrecdata_fun, trijoinrecdata_trijoin; simpl.
bundle_trijoinrecpath; intros; cbn.
- exact (functor_join_beta_jglue f _ a (joinl b)).
- exact (functor_join_beta_jglue f _ a (joinr c)).
- unfold join23.
refine ((ap_compose joinr _ _)^ @ _).
simpl.
refine (ap_compose _ joinr (jglue b c) @ _).
refine (ap (ap joinr) _).
apply join_rec_beta_jg.
- unfold prism'.
change (join123 a b c) with (triangle_v a (jglue b c)).
exact (ap_triangle_functor_join f (join_rec g) a (jglue b c) (jg g b c) (Join_rec_beta_jglue _ _ _ b c)).
Defined.
Definition functor_trijoin_as_functor_join {A B C A' B' C'}
(f : A → A') (g : B → B') (h : C → C')
: functor_join f (functor_join g h) == functor_trijoin f g h
:= functor_join_join_rec f (functor_join_recdata g h).