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.
[Loading ML file number_string_notation_plugin.cmxs (using legacy method) ... done]
LocalOpen Scope mc_scope.LocalOpen Scope mc_mult_scope.(** [IsFreeGroup] is defined in Group.v. In this file we construct free groups and and prove properties about them. *)(** We construct the free group on a type [A] as a higher inductive type. This construction is due to Kraus-Altenkirch 2018 arXiv:1805.02069. Their construction is actually more general, but we set-truncate it to suit our needs which is the free group as a set. This is a very simple HIT in a similar manner to the abelianization HIT used in Algebra.AbGroup.Abelianization. *)SectionReduction.Universeu.Context (A : Type@{u}).LocalOpen Scope list_scope.(** We define words (with inverses) on A to be lists of marked elements of A *)Local DefinitionWords : Type@{u} := list (A + A).(** Given a marked element of A we can change its mark *)Local Definitionchange_sign : A + A -> A + A := equiv_sum_symm A A.(** We introduce a local notation for [change_sign]. It is only defined in this section however. *)LocalNotation"a ^'" := (change_sign a) (at level1).(** Changing sign is an involution *)
A: Type a: (A + A)%type
(a ^') ^' = a
A: Type a: (A + A)%type
(a ^') ^' = a
bydestruct a.Defined.(** Now we wish to define the free group on A as the following HIT: HIT N(A) : hSet | eta : Words -> N(A) | tau (x : Words) (a : A + A) (y : Words) : eta (x ++ [a] ++ [a^] ++ y) = eta (x ++ y). Since we cannot write our HITs directly like this (without resorting to private inductive types), we will construct this HIT out of HITs we know. In fact, we can define N(A) as a coequalizer. *)
exact (x ++ y).Defined.Arguments map2 _ /.(** Now we can define the underlying type of the free group as the 0-truncated coequalizer of these two maps *)Definitionfreegroup_type : Type := Tr 0 (Coeq map1 map2).(** This is the point constructor *)Definitionfreegroup_eta : Words -> freegroup_type := tr o coeq.(** This is the path constructor *)
forallb : Words * (A + A) * Words,
(funx : Words =>
Coeq_rec freegroup_type
(funy : Words => freegroup_eta (x ++ y))
(funb0 : Words * (A + A) * Words =>
(funfst : Words * (A + A) =>
(fun (y : Words) (a : A + A) (z : Words) =>
(ap freegroup_eta
(app_assoc x y ([a] ++ [a ^'] ++ z)) @
freegroup_tau (x ++ y) a z) @
(ap freegroup_eta (app_assoc x y z))^
:
freegroup_eta (x ++ map1 (y, a, z)) =
freegroup_eta (x ++ map2 (y, a, z)))
(Overture.fst fst) (snd fst)) (fst b0) (snd b0))
y) (map1 b) =
(funx : Words =>
Coeq_rec freegroup_type
(funy : Words => freegroup_eta (x ++ y))
(funb0 : Words * (A + A) * Words =>
(funfst : Words * (A + A) =>
(fun (y : Words) (a : A + A) (z : Words) =>
(ap freegroup_eta
(app_assoc x y ([a] ++ [a ^'] ++ z)) @
freegroup_tau (x ++ y) a z) @
(ap freegroup_eta (app_assoc x y z))^
:
freegroup_eta (x ++ map1 (y, a, z)) =
freegroup_eta (x ++ map2 (y, a, z)))
(Overture.fst fst) (snd fst)) (fst b0) (snd b0))
y) (map2 b)
A: Type y: Coeq map1 map2 c: Words b: (A + A)%type d: Words
Coeq_rec freegroup_type
(funy : Words =>
freegroup_eta (map1 (c, b, d) ++ y))
(funb0 : Words * (A + A) * Words =>
(ap freegroup_eta
(app_assoc (map1 (c, b, d)) (fst (fst b0))
([snd (fst b0)] ++
[(snd (fst b0)) ^'] ++ snd b0)) @
freegroup_tau (map1 (c, b, d) ++ fst (fst b0))
(snd (fst b0)) (snd b0)) @
(ap freegroup_eta
(app_assoc (map1 (c, b, d)) (fst (fst b0))
(snd b0)))^) y =
Coeq_rec freegroup_type
(funy : Words =>
freegroup_eta (map2 (c, b, d) ++ y))
(funb0 : Words * (A + A) * Words =>
(ap freegroup_eta
(app_assoc (map2 (c, b, d)) (fst (fst b0))
([snd (fst b0)] ++
[(snd (fst b0)) ^'] ++ snd b0)) @
freegroup_tau (map2 (c, b, d) ++ fst (fst b0))
(snd (fst b0)) (snd b0)) @
(ap freegroup_eta
(app_assoc (map2 (c, b, d)) (fst (fst b0))
(snd b0)))^) y
A: Type c: Words b: (A + A)%type d: Words
forally : Coeq map1 map2,
Coeq_rec freegroup_type
(funy0 : Words =>
freegroup_eta (map1 (c, b, d) ++ y0))
(funb0 : Words * (A + A) * Words =>
(ap freegroup_eta
(app_assoc (map1 (c, b, d)) (fst (fst b0))
([snd (fst b0)] ++
[(snd (fst b0)) ^'] ++ snd b0)) @
freegroup_tau (map1 (c, b, d) ++ fst (fst b0))
(snd (fst b0)) (snd b0)) @
(ap freegroup_eta
(app_assoc (map1 (c, b, d)) (fst (fst b0))
(snd b0)))^) y =
Coeq_rec freegroup_type
(funy0 : Words =>
freegroup_eta (map2 (c, b, d) ++ y0))
(funb0 : Words * (A + A) * Words =>
(ap freegroup_eta
(app_assoc (map2 (c, b, d)) (fst (fst b0))
([snd (fst b0)] ++
[(snd (fst b0)) ^'] ++ snd b0)) @
freegroup_tau (map2 (c, b, d) ++ fst (fst b0))
(snd (fst b0)) (snd b0)) @
(ap freegroup_eta
(app_assoc (map2 (c, b, d)) (fst (fst b0))
(snd b0)))^) y
A: Type c: Words b: (A + A)%type d: Words
foralla : Words,
(funx : Coeq map1 map2 =>
Coeq_rec freegroup_type
(funy : Words =>
freegroup_eta (map1 (c, b, d) ++ y))
(funb0 : Words * (A + A) * Words =>
(ap freegroup_eta
(app_assoc (map1 (c, b, d)) (fst (fst b0))
([snd (fst b0)] ++
[(snd (fst b0)) ^'] ++ snd b0)) @
freegroup_tau (map1 (c, b, d) ++ fst (fst b0))
(snd (fst b0)) (snd b0)) @
(ap freegroup_eta
(app_assoc (map1 (c, b, d)) (fst (fst b0))
(snd b0)))^) x =
Coeq_rec freegroup_type
(funy : Words =>
freegroup_eta (map2 (c, b, d) ++ y))
(funb0 : Words * (A + A) * Words =>
(ap freegroup_eta
(app_assoc (map2 (c, b, d)) (fst (fst b0))
([snd (fst b0)] ++
[(snd (fst b0)) ^'] ++ snd b0)) @
freegroup_tau (map2 (c, b, d) ++ fst (fst b0))
(snd (fst b0)) (snd b0)) @
(ap freegroup_eta
(app_assoc (map2 (c, b, d)) (fst (fst b0))
(snd b0)))^) x) (coeq a)
A: Type c: Words b: (A + A)%type d, a: Words
(funx : Coeq map1 map2 =>
Coeq_rec freegroup_type
(funy : Words =>
freegroup_eta (map1 (c, b, d) ++ y))
(funb0 : Words * (A + A) * Words =>
(ap freegroup_eta
(app_assoc (map1 (c, b, d)) (fst (fst b0))
([snd (fst b0)] ++
[(snd (fst b0)) ^'] ++ snd b0)) @
freegroup_tau (map1 (c, b, d) ++ fst (fst b0))
(snd (fst b0)) (snd b0)) @
(ap freegroup_eta
(app_assoc (map1 (c, b, d)) (fst (fst b0))
(snd b0)))^) x =
Coeq_rec freegroup_type
(funy : Words =>
freegroup_eta (map2 (c, b, d) ++ y))
(funb0 : Words * (A + A) * Words =>
(ap freegroup_eta
(app_assoc (map2 (c, b, d)) (fst (fst b0))
([snd (fst b0)] ++
[(snd (fst b0)) ^'] ++ snd b0)) @
freegroup_tau (map2 (c, b, d) ++ fst (fst b0))
(snd (fst b0)) (snd b0)) @
(ap freegroup_eta
(app_assoc (map2 (c, b, d)) (fst (fst b0))
(snd b0)))^) x) (coeq a)
A: Type c: Words b: (A + A)%type d, a: Words
freegroup_eta ((c ++ [b] ++ [b ^'] ++ d) ++ a) =
freegroup_eta ((c ++ d) ++ a)
A: Type c: Words b: (A + A)%type d, a: Words
?Goal = (c ++ [b] ++ [b ^'] ++ d) ++ a
A: Type c: Words b: (A + A)%type d, a: Words
freegroup_eta ?Goal = freegroup_eta ((c ++ d) ++ a)
A: Type c: Words b: (A + A)%type d, a: Words
freegroup_eta (c ++ ([b] ++ [b ^'] ++ d) ++ a) =
freegroup_eta ((c ++ d) ++ a)
freegroup_eta (c ++ [b] ++ [b ^'] ++ d ++ a) =
freegroup_eta ((c ++ d) ++ a)
A: Type c: Words b: (A + A)%type d, a: Words
freegroup_eta (c ++ [b] ++ [b ^'] ++ d ++ a) =
freegroup_eta ?Goal
A: Type c: Words b: (A + A)%type d, a: Words
?Goal = (c ++ d) ++ a
A: Type c: Words b: (A + A)%type d, a: Words
freegroup_eta (c ++ [b] ++ [b ^'] ++ d ++ a) =
freegroup_eta (c ++ d ++ a)
napply freegroup_tau.Defined.(** The unit of the free group is the empty word *)#[export] Instancemonunit_freegroup_type : MonUnit freegroup_type
:= freegroup_eta nil.(** We can change the sign of all the elements in a word and reverse the order. This will be the inversion in the group *)Definitionword_change_sign (x : Words) : Words
:= reverse (list_map change_sign x).(** Changing the sign changes the order of word concatenation *)
A: Type x, y: Words
word_change_sign (x ++ y) =
word_change_sign y ++ word_change_sign x
A: Type x, y: Words
word_change_sign (x ++ y) =
word_change_sign y ++ word_change_sign x
forallx0 : A + A,
InList x0 x -> (funx : A + A => (x ^') ^') x0 = x0
A: Type x: Words a: (A + A)%type X: InList a x
(funx : A + A => (x ^') ^') a = a
apply change_sign_inv.Defined.(** Changing the sign gives us left inverses *)
A: Type x: Words
freegroup_eta (word_change_sign x ++ x) = 1
A: Type x: Words
freegroup_eta (word_change_sign x ++ x) = 1
A: Type
freegroup_eta (word_change_sign nil ++ nil) = 1
A: Type a: (A + A)%type x: list (A + A) IHx: freegroup_eta (word_change_sign x ++ x) = 1
freegroup_eta (word_change_sign (a :: x) ++ a :: x) =
1
A: Type a: (A + A)%type x: list (A + A) IHx: freegroup_eta (word_change_sign x ++ x) = 1
freegroup_eta (word_change_sign (a :: x) ++ a :: x) =
1
A: Type a: (A + A)%type x: list (A + A) IHx: freegroup_eta (word_change_sign x ++ x) = 1
word_change_sign (a :: x) = ?Goal
A: Type a: (A + A)%type x: list (A + A) IHx: freegroup_eta (word_change_sign x ++ x) = 1
freegroup_eta (?Goal ++ a :: x) = 1
A: Type a: (A + A)%type x: list (A + A) IHx: freegroup_eta (word_change_sign x ++ x) = 1
freegroup_eta
((reverse
((fix list_map
(A B : Type) (f : A -> B) (l : list A)
{struct l} : list B :=
match l with
| nil => nil
| x :: l0 => f x :: list_map A B f l0
end) (A + A)%type (A + A)%type change_sign x) ++
[a ^']) ++ a :: x) = 1
A: Type a: (A + A)%type x: list (A + A) IHx: freegroup_eta (word_change_sign x ++ x) = 1
A: Type x: list (A + A) x':= word_change_sign x: Words
freegroup_eta (word_change_sign x' ++ x') = 1
apply word_concat_Vw.Defined.(** Inverses are defined by changing the order of a word that appears in eta. Most of the work here is checking that it is agreeable with the path constructor. *)
A: Type
Inverse freegroup_type
A: Type
Inverse freegroup_type
A: Type x: freegroup_type
freegroup_type
A: Type x: Coeq map1 map2
freegroup_type
A: Type
Words -> freegroup_type
A: Type
forallb : Words * (A + A) * Words,
?coeq' (map1 b) = ?coeq' (map2 b)
A: Type
Words -> freegroup_type
A: Type x: Words
freegroup_type
A: Type x: Words
Words
exact (word_change_sign x).
A: Type
forallb : Words * (A + A) * Words,
(funx : Words => freegroup_eta (word_change_sign x))
(map1 b) =
(funx : Words => freegroup_eta (word_change_sign x))
(map2 b)
A: Type b: Words a: (A + A)%type c: Words
freegroup_eta (word_change_sign (map1 (b, a, c))) =
freegroup_eta (word_change_sign (map2 (b, a, c)))
freegroup_eta
(word_change_sign c ++
[a] ++ word_change_sign [a] ++ word_change_sign b) =
freegroup_eta (word_change_sign (b ++ c))
A: Type b: Words a: (A + A)%type c: Words
freegroup_eta
(word_change_sign c ++
[a] ++ word_change_sign [a] ++ word_change_sign b) =
freegroup_eta ?Goal
A: Type b: Words a: (A + A)%type c: Words
word_change_sign (b ++ c) = ?Goal
A: Type b: Words a: (A + A)%type c: Words
freegroup_eta
(word_change_sign c ++
[a] ++ word_change_sign [a] ++ word_change_sign b) =
freegroup_eta
(word_change_sign c ++ word_change_sign b)
napply freegroup_tau.Defined.(** Now we can start to prove the group laws. Since these are hprops we can ignore what happens with the path constructor. *)(** Our operation is associative *)
A: Type
Associative sg_op
A: Type
Associative sg_op
A: Type x, y, z: freegroup_type
x * (y * z) = x * y * z
A: Type z, y, x: Coeq map1 map2
tr x * (tr y * tr z) = tr x * tr y * tr z
A: Type z, y: Coeq map1 map2 x: Words
(funx : Coeq map1 map2 =>
tr x * (tr y * tr z) = tr x * tr y * tr z) (coeq x)
exact (s x)^.Defined.(** When we have a list of words we can recursively define a group element. The obvious choice would be to map [nil] to the identity and [x :: xs] to [x * words_rec xs]. This has the disadvantage that a single generating element gets mapped to [x * 1] instead of [x]. To fix this issue, we map [nil] to the identity, the singleton to the element we want, and do the rest recursively. *)
A: Type G: Group s: A -> G
Words -> G
A: Type G: Group s: A -> G
Words -> G
A: Type G: Group s: A -> G xs: Words
G
A: Type G: Group s: A -> G
G
A: Type G: Group s: A -> G x: (A + A)%type IHxs: G
G
A: Type G: Group s: A -> G x, y: (A + A)%type xs: list (A + A) IHxs: G
G
A: Type G: Group s: A -> G
G
exact mon_unit.
A: Type G: Group s: A -> G x: (A + A)%type IHxs: G
G
exact (word_rec G s x).
A: Type G: Group s: A -> G x, y: (A + A)%type xs: list (A + A) IHxs: G
G
exact (word_rec G s x * IHxs).Defined.
A: Type G: Group s: A -> G x: (A + A)%type xs: Words
words_rec G s (x :: xs) =
word_rec G s x * words_rec G s xs
A: Type G: Group s: A -> G x: (A + A)%type xs: Words
words_rec G s (x :: xs) =
word_rec G s x * words_rec G s xs
A: Type G: Group s: A -> G x: (A + A)%type
words_rec G s [x] = word_rec G s x * words_rec G s nil
A: Type G: Group s: A -> G x, a: (A + A)%type xs: list (A + A) IHxs: forallx : A + A,
words_rec G s (x :: xs) =
word_rec G s x * words_rec G s xs
words_rec G s (x :: a :: xs) =
word_rec G s x * words_rec G s (a :: xs)
A: Type G: Group s: A -> G x: (A + A)%type
words_rec G s [x] = word_rec G s x * words_rec G s nil
symmetry; napply grp_unit_r.
A: Type G: Group s: A -> G x, a: (A + A)%type xs: list (A + A) IHxs: forallx : A + A,
words_rec G s (x :: xs) =
word_rec G s x * words_rec G s xs
words_rec G s (x :: a :: xs) =
word_rec G s x * words_rec G s (a :: xs)
reflexivity.Defined.
A: Type G: Group s: A -> G x, y: Words
words_rec G s (x ++ y) =
words_rec G s x * words_rec G s y
A: Type G: Group s: A -> G x, y: Words
words_rec G s (x ++ y) =
words_rec G s x * words_rec G s y
A: Type G: Group s: A -> G y: Words
words_rec G s (nil ++ y) =
words_rec G s nil * words_rec G s y
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
words_rec G s ((x :: xs) ++ y) =
words_rec G s (x :: xs) * words_rec G s y
A: Type G: Group s: A -> G y: Words
words_rec G s (nil ++ y) =
words_rec G s nil * words_rec G s y
symmetry; napply grp_unit_l.
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
words_rec G s ((x :: xs) ++ y) =
words_rec G s (x :: xs) * words_rec G s y
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
words_rec G s (x :: xs ++ y) =
words_rec G s (x :: xs) * words_rec G s y
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
word_rec G s x * words_rec G s (xs ++ y) =
words_rec G s (x :: xs) * words_rec G s y
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
words_rec G s (xs ++ y) = ?Goal
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
word_rec G s x * ?Goal =
words_rec G s (x :: xs) * words_rec G s y
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
word_rec G s x * (words_rec G s xs * words_rec G s y) =
words_rec G s (x :: xs) * words_rec G s y
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
word_rec G s x * words_rec G s xs * words_rec G s y =
words_rec G s (x :: xs) * words_rec G s y
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
word_rec G s x * words_rec G s xs =
words_rec G s (x :: xs)
A: Type G: Group s: A -> G x: (A + A)%type xs: list (A + A) y: Words IHxs: forally : Words,
words_rec G s (xs ++ y) =
words_rec G s xs * words_rec G s y
words_rec G s (x :: xs) =
word_rec G s x * words_rec G s xs
apply words_rec_cons.Defined.
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s (map1 (b, a, c)) =
words_rec G s (map2 (b, a, c))
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s (map1 (b, a, c)) =
words_rec G s (map2 (b, a, c))
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s (b ++ [a] ++ [a ^'] ++ c) =
words_rec G s (b ++ c)
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s (b ++ [a] ++ [a ^'] ++ c) =
words_rec G s b * words_rec G s c
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s b * words_rec G s ([a] ++ [a ^'] ++ c) =
words_rec G s b * words_rec G s c
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s ([a] ++ [a ^'] ++ c) = words_rec G s c
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s [a] * words_rec G s ([a ^'] ++ c) =
words_rec G s c
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s ([a ^'] ++ c) = ?Goal
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s [a] * ?Goal = words_rec G s c
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s [a] *
(words_rec G s [a ^'] * words_rec G s c) =
words_rec G s c
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s [a] * words_rec G s [a ^'] *
words_rec G s c = words_rec G s c
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s [a] * words_rec G s [a ^'] *
words_rec G s c = 1 * words_rec G s c
A: Type G: Group s: A -> G a: (A + A)%type b, c: Words
words_rec G s [a] * words_rec G s [a ^'] = 1
A: Type G: Group s: A -> G a: A b, c: Words
s a * (s a)^ = 1
A: Type G: Group s: A -> G a: A b, c: Words
(s a)^ * s a = 1
A: Type G: Group s: A -> G a: A b, c: Words
s a * (s a)^ = 1
napply grp_inv_r.
A: Type G: Group s: A -> G a: A b, c: Words
(s a)^ * s a = 1
napply grp_inv_l.Defined.(** Given a group [G] we can construct a group homomorphism [FreeGroup A $-> G] if we have a map [A -> G]. *)
A: Type G: Group s: A -> G
FreeGroup $-> G
A: Type G: Group s: A -> G
FreeGroup $-> G
A: Type G: Group s: A -> G
FreeGroup -> G
A: Type G: Group s: A -> G
IsSemiGroupPreserving ?grp_homo_map
A: Type G: Group s: A -> G
FreeGroup -> G
A: Type G: Group s: A -> G
Coeq map1 map2 -> G
A: Type G: Group s: A -> G
Words -> G
A: Type G: Group s: A -> G
forallb : Words * (A + A) * Words,
?coeq' (map1 b) = ?coeq' (map2 b)
A: Type G: Group s: A -> G
forallb : Words * (A + A) * Words,
words_rec G s (map1 b) = words_rec G s (map2 b)
A: Type G: Group s: A -> G b: Words a: (A + A)%type c: Words
words_rec G s (map1 (b, a, c)) =
words_rec G s (map2 (b, a, c))
apply words_rec_coh.
A: Type G: Group s: A -> G
IsSemiGroupPreserving
(Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
(funfst : Words * (A + A) =>
(fun (b : Words) (a : A + A) (c : Words) =>
words_rec_coh G s a b c) (Overture.fst fst)
(snd fst)) (fst b0) (snd b0))))
A: Type G: Group s: A -> G y, x: Coeq map1 map2
Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
words_rec_coh G s (snd (fst b0)) (fst (fst b0))
(snd b0))) (tr x * tr y) =
Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
words_rec_coh G s (snd (fst b0)) (fst (fst b0))
(snd b0))) (tr x) *
Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
words_rec_coh G s (snd (fst b0)) (fst (fst b0))
(snd b0))) (tr y)
A: Type G: Group s: A -> G y: Coeq map1 map2 x: Words
(funx : Coeq map1 map2 =>
Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
words_rec_coh G s (snd (fst b0)) (fst (fst b0))
(snd b0))) (tr x * tr y) =
Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
words_rec_coh G s (snd (fst b0)) (fst (fst b0))
(snd b0))) (tr x) *
Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
words_rec_coh G s (snd (fst b0)) (fst (fst b0))
(snd b0))) (tr y)) (coeq x)
A: Type G: Group s: A -> G x, y: Words
(funx0 : Coeq map1 map2 =>
(funx : Coeq map1 map2 =>
Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
words_rec_coh G s (snd (fst b0))
(fst (fst b0)) (snd b0))) (tr x * tr x0) =
Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
words_rec_coh G s (snd (fst b0))
(fst (fst b0)) (snd b0))) (tr x) *
Trunc_rec
(Coeq_rec G (words_rec G s)
(funb0 : Words * (A + A) * Words =>
words_rec_coh G s (snd (fst b0))
(fst (fst b0)) (snd b0))) (tr x0)) (coeq x))
(coeq y)
A: Type G: Group s: A -> G x, y: Words
words_rec G s (x ++ y) =
words_rec G s x * words_rec G s y
apply words_rec_pp.Defined.Definitionfreegroup_in : A -> FreeGroup
:= freegroup_eta o (funx => [ x ]) o inl.DefinitionFreeGroup_rec_beta {G : Group} (f : A -> G)
: FreeGroup_rec f o freegroup_in == f
:= fun_ => idpath.Coercionfreegroup_in : A >-> group_type.
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: forallw : Words, P (freegroup_eta w)
forallx : FreeGroup, P x
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: forallw : Words, P (freegroup_eta w)
forallx : FreeGroup, P x
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: forallw : Words, P (freegroup_eta w)
foralla : Coeq map1 map2, P (tr a)
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: forallw : Words, P (freegroup_eta w)
foralla : Words,
(funx : Coeq map1 map2 => P (tr x)) (coeq a)
exact H1.Defined.
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y)
forallx : FreeGroup, P x
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y)
forallx : FreeGroup, P x
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y)
forallw : Words, P (freegroup_eta w)
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) w: Words
P (freegroup_eta w)
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y)
P (freegroup_eta nil)
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: (A + A)%type w: list (A + A) IHw: P (freegroup_eta w)
P (freegroup_eta (a :: w))
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y)
P (freegroup_eta nil)
exact H1.
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: (A + A)%type w: list (A + A) IHw: P (freegroup_eta w)
P (freegroup_eta (a :: w))
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P (freegroup_eta (inl a :: w))
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P (freegroup_eta (inr a :: w))
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P (freegroup_eta (inl a :: w))
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P (a * freegroup_eta w)
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P ((a^)^ * freegroup_eta w)
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P a^
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P (freegroup_eta w)
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P a^
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P (a^ * 1)
byapply Hop.
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P (freegroup_eta w)
assumption.
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P (freegroup_eta (inr a :: w))
A: Type P: FreeGroup -> Type H: forallx : FreeGroup, IsHProp (P x) H1: P 1 Hin: forallx : A, P x Hop: forallxy : FreeGroup, P x -> P y -> P (x^ * y) a: A w: list (A + A) IHw: P (freegroup_eta w)
P (a^ * freegroup_eta w)
byapply Hop.Defined.
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x
f $== f'
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x
f $== f'
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x
f 1 = f' 1
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x
forallx : A, f x = f' x
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x
forallxy : FreeGroup,
f x = f' x -> f y = f' y -> f (x^ * y) = f' (x^ * y)
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x
forallx : A, f x = f' x
exact H.
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x
forallxy : FreeGroup,
f x = f' x -> f y = f' y -> f (x^ * y) = f' (x^ * y)
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x x, y: FreeGroup p: f x = f' x q: f y = f' y
f (x^ * y) = f' (x^ * y)
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x x, y: FreeGroup p: f x = f' x q: f y = f' y
f x^ = f' x^
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x x, y: FreeGroup p: f x = f' x q: f y = f' y
(f x)^ = f' x^
A: Type G: Group f, f': FreeGroup $-> G H: forallx : A, f x = f' x x, y: FreeGroup p: f x = f' x q: f y = f' y
(f x)^ = (f' x)^
exact (ap _ p).Defined.(** Now we need to prove that the free group satisfies the universal property of the free group. *)(** TODO: remove funext from here and universal property of free group *)
A: Type H: Funext
IsFreeGroupOn A FreeGroup freegroup_in
A: Type H: Funext
IsFreeGroupOn A FreeGroup freegroup_in
A: Type H: Funext G: Group f: A -> G
Contr
(FactorsThroughFreeGroup A FreeGroup freegroup_in G
f)
A: Type H: Funext G: Group f: A -> G
FactorsThroughFreeGroup A FreeGroup freegroup_in G f
A: Type H: Funext G: Group f: A -> G
forally : FactorsThroughFreeGroup A FreeGroup freegroup_in G
f, ?center = y
A: Type H: Funext G: Group f: A -> G
FactorsThroughFreeGroup A FreeGroup freegroup_in G f
A: Type H: Funext G: Group f: A -> G
GroupHomomorphism FreeGroup G
A: Type H: Funext G: Group f: A -> G
(funx : A => ?Goal x) == f
A: Type H: Funext G: Group f: A -> G
(funx : A => FreeGroup_rec f x) == f
intro x; reflexivity.
A: Type H: Funext G: Group f: A -> G
forally : FactorsThroughFreeGroup A FreeGroup freegroup_in G
f,
(FreeGroup_rec f : FreeGroup $-> G;
((funx : A => 1%path)
:
(funx : A => FreeGroup_rec f x) == f)
:
(funf0 : FreeGroup $-> G => f0 o freegroup_in == f)
(FreeGroup_rec f : FreeGroup $-> G)) = y
A: Type H: Funext G: Group f: A -> G g: FreeGroup $-> G h: (funx : A => g x) == f
(FreeGroup_rec f; funx : A => 1%path) = (g; h)
A: Type H: Funext G: Group f: A -> G g: FreeGroup $-> G h: (funx : A => g x) == f
(FreeGroup_rec f; funx : A => 1%path).1 = (g; h).1
A: Type H: Funext G: Group f: A -> G g: FreeGroup $-> G h: (funx : A => g x) == f
FreeGroup_rec f = g
A: Type H: Funext G: Group f: A -> G g: FreeGroup $-> G h: (funx : A => g x) == f
FreeGroup_rec f == g
A: Type H: Funext G: Group f: A -> G g: FreeGroup $-> G h: (funx : A => g x) == f
g == FreeGroup_rec f
A: Type H: Funext G: Group f: A -> G g: FreeGroup $-> G h: (funx : A => g x) == f
forallx : A, g x = FreeGroup_rec f x
exact h.Defined.(** Typeclass search can already find this but we leave it here as a definition for reference. *)Definitionisfreegroup_freegroup `{Funext} : IsFreeGroup FreeGroup := _.EndReduction.Arguments FreeGroup_rec {A G}.Arguments FreeGroup_ind_homotopy {A G f f'}.Arguments freegroup_eta {A}.Arguments freegroup_in {A}.(** Properties of free groups *)(* Given a function on the generators, there is an induced group homomorphism from the free group. *)Definitionisfreegroupon_rec {S : Type} {F_S : Group}
{i : S -> F_S} `{IsFreeGroupOn S F_S i}
{G : Group} (f : S -> G) : F_S $-> G
:= (center (FactorsThroughFreeGroup S F_S i G f)).1.(* The propositional computation rule for the recursor. *)Definitionisfreegroupon_rec_beta
{S : Type} {F_S : Group} {i : S -> F_S} `{IsFreeGroupOn S F_S i}
{G : Group} (f : S -> G)
: isfreegroupon_rec f o i == f
:= (center (FactorsThroughFreeGroup S F_S i G f)).2.(* Two homomorphisms from a free group are equal if they agree on the generators. *)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i G: Group f, g: F_S $-> G K: f o i == g o i
f = g
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i G: Group f, g: F_S $-> G K: f o i == g o i
f = g
(* By assumption, the type [FactorsThroughFreeGroup S F_S i G (g o i)] of factorizations of [g o i] through [i] is contractible. Therefore the two elements we have are equal. Therefore, their first components are equal. *)exact (path_contr (f; K) (g; funx => idpath))..1.Defined.
H: Funext S: Type F_S: Group i: S -> F_S H0: IsFreeGroupOn S F_S i G: Group
IsEquiv isfreegroupon_rec
H: Funext S: Type F_S: Group i: S -> F_S H0: IsFreeGroupOn S F_S i G: Group
IsEquiv isfreegroupon_rec
H: Funext S: Type F_S: Group i: S -> F_S H0: IsFreeGroupOn S F_S i G: Group
(funx : F_S $-> G =>
isfreegroupon_rec (funx0 : S => x (i x0))) == idmap
H: Funext S: Type F_S: Group i: S -> F_S H0: IsFreeGroupOn S F_S i G: Group
(fun (x : S -> G) (x0 : S) =>
isfreegroupon_rec x (i x0)) == idmap
H: Funext S: Type F_S: Group i: S -> F_S H0: IsFreeGroupOn S F_S i G: Group
(funx : F_S $-> G =>
isfreegroupon_rec (funx0 : S => x (i x0))) == idmap
H: Funext S: Type F_S: Group i: S -> F_S H0: IsFreeGroupOn S F_S i G: Group f: F_S $-> G
isfreegroupon_rec (funx : S => f (i x)) = f
H: Funext S: Type F_S: Group i: S -> F_S H0: IsFreeGroupOn S F_S i G: Group f: F_S $-> G
(funx : S =>
isfreegroupon_rec (funx0 : S => f (i x0)) (i x)) ==
(funx : S => f (i x))
apply isfreegroupon_rec_beta.
H: Funext S: Type F_S: Group i: S -> F_S H0: IsFreeGroupOn S F_S i G: Group
(fun (x : S -> G) (x0 : S) =>
isfreegroupon_rec x (i x0)) == idmap
H: Funext S: Type F_S: Group i: S -> F_S H0: IsFreeGroupOn S F_S i G: Group f: S -> G
(funx : S => isfreegroupon_rec f (i x)) = f
(* here we need [Funext]: *)apply path_arrow, isfreegroupon_rec_beta.Defined.(** The universal property of a free group. *)Definitionequiv_isfreegroupon_rec `{Funext} {G F : Group} {A : Type}
{i : A -> F} `{IsFreeGroupOn A F i}
: (A -> G) <~> (F $-> G) := Build_Equiv _ _ isfreegroupon_rec _.(** The above theorem is true regardless of the implementation of free groups. This lets us state the more specific theorem about the canonical free groups. This can be read as [FreeGroup] is left adjoint to the forgetful functor [group_type]. *)Definitionequiv_freegroup_rec `{Funext} (G : Group) (A : Type)
: (A -> G) <~> (FreeGroup A $-> G)
:= equiv_isfreegroupon_rec.
H: Funext F: Group A: Type i: A -> F
IsHProp (IsFreeGroupOn A F i)
H: Funext F: Group A: Type i: A -> F
IsHProp (IsFreeGroupOn A F i)
H: Funext F: Group A: Type i: A -> F
IsHProp
(forall (A0 : Group) (g : A -> A0),
Contr (FactorsThroughFreeGroup A F i A0 g))
exact istrunc_forall.Defined.(** Both ways of stating the universal property are equivalent. *)
H: Funext F: Group A: Type i: A -> F
IsFreeGroupOn A F i <~>
(forallG : Group, IsEquiv (funf : F $-> G => f o i))
H: Funext F: Group A: Type i: A -> F
IsFreeGroupOn A F i <~>
(forallG : Group, IsEquiv (funf : F $-> G => f o i))
H: Funext F: Group A: Type i: A -> F
IsFreeGroupOn A F i ->
forallG : Group, IsEquiv (funf : F $-> G => f o i)
H: Funext F: Group A: Type i: A -> F
(forallG : Group, IsEquiv (funf : F $-> G => f o i)) ->
IsFreeGroupOn A F i
H: Funext F: Group A: Type i: A -> F
(forallG : Group, IsEquiv (funf : F $-> G => f o i)) ->
IsFreeGroupOn A F i
H: Funext F: Group A: Type i: A -> F k: forallG : Group,
IsEquiv (funf : F $-> G => f o i) G: Group g: A -> G
Contr (FactorsThroughFreeGroup A F i G g)
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G
Contr (FactorsThroughFreeGroup A F i G g)
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G
Type
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G
?A <~> FactorsThroughFreeGroup A F i G g
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G
Contr ?A
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G
hfiber
(fun (f : GroupHomomorphism F G) (x : A) => f (i x))
g <~> FactorsThroughFreeGroup A F i G g
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G
Contr
(hfiber
(fun (f : GroupHomomorphism F G) (x : A) =>
f (i x)) g)
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G
hfiber
(fun (f : GroupHomomorphism F G) (x : A) => f (i x))
g <~> FactorsThroughFreeGroup A F i G g
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G
foralla : GroupHomomorphism F G,
(funx : A => a (i x)) = g <~>
(funx : A => a (i x)) == g
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G y: GroupHomomorphism F G
(funx : A => y (i x)) == g <~>
(funx : A => y (i x)) = g
apply equiv_path_forall.
H: Funext F: Group A: Type i: A -> F G: Group k: IsEquiv (funf : F $-> G => f o i) g: A -> G
Contr
(hfiber
(fun (f : GroupHomomorphism F G) (x : A) =>
f (i x)) g)
exact _.Defined.(** ** Subgroups of free groups *)(* We say that a group [G] is generated by a subtype [X] if the natural map from the subgroup generated by [X] to [G] is a surjection. One could equivalently say [IsEquiv (subgroup_incl (subgroup_generated X))], [forall g, subgroup_generated X g], or [subgroup_generated X = maximal_subgroup], but the definition using surjectivity is convenient later. *)Definitionisgeneratedby (G : Group) (X : G -> Type)
:= IsSurjection (subgroup_incl (subgroup_generated X)).SectionFreeGroupGenerated.(* In this Section, we prove that the free group [F_S] on a type [S] is generated in the above sense by the image of [S]. We conclude that the inclusion map is an equivalence, and that the free group is isomorphic as a group to the subgroup. We show that the inclusion is a surjection by showing that it is split epi in the category of groups. *)Context {S : Type} {F_S : Group} {i : S -> F_S} `{IsFreeGroupOn S F_S i}.(* We define a group homomorphism from [F_S] to the subgroup [G] generated by [S] by sending a generator [s] to "itself". This map will be a section of the inclusion map. *)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i
F_S $-> subgroup_generated (hfiber i)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i
F_S $-> subgroup_generated (hfiber i)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i
S -> subgroup_generated (hfiber i)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i s: S
subgroup_generated (hfiber i)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i s: S
F_S
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i s: S
hfiber i ?g
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i s: S
F_S
exact (i s).
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i s: S
hfiber i (i s)
exact (s; idpath).Defined.(* We record the computation rule that [to_subgroup_generated] satisfies. *)Local Definitionto_subgroup_generated_beta (s : S)
: to_subgroup_generated (i s) = subgroup_generated_gen_incl (i s) (s; idpath)
:= isfreegroupon_rec_beta _ _.(* It follows that [to_subgroup_generated] is a section of the inclusion map from [G] to [F_S]. *)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i
(funx : S => (to_subgroup_generated (i x)).1) ==
(funx : S => i x)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i s: S
(to_subgroup_generated (i s)).1 = i s
exact (ap pr1 (to_subgroup_generated_beta s)).Defined.(* It follows that the inclusion map is a surjection, i.e., that [F_S] is generated by the image of [S]. *)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i
isgeneratedby F_S (hfiber i)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i
isgeneratedby F_S (hfiber i)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i
F_S -> subgroup_generated (hfiber i)
S: Type F_S: Group i: S -> F_S H: IsFreeGroupOn S F_S i