← Session 30 — ii′-5e squeeze capstone + 5f perimeter repair
thinking (7904 chars)
LiContour imports LiSides — so `zeta_order_ne_top_of_ne` is available in LiContour. And `analyticAt_riemannZeta (hρ : ρ ≠ 1)` exists (used at LiSides line ~131).
So `one_le_zeroMult` can go in LiContour right before ZeroCountProp:
```lean
/-- A nontrivial zero has multiplicity ≥ 1. -/
theorem one_le_zeroMult {ρ : ℂ} (hρ : IsNontrivialZero ρ) : 1 ≤ zeroMult ρ := by
have hρ1 : ρ ≠ 1 := by
intro h
rw [h] at hρ
-- IsNontrivialZero 1: re 1 < 1 false
exact (lt_irrefl (1 : ℝ)) (by ... )
```
hmm — IsNontrivialZero ρ gives ρ.re < 1; if ρ = 1 then ρ.re = 1: 1 < 1 false. `IsNontrivialZero (1:ℂ)`: (1:ℂ).re = 1 (Complex.one_re). So:
```lean
have hρ1 : ρ ≠ 1 := by
rintro rfl
have := hρ.2.2 -- (1:ℂ).re < 1
rw [Complex.one_re] at this
exact lt_irrefl _ this
have htop := zeta_order_ne_top_of_ne hρ1
have hne : analyticOrderAt riemannZeta ρ ≠ 0 :=
(analyticAt_riemannZeta hρ1).analyticOrderAt_ne_zero.mpr hρ.1
exact ENat.toNat_pos hne htop
```
Wait — zeroMult ρ := (analyticOrderAt riemannZeta ρ).toNat. ENat.toNat_pos (hne : n ≠ 0) (htop : n ≠ ⊤)? Check the signature: in LiDisc line 453: `ENat.toNat_pos hne htop : 0 < (analyticOrderAt ...).toNat` — wait actually the usage at line 445-455: `have h := ENat.toNat_pos hne htop` then `exact h` where goal `1 ≤ analyticOrderNatAt (scaledZeta t) w`. analyticOrderNatAt f z := (analyticOrderAt f z).toNat presumably. So ENat.toNat_pos : n ≠ 0 → n ≠ ⊤ → 0 < n.toNat. Goal 1 ≤ zeroMult ρ: unfold zeroMult → 1 ≤ (analyticOrderAt riemannZeta ρ).toNat ↔ 0 < ...toNat. ENat.toNat_pos hne htop : 0 < ...toNat — the goal 1 ≤ X vs 0 < X for ℕ: exact h works? 0 < n ↔ 1 ≤ n for ℕ — defeq? In Lean, Nat.lt is defined as n+1 ≤ m: 0 < X is 0+1 ≤ X = 1 ≤ X — is `0 + 1 ≤ X` syntactically `1 ≤ X`? Nat.lt n m := n+1 ≤ m, so 0 < X is 0+1 ≤ X; 0+1 reduces to 1 definitionally. `exact h` should work (defeq). In LiDisc they did `have h := ENat.toNat_pos hne htop; exact h` with goal `1 ≤ analyticOrderNatAt ...` — wait that goal shows `1 ≤ analyticOrderNatAt (scaledZeta t) w` and analyticOrderNatAt is a def for (analyticOrderAt ...).toNat? If analyticOrderNatAt is a def, exact h needs defeq through the def — worked in LiDisc ✓. For zeroMult — a Zeta23 def — same defeq should hold.
But hmm — `analyticOrderAt_ne_zero`: signature AnalyticAt.analyticOrderAt_ne_zero : AnalyticAt 𝕜 f z → (analyticOrderAt f z = 0 ↔ f z ≠ 0)? From LiDisc: `han.analyticOrderAt_ne_zero.mpr hw0` where hw0 : scaledZeta t w = 0 — so .mpr takes f w = 0 and produces analyticOrderAt ≠ 0 — so the iff is `analyticOrderAt f z = 0 ↔ f z ≠ 0` and .mpr : f z ≠ 0 → ... wait .mpr takes the RHS: f z ≠ 0? But hw0 : f w = 0... Let me re-read: `have hne : analyticOrderAt (scaledZeta t) w ≠ 0 := han.analyticOrderAt_ne_zero.mpr hw0`. If the iff were (= 0 ↔ f z ≠ 0), .mpr would take f z ≠ 0. hw0 : scaledZeta t w = 0 — so the iff must be `analyticOrderAt f z ≠ 0 ↔ f z = 0`! i.e. AnalyticAt.analyticOrderAt_ne_zero : AnalyticAt 𝕜 f z → (analyticOrderAt f z ≠ 0 ↔ f z = 0). Yes — that makes sense: order ≥ 1 iff vanishes. So `.mpr hρ.1` with hρ.1 : riemannZeta ρ = 0 ✓.
Check name: `AnalyticAt.analyticOrderAt_ne_zero` — from LiDisc usage ✓ exists.
Now the ZeroCountProp edit:
```lean
/-- [PERIMETER — Trudgian 2014 N(T)/S(T), D-062 §4] The unit-window
zero-count bound: the multiplicity-weighted count of nontrivial zeros
with ordinates in [t, t+1] is at most Ĉ₀(t+1) (t ≥ 2). Declared
counting input; Ĉ₀ = `C0hat` is D-056 §2's honest majorant
(LiSkeleton). [Erratum 2026-08-23 (session 035): strengthened from the
unweighted set-card form to the mult-weighted form — Trudgian's N(T)
counts with multiplicity; the set-card form under-encoded the declared
source and could not serve 5f/ii′-6's mult-weighted near sums. The
set-card bound follows from this form (card ≤ Σ mult).] -/
def ZeroCountProp : Prop :=
∀ t : ℝ, 2 ≤ t →
(((zeroWindow_finite t).toFinset.sum fun ρ => zeroMult ρ) : ℝ) ≤ C0hat (t + 1)
```
Hmm — `zeroWindow_finite t` is a theorem (proof); using it inside the def makes the def depend on a proof term — fine in Lean (it's a Finset-valued construction via Set.Finite.toFinset which is noncomputable but fine). Alternatively define the sum via a function: `zeroWindowFinset t := (zeroWindow_finite t).toFinset` then sum over it. Cleaner: add a def:
```lean
/-- The unit ordinate window as a finset. -/
noncomputable def zeroWindowFinset (t : ℝ) : Finset ℂ := (zeroWindow_finite t).toFinset
```
and ZeroCountProp: `∀ t : ℝ, 2 ≤ t → ((zeroWindowFinset t).sum (fun ρ => zeroMult ρ) : ℝ) ≤ C0hat (t + 1)`.
Wait — LiContour is already in `noncomputable section`? Line 36 open, and the section — LiContour line ~30s: import then open... does LiContour have `noncomputable section`? zeroWindow_finite is a theorem (proof of .Finite); toFinset needs noncomputable. Let me check. Actually the grep showed no "noncomputable" line in the first 40 lines... line 40 is `namespace NguyenRH1`? Let me check the structure. From the earlier read: line 40 namespace... Actually the first read of LiContour showed lines 40-80 starting "namespace NguyenRH1" at line 40. And imports at 30-34, open at 36-37. No noncomputable section visible — but maybe it's between 37 and 40. zeroWindow (a Set) doesn't need noncomputable; zeroWindow_finite is a proof. exists_avoiding_midpoint etc. are theorems. If there's no noncomputable section, adding `noncomputable def` explicitly is fine anyway.
Now the gap-circle adaptation in LiGapCircles.lean. Current code (lines 55-90):
```lean
set W1 := (zeroWindow_finite (4 * (k : ℝ) + 3 / 2)).toFinset
...
have hW1card : (W1.card : ℝ) ≤ C0hat (4 * (k : ℝ) + 3 / 2 + 1) := by
have h1 := hZC _ h2t1
have h2 : Nat.card (zeroWindow (4 * (k : ℝ) + 3 / 2)) = W1.card := by ...
```
After the change, hZC _ h2t1 : ((W1.sum fun ρ => zeroMult ρ) : ℝ) ≤ C0hat (4k+3/2+1) — wait, (zeroWindow_finite _).toFinset vs W1 := (zeroWindow_finite _).toFinset — same thing syntactically ✓ (set W1 := that exact term). Then:
```lean
have hW1card : (W1.card : ℝ) ≤ C0hat (4 * (k : ℝ) + 3 / 2 + 1) := by
have h1 := hZC _ h2t1 -- ((W1.sum fun ρ => zeroMult ρ) : ℝ) ≤ C0hat (...)
have h2 : W1.card ≤ W1.sum (fun ρ => zeroMult ρ) := by
rw [Finset.card_eq_sum_ones]
apply Finset.sum_le_sum
intro ρ hρ
exact one_le_zeroMult ((Set.Finite.mem_toFinset _ _).mp hρ).1
have h3 : (W1.card : ℝ) ≤ ((W1.sum fun ρ => zeroMult ρ) : ℝ) := Nat.cast_le.mpr h2
exact h3.trans h1
```
Check `Set.Finite.mem_toFinset (hs : s.Finite) : a ∈ hs.toFinset ↔ a ∈ s` — signature: Set.Finite.mem_toFinset (hs : s.Finite)? or (h : ...)? It's `Set.Finite.mem_toFinset (hs : s.Finite) : a ∈ hs.toFinset ↔ a ∈ s`. In LiGapCircles they must already convert membership somewhere — but h2's hρ : ρ ∈ W1; W1 is let-defined as (zeroWindow_finite _).toFinset; membership conversion needs the unfold — with `set`, W1 is the let var; `Set.Finite.mem_toFinset _ _` — hmm the implicit s... After set, hρ : ρ ∈ W1 where W1 := (zeroWindow_finite (4k+3/2)).toFinset. To convert: rw [show W1 = (zeroWindow_finite (4*(k:ℝ)+3/2)).toFinset from rfl] at hρ? With `set ... with hW1def`, hW1def : W1 = .... Hmm, actually `set` introduces the definition and hW1def. Let me instead obtain membership via: have hρ' : ρ ∈ zeroWindow (4*(k:ℝ)+3/2) := by have := (Set.Finite.mem_toFinset _ _).mp (by rwa [...]) — messy with set.
Simpler: in LiGapCircles, W1 is defined via `set W1 := (zeroWindow_finite (4 * (k : ℝ) + 3 / 2)).toFinset` — there might be a `with hW1` clause. Let me read the actual proof text around lines 55-100 to see the exact forms (also how they prove `Nat.card (zeroWindow ...) = W1.card` — probably via Set.Finite.toFinset + Nat.card_eq_finsetCard or similar; and whether membership bridging lemmas already exist there).
Also — the exact same pattern is used for W2 and W3.
Let me read LiGapCircles.lean lines 44-130.
LiContour imports LiSides — all deps available. Reading the gap-circle card steps to plan the exact adaptation.
tool: Read
input
{
"file_path": "D:\\Python\\Quantum\\research\\lean\\NguyenRH1\\NguyenRH1\\LiGapCircles.lean",
"offset": 44,
"limit": 75
}
raw content_json (database row)
[{"type":"thinking","thinking":"LiContour imports LiSides — so `zeta_order_ne_top_of_ne` is available in LiContour. And `analyticAt_riemannZeta (hρ : ρ ≠ 1)` exists (used at LiSides line ~131). \n\nSo `one_le_zeroMult` can go in LiContour right before ZeroCountProp:\n\n```lean\n/-- A nontrivial zero has multiplicity ≥ 1. -/\ntheorem one_le_zeroMult {ρ : ℂ} (hρ : IsNontrivialZero ρ) : 1 ≤ zeroMult ρ := by\n have hρ1 : ρ ≠ 1 := by\n intro h\n rw [h] at hρ\n -- IsNontrivialZero 1: re 1 < 1 false\n exact (lt_irrefl (1 : ℝ)) (by ... )\n```\nhmm — IsNontrivialZero ρ gives ρ.re < 1; if ρ = 1 then ρ.re = 1: 1 < 1 false. `IsNontrivialZero (1:ℂ)`: (1:ℂ).re = 1 (Complex.one_re). So:\n```lean\n have hρ1 : ρ ≠ 1 := by\n rintro rfl\n have := hρ.2.2 -- (1:ℂ).re < 1\n rw [Complex.one_re] at this\n exact lt_irrefl _ this\n have htop := zeta_order_ne_top_of_ne hρ1\n have hne : analyticOrderAt riemannZeta ρ ≠ 0 :=\n (analyticAt_riemannZeta hρ1).analyticOrderAt_ne_zero.mpr hρ.1\n exact ENat.toNat_pos hne htop\n```\nWait — zeroMult ρ := (analyticOrderAt riemannZeta ρ).toNat. ENat.toNat_pos (hne : n ≠ 0) (htop : n ≠ ⊤)? Check the signature: in LiDisc line 453: `ENat.toNat_pos hne htop : 0 < (analyticOrderAt ...).toNat` — wait actually the usage at line 445-455: `have h := ENat.toNat_pos hne htop` then `exact h` where goal `1 ≤ analyticOrderNatAt (scaledZeta t) w`. analyticOrderNatAt f z := (analyticOrderAt f z).toNat presumably. So ENat.toNat_pos : n ≠ 0 → n ≠ ⊤ → 0 < n.toNat. Goal 1 ≤ zeroMult ρ: unfold zeroMult → 1 ≤ (analyticOrderAt riemannZeta ρ).toNat ↔ 0 < ...toNat. ENat.toNat_pos hne htop : 0 < ...toNat — the goal 1 ≤ X vs 0 < X for ℕ: exact h works? 0 < n ↔ 1 ≤ n for ℕ — defeq? In Lean, Nat.lt is defined as n+1 ≤ m: 0 < X is 0+1 ≤ X = 1 ≤ X — is `0 + 1 ≤ X` syntactically `1 ≤ X`? Nat.lt n m := n+1 ≤ m, so 0 < X is 0+1 ≤ X; 0+1 reduces to 1 definitionally. `exact h` should work (defeq). In LiDisc they did `have h := ENat.toNat_pos hne htop; exact h` with goal `1 ≤ analyticOrderNatAt ...` — wait that goal shows `1 ≤ analyticOrderNatAt (scaledZeta t) w` and analyticOrderNatAt is a def for (analyticOrderAt ...).toNat? If analyticOrderNatAt is a def, exact h needs defeq through the def — worked in LiDisc ✓. For zeroMult — a Zeta23 def — same defeq should hold.\n\nBut hmm — `analyticOrderAt_ne_zero`: signature AnalyticAt.analyticOrderAt_ne_zero : AnalyticAt 𝕜 f z → (analyticOrderAt f z = 0 ↔ f z ≠ 0)? From LiDisc: `han.analyticOrderAt_ne_zero.mpr hw0` where hw0 : scaledZeta t w = 0 — so .mpr takes f w = 0 and produces analyticOrderAt ≠ 0 — so the iff is `analyticOrderAt f z = 0 ↔ f z ≠ 0` and .mpr : f z ≠ 0 → ... wait .mpr takes the RHS: f z ≠ 0? But hw0 : f w = 0... Let me re-read: `have hne : analyticOrderAt (scaledZeta t) w ≠ 0 := han.analyticOrderAt_ne_zero.mpr hw0`. If the iff were (= 0 ↔ f z ≠ 0), .mpr would take f z ≠ 0. hw0 : scaledZeta t w = 0 — so the iff must be `analyticOrderAt f z ≠ 0 ↔ f z = 0`! i.e. AnalyticAt.analyticOrderAt_ne_zero : AnalyticAt 𝕜 f z → (analyticOrderAt f z ≠ 0 ↔ f z = 0). Yes — that makes sense: order ≥ 1 iff vanishes. So `.mpr hρ.1` with hρ.1 : riemannZeta ρ = 0 ✓.\n\nCheck name: `AnalyticAt.analyticOrderAt_ne_zero` — from LiDisc usage ✓ exists.\n\nNow the ZeroCountProp edit:\n\n```lean\n/-- [PERIMETER — Trudgian 2014 N(T)/S(T), D-062 §4] The unit-window\nzero-count bound: the multiplicity-weighted count of nontrivial zeros\nwith ordinates in [t, t+1] is at most Ĉ₀(t+1) (t ≥ 2). Declared\ncounting input; Ĉ₀ = `C0hat` is D-056 §2's honest majorant\n(LiSkeleton). [Erratum 2026-08-23 (session 035): strengthened from the\nunweighted set-card form to the mult-weighted form — Trudgian's N(T)\ncounts with multiplicity; the set-card form under-encoded the declared\nsource and could not serve 5f/ii′-6's mult-weighted near sums. The\nset-card bound follows from this form (card ≤ Σ mult).] -/\ndef ZeroCountProp : Prop :=\n ∀ t : ℝ, 2 ≤ t →\n (((zeroWindow_finite t).toFinset.sum fun ρ => zeroMult ρ) : ℝ) ≤ C0hat (t + 1)\n```\n\nHmm — `zeroWindow_finite t` is a theorem (proof); using it inside the def makes the def depend on a proof term — fine in Lean (it's a Finset-valued construction via Set.Finite.toFinset which is noncomputable but fine). Alternatively define the sum via a function: `zeroWindowFinset t := (zeroWindow_finite t).toFinset` then sum over it. Cleaner: add a def:\n\n```lean\n/-- The unit ordinate window as a finset. -/\nnoncomputable def zeroWindowFinset (t : ℝ) : Finset ℂ := (zeroWindow_finite t).toFinset\n```\nand ZeroCountProp: `∀ t : ℝ, 2 ≤ t → ((zeroWindowFinset t).sum (fun ρ => zeroMult ρ) : ℝ) ≤ C0hat (t + 1)`.\n\nWait — LiContour is already in `noncomputable section`? Line 36 open, and the section — LiContour line ~30s: import then open... does LiContour have `noncomputable section`? zeroWindow_finite is a theorem (proof of .Finite); toFinset needs noncomputable. Let me check. Actually the grep showed no \"noncomputable\" line in the first 40 lines... line 40 is `namespace NguyenRH1`? Let me check the structure. From the earlier read: line 40 namespace... Actually the first read of LiContour showed lines 40-80 starting \"namespace NguyenRH1\" at line 40. And imports at 30-34, open at 36-37. No noncomputable section visible — but maybe it's between 37 and 40. zeroWindow (a Set) doesn't need noncomputable; zeroWindow_finite is a proof. exists_avoiding_midpoint etc. are theorems. If there's no noncomputable section, adding `noncomputable def` explicitly is fine anyway.\n\nNow the gap-circle adaptation in LiGapCircles.lean. Current code (lines 55-90):\n```lean\n set W1 := (zeroWindow_finite (4 * (k : ℝ) + 3 / 2)).toFinset\n ...\n have hW1card : (W1.card : ℝ) ≤ C0hat (4 * (k : ℝ) + 3 / 2 + 1) := by\n have h1 := hZC _ h2t1\n have h2 : Nat.card (zeroWindow (4 * (k : ℝ) + 3 / 2)) = W1.card := by ...\n```\nAfter the change, hZC _ h2t1 : ((W1.sum fun ρ => zeroMult ρ) : ℝ) ≤ C0hat (4k+3/2+1) — wait, (zeroWindow_finite _).toFinset vs W1 := (zeroWindow_finite _).toFinset — same thing syntactically ✓ (set W1 := that exact term). Then:\n```lean\n have hW1card : (W1.card : ℝ) ≤ C0hat (4 * (k : ℝ) + 3 / 2 + 1) := by\n have h1 := hZC _ h2t1 -- ((W1.sum fun ρ => zeroMult ρ) : ℝ) ≤ C0hat (...)\n have h2 : W1.card ≤ W1.sum (fun ρ => zeroMult ρ) := by\n rw [Finset.card_eq_sum_ones]\n apply Finset.sum_le_sum\n intro ρ hρ\n exact one_le_zeroMult ((Set.Finite.mem_toFinset _ _).mp hρ).1\n have h3 : (W1.card : ℝ) ≤ ((W1.sum fun ρ => zeroMult ρ) : ℝ) := Nat.cast_le.mpr h2\n exact h3.trans h1\n```\nCheck `Set.Finite.mem_toFinset (hs : s.Finite) : a ∈ hs.toFinset ↔ a ∈ s` — signature: Set.Finite.mem_toFinset (hs : s.Finite)? or (h : ...)? It's `Set.Finite.mem_toFinset (hs : s.Finite) : a ∈ hs.toFinset ↔ a ∈ s`. In LiGapCircles they must already convert membership somewhere — but h2's hρ : ρ ∈ W1; W1 is let-defined as (zeroWindow_finite _).toFinset; membership conversion needs the unfold — with `set`, W1 is the let var; `Set.Finite.mem_toFinset _ _` — hmm the implicit s... After set, hρ : ρ ∈ W1 where W1 := (zeroWindow_finite (4k+3/2)).toFinset. To convert: rw [show W1 = (zeroWindow_finite (4*(k:ℝ)+3/2)).toFinset from rfl] at hρ? With `set ... with hW1def`, hW1def : W1 = .... Hmm, actually `set` introduces the definition and hW1def. Let me instead obtain membership via: have hρ' : ρ ∈ zeroWindow (4*(k:ℝ)+3/2) := by have := (Set.Finite.mem_toFinset _ _).mp (by rwa [...]) — messy with set. \n\nSimpler: in LiGapCircles, W1 is defined via `set W1 := (zeroWindow_finite (4 * (k : ℝ) + 3 / 2)).toFinset` — there might be a `with hW1` clause. Let me read the actual proof text around lines 55-100 to see the exact forms (also how they prove `Nat.card (zeroWindow ...) = W1.card` — probably via Set.Finite.toFinset + Nat.card_eq_finsetCard or similar; and whether membership bridging lemmas already exist there).\n\nAlso — the exact same pattern is used for W2 and W3.\n\nLet me read LiGapCircles.lean lines 44-130.","signature":""},{"type":"text","text":"LiContour imports LiSides — all deps available. Reading the gap-circle card steps to plan the exact adaptation."},{"type":"tool_use","id":"Read_88","name":"Read","input":{"file_path":"D:\\Python\\Quantum\\research\\lean\\NguyenRH1\\NguyenRH1\\LiGapCircles.lean","offset":44,"limit":75},"extra_content":null}]