diff --git a/LeanPool.lean b/LeanPool.lean index a2da06359..e725f27d8 100644 --- a/LeanPool.lean +++ b/LeanPool.lean @@ -6668,6 +6668,34 @@ public import LeanPool.Odlyzko.TestFunction.TaylorBound public import LeanPool.Odlyzko.Theta.PoissonSummation public import LeanPool.Odlyzko.Theta.TraceDualIdeal public import LeanPool.Odlyzko.Theta.TraceDualLattice +public import LeanPool.OneManifold +public import LeanPool.OneManifold.OneMfld +public import LeanPool.OneManifold.OneMfld.Charts +public import LeanPool.OneManifold.OneMfld.CircleBlocks +public import LeanPool.OneManifold.OneMfld.CircleGlue +public import LeanPool.OneManifold.OneMfld.Classification +public import LeanPool.OneManifold.OneMfld.ClassifyInterval +public import LeanPool.OneManifold.OneMfld.ClassifyOverlaps +public import LeanPool.OneManifold.OneMfld.ClosureOverlap +public import LeanPool.OneManifold.OneMfld.Compactness +public import LeanPool.OneManifold.OneMfld.FiniteIntervalCharts +public import LeanPool.OneManifold.OneMfld.FinitelyCharted +public import LeanPool.OneManifold.OneMfld.GlueBlocks +public import LeanPool.OneManifold.OneMfld.GlueCore +public import LeanPool.OneManifold.OneMfld.GlueNNReal +public import LeanPool.OneManifold.OneMfld.GlueUI +public import LeanPool.OneManifold.OneMfld.IntervalCharts +public import LeanPool.OneManifold.OneMfld.LocallyConnected +public import LeanPool.OneManifold.OneMfld.NiceCharts +public import LeanPool.OneManifold.OneMfld.Noncompact +public import LeanPool.OneManifold.OneMfld.Normalize +public import LeanPool.OneManifold.OneMfld.Outer +public import LeanPool.OneManifold.OneMfld.PartialHomeomorphHelpers +public import LeanPool.OneManifold.OneMfld.RealIntervals +public import LeanPool.OneManifold.OneMfld.TransitionMono +public import LeanPool.OneManifold.OneMfld.TwoComponents +public import LeanPool.OneManifold.OneMfld.UnitInterval +public import LeanPool.OneManifold.Solution public import LeanPool.OrderPQ public import LeanPool.OrderPQ.Basic public import LeanPool.OrderPQ.IsCyclic diff --git a/LeanPool/OneManifold.lean b/LeanPool/OneManifold.lean new file mode 100644 index 000000000..88574478c --- /dev/null +++ b/LeanPool/OneManifold.lean @@ -0,0 +1,49 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ + +module + +public import LeanPool.OneManifold.OneMfld +public import LeanPool.OneManifold.OneMfld.Charts +public import LeanPool.OneManifold.OneMfld.CircleBlocks +public import LeanPool.OneManifold.OneMfld.CircleGlue +public import LeanPool.OneManifold.OneMfld.Classification +public import LeanPool.OneManifold.OneMfld.ClassifyInterval +public import LeanPool.OneManifold.OneMfld.ClassifyOverlaps +public import LeanPool.OneManifold.OneMfld.ClosureOverlap +public import LeanPool.OneManifold.OneMfld.Compactness +public import LeanPool.OneManifold.OneMfld.FiniteIntervalCharts +public import LeanPool.OneManifold.OneMfld.FinitelyCharted +public import LeanPool.OneManifold.OneMfld.GlueBlocks +public import LeanPool.OneManifold.OneMfld.GlueCore +public import LeanPool.OneManifold.OneMfld.GlueNNReal +public import LeanPool.OneManifold.OneMfld.GlueUI +public import LeanPool.OneManifold.OneMfld.IntervalCharts +public import LeanPool.OneManifold.OneMfld.LocallyConnected +public import LeanPool.OneManifold.OneMfld.NiceCharts +public import LeanPool.OneManifold.OneMfld.Noncompact +public import LeanPool.OneManifold.OneMfld.Normalize +public import LeanPool.OneManifold.OneMfld.Outer +public import LeanPool.OneManifold.OneMfld.PartialHomeomorphHelpers +public import LeanPool.OneManifold.OneMfld.RealIntervals +public import LeanPool.OneManifold.OneMfld.TransitionMono +public import LeanPool.OneManifold.OneMfld.TwoComponents +public import LeanPool.OneManifold.OneMfld.UnitInterval +public import LeanPool.OneManifold.Solution + + +/-! +# The classification of compact 1-manifolds + +Source: url:https://github.com/sweeneyde/1mfld +Authors: Jim Fowler, Dennis Sweeney +Status: verified +Main declarations: `OneMfld.homeomorph_circle_or_unitInterval` +Tags: topology +MSC: 57N99, 54F65, 68V20 +-/ + +@[expose] public section diff --git a/LeanPool/OneManifold/OneMfld.lean b/LeanPool/OneManifold/OneMfld.lean new file mode 100644 index 000000000..21014042f --- /dev/null +++ b/LeanPool/OneManifold/OneMfld.lean @@ -0,0 +1,9 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import LeanPool.OneManifold.OneMfld.Classification +public import LeanPool.OneManifold.OneMfld.RealIntervals diff --git a/LeanPool/OneManifold/OneMfld/Charts.lean b/LeanPool/OneManifold/OneMfld/Charts.lean new file mode 100644 index 000000000..ea5db4cdf --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/Charts.lean @@ -0,0 +1,109 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.PartialHomeomorphHelpers + + +/-! Interval charts on a 1-manifold charted on `ℝ≥0`, and the `Overlap` relation. + +An `OChart` has an open-interval target `Ioo x y` (an interior chart); an `HChart` has a +half-open target `Iio x` (a boundary chart); an `IChart` is either. +-/ + +@[expose] public section + +namespace OneMfld + +/-- A one-dimensional chart with an open bounded interval as target. -/ +structure OChart (M : Type*) [TopologicalSpace M] + extends OpenPartialHomeomorph M NNReal where + target_ioo : (∃ x y, (Set.Ioo x y = target)) + +/-- A boundary chart with a half-open interval as target. -/ +structure HChart (M : Type*) [TopologicalSpace M] + extends OpenPartialHomeomorph M NNReal where + target_iio : (∃ x, (Set.Iio x = target)) + +/-- A chart whose target is an open interval or a half-open interval. -/ +structure IChart (M : Type*) [TopologicalSpace M] + extends OpenPartialHomeomorph M NNReal where + is_interval : (∃ x y, (Set.Ioo x y = target)) ∨ (∃ x, (Set.Iio x = target)) + +variable + {M : Type*} + [TopologicalSpace M] + +/-- Regard an interior chart as an interval chart. -/ +def OChart.toIChart (a : OChart M) : IChart M := + { a with is_interval := Or.inl a.target_ioo } + +/-- Regard a boundary chart as an interval chart. -/ +def HChart.toIChart (a : HChart M) : IChart M := + { a with is_interval := Or.inr a.target_iio } + +/-- Two sets meet and each has a point outside the other. -/ +def Overlap (U : Set α) (V : Set α) : Prop := + (U ∩ V).Nonempty ∧ (U \ V).Nonempty ∧ (V \ U).Nonempty + +theorem does_overlap' (U : Set α) (V : Set α) (hu : ¬ U ⊆ V) + : (U \ V).Nonempty := Set.sdiff_nonempty.mpr hu + +theorem does_overlap (U : Set α) (V : Set α) (h : (U ∩ V).Nonempty) (hu : ¬ U ⊆ V) (hv : ¬ V ⊆ U) + : Overlap U V := by + apply And.intro + · exact h + · apply And.intro + · exact does_overlap' U V hu + · exact does_overlap' V U hv + +theorem overlap_symm {U : Set α} {V : Set α} (h : Overlap U V) : Overlap V U := by + dsimp [Overlap] at h + apply And.intro + · exact Set.inter_nonempty_iff_exists_right.mpr h.1 + · apply And.intro + · exact h.2.2 + · exact h.2.1 + +lemma Overlap.nonempty {U : Set α} {V : Set α} (h : Overlap U V) : (Nonempty U) ∧ (Nonempty V) := by + have nonempty : (U ∩ V).Nonempty := h.1 + apply And.intro + · exact Set.Nonempty.to_subtype (Set.Nonempty.left nonempty) + · exact Set.Nonempty.to_subtype (Set.Nonempty.right nonempty) + +lemma chart_target_nonempty (φ : OpenPartialHomeomorph M NNReal) (h : φ.source.Nonempty) : + φ.target.Nonempty := by + rw [← PartialEquiv.image_source_eq_target φ.toPartialEquiv] + exact h.image _ + +lemma OChart.connected_source (a : OChart M) (h : a.source.Nonempty) : + IsConnected a.source := by + apply (partial_homeo_source_connected_iff_target_connected a.toOpenPartialHomeomorph).mpr + obtain ⟨x, y, hxy⟩ := a.target_ioo + have hne : a.target.Nonempty := chart_target_nonempty _ h + rw [←hxy] at hne ⊢ + exact isConnected_Ioo (Set.nonempty_Ioo.mp hne) + +lemma HChart.connected_source (a : HChart M) (h : a.source.Nonempty) : + IsConnected a.source := by + apply (partial_homeo_source_connected_iff_target_connected a.toOpenPartialHomeomorph).mpr + obtain ⟨x, hx⟩ := a.target_iio + have hne : a.target.Nonempty := chart_target_nonempty _ h + rw [←hx] at hne ⊢ + exact ⟨hne, isPreconnected_Iio⟩ + +lemma IChart.connected_source (a : IChart M) (h : a.source.Nonempty) : + IsConnected a.source := by + apply (partial_homeo_source_connected_iff_target_connected a.toOpenPartialHomeomorph).mpr + have hne : a.target.Nonempty := chart_target_nonempty _ h + rcases a.is_interval with (⟨x, y, hxy⟩ | ⟨x, hx⟩) + · rw [←hxy] at hne ⊢ + exact isConnected_Ioo (Set.nonempty_Ioo.mp hne) + · rw [←hx] at hne ⊢ + exact ⟨hne, isPreconnected_Iio⟩ + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/CircleBlocks.lean b/LeanPool/OneManifold/OneMfld/CircleBlocks.lean new file mode 100644 index 000000000..37c984502 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/CircleBlocks.lean @@ -0,0 +1,326 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Algebra.Order.Star.Real +public import Mathlib.Analysis.Normed.Order.Lattice +public import Mathlib.Tactic +public import Mathlib.Topology.Algebra.ProperAction.Basic +public import Mathlib.Topology.GDelta.MetrizableSpace +public import Mathlib.Topology.Instances.AddCircle.Defs +public import Mathlib.Topology.Instances.ZMultiples + + +/-! # Building blocks for the circle case + +* `mobiusFun c` — the Möbius reparametrization `x ↦ x / (x + c(1-x))` of the unit + interval in `ℝ≥0`, packaged as an `OpenPartialHomeomorph` with its image lemmas: it + lets us shrink the lower overlap component of a chart to sit below any `ε > 0` while + preserving the end-segment structure of images. +* `affineNNRealOPH k d` — the affine chart `x ↦ k·x + d` from `Ioo 0 1 ⊆ ℝ≥0` onto + `Ioo d (d + k) ⊆ ℝ`. +* Arithmetic for arcs in `AddCircle 1`: injectivity of the quotient map on short + windows, period-shift identities, the frontier of a closed arc, and the covering of + the circle by a closed arc and its complementary open arc. +-/ + +@[expose] public section + +namespace OneMfld + +open Set + +noncomputable section + +/-- The Möbius reparametrization of the unit interval of `ℝ≥0`. -/ +def mobiusFun (c x : NNReal) : NNReal := x / (x + c * (1 - x)) + +/-- `mobiusFun c` can push any interior point below any positive level, for large `c`. -/ +lemma exists_mobiusFun_lt {r ε : NNReal} (hr0 : 0 < r) (hr1 : r < 1) (hε : 0 < ε) : + ∃ c : NNReal, 0 < c ∧ mobiusFun c r < ε := by + have h1r : 0 < 1 - r := tsub_pos_of_lt hr1 + have hden : 0 < ε * (1 - r) := mul_pos hε h1r + refine ⟨r / (ε * (1 - r)), div_pos hr0 hden, ?_⟩ + rw [mobiusFun, div_lt_iff₀ (lt_of_lt_of_le hr0 le_self_add)] + have hkey : r / (ε * (1 - r)) * (1 - r) = r / ε := by + rw [div_mul_eq_mul_div, mul_comm ε (1 - r), ← div_div, + mul_div_cancel_right₀ _ h1r.ne'] + rw [mul_add, hkey, ← mul_div_assoc, mul_div_cancel_left₀ _ hε.ne'] + exact lt_add_of_pos_left r (mul_pos hε hr0) + +lemma mobiusFun_strictMonoOn (c : NNReal) (hc : 0 < c) : + StrictMonoOn (mobiusFun c) (Ioo 0 1) := by + intro x hx y hy hxy + have hdx : 0 < x + c * (1 - x) := lt_of_lt_of_le hx.1 le_self_add + have hdy : 0 < y + c * (1 - y) := lt_of_lt_of_le hy.1 le_self_add + rw [mobiusFun, mobiusFun, div_lt_div_iff₀ hdx hdy] + rw [← NNReal.coe_lt_coe] + push_cast [NNReal.coe_sub hx.2.le, NNReal.coe_sub hy.2.le] + have hc' : (0:ℝ) < c := hc + have hxy' : (x:ℝ) < y := hxy + nlinarith [mul_pos hc' (sub_pos.mpr hxy')] + +lemma mobiusFun_mem (c : NNReal) (hc : 0 < c) {t : NNReal} (ht : t ∈ Ioo (0 : NNReal) 1) : + mobiusFun c t ∈ Ioo (0:NNReal) 1 := by + have hden : 0 < t + c * (1 - t) := lt_of_lt_of_le ht.1 le_self_add + refine ⟨div_pos ht.1 hden, ?_⟩ + rw [mobiusFun, div_lt_one hden] + exact lt_add_of_pos_right t (mul_pos hc (tsub_pos_of_lt ht.2)) + +private lemma mobiusInverse_mem (c : NNReal) (hc : 0 < c) : + ∀ y ∈ Ioo (0 : NNReal) 1, + c * y / ((1 - y) + c * y) ∈ Ioo (0 : NNReal) 1 := by + intro y hy + have hden : 0 < (1 - y) + c * y := + lt_of_lt_of_le (tsub_pos_of_lt hy.2) le_self_add + exact ⟨div_pos (mul_pos hc hy.1) hden, + (div_lt_one hden).mpr (lt_add_of_pos_left _ (tsub_pos_of_lt hy.2))⟩ + +private lemma coe_mobiusFun (c : NNReal) : ∀ x : NNReal, x ≤ 1 → + ((mobiusFun c x : NNReal) : ℝ) = (x : ℝ) / ((x : ℝ) + c * (1 - (x : ℝ))) := by + intro x hx1 + rw [mobiusFun] + push_cast [NNReal.coe_sub hx1] + ring + +private lemma mobiusInverse_left (c : NNReal) (hc : 0 < c) : + ∀ x ∈ Ioo (0 : NNReal) 1, + c * mobiusFun c x / ((1 - mobiusFun c x) + c * mobiusFun c x) = x := by + have hc' : (0 : ℝ) < c := hc + intro x hx + have hfx := mobiusFun_mem c hc hx + have hX0 : (0:ℝ) < x := hx.1 + have hX1 : (x:ℝ) < 1 := hx.2 + have hD : (0:ℝ) < (x:ℝ) + c * (1 - (x:ℝ)) := by nlinarith + apply NNReal.coe_injective + push_cast [NNReal.coe_sub hfx.2.le, coe_mobiusFun c x hx.2.le] + have hkey : (1 - (x:ℝ) / ((x:ℝ) + c * (1 - (x:ℝ)))) + + c * ((x:ℝ) / ((x:ℝ) + c * (1 - (x:ℝ)))) = c / ((x:ℝ) + c * (1 - (x:ℝ))) := by + field_simp + ring + rw [hkey] + field_simp + +private lemma mobiusInverse_right (c : NNReal) (hc : 0 < c) : + ∀ y ∈ Ioo (0 : NNReal) 1, mobiusFun c (c * y / ((1 - y) + c * y)) = y := by + have hc' : (0 : ℝ) < c := hc + intro y hy + have hgy := mobiusInverse_mem c hc y hy + have hY0 : (0:ℝ) < y := hy.1 + have hY1 : (y:ℝ) < 1 := hy.2 + have hE : (0:ℝ) < (1 - (y:ℝ)) + c * (y:ℝ) := by nlinarith + have hgy_coe : ((c * y / ((1 - y) + c * y) : NNReal) : ℝ) + = c * (y:ℝ) / ((1 - (y:ℝ)) + c * (y:ℝ)) := by + push_cast [NNReal.coe_sub hy.2.le] + ring + apply NNReal.coe_injective + rw [coe_mobiusFun c _ hgy.2.le, hgy_coe] + have hkey : c * (y:ℝ) / ((1 - (y:ℝ)) + c * (y:ℝ)) + + c * (1 - c * (y:ℝ) / ((1 - (y:ℝ)) + c * (y:ℝ))) + = c / ((1 - (y:ℝ)) + c * (y:ℝ)) := by + field_simp + ring + rw [hkey] + field_simp + +private lemma continuousOn_mobiusFun (c : NNReal) : + ContinuousOn (mobiusFun c) (Ioo 0 1) := by + change ContinuousOn (fun x : NNReal => x / (x + c * (1 - x))) (Ioo 0 1) + exact ContinuousOn.div continuousOn_id + ((continuous_id.add (continuous_const.mul (continuous_const.sub continuous_id))).continuousOn) + (fun x hx => (lt_of_lt_of_le hx.1 le_self_add).ne') + +private lemma continuousOn_mobiusInverse (c : NNReal) : + ContinuousOn (fun y : NNReal => c * y / ((1 - y) + c * y)) (Ioo 0 1) := by + exact ContinuousOn.div (continuous_const.mul continuous_id).continuousOn + (((continuous_const.sub continuous_id).add + (continuous_const.mul continuous_id)).continuousOn) + (fun y hy => (lt_of_lt_of_le (tsub_pos_of_lt hy.2) le_self_add).ne') + +private lemma mobiusFun_image_lower (c : NNReal) (hc : 0 < c) : + ∀ t ∈ Ioo (0 : NNReal) 1, + mobiusFun c '' (Ioo 0 t) = Ioo 0 (mobiusFun c t) := by + intro t ht + ext z + constructor + · rintro ⟨x, hx, rfl⟩ + have hx1 : x ∈ Ioo (0:NNReal) 1 := ⟨hx.1, hx.2.trans ht.2⟩ + exact ⟨(mobiusFun_mem c hc hx1).1, mobiusFun_strictMonoOn c hc hx1 ht hx.2⟩ + · intro hz + have hz1 : z ∈ Ioo (0:NNReal) 1 := ⟨hz.1, hz.2.trans (mobiusFun_mem c hc ht).2⟩ + have hmem := mobiusInverse_mem c hc z hz1 + refine ⟨c * z / ((1 - z) + c * z), ⟨hmem.1, ?_⟩, mobiusInverse_right c hc z hz1⟩ + by_contra hcon + rw [not_lt] at hcon + have := (mobiusFun_strictMonoOn c hc).monotoneOn ht hmem hcon + rw [mobiusInverse_right c hc z hz1] at this + exact absurd hz.2 (not_lt.mpr this) + +private lemma mobiusFun_image_upper (c : NNReal) (hc : 0 < c) : + ∀ t ∈ Ioo (0 : NNReal) 1, + mobiusFun c '' (Ioo t 1) = Ioo (mobiusFun c t) 1 := by + intro t ht + ext z + constructor + · rintro ⟨x, hx, rfl⟩ + have hx1 : x ∈ Ioo (0:NNReal) 1 := ⟨ht.1.trans hx.1, hx.2⟩ + exact ⟨mobiusFun_strictMonoOn c hc ht hx1 hx.1, (mobiusFun_mem c hc hx1).2⟩ + · intro hz + have hz1 : z ∈ Ioo (0:NNReal) 1 := ⟨lt_trans (mobiusFun_mem c hc ht).1 hz.1, hz.2⟩ + have hmem := mobiusInverse_mem c hc z hz1 + refine ⟨c * z / ((1 - z) + c * z), ⟨?_, hmem.2⟩, mobiusInverse_right c hc z hz1⟩ + by_contra hcon + rw [not_lt] at hcon + have := (mobiusFun_strictMonoOn c hc).monotoneOn hmem ht hcon + rw [mobiusInverse_right c hc z hz1] at this + exact absurd hz.1 (not_lt.mpr this) + +/-- The Möbius reparametrization as a partial homeomorphism of `Ioo 0 1 ⊆ ℝ≥0`, with +its action on lower and upper end-segments. -/ +def mobiusOPH' (c : NNReal) (hc : 0 < c) : + { e : OpenPartialHomeomorph NNReal NNReal // + e.source = Ioo 0 1 ∧ e.target = Ioo 0 1 ∧ + (∀ x : NNReal, e.toFun x = mobiusFun c x) ∧ + (∀ t ∈ Ioo (0:NNReal) 1, e.toFun '' (Ioo 0 t) = Ioo 0 (mobiusFun c t)) ∧ + (∀ t ∈ Ioo (0:NNReal) 1, e.toFun '' (Ioo t 1) = Ioo (mobiusFun c t) 1) } := by + refine ⟨{ toFun := mobiusFun c + invFun := fun y => c * y / ((1 - y) + c * y) + source := Ioo 0 1 + target := Ioo 0 1 + map_source' := fun x hx => mobiusFun_mem c hc hx + map_target' := by exact mobiusInverse_mem c hc + left_inv' := by exact fun x hx => mobiusInverse_left c hc x hx + right_inv' := by exact fun y hy => mobiusInverse_right c hc y hy + open_source := isOpen_Ioo + open_target := isOpen_Ioo + continuousOn_toFun := by exact continuousOn_mobiusFun c + continuousOn_invFun := by exact continuousOn_mobiusInverse c }, by + exact ⟨rfl, rfl, fun x => rfl, mobiusFun_image_lower c hc, mobiusFun_image_upper c hc⟩⟩ + +/-- The affine map `x ↦ k·x + d` as a chart from `Ioo 0 1 ⊆ ℝ≥0` onto +`Ioo d (d + k) ⊆ ℝ`. -/ +def affineNNRealOPH (k d : ℝ) (hk : 0 < k) : + { e : OpenPartialHomeomorph NNReal ℝ // + e.source = Ioo 0 1 ∧ e.target = Ioo d (d + k) ∧ + ∀ x : NNReal, e.toFun x = k * (x : ℝ) + d } := by + refine ⟨{ toFun := fun x => k * (x : ℝ) + d + invFun := fun y => Real.toNNReal ((y - d) / k) + source := Ioo 0 1 + target := Ioo d (d + k) + map_source' := ?_ + map_target' := ?_ + left_inv' := ?_ + right_inv' := ?_ + open_source := isOpen_Ioo + open_target := isOpen_Ioo + continuousOn_toFun := + ((continuous_const.mul NNReal.continuous_coe).add continuous_const).continuousOn + continuousOn_invFun := + (continuous_real_toNNReal.comp + ((continuous_id.sub continuous_const).div_const k)).continuousOn }, + rfl, rfl, fun x => rfl⟩ + · intro x hx + have hx0 : (0:ℝ) < x := hx.1 + have hx1 : (x:ℝ) < 1 := hx.2 + constructor + · nlinarith + · nlinarith + · intro y hy + exact ⟨Real.toNNReal_pos.mpr (div_pos (sub_pos.mpr hy.1) hk), + Real.toNNReal_lt_one.mpr ((div_lt_one hk).mpr (by linarith [hy.2]))⟩ + · intro x _ + show Real.toNNReal ((k * (x:ℝ) + d - d) / k) = x + have h : (k * (x:ℝ) + d - d) / k = (x:ℝ) := by + field_simp + ring + rw [h, Real.toNNReal_coe] + · intro y hy + show k * ((Real.toNNReal ((y - d) / k) : NNReal) : ℝ) + d = y + rw [Real.coe_toNNReal _ (div_nonneg (sub_nonneg.mpr hy.1.le) hk.le)] + field_simp + ring + +section AddCircleArith + +/-- Two reals with the same image in `AddCircle 1` and distance less than `1` are +equal. -/ +lemma addCircle_coe_inj {x y : ℝ} (h : (x : AddCircle (1 : ℝ)) = (y : AddCircle (1 : ℝ))) + (hxy : |x - y| < 1) : x = y := by + have h0 : ((x - y : ℝ) : AddCircle (1:ℝ)) = 0 := by + rw [AddCircle.coe_sub, h, sub_self] + obtain ⟨n, hn⟩ := (AddCircle.coe_eq_zero_iff _).mp h0 + rw [zsmul_one] at hn + have h1 : |(n:ℝ)| < 1 := by rw [hn]; exact hxy + have h2 : |n| < 1 := by exact_mod_cast h1 + have h3 : n = 0 := Int.abs_lt_one_iff.mp h2 + rw [h3] at hn + simp only [Int.cast_zero] at hn + linarith + +/-- Shifting a real by the period `1` does not change its image in `AddCircle 1`. -/ +lemma addCircle_coe_add_one (x : ℝ) : + ((x + 1 : ℝ) : AddCircle (1:ℝ)) = (x : AddCircle (1:ℝ)) := + AddCircle.coe_add_period 1 x + +/-- The closed arc `coe '' Icc c d` is closed in `AddCircle 1`. -/ +lemma addCircle_arc_isClosed (c d : ℝ) : + IsClosed (((↑) : ℝ → AddCircle (1:ℝ)) '' Icc c d) := by + have : Fact ((0:ℝ) < 1) := ⟨one_pos⟩ + exact (isCompact_Icc.image (AddCircle.continuous_mk' 1)).isClosed + +/-- The open arc `coe '' Ioo c d` is open in `AddCircle 1`. -/ +lemma addCircle_arc_isOpen (c d : ℝ) : + IsOpen (((↑) : ℝ → AddCircle (1:ℝ)) '' Ioo c d) := + QuotientAddGroup.isOpenMap_coe _ isOpen_Ioo + +/-- The frontier of a closed arc is contained in its two endpoints. -/ +lemma addCircle_frontier_arc_subset {c d : ℝ} (hcd : c ≤ d) : + frontier (((↑) : ℝ → AddCircle (1:ℝ)) '' Icc c d) ⊆ + {((c : ℝ) : AddCircle (1:ℝ)), ((d : ℝ) : AddCircle (1:ℝ))} := by + have hclosed := addCircle_arc_isClosed c d + have hopen := addCircle_arc_isOpen c d + have hint : ((↑) : ℝ → AddCircle (1:ℝ)) '' Ioo c d ⊆ + interior (((↑) : ℝ → AddCircle (1:ℝ)) '' Icc c d) := + interior_maximal (image_mono Ioo_subset_Icc_self) hopen + intro z hz + rw [hclosed.frontier_eq] at hz + obtain ⟨⟨y, hy, rfl⟩, hz2⟩ := hz + have hnot : y ∉ Ioo c d := fun hy' => hz2 (hint ⟨y, hy', rfl⟩) + have hy' : y = c ∨ y = d := by + rcases lt_or_eq_of_le hy.1 with h1 | h1 + · rcases lt_or_eq_of_le hy.2 with h2 | h2 + · exact absurd ⟨h1, h2⟩ hnot + · exact Or.inr h2 + · exact Or.inl h1.symm + rcases hy' with rfl | rfl + · exact mem_insert _ _ + · exact mem_insert_of_mem _ (mem_singleton _) + +/-- A closed arc and the complementary open arc cover the circle. -/ +lemma addCircle_arc_union_covers {c d : ℝ} (hcd : c ≤ d) (hlen : d < c + 1) : + (((↑) : ℝ → AddCircle (1:ℝ)) '' Icc c d) ∪ + (((↑) : ℝ → AddCircle (1:ℝ)) '' Ioo d (c + 1)) = univ := by + have : Fact ((0:ℝ) < 1) := ⟨one_pos⟩ + rw [← image_union] + have hsets : Icc c d ∪ Ioo d (c + 1) = Ico c (c + 1) := by + ext x + simp only [mem_union, mem_Icc, mem_Ioo, mem_Ico] + constructor + · rintro (⟨h1, h2⟩ | ⟨h1, h2⟩) + · exact ⟨h1, lt_of_le_of_lt h2 hlen⟩ + · exact ⟨hcd.trans h1.le, h2⟩ + · rintro ⟨h1, h2⟩ + rcases le_or_gt x d with h | h + · exact Or.inl ⟨h1, h⟩ + · exact Or.inr ⟨h, h2⟩ + rw [hsets] + exact AddCircle.coe_image_Ico_eq 1 c + +end AddCircleArith + +end + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/CircleGlue.lean b/LeanPool/OneManifold/OneMfld/CircleGlue.lean new file mode 100644 index 000000000..27ddbf1aa --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/CircleGlue.lean @@ -0,0 +1,1015 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.Charts +public import LeanPool.OneManifold.OneMfld.Normalize +public import LeanPool.OneManifold.OneMfld.GlueBlocks +public import LeanPool.OneManifold.OneMfld.TwoComponents +public import LeanPool.OneManifold.OneMfld.CircleBlocks + + +/-! # The circle chart + +Two interior charts whose overlap is disconnected wrap around and close `M` into a +circle: after normalizing, the overlap has exactly two components, one at each end of +each chart. We shrink the charts' coordinates (Möbius) into a controlled numeric regime, +choose a split point in each component, and glue the two charts — each embedded as an +arc of `AddCircle 1` — with `OpenPartialHomeomorph.piecewise` along a closed sub-arc +whose frontier is the two split points. The result is a chart of `M` onto the whole of +`AddCircle 1` with source `a.source ∪ b.source`. +-/ + +@[expose] public section + +namespace OneMfld + +open Set + +/-! ### Small helpers -/ + +/-- Two reals with the same image in `AddCircle 1` differ by an integer. -/ +private lemma addCircle_coe_eq_iff_int {x y : ℝ} : + (x : AddCircle (1 : ℝ)) = (y : AddCircle (1 : ℝ)) ↔ ∃ n : ℤ, x - y = (n : ℝ) := by + rw [QuotientAddGroup.eq_iff_sub_mem] + constructor + · intro h + obtain ⟨n, hn⟩ := AddSubgroup.mem_zmultiples_iff.mp h + exact ⟨n, by rw [← hn, zsmul_eq_mul, mul_one]⟩ + · rintro ⟨n, hn⟩ + exact AddSubgroup.mem_zmultiples_iff.mpr ⟨n, by rw [zsmul_eq_mul, mul_one, hn]⟩ + +/-- Window analysis: two reals with the same circle image and difference in `(-1, 2)` +are equal or differ by exactly one period. -/ +private lemma addCircle_eq_or_eq_add_one {x y : ℝ} + (h : (x : AddCircle (1 : ℝ)) = (y : AddCircle (1 : ℝ))) + (h1 : -1 < x - y) (h2 : x - y < 2) : x = y ∨ x = y + 1 := by + obtain ⟨n, hn⟩ := addCircle_coe_eq_iff_int.mp h + have hn1 : (-1 : ℝ) < (n : ℝ) := hn ▸ h1 + have hn2 : ((n : ℤ) : ℝ) < 2 := hn ▸ h2 + have hn1' : (-1 : ℤ) < n := by exact_mod_cast hn1 + have hn2' : n < 2 := by exact_mod_cast hn2 + interval_cases n + · left + have : x - y = 0 := by rw [hn]; norm_num + linarith + · right + have : x - y = 1 := by rw [hn]; norm_num + linarith + +/-- Pull membership in a chart image back to membership in the set, using injectivity. -/ +private lemma mem_of_image_mem {M : Type*} [TopologicalSpace M] + (e : OpenPartialHomeomorph M NNReal) {W : Set M} {I : Set NNReal} + (hW : W ⊆ e.source) (himg : e '' W = I) {x : M} (hx : x ∈ e.source) + (hval : e x ∈ I) : x ∈ W := by + rw [← himg] at hval + obtain ⟨w, hw, hvw⟩ := hval + rwa [← e.injOn (hW hw) hx hvw] + +/-- A strictly increasing correspondence on `W` matches lower cuts at a point of `W`. -/ +private lemma mono_key_le {M : Type*} [TopologicalSpace M] + (A B : OpenPartialHomeomorph M NNReal) + {W : Set M} (hWA : W ⊆ A.source) + (mono : ∀ x ∈ W, ∀ y ∈ W, A x < A y → B x < B y) + {m : M} (hm : m ∈ W) : + ∀ x ∈ W, (A m ≤ A x ↔ B m ≤ B x) := by + intro x hx + constructor + · intro hle + rcases eq_or_lt_of_le hle with heq | hlt + · rw [A.injOn (hWA hm) (hWA hx) heq] + · exact (mono m hm x hx hlt).le + · intro hle + by_contra hgt + rw [not_le] at hgt + exact absurd hle (not_le.2 (mono x hx m hm hgt)) + +/-- A strictly increasing correspondence on `W` matches upper cuts at a point of `W`. -/ +private lemma mono_key_ge {M : Type*} [TopologicalSpace M] + (A B : OpenPartialHomeomorph M NNReal) + {W : Set M} (hWA : W ⊆ A.source) + (mono : ∀ x ∈ W, ∀ y ∈ W, A x < A y → B x < B y) + {m : M} (hm : m ∈ W) : + ∀ x ∈ W, (A x ≤ A m ↔ B x ≤ B m) := by + intro x hx + constructor + · intro hle + rcases eq_or_lt_of_le hle with heq | hlt + · rw [A.injOn (hWA hx) (hWA hm) heq] + · exact (mono x hx m hm hlt).le + · intro hle + by_contra hgt + rw [not_le] at hgt + exact absurd hle (not_le.2 (mono m hm x hx hgt)) + +/-- The real parameters of the circle gluing: slopes `kα`, `kg` and offset `g0` with the +matching identities and strict separation inequalities. -/ +private lemma circle_params {ρ ν σ μ : ℝ} + (hρ0 : 0 < ρ) (hρ4 : ρ < 1 / 4) (hν3 : 3 / 4 < ν) (hν1 : ν < 1) + (hσ0 : 0 < σ) (hσ4 : σ < 1 / 4) (hμ3 : 3 / 4 < μ) (hμ1 : μ < 1) : + ∃ kα kg g0 : ℝ, 0 < kα ∧ kα < 1 ∧ 0 < kg ∧ kg < 1 ∧ + kg * σ + g0 = kα * ν ∧ + kg * μ + g0 = 1 + kα * ρ ∧ + kα * ρ < g0 ∧ + g0 < kα * ν ∧ + 1 + kα * ρ < g0 + kg ∧ + g0 + kg < 1 + kα * ν := by + have hνρ : 1/2 < ν - ρ := by linarith + have hμσ0 : 0 < μ - σ := by linarith + have hd1 : (1 - μ + σ)/(ν - ρ) < 1 := by + rw [div_lt_one (by linarith)] + linarith + have h23 : (2/3 : ℝ) < 1 := by norm_num + obtain ⟨kα, hkα1, hkα2⟩ := exists_between (max_lt hd1 h23) + have hkα23 : (2/3 : ℝ) < kα := lt_of_le_of_lt (le_max_right _ _) hkα1 + have hkαd : (1 - μ + σ)/(ν - ρ) < kα := lt_of_le_of_lt (le_max_left _ _) hkα1 + have hkα0 : (0:ℝ) < kα := by linarith + have hnum : 0 < 1 - kα * (ν - ρ) := by + nlinarith [mul_pos (show (0:ℝ) < 1 - kα by linarith) (show (0:ℝ) < ν - ρ by linarith)] + set kg := (1 - kα * (ν - ρ)) / (μ - σ) with hkgdef + have hkg0 : 0 < kg := div_pos hnum hμσ0 + have hkgden : kg * (μ - σ) = 1 - kα * (ν - ρ) := div_mul_cancel₀ _ hμσ0.ne' + have hkg1 : kg < 1 := by + rw [hkgdef, div_lt_one hμσ0] + have := (div_lt_iff₀ (show (0:ℝ) < ν - ρ by linarith)).mp hkαd + linarith + set g0 := kα * ν - kg * σ with hg0def + have hid1 : kg * σ + g0 = kα * ν := by rw [hg0def]; ring + have hid2 : kg * μ + g0 = 1 + kα * ρ := by + rw [hg0def]; linear_combination hkgden + have hP1 : (1/3 : ℝ) < kα * (ν - ρ) := by nlinarith + have hP2 : (1 / 4 : ℝ) < kα * (ν - ρ) * μ := by nlinarith + have hkey : σ < kα * (ν - ρ) * μ := by linarith + have hc₁g0 : kα * ρ < g0 := by + have h2 : kg * σ * (μ - σ) = σ * (1 - kα * (ν - ρ)) := by + rw [← hkgden]; ring + have h3 : kg * σ * (μ - σ) < kα * (ν - ρ) * (μ - σ) := by + rw [h2]; nlinarith + have h4 : kg * σ < kα * (ν - ρ) := lt_of_mul_lt_mul_right h3 hμσ0.le + rw [hg0def]; nlinarith + have hg0d : g0 < kα * ν := by + rw [hg0def] + nlinarith [mul_pos hkg0 hσ0] + have h1c : 1 + kα * ρ < g0 + kg := by + nlinarith [hid2, mul_pos hkg0 (show (0:ℝ) < 1 - μ by linarith)] + have hg1d : g0 + kg < 1 + kα * ν := by + nlinarith [hid1, mul_pos hkg0 hσ0] + exact ⟨kα, kg, g0, hkα0, hkα2, hkg0, hkg1, hid1, hid2, hc₁g0, hg0d, h1c, hg1d⟩ + +/-- The target of a chart of the form `x ↦ coe (k * X x + d)` (with `X` landing onto +`Ioo 0 1`) is the arc `coe '' Ioo d (d + k)`. -/ +private lemma affine_circle_target {M : Type*} [TopologicalSpace M] + (X : OpenPartialHomeomorph M NNReal) (hXt : X.target = Ioo 0 1) + (E : OpenPartialHomeomorph M (AddCircle (1 : ℝ))) + (hEs : E.source = X.source) {k d : ℝ} (hk : 0 < k) + (hEval : ∀ x ∈ X.source, E x = ((k * ((X x : NNReal) : ℝ) + d : ℝ) : AddCircle (1 : ℝ))) : + E.target = ((↑) : ℝ → AddCircle (1 : ℝ)) '' (Ioo d (d + k)) := by + rw [← E.image_source_eq_target, hEs] + ext z + constructor + · rintro ⟨x, hx, rfl⟩ + rw [hEval x hx] + have hXx : X x ∈ Ioo (0:NNReal) 1 := hXt ▸ X.map_source hx + have h0 : (0:ℝ) < ((X x : NNReal) : ℝ) := by exact_mod_cast hXx.1 + have h1 : ((X x : NNReal) : ℝ) < 1 := by exact_mod_cast hXx.2 + exact mem_image_of_mem _ ⟨by nlinarith, by nlinarith⟩ + · rintro ⟨u, hu, rfl⟩ + have h0 : 0 < (u - d)/k := div_pos (by linarith [hu.1]) hk + have h1 : (u - d)/k < 1 := (div_lt_one hk).mpr (by linarith [hu.2]) + set t := Real.toNNReal ((u - d)/k) with htdef + have htc : (t : ℝ) = (u - d)/k := Real.coe_toNNReal _ h0.le + have ht : t ∈ Ioo (0:NNReal) 1 := by + constructor + · rw [← NNReal.coe_lt_coe, NNReal.coe_zero, htc]; exact h0 + · rw [← NNReal.coe_lt_coe, NNReal.coe_one, htc]; exact h1 + have htT : t ∈ X.target := hXt ▸ ht + refine ⟨X.symm t, X.map_target htT, ?_⟩ + rw [hEval _ (X.map_target htT), X.right_inv htT, htc] + congr 1 + field_simp + ring + +/-- Choose the orientation whose first component is the upper end-segment. -/ +private lemma exists_oriented_overlap_chart {M : Type*} [TopologicalSpace M] + (b : OChart M) (hbt : b.target = Ioo 0 1) (W₀ W₁ : Set M) + (hW₀ : W₀ ⊆ b.source) (hW₁ : W₁ ⊆ b.source) + (s q : NNReal) (hs0 : 0 < s) (hsq : s ≤ q) (hq1 : q < 1) + (harr : (b.toOpenPartialHomeomorph '' W₀ = Ioo q 1 ∧ + b.toOpenPartialHomeomorph '' W₁ = Ioo 0 s) ∨ + (b.toOpenPartialHomeomorph '' W₀ = Ioo 0 s ∧ + b.toOpenPartialHomeomorph '' W₁ = Ioo q 1)) : + ∃ B : OpenPartialHomeomorph M NNReal, + B.source = b.source ∧ B.target = Ioo 0 1 ∧ + ∃ s' q' : NNReal, 0 < s' ∧ s' ≤ q' ∧ q' < 1 ∧ + B '' W₀ = Ioo q' 1 ∧ B '' W₁ = Ioo 0 s' := by + rcases harr with ⟨h₀, h₁⟩ | ⟨h₀, h₁⟩ + · exact ⟨b.toOpenPartialHomeomorph, rfl, hbt, s, q, hs0, hsq, hq1, h₀, h₁⟩ + · obtain ⟨b₁, hb₁s, hb₁t, hb₁f⟩ := b.flip hbt + have himg₀ : b₁.toOpenPartialHomeomorph '' W₀ + = (fun y => 1 - y) '' (b.toOpenPartialHomeomorph '' W₀) := by + rw [← image_comp] + exact image_congr (fun x hx => hb₁f x (hW₀ hx)) + have himg₁ : b₁.toOpenPartialHomeomorph '' W₁ + = (fun y => 1 - y) '' (b.toOpenPartialHomeomorph '' W₁) := by + rw [← image_comp] + exact image_congr (fun x hx => hb₁f x (hW₁ hx)) + rw [h₀, reflect_image_Ioo_lower hs0 (hsq.trans hq1.le)] at himg₀ + rw [h₁, reflect_image_Ioo_upper hq1] at himg₁ + exact ⟨b₁.toOpenPartialHomeomorph, hb₁s, hb₁t, 1 - q, 1 - s, + tsub_pos_of_lt hq1, tsub_le_tsub_left hsq 1, tsub_lt_self one_pos hs0, + himg₀, himg₁⟩ + +/-- Normalize the two charts and orient their two overlap components oppositely. -/ +private lemma exists_oriented_charts {M : Type*} [TopologicalSpace M] [T2Space M] + (a b : OChart M) (h : Overlap a.source b.source) + (hdisc : ¬ IsConnected (a.source ∩ b.source)) : + ∃ (A B : OpenPartialHomeomorph M NNReal) (W₀ W₁ : Set M) (r p s q : NNReal), + A.source = a.source ∧ B.source = b.source ∧ + A.target = Ioo 0 1 ∧ B.target = Ioo 0 1 ∧ + A.source ∩ B.source = W₀ ∪ W₁ ∧ + A '' W₀ = Ioo 0 r ∧ A '' W₁ = Ioo p 1 ∧ + B '' W₀ = Ioo q 1 ∧ B '' W₁ = Ioo 0 s ∧ + 0 < r ∧ r ≤ p ∧ p < 1 ∧ 0 < s ∧ s ≤ q ∧ q < 1 := by + have hane : a.source.Nonempty := h.1.mono inter_subset_left + have hbne : b.source.Nonempty := h.1.mono inter_subset_right + obtain ⟨a₀, ha₀s, ha₀t, -⟩ := a.rescale hane + obtain ⟨b₀, hb₀s, hb₀t, -⟩ := b.rescale hbne + have hab : (a₀.source \ b₀.source).Nonempty := by rw [ha₀s, hb₀s]; exact h.2.1 + have hba : (b₀.source \ a₀.source).Nonempty := by rw [ha₀s, hb₀s]; exact h.2.2 + have hne : (a₀.source ∩ b₀.source).Nonempty := by rw [ha₀s, hb₀s]; exact h.1 + have hdisc' : ¬ IsConnected (a₀.source ∩ b₀.source) := by rw [ha₀s, hb₀s]; exact hdisc + have hbconn : IsConnected b₀.source := b₀.connected_source (hb₀s ▸ hbne) + obtain ⟨W₀, W₁, r₀, p₁, hunion, hcomp₀, hcomp₁, hdisj, himgA₀, himgA₁, hr₀0, hr₀p, hp₁1⟩ := + two_components_structure a₀.toOpenPartialHomeomorph b₀.toOpenPartialHomeomorph + ha₀t hbconn hab hba hne hdisc' + have hW₀S : W₀ ⊆ a₀.source ∩ b₀.source := by rw [hunion]; exact subset_union_left + have hW₁S : W₁ ⊆ a₀.source ∩ b₀.source := by rw [hunion]; exact subset_union_right + have hne₀ : W₀.Nonempty := by + have himg : (a₀.toOpenPartialHomeomorph '' W₀).Nonempty := by + rw [himgA₀]; exact nonempty_Ioo.2 hr₀0 + exact himg.of_image + have hne₁ : W₁.Nonempty := by + have himg : (a₀.toOpenPartialHomeomorph '' W₁).Nonempty := by + rw [himgA₁]; exact nonempty_Ioo.2 hp₁1 + exact himg.of_image + have haconn : IsConnected a₀.source := a₀.connected_source (ha₀s ▸ hane) + obtain ⟨s₁, q₀, hs₁0, hs₁q₀, hq₀1, harr⟩ := + two_components_other_chart a₀.toOpenPartialHomeomorph b₀.toOpenPartialHomeomorph + hb₀t haconn hab hcomp₀ hcomp₁ hne₀ hne₁ hdisj + obtain ⟨B₁, hB₁s, hB₁t, s₂, q₂, hs₂0, hs₂q, hq₂1, himgB₀, himgB₁⟩ := + exists_oriented_overlap_chart b₀ hb₀t W₀ W₁ + (fun x hx => (hW₀S hx).2) (fun x hx => (hW₁S hx).2) + s₁ q₀ hs₁0 hs₁q₀ hq₀1 harr + have hunion' : a₀.source ∩ B₁.source = W₀ ∪ W₁ := by rw [hB₁s]; exact hunion + exact ⟨a₀.toOpenPartialHomeomorph, B₁, W₀, W₁, r₀, p₁, s₂, q₂, + ha₀s, hB₁s.trans hb₀s, ha₀t, hB₁t, hunion', himgA₀, himgA₁, himgB₀, himgB₁, + hr₀0, hr₀p, hp₁1, hs₂0, hs₂q, hq₂1⟩ + +/-- Composing charts with the same unit-interval interface preserves both chart sets. -/ +private lemma unitInterval_trans_source_target {M : Type*} [TopologicalSpace M] + (X : OpenPartialHomeomorph M NNReal) (m : OpenPartialHomeomorph NNReal NNReal) + (hXt : X.target = Ioo 0 1) (hms : m.source = Ioo 0 1) (hmt : m.target = Ioo 0 1) : + (X.trans m).source = X.source ∧ (X.trans m).target = Ioo 0 1 := by + constructor + · rw [OpenPartialHomeomorph.trans_source, hms] + refine inter_eq_left.mpr fun x hx => ?_ + rw [mem_preimage, ← hXt] + exact X.map_source hx + · rw [OpenPartialHomeomorph.trans_target, hmt] + refine inter_eq_left.mpr fun y hy => ?_ + rw [mem_preimage, hXt, ← hms] + exact m.map_target (by rw [hmt]; exact hy) + +/-- Möbius-shrink a chart's lower overlap below one quarter, retaining both end-segments. -/ +private lemma exists_shrunk_interval_chart {M : Type*} [TopologicalSpace M] + (X : OpenPartialHomeomorph M NNReal) (hXt : X.target = Ioo 0 1) + (W₀ W₁ : Set M) (r p : NNReal) + (himg₀ : X '' W₀ = Ioo 0 r) (himg₁ : X '' W₁ = Ioo p 1) + (hr0 : 0 < r) (hrp : r ≤ p) (hp1 : p < 1) : + ∃ (A : OpenPartialHomeomorph M NNReal) (r' p' : NNReal), + A.source = X.source ∧ A.target = Ioo 0 1 ∧ + A '' W₀ = Ioo 0 r' ∧ A '' W₁ = Ioo p' 1 ∧ + 0 < r' ∧ r' ≤ p' ∧ p' < 1 ∧ r' < 1 / 4 := by + have hr1 : r < 1 := lt_of_le_of_lt hrp hp1 + have hquarter : (0 : NNReal) < 1 / 4 := by norm_num + obtain ⟨c, hc0, hc⟩ := exists_mobiusFun_lt hr0 hr1 hquarter + obtain ⟨m, hms, hmt, hmf, hmlo, hmhi⟩ := mobiusOPH' c hc0 + set A := X.trans m with hAdef + obtain ⟨hAs, hAt⟩ := unitInterval_trans_source_target X m hXt hms hmt + have hrmem : r ∈ Ioo (0 : NNReal) 1 := ⟨hr0, hr1⟩ + have hpmem : p ∈ Ioo (0 : NNReal) 1 := ⟨lt_of_lt_of_le hr0 hrp, hp1⟩ + have himA₀ : A '' W₀ = Ioo 0 (mobiusFun c r) := by + rw [hAdef, OpenPartialHomeomorph.coe_trans, image_comp, himg₀] + exact hmlo r hrmem + have himA₁ : A '' W₁ = Ioo (mobiusFun c p) 1 := by + rw [hAdef, OpenPartialHomeomorph.coe_trans, image_comp, himg₁] + exact hmhi p hpmem + have hmono := mobiusFun_strictMonoOn c hc0 + have hrp' : mobiusFun c r ≤ mobiusFun c p := by + rcases eq_or_lt_of_le hrp with heq | hlt + · rw [heq] + · exact (hmono hrmem hpmem hlt).le + have hr'mem := mobiusFun_mem c hc0 hrmem + have hp'mem := mobiusFun_mem c hc0 hpmem + exact ⟨A, mobiusFun c r, mobiusFun c p, hAs, hAt, himA₀, himA₁, + hr'mem.1, hrp', hp'mem.2, hc⟩ + +/-- Normalize both charts to target `Ioo 0 1`, split the overlap into its two +components, orient the second chart so the component lower in `a` is upper in `b`, and +Möbius-shrink both charts so the lower end-segments sit below `1 / 4`. -/ +private lemma exists_normalized_charts {M : Type*} [TopologicalSpace M] [T2Space M] + (a b : OChart M) (h : Overlap a.source b.source) + (hdisc : ¬ IsConnected (a.source ∩ b.source)) : + ∃ (A B : OpenPartialHomeomorph M NNReal) (W₀ W₁ : Set M) (r p s q : NNReal), + A.source = a.source ∧ B.source = b.source ∧ + A.target = Ioo 0 1 ∧ B.target = Ioo 0 1 ∧ + A.source ∩ B.source = W₀ ∪ W₁ ∧ + A '' W₀ = Ioo 0 r ∧ A '' W₁ = Ioo p 1 ∧ + B '' W₀ = Ioo q 1 ∧ B '' W₁ = Ioo 0 s ∧ + 0 < r ∧ r ≤ p ∧ p < 1 ∧ r < 1 / 4 ∧ + 0 < s ∧ s ≤ q ∧ q < 1 ∧ s < 1 / 4 := by + obtain ⟨A₀, B₀, W₀, W₁, r₀, p₀, s₀, q₀, hA₀s, hB₀s, hA₀t, hB₀t, + hunion, himgA₀, himgA₁, himgB₀, himgB₁, hr₀0, hr₀p, hp₀1, hs₀0, hs₀q, hq₀1⟩ := + exists_oriented_charts a b h hdisc + obtain ⟨A, r, p, hAs, hAt, himA₀, himA₁, hr0, hrp, hp1, hr4⟩ := + exists_shrunk_interval_chart A₀ hA₀t W₀ W₁ r₀ p₀ himgA₀ himgA₁ hr₀0 hr₀p hp₀1 + obtain ⟨B, s, q, hBs, hBt, himB₁, himB₀, hs0, hsq, hq1, hs4⟩ := + exists_shrunk_interval_chart B₀ hB₀t W₁ W₀ s₀ q₀ himgB₁ himgB₀ hs₀0 hs₀q hq₀1 + have hunion' : A.source ∩ B.source = W₀ ∪ W₁ := by + rw [hAs, hBs] + exact hunion + exact ⟨A, B, W₀, W₁, r, p, s, q, hAs.trans hA₀s, hBs.trans hB₀s, + hAt, hBt, hunion', himA₀, himA₁, himB₀, himB₁, + hr0, hrp, hp1, hr4, hs0, hsq, hq1, hs4⟩ + +private theorem circle_overlap_membership + {M : Type*} + [TopologicalSpace M] + (A B : OpenPartialHomeomorph M NNReal) + (W₀ W₁ : Set M) + (r p s q : NNReal) + (hBt : B.target = Ioo 0 1) + (hunion : A.source ∩ B.source = W₀ ∪ W₁) + (himgA₀ : A '' W₀ = Ioo 0 r) + (himgA₁ : A '' W₁ = Ioo p 1) + (himgB₀ : B '' W₀ = Ioo q 1) + (himgB₁ : B '' W₁ = Ioo 0 s) + (hrp : r ≤ p) + (hsq : s ≤ q) + (hW₀A : W₀ ⊆ A.source) + (hW₀B : W₀ ⊆ B.source) + (hW₁A : W₁ ⊆ A.source) + (hW₁B : W₁ ⊆ B.source) + (ν : NNReal) + (hpν : p < ν) + (m₁ : M) + (hm₁ν : A m₁ = ν) + (μ : NNReal) + (hqμ : q < μ) + (m₀ : M) + (hm₀μ : B m₀ = μ) + (hρmem : A m₀ ∈ Ioo 0 r) + (hσmem : B m₁ ∈ Ioo 0 s) + (K₀ : ∀ x ∈ W₀, A m₀ ≤ A x ↔ B m₀ ≤ B x) + (K₁ : ∀ x ∈ W₁, A x ≤ A m₁ ↔ B x ≤ B m₁) + (sset : Set M) + (hssetdef : sset = A.source ∩ A ⁻¹' Icc (A m₀) ν) : + ∀ y ∈ B.source, B y ≤ B m₁ ∨ B m₀ ≤ B y ↔ y ∈ sset := by + classical + intro y hy + rw [hssetdef] + by_cases hyA : y ∈ A.source + · have hyS : y ∈ W₀ ∪ W₁ := by rw [← hunion]; exact ⟨hyA, hy⟩ + rcases hyS with hyW | hyW + · have hAy : A y ∈ Ioo 0 r := by rw [← himgA₀]; exact mem_image_of_mem _ hyW + have hBy : B y ∈ Ioo q 1 := by rw [← himgB₀]; exact mem_image_of_mem _ hyW + have hnots : ¬ (B y ≤ B m₁) := by + rw [not_le] + exact lt_trans (lt_of_lt_of_le hσmem.2 hsq) hBy.1 + have hK := K₀ y hyW + constructor + · rintro (hc | hc) + · exact absurd hc hnots + · refine ⟨hyA, ?_⟩ + rw [mem_preimage] + exact ⟨hK.mpr hc, le_of_lt (lt_of_lt_of_le hAy.2 (hrp.trans hpν.le))⟩ + · rintro ⟨-, hmem⟩ + rw [mem_preimage] at hmem + exact Or.inr (hK.mp hmem.1) + · have hAy : A y ∈ Ioo p 1 := by rw [← himgA₁]; exact mem_image_of_mem _ hyW + have hBy : B y ∈ Ioo 0 s := by rw [← himgB₁]; exact mem_image_of_mem _ hyW + have hnots : ¬ (B m₀ ≤ B y) := by + rw [not_le, hm₀μ] + exact lt_trans (lt_of_lt_of_le hBy.2 hsq) hqμ + have hK := K₁ y hyW + constructor + · rintro (hc | hc) + · refine ⟨hyA, ?_⟩ + rw [mem_preimage] + refine ⟨le_of_lt (lt_trans (lt_of_lt_of_le hρmem.2 hrp) hAy.1), ?_⟩ + rw [← hm₁ν] + exact hK.mpr hc + · exact absurd hc hnots + · rintro ⟨-, hmem⟩ + rw [mem_preimage] at hmem + refine Or.inl (hK.mp ?_) + rw [hm₁ν] + exact hmem.2 + · constructor + · rintro (hc | hc) + · exfalso + have hBy0 : (0:NNReal) < B y := by + have hmem := B.map_source hy + rw [hBt] at hmem + exact hmem.1 + have hmem : B y ∈ Ioo 0 s := ⟨hBy0, lt_of_le_of_lt hc hσmem.2⟩ + have hyW : y ∈ W₁ := mem_of_image_mem B hW₁B himgB₁ hy hmem + exact hyA (hW₁A hyW) + · exfalso + have hBy1 : B y < 1 := by + have hmem := B.map_source hy + rw [hBt] at hmem + exact hmem.2 + rw [hm₀μ] at hc + have hmem : B y ∈ Ioo q 1 := ⟨lt_of_lt_of_le hqμ hc, hBy1⟩ + have hyW : y ∈ W₀ := mem_of_image_mem B hW₀B himgB₀ hy hmem + exact hyA (hW₀A hyW) + · rintro ⟨hyA', -⟩ + exact absurd hyA' hyA + +private theorem glue_circle_arcs + {M : Type*} + [TopologicalSpace M] + (A B : OpenPartialHomeomorph M NNReal) + (W₀ W₁ : Set M) + (r p : NNReal) + (hAt : A.target = Ioo 0 1) + (himgA₀ : A '' W₀ = Ioo 0 r) + (himgA₁ : A '' W₁ = Ioo p 1) + (hW₀A : W₀ ⊆ A.source) + (hW₀B : W₀ ⊆ B.source) + (hW₁A : W₁ ⊆ A.source) + (hW₁B : W₁ ⊆ B.source) + (ν : NNReal) + (hpν : p < ν) + (m₁ : M) + (hm₁ν : A m₁ = ν) + (μ : NNReal) + (m₀ : M) + (hm₀μ : B m₀ = μ) + (hm₀A : m₀ ∈ A.source) + (hm₀B : m₀ ∈ B.source) + (hm₁A : m₁ ∈ A.source) + (hm₁B : m₁ ∈ B.source) + (hρmem : A m₀ ∈ Ioo 0 r) + (kα kg g0 : ℝ) + (hkα1 : kα < 1) + (hkg1 : kg < 1) + (hid1 : kg * ↑(B m₁) + g0 = kα * ↑ν) + (hid2 : kg * ↑μ + g0 = 1 + kα * ↑(A m₀)) + (hcg0 : kα * ↑(A m₀) < g0) + (hg0d : g0 < kα * ↑ν) + (h1c : 1 + kα * ↑(A m₀) < g0 + kg) + (hc₁0 : 0 < kα * ↑(A m₀)) + (hd₁kα : kα * ↑ν < kα) + (hc₁1 : kα * ↑(A m₀) < 1) + (hc₁d₁ : kα * ↑(A m₀) ≤ kα * ↑ν) + (e e' : OpenPartialHomeomorph M (AddCircle 1)) + (heval : ∀ (x : M), e x = ↑(kα * ↑(A x))) + (he'val : ∀ (x : M), e' x = ↑(kg * ↑(B x) + g0)) + (hes : e.source = A.source) + (he's : e'.source = B.source) + (htargetE : e.target = QuotientAddGroup.mk '' Ioo 0 kα) + (htargetE' : e'.target = QuotientAddGroup.mk '' Ioo g0 (g0 + kg)) + (sset : Set M) + (tset : Set (ℝ ⧸ AddSubgroup.zmultiples 1)) + (hssub : sset ⊆ A.source) + (H : e.IsImage sset tset) + (claim1 : + ∀ v ∈ Ioo g0 (g0 + kg), ↑v ∈ tset ↔ v ∈ Icc (kα * ↑(A m₀)) (kα * ↑ν) ∨ v ∈ Icc (1 + + kα * ↑(A m₀)) (1 + kα * ↑ν)) + (H' : e'.IsImage sset tset) + (hfrontier : frontier tset ⊆ {↑(kα * ↑(A m₀)), ↑(kα * ↑ν)}) + (hssetdef : sset = A.source ∩ A ⁻¹' Icc (A m₀) ν) + (htsetdef : tset = ((↑) : ℝ → AddCircle (1 : ℝ)) '' Icc (kα * (A m₀ : ℝ)) (kα * (ν : ℝ))) : + ∃ f : OpenPartialHomeomorph M (AddCircle (1 : ℝ)), + f.source = A.source ∪ B.source ∧ f.target = univ := by + classical + -- Injectivity of each chart identifies the split-point fibers. + have he₀ : e m₀ = ((kα * ((A m₀ : NNReal) : ℝ) : ℝ) : AddCircle (1 : ℝ)) := + heval m₀ + have he₁ : e m₁ = ((kα * ((ν : NNReal) : ℝ) : ℝ) : AddCircle (1 : ℝ)) := by + rw [heval m₁, hm₁ν] + have he'₀ : e' m₀ = e m₀ := by + rw [he'val m₀, hm₀μ, hid2, heval m₀, add_comm 1] + exact addCircle_coe_add_one _ + have he'₁ : e' m₁ = e m₁ := by + rw [he'val m₁, hid1, heval m₁, hm₁ν] + have Pce : ∀ x ∈ A.source, + (e x = ((kα * ((A m₀ : NNReal):ℝ) : ℝ) : AddCircle (1 : ℝ)) ↔ x = m₀) := by + intro x hx + rw [← he₀] + exact ⟨fun h => e.injOn (hes.symm ▸ hx) (hes.symm ▸ hm₀A) h, congrArg e⟩ + have Pde : ∀ x ∈ A.source, + (e x = ((kα * ((ν : NNReal):ℝ) : ℝ) : AddCircle (1 : ℝ)) ↔ x = m₁) := by + intro x hx + rw [← he₁] + exact ⟨fun h => e.injOn (hes.symm ▸ hx) (hes.symm ▸ hm₁A) h, congrArg e⟩ + have Pce' : ∀ y ∈ B.source, + (e' y = ((kα * ((A m₀ : NNReal):ℝ) : ℝ) : AddCircle (1 : ℝ)) ↔ y = m₀) := by + intro y hy + rw [← he₀, ← he'₀] + exact ⟨fun h => e'.injOn (he's.symm ▸ hy) (he's.symm ▸ hm₀B) h, congrArg e'⟩ + have Pde' : ∀ y ∈ B.source, + (e' y = ((kα * ((ν : NNReal):ℝ) : ℝ) : AddCircle (1 : ℝ)) ↔ y = m₁) := by + intro y hy + rw [← he₁, ← he'₁] + exact ⟨fun h => e'.injOn (he's.symm ▸ hy) (he's.symm ▸ hm₁B) h, congrArg e'⟩ + -- Frontier agreement and equality on the frontier. + have hpre := H.frontier.preimage_eq + have hpre' := H'.frontier.preimage_eq + have Hs : e.source ∩ frontier sset = e'.source ∩ frontier sset := by + rw [← hpre, ← hpre'] + ext x + simp only [mem_inter_iff, mem_preimage] + constructor + · rintro ⟨hx, hfx⟩ + have hx' : x ∈ A.source := by rwa [hes] at hx + have hex := hfrontier hfx + simp only [mem_insert_iff, mem_singleton_iff] at hex + rcases hex with hcx | hdx + · have hxm : x = m₀ := (Pce x hx').mp hcx + subst hxm + refine ⟨by rw [he's]; exact hm₀B, ?_⟩ + rw [(Pce' x hm₀B).mpr rfl, ← hcx] + exact hfx + · have hxm : x = m₁ := (Pde x hx').mp hdx + subst hxm + refine ⟨by rw [he's]; exact hm₁B, ?_⟩ + rw [(Pde' x hm₁B).mpr rfl, ← hdx] + exact hfx + · rintro ⟨hx, hfx⟩ + have hx' : x ∈ B.source := by rwa [he's] at hx + have hex := hfrontier hfx + simp only [mem_insert_iff, mem_singleton_iff] at hex + rcases hex with hcx | hdx + · have hxm : x = m₀ := (Pce' x hx').mp hcx + subst hxm + refine ⟨by rw [hes]; exact hm₀A, ?_⟩ + rw [(Pce x hm₀A).mpr rfl, ← hcx] + exact hfx + · have hxm : x = m₁ := (Pde' x hx').mp hdx + subst hxm + refine ⟨by rw [hes]; exact hm₁A, ?_⟩ + rw [(Pde x hm₁A).mpr rfl, ← hdx] + exact hfx + have Heq : Set.EqOn e e' (e.source ∩ frontier sset) := by + rw [← hpre] + rintro x ⟨hx, hfx⟩ + rw [mem_preimage] at hfx + have hx' : x ∈ A.source := by rwa [hes] at hx + have hex := hfrontier hfx + simp only [mem_insert_iff, mem_singleton_iff] at hex + rcases hex with hcx | hdx + · have hxm : x = m₀ := (Pce x hx').mp hcx + subst hxm + rw [hcx, (Pce' x hm₀B).mpr rfl] + · have hxm : x = m₁ := (Pde x hx').mp hdx + subst hxm + rw [hdx, (Pde' x hm₁B).mpr rfl] + -- Glue. + refine ⟨e.piecewise e' sset tset H H' Hs Heq, ?_, ?_⟩ + · change Set.ite sset e.source e'.source = A.source ∪ B.source + have hite : Set.ite sset e.source e'.source + = (e.source ∩ sset) ∪ (e'.source \ sset) := rfl + rw [hite, hes, he's] + apply Subset.antisymm + · rintro x (⟨hx, -⟩ | ⟨hx, -⟩) + · exact Or.inl hx + · exact Or.inr hx + · rintro x hx + by_cases hxs : x ∈ sset + · exact Or.inl ⟨hssub hxs, hxs⟩ + · rcases hx with hxa | hxb + · have hxB : x ∈ B.source := by + have hAx : A x ∈ Ioo (0:NNReal) 1 := hAt ▸ A.map_source hxa + have hnot : A x ∉ Icc (A m₀) ν := by + intro hmem + exact hxs (by rw [hssetdef]; exact ⟨hxa, hmem⟩) + rcases lt_or_ge (A x) (A m₀) with hlt | hge + · have hmem : A x ∈ Ioo 0 r := ⟨hAx.1, lt_trans hlt hρmem.2⟩ + exact hW₀B (mem_of_image_mem A hW₀A himgA₀ hxa hmem) + · rcases le_or_gt (A x) ν with hle | hgt + · exact absurd ⟨hge, hle⟩ hnot + · have hmem : A x ∈ Ioo p 1 := ⟨lt_trans hpν hgt, hAx.2⟩ + exact hW₁B (mem_of_image_mem A hW₁A himgA₁ hxa hmem) + exact Or.inr ⟨hxB, hxs⟩ + · exact Or.inr ⟨hxb, hxs⟩ + · change Set.ite tset e.target e'.target = univ + have hite : Set.ite tset e.target e'.target + = (e.target ∩ tset) ∪ (e'.target \ tset) := rfl + rw [hite] + have h1 : e.target ∩ tset = tset := by + apply inter_eq_right.mpr + rw [htargetE, htsetdef] + exact image_mono + (fun u hu => ⟨lt_of_lt_of_le hc₁0 hu.1, lt_of_le_of_lt hu.2 hd₁kα⟩) + have h2 : e'.target \ tset = ((↑) : ℝ → AddCircle (1 : ℝ)) '' + (Ioo (kα * ((ν : NNReal):ℝ)) (kα * ((A m₀ : NNReal):ℝ) + 1)) := by + rw [htargetE'] + ext z + constructor + · rintro ⟨⟨v, hv, rfl⟩, hnt⟩ + have hnor : ¬(v ∈ Icc (kα * ((A m₀ : NNReal):ℝ)) (kα * ((ν : NNReal):ℝ)) ∨ + v ∈ Icc (1 + kα * ((A m₀ : NNReal):ℝ)) (1 + kα * ((ν : NNReal):ℝ))) := + fun hor => hnt ((claim1 v hv).mpr hor) + push Not at hnor + obtain ⟨hn1, hn2⟩ := hnor + rw [mem_Icc] at hn1 hn2 + push Not at hn1 hn2 + refine mem_image_of_mem _ ⟨?_, ?_⟩ + · exact hn1 (by linarith [hv.1]) + · by_contra hge + push Not at hge + have := hn2 (by linarith) + linarith [hv.2] + · rintro ⟨u, hu, rfl⟩ + have huv : u ∈ Ioo g0 (g0 + kg) := + ⟨lt_trans hg0d hu.1, by linarith [hu.2]⟩ + refine ⟨mem_image_of_mem _ huv, ?_⟩ + intro ht + rcases (claim1 u huv).mp ht with hin | hin + · rw [mem_Icc] at hin + linarith [hu.1, hin.2] + · rw [mem_Icc] at hin + linarith [hu.2, hin.1] + rw [h1, h2, htsetdef] + exact addCircle_arc_union_covers hc₁d₁ (by linarith) + +private theorem circle_coordinate_H + {M : Type*} + [TopologicalSpace M] + (A : OpenPartialHomeomorph M NNReal) + (ν : NNReal) + (m₀ : M) + (kα : ℝ) + (hkα0 : 0 < kα) + (hkα1 : kα < 1) + (hc₁0 : 0 < kα * ↑(A m₀)) + (hd₁kα : kα * ↑ν < kα) + (e : OpenPartialHomeomorph M (AddCircle 1)) + (heval : ∀ (x : M), e x = ↑(kα * ↑(A x))) + (hbounds : ∀ x ∈ A.source, 0 < kα * ↑(A x) ∧ kα * ↑(A x) < kα) + (hes : e.source = A.source) + (sset : Set M) + (hssetdef : sset = A.source ∩ A ⁻¹' Icc (A m₀) ν) + (tset : Set (ℝ ⧸ AddSubgroup.zmultiples 1)) + (htsetdef : tset = QuotientAddGroup.mk '' Icc (kα * ↑(A m₀)) (kα * ↑ν)) : + e.IsImage sset tset := by + classical + intro x hx + rw [hes] at hx + rw [heval x] + obtain ⟨hb0, hb1⟩ := hbounds x hx + constructor + · intro hy + rw [htsetdef] at hy + rcases hy with ⟨y, hy, hye⟩ + have hyeq : y = kα * ((A x : NNReal):ℝ) := by + apply addCircle_coe_inj hye + rw [abs_lt] + constructor + · linarith [hy.1] + · linarith [hy.2] + rw [hyeq] at hy + have hxle : ((A m₀ : NNReal):ℝ) ≤ ((A x : NNReal):ℝ) := le_of_mul_le_mul_left hy.1 hkα0 + have hxge : ((A x : NNReal):ℝ) ≤ ((ν : NNReal):ℝ) := le_of_mul_le_mul_left hy.2 hkα0 + rw [hssetdef] + refine ⟨hx, ?_⟩ + rw [mem_preimage] + exact ⟨by exact_mod_cast hxle, by exact_mod_cast hxge⟩ + · intro hmem + rw [hssetdef] at hmem + obtain ⟨-, hmem2⟩ := hmem + rw [mem_preimage] at hmem2 + have h1' : ((A m₀ : NNReal):ℝ) ≤ ((A x : NNReal):ℝ) := by exact_mod_cast hmem2.1 + have h2' : ((A x : NNReal):ℝ) ≤ ((ν : NNReal):ℝ) := by exact_mod_cast hmem2.2 + rw [htsetdef] + exact mem_image_of_mem _ ⟨mul_le_mul_of_nonneg_left h1' hkα0.le, + mul_le_mul_of_nonneg_left h2' hkα0.le⟩ + +private theorem circle_coordinate_claim1 + {M : Type*} + [TopologicalSpace M] + (A : OpenPartialHomeomorph M NNReal) + (ν : NNReal) + (m₀ : M) + (kα kg g0 : ℝ) + (hkα1 : kα < 1) + (hkg1 : kg < 1) + (hg0d : g0 < kα * ↑ν) + (h1c : 1 + kα * ↑(A m₀) < g0 + kg) + (hc₁0 : 0 < kα * ↑(A m₀)) + (hd₁kα : kα * ↑ν < kα) + (tset : Set (ℝ ⧸ AddSubgroup.zmultiples 1)) + (htsetdef : tset = QuotientAddGroup.mk '' Icc (kα * ↑(A m₀)) (kα * ↑ν)) : + ∀ v ∈ Ioo g0 (g0 + kg), + ↑v ∈ tset ↔ v ∈ Icc (kα * ↑(A m₀)) (kα * ↑ν) ∨ v ∈ Icc (1 + kα * ↑(A m₀)) (1 + kα * ↑ν) := by + classical + intro v hv + rw [htsetdef] + constructor + · rintro ⟨z, hz, hze⟩ + have h1 : -1 < v - z := by linarith [hz.2, hv.1] + have h2 : v - z < 2 := by linarith [hz.1, hv.2] + rcases addCircle_eq_or_eq_add_one hze.symm h1 h2 with heq | heq + · left; rw [heq]; exact hz + · right; rw [heq]; exact ⟨by linarith [hz.1], by linarith [hz.2]⟩ + · rintro (hv1 | hv2) + · exact mem_image_of_mem _ hv1 + · refine ⟨v - 1, ⟨by linarith [hv2.1], by linarith [hv2.2]⟩, ?_⟩ + rw [← addCircle_coe_add_one (v - 1), sub_add_cancel] + +private theorem circle_coordinate_claim2 + {M : Type*} + [TopologicalSpace M] + (A B : OpenPartialHomeomorph M NNReal) + (ν : NNReal) + (m₁ : M) + (μ : NNReal) + (m₀ : M) + (hm₀μ : B m₀ = μ) + (kα kg g0 : ℝ) + (hkg0 : 0 < kg) + (hkg1 : kg < 1) + (hid1 : kg * ↑(B m₁) + g0 = kα * ↑ν) + (hid2 : kg * ↑μ + g0 = 1 + kα * ↑(A m₀)) + (hg0d : g0 < kα * ↑ν) + (h1c : 1 + kα * ↑(A m₀) < g0 + kg) + (hb'bounds : ∀ x ∈ B.source, g0 < kg * ↑(B x) + g0 ∧ kg * ↑(B x) + g0 < g0 + kg) : + ∀ y ∈ B.source, + kg * ↑(B y) + g0 ∈ Icc (kα * ↑(A m₀)) (kα * ↑ν) ∨ kg * ↑(B y) + g0 ∈ Icc (1 + kα * ↑(A m₀)) + (1 + kα * ↑ν) ↔ + B y ≤ B m₁ ∨ B m₀ ≤ B y := by + classical + intro y hy + obtain ⟨hb0, hb1⟩ := hb'bounds y hy + constructor + · rintro (hc | hc) + · left + have h' : kg * ((B y : NNReal):ℝ) + g0 ≤ kg * ((B m₁ : NNReal):ℝ) + g0 := by + rw [hid1]; exact hc.2 + have hby : ((B y : NNReal):ℝ) ≤ ((B m₁ : NNReal):ℝ) := + le_of_mul_le_mul_left (by linarith) hkg0 + exact_mod_cast hby + · right + have h' : kg * ((μ : NNReal):ℝ) + g0 ≤ kg * ((B y : NNReal):ℝ) + g0 := by + rw [hid2]; exact hc.1 + have hby : ((μ : NNReal):ℝ) ≤ ((B y : NNReal):ℝ) := + le_of_mul_le_mul_left (by linarith) hkg0 + rw [hm₀μ] + exact_mod_cast hby + · rintro (hc | hc) + · left + have hby : ((B y : NNReal):ℝ) ≤ ((B m₁ : NNReal):ℝ) := by exact_mod_cast hc + constructor + · linarith + · rw [← hid1] + have := mul_le_mul_of_nonneg_left hby hkg0.le + linarith + · right + rw [hm₀μ] at hc + have hby : ((μ : NNReal):ℝ) ≤ ((B y : NNReal):ℝ) := by exact_mod_cast hc + constructor + · rw [← hid2] + have := mul_le_mul_of_nonneg_left hby hkg0.le + linarith + · linarith + +/-- Assemble the circle chart from the normalized data: embed each chart as an arc of +`AddCircle 1` and glue along a closed sub-arc via `piecewise`. -/ +private lemma circle_chart_of_normalized {M : Type*} [TopologicalSpace M] [T2Space M] + (A B : OpenPartialHomeomorph M NNReal) (W₀ W₁ : Set M) (r p s q : NNReal) + (hAt : A.target = Ioo 0 1) (hBt : B.target = Ioo 0 1) + (hunion : A.source ∩ B.source = W₀ ∪ W₁) + (himgA₀ : A '' W₀ = Ioo 0 r) (himgA₁ : A '' W₁ = Ioo p 1) + (himgB₀ : B '' W₀ = Ioo q 1) (himgB₁ : B '' W₁ = Ioo 0 s) + (hr0 : 0 < r) (hrp : r ≤ p) (hp1 : p < 1) (hr4 : r < 1 / 4) + (hs0 : 0 < s) (hsq : s ≤ q) (hq1 : q < 1) (hs4 : s < 1 / 4) : + ∃ f : OpenPartialHomeomorph M (AddCircle (1 : ℝ)), + f.source = A.source ∪ B.source ∧ f.target = univ := by + classical + have hfact : Fact ((0:ℝ) < 1) := ⟨one_pos⟩ + -- Subset bookkeeping. + have hW₀S : W₀ ⊆ A.source ∩ B.source := by rw [hunion]; exact subset_union_left + have hW₁S : W₁ ⊆ A.source ∩ B.source := by rw [hunion]; exact subset_union_right + have hW₀A : W₀ ⊆ A.source := fun z hz => (hW₀S hz).1 + have hW₀B : W₀ ⊆ B.source := fun z hz => (hW₀S hz).2 + have hW₁A : W₁ ⊆ A.source := fun z hz => (hW₁S hz).1 + have hW₁B : W₁ ⊆ B.source := fun z hz => (hW₁S hz).2 + -- Numeric NNReal facts. + have hp0 : (0:NNReal) < p := lt_of_lt_of_le hr0 hrp + have hq0 : (0:NNReal) < q := lt_of_lt_of_le hs0 hsq + have hr1 : r < 1 := hr4.trans (by norm_num) + have hs1 : s < 1 := hs4.trans (by norm_num) + -- Image of the full overlap. + have himgS : A '' (A.source ∩ B.source) = Ioo 0 r ∪ Ioo p 1 := by + rw [hunion, image_union, himgA₀, himgA₁] + -- Monotone correspondences on both components. + have mono₀ : ∀ x ∈ W₀, ∀ y ∈ W₀, A x < A y → B x < B y := by + apply overlap_mono_on A B hW₀S himgA₀ himgB₀ + · rw [hAt]; exact ⟨hr0, hr1⟩ + · rw [hBt]; exact ⟨hq0, hq1⟩ + · rw [himgS] + rintro (⟨-, h2⟩ | ⟨h1, -⟩) + · exact absurd h2 (lt_irrefl r) + · exact absurd h1 (not_lt.2 hrp) + have mono₁ : ∀ x ∈ W₁, ∀ y ∈ W₁, A x < A y → B x < B y := by + apply overlap_mono_on' A B hW₁S himgA₁ himgB₁ + · rw [hAt]; exact ⟨hp0, hp1⟩ + · rw [hBt]; exact ⟨hs0, hs1⟩ + · rw [himgS] + rintro (⟨-, h2⟩ | ⟨h1, -⟩) + · exact absurd h2 (not_lt.2 hrp) + · exact absurd h1 (lt_irrefl p) + -- Split points. + have h34lt1 : (3 / 4 : NNReal) < 1 := by norm_num + obtain ⟨ν, hν1', hν2⟩ := exists_between (max_lt hp1 h34lt1) + have hpν : p < ν := lt_of_le_of_lt (le_max_left _ _) hν1' + have h34ν : (3 / 4:NNReal) < ν := lt_of_le_of_lt (le_max_right _ _) hν1' + obtain ⟨m₁, hm₁W, hm₁ν⟩ : ∃ m₁ ∈ W₁, A m₁ = ν := by + have hmem : ν ∈ A '' W₁ := by rw [himgA₁]; exact ⟨hpν, hν2⟩ + obtain ⟨m₁, hm₁, he⟩ := hmem + exact ⟨m₁, hm₁, he⟩ + obtain ⟨μ, hμ1', hμ2⟩ := exists_between (max_lt hq1 h34lt1) + have hqμ : q < μ := lt_of_le_of_lt (le_max_left _ _) hμ1' + have h34μ : (3 / 4:NNReal) < μ := lt_of_le_of_lt (le_max_right _ _) hμ1' + obtain ⟨m₀, hm₀W, hm₀μ⟩ : ∃ m₀ ∈ W₀, B m₀ = μ := by + have hmem : μ ∈ B '' W₀ := by rw [himgB₀]; exact ⟨hqμ, hμ2⟩ + obtain ⟨m₀, hm₀, he⟩ := hmem + exact ⟨m₀, hm₀, he⟩ + have hm₀A : m₀ ∈ A.source := hW₀A hm₀W + have hm₀B : m₀ ∈ B.source := hW₀B hm₀W + have hm₁A : m₁ ∈ A.source := hW₁A hm₁W + have hm₁B : m₁ ∈ B.source := hW₁B hm₁W + have hρmem : A m₀ ∈ Ioo 0 r := by rw [← himgA₀]; exact mem_image_of_mem _ hm₀W + have hσmem : B m₁ ∈ Ioo 0 s := by rw [← himgB₁]; exact mem_image_of_mem _ hm₁W + -- Key monotone-correspondence facts. + have K₀ := mono_key_le A B hW₀A mono₀ hm₀W + have K₁ := mono_key_ge A B hW₁A mono₁ hm₁W + -- Real versions of the split data. + have hρ'0 : (0:ℝ) < ((A m₀ : NNReal) : ℝ) := by exact_mod_cast hρmem.1 + have hρ'4 : ((A m₀ : NNReal) : ℝ) < 1 / 4 := by exact_mod_cast hρmem.2.trans hr4 + have hν'3 : (3 / 4:ℝ) < ((ν : NNReal) : ℝ) := by exact_mod_cast h34ν + have hν'1 : ((ν : NNReal) : ℝ) < 1 := by exact_mod_cast hν2 + have hσ'0 : (0:ℝ) < ((B m₁ : NNReal) : ℝ) := by exact_mod_cast hσmem.1 + have hσ'4 : ((B m₁ : NNReal) : ℝ) < 1 / 4 := by exact_mod_cast hσmem.2.trans hs4 + have hμ'3 : (3 / 4:ℝ) < ((μ : NNReal) : ℝ) := by exact_mod_cast h34μ + have hμ'1 : ((μ : NNReal) : ℝ) < 1 := by exact_mod_cast hμ2 + obtain ⟨kα, kg, g0, hkα0, hkα1, hkg0, hkg1, hid1, hid2, hcg0, hg0d, h1c, hg1d⟩ := + circle_params hρ'0 hρ'4 hν'3 hν'1 hσ'0 hσ'4 hμ'3 hμ'1 + -- Derived real facts. + have hc₁0 : (0:ℝ) < kα * ((A m₀ : NNReal) : ℝ) := mul_pos hkα0 hρ'0 + have hd₁kα : kα * ((ν : NNReal) : ℝ) < kα := by + have hh : kα * ((ν : NNReal) : ℝ) < kα * 1 := mul_lt_mul_of_pos_left hν'1 hkα0 + rwa [mul_one] at hh + have hd₁1 : kα * ((ν : NNReal) : ℝ) < 1 := hd₁kα.trans hkα1 + have hc₁1 : kα * ((A m₀ : NNReal) : ℝ) < 1 := by linarith + have hg00 : (0:ℝ) < g0 := lt_trans hc₁0 hcg0 + have hc₁d₁ : kα * ((A m₀ : NNReal) : ℝ) ≤ kα * ((ν : NNReal) : ℝ) := by linarith + -- The circle charts. + obtain ⟨eA, heAs, heAt, heAf⟩ := affineNNRealOPH kα 0 hkα0 + obtain ⟨eB, heBs, heBt, heBf⟩ := affineNNRealOPH kg g0 hkg0 + have heAf' : ∀ x : NNReal, eA x = kα * (x:ℝ) + 0 := heAf + have heBf' : ∀ x : NNReal, eB x = kg * (x:ℝ) + g0 := heBf + set CA := AddCircle.openPartialHomeomorphCoe (1 : ℝ) (0:ℝ) with hCAdef + set CB := AddCircle.openPartialHomeomorphCoe (1 : ℝ) (g0 + kg - 1) with hCBdef + have hCAs : CA.source = Ioo (0:ℝ) (0+1) := rfl + have hCBs : CB.source = Ioo (g0 + kg - 1) (g0 + kg - 1 + 1) := rfl + set e := (A.trans eA).trans CA with hedef + set e' := (B.trans eB).trans CB with he'def + -- Sources and pointwise formulas. + have hAeAs : (A.trans eA).source = A.source := by + rw [OpenPartialHomeomorph.trans_source, heAs] + refine inter_eq_left.mpr fun x hx => ?_ + rw [mem_preimage, ← hAt] + exact A.map_source hx + have hBeBs : (B.trans eB).source = B.source := by + rw [OpenPartialHomeomorph.trans_source, heBs] + refine inter_eq_left.mpr fun x hx => ?_ + rw [mem_preimage, ← hBt] + exact B.map_source hx + have hAeAval : ∀ x, (A.trans eA) x = kα * ((A x : NNReal):ℝ) + 0 := fun x => by + rw [OpenPartialHomeomorph.trans_apply, heAf'] + have hBeBval : ∀ x, (B.trans eB) x = kg * ((B x : NNReal):ℝ) + g0 := fun x => by + rw [OpenPartialHomeomorph.trans_apply, heBf'] + have heval : ∀ x, e x = ((kα * ((A x : NNReal) : ℝ) : ℝ) : AddCircle (1 : ℝ)) := by + intro x + rw [hedef, OpenPartialHomeomorph.trans_apply, hAeAval, add_zero] + rfl + have he'val : ∀ x, e' x = ((kg * ((B x : NNReal) : ℝ) + g0 : ℝ) : AddCircle (1 : ℝ)) := by + intro x + rw [he'def, OpenPartialHomeomorph.trans_apply, hBeBval] + rfl + have hbounds : ∀ x ∈ A.source, + 0 < kα * ((A x : NNReal):ℝ) ∧ kα * ((A x : NNReal):ℝ) < kα := by + intro x hx + have hAx : A x ∈ Ioo (0:NNReal) 1 := hAt ▸ A.map_source hx + have h0 : (0:ℝ) < ((A x : NNReal):ℝ) := by exact_mod_cast hAx.1 + have h1 : ((A x : NNReal):ℝ) < 1 := by exact_mod_cast hAx.2 + have hh : kα * ((A x : NNReal):ℝ) < kα * 1 := mul_lt_mul_of_pos_left h1 hkα0 + rw [mul_one] at hh + exact ⟨mul_pos hkα0 h0, hh⟩ + have hb'bounds : ∀ x ∈ B.source, + g0 < kg * ((B x : NNReal):ℝ) + g0 ∧ kg * ((B x : NNReal):ℝ) + g0 < g0 + kg := by + intro x hx + have hBx : B x ∈ Ioo (0:NNReal) 1 := hBt ▸ B.map_source hx + have h0 : (0:ℝ) < ((B x : NNReal):ℝ) := by exact_mod_cast hBx.1 + have h1 : ((B x : NNReal):ℝ) < 1 := by exact_mod_cast hBx.2 + have hh : kg * ((B x : NNReal):ℝ) < kg * 1 := mul_lt_mul_of_pos_left h1 hkg0 + rw [mul_one] at hh + exact ⟨by linarith [mul_pos hkg0 h0], by linarith⟩ + have hes : e.source = A.source := by + rw [hedef, OpenPartialHomeomorph.trans_source, hAeAs] + refine inter_eq_left.mpr fun x hx => ?_ + rw [mem_preimage, hAeAval, hCAs] + obtain ⟨hb0, hb1⟩ := hbounds x hx + exact ⟨by linarith, by linarith⟩ + have he's : e'.source = B.source := by + rw [he'def, OpenPartialHomeomorph.trans_source, hBeBs] + refine inter_eq_left.mpr fun x hx => ?_ + rw [mem_preimage, hBeBval, hCBs] + obtain ⟨hb0, hb1⟩ := hb'bounds x hx + exact ⟨by linarith, by linarith⟩ + -- Targets. + have htargetE : e.target = ((↑) : ℝ → AddCircle (1 : ℝ)) '' (Ioo 0 kα) := by + have hgen := affine_circle_target A hAt e hes hkα0 + (fun x _ => by rw [heval x, add_zero]) + rw [hgen, zero_add] + have htargetE' : e'.target = ((↑) : ℝ → AddCircle (1 : ℝ)) '' (Ioo g0 (g0 + kg)) := + affine_circle_target B hBt e' he's hkg0 (fun x _ => he'val x) + -- The gluing sets. + set sset := A.source ∩ A ⁻¹' (Icc (A m₀) ν) with hssetdef + set tset := ((↑) : ℝ → AddCircle (1 : ℝ)) '' + (Icc (kα * ((A m₀ : NNReal):ℝ)) (kα * ((ν : NNReal):ℝ))) with htsetdef + have hssub : sset ⊆ A.source := by rw [hssetdef]; exact inter_subset_left + -- `e` maps `sset` to `tset`. + have H : e.IsImage sset tset := + circle_coordinate_H A ν m₀ kα hkα0 hkα1 hc₁0 hd₁kα e heval hbounds hes sset hssetdef tset + htsetdef + -- Windows: membership of a point of the `e'`-arc in `tset`. + have claim1 : ∀ v : ℝ, v ∈ Ioo g0 (g0 + kg) → + (((v : ℝ) : AddCircle (1 : ℝ)) ∈ tset ↔ + v ∈ Icc (kα * ((A m₀ : NNReal):ℝ)) (kα * ((ν : NNReal):ℝ)) ∨ + v ∈ Icc (1 + kα * ((A m₀ : NNReal):ℝ)) (1 + kα * ((ν : NNReal):ℝ))) := + circle_coordinate_claim1 A ν m₀ kα kg g0 hkα1 hkg1 hg0d h1c hc₁0 hd₁kα tset htsetdef + -- Translation of the window membership into `B`-coordinate inequalities. + have claim2 : ∀ y ∈ B.source, + ((kg * ((B y : NNReal):ℝ) + g0 ∈ + Icc (kα * ((A m₀ : NNReal):ℝ)) (kα * ((ν : NNReal):ℝ)) ∨ + kg * ((B y : NNReal):ℝ) + g0 ∈ + Icc (1 + kα * ((A m₀ : NNReal):ℝ)) (1 + kα * ((ν : NNReal):ℝ))) + ↔ (B y ≤ B m₁ ∨ B m₀ ≤ B y)) := + circle_coordinate_claim2 A B ν m₁ μ m₀ hm₀μ kα kg g0 hkg0 hkg1 hid1 hid2 hg0d h1c hb'bounds + -- The `B`-coordinate inequalities describe exactly `sset`. + have claim3 : ∀ y ∈ B.source, ((B y ≤ B m₁ ∨ B m₀ ≤ B y) ↔ y ∈ sset) := + circle_overlap_membership A B W₀ W₁ r p s q hBt hunion himgA₀ himgA₁ himgB₀ himgB₁ + hrp hsq hW₀A hW₀B hW₁A hW₁B ν hpν m₁ hm₁ν μ hqμ m₀ hm₀μ hρmem hσmem K₀ K₁ sset hssetdef + -- `e'` also maps `sset` to `tset`. + have H' : e'.IsImage sset tset := by + intro y hy + rw [he's] at hy + rw [he'val y] + obtain ⟨hb0, hb1⟩ := hb'bounds y hy + rw [claim1 _ ⟨hb0, hb1⟩, claim2 y hy] + exact claim3 y hy + -- The frontier of the closed arc consists of at most the two endpoint classes. + have hfrontier : frontier tset ⊆ + {((kα * ((A m₀ : NNReal):ℝ) : ℝ) : AddCircle (1 : ℝ)), + ((kα * ((ν : NNReal):ℝ) : ℝ) : AddCircle (1 : ℝ))} := by + rw [htsetdef] + exact addCircle_frontier_arc_subset hc₁d₁ + exact glue_circle_arcs A B W₀ W₁ r p hAt himgA₀ himgA₁ hW₀A hW₀B hW₁A hW₁B ν hpν m₁ hm₁ν μ m₀ + hm₀μ hm₀A hm₀B hm₁A hm₁B hρmem kα kg g0 hkα1 hkg1 hid1 hid2 hcg0 hg0d h1c hc₁0 hd₁kα hc₁1 + hc₁d₁ e e' heval he'val hes he's htargetE htargetE' sset tset hssub H claim1 H' hfrontier + hssetdef htsetdef + +/-- **The circle chart.** Two O-charts with `Overlap` and a disconnected overlap glue to +a chart of `M` onto the whole of `AddCircle 1`. -/ +theorem exists_circle_chart {M : Type*} [TopologicalSpace M] [T2Space M] + (a b : OChart M) (h : Overlap a.source b.source) + (hdisc : ¬ IsConnected (a.source ∩ b.source)) : + ∃ f : OpenPartialHomeomorph M (AddCircle (1 : ℝ)), + f.source = a.source ∪ b.source ∧ f.target = univ := by + obtain ⟨A, B, W₀, W₁, r, p, s, q, hAs, hBs, hAt, hBt, hunion, + himgA₀, himgA₁, himgB₀, himgB₁, hr0, hrp, hp1, hr4, hs0, hsq, hq1, hs4⟩ := + exists_normalized_charts a b h hdisc + obtain ⟨f, hfs, hft⟩ := circle_chart_of_normalized A B W₀ W₁ r p s q hAt hBt hunion + himgA₀ himgA₁ himgB₀ himgB₁ hr0 hrp hp1 hr4 hs0 hsq hq1 hs4 + exact ⟨f, by rw [hfs, hAs, hBs], hft⟩ + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/Classification.lean b/LeanPool/OneManifold/OneMfld/Classification.lean new file mode 100644 index 000000000..190bc339b --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/Classification.lean @@ -0,0 +1,409 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Algebra.Order.Ring.Star +public import Mathlib.Data.Int.Star +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.UnitInterval +public import LeanPool.OneManifold.OneMfld.FiniteIntervalCharts +public import LeanPool.OneManifold.OneMfld.IntervalCharts +public import LeanPool.OneManifold.OneMfld.NiceCharts +public import LeanPool.OneManifold.OneMfld.ClassifyOverlaps +public import LeanPool.OneManifold.OneMfld.Compactness + + +/-! +# Classification + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +variable + {M : Type*} + [TopologicalSpace M] + [ConnectedSpace M] + +/-- Remove a chart whose source is contained in another chart, reducing the atlas size. -/ +noncomputable def subsumeCharts (ht : FinitelyIntervalChartedSpace M) + {a : OpenPartialHomeomorph M NNReal} (ha : a ∈ ht.atlas) + {b : OpenPartialHomeomorph M NNReal} (hb : b ∈ ht.atlas) + (hs : a.source ⊆ b.source) (hab : a ≠ b) : + { ht' : FinitelyIntervalChartedSpace M | Nat.card ht'.atlas = Nat.card ht.atlas - 1 } := by + let ht' : FinitelyIntervalChartedSpace M := + { chartAt := by + intro x + by_cases h : a = ht.chartAt x + · exact b + · exact ht.chartAt x + , atlas := ht.atlas \ { a } + , chart_mem_atlas := by + intro x + split_ifs with h + · apply Set.mem_sdiff_singleton.mpr + apply And.intro + · exact hb + · exact id (Ne.symm hab) + · apply Set.mem_sdiff_singleton.mpr + apply And.intro + · exact ChartedSpace.chart_mem_atlas x + · exact fun a_1 => h (id (Eq.symm a_1)) + , mem_chart_source := by + intro x + split_ifs with h + · rw [h] at hs + apply hs + exact ChartedSpace.mem_chart_source x + · exact ChartedSpace.mem_chart_source x + , is_finite := Set.Finite.subset ht.is_finite Set.sdiff_subset + , is_interval := by + intro φ hφ + apply ht.is_interval + exact Set.mem_of_mem_sdiff hφ + } + use ht' + exact Set.ncard_sdiff_singleton_of_mem ha + + +/-- Replace two overlapping charts by a chart on their union, reducing the atlas size. -/ +noncomputable def replaceCharts (ht : FinitelyIntervalChartedSpace M) + {a : OpenPartialHomeomorph M NNReal} (ha : a ∈ ht.atlas) + {b : OpenPartialHomeomorph M NNReal} (hb : b ∈ ht.atlas) + (ho : Overlap a.source b.source) + (f : IChart M) (hf : f.source = a.source ∪ b.source) : + { ht' : FinitelyIntervalChartedSpace M | Nat.card ht'.atlas ≤ Nat.card ht.atlas - 1 } := by + let hab : a ≠ b := by + by_contra hab + rw [hab] at ho + let ho1 := ho.2.1 + simp only [sdiff_self, Set.bot_eq_empty, Set.not_nonempty_empty] at ho1 + let ht' : FinitelyIntervalChartedSpace M := + { chartAt := by + intro x + by_cases h : a = ht.chartAt x + · exact f.toOpenPartialHomeomorph + · by_cases h : b = ht.chartAt x + · exact f.toOpenPartialHomeomorph + · exact ht.chartAt x + , atlas := (ht.atlas \ { a, b }) ∪ ({ f.toOpenPartialHomeomorph } : Set _) + , chart_mem_atlas := by + intro x + split_ifs with h h' + · simp only [Set.union_singleton, Set.mem_insert_iff, Set.mem_sdiff, Set.mem_singleton_iff, + not_or, true_or] + · simp only [Set.union_singleton, Set.mem_insert_iff, Set.mem_sdiff, Set.mem_singleton_iff, + not_or, true_or] + · simp only [Set.union_singleton, Set.mem_insert_iff, Set.mem_sdiff, chart_mem_atlas, + Set.mem_singleton_iff, not_or, true_and] + right + exact ⟨fun a_1 => h (id (Eq.symm a_1)), fun a => h' (id (Eq.symm a))⟩ + , mem_chart_source := by + intro x + split_ifs with h h' + · rw [hf] + simp only [Set.mem_union] + left + rw [h] + exact ChartedSpace.mem_chart_source x + · rw [hf] + simp only [Set.mem_union] + right + rw [h'] + exact ChartedSpace.mem_chart_source x + · exact ChartedSpace.mem_chart_source x + , is_finite := by + have : Finite ht.atlas := ht.is_finite + simp only [Set.union_singleton] + exact Finite.Set.finite_insert f.toOpenPartialHomeomorph (ChartedSpace.atlas \ {a, b}) + , is_interval := by + intro x hx + simp only [Set.union_singleton, Set.mem_insert_iff, Set.mem_sdiff, Set.mem_singleton_iff, + not_or] at hx + rcases hx with (hx|hx) + · rw [hx] + apply f.is_interval + · apply ht.is_interval + exact hx.1 + } + use ht' + have pair : ( { a, b } : Set (OpenPartialHomeomorph M NNReal) ).ncard = 2 := Set.ncard_pair hab + have equal : ht.atlas \ {a, b} ∪ {a, b} = ht.atlas := Set.sdiff_union_of_subset + (Set.pair_subset ha hb) + have union : ((ht.atlas \ { a, b } : Set _) ∪ {a, b} : Set _).ncard = (ht.atlas \ { a, b } : + Set _).ncard + ( { a, b } : Set _ ).ncard := by + apply Set.ncard_union_eq + · exact Set.disjoint_sdiff_left + · apply Set.Finite.sdiff + exact ht.is_finite + exact Set.toFinite {a, b} + rw [pair] at union + rw [equal] at union + dsimp [ht'] + have : (ht.atlas \ { a, b } : Set _).ncard = (ht.atlas).ncard - 2 := by + rw [union] + exact rfl + have t : (ht.atlas \ {a, b} ∪ {f.toOpenPartialHomeomorph}).ncard ≤ (ht.atlas \ { a, b } : + Set _).ncard + ({f.toOpenPartialHomeomorph} : Set _).ncard := by + apply Set.ncard_union_le + rw [this] at t + simp only [Set.ncard_singleton] at t + have t' : ht.atlas.ncard - 2 + 1 ≤ ht.atlas.ncard - 1 := by + have nonzero : ht.atlas.ncard ≥ 2 := by + rw [←pair] + apply Set.ncard_le_ncard + · exact Set.pair_subset ha hb + exact ht.is_finite + ring_nf + apply Nat.one_add_le_iff.mpr + exact Nat.sub_succ_lt_self ht.atlas.ncard 1 nonzero + exact Nat.le_trans t t' + +lemma nonempty_atlas (ht : FinitelyIntervalChartedSpace M) : ht.atlas.Nonempty := by + by_contra nonempty + have he : Nonempty M := ConnectedSpace.toNonempty + have x := he.some + let chart := ht.chartAt x + have hc : chart ∈ ht.atlas := ht.chart_mem_atlas x + exfalso + exact nonempty (Set.nonempty_of_mem hc) + +lemma more_than_one_chart [CompactSpace M] (ht : FinitelyIntervalChartedSpace M) : Nat.card + ht.atlas > 1 := by + have z := (ConnectedSpace.toNonempty : Nonempty M).some + let φ := ht.chartAt z + have nonempty := nonempty_atlas ht + have hzero : Nat.card ht.atlas > 0 := by + refine (Set.natCard_pos ?_).mpr nonempty + exact ht.is_finite + by_contra htwo + have hone : Nat.card ht.atlas = 1 := by linarith + have he : ∃ (a : OpenPartialHomeomorph M NNReal), ht.atlas = { a } := Set.ncard_eq_one.mp hone + rcases he with ⟨a,ha⟩ + have : φ ∈ ht.atlas := ChartedSpace.chart_mem_atlas z + rw [ha] at this + have hφa : φ = a := by exact this + have h : a.source = Set.univ := by + ext x + apply Iff.intro + · exact fun _ => trivial + · intro hx + let c := ht.chartAt x + have : c ∈ ht.atlas := ht.chart_mem_atlas x + have : c = a := by + rw [ha] at this + exact this + rw [←this] + exact ht.mem_chart_source x + -- a.source is homeo to a.target which is not compact + have notcompactφ : ¬ CompactSpace (φ.target) := by + apply noncompact_target + · exact ChartedSpace.chart_mem_atlas z + exact ChartedSpace.mem_chart_source z + have notcompact : ¬ CompactSpace (a.target) := by + rwa [←hφa] + exfalso + apply notcompact + refine isCompact_iff_compactSpace.mp ?_ + have : a.target = a.toFun '' a.source := Eq.symm (PartialEquiv.image_source_eq_target + a.toPartialEquiv) + rw [this] + apply IsCompact.image_of_continuousOn + · rw [h] + exact CompactSpace.isCompact_univ + · exact a.continuousOn_toFun + +lemma find_overlap [CompactSpace M] (ht : FinitelyIntervalChartedSpace M) {a : + OpenPartialHomeomorph M NNReal} + (ha : a ∈ ht.atlas) {x : M} (hx : x ∈ a.source) + (contains : ∀ c ∈ ChartedSpace.atlas \ {a}, ¬ a.source ⊆ c.source) : + ∃ (b : OpenPartialHomeomorph M NNReal), (b ∈ ht.atlas \ {a}) ∧ (Overlap a.source b.source) := by + by_contra h + push Not at h + have ho : IsOpen a.source := a.open_source + have hc : IsClosed a.source := by + have comp : a.sourceᶜ = ⋃ (b ∈ { c ∈ ChartedSpace.atlas \ {a} | ¬ (c.source ⊆ a.source) + }), b.source := by + ext z + apply Iff.intro + · intro hz + simp only [Set.mem_sdiff, Set.mem_singleton_iff, Set.mem_ofPred_eq, Set.mem_iUnion, + exists_prop] + use ChartedSpace.chartAt z + simp only [chart_mem_atlas, true_and, mem_chart_source, and_true] + by_contra hza + push Not at hza + apply hz + apply hza + · by_contra hc + rw [←hc] at hz + simp only [Set.mem_compl_iff, mem_chart_source, not_true_eq_false] at hz + exact ChartedSpace.mem_chart_source z + · intro hz + simp only [Set.mem_compl_iff] + by_contra hza + simp only [Set.mem_sdiff, Set.mem_singleton_iff, Set.mem_iUnion, exists_prop] at hz + rcases hz with ⟨ i, ⟨ ⟨ hia, hia' ⟩, his ⟩, hzi ⟩ + specialize h i + have hic : i ∈ ChartedSpace.atlas \ {a} := Set.mem_sdiff_of_mem hia hia' + specialize h hic + apply h + apply does_overlap + · use z + simp only [Set.mem_inter_iff] + tauto + · exact contains i hic + · exact his + refine { isOpen_compl := ?_ } + rw [comp] + apply isOpen_biUnion + exact fun i a => i.open_source + have hco : IsClopen a.source := ⟨hc, ho⟩ + have he : IsPreconnected (Set.univ : Set M) := isPreconnected_univ + have he' := he.subset_isClopen hco + have hn : (Set.univ ∩ a.source).Nonempty := by + apply Set.inter_nonempty.mpr + use x + exact (Set.mem_ite_empty_right (x ∈ Set.univ) a.source x).mp hx + have h' := he' hn + have hs : a.source ⊆ Set.univ := by exact fun ⦃a_1⦄ a => trivial + have he : a.source = Set.univ := Set.eq_univ_of_univ_subset (he' hn) + have hcpct : ¬ CompactSpace a.target := by + apply noncompact_target + · exact ha + exact hx + apply hcpct + refine isCompact_iff_compactSpace.mp ?_ + have : a.target = a.toFun '' a.source := Eq.symm (PartialEquiv.image_source_eq_target + a.toPartialEquiv) + rw [this] + rw [he] + have hcpctM : IsCompact (Set.univ : Set M) := CompactSpace.isCompact_univ + refine IsCompact.image_of_continuousOn hcpctM ?_ + rw [←he] + exact a.continuousOn_toFun + +/-- Classify a compact connected Hausdorff one-manifold using induction on a finite interval +atlas. -/ +noncomputable def classification' [T2Space M] [CompactSpace M] (ht : + FinitelyIntervalChartedSpace M) : (Homeomorph M Circle) ⊕ (Homeomorph M UnitInterval) := by + -- instead, use the fact that M is nonempty and then use chartat + have x := (ConnectedSpace.toNonempty : Nonempty M).some + let φ := ht.chartAt x + have nonempty := nonempty_atlas ht + --let φ := nonempty.some + by_cases nonempty' : (ht.atlas \ {φ}).Nonempty + · by_cases contains : ∃ (ψ : OpenPartialHomeomorph M NNReal), (ψ ∈ ht.atlas \ {φ}) ∧ + (φ.source ⊆ ψ.source) + · let ψ := contains.choose + let hψ := contains.choose_spec + have hψ' : ψ ∈ ht.atlas := by + have : ht.atlas \ { φ } ⊆ ht.atlas := Set.sdiff_subset + apply this + exact hψ.1 + have : φ.source ⊆ ψ.source := hψ.2 + have hd : φ ≠ ψ := by + have : ψ ∈ ChartedSpace.atlas \ {φ} := hψ.1 + by_contra hd + rw [←hd] at this + have this' : φ ∉ ChartedSpace.atlas \ {φ} := by exact Set.notMem_sdiff_of_mem rfl + exact this' this + have ⟨ ht', ht'' ⟩ := subsumeCharts ht (ChartedSpace.chart_mem_atlas x) hψ' this hd + exact classification' ht' + · have : ∃ (ψ : OpenPartialHomeomorph M NNReal), (ψ ∈ ht.atlas \ {φ}) ∧ (Overlap φ.source + ψ.source) := by + simp only [not_exists, not_and] at contains + exact find_overlap ht (ChartedSpace.chart_mem_atlas x) (ChartedSpace.mem_chart_source + x) contains + let ψ := this.choose + let ⟨ hψ, overlap ⟩ := this.choose_spec + have hφ := ht.is_interval φ (ChartedSpace.chart_mem_atlas x) + by_cases hφIoo : ∃ x y, Set.Ioo x y = φ.target + · have hψ'' := ht.is_interval ψ (Set.mem_of_mem_sdiff hψ) + by_cases hψIoo : ∃ x y, Set.Ioo x y = ψ.target + · -- the O O case + let a : OChart M := { φ with target_ioo := hφIoo } + let b : OChart M := { ψ with target_ioo := hψIoo } + let result := handleOO a b overlap + rcases result with (result|result) + · left + exact result + · rcases result with ⟨ f, hf ⟩ + let f := f.toIChart + have ⟨ ht', ht'' ⟩ := replaceCharts ht (ChartedSpace.chart_mem_atlas x) + (Set.mem_of_mem_sdiff hψ) overlap f hf + simp only [Set.mem_ofPred_eq] at ht'' + exact classification' ht' + · have hψIio : ∃ x, Set.Iio x = ψ.target := Or.elim hψ'' (fun x => False.elim (hψIoo + x)) id + -- an O H case + let a : OChart M := { φ with target_ioo := hφIoo } + let b : HChart M := { ψ with target_iio := hψIio } + let result := handleOH a b overlap + rcases result with ⟨ f, hf ⟩ + let f := f.toIChart + have ⟨ ht', ht'' ⟩ := replaceCharts ht (ChartedSpace.chart_mem_atlas x) + (Set.mem_of_mem_sdiff hψ) overlap f hf + simp only [Set.mem_ofPred_eq] at ht'' + exact classification' ht' + · have hφIio : ∃ x, Set.Iio x = φ.target := Or.elim hφ (fun x => False.elim (hφIoo x)) id + have hψ'' := ht.is_interval ψ (Set.mem_of_mem_sdiff hψ) + by_cases hψIoo : ∃ x y, Set.Ioo x y = ψ.target + · -- an H O case + let a : HChart M := { φ with target_iio := hφIio } + let b : OChart M := { ψ with target_ioo := hψIoo } + let result := handleOH b a (overlap_symm overlap) + rcases result with ⟨ f, hf ⟩ + let f := f.toIChart + have ⟨ ht', ht'' ⟩ := replaceCharts ht (Set.mem_of_mem_sdiff hψ) + (ChartedSpace.chart_mem_atlas x) (overlap_symm overlap) f hf + simp only [Set.mem_ofPred_eq] at ht'' + exact classification' ht' + · have hψIio : ∃ x, Set.Iio x = ψ.target := Or.elim hψ'' (fun x => False.elim (hψIoo + x)) id + -- the H H case + right + let a : HChart M := { φ with target_iio := hφIio } + let b : HChart M := { ψ with target_iio := hψIio } + exact handleHH a b overlap + · -- A compact manifold must have more than one chart + exfalso + apply nonempty' + push Not at nonempty' + have : ht.atlas ⊆ {φ} := Set.sdiff_eq_empty.mp nonempty' + have hone : Nat.card ({φ} : Set _) = 1 := by exact Nat.card_unique + have : Nat.card (ht.atlas) ≤ Nat.card ({φ} : Set _) := by + refine Nat.card_mono ?_ this + exact Set.finite_singleton φ + have t' := more_than_one_chart ht + linarith +termination_by Set.ncard ht.atlas +decreasing_by + · simp only [Nat.card_coe_set_eq, Set.mem_ofPred_eq] at ht'' + rw [ht''] + simp only [tsub_lt_self_iff, zero_lt_one, and_true] + exact (Set.natCard_pos ht.is_finite).mpr (nonempty_atlas ht) + · simp only [Nat.card_coe_set_eq, Set.mem_ofPred_eq] at ht'' + refine Nat.lt_of_le_pred ?_ ht'' + exact (Set.natCard_pos ht.is_finite).mpr nonempty + · simp only [Nat.card_coe_set_eq, Set.mem_ofPred_eq] at ht'' + refine Nat.lt_of_le_pred ?_ ht'' + exact (Set.natCard_pos ht.is_finite).mpr nonempty + · simp only [Nat.card_coe_set_eq, Set.mem_ofPred_eq] at ht'' + refine Nat.lt_of_le_pred ?_ ht'' + exact (Set.natCard_pos ht.is_finite).mpr nonempty + +/-- A compact connected Hausdorff manifold charted on the half-line is a circle or an interval. -/ +noncomputable def classification [T2Space M] [CompactSpace M] (ht : ChartedSpace NNReal M) : + (Homeomorph M Circle) ⊕ (Homeomorph M UnitInterval) := + let ht' := nicelyCharted ht + let ht'' := intervalCharted ht' + classification' (finitelyIntervalCharted ht'') + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/ClassifyInterval.lean b/LeanPool/OneManifold/OneMfld/ClassifyInterval.lean new file mode 100644 index 000000000..5719230c6 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/ClassifyInterval.lean @@ -0,0 +1,1333 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Algebra.Order.Star.Real +public import Mathlib.Analysis.InnerProductSpace.Basic +public import Mathlib.Analysis.SpecialFunctions.Log.Basic +public import Mathlib.Order.CompletePartialOrder +public import Mathlib.Tactic + + +/-! +# ClassifyInterval + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +open Function +open Set + +theorem ici_cap_iio_empty (y : Real) : (Ici y ∩ Iio y) = ∅ := by + have h' : Ici y ∩ Iio y = Ico y y := rfl + rw [h'] + simp only [lt_self_iff_false, not_false_eq_true, Ico_eq_empty] + +theorem iic_cap_ioi_empty (y : Real) : (Iic y ∩ Ioi y) = ∅ := by + have h' : Iic y ∩ Ioi y = Ioc y y := Iic_inter_Ioi + rw [h'] + simp only [lt_self_iff_false, not_false_eq_true, Ioc_eq_empty] + +theorem not_ici (U : Set Real) (y : Real) (hu : IsOpen U) : (Ici y ≠ U) := by + by_contra h + rw [←h] at hu + have h''' : IsClosed (Ici y) := by + apply isClosed_Ici + have ho : IsOpen (Ici y)ᶜ + · exact IsClosed.isOpen_compl + have hr : IsPreconnected (univ : Set Real) := isPreconnected_univ + let hr' := hr (Ici y) ((Ici y)ᶜ) hu ho + have huniv : Ici y ∪ (Ici y)ᶜ = univ := union_compl_self (Ici y) + rw [huniv] at hr' + simp only [subset_refl, univ_inter, nonempty_Ici, compl_Ici, nonempty_Iio, forall_const] at hr' + rw [ici_cap_iio_empty] at hr' + apply Set.not_nonempty_empty + exact hr' + +theorem not_iic (U : Set Real) (y : Real) (hu : IsOpen U) : (Iic y ≠ U) := by + by_contra h + rw [←h] at hu + have h''' : IsClosed (Iic y) := by + apply isClosed_Iic + have ho : IsOpen (Iic y)ᶜ + · exact IsClosed.isOpen_compl + have hr : IsPreconnected (univ : Set Real) := isPreconnected_univ + let hr' := hr (Iic y) ((Iic y)ᶜ) hu ho + have huniv : Iic y ∪ (Iic y)ᶜ = univ := union_compl_self (Iic y) + rw [huniv] at hr' + simp only [subset_refl, univ_inter, nonempty_Iic, compl_Iic, nonempty_Ioi, forall_const] at hr' + rw [iic_cap_ioi_empty] at hr' + apply Set.not_nonempty_empty + exact hr' + +lemma ioc_not_open (x y : Real) (hxy : x < y) : ¬ IsOpen (Ioc x y) := by + have hu : (Ioc x y) ∪ (Iio y) = (Iic y) + · ext p + simp only [mem_union, mem_Ioc, mem_Iio, mem_Iic] + apply Iff.intro + · intro h + rcases h with (h1|h2) + · tauto + · exact le_of_lt h2 + by_cases hp : p = y + · intro h + rw [hp] + simp only [le_refl, and_true, lt_self_iff_false, or_false] + exact hxy + intro h + right + exact lt_of_le_of_ne h hp + by_contra hopen + have hopen' : IsOpen (Iio y) := isOpen_Iio + have hopen'' : IsOpen (Iic y) + · rw [←hu] + exact IsOpen.union hopen hopen' + have hc : IsOpen ((Iic y)ᶜ) + · simp only [compl_Iic] + exact isOpen_Ioi + have hcon := isPreconnected_univ (Iic y) ((Iic y)ᶜ) hopen'' hc + (by simp only [compl_Iic, Iic_union_Ioi, subset_refl]) + (by simp only [univ_inter, nonempty_Iic]) + (by simp only [compl_Iic, univ_inter, nonempty_Ioi]) + simp only [compl_Iic, univ_inter] at hcon + rcases hcon with ⟨x,hx⟩ + simp at hx + linarith + +lemma ico_not_open (x y : Real) (hxy : x < y) : ¬ IsOpen (Ico x y) := by + have hxy' : -y < -x := neg_lt_neg_iff.mpr hxy + have hn := ioc_not_open (-y) (-x) hxy' + let f : Real → Real := fun x => - x + let fb : Bijective f := ⟨ neg_injective, neg_surjective ⟩ + let fe := Equiv.ofBijective f fb + have fc : Continuous fe := continuous_neg + have fo : IsOpenMap fe := isOpenMap_neg ℝ + have fh : Homeomorph Real Real + · apply Equiv.toHomeomorphOfContinuousOpen + · exact fc + exact fo + by_contra h + have p := fo (Ico x y) h + have he : fe '' (Ico x y) = Ioc (-y) (-x) + · ext t + have ht : ∀ s : Real, fe.symm (-s) = s := fun s => Equiv.ofBijective_symm_apply_apply f fb s + have ht' := ht (-t) + simp only [neg_neg] at ht' + simp only [mem_image_equiv, mem_Ico, mem_Ioc] + apply Iff.intro + · simp only [and_imp] + intro h1 h2 + constructor + · rw [ht'] at h1 + rw [ht'] at h2 + linarith + rw [ht'] at h1 + rw [ht'] at h2 + linarith + simp only [and_imp] + intro h1 h2 + rw [ht'] + constructor + · linarith + linarith + · rw [he] at p + exact hn p + +lemma icc_not_open (x y : Real) (hxy : x < y) : ¬ IsOpen (Icc x y) := by + by_contra h + have hc : IsClosed (Icc x y) := isClosed_Icc + have hc'' : IsOpen ((Icc x y)ᶜ) := IsClosed.isOpen_compl + have hcon := isPreconnected_univ (Icc x y) ((Icc x y)ᶜ) h hc'' + (by simp only [union_compl_self, subset_refl]) + (by simp only [univ_inter, nonempty_Icc] + linarith) + (by simp only [univ_inter] + have hy : ((y + 1) ∈ (Icc x y)ᶜ) + · simp only [mem_compl_iff, mem_Icc, add_le_iff_nonpos_right, not_and, not_le, + zero_lt_one, implies_true] + exact nonempty_of_mem hy) + simp only [inter_compl_self, inter_empty, Set.not_nonempty_empty] at hcon + +lemma not_ioc (U : Set Real) (x y : Real) (hu : IsOpen U) (h : Ioc x y = U) : (U = ∅) := by + by_cases hxy : (x < y) + · have h' : ¬ IsOpen (Ioc x y) + · apply ioc_not_open x y + exact hxy + exfalso + apply h' + rw [h] + tauto + · have h' : Ioc x y = ∅ + · exact Ioc_eq_empty hxy + rw [h] at h' + tauto + +theorem not_ico (U : Set Real) (x y : Real) (hu : IsOpen U) (h : Ico x y = U) : (U = ∅) := by + by_cases hxy : (x < y) + · have h' : ¬ IsOpen (Ico x y) + · apply ico_not_open x y + exact hxy + exfalso + apply h' + rw [h] + tauto + · have h' : Ico x y = ∅ + · exact Ico_eq_empty hxy + rw [h] at h' + tauto + +theorem not_icc (U : Set Real) (x y : Real) (hu : IsOpen U) (h : Icc x y = U) : (U = ∅) := by + by_cases hxy : (x < y) + · have h' : ¬ IsOpen (Icc x y) + · apply icc_not_open x y + exact hxy + exfalso + apply h' + rw [h] + tauto + · by_cases hyx : (x > y) + · have h' : Icc x y = ∅ + · exact Icc_eq_empty_of_lt hyx + rw [h] at h' + exact h' + · have hxy' : x ≥ y := le_of_not_gt hxy + have hyx' : x ≤ y := by exact le_of_not_gt hyx + have hxx : x = y + · have hxy'' : (x = y) ∨ (y < x) := eq_or_gt_of_not_lt hxy + have hyx'' : (x = y) ∨ (x < y) := by exact Or.symm (Decidable.lt_or_eq_of_le hyx') + rcases hyx'' with (h|h) + · exact h + tauto + rw [←hxx] at h + simp only [Icc_self] at h + rw [←h] at hu + exfalso + have c : ¬ IsOpen {x} := not_isOpen_singleton x + tauto + +theorem classify_intervals (U : Set Real) (hu : IsOpen U) (hc : IsPreconnected U) : + (∃ x y, (Set.Ioo x y = U)) ∨ + (∃ (x : Real), (U = Set.Iio x)) ∨ + (∃ (x : Real), (Set.Ioi x = U)) ∨ + (U = univ) ∨ (U = ∅) := by + have h : U ∈ (range (uncurry Icc)) ∪ range (uncurry Ico) ∪ range (uncurry Ioc) ∪ range + (uncurry Ioo) ∪ (range Ici ∪ range Ioi ∪ range Iic ∪ range Iio ∪ {univ, ∅}) + · rw [←setOfPred_isPreconnected_eq_of_ordered] + exact hc + simp only [union_insert, union_singleton, mem_insert_iff, mem_union, mem_range, Prod.exists, + uncurry_apply_pair] at h + cases h + case inl h => tauto + case inr h => + cases h + case inl h => tauto + case inr h => + cases h + case inr h => + cases h + case inl h => + cases h + case inr h => by_contra + rcases h with ⟨ x', hx ⟩ + apply not_iic + · exact hu + exact hx + case inl h => + cases h + case inr h => right; right; left; assumption + case inl h => by_contra + rcases h with ⟨ x', hx ⟩ + apply not_ici + · exact hu + exact hx + case inr h => tauto + case inl h => + cases h + case inr h => left; tauto + case inl h => + cases h + case inl h => + cases h + case inl h => rcases h with ⟨ x, y, hx ⟩ + right; right; right; right + apply not_icc + · exact hu + exact hx + case inr h => rcases h with ⟨ x, y, hx ⟩ + right; right; right; right + apply not_ico + · exact hu + exact hx + case inr h => rcases h with ⟨ x, y, hx ⟩ + right + right + right + right + apply not_ioc + · exact hu + exact hx + +/-- Select disjuncts repeatedly to reduce a nested disjunction to one of its branches. -/ +macro "solveDisj" : tactic => `(tactic| repeat (apply Or.inl <|> apply Or.inr)) + +theorem classify_connected_interval (U : Set Real) (hu : IsOpen U) (hc : IsConnected U) : + (∃ x y, (Set.Ioo x y = U)) ∨ + (∃ (x : Real), (U = Set.Iio x)) ∨ + (∃ (x : Real), (Set.Ioi x = U)) ∨ + (U = univ) := by + have hpc : IsPreconnected U := by exact IsConnected.isPreconnected hc + have hi := classify_intervals U hu hpc + have ho : ¬ (U = ∅) := by + by_contra hn + rw [hn] at hc + have he : (∅ : Set Real).Nonempty := IsConnected.nonempty hc + exact Set.not_nonempty_empty he + rcases hi with (h|h|h|h|h) + · left; assumption + · right; left; assumption + · right; right; left; assumption + · right; right; right; assumption + exfalso + exact ho h + +/-- The logarithm homeomorphism from positive nonnegative reals to the real line. -/ +noncomputable def homeoNnrealReal : (Set.Ioi 0 : Set NNReal) ≃ₜ (Set.univ : Set Real) where + toFun := fun ⟨x,_⟩ => ⟨ Real.log x, trivial ⟩ + invFun := fun ⟨x,_⟩ => ⟨ NNReal.mk (Real.exp x) (Real.exp_nonneg x), Real.exp_pos x ⟩ + left_inv := fun ⟨ ⟨ x, nn ⟩, p ⟩ => by + ext + simp only [NNReal.coe_mk] + exact Real.exp_log p + right_inv := fun x => by + simp only [NNReal.coe_mk, Real.log_exp, Subtype.coe_eta] + continuous_toFun := by + simp only + refine Continuous.subtype_mk ?_ fun x => trivial + refine Continuous.log ?_ ?_ + · exact Isometry.continuous fun x1 => congrFun rfl + intro x + rcases x with ⟨x,hx⟩ + simp only [ne_eq, NNReal.coe_eq_zero] + exact pos_iff_ne_zero.mp hx + continuous_invFun := by + simp only + apply Continuous.subtype_mk + apply Continuous.subtype_mk + apply Continuous.rexp + exact continuous_subtype_val + +/-- Truncate a real number at zero and regard the result as a nonnegative real. -/ +def relu (x : ℝ) : NNReal := + NNReal.mk (max x 0) (by simp) + +lemma relu_zero : (relu 0 = 0) := by + have : max (0 : Real) (0 : Real) = (0 : Real) := by exact max_self 0 + dsimp [relu] + simp only [max_self, NNReal.mk_zero] + +theorem continuous_relu : Continuous relu := by + apply Continuous.subtype_mk + apply Continuous.max + · exact continuous_id' + exact continuous_const + + +lemma proj_relu {x : ℝ} (h : x > 0) : (relu x).toReal = x := by + have : max x 0 = x := max_eq_left_of_lt h + exact this + +lemma proj_relu' {x : ℝ} (h : x <= 0) : (relu x).toReal = 0 := by + have : 0 = max x 0 := right_eq_sup.mpr h + dsimp [relu] + rw [←this] + +lemma relu_proj {x : NNReal} : (relu x.toReal) = x := by + by_cases h0 : x = 0 + · rw [h0] + simp only [NNReal.coe_zero] + rw [relu_zero] + · have : x > 0 := by exact pos_iff_ne_zero.mpr h0 + refine NNReal.eq ?_ + rw [proj_relu] + exact this + +lemma relu_mono : StrictMonoOn relu (Set.Ici 0) := by + intro x hx y hy h + simp only [mem_Ici] at hx + simp only [mem_Ici] at hy + apply NNReal.coe_lt_coe.mp + by_cases hx0 : x = 0 + · rw [hx0] + simp only [NNReal.coe_lt_coe] + by_cases hy0 : y = 0 + · rw [hy0] + simp only [lt_self_iff_false] + linarith + · have hy' : y > 0 := by linarith + apply NNReal.coe_lt_coe.mp + rw [proj_relu hy'] + rw [relu_zero] + simp only [NNReal.coe_zero] + assumption + · have hx' : x > 0 := by exact lt_of_le_of_ne hx fun a => hx0 (id (Eq.symm a)) + rw [proj_relu hx'] + by_cases hy0 : y = 0 + · rw [hy0] + rw [relu_zero] + simp only [NNReal.coe_zero, gt_iff_lt] + linarith + · have hy' : y > 0 := by linarith + rw [proj_relu hy'] + assumption + +lemma relu_interval_ioo {U : Set NNReal} {a b : Real} (h : Ioo a b = relu ⁻¹' U) : + (relu '' (Set.Ioo a b) = U) := by + apply subset_antisymm + · rintro _ ⟨y, hy, rfl⟩ + rw [h] at hy + exact hy + · intro x hx + refine ⟨(x : ℝ), ?_, relu_proj⟩ + rw [h] + simpa only [mem_preimage, relu_proj] using hx + +theorem StrictMonoOn.injOn_Ioo {α : Type u_1} {β : Type u_2} {f : α → β} + [LinearOrder α] [LinearOrder β] {a : α} {b : α} (h : StrictMonoOn f (Set.Icc a b)) : + InjOn f (Set.Ioo a b) := by + exact (h.mono Ioo_subset_Icc_self).injOn + +lemma relu_ioo (a b : Real) : + (relu '' (Set.Ioo a b) = Set.Ioo (relu a) (relu b)) ∨ + (relu '' (Set.Ioo a b) = Set.Ico 0 (relu b)) ∨ + (relu '' (Set.Ioo a b) = { 0 }) ∨ + (relu '' (Set.Ioo a b) = ∅) := by + by_cases ha : a > 0 + · by_cases hb : b > 0 + · left + ext z + simp only [mem_image, mem_Ioo] + constructor + · intro ⟨ x, ⟨ hx, hxz ⟩ ⟩ + have hax : relu a < relu x := by + apply NNReal.coe_lt_coe.mp + rw [proj_relu] + · rw [proj_relu] + · exact hx.1 + linarith + linarith + have hxb : relu x < relu b := by + apply NNReal.coe_lt_coe.mp + rw [proj_relu] + · rw [proj_relu] + · exact hx.2 + linarith + linarith + rw [hxz] at hax + rw [hxz] at hxb + exact ⟨hax, hxb⟩ + · intro ⟨ haz, hzb ⟩ + use z + rw [relu_proj] + simp only [and_true] + have haz' : NNReal.toReal (relu a) < NNReal.toReal z := by exact haz + have hzb' : NNReal.toReal z < NNReal.toReal (relu b) := by exact hzb + rw [proj_relu] at hzb' + · rw [proj_relu] at haz' + · exact ⟨haz', hzb'⟩ + exact ha + exact hb + · have : Ioo a b = ∅ := by + ext z + simp only [mem_Ioo, mem_empty_iff_false, iff_false, not_and, not_lt] + intro ha + linarith + rw [this] + simp only [image_empty] + simp only [or_true] + · by_cases hb : b > 0 + · by_cases ha0 : a = 0 + · rw [ha0] + left + ext z + simp only [mem_image, mem_Ioo] + apply Iff.intro + · intro ⟨ y, ⟨ h0y, hyb ⟩, hyz ⟩ + rw [←hyz] + apply And.intro + · rw [relu_zero] + have : 0 < NNReal.toReal (relu y) := by + rw [proj_relu] + · assumption + linarith + simp only [gt_iff_lt] + exact this + · have : NNReal.toReal (relu y) < NNReal.toReal (relu b) := by + rw [proj_relu] + · rw [proj_relu] + · assumption + linarith + linarith + exact this + · intro ⟨ h0z, hzb ⟩ + use z.toReal + apply And.intro + · apply And.intro + · simp only [NNReal.coe_pos] + exact pos_of_gt h0z + · have : NNReal.toReal z < NNReal.toReal (relu b) := by exact hzb + exact Real.lt_toNNReal_iff_coe_lt.mp hzb + · exact relu_proj + · right; left + ext z + simp only [mem_image, mem_Ioo, mem_Ico, zero_le, true_and] + apply Iff.intro + · intro ⟨ y, ⟨ hay, hyb ⟩ , hyz ⟩ + rw [←hyz] + dsimp [relu] + simp only [gt_iff_lt] + refine NNReal.coe_lt_coe.mp ?_ + simp only [NNReal.coe_mk, lt_sup_iff, sup_lt_iff, lt_self_iff_false, and_false, or_false] + apply And.intro <;> linarith + · intro hzb + use NNReal.toReal z + apply And.intro + · apply And.intro + · have ha0' : a < 0 := by + simp only [gt_iff_lt, not_lt] at ha + exact lt_of_le_of_ne ha ha0 + have h0z : 0 ≤ (relu z) := by exact zero_le + have h0z' : a < relu z := by exact lt_of_le_of_lt' h0z ha0' + rw [relu_proj] at h0z' + assumption + · have : NNReal.toReal z < NNReal.toReal (relu b) := by exact hzb + rw [proj_relu] at this + · assumption + assumption + · rw [relu_proj] + · by_cases hab : a ≥ b + · have : Ioo a b = ∅ := by + exact Ioo_eq_empty_of_le hab + rw [this] + simp only [image_empty, or_true] + · right; right; left + ext z + simp only [mem_image, mem_Ioo, mem_singleton_iff] + constructor + · rintro ⟨ y, hy, hyz ⟩ + have : y ≤ 0 := by linarith + have h' := proj_relu' this + rw [hyz] at h' + exact NNReal.coe_eq_zero.mp h' + · intro hz + have hab' : a < b := by linarith + use (a + b) / 2 + apply And.intro + · apply And.intro + · exact left_lt_add_div_two.mpr hab' + · exact add_div_two_lt_right.mpr hab' + · have hn : (a + b) / 2 ≤ 0 := by linarith + rw [hz] + apply NNReal.coe_eq_zero.mp + exact proj_relu' hn + +lemma relu_iio (b : Real) : + ((relu '' (Set.Iio b) = Set.Ico 0 (relu b)) ∨ + (relu '' (Set.Iio b) = { 0 })) := by + by_cases hb : b > 0 + · left + ext z + simp only [mem_image, mem_Iio, mem_Ico, zero_le, true_and] + apply Iff.intro + · intro ⟨ x, ⟨ hxb, hxz ⟩ ⟩ + rw [←hxz] + simp only [gt_iff_lt] + dsimp [relu] + apply NNReal.coe_lt_coe.mp + simp only [NNReal.coe_mk, lt_sup_iff, sup_lt_iff, lt_self_iff_false, and_false, or_false] + apply And.intro + · assumption + · assumption + · intro hzb + use z.1 + apply And.intro + · exact Nonneg.toNonneg_lt.mp hzb + · have : z ≥ 0 := by exact zero_le + simp only [NNReal.val_eq_coe] + exact relu_proj + · right + ext z + simp only [mem_image, mem_Iio, mem_singleton_iff] + apply Iff.intro + · intro ⟨ x, ⟨ hxb, hxz ⟩ ⟩ + have : x ≤ 0 := by linarith + have hn := proj_relu' this + rw [hxz] at hn + exact NNReal.coe_eq_zero.mp hn + · intro hz + use b - 1 + simp only [sub_lt_self_iff, zero_lt_one, true_and] + have : b - 1 ≤ 0 := by linarith + have hn := proj_relu' this + rw [hz] + simp only [NNReal.coe_eq_zero] at hn + assumption + +lemma relu_univ : (relu '' univ = univ) := by + ext z + simp only [image_univ, mem_range, mem_univ, iff_true] + use z.1 + apply relu_proj + +lemma relu_ioi (b : Real) : + ((relu '' (Set.Ioi b) = Set.Ioi (relu b)) ∨ + (relu '' (Set.Ioi b) = univ)) := by + by_cases hb : b > 0 + · left + ext z + simp only [mem_image, mem_Ioi] + apply Iff.intro + · intro ⟨ x, ⟨ hbx, hxz ⟩ ⟩ + rw [←hxz] + apply NNReal.coe_lt_coe.mp + rw [proj_relu, proj_relu] + · linarith + · linarith + linarith + · intro hbz + use z.1 + apply And.intro + · simp only [NNReal.val_eq_coe] + refine (Real.toNNReal_lt_iff_lt_coe ?_).mp hbz + linarith + · apply relu_proj + · by_cases hb' : b = 0 + · left + rw [hb'] + ext z + simp only [mem_image, mem_Ioi] + apply Iff.intro + · intro ⟨ x, ⟨ hbx, hxz ⟩ ⟩ + rw [relu_zero] + rw [←hxz] + apply NNReal.coe_pos.mp + rw [proj_relu] + · assumption + assumption + · intro hz + use z.1 + apply And.intro + · rw [relu_zero] at hz + exact hz + apply relu_proj + · right + ext z + simp only [mem_image, mem_Ioi, mem_univ, iff_true] + use z.1 + simp only [NNReal.val_eq_coe] + apply And.intro + · have : 0 ≤ NNReal.toReal z := by exact NNReal.zero_le_coe + have this' : b < 0 := by + simp only [gt_iff_lt, not_lt] at hb + exact lt_of_le_of_ne hb hb' + exact lt_of_le_of_lt' this this' + · exact relu_proj + +lemma a_and_b (U : Set NNReal) (ε : Real) (εpos : ε > 0) (A : Set NNReal) (B : Set NNReal) + (openA : IsOpen A) (openB : IsOpen B) + (hp : IsPreconnected U) + (interval : Set NNReal) + (interval_def : interval = Set.Ioo (0 : NNReal) (NNReal.mk ε (by linarith))) + (h1' : interval ⊆ A ∪ B) + (empty : ¬ (interval ∩ B).Nonempty) + (hA : (U \ {0} ∩ A).Nonempty) (hB : (U \ {0} ∩ B).Nonempty) + (hAB : U \ {0} ⊆ A ∪ B) : (U \ {0} ∩ (A ∩ B)).Nonempty := by + let interval' := Set.Iio (NNReal.mk ε (by linarith)) + let A' := A ∪ interval' + have openA' : IsOpen A' := by + apply IsOpen.union openA + exact isOpen_Iio + let B' := B ∩ Set.Ioi 0 + have openB' : IsOpen B' := by + exact IsOpen.inter openB isOpen_Ioi + specialize hp A' B' openA' openB' + have h1 : U ⊆ A' ∪ B' := by + intro x hx + by_cases h0' : x = 0 + · rw [h0'] + simp only [mem_union] + left + exact mem_union_right A εpos + · have : x ∈ U \ { 0 } := by exact mem_sdiff_of_mem hx h0' + specialize hAB this + simp only [mem_union] + rcases hAB with (hAB|hAB) + · left + exact mem_union_left interval' hAB + · right + apply (mem_inter_iff x B (Ioi 0)).mpr + apply And.intro + · assumption + · apply mem_Ioi.mpr + exact pos_iff_ne_zero.mpr h0' + have h2 : (U ∩ A').Nonempty := by + rcases hA with ⟨ x, hx ⟩ + use x + simp only [mem_inter_iff] + apply And.intro + · have : x ∈ U \ {0} := by exact mem_of_mem_inter_left hx + exact mem_of_mem_sdiff this + · apply (mem_union x A interval').mpr + left + exact mem_of_mem_inter_right hx + have h3 : (U ∩ B').Nonempty := by + rcases hB with ⟨ x, hx ⟩ + use x + simp only [mem_inter_iff] + apply And.intro + · have : x ∈ U \ { 0 } := by exact mem_of_mem_inter_left hx + exact mem_of_mem_sdiff this + · apply (mem_inter_iff x B (Ioi 0)).mpr + apply And.intro + · exact mem_of_mem_inter_right hx + · apply mem_Ioi.mpr + have : x ∈ U \ { 0 } := by exact mem_of_mem_inter_left hx + have this' : x ∉ ({ 0 } : Set NNReal) := by exact notMem_of_mem_sdiff this + exact pos_iff_ne_zero.mpr this' + specialize hp h1 h2 h3 + rcases hp with ⟨ x, hx ⟩ + use x + simp only [mem_inter_iff, mem_sdiff, mem_singleton_iff] + have hn0 : x ≠ 0 := by + have : x ∈ A' ∩ B' := by exact mem_of_mem_inter_right hx + have : x ∈ B' := by exact mem_of_mem_inter_right this + have : x ∈ Set.Ioi 0 := by exact mem_of_mem_inter_right this + exact pos_iff_ne_zero.mp this + apply And.intro + · apply And.intro + · exact mem_of_mem_inter_left hx + · exact hn0 + · apply And.intro + · have : x ∈ A' ∩ B' := by exact mem_of_mem_inter_right hx + have : x ∈ A' := by exact mem_of_mem_inter_left this + simp only [A'] at this + rcases this with (this|this) + · assumption + · have hi : x ∈ interval := by + rw [interval_def] + simp only [mem_Ioo] + dsimp [interval'] at this + simp only [mem_Iio] at this + apply And.intro + · exact pos_iff_ne_zero.mpr hn0 + · exact this + have this' := h1' hi + rcases this' with (this'|this') + · assumption + · exfalso + apply empty + use x + exact mem_inter hi this' + · have : x ∈ A' ∩ B' := by exact mem_of_mem_inter_right hx + have : x ∈ B' := by exact mem_of_mem_inter_right this + exact mem_of_mem_inter_left this + +lemma remove_zero_connected (U : Set NNReal) (h0 : 0 ∈ U) (hu : IsOpen U) (hc : IsConnected U) : + IsConnected (U \ {0}) := by + rcases Metric.isOpen_iff.mp hu 0 h0 with ⟨ ε, εpos, hε ⟩ + dsimp [IsConnected] + apply And.intro + · let ε' : NNReal := NNReal.mk (ε/2) (by linarith) + have : ε' ∈ Metric.ball 0 ε := by + simp only [Metric.mem_ball] + dsimp [ε'] + have hd : dist ε' 0 = ε/2 := by + rw [NNReal.dist_eq] + simp only [ε', NNReal.coe_mk, NNReal.coe_zero, sub_zero] + exact abs_of_nonneg (by linarith) + rw [hd] + simp only [half_lt_self_iff, gt_iff_lt] + linarith + use ε' + apply mem_sdiff_singleton.mpr + apply And.intro + · exact hε this + · apply NNReal.coe_ne_zero.mp + simp only [ne_eq, NNReal.coe_eq_zero] + dsimp [ε'] + apply NNReal.coe_ne_zero.mp + simp only [NNReal.coe_mk, ne_eq, div_eq_zero_iff, OfNat.ofNat_ne_zero, or_false] + exact ne_of_gt εpos + · dsimp [IsPreconnected] + by_contra h + simp only [not_forall] at h + rcases h with ⟨ A, B, openA, openB, hAB, hA, hB, hn ⟩ + have hp : IsPreconnected U := IsConnected.isPreconnected hc + let interval := Set.Ioo (0 : NNReal) (NNReal.mk ε (by linarith)) + have h1' : interval ⊆ A ∪ B := by + intro x hx + apply hAB + simp only [mem_sdiff, mem_singleton_iff] + apply And.intro + · apply hε + simp only [Metric.mem_ball] + simp only [interval, mem_Ioo] at hx + rw [NNReal.dist_eq] + simp only [NNReal.coe_zero, sub_zero, NNReal.abs_eq] + exact hx.right + · by_contra h0 + rw [h0] at hx + simp only [interval, mem_Ioo, lt_self_iff_false, false_and] at hx + have cb : IsPreconnected interval := isPreconnected_Ioo + specialize cb A B openA openB h1' + have iu : interval ⊆ U := by + intro x hx + simp only [interval, mem_Ioo] at hx + apply hε + simp only [Metric.mem_ball] + rw [NNReal.dist_eq] + simp only [NNReal.coe_zero, sub_zero, NNReal.abs_eq] + exact hx.right + have hn' : ¬ (interval ∩ (A ∩ B)).Nonempty := by + by_contra hn'' + rcases hn'' with ⟨ x, hn'' ⟩ + apply hn + use x + apply (mem_inter_iff x (U \ {0}) (A ∩ B)).mpr + apply And.intro + · apply mem_sdiff_singleton.mpr + apply And.intro + · have : x ∈ interval := by exact mem_of_mem_inter_left hn'' + apply iu + assumption + · have : x ∈ interval := by exact mem_of_mem_inter_left hn'' + by_contra x0 + rw [x0] at this + simp only [interval, mem_Ioo, lt_self_iff_false, false_and] at this + · exact mem_of_mem_inter_right hn'' + have empty : (¬ (interval ∩ A).Nonempty ∨ ¬ (interval ∩ B).Nonempty) := by tauto + apply hn + rcases empty with (empty|empty) + · have h1'' : interval ⊆ B ∪ A := by + intro x hx + simp only [mem_union] + specialize h1' hx + simp only [mem_union] at h1' + tauto + have hBA : U \ {0} ⊆ B ∪ A := by + intro x hx + simp only [mem_union] + specialize hAB hx + simp only [mem_union] at hAB + tauto + have := a_and_b U ε εpos B A openB openA hp interval (by exact rfl) h1'' empty hB hA hBA + rcases this with ⟨ x, hx ⟩ + use x + simp only [mem_inter_iff] + simp only [mem_inter_iff] at hx + tauto + · exact a_and_b U ε εpos A B openA openB hp interval (by exact rfl) h1' empty hA hB hAB + +lemma zero_in_open (a b : NNReal) (h : IsOpen ((Ioo a b) ∪ {0})) : a ≤ 0 ∧ 0 < b := by + by_contra h' + simp only [nonpos_iff_eq_zero, not_and, not_lt] at h' + by_cases ha : a = 0 + · specialize h' ha + rw [ha] at h + rw [h'] at h + simp only [lt_self_iff_false, not_false_eq_true, Ioo_eq_empty, union_singleton, + insert_empty_eq] at h + have : ¬ IsOpen ({ 0 } : Set NNReal) := not_isOpen_singleton 0 + apply this + assumption + · let U := Set.Iio (a / 2) + have openU : IsOpen U := by exact isOpen_Iio + let U' := U ∩ (Ioo a b ∪ {0}) + let openU' : IsOpen U' := by exact IsOpen.inter openU h + have U0 : U' = { 0 } := by + ext x + simp only [mem_singleton_iff] + apply Iff.intro + · intro hx + dsimp [U, U'] at hx + simp only [union_singleton, mem_inter_iff, mem_Iio, mem_insert_iff, mem_Ioo] at hx + have hx1 := hx.1 + have hx2 := hx.2 + rcases hx2 with (hx2|hx2) + · exact hx2 + · have : a < x := hx2.1 + exfalso + have this' : a < a / 2 := by exact gt_trans hx1 this + have this'' : a.toReal < a.toReal / 2 := by exact this' + have this3 : 0 ≤ a.toReal := by exact NNReal.zero_le_coe + linarith + · intro hx + dsimp [U, U'] + simp only [union_singleton, mem_inter_iff, mem_Iio, mem_insert_iff, mem_Ioo] + apply And.intro + · rw [hx] + have : 0 < a := by exact pos_iff_ne_zero.mpr ha + exact half_pos this + · left; assumption + have : ¬ IsOpen ({ 0 } : Set NNReal) := not_isOpen_singleton 0 + apply this + rwa [U0] at openU' + +private theorem classify_nnreal_of_real_Ioo (U : Set NNReal) (hu : IsOpen U) + (hr : relu ⁻¹' (U ∩ Ioi 0) = Ioi (0 : ℝ) ∩ NNReal.toReal '' U) + (h0u0 : 0 ∉ U ∩ Ioi 0) (h0u' : 0 ∉ relu ⁻¹' (U ∩ Ioi 0)) + (c : ∃ a b : ℝ, Ioo a b = relu ⁻¹' (U ∩ Ioi 0)) : + (∃ x y, Set.Ioo x y = U) ∨ (∃ x : NNReal, Set.Iio x = U) ∨ + (∃ x : NNReal, Set.Ioi x = U) ∨ U = univ := by + let U0 := U ∩ Ioi 0 + let U' := relu ⁻¹' U0 + change relu ⁻¹' U0 = Ioi (0 : ℝ) ∩ NNReal.toReal '' U at hr + change 0 ∉ U0 at h0u0 + change 0 ∉ U' at h0u' + have hu0 : IsOpen U0 := hu.inter isOpen_Ioi + change ∃ a b : ℝ, Ioo a b = U' at c + rcases c with ⟨ a, b, c ⟩ + have c' := relu_ioo a b + have hc := relu_interval_ioo c + rw [hc] at c' + by_cases hu0' : 0 ∈ U + · have uu0 : U = U0 ∪ { 0 } := by + ext x + simp only [union_singleton, mem_insert_iff] + apply Iff.intro + · intro hx + dsimp [U0] + simp only [mem_inter_iff, mem_Ioi] + by_cases h : x = 0 + · left; exact h + · right; apply And.intro + · assumption + · exact pos_iff_ne_zero.mpr h + · intro hx + rcases hx with (hx|hx) + · rw [hx] + assumption + · dsimp [U0] at hx + simp only [mem_inter_iff, mem_Ioi] at hx + exact hx.1 + rw [uu0] + rcases c' with (c'|c'|c'|c') + · right + left + rw [c'] + rw [c'] at uu0 + rw [uu0] at hu + have hz := zero_in_open (relu a) (relu b) hu + use relu b + ext x + simp only [mem_Iio, union_singleton, mem_insert_iff, mem_Ioo] + apply Iff.intro + · intro hx + by_cases h0 : x = 0 + · left; exact h0 + · right + apply And.intro + · have : 0 < x := by exact pos_iff_ne_zero.mpr h0 + exact lt_of_le_of_lt hz.1 this + · assumption + · intro hx + rcases hx with (hx|hx) + · rw [hx] + exact hz.2 + · exact hx.2 + · by_cases hb : relu b = 0 + · rw [hb] at c' + simp only [lt_self_iff_false, not_false_eq_true, Ico_eq_empty] at c' + rw [c'] at uu0 + simp only [union_singleton, insert_empty_eq] at uu0 + rw [uu0] at hu + have : ¬ IsOpen ({ 0 } : Set NNReal) := not_isOpen_singleton 0 + exfalso + exact this hu + · right + left + use relu b + rw [c'] + ext x + simp only [mem_Iio, union_singleton, mem_insert_iff, mem_Ico, zero_le, true_and, + iff_or_self] + intro hx + rw [hx] + exact pos_iff_ne_zero.mpr hb + · rw [c'] at hu0 + exfalso + have this' : ¬ IsOpen ({ 0 } : Set NNReal) := not_isOpen_singleton 0 + exact this' hu0 + · rw [c'] at uu0 + simp only [union_singleton, insert_empty_eq] at uu0 + have : ¬ IsOpen ({ 0 } : Set NNReal) := not_isOpen_singleton 0 + rw [uu0] at hu + exfalso + exact this hu + · have uu0 : U = U0 := by + dsimp [U0] + simp only [left_eq_inter] + intro x hx + simp only [mem_Ioi] + have : x ≠ 0 := by exact ne_of_mem_of_not_mem hx hu0' + exact pos_iff_ne_zero.mpr this + rw [←uu0] at c' + rcases c' with (c'|c'|c'|c') + · left + use relu a + use relu b + rw [c'] + · right + left + use (relu b) + rw [c'] + ext x + simp only [mem_Iio, mem_Ico, zero_le, true_and] + · rw [c'] at hu + exfalso + have : ¬ IsOpen ({ 0 } : Set NNReal) := not_isOpen_singleton 0 + apply this + exact hu + · left + use 0 + use 0 + rw [c'] + simp only [lt_self_iff_false, not_false_eq_true, Ioo_eq_empty] + +private theorem classify_nnreal_of_real_Ioi (U : Set NNReal) (hu : IsOpen U) (hc : IsConnected U) + (hr : relu ⁻¹' (U ∩ Ioi 0) = Ioi (0 : ℝ) ∩ NNReal.toReal '' U) + (h0u0 : 0 ∉ U ∩ Ioi 0) (h0u' : 0 ∉ relu ⁻¹' (U ∩ Ioi 0)) + (c : ∃ a : ℝ, Ioi a = relu ⁻¹' (U ∩ Ioi 0)) : + (∃ x y, Set.Ioo x y = U) ∨ (∃ x : NNReal, Set.Iio x = U) ∨ + (∃ x : NNReal, Set.Ioi x = U) ∨ U = univ := by + let U0 := U ∩ Ioi 0 + let U' := relu ⁻¹' U0 + change relu ⁻¹' U0 = Ioi (0 : ℝ) ∩ NNReal.toReal '' U at hr + change 0 ∉ U0 at h0u0 + change 0 ∉ U' at h0u' + have hu0 : IsOpen U0 := hu.inter isOpen_Ioi + change ∃ x : ℝ, Ioi x = U' at c + rcases c with ⟨ x, c ⟩ + by_cases h0 : x = 0 + · by_cases hu0 : 0 ∈ U + · right; right; right + rw [h0] at c + dsimp [U'] at c + ext z + simp only [mem_univ, iff_true] + by_cases hz0 : z = 0 + · rw [hz0] + assumption + · have zpos : z > 0 := by + exact pos_iff_ne_zero.mpr hz0 + have zi : z ∈ Set.Ioi 0 := by exact zpos + have zi' : NNReal.toReal z ∈ relu ⁻¹' U0 := by + rw [←c] + simp only [mem_Ioi, NNReal.coe_pos] + assumption + simp only [mem_preimage] at zi' + rw [relu_proj] at zi' + exact mem_of_mem_inter_left zi' + · right; right; left + use 0 + have uu0 : U = U0 := by + ext z + apply Iff.intro + · intro hz + dsimp [U0] + simp only [mem_inter_iff, mem_Ioi] + apply And.intro + · exact hz + · have : z ≠ 0 := by exact ne_of_mem_of_not_mem hz hu0 + exact pos_iff_ne_zero.mpr this + · intro hz + exact mem_of_mem_inter_left hz + rw [uu0] + rw [h0] at c + ext z + simp only [mem_Ioi] + apply Iff.intro + · intro hz + have : NNReal.toReal z > 0 := by exact hz + have this' : NNReal.toReal z ∈ U' := by + rw [←c] + simp only [mem_Ioi, NNReal.coe_pos] + assumption + dsimp [U'] at this' + simp only [mem_preimage] at this' + rw [relu_proj] at this' + assumption + · intro hz + rw [←uu0] at hz + have : z ≠ 0 := by exact ne_of_mem_of_not_mem hz hu0 + exact pos_iff_ne_zero.mpr this + · by_cases hx : x < 0 + · have : 0 ∈ U' := by + have this' : 0 ∈ Set.Ioi x := by exact hx + rw [c] at this' + assumption + exfalso + exact h0u' this + · right; right; left + by_cases hu0 : 0 ∈ U + · have : U = U0 ∪ {0} := by + ext z + simp only [union_singleton, mem_insert_iff] + apply Iff.intro + · intro hz + by_cases hz' : z = 0 + · left; assumption + · right + dsimp [U0] + refine (mem_inter_iff z U (Ioi 0)).mpr ?_ + apply And.intro + · assumption + · simp only [mem_Ioi] + exact pos_iff_ne_zero.mpr hz' + · intro hz + rcases hz with (hz|hz) + · rw [hz] + assumption + · exact mem_of_mem_inter_left hz + dsimp [U'] at c + rw [hr] at c + have contra : ¬ IsConnected U := by + dsimp [IsConnected] + simp only [not_and] + intro hn + dsimp [IsPreconnected] + simp only [not_forall] + have xpos : x ≥ 0 := by linarith + use Set.Iio (NNReal.mk x xpos) + use Set.Ioi (NNReal.mk x xpos) + simp only [exists_prop, Iio_union_Ioi, subset_compl_singleton_iff] + apply And.intro + · exact isOpen_Iio + · apply And.intro + · exact isOpen_Ioi + · apply And.intro + · by_contra h + have h' : NNReal.toReal (NNReal.mk x xpos) ∈ NNReal.toReal '' U := + mem_image_of_mem NNReal.toReal h + have h'' : NNReal.toReal (NNReal.mk x xpos) ∈ Set.Ioi 0 := by + apply mem_Ioi.mpr + exact lt_of_le_of_ne xpos fun a => h0 (id (Eq.symm a)) + have h3 : NNReal.toReal (NNReal.mk x xpos) ∈ Set.Ioi x := by + rw [c] + exact mem_inter h'' h' + simp only [NNReal.coe_mk, mem_Ioi, lt_self_iff_false] at h3 + · apply And.intro + · use 0 + simp only [mem_inter_iff, mem_Iio] + apply And.intro + · assumption + · apply NNReal.coe_pos.mp + simp only [NNReal.coe_mk] + exact lt_of_le_of_ne xpos fun a => h0 (id (Eq.symm a)) + · apply And.intro + · apply inter_nonempty.mpr + use (1 : NNReal) + (NNReal.mk x xpos) + apply And.intro + · have : 1 + x ∈ Set.Ioi x := by + apply mem_Ioi.mpr + exact lt_one_add x + rw [c] at this + simp only [mem_inter_iff, mem_Ioi, mem_image] at this + rcases this with ⟨ tpos, ⟨ t, ⟨ tu, ht ⟩ ⟩ ⟩ + have ht2 : t = 1 + (NNReal.mk x xpos) := by + exact NNReal.eq ht + rw [←ht2] + assumption + · apply mem_Ioi.mpr + exact lt_one_add ((NNReal.mk x xpos) : NNReal) + · refine not_nonempty_iff_eq_empty.mpr ?_ + let x' : NNReal := (NNReal.mk x xpos) + have : (Set.Iio x' ∩ Set.Ioi x') = ∅ := by + ext z + simp only [mem_inter_iff, mem_Iio, mem_Ioi, mem_empty_iff_false, + iff_false, not_and, not_lt] + intro hz + exact le_of_lt hz + rw [this] + simp only [inter_empty] + exfalso + exact contra hc + · have h0' : 0 ∉ Set.Ioi x := by exact Eq.mpr_not (congrFun c 0) h0u' + simp only [mem_Ioi, not_lt] at h0' + use (NNReal.mk x h0') + ext z + simp only [mem_Ioi] + apply Iff.intro + · intro hxz + have hu' : NNReal.toReal z ∈ U' := by + rw [←c] + simp only [mem_Ioi] + exact hxz + have : NNReal.toReal z ∈ Ioi 0 ∩ NNReal.toReal '' U := by + rw [←hr] + exact hu' + have this' : NNReal.toReal z ∈ NNReal.toReal '' U := mem_of_mem_inter_right this + simp only [mem_image, NNReal.coe_inj, exists_eq_right] at this' + assumption + · intro hz + apply NNReal.coe_lt_coe.mp + simp only [NNReal.coe_mk] + dsimp [U'] at c + have z0 : z ∈ U0 := by + refine (mem_inter_iff z U (Ioi 0)).mpr ?_ + apply And.intro + · exact hz + · refine mem_Ioi.mpr ?_ + refine NNReal.coe_pos.mp ?_ + simp only [NNReal.coe_pos] + have hz0 : z ≠ 0 := by exact ne_of_mem_of_not_mem hz hu0 + exact pos_iff_ne_zero.mpr hz0 + have : NNReal.toReal z ∈ Set.Ioi 0 ∩ NNReal.toReal '' U := by + simp only [mem_inter_iff, mem_Ioi, NNReal.coe_pos, mem_image, NNReal.coe_inj, + exists_eq_right] + apply And.intro + · have : z ≠ 0 := by exact ne_of_mem_of_not_mem hz hu0 + have this' : z ≥ 0 := by exact zero_le + exact pos_iff_ne_zero.mpr this + assumption + rw [←hr] at this + rw [←c] at this + simp only [mem_Ioi] at this + assumption + +theorem classify_connected_nnreal_interval (U : Set NNReal) (hu : IsOpen U) (hc : IsConnected U) : + (∃ x y, (Set.Ioo x y = U)) ∨ + (∃ (x : NNReal), (Set.Iio x = U)) ∨ + (∃ (x : NNReal), (Set.Ioi x = U)) ∨ + (U = univ) := by + let U0 := U ∩ (Set.Ioi 0) + have hu0 : IsOpen U0 := IsOpen.inter hu isOpen_Ioi + let U' := relu ⁻¹' U0 + have hr : (relu ⁻¹' U0) = (Set.Ioi (0 : Real)) ∩ (NNReal.toReal '' U) := by + ext x + simp only [mem_preimage, mem_inter_iff, mem_Ioi, mem_image] + constructor + · intro h + have : U0 ⊆ Set.Ioi 0 := inter_subset_right + have hr : relu x ∈ Set.Ioi 0 := this h + simp at hr + have hr' : 0 < x := Real.toNNReal_pos.mp (this h) + constructor + · exact hr' + · use relu x + constructor + · have : U0 ⊆ U := by exact inter_subset_left + exact this h + · have hf : (relu x).1 = x := by + apply proj_relu + assumption + assumption + · rintro ⟨h1,⟨y,⟨hu,hy⟩⟩⟩ + rw [←hy] + rw [relu_proj] + apply mem_inter hu + rw [←hy] at h1 + exact h1 + have hu' : IsOpen U' := Continuous.isOpen_preimage continuous_relu U0 hu0 + have hc' : IsConnected U' := by + by_cases h0 : 0 ∈ U + · dsimp [U'] + rw [hr] + have hs0 : IsConnected (U \ {0}) := by exact remove_zero_connected U h0 hu hc + have he : (Ioi 0 ∩ NNReal.toReal '' U) = NNReal.toReal '' (U \ {0}) := by + ext z + simp only [mem_inter_iff, mem_Ioi, mem_image, mem_sdiff, mem_singleton_iff] + apply Iff.intro + · intro ⟨ zpos, ⟨ x, ⟨ hx, hx' ⟩ ⟩ ⟩ + use x + apply And.intro + · apply And.intro + · assumption + · by_contra h' + rw [h'] at hx' + simp only [NNReal.coe_zero] at hx' + rw [←hx'] at zpos + exact (lt_self_iff_false 0).mp zpos + · assumption + · intro ⟨ x, ⟨ ⟨ h1, h2 ⟩, h3 ⟩ ⟩ + apply And.intro + · rw [←h3] + simp only [NNReal.coe_pos] + exact pos_iff_ne_zero.mpr h2 + · use x + rw [he] + have : Continuous NNReal.toReal := by exact NNReal.continuous_coe + apply IsConnected.image hs0 NNReal.toReal + apply Continuous.continuousOn + exact this + · have he : U' = NNReal.toReal '' U := by + ext z + simp only [mem_image] + apply Iff.intro + · intro hz + dsimp [U'] at hz + rw [hr] at hz + simp only [mem_inter_iff, mem_Ioi, mem_image] at hz + rcases hz with ⟨ hzpos, hz ⟩ + assumption + · rintro ⟨ x, ⟨ hx, hz ⟩ ⟩ + apply mem_preimage.mpr + rw [←hz] + rw [relu_proj] + dsimp [U0] + simp only [mem_inter_iff, mem_Ioi] + apply And.intro + · assumption + · have : x ≥ 0 := by exact zero_le + have this' : x ≠ 0 := by exact ne_of_mem_of_not_mem hx h0 + exact pos_iff_ne_zero.mpr this' + rw [he] + apply IsConnected.image hc NNReal.toReal + have : Continuous NNReal.toReal := by exact NNReal.continuous_coe + apply Continuous.continuousOn + assumption + let c := classify_connected_interval U' hu' hc' + have h0u0 : 0 ∉ U0 := by + apply zero_notMem_iff.mpr + intro z hz + have : z ∈ Set.Ioi (0 : NNReal) := mem_of_mem_inter_right hz + exact this + have h0u' : 0 ∉ U' := by + dsimp [U'] + simp only [mem_preimage] + rw [relu_zero] + assumption + rcases c with (c|c|c|c) + · exact classify_nnreal_of_real_Ioo U hu hr h0u0 h0u' c + · rcases c with ⟨ x, hx ⟩ + rw [hx] at h0u' + simp only [mem_Iio, not_lt] at h0u' + dsimp [U'] at hx + rw [hr] at hx + have hxi : x - 1 ∈ Set.Iio x := by + apply mem_Iio.mpr + linarith + rw [←hx] at hxi + have hc : x - 1 ∈ Set.Ioi 0 := by exact mem_of_mem_inter_left hxi + simp only [mem_Ioi, sub_pos] at hc + linarith + · exact classify_nnreal_of_real_Ioi U hu hc hr h0u0 h0u' c + · exfalso + have h0i : 0 ∉ Set.Ioi (0 : NNReal) := self_notMem_Ioi + have this' : 0 ∈ U0 := by + have : 0 ∈ U' := by + rw [c] + exact trivial + dsimp [U'] at this + simp only [mem_preimage] at this + rw [relu_zero] at this + assumption + exact h0u0 this' + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/ClassifyOverlaps.lean b/LeanPool/OneManifold/OneMfld/ClassifyOverlaps.lean new file mode 100644 index 000000000..6e98e6300 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/ClassifyOverlaps.lean @@ -0,0 +1,470 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Analysis.SpecialFunctions.Complex.Circle +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.UnitInterval +public import LeanPool.OneManifold.OneMfld.FiniteIntervalCharts +public import LeanPool.OneManifold.OneMfld.IntervalCharts +public import LeanPool.OneManifold.OneMfld.NiceCharts +public import LeanPool.OneManifold.OneMfld.ClosureOverlap +public import LeanPool.OneManifold.OneMfld.PartialHomeomorphHelpers +public import LeanPool.OneManifold.OneMfld.Charts +public import LeanPool.OneManifold.OneMfld.Outer +public import LeanPool.OneManifold.OneMfld.TransitionMono +public import LeanPool.OneManifold.OneMfld.Normalize +public import LeanPool.OneManifold.OneMfld.GlueCore +public import LeanPool.OneManifold.OneMfld.GlueBlocks +public import LeanPool.OneManifold.OneMfld.GlueNNReal +public import LeanPool.OneManifold.OneMfld.GlueUI +public import LeanPool.OneManifold.OneMfld.TwoComponents +public import LeanPool.OneManifold.OneMfld.CircleBlocks +public import LeanPool.OneManifold.OneMfld.CircleGlue + + +/-! +# ClassifyOverlaps + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +open Set + +variable + {M : Type*} + [TopologicalSpace M] + [ConnectedSpace M] + [T2Space M] + +open Classical in +/-- Turn a homeomorphism of open subtypes `A ≃ₜ B` into a partial homeomorphism `X ⇀ Y`. + +The `Nonempty` hypotheses are needed, and are not an artefact of the proof: a +`PartialEquiv X Y` carries a *total* `toFun : X → Y`, so `OpenPartialHomeomorph Unit Empty` +is an empty type even though `(∅ : Set Unit) ≃ₜ (∅ : Set Empty)` with both sets open. -/ +noncomputable def Homeomorph.toOpenPartialHomeomorphOnOpens + {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] [Nonempty X] [Nonempty Y] + {A : Set X} {B : Set Y} (h : A ≃ₜ B) (hA : IsOpen A) (hB : IsOpen B) : + OpenPartialHomeomorph X Y where + toFun x := if hx : x ∈ A then (h ⟨x, hx⟩ : Y) else Classical.arbitrary Y + invFun y := if hy : y ∈ B then (h.symm ⟨y, hy⟩ : X) else Classical.arbitrary X + source := A + target := B + map_source' x hx := by simp only [dite_eq_left hx]; exact (h ⟨x, hx⟩).2 + map_target' y hy := by simp only [dite_eq_left hy]; exact (h.symm ⟨y, hy⟩).2 + left_inv' x hx := by + simp only [dite_eq_left hx, dite_eq_left (h ⟨x, hx⟩).2] + have : (⟨(h ⟨x, hx⟩ : Y), (h ⟨x, hx⟩).2⟩ : B) = h ⟨x, hx⟩ := rfl + rw [this, Homeomorph.symm_apply_apply] + right_inv' y hy := by + simp only [dite_eq_left hy, dite_eq_left (h.symm ⟨y, hy⟩).2] + have : (⟨(h.symm ⟨y, hy⟩ : X), (h.symm ⟨y, hy⟩).2⟩ : A) = h.symm ⟨y, hy⟩ := rfl + rw [this, Homeomorph.apply_symm_apply] + open_source := hA + open_target := hB + continuousOn_toFun := by + rw [continuousOn_iff_continuous_domRestrict] + have : (A.domRestrict fun x => if hx : x ∈ A then (h ⟨x, hx⟩ : Y) else Classical.arbitrary Y) + = fun x : A => ((h x : B) : Y) := by + funext x; simp only [Set.domRestrict_apply, dite_eq_left x.2] + rw [this] + exact continuous_subtype_val.comp h.continuous + continuousOn_invFun := by + rw [continuousOn_iff_continuous_domRestrict] + have : (B.domRestrict fun y => if hy : y ∈ B then (h.symm ⟨y, hy⟩ : X) else + Classical.arbitrary X) + = fun y : B => ((h.symm y : A) : X) := by + funext y; simp only [Set.domRestrict_apply, dite_eq_left y.2] + rw [this] + exact continuous_subtype_val.comp h.symm.continuous + +/-- An `OpenPartialHomeomorph` from a preconnected Hausdorff space onto the whole of a +compact space, with nonempty source, is a global homeomorphism: its source is compact +(hence closed) as well as open, so it is clopen and equals `univ`. -/ +noncomputable def OpenPartialHomeomorph.toHomeomorphOfCompactTarget + {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] + [PreconnectedSpace X] [T2Space X] [CompactSpace Y] + (φ : OpenPartialHomeomorph X Y) + (hne : φ.source.Nonempty) + (ht : φ.target = Set.univ) : + Homeomorph X Y := by + have ho : IsOpen φ.source := φ.open_source + have hc : IsClosed φ.source := by + have cpct' : CompactSpace φ.target := by + rw [ht] + exact (Homeomorph.Set.univ Y).symm.compactSpace + have h : Homeomorph φ.target φ.source := φ.toHomeomorphSourceTarget.symm + have hcs : CompactSpace φ.source := h.compactSpace + have cpct : IsCompact φ.source := isCompact_iff_isCompact_univ.mpr CompactSpace.isCompact_univ + exact cpct.isClosed + have hs : φ.source = Set.univ := by + have hco : IsClopen φ.source := ⟨hc, ho⟩ + have he' := isPreconnected_univ.subset_isClopen hco (by simpa using hne) + exact Set.eq_univ_of_univ_subset he' + exact φ.toHomeomorphOfSourceEqUnivTargetEqUniv hs ht + +section OverlapHelpers + +omit [ConnectedSpace M] [T2Space M] in +/-- The chart image of the overlap is never the whole target (since `U.source` is not +contained in `V.source`). -/ +lemma overlap_image_ne_target (U V : OpenPartialHomeomorph M NNReal) + (hUV : (U.source \ V.source).Nonempty) : + U '' (U.source ∩ V.source) ≠ U.target := by + intro heq + obtain ⟨y, hyU, hyV⟩ := hUV + have hy : U y ∈ U '' (U.source ∩ V.source) := heq ▸ U.map_source hyU + obtain ⟨w, hwS, hwy⟩ := hy + have hw : w = y := U.injOn hwS.1 hyU hwy + exact hyV (hw ▸ hwS.2) + +omit [ConnectedSpace M] in +/-- The outer-overlap lemma for a boundary chart, specialized to a connected overlap. -/ +lemma overlap_image_Iio (U V : OpenPartialHomeomorph M NNReal) {v : NNReal} + (hUt : U.target = Iio v) + (hV : IsConnected V.source) + (hUV : (U.source \ V.source).Nonempty) + (hVU : (V.source \ U.source).Nonempty) + (hc : IsConnected (U.source ∩ V.source)) : + ∃ p, p < v ∧ U '' (U.source ∩ V.source) = Ioo p v := by + obtain ⟨x, hx⟩ := hc.nonempty + obtain ⟨p, hp, himg⟩ := overlap_component_outer_Iio U V hUt hV hUV hVU hx + have hcomp : connectedComponentIn (U.source ∩ V.source) x = U.source ∩ V.source := + subset_antisymm (connectedComponentIn_subset _ _) + (hc.isPreconnected.subset_connectedComponentIn hx subset_rfl) + rw [hcomp] at himg + exact ⟨p, hp, himg⟩ + +omit [ConnectedSpace M] in +/-- The outer-overlap lemma for an interior chart, specialized to a connected overlap. -/ +lemma overlap_image_Ioo (U V : OpenPartialHomeomorph M NNReal) {u v : NNReal} + (hUt : U.target = Ioo u v) + (hV : IsConnected V.source) + (hVU : (V.source \ U.source).Nonempty) + (hc : IsConnected (U.source ∩ V.source)) : + (∃ p, u ≤ p ∧ p < v ∧ U '' (U.source ∩ V.source) = Ioo p v) ∨ + (∃ q, u < q ∧ q ≤ v ∧ U '' (U.source ∩ V.source) = Ioo u q) := by + obtain ⟨x, hx⟩ := hc.nonempty + have hcomp : connectedComponentIn (U.source ∩ V.source) x = U.source ∩ V.source := + subset_antisymm (connectedComponentIn_subset _ _) + (hc.isPreconnected.subset_connectedComponentIn hx subset_rfl) + rcases overlap_component_outer_Ioo U V hUt hV hVU hx with ⟨p, h1, h2, himg⟩ | ⟨q, h1, h2, himg⟩ + · rw [hcomp] at himg + exact Or.inl ⟨p, h1, h2, himg⟩ + · rw [hcomp] at himg + exact Or.inr ⟨q, h1, h2, himg⟩ + +omit [ConnectedSpace M] in +/-- The overlap of a boundary chart with a connected chart is connected. -/ +lemma hchart_overlap_connected (b : HChart M) (V : OpenPartialHomeomorph M NNReal) + (hV : IsConnected V.source) + (hbV : (b.source \ V.source).Nonempty) + (hVb : (V.source \ b.source).Nonempty) + (hne : (b.source ∩ V.source).Nonempty) : + IsConnected (b.source ∩ V.source) := by + obtain ⟨v, hv⟩ := b.target_iio + exact overlap_connected b.toOpenPartialHomeomorph V hv.symm hV hbV hVb hne + +lemma Ioc_union_Ioo_eq_Ioo {μ ν : NNReal} (hμν : μ < ν) : + Ioc 0 μ ∪ Ioo μ ν = Ioo 0 ν := by + ext y + simp only [mem_union, mem_Ioc, mem_Ioo] + constructor + · rintro (⟨hy0, hy⟩ | ⟨hy, hy'⟩) + · exact ⟨hy0, lt_of_le_of_lt hy hμν⟩ + · exact ⟨lt_of_le_of_lt (zero_le : (0 : NNReal) ≤ μ) hy, hy'⟩ + · rintro ⟨hy0, hy⟩ + rcases le_or_gt y μ with hle | hgt + · exact Or.inl ⟨hy0, hle⟩ + · exact Or.inr ⟨hgt, hy⟩ + +lemma lt_div_self_of_lt_one {μ ρ : NNReal} (hμ0 : 0 < μ) (hρ0 : 0 < ρ) (hρ1 : ρ < 1) : + μ < μ / ρ := by + rw [lt_div_iff₀ hρ0] + exact mul_lt_of_lt_one_right hμ0 hρ1 + +end OverlapHelpers + +section Orient + +omit [ConnectedSpace M] in +/-- Re-orient an `OChart` with target `Ioo 0 1` (flipping if necessary) so that its image +of the overlap with `V.source` is the *lower* end-segment `Ioo 0 r` with `0 < r < 1`. +The source is unchanged. -/ +lemma OChart.exists_orient_lower (a : OChart M) (V : OpenPartialHomeomorph M NNReal) + (hat : a.target = Ioo 0 1) + (hV : IsConnected V.source) + (haV : (a.source \ V.source).Nonempty) + (hVa : (V.source \ a.source).Nonempty) + (hc : IsConnected (a.source ∩ V.source)) : + ∃ (a' : OChart M) (r : NNReal), a'.source = a.source ∧ a'.target = Ioo 0 1 ∧ + 0 < r ∧ r < 1 ∧ + a'.toOpenPartialHomeomorph '' (a.source ∩ V.source) = Ioo 0 r := by + have hne := overlap_image_ne_target a.toOpenPartialHomeomorph V haV + rcases overlap_image_Ioo a.toOpenPartialHomeomorph V hat hV hVa hc with + ⟨p, -, hp1, himg⟩ | ⟨r, hr0, hr1, himg⟩ + · -- upper case: flip + have hp0 : 0 < p := by + rcases eq_or_lt_of_le (zero_le : (0 : NNReal) ≤ p) with heq | hlt + · exfalso + apply hne + rw [himg, hat, ← heq] + · exact hlt + obtain ⟨a', ha's, ha't, ha'f⟩ := a.flip hat + have himg' : a'.toOpenPartialHomeomorph '' (a.source ∩ V.source) + = (fun y => 1 - y) '' (a.toOpenPartialHomeomorph '' (a.source ∩ V.source)) := by + rw [← image_comp] + exact image_congr (fun x hx => ha'f x hx.1) + rw [himg, reflect_image_Ioo_upper hp1] at himg' + exact ⟨a', 1 - p, ha's, ha't, tsub_pos_of_lt hp1, tsub_lt_self one_pos hp0, himg'⟩ + · -- lower case: already oriented + have hr1' : r < 1 := by + rcases eq_or_lt_of_le hr1 with heq | hlt + · exfalso + apply hne + rw [himg, hat, heq] + · exact hlt + exact ⟨a, r, rfl, hat, hr0, hr1', himg⟩ + +omit [ConnectedSpace M] in +/-- Re-orient an `OChart` with target `Ioo 0 1` (flipping if necessary) so that its image +of the overlap with `V.source` is the *upper* end-segment `Ioo q 1` with `0 < q < 1`. +The source is unchanged. -/ +lemma OChart.exists_orient_upper (a : OChart M) (V : OpenPartialHomeomorph M NNReal) + (hat : a.target = Ioo 0 1) + (hV : IsConnected V.source) + (haV : (a.source \ V.source).Nonempty) + (hVa : (V.source \ a.source).Nonempty) + (hc : IsConnected (a.source ∩ V.source)) : + ∃ (a' : OChart M) (q : NNReal), a'.source = a.source ∧ a'.target = Ioo 0 1 ∧ + 0 < q ∧ q < 1 ∧ + a'.toOpenPartialHomeomorph '' (a.source ∩ V.source) = Ioo q 1 := by + have hne := overlap_image_ne_target a.toOpenPartialHomeomorph V haV + rcases overlap_image_Ioo a.toOpenPartialHomeomorph V hat hV hVa hc with + ⟨q, hq0, hq1, himg⟩ | ⟨w, hw0, hw1, himg⟩ + · -- upper case: already oriented + have hq0' : 0 < q := by + rcases eq_or_lt_of_le hq0 with heq | hlt + · exfalso + apply hne + rw [himg, hat, ← heq] + · exact hlt + exact ⟨a, q, rfl, hat, hq0', hq1, himg⟩ + · -- lower case: flip + have hw1' : w < 1 := by + rcases eq_or_lt_of_le hw1 with heq | hlt + · exfalso + apply hne + rw [himg, hat, heq] + · exact hlt + obtain ⟨a', ha's, ha't, ha'f⟩ := a.flip hat + have himg' : a'.toOpenPartialHomeomorph '' (a.source ∩ V.source) + = (fun y => 1 - y) '' (a.toOpenPartialHomeomorph '' (a.source ∩ V.source)) := by + rw [← image_comp] + exact image_congr (fun x hx => ha'f x hx.1) + rw [himg, reflect_image_Ioo_lower hw0 hw1] at himg' + exact ⟨a', 1 - w, ha's, ha't, tsub_pos_of_lt hw1', tsub_lt_self one_pos hw0, himg'⟩ + +end Orient + +omit [ConnectedSpace M] in +/-- Two overlapping H-charts glue to a chart of `M` onto the unit interval: rescale both +to target `Iio 1`, note the overlap is connected and appears as an upper end-segment in +each chart (outer-overlap lemma), and apply the unit-interval gluing. -/ +lemma exists_glue_h_h (a : HChart M) (b : HChart M) (h : Overlap a.source b.source) : + ∃ φ : OpenPartialHomeomorph M UnitInterval, + φ.source = a.source ∪ b.source ∧ φ.target = Set.univ := by + have hane : a.source.Nonempty := h.1.mono inter_subset_left + have hbne : b.source.Nonempty := h.1.mono inter_subset_right + obtain ⟨a', ha's, ha't, -⟩ := a.rescale hane + obtain ⟨b', hb's, hb't, -⟩ := b.rescale hbne + -- the overlap is connected + have hc : IsConnected (a'.source ∩ b'.source) := by + rw [ha's, hb's, inter_comm] + exact hchart_overlap_connected b a.toOpenPartialHomeomorph + (a.connected_source hane) h.2.2 h.2.1 (inter_comm a.source b.source ▸ h.1) + -- each chart sees the overlap as an upper end-segment + have hb'conn : IsConnected b'.source := b'.connected_source (hb's ▸ hbne) + have ha'conn : IsConnected a'.source := a'.connected_source (ha's ▸ hane) + obtain ⟨p, hp1, haimg⟩ := overlap_image_Iio a'.toOpenPartialHomeomorph + b'.toOpenPartialHomeomorph ha't hb'conn + (by rw [ha's, hb's]; exact h.2.1) (by rw [ha's, hb's]; exact h.2.2) hc + obtain ⟨q, hq1, hbimg⟩ := overlap_image_Iio b'.toOpenPartialHomeomorph + a'.toOpenPartialHomeomorph hb't ha'conn + (by rw [ha's, hb's]; exact h.2.2) (by rw [ha's, hb's]; exact h.2.1) + (inter_comm a'.source b'.source ▸ hc) + rw [inter_comm] at hbimg + -- glue + obtain ⟨f, hfs, hft⟩ := glue_hh_ui a'.toOpenPartialHomeomorph b'.toOpenPartialHomeomorph + ha't hb't haimg hp1 hbimg + refine ⟨f, ?_, hft⟩ + rw [hfs, ha's, hb's] + +/-- Glue two overlapping H-charts into a single chart of `M` onto the unit interval. -/ +noncomputable def glueHH (a : HChart M) (b : HChart M) (h : Overlap a.source b.source) : + { φ : OpenPartialHomeomorph M UnitInterval | φ.source = a.source ∪ b.source ∧ φ.target = + Set.univ } := + ⟨(exists_glue_h_h a b h).choose, (exists_glue_h_h a b h).choose_spec⟩ + +/-- Join overlapping boundary charts into a homeomorphism with the closed unit interval. -/ +noncomputable def handleHH (a : HChart M) (b : HChart M) (h : Overlap a.source b.source) : + Homeomorph M UnitInterval := by + obtain ⟨φ, hφ, hs⟩ := glueHH a b h + have hne : φ.source.Nonempty := by + rw [hφ] + exact Set.Nonempty.inl (h.1.mono Set.inter_subset_left) + exact OneMfld.OpenPartialHomeomorph.toHomeomorphOfCompactTarget φ hne hs + +omit [ConnectedSpace M] in +/-- An O-chart and an H-chart with connected overlap glue to an H-chart on the union: +rescale both, orient the O-chart so the overlap sits at its lower end, and apply the +`ℝ≥0` gluing. -/ +lemma exists_glue_o_h (a : OChart M) (b : HChart M) (h : Overlap a.source b.source) + (hc : IsConnected (a.source ∩ b.source)) : + ∃ f : HChart M, f.source = a.source ∪ b.source := by + have hane : a.source.Nonempty := h.1.mono inter_subset_left + have hbne : b.source.Nonempty := h.1.mono inter_subset_right + obtain ⟨a₀, ha₀s, ha₀t, -⟩ := a.rescale hane + obtain ⟨b', hb's, hb't, -⟩ := b.rescale hbne + have hb'conn : IsConnected b'.source := b'.connected_source (hb's ▸ hbne) + -- orient the O-chart so the overlap is its lower end-segment + obtain ⟨a', r, ha's, ha't, hr0, hr1, haimg⟩ := + a₀.exists_orient_lower b'.toOpenPartialHomeomorph ha₀t hb'conn + (by rw [ha₀s, hb's]; exact h.2.1) (by rw [ha₀s, hb's]; exact h.2.2) + (by rw [ha₀s, hb's]; exact hc) + have ha's' : a'.source = a.source := ha's.trans ha₀s + have ha'conn : IsConnected a'.source := a'.connected_source (ha's' ▸ hane) + -- the H-chart sees the overlap as an upper end-segment + obtain ⟨q, hq1, hbimg⟩ := overlap_image_Iio b'.toOpenPartialHomeomorph + a'.toOpenPartialHomeomorph hb't ha'conn + (by rw [ha's', hb's]; exact h.2.2) (by rw [ha's', hb's]; exact h.2.1) + (by rw [ha's', hb's, inter_comm]; exact hc) + rw [inter_comm] at hbimg + -- adjust the O-chart image statement to the primed sources + have haimg' : a'.toOpenPartialHomeomorph '' (a'.source ∩ b'.source) = Ioo 0 r := by + rw [ha's] + exact haimg + -- glue + obtain ⟨f, hfs, μ, ρ, hqμ, hμ1, hρ0, hρ1, hft⟩ := + glue_nnreal a'.toOpenPartialHomeomorph b'.toOpenPartialHomeomorph ha't + (by rw [hb't]) haimg' hr0 hr1 hbimg (by rw [hb't]; exact hq1) + -- the glued target is `Iio (μ/ρ)` + have hμ0 : 0 < μ := lt_of_le_of_lt zero_le hqμ + have hμν : μ < μ / ρ := lt_div_self_of_lt_one hμ0 hρ0 hρ1 + have htarget : f.target = Iio (μ / ρ) := by + rw [hft, hb't] + have h1 : Iio 1 ∩ Iic μ = Iic μ := + inter_eq_right.mpr (fun y hy => lt_of_le_of_lt hy hμ1) + rw [h1, Iic_union_Ioo_eq_Iio hμν] + refine ⟨⟨f, ⟨μ / ρ, htarget.symm⟩⟩, ?_⟩ + change f.source = a.source ∪ b.source + rw [hfs, ha's', hb's] + +/-- Glue an O-chart and an H-chart with connected overlap into an H-chart on the union. -/ +noncomputable def handleOH' (a : OChart M) (b : HChart M) (h : Overlap a.source b.source) + (hc : IsConnected (a.source ∩ b.source)) : + { f : HChart M | f.source = a.source ∪ b.source } := + ⟨(exists_glue_o_h a b h hc).choose, (exists_glue_o_h a b h hc).choose_spec⟩ + +/-- Glue an O-chart and an H-chart: the overlap with an H-chart is automatically +connected. -/ +noncomputable def handleOH (a : OChart M) (b : HChart M) (h : Overlap a.source b.source) : + { f : HChart M | f.source = a.source ∪ b.source } := by + have hane : a.source.Nonempty := h.1.mono inter_subset_left + have hc : IsConnected (a.source ∩ b.source) := by + rw [inter_comm] + exact hchart_overlap_connected b a.toOpenPartialHomeomorph + (a.connected_source hane) h.2.2 h.2.1 (inter_comm a.source b.source ▸ h.1) + exact handleOH' a b h hc + +omit [ConnectedSpace M] in +/-- Two O-charts with connected overlap glue to an O-chart on the union: rescale both, +orient the first chart's overlap low and the second's high, and apply the `ℝ≥0` +gluing. -/ +lemma exists_glue_o_o (a : OChart M) (b : OChart M) (h : Overlap a.source b.source) + (hc : IsConnected (a.source ∩ b.source)) : + ∃ f : OChart M, f.source = a.source ∪ b.source := by + have hane : a.source.Nonempty := h.1.mono inter_subset_left + have hbne : b.source.Nonempty := h.1.mono inter_subset_right + obtain ⟨a₀, ha₀s, ha₀t, -⟩ := a.rescale hane + obtain ⟨b₀, hb₀s, hb₀t, -⟩ := b.rescale hbne + have hb₀conn : IsConnected b₀.source := b₀.connected_source (hb₀s ▸ hbne) + -- orient the first chart's overlap low + obtain ⟨a', r, ha's, ha't, hr0, hr1, haimg⟩ := + a₀.exists_orient_lower b₀.toOpenPartialHomeomorph ha₀t hb₀conn + (by rw [ha₀s, hb₀s]; exact h.2.1) (by rw [ha₀s, hb₀s]; exact h.2.2) + (by rw [ha₀s, hb₀s]; exact hc) + have ha's' : a'.source = a.source := ha's.trans ha₀s + have ha'conn : IsConnected a'.source := a'.connected_source (ha's' ▸ hane) + -- orient the second chart's overlap high + obtain ⟨b', q, hb's, hb't, hq0, hq1, hbimg⟩ := + b₀.exists_orient_upper a'.toOpenPartialHomeomorph hb₀t ha'conn + (by rw [ha's', hb₀s]; exact h.2.2) (by rw [ha's', hb₀s]; exact h.2.1) + (by rw [ha's', hb₀s, inter_comm]; exact hc) + have hb's' : b'.source = b.source := hb's.trans hb₀s + -- adjust both image statements to the primed sources + have haimg' : a'.toOpenPartialHomeomorph '' (a'.source ∩ b'.source) = Ioo 0 r := by + rw [ha's, hb's] + exact haimg + have hbimg' : b'.toOpenPartialHomeomorph '' (a'.source ∩ b'.source) = Ioo q 1 := by + rw [hb's, inter_comm] + exact hbimg + -- glue + obtain ⟨f, hfs, μ, ρ, hqμ, hμ1, hρ0, hρ1, hft⟩ := + glue_nnreal a'.toOpenPartialHomeomorph b'.toOpenPartialHomeomorph ha't + (by rw [hb't]; exact Ioo_subset_Iio_self) haimg' hr0 hr1 hbimg' + (by rw [hb't]; exact ⟨hq0, hq1⟩) + -- the glued target is `Ioo 0 (μ/ρ)` + have hμ0 : 0 < μ := lt_of_le_of_lt zero_le hqμ + have hμν : μ < μ / ρ := lt_div_self_of_lt_one hμ0 hρ0 hρ1 + have htarget : f.target = Ioo 0 (μ / ρ) := by + rw [hft, hb't] + have h1 : Ioo 0 1 ∩ Iic μ = Ioc 0 μ := by + ext y + simp only [mem_inter_iff, mem_Ioo, mem_Iic, mem_Ioc] + constructor + · rintro ⟨⟨hy0, -⟩, hy⟩ + exact ⟨hy0, hy⟩ + · rintro ⟨hy0, hy⟩ + exact ⟨⟨hy0, lt_of_le_of_lt hy hμ1⟩, hy⟩ + rw [h1, Ioc_union_Ioo_eq_Ioo hμν] + refine ⟨⟨f, ⟨0, μ / ρ, htarget.symm⟩⟩, ?_⟩ + change f.source = a.source ∪ b.source + rw [hfs, ha's', hb's'] + +/-- A disconnected overlap of two O-charts closes `M` up into a circle: the glued chart +of `exists_circle_chart` maps `a.source ∪ b.source` onto the whole of `AddCircle 1`; +transfer to `Circle` and apply the compact-target argument. -/ +noncomputable def circleOfDisconnectedOverlap (a : OChart M) (b : OChart M) + (h : Overlap a.source b.source) (hc : ¬ IsConnected (a.source ∩ b.source)) : + Homeomorph M Circle := by + have H := exists_circle_chart a b h hc + obtain ⟨hfs, hft⟩ := H.choose_spec + let f' := H.choose.transHomeomorph (AddCircle.homeomorphCircle (one_ne_zero (α := ℝ))) + have hfs' : f'.source = a.source ∪ b.source := hfs + have hft' : f'.target = Set.univ := by + change (AddCircle.homeomorphCircle _).symm ⁻¹' H.choose.target = Set.univ + rw [hft] + exact Set.preimage_univ + have hne : f'.source.Nonempty := by + rw [hfs'] + exact Set.Nonempty.inl (h.1.mono Set.inter_subset_left) + exact OneMfld.OpenPartialHomeomorph.toHomeomorphOfCompactTarget f' hne hft' + +/-- Glue two O-charts: with a connected overlap they merge into an O-chart on the union; +with a disconnected overlap, `M` is a circle. -/ +noncomputable def handleOO (a : OChart M) (b : OChart M) (h : Overlap a.source b.source) : + (Homeomorph M Circle) ⊕ { f : OChart M | f.source = a.source ∪ b.source } := by + by_cases hc : IsConnected (a.source ∩ b.source) + · exact Sum.inr ⟨(exists_glue_o_o a b h hc).choose, (exists_glue_o_o a b h hc).choose_spec⟩ + · exact Sum.inl (circleOfDisconnectedOverlap a b h hc) + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/ClosureOverlap.lean b/LeanPool/OneManifold/OneMfld/ClosureOverlap.lean new file mode 100644 index 000000000..6f609e8f1 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/ClosureOverlap.lean @@ -0,0 +1,86 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import Mathlib.Topology.Connected.Basic + + +/-! +# ClosureOverlap + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +open Set TopologicalSpace + +universe u +variable {α : Type u} +variable {X : Type u} [TopologicalSpace X] +variable {U V : Set X} + +lemma nonempty_closure_inter_diff + (hVconn : IsConnected V) (hUopen : IsOpen U) (hVopen : IsOpen V) + (hUV : (U ∩ V).Nonempty) (hVU : (V \ U).Nonempty) : + ((closure U) ∩ (V \ U)).Nonempty := by + -- prove by contradiction that `closure U ∩ (V \ U)` can't be empty. + by_contra h + push Not at h + let A := U ∩ V + let B := V \ U + have hVconn' : IsPreconnected (V : Set X) := IsConnected.isPreconnected hVconn + have openA : IsOpen (A : Set X) := IsOpen.inter hUopen hVopen + have openB : IsOpen (B : Set X) := by + have : (V \ U) = (V \ (closure U)) := by + ext x + simp only [mem_sdiff, and_congr_right_iff] + intro hv + constructor + · intro hu + have : x ∈ V \ U := mem_sdiff_of_mem hv hu + by_contra hcu + have : x ∈ closure U ∩ (V \ U) := by exact mem_inter hcu this + rw [h] at this + exact this + · intro hcu + exact notMem_of_notMem_closure hcu + dsimp [B] -- this is needed? + rw [this] + exact IsOpen.sdiff hVopen isClosed_closure + have coverAB : V ⊆ A ∪ B := by + rintro x hx + simp only [mem_union] + by_cases hxU : x ∈ U + · left + exact mem_inter hxU hx + · right + exact mem_sdiff_of_mem hx hxU + have nonemptyVA : (V ∩ A).Nonempty := by + have : (V ∩ (U ∩ V)) = U ∩ V := by + simp only [inter_eq_right, inter_subset_right] + rw [this] + exact hUV + have nonemptyVB : (V ∩ B).Nonempty := by + have : (V ∩ (V \ U)) = (V \ U) := by + simp only [inter_eq_right] + exact sdiff_subset + rw [this] + exact hVU + have disjointAB : A ∩ B = ∅ := by + ext x + simp only [mem_inter_iff, mem_empty_iff_false, iff_false, not_and] + intro hx + apply notMem_sdiff_of_mem + exact mem_of_mem_inter_left hx + have nonemptyVAB : (V ∩ (A ∩ B)).Nonempty := hVconn' A B openA openB coverAB nonemptyVA nonemptyVB + rw [disjointAB] at nonemptyVAB + simp only [inter_empty, Set.not_nonempty_empty] at nonemptyVAB + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/Compactness.lean b/LeanPool/OneManifold/OneMfld/Compactness.lean new file mode 100644 index 000000000..a8dfe3278 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/Compactness.lean @@ -0,0 +1,117 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.FiniteIntervalCharts +public import LeanPool.OneManifold.OneMfld.Noncompact + + +/-! +# Compactness + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +variable + {M : Type*} + [TopologicalSpace M] + +lemma clopen_in_r (s : Set NNReal) : (s ≠ ∅ ∧ s ≠ Set.univ) → ¬ IsClopen s := by + intro h12 + by_contra h' + have h1 := isClopen_iff.mp h' + tauto + +lemma noncompact_nnreal : NoncompactSpace NNReal := by + have := not_compactSpace_NNReal + exact not_compactSpace_iff.mp this + +lemma noncompact_ioo' (x y : NNReal) (hxy : x < y) : NoncompactSpace (Set.Ioo x y) := by + let s := Set.Ioo x y + have h : ¬ CompactSpace s := by + by_contra h' + have hc : IsCompact s := by exact isCompact_iff_compactSpace.mpr h' + have hs : IsClosed s := by exact IsCompact.isClosed hc + have ho : IsOpen s := by exact isOpen_Ioo + have hn : Nonempty s := by exact Set.nonempty_Ioo_subtype hxy + have hc' := clopen_in_r s + apply hc' + constructor + · exact Set.nonempty_iff_ne_empty'.mp hn + · by_contra ha + rw [ha] at h' + have : ¬ IsCompact (Set.univ : Set NNReal) := by + have ht : ¬ CompactSpace (NNReal) := by + exact not_compactSpace_iff.mpr noncompact_nnreal + by_contra ht' + apply ht + exact { isCompact_univ := ht' } + apply this + exact isCompact_iff_compactSpace.mpr h' + exact ⟨ hs, ho ⟩ + exact not_compactSpace_iff.mp h + +lemma noncompact_ioo (x y : NNReal) (hxy : x < y) : ¬ CompactSpace (Set.Ioo x y) := by + have h := noncompact_ioo' x y hxy + exact not_compactSpace_iff.mpr h + +lemma noncompact_iio' (x : NNReal) (hx : 0 < x) : NoncompactSpace (Set.Iio x) := by + let s := Set.Iio x + have h : ¬ CompactSpace s := by + by_contra h' + have hc : IsCompact s := by exact isCompact_iff_compactSpace.mpr h' + have hs : IsClosed s := by exact IsCompact.isClosed hc + have ho : IsOpen s := by exact isOpen_Iio + have hn : Nonempty s := by + apply nonempty_subtype.mpr + exact Exists.intro 0 hx + have hc' := clopen_in_r s + apply hc' + constructor + · exact Set.nonempty_iff_ne_empty'.mp hn + · by_contra ha + rw [ha] at h' + have : ¬ IsCompact (Set.univ : Set NNReal) := by + have ht : ¬ CompactSpace (NNReal) := by + exact not_compactSpace_iff.mpr noncompact_nnreal + by_contra ht' + apply ht + exact { isCompact_univ := ht' } + apply this + exact isCompact_iff_compactSpace.mpr h' + exact ⟨ hs, ho ⟩ + exact not_compactSpace_iff.mp h + +lemma noncompact_iio (x : NNReal) (hx : 0 < x) : ¬ CompactSpace (Set.Iio x) := by + have h:= noncompact_iio' x hx + exact not_compactSpace_iff.mpr h + +lemma noncompact_target (ht : FinitelyIntervalChartedSpace M) (z : M) (a : OpenPartialHomeomorph + M NNReal) (ha : a ∈ ht.atlas) (hz : z ∈ a.source) : ¬ CompactSpace (a.target) := by + have := ht.is_interval a ha + rcases this with (h|h) + · rcases h with ⟨ x, y, h ⟩ + rw [←h] + have hz' : a.toFun z ∈ a.target := by exact PartialEquiv.map_source a.toPartialEquiv hz + rw [←h] at hz' + simp only [Set.mem_Ioo] at hz' + rcases hz' with ⟨ hxz, hzy ⟩ + have hxy : x < y := gt_trans hzy hxz + apply noncompact_ioo x y hxy + · rcases h with ⟨ x, h ⟩ + rw [←h] + have hz' : a.toFun z ∈ a.target := by exact PartialEquiv.map_source a.toPartialEquiv hz + rw [←h] at hz' + simp only [Set.mem_Iio] at hz' + have hx : 0 < x := by exact pos_of_gt hz' + apply noncompact_iio x hx + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/FiniteIntervalCharts.lean b/LeanPool/OneManifold/OneMfld/FiniteIntervalCharts.lean new file mode 100644 index 000000000..bdf1d2944 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/FiniteIntervalCharts.lean @@ -0,0 +1,48 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.IntervalCharts +public import LeanPool.OneManifold.OneMfld.FinitelyCharted + + +/-! +# FiniteIntervalCharts + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +/-- A space equipped with a finite atlas of interval charts. -/ +class FinitelyIntervalChartedSpace (M : Type*) [TopologicalSpace M] extends IntervalChartedSpace + M where + is_finite : Set.Finite atlas + +variable + {M : Type*} + [TopologicalSpace M] + [CompactSpace M] + +/-- Extract a finite interval atlas from compactness. -/ +@[instance_reducible] noncomputable def finitelyIntervalCharted (ht : IntervalChartedSpace M) : + FinitelyIntervalChartedSpace M := by + have c : { ht' : ChartedSpace NNReal M | Set.Finite ht'.atlas ∧ ht'.atlas ⊆ ht.atlas } := + chooseCharts + rcases c with ⟨ ht', c1, c2 ⟩ + exact { ht' with + is_finite := c1 + , is_interval := by + intro x hx + apply ht.is_interval + apply c2 + exact hx + } + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/FinitelyCharted.lean b/LeanPool/OneManifold/OneMfld/FinitelyCharted.lean new file mode 100644 index 000000000..937d2115d --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/FinitelyCharted.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Geometry.Manifold.ChartedSpace +public import Mathlib.Tactic + + +/-! +# FinitelyCharted + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +variable {H : Type*} [TopologicalSpace H] {M : Type*} [TopologicalSpace M] + +variable + {M : Type*} + [TopologicalSpace M] + +/-- Choose a finite subatlas covering a compact charted space. -/ +noncomputable def chooseCharts [CompactSpace M] [ht : ChartedSpace H M] : { ht' : ChartedSpace H + M | Set.Finite ht'.atlas ∧ ht'.atlas ⊆ ht.atlas } := by + let U : M → Set M := by + intro x + exact (ht.chartAt x).source + let hUo : (i : M) → IsOpen (U i) := by + intro x + exact (ChartedSpace.chartAt x).open_source + let hsU : (Set.univ : Set M) ⊆ ⋃ (i : M), U i := by + intro x hx + simp only [Set.mem_iUnion] + use x + exact ChartedSpace.mem_chart_source x + have hc := IsCompact.elim_finite_subcover CompactSpace.isCompact_univ U hUo hsU + let t := Classical.choose hc + have t' := Classical.choose_spec hc + let f (x : M) : { x' : M | x' ∈ t ∧ x ∈ (ht.chartAt x').source } := by + have : x ∈ ⋃ i ∈ t, U i := by exact t' trivial + simp only [Set.mem_iUnion, exists_prop] at this + have hx := Classical.choose_spec this + use Classical.choose this + simp only [Set.mem_ofPred_eq] + apply And.intro + · exact hx.1 + · dsimp [U] at hx + exact hx.2 + use { chartAt := by + intro x + exact ht.chartAt (f x).1 + , atlas := Set.image ht.chartAt t + , chart_mem_atlas := by + intro x + apply Set.mem_image_of_mem ChartedSpace.chartAt + refine Finset.mem_coe.mpr ?_ + exact (f x).2.1 + , mem_chart_source := by + intro x + exact (f x).2.2 + } + simp only [Set.mem_ofPred_eq, Set.image_subset_iff] + apply And.intro + · exact Set.toFinite (ht.chartAt '' ↑t) + · intro x hx + simp only [Set.mem_preimage, chart_mem_atlas] + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/GlueBlocks.lean b/LeanPool/OneManifold/OneMfld/GlueBlocks.lean new file mode 100644 index 000000000..e6f07e800 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/GlueBlocks.lean @@ -0,0 +1,222 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Algebra.Order.Star.Real +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.UnitInterval + + +/-! # Building blocks for the H-H gluing + +Two overlapping boundary charts glue to a *closed* interval, so the glued chart cannot be +`NNReal`-valued (its target would not be open); it must land in the subtype +`UnitInterval`. The pieces: + +* `halfOPH` — `x ↦ x/2` embedding `Iio 1 ⊆ ℝ≥0` onto the lower half `[0, 1/2)` of the + unit interval (open in the subtype since `0` is an endpoint); +* `mobiusOPH k` — the decreasing Möbius map `x ↦ k/(x+k)` embedding all of `ℝ≥0` onto + the upper piece `(0, 1]` (open in the subtype since `1` is an endpoint) — a decreasing + reparametrization with no truncated-subtraction issues; +* the frontier of `{y ≤ c}` in the unit interval; +* reflection images of end-segments in `ℝ≥0`, used to re-orient `OChart`s. +-/ + +@[expose] public section + +namespace OneMfld + +open Set Topology + +noncomputable section + +/-- `x ↦ x/2` as a partial homeomorphism from `ℝ≥0` (source `Iio 1`) to the unit +interval (target the sub-half-interval `[0, 1/2)`, open in the subtype). -/ +def halfOPH : + { e : OpenPartialHomeomorph NNReal UnitInterval // + e.source = Iio 1 ∧ e.target = {y : UnitInterval | (y : ℝ) < 1/2} ∧ + ∀ x : NNReal, x ∈ Iio (1 : NNReal) → ((e x : ℝ) = x / 2) } := by + have hmem : ∀ x : NNReal, min ((x : ℝ) / 2) 1 ∈ UnitInterval := fun x => + ⟨le_min (by positivity) zero_le_one, min_le_right _ _⟩ + refine ⟨{ toFun := fun x => ⟨min ((x : ℝ) / 2) 1, hmem x⟩ + invFun := fun y => Real.toNNReal (2 * (y : ℝ)) + source := Iio 1 + target := {y : UnitInterval | (y : ℝ) < 1/2} + map_source' := ?_ + map_target' := ?_ + left_inv' := ?_ + right_inv' := ?_ + open_source := isOpen_Iio + open_target := ?_ + continuousOn_toFun := + (((NNReal.continuous_coe.div_const 2).min continuous_const).subtype_mk + hmem).continuousOn + continuousOn_invFun := + (continuous_real_toNNReal.comp + (continuous_const.mul continuous_subtype_val)).continuousOn }, + rfl, rfl, fun x hx => ?_⟩ + · -- map_source' + intro x hx + have hx1 : x < (1 : NNReal) := hx + have hx' : (x : ℝ) < 1 := by exact_mod_cast hx1 + change min ((x : ℝ) / 2) 1 < 1/2 + rw [min_eq_left (by linarith : (x : ℝ) / 2 ≤ 1)] + linarith + · -- map_target' + intro y hy + have hy' : (y : ℝ) < 1/2 := hy + have h2 : (0:ℝ) ≤ 2 * (y : ℝ) := mul_nonneg (by norm_num) y.2.1 + change Real.toNNReal (2 * (y : ℝ)) < 1 + rw [Real.toNNReal_lt_iff_lt_coe h2, NNReal.coe_one] + linarith + · -- left_inv' + intro x hx + have hx1 : x < (1 : NNReal) := hx + have hx' : (x : ℝ) < 1 := by exact_mod_cast hx1 + change Real.toNNReal (2 * min ((x : ℝ) / 2) 1) = x + rw [min_eq_left (by linarith : (x : ℝ) / 2 ≤ 1), + show 2 * ((x : ℝ) / 2) = (x : ℝ) from by ring] + exact Real.toNNReal_coe + · -- right_inv' + intro y hy + have hy' : (y : ℝ) < 1/2 := hy + have h2 : (0:ℝ) ≤ 2 * (y : ℝ) := mul_nonneg (by norm_num) y.2.1 + apply Subtype.ext + change min ((Real.toNNReal (2 * (y : ℝ)) : ℝ) / 2) 1 = (y : ℝ) + rw [Real.coe_toNNReal _ h2, + show 2 * (y : ℝ) / 2 = (y : ℝ) from by ring] + exact min_eq_left y.2.2 + · -- open_target + exact isOpen_Iio.preimage continuous_subtype_val + · -- e x = x / 2 on the source + have hx1 : x < (1 : NNReal) := hx + have hx' : (x : ℝ) < 1 := by exact_mod_cast hx1 + exact min_eq_left (by linarith : (x : ℝ) / 2 ≤ 1) + +/-- The Möbius map `x ↦ k/(x+k)` (for `k > 0`) as a partial homeomorphism from `ℝ≥0` +(source everything) to the unit interval (target `(0, 1]`, open in the subtype). It is +strictly decreasing, sends `0 ↦ 1`, and tends to `0` at infinity. -/ +def mobiusOPH (k : NNReal) (hk : 0 < k) : + { e : OpenPartialHomeomorph NNReal UnitInterval // + e.source = univ ∧ e.target = {y : UnitInterval | 0 < (y : ℝ)} ∧ + (∀ x : NNReal, (e x : ℝ) = k / (x + k)) ∧ + (∀ x₁ x₂ : NNReal, x₁ < x₂ → (e x₂ : ℝ) < (e x₁ : ℝ)) } := by + have hk' : (0:ℝ) < (k : ℝ) := by exact_mod_cast hk + have hden : ∀ x : NNReal, (0:ℝ) < (x : ℝ) + (k : ℝ) := fun x => + add_pos_of_nonneg_of_pos x.coe_nonneg hk' + have hmem : ∀ x : NNReal, (k : ℝ) / ((x : ℝ) + (k : ℝ)) ∈ UnitInterval := fun x => + ⟨le_of_lt (div_pos hk' (hden x)), + (div_le_one (hden x)).mpr (le_add_of_nonneg_left x.coe_nonneg)⟩ + have key : ∀ a : ℝ, (k : ℝ) / ((k : ℝ) / a) = a := fun a => by + rw [div_div_eq_mul_div, mul_comm, mul_div_assoc, div_self hk'.ne', mul_one] + refine ⟨{ toFun := fun x => ⟨(k : ℝ) / ((x : ℝ) + (k : ℝ)), hmem x⟩ + invFun := fun y => Real.toNNReal ((k : ℝ) / (y : ℝ) - (k : ℝ)) + source := univ + target := {y : UnitInterval | 0 < (y : ℝ)} + map_source' := fun x _ => div_pos hk' (hden x) + map_target' := fun y _ => mem_univ _ + left_inv' := ?_ + right_inv' := ?_ + open_source := isOpen_univ + open_target := isOpen_Ioi.preimage continuous_subtype_val + continuousOn_toFun := ((Continuous.div continuous_const + (NNReal.continuous_coe.add continuous_const) + (fun x => (hden x).ne')).subtype_mk hmem).continuousOn + continuousOn_invFun := continuous_real_toNNReal.comp_continuousOn + ((continuousOn_const.div continuous_subtype_val.continuousOn + (fun y hy => ne_of_gt (hy : (0:ℝ) < (y : ℝ)))).sub continuousOn_const) }, + rfl, rfl, fun x => rfl, ?_⟩ + · -- left_inv' + intro x _ + change Real.toNNReal ((k : ℝ) / ((k : ℝ) / ((x : ℝ) + (k : ℝ))) - (k : ℝ)) = x + rw [key, show (x : ℝ) + (k : ℝ) - (k : ℝ) = (x : ℝ) from by ring] + exact Real.toNNReal_coe + · -- right_inv' + intro y hy + have hy' : (0:ℝ) < (y : ℝ) := hy + have hy1 : (y : ℝ) ≤ 1 := y.2.2 + have hknn : (k : ℝ) ≤ (k : ℝ) / (y : ℝ) := by + rw [le_div_iff₀ hy'] + exact mul_le_of_le_one_right hk'.le hy1 + apply Subtype.ext + change (k : ℝ) / ((Real.toNNReal ((k : ℝ) / (y : ℝ) - (k : ℝ)) : ℝ) + (k : ℝ)) = (y : ℝ) + rw [Real.coe_toNNReal _ (sub_nonneg.mpr hknn), + show (k : ℝ) / (y : ℝ) - (k : ℝ) + (k : ℝ) = (k : ℝ) / (y : ℝ) from by ring, + key] + · -- strictly decreasing + intro x₁ x₂ h + have h' : (x₁ : ℝ) < (x₂ : ℝ) := by exact_mod_cast h + exact div_lt_div_of_pos_left hk' (hden x₁) + (by linarith : (x₁ : ℝ) + (k : ℝ) < (x₂ : ℝ) + (k : ℝ)) + +/-- The frontier of the closed lower piece `{y ≤ c}` of the unit interval is the single +level `{y = c}`, provided `0 < c < 1`. -/ +lemma frontier_UIIic {c : ℝ} (h0 : 0 < c) (h1 : c < 1) : + frontier {y : UnitInterval | (y : ℝ) ≤ c} = {y : UnitInterval | (y : ℝ) = c} := by + have hclosed : IsClosed {y : UnitInterval | (y : ℝ) ≤ c} := + isClosed_Iic.preimage continuous_subtype_val + have hint : interior {y : UnitInterval | (y : ℝ) ≤ c} + = {y : UnitInterval | (y : ℝ) < c} := by + refine Subset.antisymm ?_ (interior_maximal (fun y (hy : (y : ℝ) < c) => hy.le) + (isOpen_Iio.preimage continuous_subtype_val)) + intro y hy + change (y : ℝ) < c + by_contra hlt + have hymem : y ∈ {y : UnitInterval | (y : ℝ) ≤ c} := interior_subset hy + have hyc : (y : ℝ) = c := le_antisymm hymem (not_lt.mp hlt) + have hS : {y : UnitInterval | (y : ℝ) ≤ c} ∈ 𝓝 y := mem_interior_iff_mem_nhds.mp hy + rw [nhds_subtype] at hS + obtain ⟨V, hV, hVsub⟩ := Filter.mem_comap.mp hS + obtain ⟨ε, hε, hball⟩ := Metric.mem_nhds_iff.mp hV + set δ := min (ε/2) ((1 - c)/2) with hδdef + have hδ0 : 0 < δ := lt_min (by linarith) (by linarith) + have hδε : δ < ε := lt_of_le_of_lt (min_le_left _ _) (by linarith) + have hδ1 : c + δ ≤ 1 := by + have h := min_le_right (ε/2) ((1 - c)/2) + have : δ ≤ (1 - c)/2 := h + linarith + have hmem : c + δ ∈ UnitInterval := ⟨by linarith, hδ1⟩ + have hballmem : c + δ ∈ Metric.ball (y : ℝ) ε := by + rw [Metric.mem_ball, hyc, Real.dist_eq, + show c + δ - c = δ from by ring, abs_of_pos hδ0] + exact hδε + have hin : (⟨c + δ, hmem⟩ : UnitInterval) ∈ Subtype.val ⁻¹' V := hball hballmem + have hle : c + δ ≤ c := hVsub hin + linarith + rw [hclosed.frontier_eq, hint] + ext y + simp only [mem_sdiff, mem_ofPred_eq, not_lt] + exact ⟨fun h => le_antisymm h.1 h.2, fun h => ⟨h.le, h.ge⟩⟩ + +/-- Reflection `y ↦ 1 - y` sends the upper end-segment `Ioo p 1 ⊆ ℝ≥0` to the lower +end-segment `Ioo 0 (1-p)`. -/ +lemma reflect_image_Ioo_upper {p : NNReal} (_hp : p < 1) : + (fun y : NNReal => 1 - y) '' Ioo p 1 = Ioo 0 (1 - p) := by + ext z + constructor + · rintro ⟨y, ⟨hyp, hy1⟩, rfl⟩ + exact ⟨tsub_pos_of_lt hy1, tsub_lt_tsub_left_of_le hy1.le hyp⟩ + · rintro ⟨hz0, hzp⟩ + have hz1 : z ≤ 1 := (hzp.trans_le tsub_le_self).le + exact ⟨1 - z, ⟨lt_tsub_comm.mp hzp, tsub_lt_self one_pos hz0⟩, + tsub_tsub_cancel_of_le hz1⟩ + +/-- Reflection `y ↦ 1 - y` sends the lower end-segment `Ioo 0 q ⊆ ℝ≥0` (for `q ≤ 1`) to +the upper end-segment `Ioo (1-q) 1`. -/ +lemma reflect_image_Ioo_lower {q : NNReal} (_hq0 : 0 < q) (hq1 : q ≤ 1) : + (fun y : NNReal => 1 - y) '' Ioo 0 q = Ioo (1 - q) 1 := by + ext z + constructor + · rintro ⟨y, ⟨hy0, hyq⟩, rfl⟩ + exact ⟨tsub_lt_tsub_left_of_le hq1 hyq, tsub_lt_self one_pos hy0⟩ + · rintro ⟨hzq, hz1⟩ + refine ⟨1 - z, ⟨tsub_pos_of_lt hz1, ?_⟩, tsub_tsub_cancel_of_le hz1.le⟩ + rw [tsub_lt_iff_right hz1.le] + exact (tsub_lt_iff_left hq1).mp hzq + +end + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/GlueCore.lean b/LeanPool/OneManifold/OneMfld/GlueCore.lean new file mode 100644 index 000000000..07281150f --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/GlueCore.lean @@ -0,0 +1,273 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.Outer +public import LeanPool.OneManifold.OneMfld.TransitionMono + + +/-! # Core lemmas for gluing overlapping charts + +Two results feed the gluing construction: + +* **Connectivity**: if `U` is a boundary chart (target `Iio v`), its overlap with any + other chart is connected — every component's `U`-image is an upper end-segment + `Ioo q v` (outer-overlap lemma), and two upper end-segments always intersect. + +* **End-matching**: the transition map on the overlap is strictly monotone in the + direction forced by which ends of the two chart images are *interior* (the endpoint + belongs to the chart target). If both interior ends were paired with each other, the + overlap would accumulate at two distinct points of `M` at once, contradicting `T2`. +-/ + +@[expose] public section + +namespace OneMfld + +open Set Filter Topology + +/-- A strictly antitone map of `Ioo p v` onto `Ioo q w` tends to `q` at the top end. -/ +theorem tendsto_top_of_strictAntiOn_image {p v q w : NNReal} (hpv : p < v) + {f : NNReal → NNReal} (hm : StrictAntiOn f (Ioo p v)) + (himg : f '' Ioo p v = Ioo q w) : + Tendsto f (𝓝[<] v) (𝓝 q) := by + have hne : (Ioo p v).Nonempty := nonempty_Ioo.2 hpv + have hqw : q < w := nonempty_Ioo.1 (himg ▸ hne.image f) + have hbdd : BddBelow (f '' Ioo p v) := himg ▸ bddBelow_Ioo + have h := AntitoneOn.tendsto_nhdsWithin_Ioo_left hne hm.antitoneOn hbdd + rwa [himg, csInf_Ioo hqw] at h + +variable {M : Type*} [TopologicalSpace M] + +/-- A chart transition is continuous and injective wherever the first inverse chart lands +in the second chart's source. -/ +theorem chartTransition_continuous_injective + (a b : OpenPartialHomeomorph M NNReal) {I : Set NNReal} + (hI : I ⊆ a.target) (hmap : ∀ t ∈ I, a.symm t ∈ b.source) : + ContinuousOn (fun t => b (a.symm t)) I ∧ InjOn (fun t => b (a.symm t)) I := by + constructor + · exact b.continuousOn.comp (a.symm.continuousOn.mono hI) hmap + · intro x hx y hy hxy + exact a.symm.injOn (hI hx) (hI hy) (b.injOn (hmap x hx) (hmap y hy) hxy) + +/-- A transition limit at interior chart endpoints identifies their inverse images. -/ +theorem chartTransition_endpoint_eq [T2Space M] + (a b : OpenPartialHomeomorph M NNReal) {I : Set NNReal} {s t : NNReal} + (hI : I ⊆ a.target) (hmap : ∀ x ∈ I, a.symm x ∈ b.source) + (hs : s ∈ a.target) (ht : t ∈ b.target) (hne : (𝓝[I] s).NeBot) + (hlim : Tendsto (fun x => b (a.symm x)) (𝓝[I] s) (𝓝 t)) : + a.symm s = b.symm t := by + have hleft : Tendsto a.symm (𝓝[I] s) (𝓝 (a.symm s)) := + (a.symm.continuousOn s hs).mono hI + have htarget : Tendsto (fun x => b (a.symm x)) (𝓝[I] s) (𝓝[b.target] t) := by + rw [tendsto_nhdsWithin_iff] + refine ⟨hlim, ?_⟩ + filter_upwards [self_mem_nhdsWithin] with x hx + exact b.map_source (hmap x hx) + have hright := Tendsto.comp (b.symm.continuousOn t ht) htarget + apply tendsto_nhds_unique' hne hleft + apply Tendsto.congr' _ hright + filter_upwards [self_mem_nhdsWithin] with x hx + exact b.left_inv (hmap x hx) + +/-- The overlap of a boundary chart `U` (target `Iio v`) with any other chart is +connected: every component's `U`-image is an upper end-segment of `Iio v`, and two upper +end-segments intersect, so there is only one component. -/ +theorem overlap_connected [T2Space M] + (U V : OpenPartialHomeomorph M NNReal) {v : NNReal} + (hUt : U.target = Iio v) + (hV : IsConnected V.source) + (hUV : (U.source \ V.source).Nonempty) + (hVU : (V.source \ U.source).Nonempty) + (hne : (U.source ∩ V.source).Nonempty) : + IsConnected (U.source ∩ V.source) := by + set S := U.source ∩ V.source with hSdef + obtain ⟨x, hx⟩ := hne + have hSU : S ⊆ U.source := inter_subset_left + have hkey : S = connectedComponentIn S x := by + apply subset_antisymm _ (connectedComponentIn_subset S x) + intro y hy + obtain ⟨qx, hqx, hix⟩ := overlap_component_outer_Iio U V hUt hV hUV hVU hx + obtain ⟨qy, hqy, hiy⟩ := overlap_component_outer_Iio U V hUt hV hUV hVU hy + obtain ⟨z, hz1, hz2⟩ := exists_between (max_lt hqx hqy) + have hzx : z ∈ U '' connectedComponentIn S x := + hix ▸ mem_Ioo.mpr ⟨lt_of_le_of_lt (le_max_left _ _) hz1, hz2⟩ + have hzy : z ∈ U '' connectedComponentIn S y := + hiy ▸ mem_Ioo.mpr ⟨lt_of_le_of_lt (le_max_right _ _) hz1, hz2⟩ + obtain ⟨w1, hw1, hw1z⟩ := hzx + obtain ⟨w2, hw2, hw2z⟩ := hzy + have hw12 : w1 = w2 := + U.injOn (hSU (connectedComponentIn_subset _ _ hw1)) + (hSU (connectedComponentIn_subset _ _ hw2)) (hw1z.trans hw2z.symm) + have h1 : connectedComponentIn S x = connectedComponentIn S w1 := + connectedComponentIn_eq hw1 + have h2 : connectedComponentIn S y = connectedComponentIn S w2 := + connectedComponentIn_eq hw2 + rw [h1, hw12, ← h2] + exact mem_connectedComponentIn hy + rw [hkey] + exact isConnected_connectedComponentIn_iff.mpr hx + +/-- Component version of `overlap_mono`: on a subset `W` of the overlap whose images are +`Ioo p r` and `Ioo q w`, with the `r`-end interior to `a.target`, the `q`-end interior to +`b.target`, and `r` not in the image of the full overlap, the transition is increasing. -/ +theorem overlap_mono_on [T2Space M] (a b : OpenPartialHomeomorph M NNReal) + {W : Set M} (hW : W ⊆ a.source ∩ b.source) + {p r q w : NNReal} + (ha : a '' W = Ioo p r) + (hb : b '' W = Ioo q w) + (hr : r ∈ a.target) (hq : q ∈ b.target) + (hrS : r ∉ a '' (a.source ∩ b.source)) : + ∀ x ∈ W, ∀ y ∈ W, a x < a y → b x < b y := by + have hWa : W ⊆ a.source := hW.trans inter_subset_left + have hWb : W ⊆ b.source := hW.trans inter_subset_right + have hacoord : ∀ x ∈ W, a x ∈ Ioo p r := fun x hx => ha ▸ mem_image_of_mem a hx + by_cases hpr : p < r + swap + · intro x hx y hy hxy + exact absurd ((hacoord x hx).1.trans (hacoord x hx).2) hpr + set τ : NNReal → NNReal := fun t => b (a.symm t) with hτdef + have hsymm : ∀ t ∈ Ioo p r, a.symm t ∈ W ∧ a (a.symm t) = t := by + intro t ht + rw [← ha] at ht + obtain ⟨x, hxW, rfl⟩ := ht + rw [a.left_inv (hWa hxW)] + exact ⟨hxW, rfl⟩ + have hkey : ∀ x ∈ W, τ (a x) = b x := by + intro x hx + simp only [hτdef] + rw [a.left_inv (hWa hx)] + have hτmaps : ∀ t ∈ Ioo p r, τ t ∈ Ioo q w := by + intro t ht + have hb' : b (a.symm t) ∈ b '' W := mem_image_of_mem b (hsymm t ht).1 + exact hb ▸ hb' + have hIoo_target : Ioo p r ⊆ a.target := by + rw [← ha] + rintro y ⟨z, hz, rfl⟩ + exact a.map_source (hWa hz) + obtain ⟨hcont, hinj⟩ : ContinuousOn τ (Ioo p r) ∧ InjOn τ (Ioo p r) := + chartTransition_continuous_injective a b hIoo_target + (fun t ht => hWb (hsymm t ht).1) + rcases strictMonoOn_or_strictAntiOn_of_injOn_Ioo hcont hinj with hmono | hanti + · intro x hx y hy hxy + have h := hmono (hacoord x hx) (hacoord y hy) hxy + rwa [hkey x hx, hkey y hy] at h + · exfalso + have himg : τ '' Ioo p r = Ioo q w := by + apply subset_antisymm + · rintro _ ⟨t, ht, rfl⟩ + exact hτmaps t ht + · intro y hy + rw [← hb] at hy + obtain ⟨x, hxW, rfl⟩ := hy + exact ⟨a x, hacoord x hxW, hkey x hxW⟩ + have hτlim : Tendsto τ (𝓝[<] r) (𝓝 q) := + tendsto_top_of_strictAntiOn_image hpr hanti himg + have hF : 𝓝[Ioo p r] r = 𝓝[<] r := nhdsWithin_Ioo_eq_nhdsLT hpr + have hFne : (𝓝[Ioo p r] r).NeBot := by + apply mem_closure_iff_nhdsWithin_neBot.mp + rw [closure_Ioo hpr.ne] + exact ⟨hpr.le, le_refl r⟩ + have hτend : Tendsto τ (𝓝[Ioo p r] r) (𝓝 q) := hF ▸ hτlim + have hz : a.symm r = b.symm q := + chartTransition_endpoint_eq a b hIoo_target + (fun t ht => hWb (hsymm t ht).1) hr hq hFne hτend + have hzS : a.symm r ∈ a.source ∩ b.source := by + refine ⟨a.map_target hr, ?_⟩ + rw [hz] + exact b.map_target hq + exact hrS ⟨a.symm r, hzS, a.right_inv hr⟩ + +/-- **End-matching, increasing case.** Suppose the overlap `S = a.source ∩ b.source` has +image `Ioo p r` in chart `a` and `Ioo q w` in chart `b`, where the `r`-end is interior to +`a.target` and the `q`-end is interior to `b.target`. Then the transition is increasing: +`a x < a y → b x < b y` on `S`. (If it were decreasing, the overlap would accumulate at +both `a.symm r` and `b.symm q` along the same filter, forcing them equal — but then that +point would lie in `S`, putting `r` in `Ioo p r`.) -/ +theorem overlap_mono [T2Space M] (a b : OpenPartialHomeomorph M NNReal) + {p r q w : NNReal} + (ha : a '' (a.source ∩ b.source) = Ioo p r) + (hb : b '' (a.source ∩ b.source) = Ioo q w) + (hr : r ∈ a.target) (hq : q ∈ b.target) : + ∀ x ∈ a.source ∩ b.source, ∀ y ∈ a.source ∩ b.source, + a x < a y → b x < b y := by + apply overlap_mono_on a b (Subset.refl _) ha hb hr hq + rw [ha] + exact fun h => lt_irrefl r h.2 + +/-- **End-matching, decreasing case.** Same setting, but now the `p`-end is interior to +`a.target` (and the `q`-end interior to `b.target`): the transition is decreasing. -/ +theorem overlap_anti [T2Space M] (a b : OpenPartialHomeomorph M NNReal) + {p r q w : NNReal} + (ha : a '' (a.source ∩ b.source) = Ioo p r) + (hb : b '' (a.source ∩ b.source) = Ioo q w) + (hp : p ∈ a.target) (hq : q ∈ b.target) : + ∀ x ∈ a.source ∩ b.source, ∀ y ∈ a.source ∩ b.source, + a x < a y → b y < b x := by + set S := a.source ∩ b.source with hSdef + have hSa : S ⊆ a.source := inter_subset_left + have hSb : S ⊆ b.source := inter_subset_right + have hacoord : ∀ x ∈ S, a x ∈ Ioo p r := fun x hx => ha ▸ mem_image_of_mem a hx + by_cases hpr : p < r + swap + · intro x hx y hy hxy + exact absurd ((hacoord x hx).1.trans (hacoord x hx).2) hpr + set τ : NNReal → NNReal := fun t => b (a.symm t) with hτdef + have hsymm : ∀ t ∈ Ioo p r, a.symm t ∈ S ∧ a (a.symm t) = t := by + intro t ht + rw [← ha] at ht + obtain ⟨x, hxS, rfl⟩ := ht + rw [a.left_inv (hSa hxS)] + exact ⟨hxS, rfl⟩ + have hkey : ∀ x ∈ S, τ (a x) = b x := by + intro x hx + simp only [hτdef] + rw [a.left_inv (hSa hx)] + have hτmaps : ∀ t ∈ Ioo p r, τ t ∈ Ioo q w := by + intro t ht + have hb' : b (a.symm t) ∈ b '' S := mem_image_of_mem b (hsymm t ht).1 + exact hb ▸ hb' + have hIoo_target : Ioo p r ⊆ a.target := by + rw [← ha] + rintro y ⟨z, hz, rfl⟩ + exact a.map_source (hSa hz) + obtain ⟨hcont, hinj⟩ : ContinuousOn τ (Ioo p r) ∧ InjOn τ (Ioo p r) := + chartTransition_continuous_injective a b hIoo_target + (fun t ht => hSb (hsymm t ht).1) + rcases strictMonoOn_or_strictAntiOn_of_injOn_Ioo hcont hinj with hmono | hanti + · exfalso + have himg : τ '' Ioo p r = Ioo q w := by + apply subset_antisymm + · rintro _ ⟨t, ht, rfl⟩ + exact hτmaps t ht + · intro y hy + rw [← hb] at hy + obtain ⟨x, hxS, rfl⟩ := hy + exact ⟨a x, hacoord x hxS, hkey x hxS⟩ + have hτlim : Tendsto τ (𝓝[>] p) (𝓝 q) := + tendsto_bot_of_strictMonoOn_image hpr hmono himg + have hF : 𝓝[Ioo p r] p = 𝓝[>] p := nhdsWithin_Ioo_eq_nhdsGT hpr + have hFne : (𝓝[Ioo p r] p).NeBot := by + apply mem_closure_iff_nhdsWithin_neBot.mp + rw [closure_Ioo hpr.ne] + exact ⟨le_refl p, hpr.le⟩ + have hτend : Tendsto τ (𝓝[Ioo p r] p) (𝓝 q) := hF ▸ hτlim + have hz : a.symm p = b.symm q := + chartTransition_endpoint_eq a b hIoo_target + (fun t ht => hSb (hsymm t ht).1) hp hq hFne hτend + have hzS : a.symm p ∈ S := by + refine ⟨a.map_target hp, ?_⟩ + rw [hz] + exact b.map_target hq + have hp' : a (a.symm p) ∈ Ioo p r := hacoord _ hzS + rw [a.right_inv hp] at hp' + exact lt_irrefl p hp'.1 + · intro x hx y hy hxy + have h := hanti (hacoord x hx) (hacoord y hy) hxy + rwa [hkey x hx, hkey y hy] at h + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/GlueNNReal.lean b/LeanPool/OneManifold/OneMfld/GlueNNReal.lean new file mode 100644 index 000000000..bd086202e --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/GlueNNReal.lean @@ -0,0 +1,217 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.GlueCore +public import LeanPool.OneManifold.OneMfld.Normalize + + +/-! # Gluing two charts into an `ℝ≥0`-valued chart + +The shared assembly for the O-H and O-O (connected overlap) cases. Chart `a` is +normalized so its target is `Ioo 0 1` and the overlap image is the full lower +end-segment `Ioo 0 r`; chart `b` sees the overlap as an upper end-segment `Ioo q 1` of +its target. Pick a split value `μ ∈ Ioo q 1`, let `m := b.symm μ` be the split point and +`ρ := a m` its `a`-coordinate; glue `b` (kept as-is on the `b`-side `s`, where +`b ≤ μ`) with the rescaled chart `(μ/ρ) • a` (which agrees with `b` at `m`) using +`OpenPartialHomeomorph.piecewise` along `s := b.source ∩ b⁻¹' (Iic μ)`, `t := Iic μ`. +All the frontier conditions come from `IsImage.frontier` and `frontier_Iic`. +-/ + +@[expose] public section + +namespace OneMfld + +open Set + +/-- **NNReal gluing.** Given charts `a` (target `Ioo 0 1`, overlap image the lower +end-segment `Ioo 0 r`) and `b` (target inside `Iio 1`, overlap image the upper +end-segment `Ioo q 1` with `q` interior), there is a chart on `a.source ∪ b.source` +whose target is `(b.target ∩ Iic μ) ∪ Ioo μ (μ/ρ)` for some split values +`q < μ < 1`, `0 < ρ < 1`. -/ +theorem glue_nnreal {M : Type*} [TopologicalSpace M] [T2Space M] + (a b : OpenPartialHomeomorph M NNReal) + (hat : a.target = Ioo 0 1) + (hbt : b.target ⊆ Iio 1) + {r q : NNReal} + (ha : a '' (a.source ∩ b.source) = Ioo 0 r) (hr0 : 0 < r) (hr1 : r < 1) + (hb : b '' (a.source ∩ b.source) = Ioo q 1) + (hq : q ∈ b.target) : + ∃ f : OpenPartialHomeomorph M NNReal, f.source = a.source ∪ b.source ∧ + ∃ μ ρ : NNReal, q < μ ∧ μ < 1 ∧ 0 < ρ ∧ ρ < 1 ∧ + f.target = (b.target ∩ Iic μ) ∪ Ioo μ (μ/ρ) := by + classical + -- The overlap is nonempty, so `q < 1`. + have hSne : (a.source ∩ b.source).Nonempty := by + have h : (a '' (a.source ∩ b.source)).Nonempty := by + rw [ha]; exact nonempty_Ioo.2 hr0 + exact h.of_image + have hq1 : q < 1 := by + have h : (b '' (a.source ∩ b.source)).Nonempty := hSne.image _ + rw [hb] at h + exact nonempty_Ioo.1 h + -- Pick the split value `μ` and the split point `m`. + obtain ⟨μ, hqμ, hμ1⟩ := exists_between hq1 + have hμ0 : 0 < μ := lt_of_le_of_lt (zero_le : (0 : NNReal) ≤ q) hqμ + obtain ⟨m, hmS, hbm⟩ : ∃ m ∈ a.source ∩ b.source, b m = μ := by + have h : μ ∈ b '' (a.source ∩ b.source) := by rw [hb]; exact ⟨hqμ, hμ1⟩ + obtain ⟨m, hm, hbm⟩ := h + exact ⟨m, hm, hbm⟩ + have hρmem : a m ∈ Ioo 0 r := by rw [← ha]; exact mem_image_of_mem _ hmS + have hρ0 : 0 < a m := hρmem.1 + have hρr : a m < r := hρmem.2 + have hρ1 : a m < 1 := hρr.trans hr1 + have hc : (0 : NNReal) < μ / a m := div_pos hμ0 hρ0 + have hcρ : μ / a m * a m = μ := div_mul_cancel₀ μ hρ0.ne' + -- The rescaled chart `e' = (μ/ρ) • a`. + set e' := a.transHomeomorph (NNReal.mulHomeomorph (μ / a m) hc) with he'def + have he's : e'.source = a.source := rfl + have he'app : ∀ x, e' x = μ / a m * a x := fun _ => rfl + have he't : e'.target = Ioo 0 (μ / a m) := by + rw [he'def, OpenPartialHomeomorph.transHomeomorph_target, hat] + ext y + simp only [mem_preimage, NNReal.mulHomeomorph_symm_apply, mem_Ioo] + constructor + · rintro ⟨h1, h2⟩ + refine ⟨?_, ?_⟩ + · rcases eq_or_lt_of_le (zero_le : (0 : NNReal) ≤ y) with h | h + · rw [← h, mul_zero] at h1; exact absurd h1 (lt_irrefl 0) + · exact h + · have h3 := mul_lt_mul_of_pos_left h2 hc + rwa [mul_inv_cancel_left₀ hc.ne', mul_one] at h3 + · rintro ⟨h1, h2⟩ + refine ⟨mul_pos (inv_pos.2 hc) h1, ?_⟩ + have h3 := mul_lt_mul_of_pos_left h2 (inv_pos.2 hc) + rwa [inv_mul_cancel₀ hc.ne'] at h3 + -- The transition map is increasing. + have hr' : r ∈ a.target := by rw [hat]; exact ⟨hr0, hr1⟩ + have mono := overlap_mono a b ha hb hr' hq + -- Membership in the overlap via the image descriptions. + have hmem_of_a : ∀ x ∈ a.source, a x < r → x ∈ a.source ∩ b.source := by + intro x hx hxr + have h0 : 0 < a x := by + have h := a.map_source hx + rw [hat] at h + exact h.1 + have h : a x ∈ a '' (a.source ∩ b.source) := by rw [ha]; exact ⟨h0, hxr⟩ + obtain ⟨x', hx', hax'⟩ := h + have hxx : x' = x := a.injOn hx'.1 hx hax' + rwa [hxx] at hx' + have hmem_of_b : ∀ x ∈ b.source, q < b x → x ∈ a.source ∩ b.source := by + intro x hx hqx + have h1 : b x < 1 := hbt (b.map_source hx) + have h : b x ∈ b '' (a.source ∩ b.source) := by rw [hb]; exact ⟨hqx, h1⟩ + obtain ⟨x', hx', hbx'⟩ := h + have hxx : x' = x := b.injOn hx'.2 hx hbx' + rwa [hxx] at hx' + -- The side sets for the piecewise gluing. + set s := b.source ∩ b ⁻¹' (Iic μ) with hsdef + -- Key correspondence between the two descriptions of the `b`-side. + have key : ∀ x ∈ a.source, (a x ≤ a m ↔ x ∈ s) := by + intro x hx + constructor + · intro hle + have hxS : x ∈ a.source ∩ b.source := hmem_of_a x hx (lt_of_le_of_lt hle hρr) + refine ⟨hxS.2, ?_⟩ + simp only [mem_preimage, mem_Iic] + rcases lt_or_eq_of_le hle with hlt | heq + · exact le_of_lt (hbm ▸ mono x hxS m hmS hlt) + · have hxm : x = m := a.injOn hx hmS.1 heq + rw [hxm, hbm] + · rintro ⟨hxb, hxμ⟩ + simp only [mem_preimage, mem_Iic] at hxμ + have hxS : x ∈ a.source ∩ b.source := ⟨hx, hxb⟩ + by_contra hgt + rw [not_le] at hgt + have h := mono m hmS x hxS hgt + rw [hbm] at h + exact absurd hxμ (not_le.2 h) + -- `Iic μ` is the image of `s` under both charts. + have H : b.IsImage s (Iic μ) := by + intro x hx + simp only [hsdef, mem_inter_iff, mem_preimage] + exact ⟨fun h => ⟨hx, h⟩, fun h => h.2⟩ + have H' : e'.IsImage s (Iic μ) := by + intro x hx + rw [he's] at hx + rw [he'app x, ← key x hx, mem_Iic] + constructor + · intro h + have h2 : μ / a m * a x ≤ μ / a m * a m := by rw [hcρ]; exact h + exact le_of_mul_le_mul_left h2 hc + · intro h + calc μ / a m * a x ≤ μ / a m * a m := mul_le_mul_of_nonneg_left h (le_of_lt hc) + _ = μ := hcρ + -- Both frontier intersections are the singleton `{m}`. + have hft : frontier (Iic μ) = {μ} := frontier_Iic + have hbfront : b.source ∩ frontier s = {m} := by + have h1 := H.frontier.preimage_eq + rw [hft] at h1 + rw [← h1] + ext x + simp only [mem_inter_iff, mem_preimage, mem_singleton_iff] + constructor + · rintro ⟨hxb, hbx⟩ + exact b.injOn hxb hmS.2 (hbx.trans hbm.symm) + · rintro rfl + exact ⟨hmS.2, hbm⟩ + have he'front : e'.source ∩ frontier s = {m} := by + have h1 := H'.frontier.preimage_eq + rw [hft] at h1 + rw [← h1] + ext x + simp only [mem_inter_iff, mem_preimage, mem_singleton_iff, he's] + constructor + · rintro ⟨hxa, hex⟩ + rw [he'app x] at hex + have h2 : μ / a m * a x = μ / a m * a m := by rw [hcρ]; exact hex + have h3 : a x = a m := mul_left_cancel₀ hc.ne' h2 + exact a.injOn hxa hmS.1 h3 + · rintro rfl + exact ⟨hmS.1, by rw [he'app, hcρ]⟩ + have Hs : b.source ∩ frontier s = e'.source ∩ frontier s := by + rw [hbfront, he'front] + have Heq : EqOn b e' (b.source ∩ frontier s) := by + rw [hbfront] + intro x hx + rw [mem_singleton_iff] at hx + subst hx + rw [hbm, he'app, hcρ] + -- Glue. + refine ⟨b.piecewise e' s (Iic μ) H H' Hs Heq, ?_, μ, a m, hqμ, hμ1, hρ0, hρ1, ?_⟩ + · change Set.ite s b.source e'.source = a.source ∪ b.source + have hite : Set.ite s b.source e'.source = (b.source ∩ s) ∪ (e'.source \ s) := rfl + rw [hite, he's] + apply Subset.antisymm + · rintro x (⟨hxb, -⟩ | ⟨hxa, -⟩) + · exact Or.inr hxb + · exact Or.inl hxa + · rintro x hx + by_cases hxs : x ∈ s + · exact Or.inl ⟨hxs.1, hxs⟩ + · rcases hx with hxa | hxb + · exact Or.inr ⟨hxa, hxs⟩ + · have hμx : μ < b x := by + by_contra hle + rw [not_lt] at hle + exact hxs ⟨hxb, hle⟩ + have hxS := hmem_of_b x hxb (hqμ.trans hμx) + exact Or.inr ⟨hxS.1, hxs⟩ + · change Set.ite (Iic μ) b.target e'.target = (b.target ∩ Iic μ) ∪ Ioo μ (μ / a m) + have hite : Set.ite (Iic μ) b.target e'.target + = (b.target ∩ Iic μ) ∪ (e'.target \ Iic μ) := rfl + rw [hite, he't] + congr 1 + ext y + simp only [mem_sdiff, mem_Ioo, mem_Iic, not_le] + constructor + · rintro ⟨⟨-, h2⟩, h3⟩ + exact ⟨h3, h2⟩ + · rintro ⟨h1, h2⟩ + exact ⟨⟨lt_of_le_of_lt (zero_le : (0 : NNReal) ≤ μ) h1, h2⟩, h1⟩ + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/GlueUI.lean b/LeanPool/OneManifold/OneMfld/GlueUI.lean new file mode 100644 index 000000000..b3287be75 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/GlueUI.lean @@ -0,0 +1,343 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import Mathlib.Topology.OpenPartialHomeomorph.Constructions +public import LeanPool.OneManifold.OneMfld.GlueCore +public import LeanPool.OneManifold.OneMfld.GlueBlocks + + +/-! # Gluing two boundary charts onto the unit interval + +The H-H assembly. Both charts are normalized to target `Iio 1`; each sees the overlap +as an upper end-segment (`Ioo p 1` in `a`, `Ioo q 1` in `b`), and the transition is +decreasing (`overlap_anti`). Embed `b` into the lower half of the unit interval via +`halfOPH` (`x ↦ x/2`) and `a` into the upper piece via a decreasing Möbius map +`mobiusOPH k` (`x ↦ k/(x+k)`), with `k` chosen so the two embeddings agree at the split +point `m := b.symm μ` (that is, `k/(ρ+k) = μ/2` where `ρ := a m`). Glue with +`OpenPartialHomeomorph.piecewise` along `t := {y ≤ μ/2}`; the two targets +`[0, μ/2]` and `(μ/2, 1]` unite to the whole interval. +-/ + +@[expose] public section + +namespace OneMfld + +open Set + +/-- The half-scale and Möbius charts cover the unit interval across their common split level. -/ +private theorem half_mobius_target_cover {M : Type*} [TopologicalSpace M] + (a b : OpenPartialHomeomorph M NNReal) + (eb : OpenPartialHomeomorph NNReal UnitInterval) + (e' : OpenPartialHomeomorph M UnitInterval) + (hat : a.target = Iio 1) (hbt : b.target = Iio 1) + (hbs : eb.source = Iio 1) + (hbt' : eb.target = {y : UnitInterval | (y : ℝ) < 1 / 2}) + (he'src : e'.source = a.source) (μ ρ k : NNReal) + (hkR : (0 : ℝ) < (k : ℝ)) (hμR : (0 : ℝ) < (μ : ℝ)) + (hμ1R : (μ : ℝ) < 1) (hρ1R : (ρ : ℝ) < 1) + (hρkR : (0 : ℝ) < (ρ : ℝ) + (k : ℝ)) + (hkeyR : (k : ℝ) / ((ρ : ℝ) + (k : ℝ)) = (μ : ℝ) / 2) + (he'f : ∀ x : M, (e' x : ℝ) = (k : ℝ) / ((a x : ℝ) + (k : ℝ))) : + (b.trans eb).target ∩ {y : UnitInterval | (y : ℝ) ≤ (μ : ℝ) / 2} ∪ + e'.target \ {y : UnitInterval | (y : ℝ) ≤ (μ : ℝ) / 2} = univ := by + classical + apply eq_univ_of_forall + intro y + by_cases hy : (y : ℝ) ≤ (μ : ℝ) / 2 + · -- lower piece + left + have hyt : y ∈ eb.target := by + rw [hbt'] + change (y : ℝ) < 1 / 2 + linarith + refine ⟨?_, hy⟩ + rw [OpenPartialHomeomorph.trans_target] + refine ⟨hyt, ?_⟩ + rw [mem_preimage, hbt, ← hbs] + exact eb.map_target hyt + · -- upper piece + right + push Not at hy + refine ⟨?_, fun h => absurd h (not_le.2 hy)⟩ + have hy0 : (0 : ℝ) < (y : ℝ) := lt_trans (half_pos hμR) hy + have hy1 : (y : ℝ) ≤ 1 := y.2.2 + -- the point `w := k/y - k` of `a.target` mapping to `y` + have hknn : (k : ℝ) ≤ (k : ℝ) / (y : ℝ) := by + rw [le_div_iff₀ hy0] + nlinarith + have hnn : (0 : ℝ) ≤ (k : ℝ) / (y : ℝ) - (k : ℝ) := by linarith + have h1k : (0 : ℝ) < 1 + (k : ℝ) := by linarith + have hge : (k : ℝ) / (1 + (k : ℝ)) ≤ (k : ℝ) / ((ρ : ℝ) + (k : ℝ)) := by + rw [div_le_div_iff₀ h1k hρkR] + nlinarith + have hygt : (k : ℝ) / (1 + (k : ℝ)) < (y : ℝ) := + lt_of_le_of_lt (hge.trans (le_of_eq hkeyR)) hy + have hlt1 : (k : ℝ) / (y : ℝ) - (k : ℝ) < 1 := by + rw [div_lt_iff₀ h1k] at hygt + rw [sub_lt_iff_lt_add] + rw [div_lt_iff₀ hy0] + nlinarith + set w : NNReal := Real.toNNReal ((k : ℝ) / (y : ℝ) - (k : ℝ)) with hwdef + have hwcoe : (w : ℝ) = (k : ℝ) / (y : ℝ) - (k : ℝ) := Real.coe_toNNReal _ hnn + have hwtarget : w ∈ a.target := by + rw [hat, mem_Iio, ← NNReal.coe_lt_coe, NNReal.coe_one, hwcoe] + exact hlt1 + set x₀ := a.symm w with hx0def + have hx0src : x₀ ∈ a.source := a.map_target hwtarget + have hax0 : a x₀ = w := a.right_inv hwtarget + have hkne : (k : ℝ) ≠ 0 := hkR.ne' + have hyne : (y : ℝ) ≠ 0 := hy0.ne' + have he'x0 : (e' x₀ : ℝ) = (y : ℝ) := by + rw [he'f x₀, hax0, hwcoe, sub_add_cancel] + rw [div_div_eq_mul_div, mul_comm (k : ℝ) (y : ℝ), mul_div_assoc, + div_self hkne, mul_one] + have hexy : e' x₀ = y := Subtype.ext he'x0 + have hx0src' : x₀ ∈ e'.source := by rw [he'src]; exact hx0src + have := e'.map_source hx0src' + rwa [hexy] at this + +/-- The Möbius parameter maps the selected upper-chart coordinate to the half-scale split. -/ +private theorem mobius_split_identity (μ ρ k : NNReal) (h2μ : μ < 2) + (hkdef : k = μ * ρ / (2 - μ)) (hρkR : (0 : ℝ) < (ρ : ℝ) + (k : ℝ)) : + (k : ℝ) / ((ρ : ℝ) + (k : ℝ)) = (μ : ℝ) / 2 := by + have h2μpos : 0 < 2 - μ := tsub_pos_of_lt h2μ + have hkey2 : 2 * k = μ * (ρ + k) := by + have h1 : k * (2 - μ) = μ * ρ := by + rw [hkdef]; exact div_mul_cancel₀ _ h2μpos.ne' + calc 2 * k = ((2 - μ) + μ) * k := by rw [tsub_add_cancel_of_le h2μ.le] + _ = (2 - μ) * k + μ * k := add_mul _ _ _ + _ = μ * ρ + μ * k := by rw [mul_comm (2 - μ) k, h1] + _ = μ * (ρ + k) := (mul_add μ ρ k).symm + have h := congrArg (fun x : NNReal => (x : ℝ)) hkey2 + push_cast at h + rw [div_eq_div_iff hρkR.ne' (two_ne_zero)] + linarith + +/-- **Unit-interval gluing.** Two boundary charts (targets `Iio 1`) whose overlap is an +upper end-segment in each glue to a chart of `M` onto the whole unit interval, with +source `a.source ∪ b.source`. -/ +theorem glue_hh_ui {M : Type*} [TopologicalSpace M] [T2Space M] + (a b : OpenPartialHomeomorph M NNReal) + (hat : a.target = Iio 1) (hbt : b.target = Iio 1) + {p q : NNReal} + (ha : a '' (a.source ∩ b.source) = Ioo p 1) (hp : p < 1) + (hb : b '' (a.source ∩ b.source) = Ioo q 1) : + ∃ f : OpenPartialHomeomorph M UnitInterval, + f.source = a.source ∪ b.source ∧ f.target = univ := by + classical + -- the overlap is nonempty, so `q < 1` + have hS : (a.source ∩ b.source).Nonempty := by + have h1 : (Ioo p (1 : NNReal)).Nonempty := nonempty_Ioo.2 hp + rw [← ha] at h1 + exact h1.of_image + have hq1 : q < 1 := nonempty_Ioo.1 (hb ▸ hS.image b) + -- choose the split level `μ` and its preimage `m` + obtain ⟨μ, hqμ, hμ1⟩ := exists_between hq1 + have hμpos : 0 < μ := lt_of_le_of_lt zero_le hqμ + have hμS : μ ∈ b '' (a.source ∩ b.source) := by rw [hb]; exact ⟨hqμ, hμ1⟩ + obtain ⟨m, hmS, hbm⟩ := hμS + have hma : m ∈ a.source := hmS.1 + have hme : m ∈ b.source := hmS.2 + set ρ := a m with hρdef + have hρmem : ρ ∈ Ioo p 1 := by rw [← ha]; exact mem_image_of_mem a hmS + have hρp : p < ρ := hρmem.1 + have hρ1 : ρ < 1 := hρmem.2 + have hρpos : 0 < ρ := lt_of_le_of_lt zero_le hρp + -- the Möbius parameter `k` and its key identity `k/(ρ+k) = μ/2` + have h2μ : μ < 2 := hμ1.trans one_lt_two + have h2μpos : 0 < 2 - μ := tsub_pos_of_lt h2μ + set k : NNReal := μ * ρ / (2 - μ) with hkdef + have hkpos : 0 < k := div_pos (mul_pos hμpos hρpos) h2μpos + -- real-number versions of the positivity facts and the key identity + have hkR : (0 : ℝ) < (k : ℝ) := NNReal.coe_pos.2 hkpos + have hμR : (0 : ℝ) < (μ : ℝ) := NNReal.coe_pos.2 hμpos + have hρR : (0 : ℝ) < (ρ : ℝ) := NNReal.coe_pos.2 hρpos + have hμ1R : (μ : ℝ) < 1 := by exact_mod_cast hμ1 + have hρ1R : (ρ : ℝ) < 1 := by exact_mod_cast hρ1 + have hρkR : (0 : ℝ) < (ρ : ℝ) + (k : ℝ) := by linarith + have hkeyR : (k : ℝ) / ((ρ : ℝ) + (k : ℝ)) = (μ : ℝ) / 2 := + mobius_split_identity μ ρ k h2μ hkdef hρkR + -- the two building blocks + obtain ⟨eb, hbs, hbt', hbf⟩ := halfOPH + obtain ⟨ea, has, hat', haf, hanti⟩ := mobiusOPH k hkpos + set e := b.trans eb with hedef + set e' := a.trans ea with he'def + have hbmem : ∀ x, x ∈ b.source → b x ∈ Iio (1 : NNReal) := fun x hx => + hbt ▸ b.map_source hx + have hesrc : e.source = b.source := by + rw [hedef, OpenPartialHomeomorph.trans_source, hbs, inter_eq_left] + intro x hx + exact hbmem x hx + have he'src : e'.source = a.source := by + rw [he'def, OpenPartialHomeomorph.trans_source, has, preimage_univ, inter_univ] + have hef : ∀ x ∈ b.source, (e x : ℝ) = (b x : ℝ) / 2 := by + intro x hx + rw [hedef, OpenPartialHomeomorph.trans_apply] + exact hbf (b x) (hbmem x hx) + have he'f : ∀ x : M, (e' x : ℝ) = (k : ℝ) / ((a x : ℝ) + (k : ℝ)) := by + intro x + rw [he'def, OpenPartialHomeomorph.trans_apply] + exact haf (a x) + -- the gluing sets + set tset : Set UnitInterval := {y : UnitInterval | (y : ℝ) ≤ (μ : ℝ) / 2} with htdef + set sset : Set M := b.source ∩ b ⁻¹' (Iic μ) with hsdef + -- the anti correspondence between the two coordinates on the overlap + have hpa : p ∈ a.target := by rw [hat]; exact hp + have hqb : q ∈ b.target := by rw [hbt]; exact hq1 + have anti := overlap_anti a b ha hb hpa hqb + -- `ρ ≤ a x` on `a.source` characterizes `sset` + have hcorr : ∀ x, x ∈ a.source → (ρ ≤ a x ↔ x ∈ sset) := by + intro x hxa + constructor + · intro hle + have hax1 : a x < 1 := by + have h := a.map_source hxa + rwa [hat] at h + have haxS : a x ∈ Ioo p 1 := ⟨hρp.trans_le hle, hax1⟩ + rw [← ha] at haxS + obtain ⟨z, hzS, hz⟩ := haxS + have hzx : z = x := a.injOn hzS.1 hxa hz + subst hzx + rw [hsdef] + refine ⟨hzS.2, ?_⟩ + rcases eq_or_lt_of_le hle with heq | hlt + · have hxm : z = m := a.injOn hxa hma (heq.symm.trans hρdef) + rw [mem_preimage, hxm, hbm] + exact mem_Iic.2 le_rfl + · have h := anti m hmS z hzS (by rw [← hρdef]; exact hlt) + rw [hbm] at h + exact mem_Iic.2 h.le + · intro hxs + rw [hsdef] at hxs + obtain ⟨hxb, hxμ⟩ := hxs + have hxS : x ∈ a.source ∩ b.source := ⟨hxa, hxb⟩ + by_contra hlt + push Not at hlt + have h := anti x hxS m hmS (by rw [← hρdef]; exact hlt) + rw [hbm] at h + rw [mem_preimage, mem_Iic] at hxμ + exact absurd hxμ (not_le.mpr h) + -- `IsImage` for the lower embedding + have H : e.IsImage sset tset := by + intro x hx + rw [hesrc] at hx + have h1 : e x ∈ tset ↔ (b x : ℝ) ≤ (μ : ℝ) := by + rw [htdef, mem_ofPred_eq, hef x hx] + constructor <;> intro h <;> linarith + have h2 : x ∈ sset ↔ b x ≤ μ := by + rw [hsdef] + exact ⟨fun h => h.2, fun h => ⟨hx, h⟩⟩ + rw [h1, h2, NNReal.coe_le_coe] + -- `IsImage` for the upper embedding + have H' : e'.IsImage sset tset := by + intro x hx + rw [he'src] at hx + have haxk : (0 : ℝ) < (a x : ℝ) + (k : ℝ) := + add_pos_of_nonneg_of_pos (a x).coe_nonneg hkR + have h1 : e' x ∈ tset ↔ (k : ℝ) / ((a x : ℝ) + (k : ℝ)) ≤ (μ : ℝ) / 2 := by + rw [htdef, mem_ofPred_eq, he'f x] + have h2 : ((k : ℝ) / ((a x : ℝ) + (k : ℝ)) ≤ (μ : ℝ) / 2) ↔ ρ ≤ a x := by + rw [← hkeyR] + constructor + · intro h + rw [div_le_div_iff₀ haxk hρkR] at h + have h3 : (ρ : ℝ) + (k : ℝ) ≤ (a x : ℝ) + (k : ℝ) := + le_of_mul_le_mul_left h hkR + have h4 : (ρ : ℝ) ≤ (a x : ℝ) := by linarith + exact_mod_cast h4 + · intro h + have h3 : (ρ : ℝ) ≤ (a x : ℝ) := NNReal.coe_le_coe.2 h + rw [div_le_div_iff₀ haxk hρkR] + nlinarith + rw [h1, h2] + exact hcorr x hx + -- the frontier of `tset` is the level `{μ/2}` + have hfront_t : frontier tset = {y : UnitInterval | (y : ℝ) = (μ : ℝ) / 2} := by + rw [htdef] + exact frontier_UIIic (half_pos hμR) (by linarith) + -- both sources meet the frontier of `sset` exactly at `m` + have hfr_e : e.source ∩ frontier sset = {m} := by + rw [← H.frontier.preimage_eq, hfront_t, hesrc] + ext x + simp only [mem_inter_iff, mem_preimage, mem_ofPred_eq, mem_singleton_iff] + constructor + · rintro ⟨hxb, hx2⟩ + rw [hef x hxb] at hx2 + have hbx : b x = μ := by + have h : (b x : ℝ) = (μ : ℝ) := by linarith + exact_mod_cast h + exact b.injOn hxb hme (hbx.trans hbm.symm) + · intro hxm + rw [hxm] + exact ⟨hme, by rw [hef m hme, hbm]⟩ + have hfr_e' : e'.source ∩ frontier sset = {m} := by + rw [← H'.frontier.preimage_eq, hfront_t, he'src] + ext x + simp only [mem_inter_iff, mem_preimage, mem_ofPred_eq, mem_singleton_iff] + constructor + · rintro ⟨hxa, hx2⟩ + rw [he'f x, ← hkeyR] at hx2 + have haxk : (0 : ℝ) < (a x : ℝ) + (k : ℝ) := + add_pos_of_nonneg_of_pos (a x).coe_nonneg hkR + rw [div_eq_div_iff haxk.ne' hρkR.ne'] at hx2 + have h4 : (ρ : ℝ) + (k : ℝ) = (a x : ℝ) + (k : ℝ) := + mul_left_cancel₀ hkR.ne' hx2 + have h5 : a x = ρ := by + have h : (a x : ℝ) = (ρ : ℝ) := by linarith + exact_mod_cast h + exact a.injOn hxa hma (by rw [h5, hρdef]) + · intro hxm + rw [hxm] + refine ⟨hma, ?_⟩ + rw [he'f m, ← hρdef] + exact hkeyR + have Hs : e.source ∩ frontier sset = e'.source ∩ frontier sset := + hfr_e.trans hfr_e'.symm + -- the two embeddings agree at the frontier point `m` + have Heq : Set.EqOn e e' (e.source ∩ frontier sset) := by + rw [hfr_e] + intro x hx + rw [mem_singleton_iff] at hx + subst hx + apply Subtype.ext + rw [hef x hme, hbm, he'f x, ← hρdef, hkeyR] + -- glue + set f := e.piecewise e' sset tset H H' Hs Heq with hfdef + refine ⟨f, ?_, ?_⟩ + · -- source computation + have hfs : f.source = e.source ∩ sset ∪ e'.source \ sset := rfl + rw [hfs, hesrc, he'src] + apply subset_antisymm + · rintro x (⟨hxb, _⟩ | ⟨hxa, _⟩) + · exact Or.inr hxb + · exact Or.inl hxa + · intro x hx + by_cases hxs : x ∈ sset + · have hxb : x ∈ b.source := by + rw [hsdef] at hxs + exact hxs.1 + exact Or.inl ⟨hxb, hxs⟩ + · rcases hx with hxa | hxb + · exact Or.inr ⟨hxa, hxs⟩ + · -- `x ∈ b.source \ sset` implies `x` lies in the overlap + have hμbx : μ < b x := by + by_contra hle + push Not at hle + exact hxs (by rw [hsdef]; exact ⟨hxb, mem_Iic.2 hle⟩) + have hbx1 : b x < 1 := hbmem x hxb + have hbIoo : b x ∈ Ioo q 1 := ⟨hqμ.trans hμbx, hbx1⟩ + rw [← hb] at hbIoo + obtain ⟨z, hzS, hz⟩ := hbIoo + have hzx : z = x := b.injOn hzS.2 hxb hz + subst hzx + exact Or.inr ⟨hzS.1, hxs⟩ + · change (b.trans eb).target ∩ tset ∪ e'.target \ tset = univ + exact half_mobius_target_cover a b eb e' hat hbt hbs hbt' he'src μ ρ k + hkR hμR hμ1R hρ1R hρkR hkeyR he'f + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/IntervalCharts.lean b/LeanPool/OneManifold/OneMfld/IntervalCharts.lean new file mode 100644 index 000000000..63327680f --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/IntervalCharts.lean @@ -0,0 +1,75 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.ClassifyInterval +public import LeanPool.OneManifold.OneMfld.NiceCharts + + +/-! +# IntervalCharts + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +/-- A space charted on the half-line with interval targets. -/ +class IntervalChartedSpace (M : Type*) [TopologicalSpace M] extends ChartedSpace NNReal M where + is_interval (φ : OpenPartialHomeomorph M NNReal) (h : φ ∈ atlas) : (∃ x y, (Set.Ioo x y = + φ.target)) ∨ (∃ x, (Set.Iio x = φ.target)) + +variable + {M : Type*} + [TopologicalSpace M] + +theorem ioi_unbounded (x : NNReal) : ¬ Bornology.IsBounded (Set.Ioi x) := by + intro h + have h' : BddBelow (Set.Ioi x) ∧ BddAbove (Set.Ioi x) := + (isBounded_iff_bddBelow_bddAbove (s := Set.Ioi x)).1 h + exact (not_bddAbove_Ioi x) h'.2 + +theorem univ_unbounded : ¬ Bornology.IsBounded (Set.univ : Set NNReal) := by + intro h + -- On an `IsOrderBornology`, bounded ⇔ bddBelow ∧ bddAbove. + have h' : + BddBelow (Set.univ : Set NNReal) ∧ + BddAbove (Set.univ : Set NNReal) := + (isBounded_iff_bddBelow_bddAbove + (s := (Set.univ : Set NNReal))).1 h + -- But `univ` is not bounded above on a `NoTopOrder` type like `NNReal`. + exact not_bddAbove_univ h'.2 + +/-- Regard an atlas with bounded connected targets as an interval atlas. -/ +@[instance_reducible] noncomputable def intervalCharted (ht : NicelyChartedSpace NNReal M) : + IntervalChartedSpace M where + chartAt := ht.chartAt + atlas := ht.atlas + mem_chart_source := ht.mem_chart_source + chart_mem_atlas := ht.chart_mem_atlas + is_interval (φ : OpenPartialHomeomorph M NNReal) := by + intro h + have bounded := ht.is_bounded φ h + have connected := ht.is_connected φ h + have c := classify_connected_nnreal_interval (φ.target) (φ.open_target) + (isConnected_iff_connectedSpace.mpr connected) + rcases c with (h|h|h|h) + · left; assumption + · right; assumption + · exfalso + rcases h with ⟨x,h⟩ + apply ioi_unbounded x + rw [h] + exact bounded + · exfalso + apply univ_unbounded + rw [←h] + exact bounded + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/LocallyConnected.lean b/LeanPool/OneManifold/OneMfld/LocallyConnected.lean new file mode 100644 index 000000000..c7822c35c --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/LocallyConnected.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Analysis.InnerProductSpace.Basic +public import Mathlib.Analysis.Normed.Order.Lattice +public import Mathlib.Tactic + + +/-! +# LocallyConnected + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +instance : LocallyConnectedSpace NNReal := by + rw [locallyConnectedSpace_iff_connected_subsets] + intro x U xU + rw [NNReal.isEmbedding_coe.nhds_eq_comap x] at xU + simp only [Filter.mem_comap] at xU + let ⟨t, x_t, t_U⟩ := xU + rw [mem_nhds_iff_exists_Ioo_subset] at x_t + let ⟨l, u, x_ul, ul_t⟩ := x_t + rw [Set.Ioo] at x_ul + dsimp at x_ul + have l_lt_u : l < u := lt_trans x_ul.1 x_ul.2 + have upos : 0 < u := lt_of_lt_of_le' (x_ul.2) x.coe_nonneg + let V := {y : NNReal | l < ↑y ∧ ↑y < u} + use V + constructor + · rw [NNReal.isEmbedding_coe.nhds_eq_comap x] + simp only [Filter.mem_comap] + use Set.Ioo l u + constructor + · exact Ioo_mem_nhds x_ul.1 x_ul.2 + · intro a a_lu + dsimp [V] + rw [Set.preimage, Set.Ioo] at a_lu + dsimp at a_lu + exact a_lu + constructor + -- . apply Convex.IsConnected + · apply IsConnected.isPreconnected + apply IsPathConnected.isConnected + rw [IsPathConnected] + let z := ((max 0 l) + u) / 2 + have znonneg : 0 ≤ z := by + have nn₁ : 0 ≤ (max 0 l) := le_max_left 0 l + have nn₂ : 0 ≤ u := LT.lt.le upos + have : 0 ≤ (max 0 l) + u := add_nonneg nn₁ nn₂ + exact div_nonneg this zero_le_two + let z' : NNReal := NNReal.mk z znonneg + have l_lt_z : l < z := by + have : l ≤ max 0 l := le_max_right 0 l + change l < (max 0 l + u) / 2 + linarith + have z_lt_u : z < u := by + have : max 0 l < u := max_lt_iff.mpr ⟨upos, l_lt_u⟩ + change (max 0 l + u) / 2 < u + linarith + have : z' ∈ V := ⟨l_lt_z, z_lt_u⟩ + use z' + use this + intro y yV + dsimp [V] at yV + rw [JoinedIn] + let γ : Path z' y := { + toFun := fun t ↦ NNReal.mk ((unitInterval.symm t)*z' + t*y) + (add_nonneg (mul_nonneg unitInterval.nonneg' znonneg) + (mul_nonneg unitInterval.nonneg' y.coe_nonneg)) + continuous_toFun := by + apply Continuous.subtype_mk + simp only [unitInterval.coe_symm_eq] + fun_prop + source' := by + simp only [unitInterval.symm_zero, Set.Icc.coe_one, one_mul, + Set.Icc.coe_zero, zero_mul, add_zero] + exact rfl + target' := by + simp only [unitInterval.symm_one, Set.Icc.coe_zero, zero_mul, Set.Icc.coe_one, one_mul, + zero_add] + trivial + } + use γ + intro t + dsimp [V, γ] + constructor + · change l < ↑(unitInterval.symm t) * ((max 0 l + u) / 2) + ↑t * ↑y + by_cases tpos : t > 0 + · calc + l = ↑(unitInterval.symm t) * l + t * l := by + simp only [unitInterval.coe_symm_eq] + ring + _ < ↑(unitInterval.symm t) * l + t * y := by + have : t * l < t * y := mul_lt_mul_of_pos_left yV.1 tpos + linarith + _ ≤ ↑(unitInterval.symm t) * z + t * y := by + have : ↑(unitInterval.symm t) * l ≤ ↑(unitInterval.symm t) * z + := mul_le_mul_of_nonneg_left (le_of_lt l_lt_z) unitInterval.nonneg' + linarith + · have : t = 0 := le_antisymm (not_lt.mp tpos) (unitInterval.nonneg') + rw [this] + simp only [unitInterval.symm_zero, Set.Icc.coe_one, one_mul, Set.Icc.coe_zero, zero_mul, + add_zero, gt_iff_lt] + exact l_lt_z + · change ↑(unitInterval.symm t) * ((max 0 l + u) / 2) + ↑t * ↑y < u + by_cases tpos : t > 0 + · calc + ↑(unitInterval.symm t) * ((max 0 l + u) / 2) + ↑t * ↑y + < ↑(unitInterval.symm t) * ((max 0 l + u) / 2) + ↑t * u := by + have : ↑t * ↑y < ↑t * u := mul_lt_mul_of_pos_left yV.2 tpos + linarith + _ ≤ ↑(unitInterval.symm t) * u + ↑t * u := by + have : ↑(unitInterval.symm t) * ((max 0 l + u) / 2) ≤ ↑(unitInterval.symm t) * u + := mul_le_mul_of_nonneg_left (le_of_lt z_lt_u) unitInterval.nonneg' + linarith + _ = u := by rw [unitInterval.coe_symm_eq]; ring + · have : t = 0 := le_antisymm (not_lt.mp tpos) (unitInterval.nonneg') + rw [this] + simp only [unitInterval.symm_zero, Set.Icc.coe_one, one_mul, Set.Icc.coe_zero, zero_mul, + add_zero, gt_iff_lt] + exact z_lt_u + · intro a aV + dsimp [V] at aV + apply t_U + rw [Set.preimage] + dsimp + apply ul_t + rw [Set.Ioo] + exact aV + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/NiceCharts.lean b/LeanPool/OneManifold/OneMfld/NiceCharts.lean new file mode 100644 index 000000000..2ccd7353f --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/NiceCharts.lean @@ -0,0 +1,154 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Algebra.Order.Star.Real +public import Mathlib.Geometry.Manifold.ChartedSpace +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.LocallyConnected +public import LeanPool.OneManifold.OneMfld.PartialHomeomorphHelpers + + +/-! +# NiceCharts + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +variable + {M : Type*} + [TopologicalSpace M] + +/-- Restrict a chart around the specified point to a bounded target. -/ +noncomputable def improvedChart (φ : OpenPartialHomeomorph M NNReal) (x : M) (h : x ∈ φ.source) : + { ψ : OpenPartialHomeomorph M NNReal | (x ∈ ψ.source ∧ Bornology.IsBounded ψ.target) } := by + let y := φ x + let t := φ.target ∩ Set.Iio (y + 1) + let s := φ.symm '' t + have sOpen : IsOpen s := + (φ.isOpen_symm_image_iff_of_subset_target Set.inter_subset_left).mpr + (φ.open_target.inter isOpen_Iio) + refine ⟨φ.restrOpen s sOpen, ?_, ?_⟩ + · exact ⟨h, ⟨y, ⟨φ.map_source h, lt_add_one y⟩, φ.left_inv h⟩⟩ + · apply (Metric.isBounded_Ico (0 : NNReal) (y + 1)).subset + intro z hz + have hz' : z ∈ φ.target ∩ ↑φ.symm ⁻¹' (↑φ.symm '' t) := hz + rw [restrOpen_symm_image_target φ (t := t) Set.inter_subset_left] at hz' + exact ⟨zero_le, hz'.2⟩ + +/-- Restrict a bounded chart to the connected component containing the specified point. -/ +noncomputable def improvedChart' (φ : OpenPartialHomeomorph M NNReal) (x : M) (h : x ∈ φ.source) + (bounded : Bornology.IsBounded φ.target) : + { ψ : OpenPartialHomeomorph M NNReal | x ∈ ψ.source ∧ Bornology.IsBounded ψ.target ∧ + ConnectedSpace ψ.target } := by + let y := φ.toFun x + let t := connectedComponentIn φ.target y + let s := φ.symm '' t + let sOpen : IsOpen s := by + apply OpenPartialHomeomorph.isOpen_image_symm_of_subset_target φ + · apply IsOpen.connectedComponentIn + exact φ.open_target + · exact connectedComponentIn_subset φ.target y + let ψ := φ.restrOpen s sOpen + use ψ + apply And.intro + · dsimp [ψ] + simp only [Set.mem_inter_iff] + apply And.intro + · exact h + · dsimp [s] + simp only [Set.mem_image] + use y + apply And.intro + · apply mem_connectedComponentIn + exact PartialEquiv.map_source φ.toPartialEquiv h + · exact OpenPartialHomeomorph.left_inv φ h + · apply And.intro + · have : ψ.target ⊆ φ.target := by + intro z hz + exact Set.mem_of_mem_inter_left hz + exact Bornology.IsBounded.subset bounded this + · dsimp [ψ] + apply isConnected_iff_connectedSpace.mp + dsimp [s] + have : (φ.target ∩ ↑φ.symm ⁻¹' (↑φ.symm '' t)) = t := + restrOpen_symm_image_target φ (t := t) (connectedComponentIn_subset φ.target y) + rw [this] + apply isConnected_connectedComponentIn_iff.mpr + exact PartialEquiv.map_source φ.toPartialEquiv h + +/-- Choose a chart around the specified point with a bounded connected target. -/ +noncomputable def niceChart (φ : OpenPartialHomeomorph M NNReal) (x : M) (h : x ∈ φ.source) : + { ψ : OpenPartialHomeomorph M NNReal | x ∈ ψ.source ∧ Bornology.IsBounded ψ.target ∧ + ConnectedSpace ψ.target } := by + rcases (improvedChart φ x h) with ⟨ φ', h1, h2 ⟩ + rcases (improvedChart' φ' x h1 h2) with ⟨ φ'', h1, h2, h3 ⟩ + use φ'' + simp only [Set.mem_ofPred_eq] + exact ⟨ h1, ⟨ h2, h3 ⟩ ⟩ + +lemma nice_chart_source {φ : OpenPartialHomeomorph M NNReal} {x : M} {h : x ∈ φ.source} : + x ∈ (niceChart φ x h).1.source := by + let c := niceChart φ x h + have : c = niceChart φ x h := rfl + rw [←this] + exact c.2.1 + +lemma nice_chart_bounded {φ : OpenPartialHomeomorph M NNReal} {x : M} {h : x ∈ φ.source} : + Bornology.IsBounded (niceChart φ x h).1.target := by + let c := niceChart φ x h + have : c = niceChart φ x h := rfl + rw [←this] + exact c.2.2.1 + +lemma nice_chart_connected {φ : OpenPartialHomeomorph M NNReal} {x : M} {h : x ∈ φ.source} : + ConnectedSpace (niceChart φ x h).1.target := by + let c := niceChart φ x h + have : c = niceChart φ x h := rfl + rw [←this] + exact c.2.2.2 + +/-- A charted space whose atlas has bounded connected targets. -/ +class NicelyChartedSpace (H : Type*) [TopologicalSpace H] [Bornology H] (M : Type*) + [TopologicalSpace M] extends ChartedSpace H M where + is_bounded (φ : OpenPartialHomeomorph M H) (h : φ ∈ atlas) : Bornology.IsBounded φ.target + is_connected (φ : OpenPartialHomeomorph M H) (h : φ ∈ atlas) : ConnectedSpace φ.target + +/-- Replace an atlas by one with bounded connected chart targets. -/ +@[instance_reducible] noncomputable def nicelyCharted (ht : ChartedSpace NNReal M) : + NicelyChartedSpace NNReal M where + chartAt := by + intro x + let c := ht.chartAt x + let c' := niceChart (ht.chartAt x) x (ht.mem_chart_source x) + exact c'.1 + atlas := let f x := (niceChart (ht.chartAt x) x (ht.mem_chart_source x)).1 + Set.image f (Set.univ : Set M) + mem_chart_source (x : M) := by + simp only [Set.mem_ofPred_eq] + exact nice_chart_source + chart_mem_atlas (x : M) := by + simp only [Set.mem_ofPred_eq, Set.image_univ, Set.mem_range, exists_apply_eq_apply] + is_bounded (φ : OpenPartialHomeomorph M NNReal) := by + intro h + dsimp [atlas] at h + simp only [Set.image_univ, Set.mem_range] at h + rcases h with ⟨y, hy⟩ + rw [←hy] + apply nice_chart_bounded + is_connected (φ : OpenPartialHomeomorph M NNReal) := by + intro h + dsimp [atlas] at h + simp only [Set.image_univ, Set.mem_range] at h + rcases h with ⟨y, hy⟩ + rw [←hy] + apply nice_chart_connected + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/Noncompact.lean b/LeanPool/OneManifold/OneMfld/Noncompact.lean new file mode 100644 index 000000000..f39b1c219 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/Noncompact.lean @@ -0,0 +1,95 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Analysis.Normed.Order.Lattice +public import Mathlib.Tactic + + +/-! +# Noncompact + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +open Set + +/-- `[0, ∞) ⊆ ℝ` is not compact. -/ +lemma not_isCompact_Ici_zero_real : ¬ IsCompact (Ici (0 : ℝ)) := by + classical + intro hIci0 + -- Open cover U n = (-∞, n) + let U : ℕ → Set ℝ := fun n => Iio (n : ℝ) + have hUopen : ∀ n, IsOpen (U n) := fun _ => isOpen_Iio + -- This open cover really does cover `[0, ∞)`. + have hCover : Ici (0 : ℝ) ⊆ ⋃ n : ℕ, U n := by + intro x hx + -- For any real x, there exists n : ℕ with x < n. + obtain ⟨n, hn⟩ := exists_nat_gt x + refine mem_iUnion.mpr ?_ + exact ⟨n, hn⟩ + -- By compactness, we get a finite subcover. + obtain ⟨s, hs⟩ := hIci0.elim_finite_subcover U hUopen hCover + -- s : Finset ℕ, hs : Ici (0 : ℝ) ⊆ ⋃ n ∈ s, U n + + -- Let N be the supremum (i.e., max) of the finite set s. + let N : ℕ := s.sup id + -- N (as a real) is in `[0, ∞)`. + have hNmem : (N : ℝ) ∈ Ici (0 : ℝ) := by + have : (0 : ℝ) ≤ N := by exact_mod_cast Nat.zero_le N + exact this + -- But N is *not* in the finite union ⋃ n ∈ s, U n. + have hNnot : (N : ℝ) ∉ ⋃ n ∈ s, U n := by + intro hmem + -- From membership in the union, pick some n ∈ s with (N : ℝ) < n. + rcases mem_iUnion.mp hmem with ⟨n, hmem'⟩ + rcases mem_iUnion.mp hmem' with ⟨hn_in_s, hmemU⟩ + -- hmemU says (N : ℝ) < n, since U n = Iio n. + have hlt : (N : ℝ) < n := hmemU + -- But by definition of N as sup over s, every n ∈ s satisfies n ≤ N. + have hle : n ≤ N := by + -- `le_sup` says `id n ≤ s.sup id` for n ∈ s. + simpa [N] using (Finset.le_sup (s := s) (f := id) hn_in_s) + -- So we get (N : ℝ) < n ≤ N, hence (N : ℝ) < (N : ℝ), contradiction. + have hcontr : (N : ℝ) < (N : ℝ) := + lt_of_lt_of_le hlt (by exact_mod_cast hle) + exact lt_irrefl _ hcontr + -- But hs says the finite subcover covers `[0, ∞)`, so it must contain N. + exact hNnot (hs hNmem) + +/-- `NNReal` (the nonnegative reals with the induced topology) is not a compact space. -/ +theorem not_compactSpace_NNReal : ¬ CompactSpace NNReal := by + intro h + -- Use the assumed instance `CompactSpace NNReal`. + have : CompactSpace NNReal := h + -- The whole space is compact. + have hK : IsCompact (univ : Set NNReal) := isCompact_univ + -- Consider the inclusion `NNReal → ℝ`. + have hImage : + IsCompact ((fun x : NNReal => (x : ℝ)) '' (univ : Set NNReal)) := + hK.image (by + -- continuity of the inclusion + simpa using NNReal.continuous_coe) + -- Identify the image with `[0, ∞)` as a subset of ℝ. + have hImage_eq : ((fun x : NNReal => (x : ℝ)) '' (univ : Set NNReal)) + = Ici (0 : ℝ) := by + ext x; constructor + · -- forward direction: any `x = ↑y` with y ≥ 0 lies in `[0,∞)` + rintro ⟨y, -, rfl⟩ + exact y.coe_nonneg + · -- backward: any real x ≥ 0 comes from some nnreal y with y.val = x + intro hx + exact ⟨NNReal.mk x hx, trivial, rfl⟩ + have hIci0 : IsCompact (Ici (0 : ℝ)) := by + simpa [hImage_eq] using hImage + -- Contradiction with the previous lemma. + exact not_isCompact_Ici_zero_real hIci0 + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/Normalize.lean b/LeanPool/OneManifold/OneMfld/Normalize.lean new file mode 100644 index 000000000..38281a87b --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/Normalize.lean @@ -0,0 +1,193 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Algebra.Order.Star.Real +public import Mathlib.Tactic +public import Mathlib.Topology.OpenPartialHomeomorph.Constructions +public import LeanPool.OneManifold.OneMfld.Charts + + +/-! # Normalization of interval charts + +Affine tools for putting interval charts into standard position without changing their +sources: an `HChart` can be rescaled so its target is `Iio 1`, an `OChart` so its target +is `Ioo 0 1`, and an `OChart` with target `Ioo 0 1` can be orientation-reversed +(`x ↦ 1 - x`). H-charts cannot be flipped: the closed end at `0` is a boundary point. +-/ + +@[expose] public section + +namespace OneMfld + +open Set + +noncomputable section + +/-- Multiplication by a positive constant, as a self-homeomorphism of `ℝ≥0`. -/ +def NNReal.mulHomeomorph (c : NNReal) (hc : 0 < c) : NNReal ≃ₜ NNReal where + toFun x := c * x + invFun y := c⁻¹ * y + left_inv := by + intro x + exact inv_mul_cancel_left₀ hc.ne' x + right_inv := by + intro y + exact mul_inv_cancel_left₀ hc.ne' y + continuous_toFun := by + exact continuous_const.mul continuous_id + continuous_invFun := by + exact continuous_const.mul continuous_id + +@[simp] lemma NNReal.mulHomeomorph_apply (c : NNReal) (hc : 0 < c) (x : NNReal) : + NNReal.mulHomeomorph c hc x = c * x := rfl + +@[simp] lemma NNReal.mulHomeomorph_symm_apply (c : NNReal) (hc : 0 < c) (y : NNReal) : + (NNReal.mulHomeomorph c hc).symm y = c⁻¹ * y := rfl + +/-- The affine map `x ↦ (x - u) / (v - u)`, as an `OpenPartialHomeomorph ℝ≥0 ℝ≥0` with +source `Ioo u v` and target `Ioo 0 1`. -/ +def affineIooOPH (u v : NNReal) (h : u < v) : + { e : OpenPartialHomeomorph NNReal NNReal // + e.source = Ioo u v ∧ e.target = Ioo 0 1 ∧ + ∀ x ∈ Ioo u v, e x = (x - u) / (v - u) } := by + have hvu : 0 < v - u := tsub_pos_of_lt h + refine ⟨{ toFun := fun x => (x - u) / (v - u) + invFun := fun y => u + y * (v - u) + source := Ioo u v + target := Ioo 0 1 + map_source' := ?_ + map_target' := ?_ + left_inv' := ?_ + right_inv' := ?_ + open_source := isOpen_Ioo + open_target := isOpen_Ioo + continuousOn_toFun := + ((continuous_id.sub continuous_const).div_const _).continuousOn + continuousOn_invFun := + (continuous_const.add (continuous_id.mul continuous_const)).continuousOn }, + rfl, rfl, fun x _ => rfl⟩ + · intro x hx + exact ⟨div_pos (tsub_pos_of_lt hx.1) hvu, + (div_lt_one hvu).mpr (tsub_lt_tsub_right_of_le hx.1.le hx.2)⟩ + · intro y hy + exact ⟨lt_add_of_pos_right u (mul_pos hy.1 hvu), + lt_tsub_iff_left.mp (mul_lt_of_lt_one_left hvu hy.2)⟩ + · intro x hx + show u + (x - u) / (v - u) * (v - u) = x + rw [div_mul_cancel₀ _ hvu.ne', add_tsub_cancel_of_le hx.1.le] + · intro y _ + show (u + y * (v - u) - u) / (v - u) = y + rw [add_tsub_cancel_left, mul_div_cancel_right₀ _ hvu.ne'] + +/-- The reflection `x ↦ 1 - x`, as an `OpenPartialHomeomorph ℝ≥0 ℝ≥0` with source and +target `Ioo 0 1`. -/ +def reflectIooOPH : + { e : OpenPartialHomeomorph NNReal NNReal // + e.source = Ioo 0 1 ∧ e.target = Ioo 0 1 ∧ + ∀ x ∈ Ioo (0 : NNReal) 1, e x = 1 - x } := by + refine ⟨{ toFun := fun x => 1 - x + invFun := fun y => 1 - y + source := Ioo 0 1 + target := Ioo 0 1 + map_source' := ?_ + map_target' := ?_ + left_inv' := ?_ + right_inv' := ?_ + open_source := isOpen_Ioo + open_target := isOpen_Ioo + continuousOn_toFun := (continuous_const.sub continuous_id).continuousOn + continuousOn_invFun := (continuous_const.sub continuous_id).continuousOn }, + rfl, rfl, fun x _ => rfl⟩ + · intro x hx + exact ⟨tsub_pos_of_lt hx.2, tsub_lt_self one_pos hx.1⟩ + · intro y hy + exact ⟨tsub_pos_of_lt hy.2, tsub_lt_self one_pos hy.1⟩ + · intro x hx + exact tsub_tsub_cancel_of_le hx.2.le + · intro y hy + exact tsub_tsub_cancel_of_le hy.2.le + +variable {M : Type*} [TopologicalSpace M] + +/-- Rescale an `HChart` so its target is `Iio 1`, without changing its source. -/ +def HChart.rescale (a : HChart M) (hne : a.source.Nonempty) : + { b : HChart M // b.source = a.source ∧ b.target = Iio 1 ∧ + ∃ v : NNReal, 0 < v ∧ a.target = Iio v ∧ ∀ x ∈ a.source, b.toFun x = v⁻¹ * a.toFun x } := by + let v := a.target_iio.choose + have hv : Iio v = a.target := a.target_iio.choose_spec + have hvpos : 0 < v := by + obtain ⟨y, hy⟩ := chart_target_nonempty a.toOpenPartialHomeomorph hne + rw [← hv] at hy + exact lt_of_le_of_lt zero_le hy + have hipos : 0 < v⁻¹ := inv_pos.mpr hvpos + have htarget : + (a.toOpenPartialHomeomorph.transHomeomorph (NNReal.mulHomeomorph v⁻¹ hipos)).target + = Iio 1 := by + rw [OpenPartialHomeomorph.transHomeomorph_target, ← hv] + ext y + simp only [mem_preimage, NNReal.mulHomeomorph_symm_apply, mem_Iio, inv_inv] + exact mul_lt_iff_lt_one_right hvpos + exact ⟨⟨a.toOpenPartialHomeomorph.transHomeomorph (NNReal.mulHomeomorph v⁻¹ hipos), + ⟨1, htarget.symm⟩⟩, + rfl, htarget, ⟨v, hvpos, hv.symm, fun x _ => rfl⟩⟩ + +/-- Rescale an `OChart` so its target is `Ioo 0 1`, without changing its source. -/ +def OChart.rescale (a : OChart M) (hne : a.source.Nonempty) : + { b : OChart M // b.source = a.source ∧ b.target = Ioo 0 1 ∧ + ∃ u v : NNReal, u < v ∧ a.target = Ioo u v ∧ + ∀ x ∈ a.source, b.toFun x = (a.toFun x - u) / (v - u) } := by + let u := a.target_ioo.choose + let v := a.target_ioo.choose_spec.choose + have huv : Ioo u v = a.target := a.target_ioo.choose_spec.choose_spec + have h : u < v := by + obtain ⟨y, hy⟩ := chart_target_nonempty a.toOpenPartialHomeomorph hne + rw [← huv] at hy + exact hy.1.trans hy.2 + obtain ⟨e, hes, het, hef⟩ := affineIooOPH u v h + have hsource : (a.toOpenPartialHomeomorph.trans e).source = a.source := by + rw [OpenPartialHomeomorph.trans_source, hes] + refine inter_eq_left.mpr fun x hx => ?_ + rw [mem_preimage, huv] + exact a.toOpenPartialHomeomorph.map_source hx + have htarget : (a.toOpenPartialHomeomorph.trans e).target = Ioo 0 1 := by + rw [OpenPartialHomeomorph.trans_target, het] + refine inter_eq_left.mpr fun y hy => ?_ + rw [mem_preimage, ← huv, ← hes] + exact e.map_target (by rw [het]; exact hy) + refine ⟨⟨a.toOpenPartialHomeomorph.trans e, ⟨0, 1, htarget.symm⟩⟩, hsource, htarget, + ⟨u, v, h, huv.symm, fun x hx => ?_⟩⟩ + have hmem : a.toFun x ∈ Ioo u v := by + rw [huv] + exact a.toOpenPartialHomeomorph.map_source hx + exact hef _ hmem + +/-- Reverse the orientation of an `OChart` with target `Ioo 0 1`, without changing its +source. -/ +def OChart.flip (a : OChart M) (h01 : a.target = Ioo 0 1) : + { b : OChart M // b.source = a.source ∧ b.target = Ioo 0 1 ∧ + ∀ x ∈ a.source, b.toFun x = 1 - a.toFun x } := by + obtain ⟨e, hes, het, hef⟩ := reflectIooOPH + have hsource : (a.toOpenPartialHomeomorph.trans e).source = a.source := by + rw [OpenPartialHomeomorph.trans_source, hes] + refine inter_eq_left.mpr fun x hx => ?_ + rw [mem_preimage, ← h01] + exact a.toOpenPartialHomeomorph.map_source hx + have htarget : (a.toOpenPartialHomeomorph.trans e).target = Ioo 0 1 := by + rw [OpenPartialHomeomorph.trans_target, het] + refine inter_eq_left.mpr fun y hy => ?_ + rw [mem_preimage, h01, ← hes] + exact e.map_target (by rw [het]; exact hy) + refine ⟨⟨a.toOpenPartialHomeomorph.trans e, ⟨0, 1, htarget.symm⟩⟩, hsource, htarget, + fun x hx => ?_⟩ + have hmem : a.toFun x ∈ Ioo (0 : NNReal) 1 := by + rw [← h01] + exact a.toOpenPartialHomeomorph.map_source hx + exact hef _ hmem + +end + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/Outer.lean b/LeanPool/OneManifold/OneMfld/Outer.lean new file mode 100644 index 000000000..1f66d81e9 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/Outer.lean @@ -0,0 +1,373 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.ClosureOverlap +public import LeanPool.OneManifold.OneMfld.LocallyConnected +public import LeanPool.OneManifold.OneMfld.PartialHomeomorphHelpers +public import LeanPool.OneManifold.OneMfld.ClassifyInterval + + +/-! # The outer-overlap lemma + +The structural heart of the classification: when two interval charts `U`, `V` overlap +(neither source containing the other), the image under `U` of any connected component of +`U.source ∩ V.source` is an *end-segment* of `U.target` — its closure reaches an open +endpoint of the target. The mechanism: if the closure of the image stayed inside the +target, the component's closure in `M` would be trapped inside `U.source` (a compactness +argument), contradicting `nonempty_closure_inter_diff`, which forces the component's +closure to escape into `V.source \ U.source`. +-/ + +@[expose] public section + +namespace OneMfld + +open Set + +variable {M : Type*} [TopologicalSpace M] + +section ComponentTransport + +/-- A partial homeomorphism maps the connected component of `x` in a subset `S` of its +source onto the connected component of `φ x` in `φ '' S`. -/ +lemma OpenPartialHomeomorph.image_connectedComponentIn + {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] + (φ : OpenPartialHomeomorph X Y) {S : Set X} (hS : S ⊆ φ.source) + {x : X} (hx : x ∈ S) : + φ '' connectedComponentIn S x = connectedComponentIn (φ '' S) (φ x) := by + have hST : φ '' S ⊆ φ.target := by + rintro y ⟨z, hz, rfl⟩ + exact φ.map_source (hS hz) + apply subset_antisymm + · have hW : IsConnected (connectedComponentIn S x) := + isConnected_connectedComponentIn_iff.mpr hx + have hWS : connectedComponentIn S x ⊆ S := connectedComponentIn_subset S x + have himg : IsConnected (φ '' connectedComponentIn S x) := + hW.image _ (φ.continuousOn.mono (hWS.trans hS)) + apply himg.isPreconnected.subset_connectedComponentIn + · exact ⟨x, mem_connectedComponentIn hx, rfl⟩ + · exact image_mono hWS + · have hW' : IsConnected (connectedComponentIn (φ '' S) (φ x)) := + isConnected_connectedComponentIn_iff.mpr ⟨x, hx, rfl⟩ + have hW'T : connectedComponentIn (φ '' S) (φ x) ⊆ φ '' S := + connectedComponentIn_subset _ _ + have hsymm : IsConnected (φ.symm '' connectedComponentIn (φ '' S) (φ x)) := + hW'.image _ (φ.symm.continuousOn.mono (hW'T.trans hST)) + have hsub : φ.symm '' connectedComponentIn (φ '' S) (φ x) + ⊆ connectedComponentIn S x := by + apply hsymm.isPreconnected.subset_connectedComponentIn + · exact ⟨φ x, mem_connectedComponentIn ⟨x, hx, rfl⟩, φ.left_inv (hS hx)⟩ + · calc φ.symm '' connectedComponentIn (φ '' S) (φ x) + ⊆ φ.symm '' (φ '' S) := image_mono hW'T + _ = S := φ.toPartialEquiv.symm_image_image_of_subset_source hS + calc connectedComponentIn (φ '' S) (φ x) + = φ '' (φ.symm '' connectedComponentIn (φ '' S) (φ x)) := + (φ.toPartialEquiv.image_symm_image_of_subset_target (hW'T.trans hST)).symm + _ ⊆ φ '' connectedComponentIn S x := image_mono hsub + +/-- Connected components of an open set contained in a chart source are open, because the +model space is locally connected. -/ +lemma isOpen_connectedComponentIn_chart + {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] [LocallyConnectedSpace Y] + (φ : OpenPartialHomeomorph X Y) {S : Set X} (hS : S ⊆ φ.source) + (hSo : IsOpen S) (x : X) : + IsOpen (connectedComponentIn S x) := by + by_cases hx : x ∈ S + · have key := OpenPartialHomeomorph.image_connectedComponentIn φ hS hx + have hWS : connectedComponentIn S x ⊆ S := connectedComponentIn_subset S x + have hopen : IsOpen (connectedComponentIn (φ '' S) (φ x)) := + (φ.isOpen_image_of_subset_source hSo hS).connectedComponentIn + have hsub : connectedComponentIn (φ '' S) (φ x) ⊆ φ.target := by + intro y hy + obtain ⟨z, hz, rfl⟩ := connectedComponentIn_subset _ _ hy + exact φ.map_source (hS hz) + have hrw : connectedComponentIn S x + = φ.symm '' connectedComponentIn (φ '' S) (φ x) := by + rw [← key] + exact (φ.toPartialEquiv.symm_image_image_of_subset_source (hWS.trans hS)).symm + rw [hrw] + exact φ.isOpen_image_symm_of_subset_target hopen hsub + · rw [connectedComponentIn_eq_empty hx] + exact isOpen_empty + +/-- Components of an open set contained in a chart source are relatively closed: a point +of `S` in the closure of a component of `S` lies in that component. -/ +lemma mem_connectedComponentIn_of_mem_closure + {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] [LocallyConnectedSpace Y] + (φ : OpenPartialHomeomorph X Y) {S : Set X} (hS : S ⊆ φ.source) (hSo : IsOpen S) + {x z : X} (hz : z ∈ closure (connectedComponentIn S x)) (hzS : z ∈ S) : + z ∈ connectedComponentIn S x := by + have hW2open : IsOpen (connectedComponentIn S z) := + isOpen_connectedComponentIn_chart φ hS hSo z + have hzW2 : z ∈ connectedComponentIn S z := mem_connectedComponentIn hzS + obtain ⟨y, hyW2, hyW⟩ := mem_closure_iff.mp hz _ hW2open hzW2 + have h1 : connectedComponentIn S z = connectedComponentIn S y := + connectedComponentIn_eq hyW2 + have h2 : connectedComponentIn S x = connectedComponentIn S y := + connectedComponentIn_eq hyW + rw [h2, ← h1] + exact hzW2 + +end ComponentTransport + +section Escape + +/-- The image under `U` of a component of the overlap is not all of `U.target` +(because `U.source` is not contained in `V.source`). -/ +lemma image_component_ne_target + (U V : OpenPartialHomeomorph M NNReal) + (hUV : (U.source \ V.source).Nonempty) + {x : M} : + U '' connectedComponentIn (U.source ∩ V.source) x ≠ U.target := by + intro h + obtain ⟨y, hyU, hyV⟩ := hUV + have hWsub : connectedComponentIn (U.source ∩ V.source) x ⊆ U.source ∩ V.source := + connectedComponentIn_subset _ _ + have hyt : U y ∈ U '' connectedComponentIn (U.source ∩ V.source) x := by + rw [h] + exact U.map_source hyU + obtain ⟨w, hwW, hwy⟩ := hyt + have hwyeq : w = y := U.injOn (hWsub hwW).1 hyU hwy + exact hyV (hwyeq ▸ (hWsub hwW).2) + +/-- The heart of the outer-overlap argument: the closure (in `ℝ≥0`) of the image of an +overlap component cannot stay inside the (bounded) chart target. Otherwise the +component's closure in `M` would be a subset of the compact set +`U.symm '' closure (U '' W)` inside `U.source` — but by `nonempty_closure_inter_diff` +the component's closure must reach a point of `V.source` outside the component, and any +such point trapped in `U.source ∩ V.source` would belong to the component after all. -/ +theorem not_closure_image_subset_target [T2Space M] + (U V : OpenPartialHomeomorph M NNReal) + (hUb : Bornology.IsBounded U.target) + (hV : IsConnected V.source) + (hVU : (V.source \ U.source).Nonempty) + {x : M} (hx : x ∈ U.source ∩ V.source) : + ¬ closure (U '' connectedComponentIn (U.source ∩ V.source) x) ⊆ U.target := by + intro hcl + set S := U.source ∩ V.source with hSdef + set W := connectedComponentIn S x with hWdef + set A := U '' W with hAdef + have hSo : IsOpen S := U.open_source.inter V.open_source + have hSU : S ⊆ U.source := inter_subset_left + have hWS : W ⊆ S := connectedComponentIn_subset _ _ + -- the closure of the image is compact + have hAT : A ⊆ U.target := by + rintro y ⟨w, hw, rfl⟩ + exact U.map_source (hSU (hWS hw)) + have hAcomp : IsCompact (closure A) := (hUb.subset hAT).isCompact_closure + -- its preimage under `U.symm` is compact, hence closed, and lives in `U.source` + have hKcomp : IsCompact (U.symm '' closure A) := + hAcomp.image_of_continuousOn (U.symm.continuousOn.mono hcl) + have hKclosed : IsClosed (U.symm '' closure A) := hKcomp.isClosed + have hKU : U.symm '' closure A ⊆ U.source := by + rintro z ⟨a, ha, rfl⟩ + exact U.map_target (hcl ha) + have hWK : W ⊆ U.symm '' closure A := by + intro w hw + exact ⟨U w, subset_closure ⟨w, hw, rfl⟩, U.left_inv (hSU (hWS hw))⟩ + have hclWU : closure W ⊆ U.source := + (closure_minimal hWK hKclosed).trans hKU + -- the component's closure must escape into `V.source \ W` + have hWo : IsOpen W := isOpen_connectedComponentIn_chart U hSU hSo x + have hWV : (W ∩ V.source).Nonempty := ⟨x, mem_connectedComponentIn hx, hx.2⟩ + obtain ⟨y, hyV, hyU⟩ := hVU + have hVW : (V.source \ W).Nonempty := + ⟨y, hyV, fun hyW => hyU (hSU (hWS hyW))⟩ + obtain ⟨z, hzcl, hzV, hzW⟩ := + nonempty_closure_inter_diff hV hWo V.open_source hWV hVW + have hzS : z ∈ S := ⟨hclWU hzcl, hzV⟩ + exact hzW (mem_connectedComponentIn_of_mem_closure U hSU hSo hzcl hzS) + +end Escape + +section EndpointAnalysis + +/-- A nonempty open connected subset of `Iio v ⊆ ℝ≥0`, other than `Iio v` itself, whose +closure is not contained in `Iio v`, is an upper end-segment `Ioo p v`. -/ +theorem eq_Ioo_of_closure_not_subset_Iio {v : NNReal} {A : Set NNReal} + (hA : A ⊆ Iio v) (hAo : IsOpen A) (hAc : IsConnected A) + (hAne : A ≠ Iio v) + (hesc : ¬ closure A ⊆ Iio v) : + ∃ p, p < v ∧ A = Ioo p v := by + obtain ⟨e, heA, hev⟩ := not_subset.mp hesc + have hve : v ≤ e := not_lt.mp hev + obtain ⟨a, ha⟩ := hAc.nonempty + rcases classify_connected_nnreal_interval A hAo hAc with ⟨p, q, hpq⟩ | ⟨q, hq⟩ | ⟨p, hp⟩ | h + · -- `A = Ioo p q`: the escape forces `q = v` + have hpq' : p < q := by + have ha' : a ∈ Ioo p q := hpq.symm ▸ ha + exact lt_trans ha'.1 ha'.2 + have hqv : q ≤ v := by + by_contra hqv + push Not at hqv + obtain ⟨t, ht1, ht2⟩ := exists_between (max_lt hpq' hqv) + have htA : t ∈ A := hpq ▸ mem_Ioo.mpr ⟨lt_of_le_of_lt (le_max_left p v) ht1, ht2⟩ + exact absurd (mem_Iio.mp (hA htA)) + (not_lt.mpr (le_trans (le_max_right p v) ht1.le)) + have hclA : closure A = Icc p q := by + rw [← hpq] + exact closure_Ioo hpq'.ne + have heIcc : e ∈ Icc p q := hclA ▸ heA + have hqveq : q = v := le_antisymm hqv (le_trans hve heIcc.2) + exact ⟨p, hqveq ▸ hpq', by rw [← hpq, hqveq]⟩ + · -- `A = Iio q`: forces `A = Iio v`, contradiction + exfalso + have hqv : q ≤ v := by + by_contra hqv + push Not at hqv + exact lt_irrefl v (hA (hq ▸ mem_Iio.mpr hqv)) + have hclA : closure A = Iic q := by + rw [← hq] + exact closure_Iio' ⟨a, hq.symm ▸ ha⟩ + have heq : e ≤ q := (hclA ▸ heA : e ∈ Iic q) + have : q = v := le_antisymm hqv (le_trans hve heq) + exact hAne (by rw [← hq, this]) + · -- `A = Ioi p`: unbounded, contradiction + exfalso + have h1 : max (p + 1) v ∈ A := + hp ▸ mem_Ioi.mpr (lt_of_lt_of_le (lt_add_one p) (le_max_left _ _)) + exact absurd (mem_Iio.mp (hA h1)) (not_lt.mpr (le_max_right _ _)) + · -- `A = univ`: unbounded, contradiction + exfalso + have h1 : v ∈ A := h.symm ▸ mem_univ v + exact lt_irrefl v (hA h1) + +/-- A nonempty open connected subset of `Ioo u v ⊆ ℝ≥0` whose closure is not contained in +`Ioo u v` is an end-segment: `Ioo p v` or `Ioo u q`. -/ +theorem eq_end_segment_of_closure_not_subset_Ioo {u v : NNReal} {A : Set NNReal} + (hA : A ⊆ Ioo u v) (hAo : IsOpen A) (hAc : IsConnected A) + (hesc : ¬ closure A ⊆ Ioo u v) : + (∃ p, u ≤ p ∧ p < v ∧ A = Ioo p v) ∨ (∃ q, u < q ∧ q ≤ v ∧ A = Ioo u q) := by + obtain ⟨e, heA, hev⟩ := not_subset.mp hesc + obtain ⟨a, ha⟩ := hAc.nonempty + rcases classify_connected_nnreal_interval A hAo hAc with ⟨p, q, hpq⟩ | ⟨q, hq⟩ | ⟨p, hp⟩ | h + · -- `A = Ioo p q`: the escape point pins one endpoint + have hpq' : p < q := by + have ha' : a ∈ Ioo p q := hpq.symm ▸ ha + exact lt_trans ha'.1 ha'.2 + have hup : u ≤ p := by + by_contra hup + push Not at hup + obtain ⟨t, ht1, ht2⟩ := exists_between (lt_min_iff.mpr ⟨hpq', hup⟩ : p < min q u) + have htA : t ∈ A := hpq ▸ mem_Ioo.mpr ⟨ht1, lt_of_lt_of_le ht2 (min_le_left q u)⟩ + exact absurd (hA htA).1 (not_lt.mpr (le_trans ht2.le (min_le_right q u))) + have hqv : q ≤ v := by + by_contra hqv + push Not at hqv + obtain ⟨t, ht1, ht2⟩ := exists_between (max_lt_iff.mpr ⟨hpq', hqv⟩ : max p v < q) + have htA : t ∈ A := hpq ▸ mem_Ioo.mpr ⟨lt_of_le_of_lt (le_max_left p v) ht1, ht2⟩ + exact absurd (hA htA).2 (not_lt.mpr (le_trans (le_max_right p v) ht1.le)) + have hclA : closure A = Icc p q := by + rw [← hpq] + exact closure_Ioo hpq'.ne + have heIcc : e ∈ Icc p q := hclA ▸ heA + rw [mem_Ioo, not_and_or, not_lt, not_lt] at hev + rcases hev with heu | hve + · -- `e ≤ u`: the lower endpoint is `u` + have hpu : p = u := le_antisymm (le_trans heIcc.1 heu) hup + exact Or.inr ⟨q, hpu ▸ hpq', hqv, by rw [← hpq, hpu]⟩ + · -- `v ≤ e`: the upper endpoint is `v` + have hqveq : q = v := le_antisymm hqv (le_trans hve heIcc.2) + exact Or.inl ⟨p, hup, hqveq ▸ hpq', by rw [← hpq, hqveq]⟩ + · -- `A = Iio q`: contains `0`, but `A ⊆ Ioo u v` — impossible + exfalso + have h0q : (0 : NNReal) < q := + lt_of_le_of_lt (zero_le : (0 : NNReal) ≤ a) + (mem_Iio.mp (hq.symm ▸ ha : a ∈ Iio q)) + have h0A : (0 : NNReal) ∈ A := hq ▸ mem_Iio.mpr h0q + exact absurd (hA h0A).1 (not_lt.mpr (zero_le : (0 : NNReal) ≤ u)) + · -- `A = Ioi p`: unbounded, contradiction + exfalso + have h1 : max (p + 1) v ∈ A := + hp ▸ mem_Ioi.mpr (lt_of_lt_of_le (lt_add_one p) (le_max_left _ _)) + exact absurd (hA h1).2 (not_lt.mpr (le_max_right _ _)) + · -- `A = univ`: unbounded, contradiction + exfalso + have h1 : v ∈ A := h.symm ▸ mem_univ v + exact lt_irrefl v (hA h1).2 + +end EndpointAnalysis + +section Outer + +/-- **Outer-overlap lemma, boundary-chart case.** If `U` has target `Iio v` and overlaps +`V` (neither source containing the other), the image under `U` of any connected component +of the overlap is an upper end-segment `Ioo p v`. -/ +theorem overlap_component_outer_Iio [T2Space M] + (U V : OpenPartialHomeomorph M NNReal) {v : NNReal} + (hUt : U.target = Iio v) + (hV : IsConnected V.source) + (hUV : (U.source \ V.source).Nonempty) + (hVU : (V.source \ U.source).Nonempty) + {x : M} (hx : x ∈ U.source ∩ V.source) : + ∃ p, p < v ∧ U '' connectedComponentIn (U.source ∩ V.source) x = Ioo p v := by + set S := U.source ∩ V.source with hSdef + set W := connectedComponentIn S x with hWdef + set A := U '' W with hAdef + have hSU : S ⊆ U.source := inter_subset_left + have hSo : IsOpen S := U.open_source.inter V.open_source + have hWo : IsOpen W := isOpen_connectedComponentIn_chart U hSU hSo x + have hWS : W ⊆ S := connectedComponentIn_subset _ _ + have hAo : IsOpen A := U.isOpen_image_of_subset_source hWo (hWS.trans hSU) + have hAc : IsConnected A := + (isConnected_connectedComponentIn_iff.mpr hx).image _ + (U.continuousOn.mono (hWS.trans hSU)) + have hAsub : A ⊆ U.target := by + rintro y ⟨w, hw, rfl⟩ + exact U.map_source (hSU (hWS hw)) + have hUb : Bornology.IsBounded U.target := by + rw [hUt] + have : Iio v = Ico 0 v := by + ext z + simp only [mem_Iio, mem_Ico, zero_le, true_and] + rw [this] + exact Metric.isBounded_Ico 0 v + have hAne : A ≠ Iio v := by + rw [← hUt] + exact image_component_ne_target U V hUV + have hesc : ¬ closure A ⊆ Iio v := by + rw [← hUt] + exact not_closure_image_subset_target U V hUb hV hVU hx + exact eq_Ioo_of_closure_not_subset_Iio (hUt ▸ hAsub) hAo hAc hAne hesc + +/-- **Outer-overlap lemma, interior-chart case.** If `U` has target `Ioo u v` and +overlaps `V`, the image under `U` of any connected component of the overlap is an +end-segment `Ioo p v` or `Ioo u q`. -/ +theorem overlap_component_outer_Ioo [T2Space M] + (U V : OpenPartialHomeomorph M NNReal) {u v : NNReal} + (hUt : U.target = Ioo u v) + (hV : IsConnected V.source) + (hVU : (V.source \ U.source).Nonempty) + {x : M} (hx : x ∈ U.source ∩ V.source) : + (∃ p, u ≤ p ∧ p < v ∧ U '' connectedComponentIn (U.source ∩ V.source) x = Ioo p v) ∨ + (∃ q, u < q ∧ q ≤ v ∧ U '' connectedComponentIn (U.source ∩ V.source) x = Ioo u q) := by + set S := U.source ∩ V.source with hSdef + set W := connectedComponentIn S x with hWdef + set A := U '' W with hAdef + have hSU : S ⊆ U.source := inter_subset_left + have hSo : IsOpen S := U.open_source.inter V.open_source + have hWo : IsOpen W := isOpen_connectedComponentIn_chart U hSU hSo x + have hWS : W ⊆ S := connectedComponentIn_subset _ _ + have hAo : IsOpen A := U.isOpen_image_of_subset_source hWo (hWS.trans hSU) + have hAc : IsConnected A := + (isConnected_connectedComponentIn_iff.mpr hx).image _ + (U.continuousOn.mono (hWS.trans hSU)) + have hAsub : A ⊆ U.target := by + rintro y ⟨w, hw, rfl⟩ + exact U.map_source (hSU (hWS hw)) + have hUb : Bornology.IsBounded U.target := by + rw [hUt] + exact Metric.isBounded_Ioo u v + have hesc : ¬ closure A ⊆ Ioo u v := by + rw [← hUt] + exact not_closure_image_subset_target U V hUb hV hVU hx + exact eq_end_segment_of_closure_not_subset_Ioo (hUt ▸ hAsub) hAo hAc hesc + +end Outer + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/PartialHomeomorphHelpers.lean b/LeanPool/OneManifold/OneMfld/PartialHomeomorphHelpers.lean new file mode 100644 index 000000000..c938e4223 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/PartialHomeomorphHelpers.lean @@ -0,0 +1,66 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import Mathlib.Topology.Connected.Basic +public import Mathlib.Topology.OpenPartialHomeomorph.Defs + + +/-! +# PartialHomeomorphHelpers + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +lemma partial_homeo_connected {X : Type*} {Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] + (h : OpenPartialHomeomorph X Y) (conn : IsConnected h.source) : (IsConnected h.target) := by + have ht : h.target = h.toFun '' h.source + · exact Eq.symm (PartialEquiv.image_source_eq_target h.toPartialEquiv) + rw [ht] + apply IsConnected.image conn ↑h.toPartialEquiv h.continuousOn_toFun + +lemma partial_homeo_connected' {X : Type*} {Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] + (h : OpenPartialHomeomorph X Y) (conn : IsConnected h.target) : (IsConnected h.source) := by + have hst : h.symm.source = h.target := rfl + have hts : h.symm.target = h.source := rfl + have conn' : IsConnected h.symm.source := by + rw [hst] + exact conn + have h'' := partial_homeo_connected h.symm conn' + rw [←hts] + exact h'' + +lemma partial_homeo_source_connected_iff_target_connected {X : Type*} {Y : Type*} + [TopologicalSpace X] [TopologicalSpace Y] + (h : OpenPartialHomeomorph X Y) : IsConnected h.source ↔ IsConnected h.target := by + constructor + · exact fun a => partial_homeo_connected h a + · exact fun a => partial_homeo_connected' h a + +/-- For `t ⊆ φ.target`, the target of `φ` restricted to `φ.symm '' t` is exactly `t`; +this computes `(φ.restrOpen (φ.symm '' t) _).target`. -/ +lemma restrOpen_symm_image_target {X : Type*} {Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] + (φ : OpenPartialHomeomorph X Y) {t : Set Y} (ht : t ⊆ φ.target) : + φ.target ∩ ↑φ.symm ⁻¹' (↑φ.symm '' t) = t := by + ext z + apply Iff.intro + · intro hz + simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_image] at hz + rcases hz with ⟨hz1, ⟨z', hz2, hz3⟩⟩ + have inj : Set.InjOn φ.symm φ.target := OpenPartialHomeomorph.injOn φ.symm + have : z' = z := inj (ht hz2) hz1 hz3 + rw [←this] + exact hz2 + · intro hz + simp only [Set.mem_inter_iff, Set.mem_preimage, Set.mem_image] + exact ⟨ht hz, z, hz, rfl⟩ + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/RealIntervals.lean b/LeanPool/OneManifold/OneMfld/RealIntervals.lean new file mode 100644 index 000000000..f7aa1a496 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/RealIntervals.lean @@ -0,0 +1,515 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Analysis.InnerProductSpace.Basic +public import Mathlib.Analysis.Normed.Module.RCLike.Real +public import Mathlib.Analysis.Normed.Order.Lattice +public import Mathlib.Order.CompletePartialOrder +public import Mathlib.Tactic + + +/-! +# RealIntervals + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +open Set + +namespace RealIntervals + +-- Use the order topology on ℝ throughout +lemma ordconn_of_connected {X : Set ℝ} (conn : IsConnected X) + (a : ℝ) (aX : a ∈ X) (b : ℝ) (bX : b ∈ X) : Icc a b ⊆ X := by + have : OrdConnected X := + IsPreconnected.ordConnected (IsConnected.isPreconnected conn) + exact Set.Icc_subset X aX bX + +-- Shouldn't this already be in mathlib? +lemma Real.exists_isGLB {S : Set ℝ} (hne : S.Nonempty) (hbdd : BddBelow S) + : ∃ x, IsGLB S x := by + exact ⟨sInf S, isGLB_csInf hne hbdd⟩ + +-- classifying real intervals + +-- Note: Much of what's below already exists in mathlib: +-- Use IsPreconnected.mem_intervals +-- and Real.instConditionallyCompleteLinearOrderReal + +lemma connected_bddAbove_subset_contains_Ioo {X : Set ℝ} {supX : ℝ} {x : ℝ} + (conn : IsConnected X) (h_supX : IsLUB X supX) (xX : x ∈ X) + : Ioo x supX ⊆ X := by + intro y ⟨x_lt_y, y_lt_supX⟩ + by_cases h: ∃ z ∈ X, y ≤ z + · rcases h with ⟨z, zX, y_le_z⟩ + have : Icc x z ⊆ X := ordconn_of_connected conn x xX z zX + exact this ⟨LT.lt.le x_lt_y, y_le_z⟩ + · push Not at h + have : y ∈ upperBounds X := fun z ↦ fun zX ↦ LT.lt.le (h z zX) + have : supX ≤ y := h_supX.2 this + linarith + +lemma connected_bddBelow_subset_contains_Ioo {X : Set ℝ} {infX : ℝ} {x : ℝ} + (conn : IsConnected X) (h_infX : IsGLB X infX) (xX : x ∈ X) + : Ioo infX x ⊆ X := by + intro y ⟨infX_lt_y, y_lt_x⟩ + by_cases h: ∃ z ∈ X, z ≤ y + · rcases h with ⟨z, zX, z_le_y⟩ + have : Icc z x ⊆ X := ordconn_of_connected conn z zX x xX + exact this ⟨z_le_y, LT.lt.le y_lt_x⟩ + · push Not at h + have : y ∈ lowerBounds X := fun z ↦ fun zX ↦ LT.lt.le (h z zX) + have : y ≤ infX := h_infX.2 this + linarith + +lemma connected_bdd_subset_contains_Ioo + {X : Set ℝ} {infX supX : ℝ} + (conn : IsConnected X) (h_infX : IsGLB X infX) (h_supX : IsLUB X supX) + : Ioo infX supX ⊆ X := by + let ⟨z, zX⟩ := IsConnected.nonempty conn + have h₁ : Ioo infX z ⊆ X := connected_bddBelow_subset_contains_Ioo conn h_infX zX + have h₂ : Ioo z supX ⊆ X := connected_bddAbove_subset_contains_Ioo conn h_supX zX + have h₃ : Ioo infX supX ⊆ Ioo infX z ∪ {z} ∪ Ioo z supX := by + intro x ⟨infX_lt_x, x_lt_supX⟩ + rcases lt_trichotomy x z with (x_lt_z | x_eq_z | z_lt_z) + · left; left + exact ⟨infX_lt_x, x_lt_z⟩ + · left; right + rw [mem_singleton_iff, x_eq_z] + · right + exact ⟨z_lt_z, x_lt_supX⟩ + have h₄ : Ioo infX z ∪ {z} ∪ Ioo z supX ⊆ X := by + rintro x ((_ | x_eq_z) | _) + · apply h₁; assumption + · rw [mem_singleton_iff] at x_eq_z + rw [x_eq_z] + exact zX + · apply h₂; assumption + intro x x_Ioo + exact h₄ (h₃ x_Ioo) + +lemma bdd_subset_Icc + {X : Set ℝ} {infX supX : ℝ} + (h_infX : IsGLB X infX) (h_supX : IsLUB X supX) + : X ⊆ Icc infX supX := by + intro x xX + exact ⟨h_infX.1 xX, h_supX.1 xX⟩ + +lemma x_lt_excluded_supX {x supX : ℝ} (xX : x ∈ X) + (h_supX : IsLUB X supX) (supX_X : supX ∉ X) : x < supX := by + have : x ≠ supX := by + intro x_eq_supX + rw [← x_eq_supX] at supX_X + contradiction + exact Ne.lt_of_le this (h_supX.1 xX) + +lemma excluded_infX_lt_x {x infX : ℝ} (xX : x ∈ X) + (h_infX : IsGLB X infX) (infX_X : infX ∉ X) : infX < x := by + have : infX ≠ x := by + intro x_eq_supX + rw [x_eq_supX] at infX_X + contradiction + exact Ne.lt_of_le this (h_infX.1 xX) + +lemma Ico_Ioo_with_endpoint {a b : ℝ} (hab : a < b) + : Ico a b = (Ioo a b) ∪ {a} := by + apply Subset.antisymm + · intro x ⟨x_le_a, x_lt_b⟩ + by_cases xa : x = a + · right; exact xa + · left; exact ⟨Ne.lt_of_le' xa x_le_a, x_lt_b⟩ + · rintro x (x_Ioo | x_a) + · exact ⟨LT.lt.le x_Ioo.1, x_Ioo.2⟩ + · exact ⟨Eq.ge x_a, Eq.trans_lt x_a hab⟩ + +lemma Ioc_Ioo_with_endpoint {a b : ℝ} (hab : a < b) + : Ioc a b = (Ioo a b) ∪ {b} := by + apply Subset.antisymm + · intro x ⟨a_lt_x, x_le_b⟩ + by_cases xb : x = b + · right; exact xb + have : x < b := Ne.lt_of_le xb x_le_b + left; exact ⟨a_lt_x, this⟩ + · rintro x (x_Ioo | x_b) + · exact ⟨x_Ioo.1, LT.lt.le x_Ioo.2⟩ + · exact ⟨(by rw [x_b]; exact hab), Eq.le x_b⟩ + +lemma characterize_Ioo {X : Set ℝ} (conn : IsConnected X) {infX supX : ℝ} + (h_infX : IsGLB X infX) (h_supX : IsLUB X supX) + (infX_X : infX ∉ X) (supX_X : supX ∉ X) + : X = Ioo infX supX := by + apply Subset.antisymm + · intro x xX + exact ⟨excluded_infX_lt_x xX h_infX infX_X, + x_lt_excluded_supX xX h_supX supX_X⟩ + · exact connected_bdd_subset_contains_Ioo conn h_infX h_supX + +lemma characterize_Ioc {X : Set ℝ} (conn : IsConnected X) {infX supX : ℝ} + (inf_lt_sup : infX < supX) + (h_infX : IsGLB X infX) (h_supX : IsLUB X supX) + (infX_X : infX ∉ X) (supX_X : supX ∈ X) + : X = Ioc infX supX := by + apply Subset.antisymm + · intro x xX + exact ⟨excluded_infX_lt_x xX h_infX infX_X, + h_supX.1 xX⟩ + · rw [Ioc_Ioo_with_endpoint inf_lt_sup, union_subset_iff] + exact ⟨connected_bdd_subset_contains_Ioo conn h_infX h_supX, + singleton_subset_iff.mpr supX_X⟩ + +lemma characterize_Ico {X : Set ℝ} (conn : IsConnected X) {infX supX : ℝ} + (inf_lt_sup : infX < supX) + (h_infX : IsGLB X infX) (h_supX : IsLUB X supX) + (infX_X : infX ∈ X) (supX_X : supX ∉ X) + : X = Ico infX supX := by + apply Subset.antisymm + · intro x xX + exact ⟨h_infX.1 xX, + x_lt_excluded_supX xX h_supX supX_X⟩ + · rw [Ico_Ioo_with_endpoint inf_lt_sup, union_subset_iff] + exact ⟨connected_bdd_subset_contains_Ioo conn h_infX h_supX, + singleton_subset_iff.mpr infX_X⟩ + +lemma characterize_Icc {X : Set ℝ} (conn : IsConnected X) {infX supX : ℝ} + (h_infX : IsGLB X infX) (h_supX : IsLUB X supX) + (infX_X : infX ∈ X) (supX_X : supX ∈ X) + : X = Icc infX supX := by + apply Subset.antisymm + · exact bdd_subset_Icc h_infX h_supX + · exact (ordconn_of_connected conn) infX infX_X supX supX_X + +lemma characterize_singleton {X : Set ℝ} {a : ℝ} + (h_infX : IsGLB X a) (h_supX : IsLUB X a) + : (X = {a}) := by + apply Subset.antisymm + · apply subset_trans (bdd_subset_Icc h_infX h_supX) + exact Set.Icc_subset {a} rfl rfl + · rw [singleton_subset_iff] + rcases IsLUB.nonempty h_supX with ⟨x, xX⟩ + have : a = x := LE.le.antisymm (h_infX.1 xX) (h_supX.1 xX) + exact mem_of_eq_of_mem (id this) xX + +/-- The four possibilities for including the two endpoints of a bounded interval. -/ +inductive BoundedIntervalKind | IooKind | IocKind | IcoKind | IccKind + +open BoundedIntervalKind + +/-- Two ordered real endpoints together with their inclusion convention. -/ +structure BoundedInterval where + /-- The left endpoint of the interval. -/ + leftEndpoint : ℝ + /-- The right endpoint of the interval. -/ + rightEndpoint : ℝ + left_lt_right : leftEndpoint < rightEndpoint + /-- Which endpoints belong to the interval. -/ + kind : BoundedIntervalKind + +/-- Interpret the interval's endpoints and inclusion convention as a set of reals. -/ +def BoundedIntervalAsSet : BoundedInterval → Set ℝ + | ⟨a, b, _, IooKind⟩ => Ioo a b + | ⟨a, b, _, IocKind⟩ => Ioc a b + | ⟨a, b, _, IcoKind⟩ => Ico a b + | ⟨a, b, _, IccKind⟩ => Icc a b + +/-- The set is a nondegenerate bounded interval with some endpoint convention. -/ +def isBoundedInterval (X : Set ℝ) := + ∃ I : BoundedInterval, X = BoundedIntervalAsSet I + +lemma isBoundedInterval_Ioo (a b : ℝ) (lt : a < b) : isBoundedInterval (Ioo a b) := by + use ⟨a, b, lt, IooKind⟩; simp only [BoundedIntervalAsSet] +lemma isBoundedInterval_Ioc (a b : ℝ) (lt : a < b) : isBoundedInterval (Ioc a b) := by + use ⟨a, b, lt, IocKind⟩; simp only [BoundedIntervalAsSet] +lemma isBoundedInterval_Ico (a b : ℝ) (lt : a < b) : isBoundedInterval (Ico a b) := by + use ⟨a, b, lt, IcoKind⟩; simp only [BoundedIntervalAsSet] +lemma isBoundedInterval_Icc (a b : ℝ) (lt : a < b) : isBoundedInterval (Icc a b) := by + use ⟨a, b, lt, IccKind⟩; simp only [BoundedIntervalAsSet] + +/-- The set consists of exactly one real number. -/ +def isSingleton (X : Set ℝ) := + ∃ a : ℝ, X = {a} + +lemma classify_connected_reals_with_GLB_lt_LUB + {X : Set ℝ} (conn : IsConnected X) {infX supX : ℝ} + (h_infX : IsGLB X infX) (h_supX : IsLUB X supX) (lt : infX < supX) + : isBoundedInterval X := by + by_cases infX_X : infX ∈ X + · by_cases supX_X : supX ∈ X + · use ⟨infX, supX, lt, IccKind⟩ + exact characterize_Icc conn h_infX h_supX infX_X supX_X + · use ⟨infX, supX, lt, IcoKind⟩ + exact characterize_Ico conn lt h_infX h_supX infX_X supX_X + · by_cases supX_X : supX ∈ X + · use ⟨infX, supX, lt, IocKind⟩ + exact characterize_Ioc conn lt h_infX h_supX infX_X supX_X + · use ⟨infX, supX, lt, IooKind⟩ + exact characterize_Ioo conn h_infX h_supX infX_X supX_X + +theorem classify_connected_bounded_reals + {X : Set ℝ} (conn : IsConnected X) (above : BddAbove X) (below : BddBelow X) + : isSingleton X ∨ isBoundedInterval X := by + have nonempty := IsConnected.nonempty conn + have ⟨supX, h_supX⟩ := Real.exists_isLUB nonempty above + have ⟨infX, h_infX⟩ := Real.exists_isGLB nonempty below + by_cases inf_eq_sup : infX = supX + · left; rw [isSingleton]; use infX + rw [← inf_eq_sup] at h_supX + apply characterize_singleton h_infX h_supX + · right + have : infX ≤ supX := isGLB_le_isLUB h_infX h_supX nonempty + have : infX < supX := Ne.lt_of_le inf_eq_sup this + exact classify_connected_reals_with_GLB_lt_LUB conn h_infX h_supX this + +theorem classify_connected_bounded_reals_nonempty_interior + {X : Set ℝ} (conn : IsConnected X) (above : BddAbove X) (below : BddBelow X) + (h : (interior X) ≠ ∅) + : isBoundedInterval X := by + rcases classify_connected_bounded_reals conn above below with ⟨a, ha⟩ | bdd + · rw [ha, interior_singleton] at h + exact (h rfl).elim + · exact bdd + +-- Some lemmas about bounded intervals in ℝ + +lemma isConnected_BoundedInterval (I : BoundedInterval) + : IsConnected (BoundedIntervalAsSet I) := by + rw [BoundedIntervalAsSet.eq_def] + have ⟨a, b, lt, kind⟩ := I + match kind with + | IooKind => exact isConnected_Ioo lt + | IocKind => exact isConnected_Ioc lt + | IcoKind => exact isConnected_Ico lt + | IccKind => exact isConnected_Icc (LT.lt.le lt) + +lemma BoundedInterval_subset_Icc (I : BoundedInterval) + : BoundedIntervalAsSet I ⊆ Icc I.leftEndpoint I.rightEndpoint := by + have ⟨a, b, lt, kind⟩ := I + match kind with + | IooKind => exact Ioo_subset_Icc_self + | IocKind => exact Ioc_subset_Icc_self + | IcoKind => exact Ico_subset_Icc_self + | IccKind => exact Eq.subset rfl + +lemma BoundedInterval_contains_Ioo (I : BoundedInterval) + : Ioo I.leftEndpoint I.rightEndpoint ⊆ BoundedIntervalAsSet I := by + have ⟨a, b, lt, kind⟩ := I + match kind with + | IooKind => exact Eq.subset rfl + | IocKind => exact Ioo_subset_Ioc_self + | IcoKind => exact Ioo_subset_Ico_self + | IccKind => exact Ioo_subset_Icc_self + +lemma closure_BoundedInterval (I : BoundedInterval) : closure (BoundedIntervalAsSet I) = Icc + I.leftEndpoint I.rightEndpoint := by + apply Subset.antisymm + · calc closure (BoundedIntervalAsSet I) + ⊆ closure (Icc I.leftEndpoint I.rightEndpoint) + := closure_mono (BoundedInterval_subset_Icc I) + _ = Icc I.leftEndpoint I.rightEndpoint + := IsClosed.closure_eq isClosed_Icc + · calc Icc I.leftEndpoint I.rightEndpoint + = closure (Ioo I.leftEndpoint I.rightEndpoint) + := (closure_Ioo (ne_of_lt I.left_lt_right)).symm + _ ⊆ closure (BoundedIntervalAsSet I) + := closure_mono (BoundedInterval_contains_Ioo I) + +lemma isBoundedBelow_BoundedInterval (I : BoundedInterval) + : BddBelow (BoundedIntervalAsSet I) := + BddBelow.mono (BoundedInterval_subset_Icc I) bddBelow_Icc + +lemma isBoundedAbove_BoundedInterval (I : BoundedInterval) + : BddAbove (BoundedIntervalAsSet I) := + BddAbove.mono (BoundedInterval_subset_Icc I) bddAbove_Icc + +@[simp] +lemma interior_BoundedInterval (I : BoundedInterval) + : interior (BoundedIntervalAsSet I) = Ioo I.leftEndpoint I.rightEndpoint := by + rw [BoundedIntervalAsSet.eq_def] + have ⟨a, b, lt, kind⟩ := I + match kind with + | IooKind => dsimp; exact interior_Ioo + | IocKind => dsimp; exact interior_Ioc + | IcoKind => dsimp; exact interior_Ico + | IccKind => dsimp; exact interior_Icc + +-- Shouldn't this already be in mathlib? +@[simp] +lemma Icc_diff_Ioo {a b : ℝ} (lt : a < b) : (Icc a b) \ (Ioo a b) = {a, b} := by + apply Subset.antisymm + · rw [← Icc_sdiff_both] + exact sdiff_sdiff_le + · rintro x (xleft | xright) + · rw [mem_sdiff, mem_Icc, mem_Ioo, not_and] + exact ⟨⟨by linarith, by linarith⟩, (by intro _; linarith)⟩ + · simp only [mem_singleton_iff] at xright + rw [xright] + simp only [mem_sdiff, mem_Icc, le_refl, and_true, mem_Ioo, lt_self_iff_false, and_false, + not_false_eq_true] + exact LT.lt.le lt + +@[simp] +lemma frontier_BoundedInterval (I : BoundedInterval) + : frontier (BoundedIntervalAsSet I) = {I.leftEndpoint, I.rightEndpoint} := by + rw [frontier, closure_BoundedInterval I, interior_BoundedInterval I] + exact Icc_diff_Ioo I.left_lt_right + +lemma pair_has_other {a b c : ℝ} (ne : a ≠ b) (h : c ∈ ({a, b} : Set ℝ)) + : ∃ (d : ℝ), d ∈ ({a, b} : Set ℝ) ∧ d ≠ c := by + simp only [mem_insert_iff, mem_singleton_iff, ne_eq, exists_eq_or_imp, exists_eq_left] + simp only [mem_insert_iff, mem_singleton_iff] at h + rcases h with ca | cb + · rw [← ca] at ne; right; exact ne.symm + · rw [← cb] at ne; left; exact ne + +theorem other_endpoint + {X : Set ℝ} (int : (interior X) ≠ ∅) + (conn : IsConnected X) (above : BddAbove X) (below : BddBelow X) + (a : ℝ) (ha : a ∈ frontier X) : ∃b : ℝ, b ∈ frontier X ∧ b ≠ a:= by + have ⟨I, XI⟩ := classify_connected_bounded_reals_nonempty_interior conn above below int + rw [XI, frontier_BoundedInterval] + rw [XI, frontier_BoundedInterval] at ha + have : I.leftEndpoint ≠ I.rightEndpoint := by + exact ne_of_lt I.left_lt_right + exact pair_has_other (ne_of_lt I.left_lt_right) ha + +-- Continue to classify all connected sets in ℝ +lemma characterize_univ {X : Set ℝ} (conn : IsConnected X) + (below : ¬ BddBelow X) (above : ¬ BddAbove X) + : X = univ := by + rw [bddBelow_def] at below; push Not at below + rw [bddAbove_def] at above; push Not at above + ext x + simp only [mem_univ, iff_true] + have ⟨B, BX, Bbig⟩ := above x + have ⟨A, AX, Asmall⟩ := below x + have : Icc A B ⊆ X := (ordconn_of_connected conn) A AX B BX + exact this ⟨LT.lt.le Asmall, LT.lt.le Bbig⟩ + +lemma characterize_Ioi {X : Set ℝ} (conn : IsConnected X) + {infX : ℝ} (h_infX : IsGLB X infX) (infX_X : infX ∉ X) + (above : ¬ BddAbove X) : X = Ioi infX := by + rw [bddAbove_def] at above; push Not at above + apply Subset.antisymm + · intro x xX + exact excluded_infX_lt_x xX h_infX infX_X + · intro x infX_lt_x + rw [mem_Ioi] at infX_lt_x + let ⟨B, BX, Bbig⟩ := above x + have : Ioo infX B ⊆ X := + connected_bddBelow_subset_contains_Ioo conn h_infX BX + exact this ⟨infX_lt_x, Bbig⟩ + +lemma characterize_Ici {X : Set ℝ} (conn : IsConnected X) + {infX : ℝ} (h_infX : IsGLB X infX) (infX_X : infX ∈ X) + (above : ¬ BddAbove X) : X = Ici infX := by + rw [bddAbove_def] at above; push Not at above + apply Set.Subset.antisymm + · intro x xX + exact h_infX.1 xX + · intro x infX_le_x + have ⟨B, BX, Bbig⟩ := above x + have : Set.Icc infX B ⊆ X := (ordconn_of_connected conn) infX infX_X B BX + exact this ⟨infX_le_x, LT.lt.le Bbig⟩ + +lemma classify_Ixi {X : Set ℝ} (conn : IsConnected X) + (below : BddBelow X) (above : ¬ BddAbove X) + : ∃ a : ℝ, (X = Ioi a ∨ X = Ici a) := by + have ⟨infX, h_infX⟩ := Real.exists_isGLB (IsConnected.nonempty conn) below + use infX + by_cases infX_X : infX ∈ X + · right; exact characterize_Ici conn h_infX infX_X above + · left; exact characterize_Ioi conn h_infX infX_X above + +lemma characterize_Iio {X : Set ℝ} (conn : IsConnected X) + {supX : ℝ} (h_supX : IsLUB X supX) (supX_X : supX ∉ X) + (below : ¬ BddBelow X) : X = Iio supX := by + rw [bddBelow_def] at below; push Not at below + apply Set.Subset.antisymm + · intro x xX + exact x_lt_excluded_supX xX h_supX supX_X + · intro x x_lt_supX + rw [Set.mem_Iio] at x_lt_supX + let ⟨A, AX, Asmall⟩ := below x + have : Set.Ioo A supX ⊆ X := + connected_bddAbove_subset_contains_Ioo conn h_supX AX + exact this ⟨Asmall, x_lt_supX⟩ + +lemma characterize_Iic {X : Set ℝ} (conn : IsConnected X) + {supX : ℝ} (h_supX : IsLUB X supX) (supX_X : supX ∈ X) + (below : ¬ BddBelow X) : X = Iic supX := by + rw [bddBelow_def] at below; push Not at below + apply Set.Subset.antisymm + · intro x xX + exact h_supX.1 xX + · intro x x_le_supX + let ⟨A, AX, Asmall⟩ := below x + have : Set.Icc A supX ⊆ X := (ordconn_of_connected conn) A AX supX supX_X + exact this ⟨LT.lt.le Asmall, x_le_supX⟩ + +lemma classify_Iix {X : Set ℝ} (conn : IsConnected X) + (below : ¬ BddBelow X) (above : BddAbove X) + : ∃ a : ℝ, (X = Iio a ∨ X = Iic a) := by + have ⟨supX, h_supX⟩ := Real.exists_isLUB (IsConnected.nonempty conn) above + use supX + by_cases supX_X : supX ∈ X + · right; exact characterize_Iic conn h_supX supX_X below + · left; exact characterize_Iio conn h_supX supX_X below + +-- Give names to the cases +/-- The possible forms of a nonempty connected subset of the real line. -/ +inductive ConnectedRealClassification + | of_univ : ConnectedRealClassification + | of_Iio : ℝ → ConnectedRealClassification + | of_Iic : ℝ → ConnectedRealClassification + | of_Ioi : ℝ → ConnectedRealClassification + | of_Ici : ℝ → ConnectedRealClassification + | of_singleton : ℝ → ConnectedRealClassification + | of_bounded_interval : BoundedInterval → ConnectedRealClassification + +open ConnectedRealClassification + +/-- Interpret a connected-set classification as a subset of the real line. -/ +def ConnectedRealClassificationAsSet : ConnectedRealClassification → Set ℝ + | of_univ => univ + | of_Iio a => Iio a + | of_Iic a => Iic a + | of_Ioi a => Ioi a + | of_Ici a => Ici a + | of_singleton a => {a} + | of_bounded_interval I => BoundedIntervalAsSet I + +/-- The set is represented by one of the connected real-set forms. -/ +def isConnectedRealClassification (X : Set ℝ) := + ∃ I : ConnectedRealClassification, X = ConnectedRealClassificationAsSet I + +theorem classify_connected_reals {X : Set ℝ} (conn : IsConnected X) + : isConnectedRealClassification X := by + by_cases below : BddBelow X + · by_cases above : BddAbove X + · rcases classify_connected_bounded_reals conn above below with sing | bdd + · let ⟨a, ha⟩ := sing + use of_singleton a; exact ha + · let ⟨I, hI⟩ := bdd + use of_bounded_interval I; exact hI + · rcases classify_Ixi conn below above with ⟨a, hIoi | hIci⟩ + · use of_Ioi a; exact hIoi + · use of_Ici a; exact hIci + · by_cases above : BddAbove X + · rcases classify_Iix conn below above with ⟨a, hIio | hIic⟩ + · use of_Iio a + exact hIio + · use of_Iic a + exact hIic + · use of_univ + exact characterize_univ conn below above + +end RealIntervals + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/TransitionMono.lean b/LeanPool/OneManifold/OneMfld/TransitionMono.lean new file mode 100644 index 000000000..5a4265800 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/TransitionMono.lean @@ -0,0 +1,60 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import Mathlib.Topology.Bornology.Real +public import Mathlib.Topology.UniformSpace.Real + + +/-! # Monotonicity of transition maps + +A transition map between interval charts, restricted to (the image of) a component of the +overlap, is continuous and injective on an open interval of `ℝ≥0`, hence strictly +monotone or strictly antitone; and a strictly monotone map of one open interval onto +another sends ends to ends. +-/ + +@[expose] public section + +namespace OneMfld + +open Set Filter Topology + +/-- A continuous injective function on an open interval of `ℝ≥0` is strictly monotone or +strictly antitone there. -/ +theorem strictMonoOn_or_strictAntiOn_of_injOn_Ioo {p v : NNReal} + {f : NNReal → NNReal} (hc : ContinuousOn f (Ioo p v)) (hi : InjOn f (Ioo p v)) : + StrictMonoOn f (Ioo p v) ∨ StrictAntiOn f (Ioo p v) := by + by_cases hpv : p < v + · exact ContinuousOn.strictMonoOn_of_injOn_Ioo hpv hc hi + · left + intro x hx + simp [Ioo_eq_empty hpv] at hx + +/-- A strictly monotone map of `Ioo p v` onto `Ioo q w` tends to `w` at the top end. -/ +theorem tendsto_top_of_strictMonoOn_image {p v q w : NNReal} (hpv : p < v) + {f : NNReal → NNReal} (hm : StrictMonoOn f (Ioo p v)) + (himg : f '' Ioo p v = Ioo q w) : + Tendsto f (𝓝[<] v) (𝓝 w) := by + have hne : (Ioo p v).Nonempty := nonempty_Ioo.2 hpv + have hqw : q < w := nonempty_Ioo.1 (himg ▸ hne.image f) + have hbdd : BddAbove (f '' Ioo p v) := himg ▸ bddAbove_Ioo + have h := MonotoneOn.tendsto_nhdsWithin_Ioo_left hne hm.monotoneOn hbdd + rwa [himg, csSup_Ioo hqw] at h + +/-- A strictly monotone map of `Ioo p v` onto `Ioo q w` tends to `q` at the bottom end. -/ +theorem tendsto_bot_of_strictMonoOn_image {p v q w : NNReal} (hpv : p < v) + {f : NNReal → NNReal} (hm : StrictMonoOn f (Ioo p v)) + (himg : f '' Ioo p v = Ioo q w) : + Tendsto f (𝓝[>] p) (𝓝 q) := by + have hne : (Ioo p v).Nonempty := nonempty_Ioo.2 hpv + have hqw : q < w := nonempty_Ioo.1 (himg ▸ hne.image f) + have hbdd : BddBelow (f '' Ioo p v) := himg ▸ bddBelow_Ioo + have h := MonotoneOn.tendsto_nhdsWithin_Ioo_right hne hm.monotoneOn hbdd + rwa [himg, csInf_Ioo hqw] at h + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/TwoComponents.lean b/LeanPool/OneManifold/OneMfld/TwoComponents.lean new file mode 100644 index 000000000..6346a8e7c --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/TwoComponents.lean @@ -0,0 +1,336 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import LeanPool.OneManifold.OneMfld.Outer +public import LeanPool.OneManifold.OneMfld.TransitionMono +public import LeanPool.OneManifold.OneMfld.GlueCore + + +/-! # The two-component structure of a disconnected overlap + +When two interior charts overlap disconnectedly, the overlap has exactly two connected +components, one at each end of each chart (outer-overlap lemma + two end-segments at the +same end must intersect). We also generalize the end-matching theorems of `GlueCore` from +the full overlap to a single component `W`: the extra hypothesis is that the relevant +interior endpoint is not in the image of the *full* overlap. +-/ + +@[expose] public section + +namespace OneMfld + +open Set Filter Topology + +/-- A strictly antitone map of `Ioo p v` onto `Ioo q w` tends to `w` at the bottom end. -/ +theorem tendsto_bot_of_strictAntiOn_image {p v q w : NNReal} (hpv : p < v) + {f : NNReal → NNReal} (hm : StrictAntiOn f (Ioo p v)) + (himg : f '' Ioo p v = Ioo q w) : + Tendsto f (𝓝[>] p) (𝓝 w) := by + have hne : (Ioo p v).Nonempty := nonempty_Ioo.2 hpv + have hqw : q < w := nonempty_Ioo.1 (himg ▸ hne.image f) + have hbdd : BddAbove (f '' Ioo p v) := himg ▸ bddAbove_Ioo + have h := AntitoneOn.tendsto_nhdsWithin_Ioo_right hne hm.antitoneOn hbdd + rwa [himg, csSup_Ioo hqw] at h + +variable {M : Type*} [TopologicalSpace M] + +/-- Mirror component version: with the `p`-end interior to `a.target`, the `w`-end +interior to `b.target`, and `p` not in the image of the full overlap, the transition is +again increasing. -/ +theorem overlap_mono_on' [T2Space M] (a b : OpenPartialHomeomorph M NNReal) + {W : Set M} (hW : W ⊆ a.source ∩ b.source) + {p r q w : NNReal} + (ha : a '' W = Ioo p r) + (hb : b '' W = Ioo q w) + (hp : p ∈ a.target) (hw : w ∈ b.target) + (hpS : p ∉ a '' (a.source ∩ b.source)) : + ∀ x ∈ W, ∀ y ∈ W, a x < a y → b x < b y := by + have hWa : W ⊆ a.source := hW.trans inter_subset_left + have hWb : W ⊆ b.source := hW.trans inter_subset_right + have hacoord : ∀ x ∈ W, a x ∈ Ioo p r := fun x hx => ha ▸ mem_image_of_mem a hx + by_cases hpr : p < r + swap + · intro x hx y hy hxy + exact absurd ((hacoord x hx).1.trans (hacoord x hx).2) hpr + set τ : NNReal → NNReal := fun t => b (a.symm t) with hτdef + have hsymm : ∀ t ∈ Ioo p r, a.symm t ∈ W ∧ a (a.symm t) = t := by + intro t ht + rw [← ha] at ht + obtain ⟨x, hxW, rfl⟩ := ht + rw [a.left_inv (hWa hxW)] + exact ⟨hxW, rfl⟩ + have hkey : ∀ x ∈ W, τ (a x) = b x := by + intro x hx + simp only [hτdef] + rw [a.left_inv (hWa hx)] + have hτmaps : ∀ t ∈ Ioo p r, τ t ∈ Ioo q w := by + intro t ht + have hb' : b (a.symm t) ∈ b '' W := mem_image_of_mem b (hsymm t ht).1 + exact hb ▸ hb' + have hIoo_target : Ioo p r ⊆ a.target := by + rw [← ha] + rintro y ⟨z, hz, rfl⟩ + exact a.map_source (hWa hz) + obtain ⟨hcont, hinj⟩ : ContinuousOn τ (Ioo p r) ∧ InjOn τ (Ioo p r) := + chartTransition_continuous_injective a b hIoo_target + (fun t ht => hWb (hsymm t ht).1) + rcases strictMonoOn_or_strictAntiOn_of_injOn_Ioo hcont hinj with hmono | hanti + · intro x hx y hy hxy + have h := hmono (hacoord x hx) (hacoord y hy) hxy + rwa [hkey x hx, hkey y hy] at h + · exfalso + have himg : τ '' Ioo p r = Ioo q w := by + apply subset_antisymm + · rintro _ ⟨t, ht, rfl⟩ + exact hτmaps t ht + · intro y hy + rw [← hb] at hy + obtain ⟨x, hxW, rfl⟩ := hy + exact ⟨a x, hacoord x hxW, hkey x hxW⟩ + have hτlim : Tendsto τ (𝓝[>] p) (𝓝 w) := + tendsto_bot_of_strictAntiOn_image hpr hanti himg + have hF : 𝓝[Ioo p r] p = 𝓝[>] p := nhdsWithin_Ioo_eq_nhdsGT hpr + have hFne : (𝓝[Ioo p r] p).NeBot := by + apply mem_closure_iff_nhdsWithin_neBot.mp + rw [closure_Ioo hpr.ne] + exact ⟨le_refl p, hpr.le⟩ + have hτend : Tendsto τ (𝓝[Ioo p r] p) (𝓝 w) := hF ▸ hτlim + have hz : a.symm p = b.symm w := + chartTransition_endpoint_eq a b hIoo_target + (fun t ht => hWb (hsymm t ht).1) hp hw hFne hτend + have hzS : a.symm p ∈ a.source ∩ b.source := by + refine ⟨a.map_target hp, ?_⟩ + rw [hz] + exact b.map_target hw + exact hpS ⟨a.symm p, hzS, a.right_inv hp⟩ + +/-- **Two-component structure.** A disconnected overlap of a chart with target `Ioo 0 1` +and a connected-source chart has exactly two components: one whose image is a lower +end-segment `Ioo 0 r` and one whose image is an upper end-segment `Ioo p 1`, with +`r ≤ p`. -/ +theorem two_components_structure [T2Space M] (a b : OpenPartialHomeomorph M NNReal) + (hat : a.target = Ioo 0 1) + (hb : IsConnected b.source) + (_hab : (a.source \ b.source).Nonempty) + (hba : (b.source \ a.source).Nonempty) + (hne : (a.source ∩ b.source).Nonempty) + (hdisc : ¬ IsConnected (a.source ∩ b.source)) : + ∃ (W₀ W₁ : Set M) (r p : NNReal), + a.source ∩ b.source = W₀ ∪ W₁ ∧ + (∀ z ∈ W₀, connectedComponentIn (a.source ∩ b.source) z = W₀) ∧ + (∀ z ∈ W₁, connectedComponentIn (a.source ∩ b.source) z = W₁) ∧ + Disjoint W₀ W₁ ∧ + a '' W₀ = Ioo 0 r ∧ a '' W₁ = Ioo p 1 ∧ + 0 < r ∧ r ≤ p ∧ p < 1 := by + set S := a.source ∩ b.source with hSdef + have hSa : S ⊆ a.source := inter_subset_left + have outer : ∀ x ∈ S, + (∃ p, 0 ≤ p ∧ p < 1 ∧ a '' connectedComponentIn S x = Ioo p 1) ∨ + (∃ q, 0 < q ∧ q ≤ 1 ∧ a '' connectedComponentIn S x = Ioo 0 q) := + fun x hx => overlap_component_outer_Ioo a b hat hb hba hx + -- Two components whose images are both lower end-segments coincide. + have hlow : ∀ x y : M, ∀ r r' : NNReal, 0 < r → 0 < r' → + a '' connectedComponentIn S x = Ioo 0 r → + a '' connectedComponentIn S y = Ioo 0 r' → + connectedComponentIn S x = connectedComponentIn S y := by + intro x y r r' hr hr' hix hiy + obtain ⟨z, hz1, hz2⟩ := exists_between (lt_min hr hr') + have hzx : z ∈ a '' connectedComponentIn S x := + hix ▸ mem_Ioo.mpr ⟨hz1, hz2.trans_le (min_le_left _ _)⟩ + have hzy : z ∈ a '' connectedComponentIn S y := + hiy ▸ mem_Ioo.mpr ⟨hz1, hz2.trans_le (min_le_right _ _)⟩ + obtain ⟨w1, hw1, hw1z⟩ := hzx + obtain ⟨w2, hw2, hw2z⟩ := hzy + have hw12 : w1 = w2 := + a.injOn (hSa (connectedComponentIn_subset _ _ hw1)) + (hSa (connectedComponentIn_subset _ _ hw2)) (hw1z.trans hw2z.symm) + calc connectedComponentIn S x + = connectedComponentIn S w1 := connectedComponentIn_eq hw1 + _ = connectedComponentIn S w2 := by rw [hw12] + _ = connectedComponentIn S y := (connectedComponentIn_eq hw2).symm + -- Two components whose images are both upper end-segments coincide. + have hup : ∀ x y : M, ∀ p p' : NNReal, p < 1 → p' < 1 → + a '' connectedComponentIn S x = Ioo p 1 → + a '' connectedComponentIn S y = Ioo p' 1 → + connectedComponentIn S x = connectedComponentIn S y := by + intro x y p p' hp hp' hix hiy + obtain ⟨z, hz1, hz2⟩ := exists_between (max_lt hp hp') + have hzx : z ∈ a '' connectedComponentIn S x := + hix ▸ mem_Ioo.mpr ⟨(le_max_left _ _).trans_lt hz1, hz2⟩ + have hzy : z ∈ a '' connectedComponentIn S y := + hiy ▸ mem_Ioo.mpr ⟨(le_max_right _ _).trans_lt hz1, hz2⟩ + obtain ⟨w1, hw1, hw1z⟩ := hzx + obtain ⟨w2, hw2, hw2z⟩ := hzy + have hw12 : w1 = w2 := + a.injOn (hSa (connectedComponentIn_subset _ _ hw1)) + (hSa (connectedComponentIn_subset _ _ hw2)) (hw1z.trans hw2z.symm) + calc connectedComponentIn S x + = connectedComponentIn S w1 := connectedComponentIn_eq hw1 + _ = connectedComponentIn S w2 := by rw [hw12] + _ = connectedComponentIn S y := (connectedComponentIn_eq hw2).symm + -- Main construction from one lower-imaged and one upper-imaged (distinct) component. + have main : ∀ x' y' : M, ∀ r p : NNReal, 0 < r → p < 1 → + a '' connectedComponentIn S x' = Ioo 0 r → + a '' connectedComponentIn S y' = Ioo p 1 → + connectedComponentIn S x' ≠ connectedComponentIn S y' → + ∃ (W₀ W₁ : Set M) (r' p' : NNReal), + S = W₀ ∪ W₁ ∧ + (∀ z ∈ W₀, connectedComponentIn S z = W₀) ∧ + (∀ z ∈ W₁, connectedComponentIn S z = W₁) ∧ + Disjoint W₀ W₁ ∧ + a '' W₀ = Ioo 0 r' ∧ a '' W₁ = Ioo p' 1 ∧ + 0 < r' ∧ r' ≤ p' ∧ p' < 1 := by + intro x' y' r p hr0 hp1 hxi hyi hne' + have hcomp₀ : ∀ z ∈ connectedComponentIn S x', + connectedComponentIn S z = connectedComponentIn S x' := + fun z hz => (connectedComponentIn_eq hz).symm + have hcomp₁ : ∀ z ∈ connectedComponentIn S y', + connectedComponentIn S z = connectedComponentIn S y' := + fun z hz => (connectedComponentIn_eq hz).symm + have hdisj : Disjoint (connectedComponentIn S x') (connectedComponentIn S y') := by + rw [Set.disjoint_left] + intro z hz₀ hz₁ + exact hne' ((hcomp₀ z hz₀).symm.trans (hcomp₁ z hz₁)) + have hunion : S = connectedComponentIn S x' ∪ connectedComponentIn S y' := by + apply subset_antisymm + · intro z hz + rcases outer z hz with ⟨p'', _, hp''1, hpi''⟩ | ⟨r'', hr''0, _, hri''⟩ + · right + rw [← hup z y' p'' p hp''1 hp1 hpi'' hyi] + exact mem_connectedComponentIn hz + · left + rw [← hlow z x' r'' r hr''0 hr0 hri'' hxi] + exact mem_connectedComponentIn hz + · exact union_subset (connectedComponentIn_subset _ _) + (connectedComponentIn_subset _ _) + have hrp : r ≤ p := by + by_contra h + rw [not_le] at h + obtain ⟨t, ht1, ht2⟩ := exists_between (lt_min h hp1) + have htW₀ : t ∈ a '' connectedComponentIn S x' := + hxi ▸ mem_Ioo.mpr ⟨(zero_le : (0 : NNReal) ≤ p).trans_lt ht1, + ht2.trans_le (min_le_left _ _)⟩ + have htW₁ : t ∈ a '' connectedComponentIn S y' := + hyi ▸ mem_Ioo.mpr ⟨ht1, ht2.trans_le (min_le_right _ _)⟩ + obtain ⟨w1, hw1, hw1t⟩ := htW₀ + obtain ⟨w2, hw2, hw2t⟩ := htW₁ + have hw12 : w1 = w2 := + a.injOn (hSa (connectedComponentIn_subset _ _ hw1)) + (hSa (connectedComponentIn_subset _ _ hw2)) (hw1t.trans hw2t.symm) + have hw2' : w1 ∈ connectedComponentIn S y' := by rw [hw12]; exact hw2 + exact Set.disjoint_left.mp hdisj hw1 hw2' + exact ⟨connectedComponentIn S x', connectedComponentIn S y', r, p, + hunion, hcomp₀, hcomp₁, hdisj, hxi, hyi, hr0, hrp, hp1⟩ + -- Produce two distinct components. + obtain ⟨x₀, hx₀⟩ := hne + have hSnW : ¬ (S ⊆ connectedComponentIn S x₀) := by + intro h + apply hdisc + have hSW : S = connectedComponentIn S x₀ := + subset_antisymm h (connectedComponentIn_subset S x₀) + rw [hSW] + exact isConnected_connectedComponentIn_iff.mpr hx₀ + obtain ⟨y₀, hy₀S, hy₀W⟩ := not_subset.mp hSnW + have hWW' : connectedComponentIn S x₀ ≠ connectedComponentIn S y₀ := by + intro h + apply hy₀W + rw [h] + exact mem_connectedComponentIn hy₀S + rcases outer x₀ hx₀ with ⟨p, _, hp1, hpi⟩ | ⟨r, hr0, _, hri⟩ <;> + rcases outer y₀ hy₀S with ⟨p', _, hp'1, hpi'⟩ | ⟨r', hr'0, _, hri'⟩ + · exact absurd (hup x₀ y₀ p p' hp1 hp'1 hpi hpi') hWW' + · exact main y₀ x₀ r' p hr'0 hp1 hri' hpi (Ne.symm hWW') + · exact main x₀ y₀ r p' hr0 hp'1 hri hpi' hWW' + · exact absurd (hlow x₀ y₀ r r' hr0 hr'0 hri hri') hWW' + +/-- The other chart also sees the two components as end-segments, one lower and one +upper (in one of the two possible arrangements). -/ +theorem two_components_other_chart [T2Space M] (a b : OpenPartialHomeomorph M NNReal) + (hbt : b.target = Ioo 0 1) + (haconn : IsConnected a.source) + (hab : (a.source \ b.source).Nonempty) + {W₀ W₁ : Set M} + (hW₀ : ∀ z ∈ W₀, connectedComponentIn (a.source ∩ b.source) z = W₀) + (hW₁ : ∀ z ∈ W₁, connectedComponentIn (a.source ∩ b.source) z = W₁) + (hne₀ : W₀.Nonempty) (hne₁ : W₁.Nonempty) (hd : Disjoint W₀ W₁) : + ∃ s q : NNReal, 0 < s ∧ s ≤ q ∧ q < 1 ∧ + ((b '' W₀ = Ioo q 1 ∧ b '' W₁ = Ioo 0 s) ∨ + (b '' W₀ = Ioo 0 s ∧ b '' W₁ = Ioo q 1)) := by + set S := a.source ∩ b.source with hSdef + -- The components are inside the overlap. + have hW₀S : W₀ ⊆ S := by + intro z hz + by_contra hzS + have h1 : W₀ = (∅ : Set M) := + (hW₀ z hz).symm.trans (connectedComponentIn_eq_empty hzS) + rw [h1] at hz + simp at hz + have hW₁S : W₁ ⊆ S := by + intro z hz + by_contra hzS + have h1 : W₁ = (∅ : Set M) := + (hW₁ z hz).symm.trans (connectedComponentIn_eq_empty hzS) + rw [h1] at hz + simp at hz + have hcomm : b.source ∩ a.source = S := by rw [hSdef]; exact inter_comm _ _ + -- The outer-overlap lemma seen from chart `b`. + have outer : ∀ z ∈ S, + (∃ q, 0 ≤ q ∧ q < 1 ∧ b '' connectedComponentIn S z = Ioo q 1) ∨ + (∃ s, 0 < s ∧ s ≤ 1 ∧ b '' connectedComponentIn S z = Ioo 0 s) := by + intro z hz + have hz' : z ∈ b.source ∩ a.source := ⟨hz.2, hz.1⟩ + have h := overlap_component_outer_Ioo b a hbt haconn hab hz' + rwa [hcomm] at h + obtain ⟨z₀, hz₀⟩ := hne₀ + obtain ⟨z₁, hz₁⟩ := hne₁ + have h₀ := outer z₀ (hW₀S hz₀) + rw [hW₀ z₀ hz₀] at h₀ + have h₁ := outer z₁ (hW₁S hz₁) + rw [hW₁ z₁ hz₁] at h₁ + -- A common point of the two `b`-images contradicts disjointness. + have hcommon : ∀ t : NNReal, t ∈ b '' W₀ → t ∈ b '' W₁ → False := by + intro t ht₀ ht₁ + obtain ⟨w1, hw1, hw1t⟩ := ht₀ + obtain ⟨w2, hw2, hw2t⟩ := ht₁ + have h12 : w1 = w2 := + b.injOn ((hW₀S.trans inter_subset_right) hw1) + ((hW₁S.trans inter_subset_right) hw2) (hw1t.trans hw2t.symm) + have hw2' : w1 ∈ W₁ := by rw [h12]; exact hw2 + exact Set.disjoint_left.mp hd hw1 hw2' + rcases h₀ with ⟨q₀, _hq₀0, hq₀1, hbW₀⟩ | ⟨s₀, hs₀0, _hs₀1, hbW₀⟩ <;> + rcases h₁ with ⟨q₁, _hq₁0, hq₁1, hbW₁⟩ | ⟨s₁, hs₁0, _hs₁1, hbW₁⟩ + · -- both upper: impossible + exfalso + obtain ⟨t, ht1, ht2⟩ := exists_between (max_lt hq₀1 hq₁1) + exact hcommon t (hbW₀ ▸ mem_Ioo.mpr ⟨(le_max_left _ _).trans_lt ht1, ht2⟩) + (hbW₁ ▸ mem_Ioo.mpr ⟨(le_max_right _ _).trans_lt ht1, ht2⟩) + · -- `W₀` upper, `W₁` lower + refine ⟨s₁, q₀, hs₁0, ?_, hq₀1, Or.inl ⟨hbW₀, hbW₁⟩⟩ + by_contra h + rw [not_le] at h + obtain ⟨t, ht1, ht2⟩ := exists_between (lt_min h hq₀1) + exact hcommon t + (hbW₀ ▸ mem_Ioo.mpr ⟨ht1, ht2.trans_le (min_le_right _ _)⟩) + (hbW₁ ▸ mem_Ioo.mpr ⟨(zero_le : (0 : NNReal) ≤ q₀).trans_lt ht1, + ht2.trans_le (min_le_left _ _)⟩) + · -- `W₀` lower, `W₁` upper + refine ⟨s₀, q₁, hs₀0, ?_, hq₁1, Or.inr ⟨hbW₀, hbW₁⟩⟩ + by_contra h + rw [not_le] at h + obtain ⟨t, ht1, ht2⟩ := exists_between (lt_min h hq₁1) + exact hcommon t + (hbW₀ ▸ mem_Ioo.mpr ⟨(zero_le : (0 : NNReal) ≤ q₁).trans_lt ht1, + ht2.trans_le (min_le_left _ _)⟩) + (hbW₁ ▸ mem_Ioo.mpr ⟨ht1, ht2.trans_le (min_le_right _ _)⟩) + · -- both lower: impossible + exfalso + obtain ⟨t, ht1, ht2⟩ := exists_between (lt_min hs₀0 hs₁0) + exact hcommon t (hbW₀ ▸ mem_Ioo.mpr ⟨ht1, ht2.trans_le (min_le_left _ _)⟩) + (hbW₁ ▸ mem_Ioo.mpr ⟨ht1, ht2.trans_le (min_le_right _ _)⟩) + +end OneMfld diff --git a/LeanPool/OneManifold/OneMfld/UnitInterval.lean b/LeanPool/OneManifold/OneMfld/UnitInterval.lean new file mode 100644 index 000000000..294796eb6 --- /dev/null +++ b/LeanPool/OneManifold/OneMfld/UnitInterval.lean @@ -0,0 +1,40 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import Mathlib.Tactic +public import Mathlib.Topology.MetricSpace.Pseudo.Lemmas +public import Mathlib.Topology.Order.Compact + + +/-! +# UnitInterval + +Supporting results for the classification of compact one-dimensional manifolds. +-/ + +@[expose] public section + +namespace OneMfld + +/-- The closed unit interval in the real line. -/ +def UnitInterval : Set Real := { x : Real | 0 ≤ x ∧ x ≤ 1 } + +/-- `UnitInterval` is definitionally `Set.Icc 0 1`; this bridge unlocks Mathlib's +`Icc` API (e.g. `iccHomeoI`, `isCompact_Icc`) for it. -/ +lemma UnitInterval_eq_Icc : UnitInterval = Set.Icc (0 : Real) 1 := rfl + +lemma isCompact_UnitInterval : IsCompact UnitInterval := by + rw [UnitInterval_eq_Icc] + exact isCompact_Icc + +instance : CompactSpace UnitInterval := + isCompact_iff_compactSpace.mp isCompact_UnitInterval + +instance : Nonempty UnitInterval := + ⟨⟨0, le_refl 0, zero_le_one⟩⟩ + +end OneMfld diff --git a/LeanPool/OneManifold/Solution.lean b/LeanPool/OneManifold/Solution.lean new file mode 100644 index 000000000..da5418177 --- /dev/null +++ b/LeanPool/OneManifold/Solution.lean @@ -0,0 +1,34 @@ +/- +Copyright (c) 2026 Jim Fowler, Dennis Sweeney. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jim Fowler, Dennis Sweeney +-/ +module + +public import LeanPool.OneManifold.OneMfld.Classification + + +/-! +# Proved solution + +This module imports the full proof development and restates the upstream Challenge +theorem with the identical name and type. The library's `OneMfld.classification` +(in `OneMfld/Classification.lean`) produces the homeomorphism as data, as a +term of `(M ≃ₜ Circle) ⊕ (M ≃ₜ UnitInterval)`; here we only need the +Prop-level disjunction. The Challenge's `{x : ℝ // 0 ≤ x ∧ x ≤ 1}` is +definitionally `↥OneMfld.UnitInterval` (and Mathlib's `↥unitInterval`). +-/ + +@[expose] public section + +namespace OneMfld + +theorem homeomorph_circle_or_unitInterval + (M : Type*) [TopologicalSpace M] [CompactSpace M] [ConnectedSpace M] + [T2Space M] [ChartedSpace NNReal M] : + Nonempty (M ≃ₜ Circle) ∨ Nonempty (M ≃ₜ {x : ℝ // 0 ≤ x ∧ x ≤ 1}) := by + rcases classification (M := M) ‹ChartedSpace NNReal M› with e | e + · exact Or.inl ⟨e⟩ + · exact Or.inr ⟨e⟩ + +end OneMfld diff --git a/LeanPool/projects.yml b/LeanPool/projects.yml index 5d174fc0b..d4a045be9 100644 --- a/LeanPool/projects.yml +++ b/LeanPool/projects.yml @@ -10109,6 +10109,39 @@ projects: msc: - '90C35' - '05C21' + - slug: 1mfld + title: The classification of compact 1-manifolds + summary: 'A Lean 4 (mathlib) proof of the classification of 1-manifolds, specifically that + every compact, connected, Hausdorff topological space charted on the half-line [0,∞) is + homeomorphic to the circle S¹ or to the closed unit interval [0,1]. The proof follows + David Gale''s outline: shrink to a finite atlas of interval charts, then induct on the + atlas size, merging two overlapping charts at each step; a connected overlap glues two + charts into one (two boundary charts glue onto [0, 1]), while a disconnected overlap has + exactly two components and yields the circle.' + branch: topology + entry_module: LeanPool.OneManifold + authors: + - Jim Fowler + - Dennis Sweeney + source: + url: https://github.com/sweeneyde/1mfld + github_repo: sweeneyde/1mfld + commit: 83e961ba28dd8182e7bae844756aba614eb1512e + license: Apache-2.0 + status: verified + main_declarations: + - OneMfld.homeomorph_circle_or_unitInterval + main_results: + - declaration: OneMfld.homeomorph_circle_or_unitInterval + informal: Every compact connected Hausdorff space charted on the nonnegative real half-line + is homeomorphic to the circle or the closed unit interval. + tags: + - topology + msc: + - 57N99 + - 54F65 + - 68V20 + provenance: mix - slug: asymptotic-triangle-packing title: Finite near-regular hypergraph rounding summary: >-