Skip to content

Commit 11bed57

Browse files
committed
Bump mathlib
1 parent 7c47eb5 commit 11bed57

File tree

7 files changed

+202
-121
lines changed

7 files changed

+202
-121
lines changed

LeanCamCombi.lean

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import LeanCamCombi.GrowthInGroups.Lecture1
1010
import LeanCamCombi.GrowthInGroups.Lecture2
1111
import LeanCamCombi.GrowthInGroups.Lecture3
1212
import LeanCamCombi.GrowthInGroups.Lecture4
13-
import LeanCamCombi.Mathlib.Algebra.BigOperators.Field
1413
import LeanCamCombi.Mathlib.Combinatorics.Additive.ApproximateSubgroup
1514
import LeanCamCombi.Mathlib.Combinatorics.SetFamily.LYM
1615
import LeanCamCombi.Mathlib.Combinatorics.SimpleGraph.Density

LeanCamCombi/ExtrProbCombi/Containment.lean

Lines changed: 195 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,18 @@ equivalent to saying that there is a graph embedding `G ↪ H`.
2222
2323
## Main declarations
2424
25-
* `SimpleGraph.is_contained G H` : `G` is contained in `H`.
26-
* `SimpleGraph.isIndContained G H` : `G` is contained as an induced subgraph in `H`.
25+
* `SimpleGraph.Copy A B` is the type of copies of `A` in `B`, implemented as the subtype of
26+
*injective* homomorphisms.
27+
* `SimpleGraph.IsContained A B`, `A ⊑ B` is the relation that `B` contains a copy of `A`, that
28+
is, the type of copies of `A` in `B` is nonempty. This is equivalent to the existence of an
29+
isomorphism from `A` to a subgraph of `B`.
30+
31+
This is similar to `SimpleGraph.IsSubgraph` except that the simple graphs here need not have the
32+
same underlying vertex type.
33+
* `SimpleGraph.Free` is the predicate that `B` is `A`-free, that is, `B` does not contain a copy of
34+
`A`. This is the negation of `SimpleGraph.IsContained` implemented for convenience.
35+
36+
* `SimpleGraph.IsIndContained G H` : `G` is contained as an induced subgraph in `H`.
2737
* `SimpleGraph.copyCount G H`: Number of copies `G` in `H`.
2838
* `SimpleGraph.kill G H`: Subgraph of `H` that does not contain `G`. Obtained by arbitrarily
2939
removing an edge from each copy of `G` in `H`.
@@ -40,60 +50,192 @@ open Fintype (card)
4050
open scoped BigOperators Classical
4151

4252
namespace SimpleGraph
43-
variable {α β γ : Type*} {G G₁ G₂ G₃ : SimpleGraph α} {H : SimpleGraph β} {I : SimpleGraph γ}
4453

45-
/-!
46-
### Containment
54+
variable {V α β γ : Type*} {G G₁ G₂ G₃ : SimpleGraph V}
55+
{A : SimpleGraph α} {B : SimpleGraph β} {C : SimpleGraph γ}
4756

48-
A graph `H` *contains* a graph `G` if there is some injective graph homomorphism `G → H`. This
49-
amounts to `H` having a (not necessarily induced) subgraph isomorphic to `G`.
57+
section Copy
5058

