Skip to content
Closed
1 change: 1 addition & 0 deletions Mathlib.lean
Original file line number Diff line number Diff line change
Expand Up @@ -8179,6 +8179,7 @@ public import Mathlib.Topology.Convenient.OpenClosed
public import Mathlib.Topology.Covering.AddCircle
public import Mathlib.Topology.Covering.Basic
public import Mathlib.Topology.Covering.Deck
public import Mathlib.Topology.Covering.FundamentalGroupCircle
public import Mathlib.Topology.Covering.Quotient
public import Mathlib.Topology.Defs.Basic
public import Mathlib.Topology.Defs.Filter
Expand Down
23 changes: 23 additions & 0 deletions Mathlib/Algebra/Group/Subgroup/ZPowers/Lemmas.lean
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,29 @@ namespace AddSubgroup
theorem range_zmultiplesHom (a : A) : (zmultiplesHom A a).range = zmultiples a :=
rfl

section IsAddTorsionFree

variable [IsAddTorsionFree A] {a : A}

/-- The subgroup generated by a nonzero element `a` of a torsion-free additive group is isomorphic
to `ℤ`, sending `n • a` to `n`. -/
noncomputable def zmultiplesEquivInt (ha : a ≠ 0) : zmultiples a ≃+ ℤ :=
((AddMonoidHom.ofInjective (zmultiplesHom_injective ha)).trans
(AddEquiv.addSubgroupCongr (range_zmultiplesHom a))).symm

@[simp]
theorem coe_zmultiplesEquivInt_symm_apply (ha : a ≠ 0) (n : ℤ) :
((zmultiplesEquivInt ha).symm n : A) = n • a := by
simp only [zmultiplesEquivInt, AddEquiv.symm_symm, AddEquiv.trans_apply,
AddEquiv.addSubgroupCongr_apply, AddMonoidHom.ofInjective_apply, zmultiplesHom_apply]

@[simp]
theorem zmultiplesEquivInt_apply_zsmul (ha : a ≠ 0) (n : ℤ) :
zmultiplesEquivInt ha ⟨n • a, zsmul_mem_zmultiples a n⟩ = n :=
(zmultiplesEquivInt ha).eq_symm_apply.mp (Subtype.ext (by simp))

end IsAddTorsionFree

section Ring

variable {R : Type*} [Ring R] (r : R) (k : ℤ)
Expand Down
6 changes: 6 additions & 0 deletions Mathlib/Algebra/Group/TypeTags/Basic.lean
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,12 @@ instance [Monoid α] [IsMulTorsionFree α] : IsAddTorsionFree (Additive α) wher
instance [AddMonoid α] [IsAddTorsionFree α] : IsMulTorsionFree (Multiplicative α) where
pow_left_injective _ := nsmul_right_injective (M := α)

