Built with Alectryon, running Coq+SerAPI v8.19.0+0.19.3. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use ⌘ instead of Ctrl.
(** * Theorems about cartesian products *)
[Loading ML file number_string_notation_plugin.cmxs (using legacy method) ... done]
Require Import Types.Empty.LocalOpen Scope path_scope.Local Set Universe Minimization ToSet.Generalizable VariablesX A B f g n.Schemeprod_ind := Induction for prod SortType.Arguments prod_ind {A B} P f p.(** ** Unpacking *)(** Sometimes we would like to prove [Q u] where [u : A * B] by writing [u] as a pair [(fst u ; snd u)]. This is accomplished by [unpack_prod]. We want tight control over the proof, so we just write it down even though is looks a bit scary. *)Definitionunpack_prod `{P : A * B -> Type} (u : A * B) :
P (fst u, snd u) -> P u
:= idmap.Arguments unpack_prod / .(** Now we write down the reverse. *)Definitionpack_prod `{P : A * B -> Type} (u : A * B) :
P u -> P (fst u, snd u)
:= idmap.Arguments pack_prod / .(** ** Eta conversion *)Definitioneta_prod `(z : A * B) : (fst z, snd z) = z
:= 1.Arguments eta_prod / .(** ** Paths *)(** With this version of the function, we often have to give [z] and [z'] explicitly, so we make them explicit arguments. *)
A, B: Type z, z': A * B pq: (fst z = fst z') * (snd z = snd z')
z = z'
A, B: Type z, z': A * B pq: (fst z = fst z') * (snd z = snd z')
z = z'
A, B: Type z, z': A * B pq: (fst z = fst z') * (snd z = snd z')
(fst z, snd z) = (fst z', snd z')
A, B: Type z, z': A * B pq: (fst z = fst z') * (snd z = snd z')
(fst z, snd z) = (fst z, snd z')
A, B: Type z, z': A * B pq: (fst z = fst z') * (snd z = snd z')
(fst z, snd z) = (fst z, snd z)
reflexivity.Defined.(** This is the curried one you usually want to use in practice. We define it in terms of the uncurried one, since it's the uncurried one that is proven below to be an equivalence. *)Definitionpath_prod {AB : Type} (zz' : A * B) :
(fst z = fst z') -> (snd z = snd z') -> (z = z')
:= funpq => path_prod_uncurried z z' (p,q).(** This version produces only paths between pairs, as opposed to paths between arbitrary inhabitants of product types. But it has the advantage that the components of those pairs can more often be inferred. *)Definitionpath_prod' {AB : Type} {xx' : A} {yy' : B}
: (x = x') -> (y = y') -> ((x,y) = (x',y'))
:= funpq => path_prod (x,y) (x',y') p q.(** Now we show how these things compute. *)
A, B: Type z, z': A * B p: fst z = fst z' q: snd z = snd z'
ap fst (path_prod z z' p q) = p
A, B: Type z, z': A * B p: fst z = fst z' q: snd z = snd z'
ap fst (path_prod z z' p q) = p
A, B: Type z, z': A * B p: fst z = fst z' q: snd z = snd z'
ap fst (path_prod (fst z, snd z) z' p q) = p
A, B: Type z, z': A * B p: fst z = fst z' q: snd z = snd z'
ap fst (path_prod (fst z, snd z) (fst z', snd z') p q) =
p
A, B, C: Type f: A -> B -> C z, z': A * B p: fst z = fst z' q: snd z = snd z'
ap (funz : A * B => f (fst z) (snd z))
(path_prod z z' p q) = ap011 f p q
A, B, C: Type f: A -> B -> C z, z': A * B p: fst z = fst z' q: snd z = snd z'
ap (funz : A * B => f (fst z) (snd z))
(path_prod z z' p q) = ap011 f p q
destruct z, z'; simplin *; destruct p, q; reflexivity.Defined.(** Now we show how these compute with transport. *)
A, B: Type P: A * B -> Type x, y: A * B H: (fst x = fst y) * (snd x = snd y) Px: P x
transport P (path_prod_uncurried x y H) Px =
transport (funx : A => P (x, snd y)) (fst H)
(transport (funy : B => P (fst x, y)) (snd H) Px)
A, B: Type P: A * B -> Type x, y: A * B H: (fst x = fst y) * (snd x = snd y) Px: P x
transport P (path_prod_uncurried x y H) Px =
transport (funx : A => P (x, snd y)) (fst H)
(transport (funy : B => P (fst x, y)) (snd H) Px)
A, B: Type P: A * B -> Type fst: A snd: B fst0: A snd0: B fst1: fst = fst0 snd1: snd = snd0 Px: P (fst, snd)
transport P
match
fst1 in (_ = a) return ((fst, snd) = (a, snd0))
with
| 1 =>
match
snd1 in (_ = b) return ((fst, snd) = (fst, b))
with
| 1 => 1endend Px =
transport (funx : A => P (x, snd0)) fst1
(transport (funy : B => P (fst, y)) snd1 Px)
A, B: Type P: A * B -> Type fst: A snd: B Px: P (fst, snd)
transport P 1 Px =
transport (funx : A => P (x, snd)) 1
(transport (funy : B => P (fst, y)) 1 Px)
reflexivity.Defined.Definitiontransport_path_prod {AB} (P : A * B -> Type) {xy : A * B}
(HA : fst x = fst y)
(HB : snd x = snd y)
(Px : P x)
: transport P (path_prod _ _ HA HB) Px
= transport (funx => P (x, snd y))
HA
(transport (funy => P (fst x, y))
HB
Px)
:= transport_path_prod_uncurried P (HA, HB) Px.Definitiontransport_path_prod'
{AB} (P : A * B -> Type)
{xy : A}
{x'y' : B}
(HA : x = y)
(HB : x' = y')
(Px : P (x,x'))
: transport P (path_prod' HA HB) Px
= transport (funx => P (x, y'))
HA
(transport (funy => P (x, y))
HB
Px)
:= @transport_path_prod _ _ P (x, x') (y, y') HA HB Px.(** This lets us identify the path space of a product type, up to equivalence. *)
A, B: Type z, z': A * B
IsEquiv (path_prod_uncurried z z')
A, B: Type z, z': A * B
IsEquiv (path_prod_uncurried z z')
A, B: Type z, z': A * B
forallx : (fst z = fst z') * (snd z = snd z'),
eta_path_prod (path_prod_uncurried z z' x) =
ap (path_prod_uncurried z z')
(path_prod' (ap_fst_path_prod (fst x) (snd x))
(ap_snd_path_prod (fst x) (snd x)))
destruct p, q; reflexivity.Defined.Definitionequiv_path_prod {AB : Type} (zz' : A * B)
: (fst z = fst z') * (snd z = snd z') <~> (z = z')
:= Build_Equiv _ _ (path_prod_uncurried z z') _.(** Path algebra *)(** Composition. The next three lemma are displayed equations in section 2.6 of the book, but they have no numbers so we can't put them into [HoTTBook.v]. *)
A, B: Type z, z', z'': A * B p: fst z = fst z' p': fst z' = fst z'' q: snd z = snd z' q': snd z' = snd z''
path_prod z z'' (p @ p') (q @ q') =
path_prod z z' p q @ path_prod z' z'' p' q'
A, B: Type z, z', z'': A * B p: fst z = fst z' p': fst z' = fst z'' q: snd z = snd z' q': snd z' = snd z''
path_prod z z'' (p @ p') (q @ q') =
path_prod z z' p q @ path_prod z' z'' p' q'
A, B: Type u, v, z, w: A * B p: fst u = fst v q: fst v = fst z r: fst z = fst w p': snd u = snd v q': snd v = snd z r': snd z = snd w
(path_prod_pp u z w (p @ q) r (p' @ q') r' @
whiskerR (path_prod_pp u v z p q p' q')
(path_prod z w r r')) @
concat_pp_p (path_prod u v p p') (path_prod v z q q')
(path_prod z w r r') =
(ap011 (path_prod u w) (concat_pp_p p q r)
(concat_pp_p p' q' r') @
path_prod_pp u v w p (q @ r) p' (q' @ r')) @
whiskerL (path_prod u v p p')
(path_prod_pp v z w q r q' r')
A, B: Type u, v, z, w: A * B p: fst u = fst v q: fst v = fst z r: fst z = fst w p': snd u = snd v q': snd v = snd z r': snd z = snd w
(path_prod_pp u z w (p @ q) r (p' @ q') r' @
whiskerR (path_prod_pp u v z p q p' q')
(path_prod z w r r')) @
concat_pp_p (path_prod u v p p') (path_prod v z q q')
(path_prod z w r r') =
(ap011 (path_prod u w) (concat_pp_p p q r)
(concat_pp_p p' q' r') @
path_prod_pp u v w p (q @ r) p' (q' @ r')) @
whiskerL (path_prod u v p p')
(path_prod_pp v z w q r q' r')
reflexivity.Defined.(** ** Transport *)Definitiontransport_prod {A : Type} {PQ : A -> Type} {aa' : A} (p : a = a')
(z : P a * Q a)
: transport (funa => P a * Q a) p z = (p # (fst z), p # (snd z))
:= match p with idpath => 1end.(** ** Functorial action *)Definitionfunctor_prod {AA'BB' : Type} (f:A->A') (g:B->B')
: A * B -> A' * B'
:= funz => (f (fst z), g (snd z)).
A, A', B, B': Type f: A -> A' g: B -> B' z, z': A * B p: fst z = fst z' q: snd z = snd z'
ap (functor_prod f g) (path_prod z z' p q) =
path_prod (functor_prod f g z) (functor_prod f g z')
(ap f p) (ap g q)
A, A', B, B': Type f: A -> A' g: B -> B' z, z': A * B p: fst z = fst z' q: snd z = snd z'
ap (functor_prod f g) (path_prod z z' p q) =
path_prod (functor_prod f g z) (functor_prod f g z')
(ap f p) (ap g q)
A, A', B, B': Type f: A -> A' g: B -> B' a: A b: B a': A b': B p: fst (a, b) = fst (a', b') q: snd (a, b) = snd (a', b')
ap (functor_prod f g) (path_prod (a, b) (a', b') p q) =
path_prod (functor_prod f g (a, b))
(functor_prod f g (a', b')) (ap f p) (ap g q)
A, A', B, B': Type f: A -> A' g: B -> B' a: A b: B a': A b': B p: a = a' q: b = b'
ap (functor_prod f g) (path_prod (a, b) (a', b') p q) =
path_prod (functor_prod f g (a, b))
(functor_prod f g (a', b')) (ap f p) (ap g q)
A, A', B, B': Type f: A -> A' g: B -> B' a: A b: B
ap (functor_prod f g) (path_prod (a, b) (a, b) 11) =
path_prod (functor_prod f g (a, b))
(functor_prod f g (a, b)) (ap f 1) (ap g 1)
reflexivity.Defined.(** ** Equivalences *)
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
IsEquiv (functor_prod f g)
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
IsEquiv (functor_prod f g)
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
A' * B' -> A * B
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
functor_prod f g o ?equiv_inv == idmap
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
?equiv_inv o functor_prod f g == idmap
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
forallx : A * B,
?eisretr (functor_prod f g x) =
ap (functor_prod f g) (?eissect x)
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
A' * B' -> A * B
exact (functor_prod f^-1 g^-1).
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
functor_prod f g o functor_prod f^-1 g^-1 == idmap
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g z: A' * B'
functor_prod f g (functor_prod f^-1 g^-1 z) = z
exact (path_prod' (eisretr f _) (eisretr g _)).
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
functor_prod f^-1 g^-1 o functor_prod f g == idmap
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g w: A * B
functor_prod f^-1 g^-1 (functor_prod f g w) = w
exact (path_prod' (eissect f _) (eissect g _)).
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
forallx : A * B,
((funz : A' * B' =>
path_prod' (eisretr f (fst z)) (eisretr g (snd z)))
:
functor_prod f g o functor_prod f^-1 g^-1 == idmap)
(functor_prod f g x) =
ap (functor_prod f g)
(((funw : A * B =>
path_prod' (eissect f (fst w))
(eissect g (snd w)))
:
functor_prod f^-1 g^-1 o functor_prod f g == idmap)
x)
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g a: A b: B
path_prod' (eisretr f (f a)) (eisretr g (g b)) =
ap (functor_prod f g)
(path_prod' (eissect f a) (eissect g b))
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g a: A b: B
path_prod' (ap f (eissect f (fst (a, b))))
(eisretr g (g b)) =
ap (functor_prod f g)
(path_prod' (eissect f a) (eissect g b))
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g a: A b: B
path_prod' (ap f (eissect f (fst (a, b))))
(eisretr g (g b)) =
path_prod
(functor_prod f g
(functor_prod f^-1 g^-1 (functor_prod f g (a, b))))
(functor_prod f g (a, b)) (ap f (eissect f a))
(ap g (eissect g b))
exact (ap _ (eisadj g _)).Defined.
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
A * B <~> A' * B'
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
A * B <~> A' * B'
A, A': Type f: A -> A' H: IsEquiv f B, B': Type g: B -> B' H0: IsEquiv g
IsEquiv (functor_prod f g)
exact _. (* i.e., search the context for instances *)Defined.
A, A', B, B': Type f: A <~> A' g: B <~> B'
A * B <~> A' * B'
A, A', B, B': Type f: A <~> A' g: B <~> B'
A * B <~> A' * B'
A, A', B, B': Type f: A <~> A' g: B <~> B'
IsEquiv (functor_prod f g)
exact _.Defined.Notation"f *E g" := (equiv_functor_prod' f g) : equiv_scope.Definitionequiv_functor_prod_l {ABB' : Type} (g : B <~> B')
: A * B <~> A * B'
:= 1 *E g.Definitionequiv_functor_prod_r {AA'B : Type} (f : A <~> A')
: A * B <~> A' * B
:= f *E 1.(** ** Logical equivalences *)Definitioniff_functor_prod {AA'BB' : Type} (f : A <-> A') (g : B <-> B')
: A * B <-> A' * B'
:= (functor_prod (fst f) (fst g) , functor_prod (snd f) (snd g)).(** ** Symmetry *)(** This is a special property of [prod], of course, not an instance of a general family of facts about types. *)
A, B: Type
A * B <~> B * A
A, B: Type
A * B <~> B * A
make_equiv.Defined.(** ** Associativity *)(** This, too, is a special property of [prod], of course, not an instance of a general family of facts about types. *)
A, B, C: Type
A * (B * C) <~> A * B * C
A, B, C: Type
A * (B * C) <~> A * B * C
make_equiv.Defined.(** ** Unit and annihilation *)Definitionprod_empty_r@{u} (X : Type@{u}) : X * Empty <~> Empty
:= (Build_Equiv@{u u} _ _ snd _).Definitionprod_empty_l@{u} (X : Type@{u}) : Empty * X <~> Empty
:= (Build_Equiv@{u u} _ _ fst _).
X: Type
X * Unit <~> X
X: Type
X * Unit <~> X
X: Type
IsEquiv fst
X: Type
fst o (funx : X => (x, tt)) == idmap
X: Type
(funx : X => (x, tt)) o fst == idmap
X: Type
forallx : X * Unit,
?eisretr (fst x) = ap fst (?eissect x)
X: Type
fst o (funx : X => (x, tt)) == idmap
intros x; reflexivity.
X: Type
(funx : X => (x, tt)) o fst == idmap
intros [x []]; reflexivity.
X: Type
forallx : X * Unit,
((funx0 : X => 1)
:
fst o (funx0 : X => (x0, tt)) == idmap) (fst x) =
ap fst
(((funx0 : X * Unit =>
(fun (x1 : X) (snd : Unit) =>
match
snd as u return ((fst (x1, u), tt) = (x1, u))
with
| tt => 1end) (fst x0) (snd x0))
:
(funx0 : X => (x0, tt)) o fst == idmap) x)
intros [x []]; reflexivity.Defined.
X: Type
Unit * X <~> X
X: Type
Unit * X <~> X
X: Type
IsEquiv snd
X: Type
snd o (funx : X => (tt, x)) == idmap
X: Type
(funx : X => (tt, x)) o snd == idmap
X: Type
forallx : Unit * X,
?eisretr (snd x) = ap snd (?eissect x)
X: Type
snd o (funx : X => (tt, x)) == idmap
intros x; reflexivity.
X: Type
(funx : X => (tt, x)) o snd == idmap
intros [[] x]; reflexivity.
X: Type
forallx : Unit * X,
((funx0 : X => 1)
:
snd o (funx0 : X => (tt, x0)) == idmap) (snd x) =
ap snd
(((funx0 : Unit * X =>
(funfst : Unit =>
match
fst as u
return
(forallsnd0 : X,
(tt, snd (u, snd0)) = (u, snd0))
with
| tt => funx1 : X => 1end) (fst x0) (snd x0))
:
(funx0 : X => (tt, x0)) o snd == idmap) x)
intros [[] x]; reflexivity.Defined.(** ** Universal mapping properties *)(** Ordinary universal mapping properties are expressed as equivalences of sets or spaces of functions. In type theory, we can go beyond this and express an equivalence of types of *dependent* functions. Moreover, because the product type can expressed both positively and negatively, it has both a left universal property and a right one. *)(* First the positive universal property. *)Instanceisequiv_prod_ind `(P : A * B -> Type)
: IsEquiv (prod_ind P) | 0
:= Build_IsEquiv
_ _
(prod_ind P)
(funfxy => f (x, y))
(fun_ => 1)
(fun_ => 1)
(fun_ => 1).Definitionequiv_prod_ind `(P : A * B -> Type)
: (forall (a : A) (b : B), P (a, b)) <~> (forallp : A * B, P p)
:= Build_Equiv _ _ (prod_ind P) _.(* The non-dependent version, which is a special case, is the currying equivalence. *)Definitionequiv_uncurry (ABC : Type)
: (A -> B -> C) <~> (A * B -> C)
:= equiv_prod_ind (fun_ => C).(* Now the negative universal property. *)Definitionprod_coind_uncurried `{A : X -> Type} `{B : X -> Type}
: (forallx, A x) * (forallx, B x) -> (forallx, A x * B x)
:= funfgx => (fst fg x, snd fg x).Definitionprod_coind `(f : forallx:X, A x) `(g : forallx:X, B x)
: forallx, A x * B x
:= prod_coind_uncurried (f, g).Instanceisequiv_prod_coind `(A : X -> Type) (B : X -> Type)
: IsEquiv (@prod_coind_uncurried X A B) | 0
:= Build_IsEquiv
_ _
(@prod_coind_uncurried X A B)
(funh => (funx => fst (h x), funx => snd (h x)))
(fun_ => 1)
(fun_ => 1)
(fun_ => 1).Definitionequiv_prod_coind `(A : X -> Type) (B : X -> Type)
: ((forallx, A x) * (forallx, B x)) <~> (forallx, A x * B x)
:= Build_Equiv _ _ (@prod_coind_uncurried X A B) _.(** ** Products preserve truncation *)
A: Type n: trunc_index IsTrunc0: IsTrunc n A B: Type IsTrunc1: IsTrunc n B
IsTrunc n (A * B)
A: Type n: trunc_index IsTrunc0: IsTrunc n A B: Type IsTrunc1: IsTrunc n B
IsTrunc n (A * B)
n: trunc_index
forallA : Type,
IsTrunc n A ->
forallB : Type, IsTrunc n B -> IsTrunc n (A * B)
A: Type IsTrunc0: Contr A B: Type IsTrunc1: Contr B
Contr (A * B)
n: trunc_index IH: forallA : Type,
IsTrunc n A ->
forallB : Type, IsTrunc n B -> IsTrunc n (A * B) A: Type IsTrunc0: IsTrunc n.+1 A B: Type IsTrunc1: IsTrunc n.+1 B
IsTrunc n.+1 (A * B)
A: Type IsTrunc0: Contr A B: Type IsTrunc1: Contr B
Contr (A * B)
A: Type IsTrunc0: Contr A B: Type IsTrunc1: Contr B
forally : A * B, (center A, center B) = y
intros z; apply path_prod; apply contr.
n: trunc_index IH: forallA : Type,
IsTrunc n A ->
forallB : Type, IsTrunc n B -> IsTrunc n (A * B) A: Type IsTrunc0: IsTrunc n.+1 A B: Type IsTrunc1: IsTrunc n.+1 B
IsTrunc n.+1 (A * B)
n: trunc_index IH: forallA : Type,
IsTrunc n A ->
forallB : Type, IsTrunc n B -> IsTrunc n (A * B) A: Type IsTrunc0: IsTrunc n.+1 A B: Type IsTrunc1: IsTrunc n.+1 B
forallxy : A * B, IsTrunc n (x = y)
n: trunc_index IH: forallA : Type,
IsTrunc n A ->
forallB : Type, IsTrunc n B -> IsTrunc n (A * B) A: Type IsTrunc0: IsTrunc n.+1 A B: Type IsTrunc1: IsTrunc n.+1 B x, y: A * B
IsTrunc n (x = y)
exact (istrunc_equiv_istrunc _ (equiv_path_prod x y)).Defined.Instancecontr_prod `{CA : Contr A} `{CB : Contr B} : Contr (A * B) | 100
:= istrunc_prod.(** ** Decidability *)
A, B: Type H: Decidable A H0: Decidable B
Decidable (A * B)
A, B: Type H: Decidable A H0: Decidable B
Decidable (A * B)
A, B: Type H: Decidable A H0: Decidable B x1: A x2: B
Decidable (A * B)
A, B: Type H: Decidable A H0: Decidable B x1: A y2: ~ B
Decidable (A * B)
A, B: Type H: Decidable A H0: Decidable B y1: ~ A x2: B
Decidable (A * B)
A, B: Type H: Decidable A H0: Decidable B y1: ~ A y2: ~ B
Decidable (A * B)
A, B: Type H: Decidable A H0: Decidable B x1: A x2: B
Decidable (A * B)
exact (inl@{k k} (x1,x2)).
A, B: Type H: Decidable A H0: Decidable B x1: A y2: ~ B
Decidable (A * B)
apply inr@{k k}; intros [_ x2]; exact (y2 x2).
A, B: Type H: Decidable A H0: Decidable B y1: ~ A x2: B
Decidable (A * B)
apply inr@{k k}; intros [x1 _]; exact (y1 x1).
A, B: Type H: Decidable A H0: Decidable B y1: ~ A y2: ~ B
Decidable (A * B)
apply inr@{k k}; intros [x1 _]; exact (y1 x1).Defined.(** Interaction of [ap] and uncurry *)(** The function in [ap011] can be uncurried *)
A, B, C: Type f: A -> B -> C a, a': A p: a = a' b, b': B q: b = b'
ap (uncurry f) (path_prod' p q) = ap011 f p q
A, B, C: Type f: A -> B -> C a, a': A p: a = a' b, b': B q: b = b'
ap (uncurry f) (path_prod' p q) = ap011 f p q
bydestruct q, p.Defined.(** Fibers *)(** ** Fibers of [functor_prod] *)
A, B, C, D: Type f: A -> B g: C -> D y: B * D
hfiber (functor_prod f g) y <~>
hfiber f (fst y) * hfiber g (snd y)
A, B, C, D: Type f: A -> B g: C -> D y: B * D
hfiber (functor_prod f g) y <~>
hfiber f (fst y) * hfiber g (snd y)
A, B, C, D: Type f: A -> B g: C -> D y: B * D
hfiber (funz : A * C => (f (fst z), g (snd z))) y <~>
hfiber f (fst y) * hfiber g (snd y)
A, B, C, D: Type f: A -> B g: C -> D y: B * D
hfiber (funz : A * C => (f (fst z), g (snd z))) y ->
hfiber f (fst y) * hfiber g (snd y)
A, B, C, D: Type f: A -> B g: C -> D y: B * D
hfiber f (fst y) * hfiber g (snd y) ->
hfiber (funz : A * C => (f (fst z), g (snd z))) y
A, B, C, D: Type f: A -> B g: C -> D y: B * D
?f o ?g == idmap
A, B, C, D: Type f: A -> B g: C -> D y: B * D
?g o ?f == idmap
A, B, C, D: Type f: A -> B g: C -> D y: B * D
hfiber (funz : A * C => (f (fst z), g (snd z))) y ->
hfiber f (fst y) * hfiber g (snd y)
exact (funx => ((fst x.1; ap fst x.2), (snd x.1; ap snd x.2))).
A, B, C, D: Type f: A -> B g: C -> D y: B * D
hfiber f (fst y) * hfiber g (snd y) ->
hfiber (funz : A * C => (f (fst z), g (snd z))) y
A, B, C, D: Type f: A -> B g: C -> D y: B * D xs: hfiber f (fst y) * hfiber g (snd y)
(f (fst ((fst xs).1, (snd xs).1)),
g (snd ((fst xs).1, (snd xs).1))) = y
A, B, C, D: Type f: A -> B g: C -> D y: B * D xs: hfiber f (fst y) * hfiber g (snd y)
f (fst xs).1 = fst y
A, B, C, D: Type f: A -> B g: C -> D y: B * D xs: hfiber f (fst y) * hfiber g (snd y)
g (snd xs).1 = snd y
A, B, C, D: Type f: A -> B g: C -> D y: B * D xs: hfiber f (fst y) * hfiber g (snd y)
f (fst xs).1 = fst y
exact (fst xs).2.
A, B, C, D: Type f: A -> B g: C -> D y: B * D xs: hfiber f (fst y) * hfiber g (snd y)
g (snd xs).1 = snd y
exact (snd xs).2.
A, B, C, D: Type f: A -> B g: C -> D y: B * D
(funx : hfiber
(funz : A * C => (f (fst z), g (snd z))) y
=> ((fst x.1; ap fst x.2), (snd x.1; ap snd x.2)))
o (funxs : hfiber f (fst y) * hfiber g (snd y) =>
(((fst xs).1, (snd xs).1);
path_prod
(f (fst ((fst xs).1, (snd xs).1)),
g (snd ((fst xs).1, (snd xs).1))) y
((fst xs).2
:
fst
(f (fst ((fst xs).1, (snd xs).1)),
g (snd ((fst xs).1, (snd xs).1))) = fst y)
((snd xs).2
:
snd
(f (fst ((fst xs).1, (snd xs).1)),
g (snd ((fst xs).1, (snd xs).1))) = snd y))) ==
idmap
A, B, C, D: Type f: A -> B g: C -> D y1: B y2: D x1: A p1: f x1 = fst (y1, y2) x2: C p2: g x2 = snd (y1, y2)