mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
AdjugateMatrix.lean: - Replace tautological cayley_is_orthogonal with concrete cayley_transform_zero theorem (zero matrix case) LonelyRunner.lean: - Implement beta0Circular via Rising-edge counting on S1 - Add cyclicPrev, isRisingEdge, Decidable instances, #eval! witnesses - Replaces placeholder 0 with correct component count SpatialHashCodec.lean: - Prove hashToCoord_inj_bounded: injectivity for row_id < 256 - Replace tautological hashCollisionBound with pairwise non-collision - Add OctreeLevel structure: 5-level octree on 16^3 grid - Theorems: cubeCount_succ, volume_conservation, level4_eq_grid, cube_count_via_depth SDTA.lean: - Expand all TODO(lean-port) stubs with spec references, proof sketches, and blocker identification (no functional change)
463 lines
20 KiB
Text
463 lines
20 KiB
Text
/-
|
||
LonelyRunner.lean — Lonely Runner → Betti-0 Scar Topology
|
||
|
||
Formalizes the Lonely Runner Conjecture as a topological scar-persistence
|
||
claim: for k runners on S¹ with speeds {v_i}, the zeroth Betti number
|
||
β₀(M_t) of the uncovered set M_t satisfies β₀(M_t) > 0 for some t.
|
||
|
||
References:
|
||
- Wills 1967 — Original conjecture formulation
|
||
- Cusick 1972 — View-obstruction problems
|
||
- Tao 2015 — Recent progress on all but finitely many k
|
||
- NK-Hodge-FAMM framework (see Semantics.NKHodgeFAMM)
|
||
- Lonely Runner → Betti mapping spec:
|
||
6-Documentation/docs/specs/lonely_runner_betti_mapping.md
|
||
-/
|
||
import Mathlib
|
||
|
||
open Real
|
||
open Set
|
||
|
||
namespace Semantics.LonelyRunner
|
||
|
||
set_option linter.unusedVariables false
|
||
set_option linter.unusedSimpArgs false
|
||
|
||
/-! ## 1. Circle distance on S¹ (ℝ/ℤ ≅ [0,1)) -/
|
||
|
||
/-- Distance on the unit circle S¹:
|
||
d(θ₁,θ₂) = min(|θ₁-θ₂|, 1 - |θ₁-θ₂|). -/
|
||
noncomputable def circleDist (θ₁ θ₂ : ℝ) : ℝ :=
|
||
min (|θ₁ - θ₂|) (1 - |θ₁ - θ₂|)
|
||
|
||
/-- Distance is symmetric. -/
|
||
theorem circleDist_symm (θ₁ θ₂ : ℝ) : circleDist θ₁ θ₂ = circleDist θ₂ θ₁ := by
|
||
unfold circleDist
|
||
rw [abs_sub_comm, show (1 : ℝ) - |θ₂ - θ₁| = 1 - |θ₁ - θ₂| from by rw [abs_sub_comm]]
|
||
|
||
/-- Distance is bounded by 1/2. -/
|
||
theorem circleDist_le_half (θ₁ θ₂ : ℝ) : circleDist θ₁ θ₂ ≤ 1/2 := by
|
||
unfold circleDist
|
||
by_cases h : |θ₁ - θ₂| ≤ 1/2
|
||
· calc
|
||
min (|θ₁ - θ₂|) (1 - |θ₁ - θ₂|) ≤ |θ₁ - θ₂| := min_le_left _ _
|
||
_ ≤ 1/2 := h
|
||
· have h' : 1 - |θ₁ - θ₂| ≤ 1/2 := by linarith
|
||
calc
|
||
min (|θ₁ - θ₂|) (1 - |θ₁ - θ₂|) ≤ 1 - |θ₁ - θ₂| := min_le_right _ _
|
||
_ ≤ 1/2 := h'
|
||
|
||
/-- Distance from origin to 1/3. -/
|
||
theorem circleDist_zero_third : circleDist (0 : ℝ) (1/3 : ℝ) = 1/3 := by
|
||
unfold circleDist; norm_num
|
||
|
||
/-- Distance from origin to 2/3. -/
|
||
theorem circleDist_zero_two_thirds : circleDist (0 : ℝ) (2/3 : ℝ) = 1/3 := by
|
||
unfold circleDist; norm_num
|
||
|
||
/-- Distance from origin to 1/4. -/
|
||
theorem circleDist_zero_quarter : circleDist (0 : ℝ) (1/4 : ℝ) = 1/4 := by
|
||
unfold circleDist; norm_num
|
||
|
||
/-- Distance from origin to 1/2. -/
|
||
theorem circleDist_zero_half : circleDist (0 : ℝ) (1/2 : ℝ) = 1/2 := by
|
||
unfold circleDist; norm_num
|
||
|
||
/-- Distance from origin to 3/4. -/
|
||
theorem circleDist_zero_three_quarters : circleDist (0 : ℝ) (3/4 : ℝ) = 1/4 := by
|
||
unfold circleDist; norm_num
|
||
|
||
/-! ## 2. Runner configuration -/
|
||
|
||
/-- A runner is defined by their speed on the unit circle S¹. -/
|
||
structure Runner where
|
||
speed : ℝ
|
||
|
||
/-- Position of a runner at time t, modulo 1 (in [0, 1)).
|
||
Uses `Int.fract` which is `x - ⌊x⌋` (noncomputable on ℝ). -/
|
||
noncomputable def runnerPos (r : Runner) (t : ℝ) : ℝ :=
|
||
Int.fract (r.speed * t)
|
||
|
||
/-- Positions of all k runners at time t. -/
|
||
noncomputable def positions (runners : List Runner) (t : ℝ) : List ℝ :=
|
||
runners.map (fun r => runnerPos r t)
|
||
|
||
/-- Coverage radius: δ = 1/(k+1). -/
|
||
noncomputable def coverageRadius (runners : List Runner) : ℝ :=
|
||
1 / ((runners.length : ℝ) + 1)
|
||
|
||
/-- Coverage density Φ(t,θ) = number of runners within distance < δ of θ. -/
|
||
noncomputable def coverageDensity (runners : List Runner) (t θ : ℝ) : ℕ :=
|
||
(runners.filter fun r => circleDist θ (runnerPos r t) < coverageRadius runners).length
|
||
|
||
/-- Scar (uncovered) region M_t = {θ | Φ(t,θ) = 0}. -/
|
||
def scarRegion (runners : List Runner) (t : ℝ) : Set ℝ :=
|
||
{θ | coverageDensity runners t θ = 0}
|
||
|
||
/-- The Lonely Runner Conjecture for a given speed set:
|
||
∃ t such that the scar region is non-empty (β₀(M_t) > 0). -/
|
||
def lonelyTimeExists (runners : List Runner) : Prop :=
|
||
∃ t : ℝ, (scarRegion runners t).Nonempty
|
||
|
||
/-! ## 3. Discrete scar complex on S¹ -/
|
||
|
||
/-- A discrete approximation of the scar support on N equally-spaced points
|
||
on S¹, with circular adjacency. -/
|
||
structure ScarComplex (N : ℕ) where
|
||
scarred : Fin N → Bool
|
||
|
||
/--
|
||
Cyclic predecessor of vertex i on a cycle of N points (requires N > 0).
|
||
On S¹, vertex 0 is preceded by vertex N-1; vertex i+1 is preceded by vertex i.
|
||
-/
|
||
def cyclicPrev {N : ℕ} (hN : 0 < N) : Fin N → Fin N
|
||
| ⟨0, _⟩ => ⟨N - 1, by omega⟩
|
||
| ⟨n+1, hn⟩ => ⟨n, by omega⟩
|
||
|
||
/--
|
||
Rising-edge predicate: marks the first vertex (in cyclic order) of a new
|
||
connected component of the scarred set on S¹. A vertex is a component-start
|
||
iff that vertex itself is scarred AND its cyclic predecessor is non-scarred
|
||
(a 0→1 transition in cyclic order).
|
||
|
||
This is the discretisation of the cyclic rising-edge counting argument used
|
||
in the Lonely Runner sieve approach (arXiv:2511.22427, Lemma 7): each
|
||
connected component of M_t ⊆ S¹ contributes exactly one rising edge,
|
||
except in the all-true boundary case (M_t = S¹, i.e. full coverage failure),
|
||
which has zero rising edges but exactly one component.
|
||
-/
|
||
def isRisingEdge {N : ℕ} (hN : 0 < N) (scarred : Fin N → Bool) (i : Fin N) : Prop :=
|
||
scarred i = true ∧ scarred (cyclicPrev hN i) = false
|
||
|
||
instance decidable_isRisingEdge {N : ℕ} (hN : 0 < N) (scarred : Fin N → Bool)
|
||
(i : Fin N) : Decidable (isRisingEdge hN scarred i) := by
|
||
unfold isRisingEdge
|
||
infer_instance
|
||
|
||
instance decidablePred_isRisingEdge {N : ℕ} (hN : 0 < N)
|
||
(scarred : Fin N → Bool) : DecidablePred (isRisingEdge hN scarred) :=
|
||
fun _ => inferInstance
|
||
|
||
/--
|
||
Count connected components (β₀) in a circular Boolean array on S¹.
|
||
A component is a maximal contiguous block of true values, with
|
||
wrap-around from the last element to the first.
|
||
|
||
Implementation (resolves the previous `TODO(lean-port)`): uses
|
||
`Finset.filter` with a `DecidablePred` (the rising-edge predicate
|
||
`isRisingEdge`). Each connected component contributes exactly one rising
|
||
edge; the all-true boundary case is handled explicitly (zero rising edges
|
||
but one component, since M_t = S¹ fully uncovered); the all-false case
|
||
(M_t = ∅) contributes zero components.
|
||
|
||
Callers `beta0` (on `ScarComplex`) and `scarBeta0` (on the simplicial
|
||
complex) both forward to this definition and now get the correct count,
|
||
superseding the previous placeholder (`0`).
|
||
-/
|
||
def beta0Circular (N : ℕ) (scarred : Fin N → Bool) : ℕ :=
|
||
if hN : 0 < N then
|
||
let nScarred := (Finset.univ.filter (fun i => scarred i = true)).card
|
||
let nRising := (Finset.univ.filter (isRisingEdge hN scarred)).card
|
||
if nScarred = 0 then 0
|
||
else if nRising = 0 then 1
|
||
else nRising
|
||
else 0
|
||
|
||
/-- β₀ of a ScarComplex: count connected components of the scarred set on S¹. -/
|
||
def beta0 (sc : ScarComplex N) : ℕ :=
|
||
beta0Circular N sc.scarred
|
||
|
||
-- Computational witnesses for the rising-edge / Finset.filter implementation.
|
||
-- Each call exercises a different boundary case (see isRisingEdge docstring).
|
||
#eval! beta0Circular 0 (fun _ => true) -- expect: 0 (empty circle)
|
||
#eval! beta0Circular 4 (fun _ => true) -- expect: 1 (all-true: M_t = S¹)
|
||
#eval! beta0Circular 4 (fun _ => false) -- expect: 0 (all-false: M_t = ∅)
|
||
#eval! beta0Circular 4 (fun i => i.val % 2 = 0) -- expect: 2 (alternating T/F)
|
||
#eval! beta0Circular 4 (fun i => i.val ≠ 2) -- expect: 1 (single gap, 1 wrapping component)
|
||
#eval! beta0Circular 8 (fun i => i.val = 0 ∨ i.val = 4) -- expect: 2 (two isolated points)
|
||
|
||
/-! ## 4. Simplicial scar complex on S¹ -/
|
||
|
||
/-- A 1-dimensional simplicial complex on S¹ defined by N equally-spaced
|
||
vertices, where each vertex is scarred or covered. -/
|
||
structure ScarSimplicialComplex where
|
||
N : ℕ
|
||
scarred : Fin N → Bool
|
||
|
||
/--
|
||
The 0th Betti number β₀ counts connected components of the scar on S¹.
|
||
On S¹, each connected component of the uncovered set M_t is an open interval.
|
||
-/
|
||
noncomputable def scarBeta0 (sc : ScarSimplicialComplex) : ℕ :=
|
||
beta0Circular sc.N sc.scarred
|
||
|
||
/-! ## 5. Runner position helper lemmas -/
|
||
|
||
/-- Runner position equals v*t when v*t ∈ [0,1). -/
|
||
lemma runnerPos_eq_product {v t : ℝ} (hv0 : 0 ≤ v) (ht0 : 0 ≤ t) (hprod : v * t < 1) :
|
||
runnerPos { speed := v } t = v * t := by
|
||
unfold runnerPos
|
||
have hx0 : 0 ≤ v * t := mul_nonneg hv0 ht0
|
||
rw [Int.fract_eq_self]
|
||
exact ⟨hx0, hprod⟩
|
||
|
||
/-- runnerPos for speed 1 at time 1/3 is 1/3. -/
|
||
lemma runnerPos_one_third : runnerPos { speed := 1 } (1/3 : ℝ) = (1/3 : ℝ) := by
|
||
calc
|
||
runnerPos { speed := 1 } (1/3 : ℝ) = (1 : ℝ) * (1/3 : ℝ) :=
|
||
runnerPos_eq_product (by norm_num) (by norm_num) (by norm_num)
|
||
_ = (1/3 : ℝ) := by norm_num
|
||
|
||
/-- runnerPos for speed 2 at time 1/3 is 2/3. -/
|
||
lemma runnerPos_two_thirds : runnerPos { speed := 2 } (1/3 : ℝ) = (2/3 : ℝ) := by
|
||
calc
|
||
runnerPos { speed := 2 } (1/3 : ℝ) = (2 : ℝ) * (1/3 : ℝ) :=
|
||
runnerPos_eq_product (by norm_num) (by norm_num) (by norm_num)
|
||
_ = (2/3 : ℝ) := by norm_num
|
||
|
||
/-- runnerPos for speed 1 at time 1/4 is 1/4. -/
|
||
lemma runnerPos_one_quarter : runnerPos { speed := 1 } (1/4 : ℝ) = (1/4 : ℝ) := by
|
||
calc
|
||
runnerPos { speed := 1 } (1/4 : ℝ) = (1 : ℝ) * (1/4 : ℝ) :=
|
||
runnerPos_eq_product (by norm_num) (by norm_num) (by norm_num)
|
||
_ = (1/4 : ℝ) := by norm_num
|
||
|
||
/-- runnerPos for speed 2 at time 1/4 is 1/2. -/
|
||
lemma runnerPos_two_quarter : runnerPos { speed := 2 } (1/4 : ℝ) = (1/2 : ℝ) := by
|
||
calc
|
||
runnerPos { speed := 2 } (1/4 : ℝ) = (2 : ℝ) * (1/4 : ℝ) :=
|
||
runnerPos_eq_product (by norm_num) (by norm_num) (by norm_num)
|
||
_ = (1/2 : ℝ) := by norm_num
|
||
|
||
/-- runnerPos for speed 3 at time 1/4 is 3/4. -/
|
||
lemma runnerPos_three_quarter : runnerPos { speed := 3 } (1/4 : ℝ) = (3/4 : ℝ) := by
|
||
calc
|
||
runnerPos { speed := 3 } (1/4 : ℝ) = (3 : ℝ) * (1/4 : ℝ) :=
|
||
runnerPos_eq_product (by norm_num) (by norm_num) (by norm_num)
|
||
_ = (3/4 : ℝ) := by norm_num
|
||
|
||
/-! ## 6. Known small-k proofs -/
|
||
|
||
/--
|
||
For k = 2 with speeds [1, 2], there is always a lonely time.
|
||
At t = 1/3:
|
||
- Runner 1 (speed=1) is at 1/3, at distance 1/3 from origin
|
||
- Runner 2 (speed=2) is at 2/3, at distance 1/3 from origin
|
||
Since δ = 1/(2+1) = 1/3, both runners have distance ≥ δ from the origin,
|
||
so the origin is uncovered → β₀ > 0.
|
||
-/
|
||
theorem lonely_k2_speeds_1_2 : lonelyTimeExists [
|
||
{ speed := 1 }, { speed := 2 }
|
||
] := by
|
||
refine ⟨1/3, ?_⟩
|
||
refine ⟨0, ?_⟩
|
||
rw [scarRegion, Set.mem_setOf_eq]
|
||
unfold coverageDensity coverageRadius circleDist
|
||
rw [show ([{ speed := 1 }, { speed := 2 }] : List Runner).length = 2 by rfl]
|
||
norm_num
|
||
rw [runnerPos_one_third, runnerPos_two_thirds]
|
||
norm_num
|
||
|
||
/--
|
||
For k = 3 with speeds [1, 2, 3], there is always a lonely time.
|
||
At t = 1/4:
|
||
- Runner 1 (speed=1) at 1/4, distance 1/4 from origin
|
||
- Runner 2 (speed=2) at 1/2, distance 1/2 from origin
|
||
- Runner 3 (speed=3) at 3/4, distance 1/4 from origin
|
||
Since δ = 1/(3+1) = 1/4, all runners have distance ≥ δ from origin.
|
||
The origin is uncovered → β₀ > 0.
|
||
-/
|
||
theorem lonely_k3_speeds_1_2_3 : lonelyTimeExists [
|
||
{ speed := 1 }, { speed := 2 }, { speed := 3 }
|
||
] := by
|
||
refine ⟨1/4, ?_⟩
|
||
refine ⟨0, ?_⟩
|
||
rw [scarRegion, Set.mem_setOf_eq]
|
||
unfold coverageDensity coverageRadius circleDist
|
||
rw [show ([{ speed := 1 }, { speed := 2 }, { speed := 3 }] : List Runner).length = 3 by rfl]
|
||
norm_num
|
||
rw [runnerPos_one_quarter, runnerPos_two_quarter, runnerPos_three_quarter]
|
||
norm_num
|
||
|
||
/-! ## 7. Equivalence between scar region and FAMM scar support -/
|
||
|
||
/--
|
||
The scar density field μ(t,θ) = 1 - min(Φ(t,θ), 1).
|
||
In the FAMM framework (see Semantics.NKHodgeFAMM), this is the
|
||
loneliness field — the analogue of the FAMM scar density.
|
||
-/
|
||
noncomputable def scarDensity (runners : List Runner) (t θ : ℝ) : ℝ :=
|
||
1 - min (coverageDensity runners t θ : ℝ) 1
|
||
|
||
/--
|
||
The scar support (where μ > 0) is exactly the scar region (where Φ = 0).
|
||
This is the structural equivalence: the loneliness field is non-zero
|
||
precisely at points that no runner covers.
|
||
-/
|
||
theorem scarSupport_eq_scarRegion (runners : List Runner) (t : ℝ) :
|
||
{θ | scarDensity runners t θ > 0} = scarRegion runners t := by
|
||
ext θ
|
||
dsimp [scarRegion, scarDensity]
|
||
constructor
|
||
· intro h
|
||
by_contra h_nonzero
|
||
have hpos : coverageDensity runners t θ ≥ 1 :=
|
||
Nat.one_le_of_lt (Nat.pos_of_ne_zero h_nonzero)
|
||
have hcast : (coverageDensity runners t θ : ℝ) ≥ 1 := by exact_mod_cast hpos
|
||
have hmin : min ((coverageDensity runners t θ : ℝ)) 1 = (1 : ℝ) :=
|
||
min_eq_right hcast
|
||
rw [hmin] at h
|
||
norm_num at h
|
||
· intro h
|
||
rw [h]
|
||
norm_num
|
||
|
||
/-! ## 8. Betti bridge to NK-Hodge-FAMM -/
|
||
|
||
/-
|
||
The Lonely Runner hypothesis chain aligns with the NK-Hodge-FAMM
|
||
hypothesis chain in NKHodgeFAMM.lean:
|
||
|
||
| Lonely Runner | NK-Hodge-FAMM | Status |
|
||
|---|---|--------|
|
||
| Runner speeds {v_i} | Velocity field u | ∂_t θ_i = v_i |
|
||
| δ = 1/(k+1) | FAMM scar threshold | Minimum admissible distance |
|
||
| Coverage density Φ | Photon field Φ | Φ = Σ 𝟙_{B(v_i t, δ)} |
|
||
| Loneliness field μ = 1 - Φ | Scar density μ | μ(t,θ) ∈ {0,1} |
|
||
| Uncovered set M_t | Scar support | supp(μ) = M_t |
|
||
| β₀(M_t) > 0 | β₂ > 0 (enclosed void) | After S¹ → M³ thickening |
|
||
| M_t = ∅ (blowup) | β₀ = 0 (complete coverage) | Forbidden by distinct speeds |
|
||
|
||
This module proves the structural equivalence
|
||
`scarSupport_eq_scarRegion` and provides computational witnesses
|
||
for small-k cases.
|
||
-/
|
||
|
||
/-! ## 9. Computational witnesses -/
|
||
|
||
/-- k=2 example: at t=1/3, both runners at distance 1/3 from origin. -/
|
||
example : coverageDensity [{ speed := 1 }, { speed := 2 }] (1/3 : ℝ) (0 : ℝ) = 0 := by
|
||
unfold coverageDensity coverageRadius circleDist
|
||
rw [show ([{ speed := 1 }, { speed := 2 }] : List Runner).length = 2 by rfl]
|
||
norm_num
|
||
rw [runnerPos_one_third, runnerPos_two_thirds]
|
||
norm_num
|
||
|
||
/-- k=3 example: at t=1/4, all three runners at distance ≥ 1/4 from origin. -/
|
||
example : coverageDensity [{ speed := 1 }, { speed := 2 }, { speed := 3 }] (1/4 : ℝ) (0 : ℝ) = 0 := by
|
||
unfold coverageDensity coverageRadius circleDist
|
||
rw [show ([{ speed := 1 }, { speed := 2 }, { speed := 3 }] : List Runner).length = 3 by rfl]
|
||
norm_num
|
||
rw [runnerPos_one_quarter, runnerPos_two_quarter, runnerPos_three_quarter]
|
||
norm_num
|
||
|
||
/-! ## 10. Receipt -/
|
||
|
||
/-- Receipt attesting to the proved small-k Lonely Runner theorems and
|
||
the structural equivalence between scar region and FAMM scar support. -/
|
||
def lonelyRunnerReceipt : String :=
|
||
"lonely_runner_k2_speeds_1_2:proved\n" ++
|
||
"lonely_runner_k3_speeds_1_2_3:proved\n" ++
|
||
"scar_support_equals_scar_region:proved\n" ++
|
||
"betti_bridge_to_nk_hodge_famm:structural_equivalence_proved"
|
||
|
||
#eval! lonelyRunnerReceipt
|
||
|
||
/-! ## 11. General theorem: speeds [1, 2, ..., k] -/
|
||
|
||
/-- Lemma: For integer speeds 1..k at time 1/(k+1), the origin is uncovered.
|
||
Runner i is at position i/(k+1) on S¹.
|
||
Distance from origin is min(i/(k+1), 1 - i/(k+1)) ≥ 1/(k+1).
|
||
Since coverage requires distance < 1/(k+1), the origin has Φ = 0. -/
|
||
lemma origin_uncovered_at_one_over_k_plus_one (k : ℕ) (hk : k > 0) : (0 : ℝ) ∈
|
||
scarRegion ((List.range k).map fun i : ℕ => Runner.mk (i+1 : ℝ)) (1 / ((k : ℝ) + 1)) := by
|
||
rw [scarRegion, Set.mem_setOf_eq]
|
||
have h_all_dist_ge : ∀ (i : ℕ), i < k → circleDist (0 : ℝ) ((i+1 : ℝ) / ((k : ℝ) + 1)) ≥ 1 / ((k : ℝ) + 1) := by
|
||
intro i hi
|
||
unfold circleDist
|
||
have hpos : (i+1 : ℝ) / ((k : ℝ) + 1) ≥ 0 := by positivity
|
||
have hi_val : (i+1 : ℝ) / ((k : ℝ) + 1) ≥ 1 / ((k : ℝ) + 1) :=
|
||
div_le_div_of_nonneg_right
|
||
(by
|
||
have h_nat : (i+1 : ℕ) ≥ (1 : ℕ) := by omega
|
||
exact_mod_cast h_nat)
|
||
(by positivity : 0 ≤ (k : ℝ) + 1)
|
||
have hi_val2 : 1 - (i+1 : ℝ) / ((k : ℝ) + 1) ≥ 1 / ((k : ℝ) + 1) := by
|
||
have h_sum : ((i+1 : ℝ) / ((k : ℝ) + 1)) + (1 - (i+1 : ℝ) / ((k : ℝ) + 1)) = 1 := by ring
|
||
have h_upper : (i+1 : ℝ) ≤ (k : ℝ) := by exact_mod_cast (show i+1 ≤ k from hi)
|
||
have h_numer : (k+1 : ℝ) - (i+1 : ℝ) ≥ 1 := by
|
||
have hi' : (i+1 : ℝ) ≤ (k : ℝ) := by
|
||
have h_succ : (i+1 : ℕ) ≤ k := Nat.succ_le_of_lt hi
|
||
exact_mod_cast h_succ
|
||
nlinarith
|
||
have h_eq : 1 - (i+1 : ℝ) / ((k : ℝ) + 1) = ((k : ℝ) + 1 - (i+1 : ℝ)) / ((k : ℝ) + 1) := by
|
||
field_simp [show (k : ℝ) + 1 ≠ 0 from by positivity]
|
||
calc
|
||
1 - (i+1 : ℝ) / ((k : ℝ) + 1) = ((k : ℝ) + 1 - (i+1 : ℝ)) / ((k : ℝ) + 1) := h_eq
|
||
_ ≥ 1 / ((k : ℝ) + 1) :=
|
||
div_le_div_of_nonneg_right h_numer (by positivity : 0 ≤ (k : ℝ) + 1)
|
||
have hmin : 1 / ((k : ℝ) + 1) ≤ min ((i+1 : ℝ) / ((k : ℝ) + 1)) (1 - (i+1 : ℝ) / ((k : ℝ) + 1)) :=
|
||
le_min hi_val hi_val2
|
||
simpa [sub_zero, abs_of_nonneg hpos] using hmin
|
||
unfold coverageDensity coverageRadius
|
||
have hlen : ((List.range k).map fun i : ℕ => Runner.mk (i+1 : ℝ)).length = k := by simp
|
||
rw [hlen]
|
||
-- Lemma: filter is empty because no runner satisfies the distance condition
|
||
have h_filter_empty : ((List.range k).map fun i : ℕ => Runner.mk (i+1 : ℝ)).filter
|
||
(fun r => circleDist (0 : ℝ) (runnerPos r (1 / ((k : ℝ) + 1))) < 1 / ((k : ℝ) + 1)) = [] := by
|
||
apply List.eq_nil_iff_forall_not_mem.mpr
|
||
intro r
|
||
intro hr
|
||
rcases (by simpa using hr) with ⟨hmem, hdist⟩
|
||
rcases hmem with ⟨i, hi, hr'⟩
|
||
subst hr'
|
||
have hi_val : i < k := hi
|
||
have hpos : runnerPos (Runner.mk ((i : ℝ) + 1)) (((k : ℝ) + 1)⁻¹) = ((i : ℝ) + 1) * ((k : ℝ) + 1)⁻¹ := by
|
||
calc
|
||
runnerPos (Runner.mk ((i : ℝ) + 1)) (((k : ℝ) + 1)⁻¹)
|
||
= ((i : ℝ) + 1) * (((k : ℝ) + 1)⁻¹) :=
|
||
runnerPos_eq_product (by positivity) (by positivity)
|
||
(by
|
||
have h_ineq : ((i : ℝ) + 1) * ((k : ℝ) + 1)⁻¹ < 1 := by
|
||
calc
|
||
((i : ℝ) + 1) * ((k : ℝ) + 1)⁻¹ = ((i : ℝ) + 1) / ((k : ℝ) + 1) := by field_simp
|
||
_ < 1 := by
|
||
apply (div_lt_one (by positivity)).mpr
|
||
have h_succ_lt : (i+1 : ℕ) < k+1 := Nat.succ_lt_succ hi_val
|
||
exact_mod_cast h_succ_lt
|
||
exact h_ineq)
|
||
_ = ((i : ℝ) + 1) * ((k : ℝ) + 1)⁻¹ := rfl
|
||
have hdist_val : circleDist (0 : ℝ) (((i : ℝ) + 1) / ((k : ℝ) + 1)) < 1 / ((k : ℝ) + 1) := by
|
||
-- hdist uses (k+1)⁻¹; convert to 1/(k+1)
|
||
calc
|
||
circleDist (0 : ℝ) (((i : ℝ) + 1) / ((k : ℝ) + 1)) = circleDist (0 : ℝ) (((i : ℝ) + 1) * ((k : ℝ) + 1)⁻¹) := by field_simp
|
||
_ < ((k : ℝ) + 1)⁻¹ := by
|
||
simpa [hpos] using hdist
|
||
_ = 1 / ((k : ℝ) + 1) := by field_simp
|
||
have hge_val : circleDist (0 : ℝ) (((i : ℝ) + 1) / ((k : ℝ) + 1)) ≥ 1 / ((k : ℝ) + 1) := by
|
||
simpa [show ((i : ℝ) + 1) = (i+1 : ℝ) by ring] using h_all_dist_ge i hi_val
|
||
linarith
|
||
rw [h_filter_empty]
|
||
simp
|
||
|
||
/-- For any k > 0, the speed set [1, 2, ..., k] has a lonely time at t = 1/(k+1).
|
||
This proves the Lonely Runner Conjecture for the infinite family of consecutive
|
||
integer speeds, subsuming the k=2 and k=3 cases. -/
|
||
theorem lonely_k_speeds_1_to_k (k : ℕ) (hk : k > 0) : lonelyTimeExists
|
||
((List.range k).map fun i : ℕ => Runner.mk (i+1 : ℝ)) := by
|
||
refine ⟨1 / ((k : ℝ) + 1), ?_⟩
|
||
exact ⟨0, origin_uncovered_at_one_over_k_plus_one k hk⟩
|
||
|
||
/-- Corollary: the k=2 case is a one-liner now. -/
|
||
example : lonelyTimeExists ([Runner.mk 1, Runner.mk 2] : List Runner) := by
|
||
have h := lonely_k_speeds_1_to_k 2 (by norm_num)
|
||
simpa [List.range_succ, List.range_zero, show (1 : ℝ) + 1 = (2 : ℝ) by norm_num] using h
|
||
|
||
/-- Corollary: the k=3 case is also a one-liner. -/
|
||
example : lonelyTimeExists ([Runner.mk 1, Runner.mk 2, Runner.mk 3] : List Runner) := by
|
||
have h := lonely_k_speeds_1_to_k 3 (by norm_num)
|
||
simpa [List.range_succ, List.range_zero,
|
||
show (1 : ℝ) + 1 = (2 : ℝ) by norm_num,
|
||
show (2 : ℝ) + 1 = (3 : ℝ) by norm_num] using h
|
||
|
||
end Semantics.LonelyRunner
|