instance [Mul α] [IsMulCommutative α] : IsAddCommutative (Additive α) where
is_comm := ⟨fun a b ↦ congrArg Additive.ofMul (mul_comm' a.toMul b.toMul)⟩

instance [Add α] [IsAddCommutative α] : IsMulCommutative (Multiplicative α) where
is_comm := ⟨fun a b ↦ congrArg Multiplicative.ofAdd (add_comm' a.toAdd b.toAdd)⟩

/-- If `α` has some multiplicative structure and coerces to a function,
then `Additive α` should also coerce to the same function.

Expand Down
5 changes: 5 additions & 0 deletions Mathlib/Data/Int/Cast/Lemmas.lean
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ def zpowersHom : α ≃ (Multiplicative ℤ →* α) :=

@[simp] lemma zmultiplesHom_symm_apply (f : ℤ →+ β) : (zmultiplesHom β).symm f = f 1 := rfl

variable {β} in
lemma zmultiplesHom_injective [IsAddTorsionFree β] {x : β} (hx : x ≠ 0) :
Function.Injective (zmultiplesHom β x) :=
(injective_iff_map_eq_zero _).2 fun _ hn ↦ (IsAddTorsionFree.zsmul_eq_zero_iff_left hx).1 hn

@[simp] lemma zpowersHom_apply (x : α) (n : Multiplicative ℤ) :
zpowersHom α x n = x ^ n.toAdd := rfl

Expand Down
80 changes: 80 additions & 0 deletions Mathlib/Topology/Covering/FundamentalGroupCircle.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/-
Copyright (c) 2026 Ruize Chen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Ruize Chen
-/
module

public import Mathlib.Topology.Covering.AddCircle
public import Mathlib.Topology.Homotopy.Lifting
public import Mathlib.Topology.Instances.ZMultiples
public import Mathlib.Analysis.Convex.Contractible

/-!
# The fundamental group of the circle

For `0 < p`, the fundamental group of `AddCircle p` at any basepoint is isomorphic to `ℤ`
(`AddCircle.windingNumberIso`). The winding number `AddCircle.windingNumber` of a loop is
`(f 1 - f 0) / p` for any continuous lift `f : I → ℝ` of the loop
(`AddCircle.windingNumber_eq_div`); in particular the loop `t ↦ n • (t * p) + x` has winding
number `n` (`AddCircle.windingNumber_zsmulLoop`).
-/

@[expose] public section

open unitInterval

namespace AddCircle

variable (p : ℝ)

/-- The loop in `AddCircle p` based at `x` that winds `n` times, defined as
`t ↦ n • (t * p) + x`. -/
def zsmulLoop (x : ℝ) (n : ℤ) : Path (x : AddCircle p) x where
toFun t := n • (t * p : ℝ) + x
source' := by simp
target' := by simp [coe_period]
continuous_toFun := by fun_prop

@[simp]
theorem zsmulLoop_apply (x : ℝ) (n : ℤ) (t : I) :
zsmulLoop p x n t = n • (t * p : ℝ) + x :=
rfl

variable {p} [hp : Fact (0 < p)]

/-- **The fundamental group of the circle is `ℤ`**: the isomorphism sends the class of a loop to
its winding number (`windingNumber_eq_div`). It does not depend on the choice of a lift of the
basepoint (`IsAddQuotientCoveringMap.fundamentalGroupEquiv_eq`). -/
noncomputable def windingNumberIso (x : AddCircle p) :
FundamentalGroup (AddCircle p) x ≃* Multiplicative ℤ :=
((isAddQuotientCoveringMap_coe p).fundamentalGroupEquiv (x := x)
⟨(equivIco p 0 x : ℝ), coe_equivIco⟩).trans <|
MulOpposite.opMulEquiv.symm.trans (AddSubgroup.zmultiplesEquivInt hp.out.ne').toMultiplicative

/-- The winding number of a loop in `AddCircle p`, defined as its image under `windingNumberIso`. -/
noncomputable def windingNumber {x : AddCircle p} (γ : FundamentalGroup (AddCircle p) x) : ℤ :=
(windingNumberIso x γ).toAdd

/-- The winding number of a loop in `AddCircle p` is `(f 1 - f 0) / p`, for any continuous lift
`f` of the loop to `ℝ`. -/
theorem windingNumber_eq_div {x : AddCircle p} (γ : Path x x) (f : C(I, ℝ)) (hf : (↑) ∘ f = γ) :
(windingNumber (.mk γ) : ℝ) = (f 1 - f 0) / p := by
have h0 : (f 0 : AddCircle p) = x := congr($hf 0).trans γ.source
obtain ⟨n, hn⟩ : ∃ n : ℤ, n • p = f 1 - f 0 :=
AddSubgroup.mem_zmultiples_iff.mp <| QuotientAddGroup.eq_iff_sub_mem.mp <|
congr($hf 1).trans <| γ.target.trans h0.symm
have h : (isAddQuotientCoveringMap_coe p).fundamentalGroupEquiv (x := x) ⟨f 0, h0⟩ (.mk γ) =
.op (.ofAdd ⟨n • p, n, rfl⟩) :=
(isAddQuotientCoveringMap_coe p).fundamentalGroupToMulOpposite_apply_mk_eq hf rfl (by simp [hn])
simp [windingNumber, windingNumberIso, AddSubgroup.zmultiplesEquivInt_apply_zsmul hp.out.ne' n,
(isAddQuotientCoveringMap_coe p).fundamentalGroupEquiv_eq _ ⟨f 0, h0⟩, h,
eq_div_iff hp.out.ne', ← zsmul_eq_mul, ← hn]

/-- The loop `t ↦ n • (t * p) + x` has winding number `n`. -/
theorem windingNumber_zsmulLoop (x : ℝ) (n : ℤ) :
windingNumber (x := x) (.mk (zsmulLoop p x n)) = n := by
simpa [hp.out.ne'] using windingNumber_eq_div (zsmulLoop p x n)
⟨fun t ↦ n • (t * p) + x, by fun_prop⟩ rfl

end AddCircle
69 changes: 69 additions & 0 deletions Mathlib/Topology/Homotopy/Lifting.lean
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,15 @@ def monodromy {x y : X} (γ : Path.Homotopic.Quotient x y) :
congr($(cov.liftPath_lifts ..) 1).trans γ.target⟩)
fun _ _ h ↦ Subtype.ext (cov.liftPath_apply_one_eq_of_homotopicRel h ..)

theorem coe_monodromy_mk {x y : X} (γ : Path x y) (e : p ⁻¹' {x}) :
(cov.monodromy (.mk γ) e : E) = cov.liftPath γ e (γ.source.trans e.2.symm) 1 :=
rfl

lemma monodromy_eq_apply_one {x y : X} {γ : Path x y} {e : p ⁻¹' {x}} {Γ : C(I, E)}
(hpΓ : p ∘ Γ = γ) (Γ_0 : Γ 0 = e) : cov.monodromy (.mk γ) e = Γ 1 := by
rw [show Γ = cov.liftPath γ e (γ.source.trans e.2.symm) from (eq_liftPath_iff' ..).mpr ⟨hpΓ, Γ_0⟩,
coe_monodromy_mk]

/-- Lift a homotopy class of paths to a covering space. -/
def liftPathQuotient {x y : X} (γ : Path.Homotopic.Quotient x y) (e : p ⁻¹' {x}) :
Path.Homotopic.Quotient e.1 (cov.monodromy γ e) :=
Expand Down Expand Up @@ -647,11 +656,37 @@ theorem fundamentalGroupToMulOpposite_apply_eq_Iff {g : Gᵐᵒᵖ} :
have := hp.isCancelSMul.right_cancel'
aesop

variable {e} in
theorem fundamentalGroupToMulOpposite_apply_mk_eq {γ : Path x x} {g : G} {Γ : C(I, E)}
(hpΓ : p ∘ Γ = γ) (Γ_0 : Γ 0 = e) (Γ_1 : Γ 1 = g • e) :
hp.fundamentalGroupToMulOpposite e (.mk γ) = .op g :=
hp.fundamentalGroupToMulOpposite_apply_eq_Iff.mpr <| by
simp [hp.isCoveringMap.monodromy_eq_apply_one hpΓ Γ_0, Γ_1]

variable {e} in
theorem unop_fundamentalGroupToMulOpposite_smul :
(hp.fundamentalGroupToMulOpposite e γ).unop • e.1 = hp.isCoveringMap.monodromy γ e := by
simp [fundamentalGroupToMulOpposite, fiberEquivGroup_smul_self]

variable {e} in
/-- Moving the basepoint `e` in the fiber by `g` conjugates `fundamentalGroupToMulOpposite`
by `g`. -/
theorem fundamentalGroupToMulOpposite_toPermFiber :
hp.fundamentalGroupToMulOpposite (hp.toPermFiber x g e) γ =
MulOpposite.op (g * (hp.fundamentalGroupToMulOpposite e γ).unop * g⁻¹) := by
rw [fundamentalGroupToMulOpposite_apply_eq_Iff, hp.monodromy_toPermFiber]
simp only [MulOpposite.unop_op, toPermFiber_apply_apply_coe,
← hp.unop_fundamentalGroupToMulOpposite_smul, mul_smul, inv_smul_smul]

/-- If `G` is commutative, `fundamentalGroupToMulOpposite` does not depend on the choice of the
basepoint `e` in the fiber. -/
theorem fundamentalGroupToMulOpposite_eq [IsMulCommutative G] (e' : p ⁻¹' {x}) :
hp.fundamentalGroupToMulOpposite e = hp.fundamentalGroupToMulOpposite e' := by
obtain ⟨g, rfl⟩ := hp.exists_toPermFiber_eq e e'
ext γ
rw [fundamentalGroupToMulOpposite_toPermFiber, mul_comm' g, mul_inv_cancel_right,
MulOpposite.op_unop]

variable {e} in
theorem fundamentalGroupToMulOpposite_eq_one_iff :
hp.fundamentalGroupToMulOpposite e γ = 1 ↔ hp.isCoveringMap.monodromy γ e = e where
Expand Down Expand Up @@ -693,6 +728,12 @@ def fundamentalGroupEquiv [SimplyConnectedSpace E] :
⟨hp.fundamentalGroupToMulOpposite_injective e,
hp.fundamentalGroupToMulOpposite_surjective e⟩

/-- If `G` is commutative, `fundamentalGroupEquiv` does not depend on the choice of the
basepoint `e` in the fiber. -/
theorem fundamentalGroupEquiv_eq [SimplyConnectedSpace E] [IsMulCommutative G] (e' : p ⁻¹' {x}) :
hp.fundamentalGroupEquiv e = hp.fundamentalGroupEquiv e' :=
MulEquiv.ext fun γ ↦ congr($(hp.fundamentalGroupToMulOpposite_eq e e') γ)

end IsQuotientCoveringMap

namespace IsAddQuotientCoveringMap
Expand Down Expand Up @@ -745,11 +786,33 @@ theorem fundamentalGroupToMulOpposite_apply_eq_Iff {g : (Multiplicative G)ᵐᵒ
hp.fundamentalGroupToMulOpposite e γ = g ↔ g.unop • e.1 = hp.isCoveringMap.monodromy γ e :=
hp.toMultiplicative.fundamentalGroupToMulOpposite_apply_eq_Iff

variable {e} in
theorem fundamentalGroupToMulOpposite_apply_mk_eq {γ : Path x x} {g : Multiplicative G}
{Γ : C(I, E)} (hpΓ : p ∘ Γ = γ) (Γ_0 : Γ 0 = e) (Γ_1 : Γ 1 = g • e) :
hp.fundamentalGroupToMulOpposite e (.mk γ) = .op g :=
hp.fundamentalGroupToMulOpposite_apply_eq_Iff.mpr <| by
rw [hp.isCoveringMap.monodromy_eq_apply_one hpΓ Γ_0, Γ_1]; rfl

variable {e} in
theorem unop_fundamentalGroupToMulOpposite_smul :
(hp.fundamentalGroupToMulOpposite e γ).unop • e.1 = hp.isCoveringMap.monodromy γ e :=
hp.toMultiplicative.unop_fundamentalGroupToMulOpposite_smul

variable {e} in
/-- Moving the basepoint `e` in the fiber by `g` conjugates `fundamentalGroupToMulOpposite`
by `g`. -/
theorem fundamentalGroupToMulOpposite_toPermFiber :
hp.fundamentalGroupToMulOpposite (hp.toMultiplicative.toPermFiber x g e) γ =
MulOpposite.op (Multiplicative.ofAdd g * (hp.fundamentalGroupToMulOpposite e γ).unop *
(Multiplicative.ofAdd g)⁻¹) :=
hp.toMultiplicative.fundamentalGroupToMulOpposite_toPermFiber

/-- If `G` is commutative, `fundamentalGroupToMulOpposite` does not depend on the choice of the
basepoint `e` in the fiber. -/
theorem fundamentalGroupToMulOpposite_eq [IsAddCommutative G] (e' : p ⁻¹' {x}) :
hp.fundamentalGroupToMulOpposite e = hp.fundamentalGroupToMulOpposite e' :=
hp.toMultiplicative.fundamentalGroupToMulOpposite_eq e e'

variable {e} in
theorem fundamentalGroupToMulOpposite_eq_one_iff :
hp.fundamentalGroupToMulOpposite e γ = 1 ↔ hp.isCoveringMap.monodromy γ e = e :=
Expand All @@ -773,4 +836,10 @@ def fundamentalGroupEquiv [SimplyConnectedSpace E] :
FundamentalGroup X x ≃* (Multiplicative G)ᵐᵒᵖ :=
hp.toMultiplicative.fundamentalGroupEquiv e

/-- If `G` is commutative, `fundamentalGroupEquiv` does not depend on the choice of the
basepoint `e` in the fiber. -/
theorem fundamentalGroupEquiv_eq [SimplyConnectedSpace E] [IsAddCommutative G] (e' : p ⁻¹' {x}) :
hp.fundamentalGroupEquiv e = hp.fundamentalGroupEquiv e' :=
hp.toMultiplicative.fundamentalGroupEquiv_eq e e'

end IsAddQuotientCoveringMap
Loading