51-
We denote "`G` is contained in `H`" by `G ⊑ H` (`\squ`).
52-
-/
59+
/-- The type of copies as a subtype of *injective* homomorphisms. -/
60+
abbrev Copy (A : SimpleGraph α) (B : SimpleGraph β) :=
61+
{ f : A →g B // Function.Injective f }
62+
63+
/-- An injective homomorphism gives rise to a copy. -/
64+
abbrev Hom.toCopy (f : A →g B) (h : Function.Injective f) : Copy A B := ⟨f, h⟩
65+
66+
/-- An embedding gives rise to a copy. -/
67+
abbrev Embedding.toCopy (f : A ↪g B) : Copy A B := f.toHom.toCopy f.injective
68+
69+
/-- An isomorphism gives rise to a copy. -/
70+
abbrev Iso.toCopy (f : A ≃g B) : Copy A B := f.toEmbedding.toCopy
71+
72+
namespace Copy
73+
74+
/-- A copy gives rise to a homomorphism. -/
75+
abbrev toHom : Copy A B → A →g B := Subtype.val
76+
77+
@[simp] lemma coe_toHom (f : Copy A B) : ⇑f.toHom = f := rfl
78+
79+
lemma injective : (f : Copy A B) → (Function.Injective f.toHom) := Subtype.prop
80+
81+
instance : FunLike (Copy A B) α β where
82+
coe f := DFunLike.coe f.toHom
83+
coe_injective' _ _ h := Subtype.val_injective (DFunLike.coe_injective h)
84+
85+
@[simp] lemma coe_toHom_apply (f : Copy A B) (a : α) : ⇑f.toHom a = f a := rfl
86+
87+
/-- A copy induces an embedding of edge sets. -/
88+
def mapEdgeSet (f : Copy A B) : A.edgeSet ↪ B.edgeSet where
89+
toFun := Hom.mapEdgeSet f.toHom
90+
inj' := Hom.mapEdgeSet.injective f.toHom f.injective
91+
92+
/-- A copy induces an embedding of neighbor sets. -/
93+
def mapNeighborSet (f : Copy A B) (a : α) :
94+
A.neighborSet a ↪ B.neighborSet (f a) where
95+
toFun v := ⟨f v, f.toHom.apply_mem_neighborSet v.prop⟩
96+
inj' _ _ h := by
97+
rw [Subtype.mk_eq_mk] at h ⊢
98+
exact f.injective h
99+
100+
/-- A copy gives rise to an embedding of vertex types. -/
101+
def toEmbedding (f : Copy A B) : α ↪ β := ⟨f, f.injective⟩
102+
103+
/-- The identity copy from a simple graph to itself. -/
104+
@[refl] def id (G : SimpleGraph V) : Copy G G := ⟨Hom.id, Function.injective_id⟩
105+
106+
@[simp, norm_cast] lemma coe_id : ⇑(id G) = _root_.id := rfl
107+
108+
/-- The composition of copies is a copy. -/
109+
def comp (g : Copy B C) (f : Copy A B) : Copy A C := by
110+
use g.toHom.comp f.toHom
111+
rw [Hom.coe_comp]
112+
exact Function.Injective.comp g.injective f.injective
113+
114+
@[simp]
115+
theorem comp_apply (g : Copy B C) (f : Copy A B) (a : α) : g.comp f a = g (f a) :=
116+
RelHom.comp_apply g.toHom f.toHom a
117+
118+
/-- The copy from a subgraph to the supergraph. -/
119+
def ofLE (G₁ G₂ : SimpleGraph V) (h : G₁ ≤ G₂) : Copy G₁ G₂ := ⟨Hom.ofLE h, Function.injective_id⟩
120+
121+
@[simp, norm_cast]
122+
theorem coe_comp (g : Copy B C) (f : Copy A B) : ⇑(g.comp f) = g ∘ f := by ext; simp
123+
124+
@[simp, norm_cast] lemma coe_ofLE (h : G₁ ≤ G₂) : ⇑(ofLE G₁ G₂ h) = _root_.id := rfl
125+
126+
@[simp] theorem ofLE_refl : ofLE G G (le_refl G) = id G := by ext; simp
127+
128+
@[simp]
129+
theorem ofLE_comp (h₁₂ : G₁ ≤ G₂) (h₂₃ : G₂ ≤ G₃) :
130+
(ofLE _ _ h₂₃).comp (ofLE _ _ h₁₂) = ofLE _ _ (h₁₂.trans h₂₃) := by ext; simp
53131

54-
/-- A simple graph `G` is contained in a simple graph `H` if there exists a subgraph of `H`
55-
isomorphic to `G`. This is denoted by `G ⊑ H`. -/
56-
def IsContained (G : SimpleGraph α) (H : SimpleGraph β) : Prop :=
57-
∃ f : G →g H, Injective f
132+
/-- The copy from an induced subgraph to the initial simple graph. -/
133+
def induce (G : SimpleGraph V) (s : Set V) : Copy (G.induce s) G :=
134+
(Embedding.induce s).toCopy
58135

59-
scoped infixl:50 " ⊑ " => SimpleGraph.IsContained
136+
end Copy
60137

61-
lemma isContained_of_le (h : G₁ ≤ G₂) : G₁ ⊑ G₂ := ⟨.ofLE h, injective_id⟩
138+
/-- A `Subgraph G` gives rise to a copy from the coercion to `G`. -/
139+
def Subgraph.coeCopy (G' : G.Subgraph) : Copy G'.coe G :=
140+
G'.hom.toCopy Subgraph.hom.injective
62141

63-
protected lemma Iso.isContained (e : G ≃g H) : G ⊑ H := ⟨e, e.injective⟩
64-
protected lemma Iso.isContained' (e : G ≃g H) : H ⊑ G := e.symm.isContained
142+
end Copy
65143

66-
lemma Subgraph.coe_isContained (G' : G.Subgraph) : G'.coe ⊑ G :=
67-
⟨G'.hom, Subtype.val_injective⟩
144+
section IsContained
68145

69-
@[refl] lemma isContained_refl (G : SimpleGraph α) : G ⊑ G := isContained_of_le le_rfl
70-
lemma isContained_rfl : G ⊑ G := isContained_refl _
71-
lemma IsContained.trans : G ⊑ H → H ⊑ I → G ⊑ I := fun ⟨f, hf⟩ ⟨g, hg⟩ ↦ ⟨g.comp f, hg.comp hf⟩
146+
/-- The relation `IsContained A B`, `A ⊑ B` says that `B` contains a copy of `A`.
72147
73-
lemma IsContained.mono_left (h₁₂ : G₁ ≤ G₂) (h₂₃ : G₂ ⊑ G₃) : G₁ ⊑ G₃ :=
74-
(isContained_of_le h₁₂).trans h₂₃
148+
This is equivalent to the existence of an isomorphism from `A` to a subgraph of `B`. -/
149+
abbrev IsContained (A : SimpleGraph α) (B : SimpleGraph β) := Nonempty (Copy A B)
75150

76-
lemma IsContained.mono_right (h₁₂ : G₁ ⊑ G₂) (h₂₃ : G₂ ≤ G₃) : G₁ ⊑ G₃ :=
77-
h₁₂.trans <| isContained_of_le h₂₃
151+
@[inherit_doc] scoped infixl:50 " ⊑ " => SimpleGraph.IsContained
152+
153+
/-- A simple graph contains itself. -/
154+
@[refl] protected theorem IsContained.refl (G : SimpleGraph V) : G ⊑ G := ⟨.id G⟩
155+
156+
protected theorem IsContained.rfl : G ⊑ G := IsContained.refl G
157+
158+
/-- A simple graph contains its subgraphs. -/
159+
theorem isContained_of_le (h : G₁ ≤ G₂) : G₁ ⊑ G₂ := ⟨Copy.ofLE G₁ G₂ h⟩
160+
161+
/-- If `A` contains `B` and `B` contains `C`, then `A` contains `C`. -/
162+
theorem IsContained.trans : A ⊑ B → B ⊑ C → A ⊑ C := fun ⟨f⟩ ⟨g⟩ ↦ ⟨g.comp f⟩
163+
164+
/-- If `B` contains `C` and `A` contains `B`, then `A` contains `C`. -/
165+
theorem IsContained.trans' : B ⊑ C → A ⊑ B → A ⊑ C := flip IsContained.trans
166+
167+
lemma IsContained.mono_right {B' : SimpleGraph β} (h_isub : A ⊑ B) (h_sub : B ≤ B') : A ⊑ B' :=
168+
h_isub.trans <| isContained_of_le h_sub
78169

79170
alias IsContained.trans_le := IsContained.mono_right
80171

81-
lemma isContained_of_isEmpty [IsEmpty α] : G ⊑ H :=
82-
⟨{ toFun := isEmptyElim
83-
map_rel' := fun {a} ↦ isEmptyElim a }, isEmptyElim⟩
172+
lemma IsContained.mono_left {A' : SimpleGraph α} (h_sub : A ≤ A') (h_isub : A' ⊑ B) : A ⊑ B :=
173+
(isContained_of_le h_sub).trans h_isub
84174

85-
lemma bot_isContained (f : α ↪ β) : (⊥ : SimpleGraph α) ⊑ H :=
86-
⟨{ toFun := f
87-
map_rel' := False.elim }, f.injective⟩
175+
alias IsContained.trans_le' := IsContained.mono_left
88176

89-
lemma isContained_iff_exists_subgraph : G ⊑ H ↔ ∃ H' : H.Subgraph, Nonempty <| G ≃g H'.coe := by
90-
constructor
91-
· rintro ⟨f, hf⟩
92-
exact ⟨Subgraph.map f ⊤, ⟨(Subgraph.isoMap _ hf _).comp Subgraph.topIso.symm⟩⟩
93-
· rintro ⟨H', ⟨e⟩⟩
94-
exact e.isContained.trans H'.coe_isContained
177+
/-- If `A ≃g B`, then `A` is contained in `C` if and only if `B` is contained in `C`. -/
178+
theorem isContained_congr (e : A ≃g B) : A ⊑ C ↔ B ⊑ C :=
179+
⟨.trans ⟨e.symm.toCopy⟩, .trans ⟨e.toCopy⟩⟩
180+
181+
/-- A simple graph having no vertices is contained in any simple graph. -/
182+
lemma IsContained.of_isEmpty [IsEmpty α] : A ⊑ B :=
183+
⟨⟨isEmptyElim, fun {a} ↦ isEmptyElim a⟩, isEmptyElim⟩
184+
185+
/-- A simple graph having no edges is contained in any simple graph having sufficent vertices. -/
186+
theorem isContained_of_isEmpty_edgeSet [IsEmpty A.edgeSet] [Fintype α] [Fintype β]
187+
(h : card α ≤ card β) : A ⊑ B := by
188+
haveI := Function.Embedding.nonempty_of_card_le h
189+
let ι : α ↪ β := Classical.arbitrary (α ↪ β)
190+
exact ⟨⟨ι, isEmptyElim ∘ fun hadj ↦ (⟨s(_, _), hadj⟩ : A.edgeSet)⟩, ι.injective⟩
191+
192+
lemma bot_isContained (f : α ↪ β) : (⊥ : SimpleGraph α) ⊑ B := ⟨⟨f, False.elim⟩, f.injective⟩
193+
194+
protected alias IsContained.bot := bot_isContained
95195

96-
alias ⟨IsContained.exists_subgraph, _⟩ := isContained_iff_exists_subgraph
196+
/-- A simple graph `G` contains all `Subgraph G` coercions. -/
197+
lemma Subgraph.coe_isContained (G' : G.Subgraph) : G'.coe ⊑ G := ⟨G'.coeCopy⟩
198+
199+
/-- The isomorphism from `Subgraph A` to its map under a copy `Copy A B`. -/
200+
noncomputable def Subgraph.isoMap (f : Copy A B) (A' : A.Subgraph) :
201+
A'.coe ≃g (A'.map f.toHom).coe := by
202+
use Equiv.Set.image f.toHom _ f.injective
203+
simp_rw [map_verts, Equiv.Set.image_apply, coe_adj, map_adj, Relation.map_apply,
204+
Function.Injective.eq_iff f.injective, exists_eq_right_right, exists_eq_right, forall_true_iff]
205+
206+
/-- `B` contains `A` if and only if `B` has a subgraph `B'` and `B'` is isomorphic to `A`. -/
207+
theorem isContained_iff_exists_iso_subgraph :
208+
A ⊑ B ↔ ∃ B' : B.Subgraph, Nonempty (A ≃g B'.coe) :=
209+
fun ⟨f⟩ ↦ ⟨Subgraph.map f.toHom ⊤, ⟨(Subgraph.isoMap f ⊤).comp Subgraph.topIso.symm⟩⟩,
210+
fun ⟨B', ⟨e⟩⟩ ↦ B'.coe_isContained.trans' ⟨e.toCopy⟩⟩
211+
212+
alias ⟨IsContained.exists_iso_subgraph, IsContained.of_exists_iso_subgraph⟩ :=
213+
isContained_iff_exists_iso_subgraph
214+
215+
end IsContained
216+
217+
section Free
218+
219+
/-- The proposition that a simple graph does not contain a copy of another simple graph. -/
220+
abbrev Free (A : SimpleGraph α) (B : SimpleGraph β) := ¬A ⊑ B
221+
222+
lemma not_free : ¬A.Free B ↔ A ⊑ B := not_not
223+
224+
/-- If `A ≃g B`, then `C` is `A`-free if and only if `C` is `B`-free. -/
225+
theorem free_congr (e : A ≃g B) : A.Free C ↔ B.Free C := by
226+
rw [not_iff_not]
227+
exact isContained_congr e
228+
229+
lemma free_bot (h : A ≠ ⊥) : A.Free (⊥ : SimpleGraph β) := by
230+
rw [← edgeSet_nonempty] at h
231+
intro ⟨f, hf⟩
232+
absurd f.map_mem_edgeSet h.choose_spec
233+
rw [edgeSet_bot]
234+
exact Set.not_mem_empty (h.choose.map f)
235+
236+
end Free
237+
238+
variable {α β γ : Type*} {G G₁ G₂ G₃ : SimpleGraph α} {H : SimpleGraph β} {I : SimpleGraph γ}
97239

98240
/-!
99241
### Induced containment
@@ -120,27 +262,28 @@ protected lemma Subgraph.IsInduced'.isIndContained {G' : G.Subgraph} (hG' : G'.I
120262
inj' := Subtype.coe_injective
121263
map_rel_iff' := hG'.adj.symm }⟩
122264

123-
@[refl] lemma isIndContained_refl (G : SimpleGraph α) : G ⊴ G := ⟨Embedding.refl⟩
124-
lemma isIndContained_rfl : G ⊴ G := isIndContained_refl _
265+
@[refl] lemma IsIndContained.refl (G : SimpleGraph α) : G ⊴ G := ⟨Embedding.refl⟩
266+
lemma IsIndContained.rfl : G ⊴ G := .refl _
125267
lemma IsIndContained.trans : G ⊴ H → H ⊴ I → G ⊴ I := fun ⟨f⟩ ⟨g⟩ ↦ ⟨g.comp f⟩
126268

127269
lemma isIndContained_of_isEmpty [IsEmpty α] : G ⊴ H :=
128270
⟨{ toFun := isEmptyElim
129271
inj' := isEmptyElim
130272
map_rel_iff' := fun {a} ↦ isEmptyElim a }⟩
131273

132-
lemma isIndContained_iff_exists_subgraph :
274+
lemma isIndContained_iff_exists_iso_subgraph :
133275
G ⊴ H ↔ ∃ (H' : H.Subgraph) (_e : G ≃g H'.coe), H'.IsInduced' := by
134276
constructor
135277
· rintro ⟨f⟩
136278
refine ⟨Subgraph.map f.toHom ⊤,
137-
(Subgraph.isoMap f.toHom f.injective _).comp Subgraph.topIso.symm, ?_⟩
279+
(Subgraph.isoMap f.toHom, f.injective _).comp Subgraph.topIso.symm, ?_⟩
138280
rintro _ _ ⟨a, -, rfl⟩ ⟨b, -, rfl⟩
139281
simp [Relation.map_apply_apply, f.injective]
140282
· rintro ⟨H', e, hH'⟩
141283
exact e.isIndContained.trans hH'.isIndContained
142284

143-
alias ⟨IsIndContained.exists_subgraph, _⟩ := isIndContained_iff_exists_subgraph
285+
alias ⟨IsIndContained.exists_iso_subgraph, IsIndContained.of_exists_iso_subgraph⟩ :=
286+
isIndContained_iff_exists_iso_subgraph
144287

145288
/-!
146289
### Counting the copies
@@ -190,10 +333,12 @@ noncomputable def copyCount (G : SimpleGraph α) (H : SimpleGraph β) : ℕ :=
190333
exact fun hab ↦ e.symm.map_rel_iff.2 hab.coe
191334

192335
@[simp] lemma copyCount_eq_zero : G.copyCount H = 0 ↔ ¬ G ⊑ H := by
193-
simp [copyCount, isContained_iff_exists_subgraph, card_pos, filter_eq_empty_iff]
336+
simp [copyCount, -nonempty_subtype, isContained_iff_exists_iso_subgraph, card_pos,
337+
filter_eq_empty_iff]
194338

195339
@[simp] lemma copyCount_pos : 0 < G.copyCount H ↔ G ⊑ H := by
196-
simp [copyCount, isContained_iff_exists_subgraph, card_pos, filter_nonempty_iff]
340+
simp [copyCount, -nonempty_subtype, isContained_iff_exists_iso_subgraph, card_pos,
341+
filter_nonempty_iff]
197342

198343
end CopyCount
199344

@@ -262,7 +407,7 @@ private lemma kill_of_ne_bot (hG : G ≠ ⊥) (H : SimpleGraph β) :
262407
rw [kill]; exact dif_neg hG
263408

264409
lemma kill_eq_right (hG : G ≠ ⊥) : G.kill H = H ↔ ¬ G ⊑ H := by
265-
simp only [kill_of_ne_bot hG, Set.disjoint_left, isContained_iff_exists_subgraph,
410+
simp only [kill_of_ne_bot hG, Set.disjoint_left, isContained_iff_exists_iso_subgraph,
266411
@forall_swap _ H.Subgraph, Set.iUnion_singleton_eq_range, deleteEdges_eq_self, Set.mem_iUnion,
267412
Set.mem_range, not_exists, not_nonempty_iff, Nonempty.forall]
268413
exact forall_congr' fun H' ↦ ⟨fun h ↦ ⟨fun f ↦ h _
@@ -276,7 +421,7 @@ lemma kill_of_not_isContained (hGH : ¬ G ⊑ H) : G.kill H = H := by
276421
/-- Removing an edge from `H` for each subgraph isomorphic to `G` results in a graph that doesn't
277422
contain `G`. -/
278423
lemma not_isContained_kill (hG : G ≠ ⊥) : ¬ G ⊑ G.kill H := by
279-
rw [kill_of_ne_bot hG, deleteEdges, isContained_iff_exists_subgraph]
424+
rw [kill_of_ne_bot hG, deleteEdges, isContained_iff_exists_iso_subgraph]
280425
rintro ⟨H', hGH'⟩
281426
have hH' : (H'.map <| Hom.ofLE (sdiff_le : H \ _ ≤ H)).edgeSet.Nonempty := by
282427
rw [Subgraph.edgeSet_map]
@@ -287,14 +432,13 @@ lemma not_isContained_kill (hG : G ≠ ⊥) : ¬ G ⊑ G.kill H := by
287432
rw [← Subgraph.image_coe_edgeSet_coe] at this
288433
subst he
289434
obtain ⟨e, he₀, he₁⟩ := this
290-
let e' : Sym2 H'.verts := Sym2.map (Subgraph.isoMap (Hom.ofLE _) injective_id _).symm e
435+
let e' : Sym2 H'.verts := Sym2.map (Subgraph.isoMap (.ofLE _ _ _) _).symm e
291436
have he' : e' ∈ H'.coe.edgeSet := (Iso.map_mem_edgeSet_iff _).2 he₀
292437
rw [Subgraph.edgeSet_coe] at he'
293438
have := Subgraph.edgeSet_subset _ he'
294439
simp only [edgeSet_sdiff, edgeSet_fromEdgeSet, edgeSet_sdiff_sdiff_isDiag, Set.mem_diff,
295440
Set.mem_iUnion, not_exists] at this
296-
refine this.2 (H'.map <| Hom.ofLE sdiff_le)
297-
⟨(Subgraph.isoMap (Hom.ofLE _) injective_id _).comp hGH'.some⟩ ?_
441+
refine this.2 (H'.map <| Hom.ofLE sdiff_le) ⟨(Subgraph.isoMap (.ofLE _ _ _) _).comp hGH'.some⟩ ?_
298442
rw [Sym2.map_map, Set.mem_singleton_iff, ← he₁]
299443
congr 1 with x
300444
exact congr_arg _ (Equiv.Set.image_symm_apply _ _ injective_id _ _)

LeanCamCombi/Mathlib/Algebra/BigOperators/Field.lean

Lines changed: 0 additions & 31 deletions
This file was deleted.

LeanCamCombi/Mathlib/Combinatorics/SimpleGraph/Density.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Mathlib.Algebra.Order.Ring.Unbundled.Rat
2-
import LeanCamCombi.Mathlib.Combinatorics.SimpleGraph.Subgraph
2+
import Mathlib.Combinatorics.SimpleGraph.Subgraph
33

44
open Finset
55
open scoped Classical

0 commit comments

Comments
 (0)