/-
Copyright (c) 2025 Michael R. Douglas. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

# Hermite Functions and Nuclear Structure of Schwartz Space

Hermite functions ψₙ(x) = cₙ · Hₙ(x) · exp(-x²/2) form an orthonormal basis
of L²(ℝ) that also lies in Schwartz space 𝓢(ℝ). The Schwartz seminorm
‖ψₙ‖_{k,m} grows polynomially in n, making the inclusion maps between
Sobolev-Hermite levels Hilbert-Schmidt (and their compositions trace-class).

This file provides:
- Definitions of Hermite functions (1D and multi-dimensional)
- Key properties as axioms (L² orthonormality, Schwartz membership, seminorm bounds)
- These properties are sufficient to prove `schwartz_nuclear` (in DyninMityagin.lean)

The polynomial infrastructure is available in
  ../auto1/lean/SpecialFunctions/Hermite/Basic.lean
(probabilist's Hermite polynomials with full algebraic proofs).

## References

- Reed-Simon, "Methods of Modern Mathematical Physics" Vol. 1, §V.3
- Folland, "Harmonic Analysis in Phase Space", Ch. 1
- Thangavelu, "Lectures on Hermite and Laguerre Expansions"
- DLMF Chapter 18 (Orthogonal Polynomials)
-/

import Mathlib.RingTheory.Polynomial.Hermite.Basic
import Mathlib.RingTheory.Polynomial.Hermite.Gaussian
import Mathlib.Analysis.SpecialFunctions.Gaussian.GaussianIntegral
import Mathlib.Analysis.Distribution.SchwartzSpace.Deriv
import Mathlib.Analysis.InnerProductSpace.Basic
import Mathlib.Analysis.PSeries
import Mathlib.Topology.Algebra.InfiniteSum.Order
import Mathlib.MeasureTheory.Function.L2Space
import Mathlib.Topology.Algebra.Polynomial
import Mathlib.Analysis.Calculus.LineDeriv.IntegrationByParts
import Mathlib.MeasureTheory.Measure.Haar.NormedSpace
import Mathlib.Analysis.Distribution.AEEqOfIntegralContDiff
import Mathlib.Analysis.Fourier.FourierTransform
import Mathlib.Analysis.Fourier.Inversion
import Mathlib.Analysis.Distribution.SchwartzSpace.Fourier

open MeasureTheory Polynomial Real
open scoped ContDiff

noncomputable section

/-! ## Hermite Functions (1D)

The n-th Hermite function is:
  ψₙ(x) = (n! √π)^{-1/2} · Hₙ(x√2) · exp(-x²/2)
where Hₙ is the probabilist's Hermite polynomial (Mathlib's `hermite n`),
evaluated at x√2 to give the standard physicist's normalization.

This is equivalent to the physicist's convention:
  ψₙ(x) = (2ⁿ n! √π)^{-1/2} · Heₙ(x) · exp(-x²/2)
where Heₙ(x) = 2^{n/2} Hₙ(x√2) is the physicist's Hermite polynomial.

Key properties:
1. {ψₙ} forms an ONB of L²(ℝ)
2. Each ψₙ ∈ 𝓢(ℝ, ℝ) (Schwartz function)
3. ψₙ is an eigenfunction of the Fourier transform: ℱψₙ = (-i)ⁿ ψₙ
4. ψₙ is an eigenfunction of the harmonic oscillator: (-d²/dx² + x²)ψₙ = (2n+1)ψₙ
-/

/-- Normalization constant for the n-th Hermite function:
    cₙ = (n! · √π)^{-1/2} -/
def hermiteFunctionNormConst (n : ℕ) : ℝ :=
  Real.sqrt ((n.factorial * Real.sqrt Real.pi)⁻¹)

/-- The n-th Hermite function ψₙ(x) = cₙ · Hₙ(x√2) · exp(-x²/2)
    where Hₙ is the probabilist's Hermite polynomial evaluated at x√2. -/
def hermiteFunction (n : ℕ) (x : ℝ) : ℝ :=
  hermiteFunctionNormConst n *
  ((hermite n).map (Int.castRingHom ℝ)).eval (x * Real.sqrt 2) *
  Real.exp (-(x ^ 2) / 2)

/-! ## Key Properties (axioms — to be proved from Gaussian integral theory)

These are standard results from harmonic analysis. Proofs require:
- Gaussian integration: ∫ x^k exp(-x²) dx (partially in Mathlib)
- Recurrence-based orthogonality
- Schwartz space membership via polynomial bounds on derivatives

The polynomial algebraic infrastructure is available in auto1/lean/SpecialFunctions/Hermite/. -/

/-- Hermite functions are continuous. -/
private lemma continuous_hermiteFunction (n : ℕ) : Continuous (hermiteFunction n) := by
  unfold hermiteFunction hermiteFunctionNormConst
  fun_prop

/-- |x|^k * exp(-b*x²) ≤ k! * exp(1/(2b)) for all x, using Taylor + completing the square. -/
private lemma pow_mul_exp_neg_le (k : ℕ) {b : ℝ} (hb : 0 < b) (x : ℝ) :
    |x| ^ k * Real.exp (-b * x ^ 2) ≤ ↑k.factorial * Real.exp (1 / (2 * b)) := by
  have hpow : |x| ^ k ≤ ↑k.factorial * Real.exp (|x|) := by
    have hfact_pos : (0 : ℝ) < ↑k.factorial := Nat.cast_pos.mpr k.factorial_pos
    have h : |x| ^ k / ↑k.factorial ≤ Real.exp (|x|) := le_trans
      (Finset.single_le_sum
        (fun i (_ : i ∈ Finset.range (k + 1)) =>
          div_nonneg (pow_nonneg (abs_nonneg x) i) (Nat.cast_nonneg' (i.factorial)))
        (Finset.mem_range.mpr (by omega) : k ∈ Finset.range (k + 1)))
      (Real.sum_le_exp_of_nonneg (abs_nonneg x) (k + 1))
    rwa [div_le_iff₀ hfact_pos, mul_comm] at h
  have hquad : |x| ≤ b / 2 * x ^ 2 + 1 / (2 * b) := by
    have heq : b / 2 * x ^ 2 + 1 / (2 * b) = (b ^ 2 * x ^ 2 + 1) / (2 * b) := by field_simp
    rw [heq, le_div_iff₀ (by positivity : (0 : ℝ) < 2 * b)]
    nlinarith [sq_abs x, sq_nonneg (b * |x| - 1)]
  calc |x| ^ k * Real.exp (-b * x ^ 2)
      ≤ |x| ^ k * Real.exp (-(b / 2) * x ^ 2) := by
        apply mul_le_mul_of_nonneg_left _ (pow_nonneg (abs_nonneg x) k)
        exact Real.exp_le_exp.mpr (by nlinarith [sq_nonneg x])
    _ ≤ ↑k.factorial * Real.exp (|x|) * Real.exp (-(b / 2) * x ^ 2) :=
        mul_le_mul_of_nonneg_right hpow (Real.exp_pos _).le
    _ = ↑k.factorial * (Real.exp (|x|) * Real.exp (-(b / 2) * x ^ 2)) := by ring
    _ = ↑k.factorial * Real.exp (|x| + -(b / 2) * x ^ 2) := by rw [← Real.exp_add]
    _ ≤ ↑k.factorial * Real.exp (1 / (2 * b)) := by
        apply mul_le_mul_of_nonneg_left _ (Nat.cast_nonneg' _)
        exact Real.exp_le_exp.mpr (by linarith)

/-- Monomial times Gaussian is integrable: ∫ |x^k · exp(-b·x²)| dx < ∞.
    Uses the bound |x|^k ≤ k! · exp(|x|) (from Taylor series of exp) and
    |x| ≤ (b/2)·x² + 1/(2b) (completing the square) to get
    |x^k · exp(-b·x²)| ≤ k!·exp(1/(2b)) · exp(-(b/2)·x²). -/
private lemma integrable_pow_mul_exp_neg_mul_sq (k : ℕ) {b : ℝ} (hb : 0 < b) :
    Integrable (fun x : ℝ => x ^ k * Real.exp (-b * x ^ 2)) volume := by
  have hcont : Continuous (fun x : ℝ => x ^ k * Real.exp (-b * x ^ 2)) := by fun_prop
  set C : ℝ := ↑k.factorial * Real.exp (1 / (2 * b))
  refine Integrable.mono' ((integrable_exp_neg_mul_sq (half_pos hb)).const_mul C)
    hcont.aestronglyMeasurable ?_
  filter_upwards with x
  rw [Real.norm_eq_abs, abs_mul, abs_pow, abs_of_pos (Real.exp_pos _)]
  -- Goal: |x|^k * exp(-b*x²) ≤ C * exp(-(b/2)*x²)
  -- Step 1: |x|^k ≤ k! * exp(|x|) from Taylor bound
  have hpow : |x| ^ k ≤ ↑k.factorial * Real.exp (|x|) := by
    have hfact_pos : (0 : ℝ) < ↑k.factorial := Nat.cast_pos.mpr k.factorial_pos
    have h : |x| ^ k / ↑k.factorial ≤ Real.exp (|x|) := le_trans
      (Finset.single_le_sum
        (fun i (_ : i ∈ Finset.range (k + 1)) =>
          div_nonneg (pow_nonneg (abs_nonneg x) i) (Nat.cast_nonneg' (i.factorial)))
        (Finset.mem_range.mpr (by omega) : k ∈ Finset.range (k + 1)))
      (Real.sum_le_exp_of_nonneg (abs_nonneg x) (k + 1))
    rwa [div_le_iff₀ hfact_pos, mul_comm] at h
  -- Step 2: |x| ≤ (b/2)·x² + 1/(2b) by completing the square
  have hquad : |x| ≤ b / 2 * x ^ 2 + 1 / (2 * b) := by
    have heq : b / 2 * x ^ 2 + 1 / (2 * b) = (b ^ 2 * x ^ 2 + 1) / (2 * b) := by
      field_simp
    rw [heq, le_div_iff₀ (by positivity : (0 : ℝ) < 2 * b)]
    nlinarith [sq_abs x, sq_nonneg (b * |x| - 1)]
  -- Step 3: Factor exp(-b·x²) = exp(-(b/2)·x²) · exp(-(b/2)·x²) and combine
  have hfactor : Real.exp (-b * x ^ 2) =
      Real.exp (-(b / 2) * x ^ 2) * Real.exp (-(b / 2) * x ^ 2) := by
    rw [← Real.exp_add]; congr 1; ring
  rw [hfactor, show |x| ^ k * (Real.exp (-(b / 2) * x ^ 2) * Real.exp (-(b / 2) * x ^ 2)) =
    (|x| ^ k * Real.exp (-(b / 2) * x ^ 2)) * Real.exp (-(b / 2) * x ^ 2) from by ring]
  apply mul_le_mul_of_nonneg_right _ (Real.exp_pos _).le
  calc |x| ^ k * Real.exp (-(b / 2) * x ^ 2)
      ≤ ↑k.factorial * Real.exp (|x|) * Real.exp (-(b / 2) * x ^ 2) :=
        mul_le_mul_of_nonneg_right hpow (Real.exp_pos _).le
    _ = ↑k.factorial * (Real.exp (|x|) * Real.exp (-(b / 2) * x ^ 2)) := by ring
    _ = ↑k.factorial * Real.exp (|x| + -(b / 2) * x ^ 2) := by
        rw [← Real.exp_add]
    _ ≤ ↑k.factorial * Real.exp (1 / (2 * b)) := by
        apply mul_le_mul_of_nonneg_left _ (Nat.cast_nonneg' _)
        exact Real.exp_le_exp.mpr (by linarith)

/-- For any polynomial p and b > 0, the function x ↦ p(x) · exp(-b·x²) is integrable.
    Proved by polynomial induction, reducing to monomial integrability. -/
private lemma integrable_eval_mul_exp_neg_mul_sq (p : ℝ[X]) {b : ℝ} (hb : 0 < b) :
    Integrable (fun x : ℝ => p.eval x * Real.exp (-b * x ^ 2)) volume := by
  induction p using Polynomial.induction_on' with
  | add p q hp hq =>
    have : (fun x : ℝ => (p + q).eval x * Real.exp (-b * x ^ 2)) =
           (fun x => p.eval x * Real.exp (-b * x ^ 2)) +
           (fun x => q.eval x * Real.exp (-b * x ^ 2)) := by
      ext x; simp [Polynomial.eval_add, add_mul]
    rw [this]; exact hp.add hq
  | monomial k a =>
    simp only [Polynomial.eval_monomial]
    show Integrable (fun x : ℝ => a * x ^ k * Real.exp (-b * x ^ 2)) volume
    have heq : (fun x : ℝ => a * x ^ k * Real.exp (-b * x ^ 2)) =
               fun x => a * (x ^ k * Real.exp (-b * x ^ 2)) := by ext x; ring
    rw [heq]
    exact (integrable_pow_mul_exp_neg_mul_sq k hb).const_mul a

/-- For any polynomial P and b > 0, |P(x) * exp(-b*x²)| is uniformly bounded. -/
private lemma polynomial_eval_mul_exp_neg_bound (P : ℝ[X]) {b : ℝ} (hb : 0 < b) :
    ∃ C : ℝ, 0 ≤ C ∧ ∀ x : ℝ, |P.eval x * Real.exp (-b * x ^ 2)| ≤ C := by
  induction P using Polynomial.induction_on' with
  | add p q hp hq =>
    obtain ⟨Cp, hCp, hp⟩ := hp
    obtain ⟨Cq, hCq, hq⟩ := hq
    exact ⟨Cp + Cq, add_nonneg hCp hCq, fun x => by
      simp only [Polynomial.eval_add, add_mul]
      exact (abs_add_le _ _).trans (add_le_add (hp x) (hq x))⟩
  | monomial k a =>
    refine ⟨|a| * (↑k.factorial * Real.exp (1 / (2 * b))), by positivity, fun x => ?_⟩
    simp only [Polynomial.eval_monomial]
    rw [show a * x ^ k * Real.exp (-b * x ^ 2) =
      a * (x ^ k * Real.exp (-b * x ^ 2)) from by ring, abs_mul,
      show |x ^ k * Real.exp (-b * x ^ 2)| =
        |x| ^ k * Real.exp (-b * x ^ 2) from by
          rw [abs_mul, abs_pow, abs_of_pos (Real.exp_pos _)]]
    exact mul_le_mul_of_nonneg_left (pow_mul_exp_neg_le k hb x) (abs_nonneg a)

/-- Hermite functions are square-integrable. -/
theorem hermiteFunction_memLp (n : ℕ) :
    MemLp (hermiteFunction n) 2 MeasureTheory.volume := by
  have hcont := continuous_hermiteFunction n
  rw [← integrable_norm_rpow_iff hcont.aestronglyMeasurable two_ne_zero ENNReal.ofNat_ne_top]
  simp only [ENNReal.toReal_ofNat]
  -- Reduce to showing (hermiteFunction n)² is integrable (npow, avoiding rpow headaches)
  suffices h : Integrable (fun x : ℝ => (hermiteFunction n x) ^ 2) volume by
    exact h.congr (by
      filter_upwards with x
      rw [Real.norm_eq_abs]
      simp [sq_abs])
  -- Express (hermiteFunction n x)² as c² · (Q·Q)(x) · exp(-x²)
  -- where Q = (hermiteR n) ∘ (√2 · X), so Q(x) = Hₙ(x√2)
  set P := (hermite n).map (Int.castRingHom ℝ) with hP_def
  set c := hermiteFunctionNormConst n with hc_def
  set Q := P.comp (C (Real.sqrt 2) * X) with hQ_def
  have hQeval : ∀ x : ℝ, Q.eval x = P.eval (x * Real.sqrt 2) := by
    intro x; simp only [hQ_def, Polynomial.eval_comp, Polynomial.eval_mul,
      Polynomial.eval_C, Polynomial.eval_X]; congr 1; ring
  have h_eq : (fun x : ℝ => (hermiteFunction n x) ^ 2) =
              fun x => c ^ 2 * ((Q * Q).eval x * Real.exp (-1 * x ^ 2)) := by
    ext x
    unfold hermiteFunction
    simp only [← hc_def, ← hP_def, Polynomial.eval_mul, ← hQeval]
    have hexp : Real.exp (-(x ^ 2) / 2) * Real.exp (-(x ^ 2) / 2) = Real.exp (-1 * x ^ 2) := by
      rw [← Real.exp_add]; congr 1; ring
    calc (c * Q.eval x * Real.exp (-(x ^ 2) / 2)) ^ 2
        = c ^ 2 * Q.eval x ^ 2 *
          (Real.exp (-(x ^ 2) / 2) * Real.exp (-(x ^ 2) / 2)) := by ring
      _ = c ^ 2 * Q.eval x ^ 2 * Real.exp (-1 * x ^ 2) := by rw [hexp]
      _ = c ^ 2 * (Q.eval x * Q.eval x * Real.exp (-1 * x ^ 2)) := by ring
  rw [h_eq]
  exact (integrable_eval_mul_exp_neg_mul_sq (Q * Q) one_pos).const_mul (c ^ 2)

/-! ## Orthonormality proof infrastructure

We prove `hermiteFunction_orthonormal` via the IBP recurrence for
`J(n, m) = ∫ Hₙ(x) · Hₘ(x) · exp(-x²/2) dx`, where Hₙ = `hermite n` (probabilist's).
Key identity: `J(n, m+1) = n · J(n-1, m)`, giving `J(n,m) = δₙₘ · n! · √(2π)`.
-/

/-- Abbreviation: Hermite polynomial evaluated over ℝ. -/
abbrev hermiteR (n : ℕ) : ℝ[X] := (hermite n).map (Int.castRingHom ℝ)

/-- The derivative of the (n+1)-th Hermite polynomial is (n+1) times the n-th.
    Adapted from auto1's `probHermite_derivative`. -/
theorem hermite_derivative (n : ℕ) :
    derivative (hermite (n + 1)) = (↑(n + 1) : ℤ[X]) * hermite n := by
  induction n with
  | zero => simp [hermite_succ, hermite_zero]
  | succ n ih =>
    show derivative (X * hermite (n + 1) - derivative (hermite (n + 1)))
        = (↑(n + 2) : ℤ[X]) * hermite (n + 1)
    rw [map_sub, derivative_mul, derivative_X, one_mul]
    simp only [ih]
    rw [derivative_mul, derivative_natCast, zero_mul, zero_add]
    have step : hermite (n + 1) + X * ((↑(n + 1) : ℤ[X]) * hermite n) -
        (↑(n + 1) : ℤ[X]) * derivative (hermite n) =
        hermite (n + 1) +
        (↑(n + 1) : ℤ[X]) * (X * hermite n - derivative (hermite n)) := by ring
    rw [step, ← hermite_succ,
        show (↑(n + 2) : ℤ[X]) = 1 + ↑(n + 1) from by push_cast; ring]
    ring

/-- Derivative of Hermite polynomial over ℝ. -/
private theorem hermiteR_derivative (n : ℕ) :
    derivative (hermiteR (n + 1)) = (↑(n + 1) : ℝ[X]) * hermiteR n := by
  simp only [hermiteR]
  rw [Polynomial.derivative_map, hermite_derivative, Polynomial.map_mul, Polynomial.map_natCast]

/-- The unnormalized Gaussian weight `exp(-x²/2)` (no `1/√(2π)` factor). -/
abbrev gaussian (x : ℝ) : ℝ := Real.exp (-(x ^ 2 / 2))

/-- Derivative of `Hₘ(x) · exp(-x²/2)` equals `-H_{m+1}(x) · exp(-x²/2)`.
    From `H_{m+1} = X·Hₘ - Hₘ'` and the product rule. -/
private theorem deriv_hermiteEval_mul_gaussian (m : ℕ) (x : ℝ) :
    deriv (fun u => (hermiteR m).eval u * gaussian u) x =
    -((hermiteR (m + 1)).eval x) * gaussian x := by
  have hdiff_exp : DifferentiableAt ℝ (fun u : ℝ => Real.exp (-(u ^ 2 / 2))) x :=
    DifferentiableAt.exp (by fun_prop)
  have hd := deriv_fun_mul (hermiteR m).differentiable.differentiableAt hdiff_exp
  rw [hd]
  -- Convert deriv of polynomial eval via aeval
  have hpoly : deriv (fun u => (hermiteR m).eval u) x = (derivative (hermiteR m)).eval x := by
    have h1 : (fun u => (hermiteR m).eval u) = fun u => Polynomial.aeval u (hermiteR m) := by
      ext u; simp [Polynomial.aeval_def, Polynomial.eval₂_eq_eval_map, Polynomial.map_id]
    rw [h1, Polynomial.deriv_aeval]
    simp [Polynomial.aeval_def, Polynomial.eval₂_eq_eval_map, Polynomial.map_id]
  rw [hpoly]
  have hexp : deriv (fun u : ℝ => Real.exp (-(u ^ 2 / 2))) x = -x * gaussian x := by
    have h1 : HasDerivAt (fun u : ℝ => -(u ^ 2 / 2)) (-x) x := by
      have := ((hasDerivAt_pow 2 x).div_const 2).neg
      convert this using 1 <;> first | rfl | ring
    exact h1.exp.deriv.trans (mul_comm _ _)
  rw [hexp]
  -- Hermite recurrence: H_{m+1}(x) = x · Hₘ(x) - Hₘ'(x)
  have hrec : (hermiteR (m + 1)).eval x = x * (hermiteR m).eval x - (derivative (hermiteR m)).eval x := by
    have h1 : hermiteR (m + 1) = X * hermiteR m - derivative (hermiteR m) := by
      unfold hermiteR
      rw [hermite_succ, Polynomial.map_sub, Polynomial.map_mul, Polynomial.map_X,
        Polynomial.derivative_map]
    rw [h1]; simp [Polynomial.eval_sub, Polynomial.eval_mul, Polynomial.eval_X]
  rw [hrec]; ring

/-- Polynomial times `exp(-x²/2)` is integrable (wrapper around `integrable_eval_mul_exp_neg_mul_sq`). -/
private theorem integrable_hermiteR_mul_gaussian (p : ℝ[X]) :
    Integrable (fun x : ℝ => p.eval x * gaussian x) volume := by
  have h : (fun x : ℝ => p.eval x * gaussian x) =
      fun x => p.eval x * Real.exp (-(1/2) * x ^ 2) := by
    ext x; show p.eval x * Real.exp (-(x ^ 2 / 2)) = _; congr 1; congr 1; ring
  rw [h]
  exact integrable_eval_mul_exp_neg_mul_sq p (by positivity : (0:ℝ) < 1/2)

/-- Product of two Hermite polynomials times `exp(-x²/2)` is integrable. -/
private theorem integrable_hermiteProd_mul_gaussian (n m : ℕ) :
    Integrable (fun x : ℝ => (hermiteR n).eval x * (hermiteR m).eval x * gaussian x) volume := by
  have h : (fun x : ℝ => (hermiteR n).eval x * (hermiteR m).eval x * gaussian x) =
      fun x => (hermiteR n * hermiteR m).eval x * gaussian x := by
    ext x; simp [Polynomial.eval_mul, mul_assoc]
  rw [h]
  exact integrable_hermiteR_mul_gaussian (hermiteR n * hermiteR m)

/-- `fun x => Hₙ(x) * (-H_{m+1}(x) * exp(-x²/2))` is integrable. -/
private theorem integrable_hermite_mul_neg_hermite_gaussian (n m : ℕ) :
    Integrable (fun x : ℝ => (hermiteR n).eval x *
      (-(hermiteR (m + 1)).eval x * gaussian x)) volume := by
  have h : (fun x : ℝ => (hermiteR n).eval x * (-(hermiteR (m + 1)).eval x * gaussian x)) =
      fun x => -((hermiteR n * hermiteR (m + 1)).eval x * gaussian x) := by
    ext x; simp [Polynomial.eval_mul]; ring
  rw [h]
  exact (integrable_hermiteR_mul_gaussian (hermiteR n * hermiteR (m + 1))).neg

/-- `Hₘ(x) · exp(-x²/2)` is differentiable. -/
private theorem differentiable_hermiteEval_mul_gaussian (m : ℕ) :
    Differentiable ℝ (fun u => (hermiteR m).eval u * gaussian u) :=
  (hermiteR m).differentiable.mul
    (Differentiable.exp (by fun_prop : Differentiable ℝ (fun u : ℝ => -(u ^ 2 / 2))))

/-- The `J` integral: `∫ Hₙ(x) · Hₘ(x) · exp(-x²/2) dx`. -/
noncomputable def J (n m : ℕ) : ℝ :=
  ∫ x, (hermiteR n).eval x * ((hermiteR m).eval x * gaussian x)

/-- IBP recurrence: `J(n+1, m+1) = (n+1) · J(n, m)`. -/
private theorem J_succ_succ (n m : ℕ) : J (n + 1) (m + 1) = (↑(n + 1) : ℝ) * J n m := by
  unfold J
  -- We use IBP: ∫ f * g' = -∫ f' * g
  -- with f(x) = Hₙ₊₁(x) (polynomial) and g(x) = Hₘ(x)·exp(-x²/2)
  -- g'(x) = -H_{m+1}(x)·exp(-x²/2) (Step 2)
  -- f'(x) = (n+1)·Hₙ(x) (hermite_derivative)
  -- Set up: J(n+1, m+1) = ∫ Hₙ₊₁ · Hₘ₊₁ · G = -∫ Hₙ₊₁ · (d/dx[Hₘ·G])
  have hJ : ∫ x, (hermiteR (n + 1)).eval x * ((hermiteR (m + 1)).eval x * gaussian x) =
      -∫ x, (hermiteR (n + 1)).eval x *
        (deriv (fun u => (hermiteR m).eval u * gaussian u) x) := by
    have h_eq : ∀ x : ℝ, (hermiteR (n + 1)).eval x * ((hermiteR (m + 1)).eval x * gaussian x) =
        -((hermiteR (n + 1)).eval x *
          deriv (fun u => (hermiteR m).eval u * gaussian u) x) := by
      intro x; rw [deriv_hermiteEval_mul_gaussian]; ring
    simp_rw [h_eq, integral_neg]
  rw [hJ]
  -- Apply IBP with v = 1
  have hibp := integral_mul_fderiv_eq_neg_fderiv_mul_of_integrable (𝕜 := ℝ) (E := ℝ)
    (μ := volume)
    (f := fun x => (hermiteR (n + 1)).eval x)
    (g := fun u => (hermiteR m).eval u * gaussian u)
    (v := (1 : ℝ))
    ?_ ?_ ?_ (fun x _ => (hermiteR (n + 1)).differentiable.differentiableAt)
    (fun x _ => (differentiable_hermiteEval_mul_gaussian m).differentiableAt)
  -- IBP gives: ∫ f * g' = -∫ f' * g
  -- So -∫ f * g' = ∫ f' * g
  simp only [fderiv_apply_one_eq_deriv] at hibp
  rw [neg_eq_iff_eq_neg.mpr hibp]
  -- Simplify deriv of polynomial and factor out constant
  have h_rw : ∀ x : ℝ, deriv (fun x => (hermiteR (n + 1)).eval x) x *
      ((hermiteR m).eval x * gaussian x) =
      (↑(n + 1) : ℝ) * ((hermiteR n).eval x * ((hermiteR m).eval x * gaussian x)) := by
    intro x
    rw [Polynomial.deriv, hermiteR_derivative]
    simp [Polynomial.eval_mul, Polynomial.eval_natCast]; ring
  simp_rw [h_rw, integral_const_mul]
  -- Integrability obligations
  · -- fderiv f · g integrable
    show Integrable (fun x => fderiv ℝ (fun x => (hermiteR (n + 1)).eval x) x 1 *
      ((hermiteR m).eval x * gaussian x)) volume
    have : (fun x => fderiv ℝ (fun x => (hermiteR (n + 1)).eval x) x 1 *
        ((hermiteR m).eval x * gaussian x)) =
        fun x => ((↑(n + 1) : ℝ) * (hermiteR n).eval x) *
          ((hermiteR m).eval x * gaussian x) := by
      ext x
      rw [fderiv_apply_one_eq_deriv, Polynomial.deriv, hermiteR_derivative]
      simp [Polynomial.eval_mul, Polynomial.eval_natCast]
    rw [this]
    have : (fun x => ((↑(n + 1) : ℝ) * (hermiteR n).eval x) *
        ((hermiteR m).eval x * gaussian x)) =
        fun x => (↑(n + 1) : ℝ) * ((hermiteR n).eval x * (hermiteR m).eval x * gaussian x) := by
      ext x; ring
    rw [this]
    exact (integrable_hermiteProd_mul_gaussian n m).const_mul _
  · -- f · fderiv g integrable
    show Integrable (fun x => (hermiteR (n + 1)).eval x *
      (fderiv ℝ (fun u => (hermiteR m).eval u * gaussian u) x 1)) volume
    have : (fun x => (hermiteR (n + 1)).eval x *
        (fderiv ℝ (fun u => (hermiteR m).eval u * gaussian u) x 1)) =
        fun x => (hermiteR (n + 1)).eval x *
          deriv (fun u => (hermiteR m).eval u * gaussian u) x := by
      ext x; rw [fderiv_apply_one_eq_deriv]
    rw [this]
    have : (fun x => (hermiteR (n + 1)).eval x *
        deriv (fun u => (hermiteR m).eval u * gaussian u) x) =
        fun x => (hermiteR (n + 1)).eval x *
          (-(hermiteR (m + 1)).eval x * gaussian x) := by
      ext x; rw [deriv_hermiteEval_mul_gaussian]
    rw [this]
    exact integrable_hermite_mul_neg_hermite_gaussian (n + 1) m
  · -- f · g integrable
    exact (integrable_hermiteProd_mul_gaussian (n + 1) m).congr
      (by filter_upwards; intro x; ring)

/-- Symmetry of the J integral. -/
private theorem J_comm (n m : ℕ) : J n m = J m n := by
  unfold J; congr 1; ext x; ring

/-- Base case: `J(0, 0) = √(2π)`. -/
private theorem J_zero_zero : J 0 0 = Real.sqrt (2 * Real.pi) := by
  unfold J hermiteR
  simp only [hermite_zero]
  have h : (fun x : ℝ => (Polynomial.map (Int.castRingHom ℝ) (C 1)).eval x *
      ((Polynomial.map (Int.castRingHom ℝ) (C 1)).eval x * gaussian x)) =
      fun x => Real.exp (-(1/2) * x ^ 2) := by
    ext x; simp [gaussian]; ring
  rw [h, integral_gaussian (1/2)]
  congr 1; ring

/-- Base case: `J(k+1, 0) = 0` for all k.
    Proof: from `J_succ_succ` and `J_comm`, `(k+2)·J(k+1,0) = J(k+1,0)`, so `(k+1)·J(k+1,0) = 0`. -/
private theorem J_succ_zero (k : ℕ) : J (k + 1) 0 = 0 := by
  -- J(k+2, 1) = (k+2) * J(k+1, 0) by J_succ_succ
  have h1 : J (k + 2) 1 = (↑(k + 2) : ℝ) * J (k + 1) 0 := J_succ_succ (k + 1) 0
  -- J(k+2, 1) = J(1, k+2) = 1 * J(0, k+1) = J(k+1, 0)
  have h2 : J (k + 2) 1 = J (k + 1) 0 := by
    rw [J_comm (k + 2) 1]
    show J 1 (k + 2) = J (k + 1) 0
    rw [show (1 : ℕ) = 0 + 1 from rfl, show k + 2 = (k + 1) + 1 from rfl,
        J_succ_succ 0 (k + 1)]
    simp [J_comm]
  -- So (k+2) * J(k+1, 0) = J(k+1, 0), hence (k+1) * J(k+1, 0) = 0
  have h3 : (↑(k + 1) : ℝ) * J (k + 1) 0 = 0 := by
    have h12 : (↑(k + 2) : ℝ) * J (k + 1) 0 - J (k + 1) 0 = 0 := by linarith
    have : (↑(k + 1) : ℝ) * J (k + 1) 0 =
        (↑(k + 2) : ℝ) * J (k + 1) 0 - J (k + 1) 0 := by push_cast; ring
    linarith
  have h4 : (↑(k + 1) : ℝ) ≠ 0 := Nat.cast_ne_zero.mpr (by omega)
  exact (mul_eq_zero.mp h3).resolve_left h4

/-- `J(n, m) = if n = m then n! * √(2π) else 0`. -/
theorem J_eq : ∀ m n : ℕ,
    J n m = if n = m then ↑(n.factorial) * Real.sqrt (2 * Real.pi) else 0 := by
  intro m
  induction m with
  | zero =>
    intro n
    cases n with
    | zero => simp [J_zero_zero]
    | succ k => simp [J_succ_zero k]
  | succ m ih =>
    intro n
    cases n with
    | zero =>
      rw [J_comm, J_succ_zero]
      simp
    | succ k =>
      rw [J_succ_succ, ih]
      by_cases h : k = m
      · subst h; simp [Nat.factorial_succ, Nat.cast_mul]; ring
      · simp [h]

/-- Substitution lemma: ∫ Hₙ(x√2)·Hₘ(x√2)·exp(-x²) dx = (√2)⁻¹ · J(n,m).
    Uses the substitution u = √2·x and `integral_comp_mul_left`. -/
private theorem integral_hermiteProd_gaussian_sq (n m : ℕ) :
    ∫ x, (hermiteR n).eval (x * Real.sqrt 2) *
      ((hermiteR m).eval (x * Real.sqrt 2) * Real.exp (-(x ^ 2))) =
    (Real.sqrt 2)⁻¹ * J n m := by
  -- The integrand equals g(√2·x) where g(u) = Hₙ(u)·(Hₘ(u)·exp(-u²/2))
  set g : ℝ → ℝ := fun u => (hermiteR n).eval u * ((hermiteR m).eval u * gaussian u)
  have hg_eq : ∀ x, (hermiteR n).eval (x * Real.sqrt 2) *
      ((hermiteR m).eval (x * Real.sqrt 2) * Real.exp (-(x ^ 2))) =
      g (Real.sqrt 2 * x) := by
    intro x
    simp only [g, gaussian, mul_comm (Real.sqrt 2) x]
    congr 1; congr 1; congr 1
    rw [mul_pow, Real.sq_sqrt two_pos.le]; ring
  simp_rw [hg_eq]
  rw [Measure.integral_comp_mul_left g (Real.sqrt 2),
    show |(Real.sqrt 2)⁻¹| = (Real.sqrt 2)⁻¹ from
      abs_of_pos (inv_pos.mpr (Real.sqrt_pos.mpr two_pos)),
    smul_eq_mul]
  -- Goal: (√2)⁻¹ * ∫ g = (√2)⁻¹ * J n m
  congr 1

/-- Product of two Hermite functions expressed via J.
    `∫ ψₙ·ψₘ = cₙ·cₘ·(√2)⁻¹·J(n,m)` after substitution u = x√2. -/
private theorem hermiteFunction_integral_eq (n m : ℕ) :
    ∫ x, hermiteFunction n x * hermiteFunction m x =
    hermiteFunctionNormConst n * hermiteFunctionNormConst m *
      ((Real.sqrt 2)⁻¹ * J n m) := by
  -- Rewrite integrand factoring out constants
  have h_integrand : (fun x => hermiteFunction n x * hermiteFunction m x) =
      fun x => hermiteFunctionNormConst n * hermiteFunctionNormConst m *
        ((hermiteR n).eval (x * Real.sqrt 2) *
         ((hermiteR m).eval (x * Real.sqrt 2) * Real.exp (-(x ^ 2)))) := by
    ext x; unfold hermiteFunction hermiteR
    set cn := hermiteFunctionNormConst n
    set cm := hermiteFunctionNormConst m
    set Hn := ((hermite n).map (Int.castRingHom ℝ)).eval (x * Real.sqrt 2)
    set Hm := ((hermite m).map (Int.castRingHom ℝ)).eval (x * Real.sqrt 2)
    set G := Real.exp (-(x ^ 2) / 2)
    show cn * Hn * G * (cm * Hm * G) = cn * cm * (Hn * (Hm * Real.exp (-(x ^ 2))))
    have hG : G * G = Real.exp (-(x ^ 2)) := by
      show Real.exp (-(x ^ 2) / 2) * Real.exp (-(x ^ 2) / 2) = Real.exp (-(x ^ 2))
      rw [← Real.exp_add]; congr 1; ring
    calc cn * Hn * G * (cm * Hm * G)
        = cn * cm * (Hn * Hm) * (G * G) := by ring
      _ = cn * cm * (Hn * Hm) * Real.exp (-(x ^ 2)) := by rw [hG]
      _ = cn * cm * (Hn * (Hm * Real.exp (-(x ^ 2)))) := by ring
  rw [h_integrand, integral_const_mul]
  congr 1
  exact integral_hermiteProd_gaussian_sq n m

/-- Hermite functions are orthonormal in L²(ℝ):
    ∫ ψₙ(x) ψₘ(x) dx = δₙₘ -/
theorem hermiteFunction_orthonormal :
    ∀ n m : ℕ, ∫ x, hermiteFunction n x * hermiteFunction m x = if n = m then 1 else 0 := by
  intro n m
  rw [hermiteFunction_integral_eq, J_eq]
  split_ifs with h
  · -- n = m: cₙ² · (√2)⁻¹ · n! · √(2π) = 1
    subst h
    simp only [hermiteFunctionNormConst]
    have hfact : (0 : ℝ) < ↑n.factorial := Nat.cast_pos.mpr n.factorial_pos
    have hpi : (0 : ℝ) < Real.sqrt Real.pi := Real.sqrt_pos.mpr Real.pi_pos
    have hsqrt2 : (0 : ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr two_pos
    have h_sq : Real.sqrt ((↑n.factorial * Real.sqrt Real.pi)⁻¹) *
        Real.sqrt ((↑n.factorial * Real.sqrt Real.pi)⁻¹) =
        (↑n.factorial * Real.sqrt Real.pi)⁻¹ :=
      Real.mul_self_sqrt (inv_nonneg.mpr (mul_nonneg (Nat.cast_nonneg' _) (Real.sqrt_nonneg _)))
    rw [h_sq]
    have h2pi : Real.sqrt (2 * Real.pi) = Real.sqrt 2 * Real.sqrt Real.pi :=
      Real.sqrt_mul two_pos.le Real.pi
    rw [h2pi]
    field_simp
  · -- n ≠ m: cₙ · cₘ · (√2)⁻¹ · 0 = 0
    ring

-- hermiteFunction_complete is proved below, after the supporting lemmas.

/-- The Gaussian exp(-x²/2) as a Schwartz function. -/
noncomputable def gaussianSchwartz : SchwartzMap ℝ ℝ where
  toFun x := Real.exp (-(x ^ 2 / 2))
  smooth' := by fun_prop
  decay' := by
    intro k n
    set P := (Polynomial.hermite n).map (algebraMap ℤ ℝ)
    obtain ⟨C, hC_nonneg, hbound⟩ :=
      polynomial_eval_mul_exp_neg_bound (X ^ k * P) (by positivity : (0 : ℝ) < 1 / 2)
    refine ⟨C, fun x => ?_⟩
    rw [norm_iteratedFDeriv_eq_norm_iteratedDeriv, Real.norm_eq_abs, Real.norm_eq_abs]
    have hiter : iteratedDeriv n (fun x => Real.exp (-(x ^ 2 / 2))) x =
        (-1 : ℝ) ^ n * Polynomial.aeval x (Polynomial.hermite n) *
        Real.exp (-(x ^ 2 / 2)) := by
      rw [iteratedDeriv_eq_iterate]
      exact Polynomial.deriv_gaussian_eq_hermite_mul_gaussian n x
    rw [hiter, abs_mul, abs_mul, abs_pow, abs_neg, abs_one, one_pow, one_mul,
        abs_of_pos (Real.exp_pos _)]
    have haeval : Polynomial.aeval x (Polynomial.hermite n) = P.eval x := by
      simp [P, Polynomial.aeval_def, Polynomial.eval₂_eq_eval_map]
    rw [haeval]
    -- Goal: |x| ^ k * (|P.eval x| * exp(-(x²/2))) ≤ C
    -- Rewrite as |(X^k * P).eval x * exp(-(1/2)*x²)| ≤ C
    have hrw : |x| ^ k * (|P.eval x| * Real.exp (-(x ^ 2 / 2))) =
        |(X ^ k * P).eval x * Real.exp (-(1 / 2) * x ^ 2)| := by
      rw [Polynomial.eval_mul, Polynomial.eval_pow, Polynomial.eval_X,
          abs_mul, abs_mul, abs_pow, abs_of_pos (Real.exp_pos _)]
      ring_nf
    rw [hrw]
    exact hbound x

/-- Iterated derivCLM agrees pointwise with iterated deriv on any Schwartz function. -/
private lemma derivCLM_iterate_apply (n : ℕ) (g : SchwartzMap ℝ ℝ) (x : ℝ) :
    (⇑(SchwartzMap.derivCLM ℝ ℝ))^[n] g x = deriv^[n] (⇑g) x := by
  induction n generalizing g with
  | zero => rfl
  | succ n ih => exact ih _

/-- `x ^ k * exp(-x²/2)` is a Schwartz function, proved by iterating multiplication by x
    via `SchwartzMap.smulLeftCLM`. -/
private lemma pow_mul_gaussian_schwartz (k : ℕ) :
    ∃ (φ : SchwartzMap ℝ ℝ), ∀ x, φ x = x ^ k * Real.exp (-(x ^ 2 / 2)) := by
  induction k with
  | zero =>
    exact ⟨gaussianSchwartz, fun x => by
      change Real.exp (-(x ^ 2 / 2)) = x ^ 0 * Real.exp (-(x ^ 2 / 2)); simp⟩
  | succ k ih =>
    obtain ⟨ψ, hψ⟩ := ih
    refine ⟨SchwartzMap.smulLeftCLM ℝ (fun x => x) ψ, fun x => ?_⟩
    rw [SchwartzMap.smulLeftCLM_apply_apply Function.HasTemperateGrowth.id']
    simp only [smul_eq_mul, hψ x]
    ring

/-- For any polynomial P, x ↦ P(x) · exp(-x²/2) is a Schwartz function. -/
private lemma poly_mul_gaussian_schwartz (P : ℝ[X]) :
    ∃ (φ : SchwartzMap ℝ ℝ), ∀ x, φ x = P.eval x * Real.exp (-(x ^ 2 / 2)) := by
  induction P using Polynomial.induction_on' with
  | add p q hp hq =>
    obtain ⟨φp, hφp⟩ := hp
    obtain ⟨φq, hφq⟩ := hq
    exact ⟨φp + φq, fun x => by
      simp only [add_apply, Polynomial.eval_add, add_mul, hφp x, hφq x]⟩
  | monomial k a =>
    obtain ⟨ψ, hψ⟩ := pow_mul_gaussian_schwartz k
    exact ⟨a • ψ, fun x => by
      simp only [smul_apply, smul_eq_mul, Polynomial.eval_monomial, hψ x]
      ring⟩

/-- Each Hermite function is a Schwartz function. -/
theorem hermiteFunction_schwartz (n : ℕ) :
    ∃ (φ : SchwartzMap ℝ ℝ), ∀ x, φ x = hermiteFunction n x := by
  -- Hₙ(x√2) = Q(x) where Q is the composed polynomial
  set Q := (hermiteR n).comp (C (Real.sqrt 2) * X) with hQ_def
  obtain ⟨ψ, hψ⟩ := poly_mul_gaussian_schwartz Q
  refine ⟨hermiteFunctionNormConst n • ψ, fun x => ?_⟩
  simp only [smul_apply, smul_eq_mul, hψ x]
  unfold hermiteFunction
  -- Q.eval x = (hermiteR n).eval (x * √2) = (hermite n).map(...).eval (x * √2)
  have hQeval : Q.eval x = ((hermite n).map (Int.castRingHom ℝ)).eval (x * Real.sqrt 2) := by
    simp [hQ_def, hermiteR, Polynomial.eval_comp, Polynomial.eval_mul,
      Polynomial.eval_C, Polynomial.eval_X, mul_comm x (Real.sqrt 2)]
  rw [← hQeval]
  -- Handle exp argument and associativity: c * (Q * exp₁) = c * Q * exp₂
  have hexp : Real.exp (-(x ^ 2 / 2)) = Real.exp (-(x ^ 2) / 2) := by
    congr 1; ring
  rw [hexp, mul_assoc]

/-! ### Raising/Lowering Operators and Seminorm Bounds

We prove polynomial growth of Schwartz seminorms via:
1. Raising/lowering operator identities for Hermite functions
2. L² norm bounds from orthonormality
3. Agmon (Sobolev) inequality: ‖f‖_∞² ≤ 2‖f‖₂·‖f'‖₂
4. Assembly: seminorm ≤ C·(1+n)^s
-/

/-- Normalization constant is positive. -/
private lemma normConst_pos (n : ℕ) : 0 < hermiteFunctionNormConst n := by
  unfold hermiteFunctionNormConst
  exact Real.sqrt_pos.mpr (inv_pos.mpr (mul_pos
    (Nat.cast_pos.mpr n.factorial_pos) (Real.sqrt_pos.mpr Real.pi_pos)))

/-- Normalization constant is non-negative. -/
private lemma normConst_nonneg (n : ℕ) : 0 ≤ hermiteFunctionNormConst n :=
  le_of_lt (normConst_pos n)

/-- Square of normalization constant: cₙ² = (n!·√π)⁻¹ -/
private lemma normConst_sq (n : ℕ) :
    hermiteFunctionNormConst n * hermiteFunctionNormConst n =
    (↑n.factorial * Real.sqrt Real.pi)⁻¹ := by
  unfold hermiteFunctionNormConst
  exact Real.mul_self_sqrt (inv_nonneg.mpr (mul_nonneg (Nat.cast_nonneg' _) (Real.sqrt_nonneg _)))

/-- Normalization constant relation: cₙ = √(n+1) · c_{n+1} -/
private lemma normConst_succ (n : ℕ) :
    hermiteFunctionNormConst n = Real.sqrt (↑(n + 1)) * hermiteFunctionNormConst (n + 1) := by
  simp only [hermiteFunctionNormConst]
  rw [← Real.sqrt_mul (Nat.cast_nonneg' (n + 1))]
  congr 1
  rw [Nat.factorial_succ, Nat.cast_mul]
  have hfact : (0 : ℝ) < ↑n.factorial := Nat.cast_pos.mpr n.factorial_pos
  have hpi : (0 : ℝ) < Real.sqrt Real.pi := Real.sqrt_pos.mpr Real.pi_pos
  have hn1 : (0 : ℝ) < ↑(n + 1) := Nat.cast_pos.mpr (Nat.succ_pos n)
  field_simp

/-- X · Hₙ₊₁ = Hₙ₊₂ + (n+1) · Hₙ in ℤ[X]. -/
lemma hermite_X_mul (n : ℕ) :
    X * hermite (n + 1) = hermite (n + 2) + (↑(n + 1) : ℤ[X]) * hermite n := by
  linear_combination -(hermite_succ (n + 1)) + hermite_derivative n

lemma hermiteR_recurrence_succ (n : ℕ) (t : ℝ) :
    t * (hermiteR (n + 1)).eval t =
    (hermiteR (n + 2)).eval t + ↑(n + 1) * (hermiteR n).eval t := by
  have h := congr_arg (fun p => (Polynomial.map (Int.castRingHom ℝ) p).eval t)
    (hermite_X_mul n)
  simp only [Polynomial.map_mul, Polynomial.map_add, Polynomial.map_X,
    Polynomial.map_natCast, Polynomial.eval_mul, Polynomial.eval_add,
    Polynomial.eval_X, Polynomial.eval_natCast] at h
  linarith

/-- Hermite recurrence for n = 0: t · H₀(t) = H₁(t). -/
lemma hermiteR_recurrence_zero (t : ℝ) :
    t * (hermiteR 0).eval t = (hermiteR 1).eval t := by
  simp [hermiteR, hermite_zero, hermite_succ, Polynomial.eval_X,
    Polynomial.map_X]

/-- x·ψₙ expressed using normConst and hermiteR, factoring out the exponential. -/
private lemma mul_x_hermiteFunction_aux (n : ℕ) (x : ℝ) :
    x * hermiteFunction n x =
    hermiteFunctionNormConst n *
    (x * (hermiteR n).eval (x * Real.sqrt 2)) *
    Real.exp (-(x ^ 2) / 2) := by
  unfold hermiteFunction
  ring

/-- Key coefficient identity: c_n / √2 = √((n+1)/2) · c_{n+1}.
    Proof: c_n = √(n+1) · c_{n+1} (normConst_succ), so c_n/√2 = √(n+1)/√2 · c_{n+1} = √((n+1)/2) · c_{n+1}.-/
private lemma normConst_div_sqrt2 (n : ℕ) :
    hermiteFunctionNormConst n / Real.sqrt 2 =
    Real.sqrt ((↑(n + 1) : ℝ) / 2) * hermiteFunctionNormConst (n + 1) := by
  rw [normConst_succ n, mul_div_assoc, Real.sqrt_div (Nat.cast_nonneg' (n + 1))]
  ring

/-- Key coefficient identity: (n+1) · c_{n+1} / √2 = √((n+1)/2) · c_n.
    Proof: c_n = √(n+1) · c_{n+1}, so √(n+1) · c_{n+1} = c_n, and
    (n+1) · c_{n+1} / √2 = √(n+1) · √(n+1) · c_{n+1} / √2 = √(n+1)/√2 · c_n = √((n+1)/2) · c_n. -/
private lemma normConst_mul_div_sqrt2 (n : ℕ) :
    ↑(n + 1) * hermiteFunctionNormConst (n + 1) / Real.sqrt 2 =
    Real.sqrt ((↑(n + 1) : ℝ) / 2) * hermiteFunctionNormConst n := by
  have hn1 : (0 : ℝ) ≤ ↑(n + 1) := Nat.cast_nonneg' (n + 1)
  have hc := normConst_succ n  -- c_n = √(n+1) * c_{n+1}
  -- Rewrite ↑(n+1) = √(n+1) * √(n+1)
  conv_lhs => rw [show (↑(n + 1) : ℝ) = Real.sqrt (↑(n + 1)) * Real.sqrt (↑(n + 1)) from
    (Real.mul_self_sqrt hn1).symm]
  -- ↑(n+1) * c_{n+1} / √2 = √(n+1) * (√(n+1) * c_{n+1}) / √2 = √(n+1) * c_n / √2
  rw [mul_assoc, hc.symm, mul_div_assoc, Real.sqrt_div hn1]
  ring

/-- Multiplication by x: x·ψₙ = √((n+1)/2)·ψ_{n+1} + √(n/2)·ψ_{n-1}. -/
theorem mul_x_hermiteFunction (n : ℕ) (x : ℝ) :
    x * hermiteFunction n x =
    Real.sqrt ((↑(n + 1) : ℝ) / 2) * hermiteFunction (n + 1) x +
    Real.sqrt ((↑n : ℝ) / 2) * hermiteFunction (n - 1) x := by
  have h2pos : (0 : ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num : (0:ℝ) < 2)
  have h2ne : Real.sqrt 2 ≠ 0 := ne_of_gt h2pos
  -- Set up abbreviations
  set t := x * Real.sqrt 2 with ht_def
  set e := Real.exp (-(x ^ 2) / 2) with he_def
  -- Express x as t/√2
  have hx_eq : x = t / Real.sqrt 2 := by
    rw [ht_def, mul_div_cancel_right₀ x h2ne]
  cases n with
  | zero =>
    -- n = 0: second term vanishes since √(0/2) = 0
    simp only [Nat.cast_zero, zero_div, Real.sqrt_zero, zero_mul, add_zero, Nat.zero_sub]
    -- Need: x * ψ₀(x) = √(1/2) * ψ₁(x)
    unfold hermiteFunction
    -- Simplify t/√2 * √2 = t
    have ht_simp : t / Real.sqrt 2 * Real.sqrt 2 = t := div_mul_cancel₀ t h2ne
    rw [hx_eq]
    simp only [ht_simp]
    -- Use the recurrence: t * H₀(t) = H₁(t)
    have hrec := hermiteR_recurrence_zero t
    set e' := Real.exp (-(t / Real.sqrt 2) ^ 2 / 2) with he'_def
    -- LHS: (t/√2) * c₀ * H₀(t) * e' = c₀/√2 * (t * H₀(t)) * e'
    have lhs_eq : t / Real.sqrt 2 * (hermiteFunctionNormConst 0 * (hermiteR 0).eval t * e') =
      hermiteFunctionNormConst 0 / Real.sqrt 2 * (t * (hermiteR 0).eval t) * e' := by ring
    rw [lhs_eq, hrec, normConst_div_sqrt2 0]
    ring
  | succ n =>
    -- n = n' + 1: use three-term recurrence
    rw [show (n + 1 : ℕ) - 1 = n from by omega]
    unfold hermiteFunction
    -- Simplify t/√2 * √2 = t everywhere
    have ht_simp : t / Real.sqrt 2 * Real.sqrt 2 = t := div_mul_cancel₀ t h2ne
    -- Express x in terms of t
    rw [hx_eq]
    simp only [ht_simp]
    -- Now both sides use t = x * √2 as the argument to hermiteR
    -- Use the recurrence: t * H_{n+1}(t) = H_{n+2}(t) + (n+1) * H_n(t)
    have hrec := hermiteR_recurrence_succ n t
    -- LHS: (t/√2) * c_{n+1} * H_{n+1}(t) * e'
    -- where e' = exp(-(t/√2)²/2)
    -- Factor to: c_{n+1}/√2 * (t * H_{n+1}(t)) * e'
    set e' := Real.exp (-(t / Real.sqrt 2) ^ 2 / 2) with he'_def
    have lhs_eq : t / Real.sqrt 2 *
      (hermiteFunctionNormConst (n + 1) * (hermiteR (n + 1)).eval t * e') =
      hermiteFunctionNormConst (n + 1) / Real.sqrt 2 * (t * (hermiteR (n + 1)).eval t) * e' := by
      ring
    rw [lhs_eq, hrec]
    have key1 := normConst_div_sqrt2 (n + 1)
    have key2 := normConst_mul_div_sqrt2 n
    -- Distribute
    have expand : hermiteFunctionNormConst (n + 1) / Real.sqrt 2 *
      ((hermiteR (n + 2)).eval t + ↑(n + 1) * (hermiteR n).eval t) * e' =
      hermiteFunctionNormConst (n + 1) / Real.sqrt 2 * (hermiteR (n + 2)).eval t * e' +
      (↑(n + 1) * hermiteFunctionNormConst (n + 1) / Real.sqrt 2) * (hermiteR n).eval t * e' := by
      ring
    rw [expand, key1, key2]
    ring

/-- Hermite functions are smooth (ContDiff ℝ ⊤). -/
lemma hermiteFunction_contDiff (j : ℕ) (m : ℕ) :
    ContDiff ℝ m (hermiteFunction j) := by
  obtain ⟨φ, hφ⟩ := hermiteFunction_schwartz j
  have : hermiteFunction j = ⇑φ := funext fun x => (hφ x).symm
  rw [this]; exact φ.smooth _

/-- Derivative identity: ψₙ'(x) = √(n/2) ψ_{n-1}(x) - √((n+1)/2) ψ_{n+1}(x).
    Proved from the product rule on cₙ · Hₙ(x√2) · exp(-x²/2) and Hermite recurrence. -/
theorem deriv_hermiteFunction (n : ℕ) (x : ℝ) :
    deriv (hermiteFunction n) x =
    Real.sqrt ((↑n : ℝ) / 2) * hermiteFunction (n - 1) x -
    Real.sqrt ((↑(n + 1) : ℝ) / 2) * hermiteFunction (n + 1) x := by
  have h2pos : (0 : ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num : (0:ℝ) < 2)
  have h2ne : Real.sqrt 2 ≠ 0 := ne_of_gt h2pos
  -- Set up abbreviations
  set t := x * Real.sqrt 2 with ht_def
  set e := Real.exp (-(x ^ 2) / 2) with he_def
  -- Express x as t/√2
  have hx_eq : x = t / Real.sqrt 2 := by
    rw [ht_def, mul_div_cancel_right₀ x h2ne]
  -- Compute HasDerivAt for the polynomial part: chain rule
  -- H_n(u * √2) has derivative √2 * H_n'(u * √2) = √2 * (derivative H_n).eval(u * √2)
  have hpoly_hasderiv : HasDerivAt (fun u => (hermiteR n).eval (u * Real.sqrt 2))
      (Real.sqrt 2 * (Polynomial.derivative (hermiteR n)).eval (x * Real.sqrt 2)) x := by
    have h_inner : HasDerivAt (fun u : ℝ => u * Real.sqrt 2) (Real.sqrt 2) x := by
      have := (hasDerivAt_id x).mul_const (Real.sqrt 2)
      simpa using this
    have h_outer : HasDerivAt (fun u => (hermiteR n).eval u)
        ((Polynomial.derivative (hermiteR n)).eval (x * Real.sqrt 2)) (x * Real.sqrt 2) := by
      have : (fun u => (hermiteR n).eval u) = fun u => Polynomial.aeval u (hermiteR n) := by
        ext u; simp [Polynomial.aeval_def, Polynomial.eval₂_eq_eval_map, Polynomial.map_id]
      rw [this]
      exact Polynomial.hasDerivAt_aeval (hermiteR n) (x * Real.sqrt 2)
    have hcomp := h_outer.comp x h_inner
    rwa [mul_comm] at hcomp
  -- Compute HasDerivAt for the exponential part
  have hexp_hasderiv : HasDerivAt (fun u : ℝ => Real.exp (-(u ^ 2) / 2)) (-x * e) x := by
    have h1 : HasDerivAt (fun u : ℝ => -(u ^ 2) / 2) (-x) x := by
      have := ((hasDerivAt_pow 2 x).neg.div_const 2)
      convert this using 1 <;> first | rfl | ring
    have h2 := h1.exp
    rw [he_def]
    convert h2 using 1; ring
  -- Compute HasDerivAt for the product poly * exp (pointwise, not Pi.mul)
  have hprod : HasDerivAt (fun u => (hermiteR n).eval (u * Real.sqrt 2) * Real.exp (-(u ^ 2) / 2))
      (Real.sqrt 2 * (Polynomial.derivative (hermiteR n)).eval (x * Real.sqrt 2) * e +
       (hermiteR n).eval (x * Real.sqrt 2) * (-x * e)) x := by
    have := hpoly_hasderiv.mul hexp_hasderiv
    rw [he_def] at this
    convert this using 2 <;> rfl
  -- Compute HasDerivAt for the full hermiteFunction = c_n * (poly * exp)
  have hfn_eq : hermiteFunction n = fun u =>
      hermiteFunctionNormConst n * ((hermiteR n).eval (u * Real.sqrt 2) * Real.exp (-(u ^ 2) / 2)) := by
    ext u; unfold hermiteFunction; ring
  have hfull := hprod.const_mul (hermiteFunctionNormConst n)
  rw [hfn_eq, hfull.deriv]
  -- Now the goal is algebraic:
  -- c_n * (√2 * (derivative H_n).eval t * e + H_n.eval t * (-x * e)) = RHS
  -- Case split on n
  cases n with
  | zero =>
    -- n = 0: derivative H_0 = 0, so deriv = c_0 * (0 - x * H_0(t)) * e = -x * ψ_0
    simp only [Nat.cast_zero, zero_div, Real.sqrt_zero, zero_mul, zero_sub, Nat.zero_sub]
    -- derivative of hermiteR 0 is 0
    have hd0 : Polynomial.derivative (hermiteR 0) = 0 := by
      simp [hermiteR, hermite_zero]
    rw [hd0, Polynomial.eval_zero, mul_zero, zero_mul, zero_add]
    -- Need: c_0 * (H_0(t) * (-x * e)) = -(√(1/2) * (c_1 * H_1(t) * e))
    unfold hermiteFunction
    have ht_simp : t / Real.sqrt 2 * Real.sqrt 2 = t := div_mul_cancel₀ t h2ne
    rw [hx_eq]
    simp only [ht_simp]
    have hrec := hermiteR_recurrence_zero t
    set e' := Real.exp (-(t / Real.sqrt 2) ^ 2 / 2) with he'_def
    have he_eq : e = e' := by
      rw [he_def, he'_def]; congr 1; rw [hx_eq]
    rw [he_eq]
    have lhs_eq : hermiteFunctionNormConst 0 *
      ((hermiteR 0).eval t * (-(t / Real.sqrt 2) * e')) =
      -(hermiteFunctionNormConst 0 / Real.sqrt 2) * (t * (hermiteR 0).eval t) * e' := by ring
    rw [lhs_eq, hrec, normConst_div_sqrt2 0]
    ring
  | succ n =>
    rw [show (n + 1 : ℕ) - 1 = n from by omega]
    have hd := hermiteR_derivative n
    rw [hd, Polynomial.eval_mul, Polynomial.eval_natCast]
    have hrec := hermiteR_recurrence_succ n t
    unfold hermiteFunction
    have ht_simp : t / Real.sqrt 2 * Real.sqrt 2 = t := div_mul_cancel₀ t h2ne
    rw [hx_eq]
    simp only [ht_simp]
    set e' := Real.exp (-(t / Real.sqrt 2) ^ 2 / 2) with he'_def
    have he_eq : e = e' := by
      rw [he_def, he'_def]; congr 1; rw [hx_eq]
    rw [he_eq]
    have key1 := normConst_mul_div_sqrt2 n
    have key2 := normConst_div_sqrt2 (n + 1)
    -- Rewrite √2 = √2*√2/√2 to factor out 1/√2
    have h_sqrt2_eq : Real.sqrt 2 = Real.sqrt 2 * Real.sqrt 2 / Real.sqrt 2 := by
      rw [mul_div_cancel_right₀ _ h2ne]
    have hsq2 : Real.sqrt 2 * Real.sqrt 2 = 2 := Real.mul_self_sqrt (by norm_num : (0:ℝ) ≤ 2)
    -- Rewrite the full LHS in one step:
    -- c_{n+1} * (√2 * (n+1) * H_n(t) * e' + H_{n+1}(t) * (-t/√2 * e'))
    -- After substituting recurrence for t * H_{n+1}(t), this equals
    -- (n+1)*c_{n+1}/√2 * H_n(t) * e' - c_{n+1}/√2 * H_{n+2}(t) * e'
    -- Step 1: Factor everything to c_{n+1}/√2
    -- √2 * (n+1) * H_n = (n+1) * H_n * √2*√2/√2 = 2*(n+1)*H_n/√2
    -- -(t/√2) * H_{n+1} = -t*H_{n+1}/√2
    -- So: c_{n+1}/√2 * (2*(n+1)*H_n(t) - t*H_{n+1}(t)) * e'
    -- Substitute recurrence: t*H_{n+1}(t) = H_{n+2}(t) + (n+1)*H_n(t)
    -- = c_{n+1}/√2 * (2*(n+1)*H_n(t) - H_{n+2}(t) - (n+1)*H_n(t)) * e'
    -- = c_{n+1}/√2 * ((n+1)*H_n(t) - H_{n+2}(t)) * e'
    -- = (n+1)*c_{n+1}/√2 * H_n(t) * e' - c_{n+1}/√2 * H_{n+2}(t) * e'
    have expand : hermiteFunctionNormConst (n + 1) *
      (Real.sqrt 2 * (↑(n + 1) * (hermiteR n).eval t) * e' +
       (hermiteR (n + 1)).eval t * (-(t / Real.sqrt 2) * e')) =
      (↑(n + 1) * hermiteFunctionNormConst (n + 1) / Real.sqrt 2) * (hermiteR n).eval t * e' -
      (hermiteFunctionNormConst (n + 1) / Real.sqrt 2) * (hermiteR (n + 2)).eval t * e' := by
      -- Use hrec and hsq2 to close this
      have lhs_factor : hermiteFunctionNormConst (n + 1) *
        (Real.sqrt 2 * (↑(n + 1) * (hermiteR n).eval t) * e' +
         (hermiteR (n + 1)).eval t * (-(t / Real.sqrt 2) * e')) =
        hermiteFunctionNormConst (n + 1) / Real.sqrt 2 *
        (Real.sqrt 2 * Real.sqrt 2 * (↑(n + 1) * (hermiteR n).eval t) -
         t * (hermiteR (n + 1)).eval t) * e' := by
        field_simp; ring
      rw [lhs_factor, hsq2, hrec]
      ring
    rw [expand, key1, key2]
    ring

/-- Hermite function squared is integrable. -/
private lemma hermiteFunction_sq_integrable (j : ℕ) :
    Integrable (fun x => hermiteFunction j x * hermiteFunction j x) volume := by
  exact (hermiteFunction_memLp j).integrable_mul (hermiteFunction_memLp j)

/-- L² norm squared of ψ_n equals 1, from orthonormality. -/
private lemma hermiteFunction_l2_sq (j : ℕ) :
    ∫ x, hermiteFunction j x * hermiteFunction j x = 1 := by
  have := hermiteFunction_orthonormal j j
  simp only [ite_true] at this; exact this

/-- The L² integral of (ψₙ')² equals (2n+1)/2, from the derivative identity + orthonormality. -/
private lemma hermiteFunction_deriv_l2_sq (n : ℕ) :
    ∫ x, deriv (hermiteFunction n) x * deriv (hermiteFunction n) x = (2 * ↑n + 1) / 2 := by
  -- Define the three component functions
  let f_A : ℝ → ℝ := fun x => ↑n / 2 * (hermiteFunction (n - 1) x * hermiteFunction (n - 1) x)
  let f_B : ℝ → ℝ := fun x => ↑(n + 1) / 2 * (hermiteFunction (n + 1) x * hermiteFunction (n + 1) x)
  let f_C : ℝ → ℝ := fun x => 2 * Real.sqrt (↑n / 2) * Real.sqrt (↑(n + 1) / 2) *
      (hermiteFunction (n - 1) x * hermiteFunction (n + 1) x)
  -- Rewrite integrand as f_A + f_B - f_C
  have h_eq : (fun x => deriv (hermiteFunction n) x * deriv (hermiteFunction n) x) =
      fun x => f_A x + f_B x - f_C x := by
    ext x; simp only [f_A, f_B, f_C]
    rw [deriv_hermiteFunction]
    have h1 : Real.sqrt (↑n / 2) * Real.sqrt (↑n / 2) = ↑n / 2 :=
      Real.mul_self_sqrt (div_nonneg (Nat.cast_nonneg' n) (by norm_num))
    have h2 : Real.sqrt (↑(n + 1) / 2) * Real.sqrt (↑(n + 1) / 2) = ↑(n + 1) / 2 :=
      Real.mul_self_sqrt (div_nonneg (Nat.cast_nonneg' (n+1)) (by norm_num))
    nlinarith [h1, h2]
  rw [h_eq]
  -- Integrability
  have hA : Integrable f_A := (hermiteFunction_sq_integrable (n - 1)).const_mul _
  have hB : Integrable f_B := (hermiteFunction_sq_integrable (n + 1)).const_mul _
  have hC : Integrable f_C :=
    ((hermiteFunction_memLp (n - 1)).integrable_mul (hermiteFunction_memLp (n + 1))).const_mul _
  -- Split integral step by step using helper
  have split_add : ∀ (f g : ℝ → ℝ), Integrable f → Integrable g →
      ∫ x, f x + g x = (∫ x, f x) + ∫ x, g x :=
    fun f g hf hg => integral_add hf hg
  have split_sub : ∀ (f g : ℝ → ℝ), Integrable f → Integrable g →
      ∫ x, f x - g x = (∫ x, f x) - ∫ x, g x :=
    fun f g hf hg => integral_sub hf hg
  -- Convert pointwise to Pi form and split
  have step1 : ∫ x, f_A x + f_B x - f_C x =
      ∫ x, f_A x + (f_B x - f_C x) := by congr 1; ext x; ring
  rw [step1]
  simp only [show ∀ x, f_B x - f_C x = (f_B - f_C) x from fun x => rfl]
  rw [split_add _ _ hA (hB.sub hC)]
  simp only [show ∀ x, (f_B - f_C) x = f_B x - f_C x from fun x => rfl]
  rw [split_sub _ _ hB hC]
  simp only [f_A, f_B, f_C, integral_const_mul, hermiteFunction_l2_sq, mul_one]
  have hne : n - 1 ≠ n + 1 := by omega
  rw [hermiteFunction_orthonormal (n - 1) (n + 1), if_neg hne, mul_zero]
  push_cast; ring

/-- The squared hermiteFunction has a HasDerivAt using the chain/product rule. -/
private lemma hasDerivAt_hermiteFunction_sq (n : ℕ) (x : ℝ) :
    HasDerivAt (fun t => hermiteFunction n t * hermiteFunction n t)
      (2 * hermiteFunction n x * deriv (hermiteFunction n) x) x := by
  have hd : HasDerivAt (hermiteFunction n) (deriv (hermiteFunction n) x) x :=
    ((hermiteFunction_contDiff n 1).differentiable one_ne_zero x).hasDerivAt
  have := hd.mul hd
  convert this using 1 <;> first | rfl | ring

/-- The derivative of hermiteFunction n is in L² (as a MemLp function). -/
private lemma deriv_hermiteFunction_memLp (n : ℕ) :
    MemLp (deriv (hermiteFunction n)) 2 volume := by
  -- ψₙ' = √(n/2) ψ_{n-1} - √((n+1)/2) ψ_{n+1}, and each ψⱼ is in L²
  have h_eq : deriv (hermiteFunction n) =
      fun x => Real.sqrt (↑n / 2) * hermiteFunction (n - 1) x -
               Real.sqrt (↑(n + 1) / 2) * hermiteFunction (n + 1) x := by
    ext x; exact deriv_hermiteFunction n x
  rw [h_eq]
  exact (hermiteFunction_memLp (n - 1)).const_mul (Real.sqrt (↑n / 2))
    |>.sub ((hermiteFunction_memLp (n + 1)).const_mul (Real.sqrt (↑(n + 1) / 2)))

/-- The product ψₙ · ψₙ' is integrable (for Cauchy-Schwarz/FTC). -/
private lemma integrable_hermiteFunction_mul_deriv (n : ℕ) :
    Integrable (fun x => hermiteFunction n x * deriv (hermiteFunction n) x) volume :=
  (hermiteFunction_memLp n).integrable_mul (deriv_hermiteFunction_memLp n)

/-- A Schwartz function on ℝ tends to 0 at atBot. -/
private lemma schwartz_tendsto_atBot (φ : SchwartzMap ℝ ℝ) :
    Filter.Tendsto (fun x => φ x) Filter.atBot (nhds 0) := by
  have hza := ZeroAtInftyContinuousMapClass.zero_at_infty φ
  rw [cocompact_eq_atBot_atTop] at hza
  exact hza.mono_left le_sup_left

/-- A Schwartz function on ℝ tends to 0 at atTop. -/
private lemma schwartz_tendsto_atTop (φ : SchwartzMap ℝ ℝ) :
    Filter.Tendsto (fun x => φ x) Filter.atTop (nhds 0) := by
  have hza := ZeroAtInftyContinuousMapClass.zero_at_infty φ
  rw [cocompact_eq_atBot_atTop] at hza
  exact hza.mono_left le_sup_right

/-- ψₙ(x)² → 0 as x → -∞. -/
private lemma hermiteFunction_sq_tendsto_atBot (n : ℕ) :
    Filter.Tendsto (fun x => hermiteFunction n x * hermiteFunction n x)
      Filter.atBot (nhds 0) := by
  obtain ⟨φ, hφ⟩ := hermiteFunction_schwartz n
  have heq : (fun x => hermiteFunction n x * hermiteFunction n x) =
      fun x => φ x * φ x := by ext x; rw [← hφ x]
  rw [heq, show (0 : ℝ) = 0 * 0 from (mul_zero 0).symm]
  exact (schwartz_tendsto_atBot φ).mul (schwartz_tendsto_atBot φ)

/-- ψₙ(x)² → 0 as x → +∞. -/
private lemma hermiteFunction_sq_tendsto_atTop (n : ℕ) :
    Filter.Tendsto (fun x => hermiteFunction n x * hermiteFunction n x)
      Filter.atTop (nhds 0) := by
  obtain ⟨φ, hφ⟩ := hermiteFunction_schwartz n
  have heq : (fun x => hermiteFunction n x * hermiteFunction n x) =
      fun x => φ x * φ x := by ext x; rw [← hφ x]
  rw [heq, show (0 : ℝ) = 0 * 0 from (mul_zero 0).symm]
  exact (schwartz_tendsto_atTop φ).mul (schwartz_tendsto_atTop φ)

/-- Agmon-type sup bound: |ψₙ(x)| grows at most polynomially in n.
    Uses FTC + Cauchy-Schwarz + orthonormality + derivative identity:
    |ψₙ(x)|² ≤ 2‖ψₙ‖₂ · ‖ψₙ'‖₂ = 2√((2n+1)/2) ≤ C·(1+n)^{1/2}. -/
theorem hermiteFunction_sup_bound :
    ∃ (C : ℝ) (s : ℝ), 0 < C ∧ 0 ≤ s ∧
    ∀ (n : ℕ) (x : ℝ), |hermiteFunction n x| ≤ C * (1 + ↑n) ^ s := by
  -- We use FTC + AM-GM: |ψ_n(x)|² ≤ ||ψ_n||₂² + ||ψ_n'||₂² = 1 + (2n+1)/2 ≤ 2(1+n)
  -- Hence |ψ_n(x)| ≤ √2 · (1+n)^{1/2}
  refine ⟨Real.sqrt 2, 1 / 2, Real.sqrt_pos.mpr (by norm_num : (0:ℝ) < 2),
    by norm_num, fun n a => ?_⟩
  -- Step 1: FTC gives ψ_n(a)² = ∫_{-∞}^a 2·ψ_n·ψ_n' dt
  -- First establish the FTC setup
  set f := fun t => hermiteFunction n t * hermiteFunction n t with hf_def
  set f' := fun t => 2 * (hermiteFunction n t * deriv (hermiteFunction n) t) with hf'_def
  have hderiv : ∀ t ∈ Set.Iic a, HasDerivAt f (f' t) t := by
    intro t _
    have := hasDerivAt_hermiteFunction_sq n t
    simp only [hf'_def, mul_assoc] at this ⊢
    exact this
  have hf'_int : IntegrableOn f' (Set.Iic a) volume :=
    ((integrable_hermiteFunction_mul_deriv n).const_mul 2).integrableOn
  have htend : Filter.Tendsto f Filter.atBot (nhds 0) :=
    hermiteFunction_sq_tendsto_atBot n
  have hFTC := MeasureTheory.integral_Iic_of_hasDerivAt_of_tendsto' hderiv hf'_int htend
  -- FTC gives: ∫_{Iic a} f' = f(a) - 0 = ψ_n(a)²
  -- Step 2: |ψ_n(a)²| ≤ ∫_ℝ |f'|
  have hfa : f a = hermiteFunction n a * hermiteFunction n a := rfl
  rw [sub_zero] at hFTC
  -- From FTC: f a = ∫_{Iic a} f'
  -- |f a| ≤ ∫_{Iic a} |f'| ≤ ∫_ℝ |f'|
  -- Integrability of |f'|
  have hf'_abs_int : Integrable (fun t => |f' t|) volume :=
    ((integrable_hermiteFunction_mul_deriv n).const_mul 2).abs
  -- Step 2: ψ_n(a)² ≤ ∫_ℝ |f'|
  have h_sq_bound : hermiteFunction n a * hermiteFunction n a ≤ ∫ t, |f' t| := by
    calc hermiteFunction n a * hermiteFunction n a
        = f a := rfl
      _ ≤ |f a| := le_abs_self _
      _ = |∫ t in Set.Iic a, f' t| := by rw [hFTC]
      _ ≤ ∫ t in Set.Iic a, |f' t| := by
          rw [show |∫ t in Set.Iic a, f' t| = ‖∫ t in Set.Iic a, f' t‖ from
              (Real.norm_eq_abs _).symm]
          exact le_trans (norm_integral_le_integral_norm _)
            (le_of_eq (integral_congr_ae (Filter.Eventually.of_forall
              (fun t => Real.norm_eq_abs (f' t)))))
      _ ≤ ∫ t, |f' t| :=
          MeasureTheory.setIntegral_le_integral hf'_abs_int
            (Filter.Eventually.of_forall (fun t => abs_nonneg _))
  -- Step 3: AM-GM bound: |2·f·f'| ≤ f² + f'², so ∫|f'| ≤ ∫f² + ∫f'²
  have h_amgm : ∀ t : ℝ,
      |f' t| ≤ hermiteFunction n t * hermiteFunction n t +
      deriv (hermiteFunction n) t * deriv (hermiteFunction n) t := by
    intro t
    show |2 * (hermiteFunction n t * deriv (hermiteFunction n) t)| ≤ _
    rw [abs_mul, abs_of_nonneg (by positivity : (0:ℝ) ≤ 2), abs_mul]
    -- 2 * (|a| * |b|) ≤ a² + b² from AM-GM: 2ab ≤ a² + b²
    have hab := two_mul_le_add_sq (|hermiteFunction n t|) (|deriv (hermiteFunction n) t|)
    rw [mul_assoc] at hab
    calc 2 * (|hermiteFunction n t| * |deriv (hermiteFunction n) t|)
        ≤ |hermiteFunction n t| ^ 2 + |deriv (hermiteFunction n) t| ^ 2 := hab
      _ = hermiteFunction n t * hermiteFunction n t +
          deriv (hermiteFunction n) t * deriv (hermiteFunction n) t := by
          rw [sq_abs, sq_abs]; simp [sq]
  -- Integrability of (ψ_n')²
  have hderiv_sq_int :
      Integrable (fun x => deriv (hermiteFunction n) x * deriv (hermiteFunction n) x) volume :=
    (deriv_hermiteFunction_memLp n).integrable_mul (deriv_hermiteFunction_memLp n)
  have h_int_bound : ∫ t, |f' t| ≤
      (∫ t, hermiteFunction n t * hermiteFunction n t) +
      (∫ t, deriv (hermiteFunction n) t * deriv (hermiteFunction n) t) := by
    calc ∫ t, |f' t|
        ≤ ∫ t, (hermiteFunction n t * hermiteFunction n t +
                deriv (hermiteFunction n) t * deriv (hermiteFunction n) t) :=
          MeasureTheory.integral_mono_of_nonneg
            (Filter.Eventually.of_forall (fun t => abs_nonneg _))
            ((hermiteFunction_sq_integrable n).add hderiv_sq_int)
            (Filter.Eventually.of_forall h_amgm)
      _ = (∫ t, hermiteFunction n t * hermiteFunction n t) +
          (∫ t, deriv (hermiteFunction n) t * deriv (hermiteFunction n) t) :=
          integral_add (hermiteFunction_sq_integrable n) hderiv_sq_int
  -- Step 4: Substitute known L² norms
  rw [hermiteFunction_l2_sq, hermiteFunction_deriv_l2_sq] at h_int_bound
  -- Now h_int_bound : ∫|2ff'| ≤ 1 + (2n+1)/2
  -- And h_sq_bound : ψ_n(a)² ≤ ∫|2ff'|
  -- So ψ_n(a)² ≤ 1 + (2n+1)/2 = (2n+3)/2
  have h_sq_final : hermiteFunction n a * hermiteFunction n a ≤ 1 + (2 * ↑n + 1) / 2 :=
    le_trans h_sq_bound h_int_bound
  -- Step 5: (2n+3)/2 ≤ 2·(1+n) and take square roots
  -- 1 + (2n+1)/2 = (2n+3)/2 ≤ 2(1+n) since 2n+3 ≤ 4+4n iff 0 ≤ 2+2n
  have h_arith : 1 + (2 * (↑n : ℝ) + 1) / 2 ≤ 2 * (1 + ↑n) := by
    have : (0 : ℝ) ≤ ↑n := Nat.cast_nonneg n
    linarith
  have h_sq_le : hermiteFunction n a * hermiteFunction n a ≤ 2 * (1 + ↑n) :=
    le_trans h_sq_final h_arith
  -- Now |ψ_n(a)|² ≤ 2·(1+n), so |ψ_n(a)| ≤ √(2·(1+n)) = √2 · (1+n)^{1/2}
  have h1n_pos : (0 : ℝ) < 1 + ↑n := by positivity
  have h1n_nonneg : (0 : ℝ) ≤ 1 + ↑n := le_of_lt h1n_pos
  -- |f|² = f * f
  have habs_sq : |hermiteFunction n a| ^ 2 ≤ 2 * (1 + ↑n) := by
    rw [sq_abs, sq]; exact h_sq_le
  -- |f| ≤ √(2*(1+n))
  have h_abs_le : |hermiteFunction n a| ≤ Real.sqrt (2 * (1 + ↑n)) := by
    rw [← Real.sqrt_sq (abs_nonneg _)]
    exact Real.sqrt_le_sqrt habs_sq
  -- √(2*(1+n)) = √2 * √(1+n)
  have h_sqrt_split : Real.sqrt (2 * (1 + ↑n)) = Real.sqrt 2 * Real.sqrt (1 + ↑n) :=
    Real.sqrt_mul (by norm_num : (0:ℝ) ≤ 2) _
  -- √(2*(1+n)) = √2 * (1+n)^{1/2}
  rw [h_sqrt_split] at h_abs_le
  conv at h_abs_le => rhs; rw [show Real.sqrt (1 + ↑n) = (1 + ↑n) ^ ((1:ℝ) / 2)
    from Real.sqrt_eq_rpow _]
  exact h_abs_le

/-- Pointwise bound on |x|^k · ‖d^m ψₙ(x)‖ ≤ C · (1+n)^s.
    Proved via raising/lowering operators, L² bounds, and Agmon inequality. -/
private theorem hermiteFunction_pointwise_bound (k m : ℕ) :
    ∃ (C : ℝ) (s : ℝ), 0 < C ∧ 0 ≤ s ∧
    ∀ (n : ℕ) (x : ℝ),
    |x| ^ k * ‖iteratedDeriv m (hermiteFunction n) x‖ ≤
    C * (1 + ↑n) ^ s := by
  induction m with
  | zero =>
    -- For m = 0, iteratedDeriv 0 f = f, reduce to bounding |x|^k |ψ_n(x)|
    induction k with
    | zero =>
      -- Base case: k=0, m=0. Use hermiteFunction_sup_bound.
      obtain ⟨C, s, hC, hs, hbound⟩ := hermiteFunction_sup_bound
      exact ⟨C, s, hC, hs, fun n x => by
        simp only [pow_zero, one_mul, iteratedDeriv_zero, Real.norm_eq_abs]
        exact hbound n x⟩
    | succ k ih =>
      -- Step k → k+1 (m=0): use mul_x_hermiteFunction
      -- |x|^{k+1} |ψ_n| = |x|^k |x·ψ_n| ≤ |x|^k (√((n+1)/2)|ψ_{n+1}| + √(n/2)|ψ_{n-1}|)
      obtain ⟨C, s, hC, hs, hbound⟩ := ih
      refine ⟨C * ((2 : ℝ) ^ s + 1), s + 1, by positivity, by linarith, fun n x => ?_⟩
      simp only [iteratedDeriv_zero, Real.norm_eq_abs] at hbound ⊢
      -- |x|^{k+1} * |ψ_n(x)| = |x|^k * |x * ψ_n(x)|
      rw [pow_succ, mul_assoc, ← abs_mul]
      -- Use mul_x_hermiteFunction: x·ψ_n = √((n+1)/2)·ψ_{n+1} + √(n/2)·ψ_{n-1}
      rw [mul_x_hermiteFunction]
      -- Triangle inequality + rearrange
      have h_tri : |Real.sqrt ((↑(n + 1) : ℝ) / 2) * hermiteFunction (n + 1) x +
          Real.sqrt ((↑n : ℝ) / 2) * hermiteFunction (n - 1) x| ≤
          Real.sqrt ((↑(n + 1) : ℝ) / 2) * |hermiteFunction (n + 1) x| +
          Real.sqrt ((↑n : ℝ) / 2) * |hermiteFunction (n - 1) x| :=
        (abs_add_le _ _).trans (by rw [abs_mul, abs_of_nonneg (Real.sqrt_nonneg _),
          abs_mul, abs_of_nonneg (Real.sqrt_nonneg _)])
      -- Bound each term
      have h_main : |x| ^ k *
          (Real.sqrt ((↑(n + 1) : ℝ) / 2) * |hermiteFunction (n + 1) x| +
           Real.sqrt ((↑n : ℝ) / 2) * |hermiteFunction (n - 1) x|) ≤
          C * ((2 : ℝ) ^ s + 1) * (1 + ↑n) ^ (s + 1) := by
        -- Rearrange: |x|^k * (a + b) = a' + b' where a' uses IH at n+1, b' at n-1
        have hrearr : |x| ^ k *
            (Real.sqrt ((↑(n + 1) : ℝ) / 2) * |hermiteFunction (n + 1) x| +
             Real.sqrt ((↑n : ℝ) / 2) * |hermiteFunction (n - 1) x|) =
            Real.sqrt ((↑(n + 1) : ℝ) / 2) * (|x| ^ k * |hermiteFunction (n + 1) x|) +
            Real.sqrt ((↑n : ℝ) / 2) * (|x| ^ k * |hermiteFunction (n - 1) x|) := by ring
        rw [hrearr]
        -- Apply IH to each term
        have hb1 := hbound (n + 1) x
        have hb2 := hbound (n - 1) x
        -- Arithmetic bound (tested separately)
        have hsqrt1 : Real.sqrt ((↑(n + 1) : ℝ) / 2) ≤ 1 + (↑n : ℝ) := by
          rw [← Real.sqrt_sq (by positivity : (0:ℝ) ≤ 1 + ↑n)]
          apply Real.sqrt_le_sqrt; push_cast
          have : (0 : ℝ) ≤ (n : ℝ) := Nat.cast_nonneg n
          nlinarith [sq_nonneg ((n : ℝ) + 1)]
        have hsqrt2 : Real.sqrt ((↑n : ℝ) / 2) ≤ 1 + (↑n : ℝ) := by
          rw [← Real.sqrt_sq (by positivity : (0:ℝ) ≤ 1 + ↑n)]
          apply Real.sqrt_le_sqrt
          have : (0 : ℝ) ≤ (n : ℝ) := Nat.cast_nonneg n
          nlinarith [sq_nonneg ((n : ℕ) : ℝ)]
        have hrpow1 : (1 + ↑(n + 1) : ℝ) ^ s ≤ (2:ℝ) ^ s * (1 + ↑n) ^ s := by
          rw [← Real.mul_rpow (by positivity : (0:ℝ) ≤ 2) (by positivity : (0:ℝ) ≤ 1 + ↑n)]
          apply Real.rpow_le_rpow (by positivity) _ hs; push_cast; linarith
        have hrpow2 : (1 + ↑(n - 1) : ℝ) ^ s ≤ (1 + (↑n : ℝ)) ^ s := by
          apply Real.rpow_le_rpow (by positivity) _ hs
          have : (↑(n - 1) : ℝ) ≤ (↑n : ℝ) := Nat.cast_le.mpr (Nat.sub_le n 1)
          linarith
        have hrpow_split : (1 + (↑n : ℝ)) ^ (s + 1) = (1 + ↑n) ^ s * (1 + ↑n) := by
          rw [Real.rpow_add (by positivity : (0:ℝ) < 1 + ↑n), Real.rpow_one]
        calc Real.sqrt ((↑(n + 1) : ℝ) / 2) * (|x| ^ k * |hermiteFunction (n + 1) x|) +
             Real.sqrt ((↑n : ℝ) / 2) * (|x| ^ k * |hermiteFunction (n - 1) x|)
            ≤ Real.sqrt ((↑(n + 1) : ℝ) / 2) * (C * (1 + ↑(n + 1)) ^ s) +
              Real.sqrt ((↑n : ℝ) / 2) * (C * (1 + ↑(n - 1)) ^ s) := by gcongr
          _ ≤ (1 + ↑n) * (C * ((2:ℝ) ^ s * (1 + ↑n) ^ s)) +
              (1 + ↑n) * (C * (1 + ↑n) ^ s) := by gcongr
          _ = C * ((2:ℝ) ^ s + 1) * ((1 + ↑n) ^ s * (1 + ↑n)) := by ring
          _ = C * ((2:ℝ) ^ s + 1) * (1 + ↑n) ^ (s + 1) := by rw [hrpow_split]
      calc |x| ^ k * |Real.sqrt ((↑(n + 1) : ℝ) / 2) * hermiteFunction (n + 1) x +
              Real.sqrt ((↑n : ℝ) / 2) * hermiteFunction (n - 1) x|
          ≤ |x| ^ k * (Real.sqrt ((↑(n + 1) : ℝ) / 2) * |hermiteFunction (n + 1) x| +
              Real.sqrt ((↑n : ℝ) / 2) * |hermiteFunction (n - 1) x|) := by
            gcongr
        _ ≤ C * ((2 : ℝ) ^ s + 1) * (1 + ↑n) ^ (s + 1) := h_main
  | succ m ih =>
    -- Step m → m+1: use deriv_hermiteFunction + iteratedDeriv linearity
    -- iteratedDeriv (m+1) ψ_n = iteratedDeriv m (ψ_n')
    --   = √(n/2) · iteratedDeriv m ψ_{n-1} - √((n+1)/2) · iteratedDeriv m ψ_{n+1}
    obtain ⟨C, s, hC, hs, hbound⟩ := ih
    refine ⟨C * ((2 : ℝ) ^ s + 1), s + 1, by positivity, by linarith, fun n x => ?_⟩
    -- iteratedDeriv (m+1) ψ_n = iteratedDeriv m (deriv ψ_n)
    rw [iteratedDeriv_succ']
    -- deriv ψ_n = √(n/2) · ψ_{n-1} - √((n+1)/2) · ψ_{n+1}
    have hderiv_eq : deriv (hermiteFunction n) =
        fun x => Real.sqrt ((↑n : ℝ) / 2) * hermiteFunction (n - 1) x -
                 Real.sqrt ((↑(n + 1) : ℝ) / 2) * hermiteFunction (n + 1) x := by
      ext x; exact deriv_hermiteFunction n x
    rw [hderiv_eq]
    -- Linearity of iteratedDeriv: split const_mul and sub
    have hcd1 : ContDiffAt ℝ m (fun x => Real.sqrt ((↑n : ℝ) / 2) * hermiteFunction (n - 1) x) x :=
      ((hermiteFunction_contDiff (n - 1) m).const_smul (Real.sqrt ((↑n : ℝ) / 2))).contDiffAt
    have hcd2 : ContDiffAt ℝ m (fun x => Real.sqrt ((↑(n + 1) : ℝ) / 2) * hermiteFunction (n + 1) x) x :=
      ((hermiteFunction_contDiff (n + 1) m).const_smul (Real.sqrt ((↑(n + 1) : ℝ) / 2))).contDiffAt
    rw [iteratedDeriv_fun_sub hcd1 hcd2]
    simp only [iteratedDeriv_const_mul_field]
    -- Goal: |x|^k * ‖√(n/2) * iteratedDeriv m ψ_{n-1} x - √((n+1)/2) * iteratedDeriv m ψ_{n+1} x‖
    --       ≤ C*(2^s+1)*(1+n)^(s+1)
    -- Triangle inequality: ‖a - b‖ ≤ ‖a‖ + ‖b‖
    have h_tri : ‖Real.sqrt ((↑n : ℝ) / 2) * iteratedDeriv m (hermiteFunction (n - 1)) x -
        Real.sqrt ((↑(n + 1) : ℝ) / 2) * iteratedDeriv m (hermiteFunction (n + 1)) x‖ ≤
        ‖Real.sqrt ((↑n : ℝ) / 2) * iteratedDeriv m (hermiteFunction (n - 1)) x‖ +
        ‖Real.sqrt ((↑(n + 1) : ℝ) / 2) * iteratedDeriv m (hermiteFunction (n + 1)) x‖ :=
      norm_sub_le _ _
    calc |x| ^ k * ‖Real.sqrt ((↑n : ℝ) / 2) * iteratedDeriv m (hermiteFunction (n - 1)) x -
            Real.sqrt ((↑(n + 1) : ℝ) / 2) * iteratedDeriv m (hermiteFunction (n + 1)) x‖
        ≤ |x| ^ k * (‖Real.sqrt ((↑n : ℝ) / 2) * iteratedDeriv m (hermiteFunction (n - 1)) x‖ +
            ‖Real.sqrt ((↑(n + 1) : ℝ) / 2) * iteratedDeriv m (hermiteFunction (n + 1)) x‖) := by
          gcongr
      _ = |x| ^ k * (Real.sqrt ((↑n : ℝ) / 2) * ‖iteratedDeriv m (hermiteFunction (n - 1)) x‖ +
            Real.sqrt ((↑(n + 1) : ℝ) / 2) * ‖iteratedDeriv m (hermiteFunction (n + 1)) x‖) := by
          congr 1
          rw [norm_mul, norm_mul, Real.norm_of_nonneg (Real.sqrt_nonneg _),
              Real.norm_of_nonneg (Real.sqrt_nonneg _)]
      _ = Real.sqrt ((↑n : ℝ) / 2) * (|x| ^ k * ‖iteratedDeriv m (hermiteFunction (n - 1)) x‖) +
          Real.sqrt ((↑(n + 1) : ℝ) / 2) * (|x| ^ k * ‖iteratedDeriv m (hermiteFunction (n + 1)) x‖) := by
          ring
      _ ≤ Real.sqrt ((↑n : ℝ) / 2) * (C * (1 + ↑(n - 1)) ^ s) +
          Real.sqrt ((↑(n + 1) : ℝ) / 2) * (C * (1 + ↑(n + 1)) ^ s) := by
          apply add_le_add
          · exact mul_le_mul_of_nonneg_left (hbound (n - 1) x) (Real.sqrt_nonneg _)
          · exact mul_le_mul_of_nonneg_left (hbound (n + 1) x) (Real.sqrt_nonneg _)
      _ ≤ (1 + ↑n) * (C * (1 + ↑n) ^ s) +
          (1 + ↑n) * (C * ((2:ℝ) ^ s * (1 + ↑n) ^ s)) := by
          apply add_le_add
          · apply mul_le_mul _ _ (by positivity) (by positivity)
            · rw [← Real.sqrt_sq (by positivity : (0:ℝ) ≤ 1 + ↑n)]
              apply Real.sqrt_le_sqrt
              have : (0 : ℝ) ≤ (n : ℝ) := Nat.cast_nonneg n
              nlinarith [sq_nonneg ((n : ℕ) : ℝ)]
            · apply mul_le_mul_of_nonneg_left _ (le_of_lt hC)
              apply Real.rpow_le_rpow (by positivity) _ hs
              have : (↑(n - 1) : ℝ) ≤ (↑n : ℝ) := Nat.cast_le.mpr (Nat.sub_le n 1)
              linarith
          · apply mul_le_mul _ _ (by positivity) (by positivity)
            · rw [← Real.sqrt_sq (by positivity : (0:ℝ) ≤ 1 + ↑n)]
              apply Real.sqrt_le_sqrt; push_cast
              have : (0 : ℝ) ≤ (n : ℝ) := Nat.cast_nonneg n
              nlinarith [sq_nonneg ((n : ℝ) + 1)]
            · apply mul_le_mul_of_nonneg_left _ (le_of_lt hC)
              rw [← Real.mul_rpow (by positivity : (0:ℝ) ≤ 2) (by positivity : (0:ℝ) ≤ 1 + ↑n)]
              apply Real.rpow_le_rpow (by positivity) _ hs; push_cast; linarith
      _ = C * ((2:ℝ) ^ s + 1) * ((1 + ↑n) ^ s * (1 + ↑n)) := by ring
      _ = C * ((2:ℝ) ^ s + 1) * (1 + ↑n) ^ (s + 1) := by
          rw [Real.rpow_add (by positivity : (0:ℝ) < 1 + ↑n), Real.rpow_one]

/-- Schwartz seminorm bound: ‖ψₙ‖_{k,m} grows at most polynomially in n.
    Specifically, there exist constants C, s depending on k, m such that
    ‖ψₙ‖_{k,m} ≤ C · (1 + n)^s.

    This is the key property ensuring that the inclusion between Sobolev-Hermite
    levels is Hilbert-Schmidt. The exponent s depends on the seminorm order:
    for the Schwartz seminorm p_{k,m}(f) = sup_x (1+|x|)^k |f^(m)(x)|,
    we have ‖ψₙ‖_{k,m} ≤ C_{k,m} · (1+n)^{(k+m)/2 + 1/4}. -/
theorem hermiteFunction_seminorm_bound (k m : ℕ) :
    ∃ (C : ℝ) (s : ℝ), 0 < C ∧ 0 ≤ s ∧
    ∀ n : ℕ, SchwartzMap.seminorm ℝ k m
      (Classical.choose (hermiteFunction_schwartz n)) ≤ C * (1 + n) ^ s := by
  obtain ⟨C, s, hC, hs, hbound⟩ := hermiteFunction_pointwise_bound k m
  refine ⟨C, s, hC, hs, fun n => ?_⟩
  apply SchwartzMap.seminorm_le_bound' ℝ k m
  · positivity
  · intro x
    have hext : ⇑(Classical.choose (hermiteFunction_schwartz n)) = hermiteFunction n :=
      funext (Classical.choose_spec (hermiteFunction_schwartz n))
    simp only [hext]
    exact hbound n x

/-! ## Sobolev-Hermite Spaces

Using the Hermite expansion f = ∑ₙ cₙ ψₙ (where cₙ = ∫ f ψₙ),
the Sobolev-Hermite space of order s is:
  Hˢ = { f : ∑ₙ (1+n)^{2s} |cₙ|² < ∞ }

The inclusion Hˢ⁺¹ ↪ Hˢ is diagonal with eigenvalues (1+n)^{-1},
and ∑ₙ (1+n)^{-2} = π²/6 < ∞, so each inclusion is Hilbert-Schmidt.

The Schwartz space equals ∩ₛ Hˢ, and the Schwartz topology is generated
by the Sobolev-Hermite norms. -/

/-- The Hilbert-Schmidt sum for the inclusion between Sobolev-Hermite levels:
    ∑_{n=0}^{N-1} (1+n)^{-2s} is bounded for s > 1/2, uniformly in N. -/
theorem sobolevHermite_hs_sum_bound (s : ℝ) (hs : 1/2 < s) :
    ∃ C : ℝ, ∀ N : ℕ, ∑ i ∈ Finset.range N, ((1 + (i : ℝ)) ^ (-2 * s)) ≤ C := by
  -- The series ∑ (1+n)^{-2s} converges when 2s > 1, so partial sums are bounded by tsum
  have hexp : -2 * s < -1 := by linarith
  have hsumm : Summable (fun i : ℕ => (1 + (i : ℝ)) ^ (-2 * s)) := by
    have h := (summable_nat_add_iff (f := fun n => (↑n : ℝ) ^ (-2 * s)) 1).mpr
      (Real.summable_nat_rpow.mpr hexp)
    simp only [Nat.cast_add, Nat.cast_one] at h
    exact h.congr fun m => by rw [add_comm]
  exact ⟨∑' (i : ℕ), (1 + (i : ℝ)) ^ (-2 * s), fun N =>
    hsumm.sum_le_tsum (Finset.range N) (fun i _ =>
      Real.rpow_nonneg (by positivity : (0 : ℝ) ≤ 1 + ↑i) _)⟩

/-! ### Proof of Hermite completeness

We now prove `hermiteFunction_complete` (stated as an axiom above).
Phase 1: Show ∫ f·xᵏ·exp(-x²/2) = 0 using the `mul_x_hermiteFunction` recurrence.
Phase 2-3: Conclude f = 0 via Schwartz density in L². -/

/-- ψₙ(x) = cₙ · (hermiteR n).eval(x·√2) · exp(-x²/2), so
    (hermiteR n).eval(x·√2) · exp(-x²/2) = cₙ⁻¹ · ψₙ(x). -/
private lemma hermiteR_eval_mul_gaussian_eq (n : ℕ) (x : ℝ) :
    (hermiteR n).eval (x * Real.sqrt 2) * Real.exp (-(x ^ 2 / 2)) =
    (hermiteFunctionNormConst n)⁻¹ * hermiteFunction n x := by
  unfold hermiteFunction
  rw [show -(x ^ 2 / 2) = -(x ^ 2) / 2 from by ring]
  rw [show hermiteFunctionNormConst n * (hermiteR n).eval (x * Real.sqrt 2) *
    Real.exp (-(x ^ 2) / 2) = hermiteFunctionNormConst n *
    ((hermiteR n).eval (x * Real.sqrt 2) * Real.exp (-(x ^ 2) / 2)) from by ring]
  rw [inv_mul_cancel_left₀ (ne_of_gt (normConst_pos n))]

/-- If f ∈ L² is orthogonal to all ψₙ, then ∫ f · (hermiteR n).eval(x√2) · G = 0. -/
private lemma integral_f_hermiteR_gaussian_zero
    (f : ℝ → ℝ) (_hf : MemLp f 2 volume)
    (horth : ∀ n, ∫ x, f x * hermiteFunction n x = 0)
    (n : ℕ) :
    ∫ x, f x * ((hermiteR n).eval (x * Real.sqrt 2) * Real.exp (-(x ^ 2 / 2))) = 0 := by
  simp_rw [hermiteR_eval_mul_gaussian_eq]
  rw [show (fun x => f x * ((hermiteFunctionNormConst n)⁻¹ * hermiteFunction n x)) =
      fun x => (hermiteFunctionNormConst n)⁻¹ * (f x * hermiteFunction n x) from by ext x; ring]
  rw [integral_const_mul, horth n, mul_zero]

/-- Phase 1: If f ∈ L² is orthogonal to all Hermite functions, then
    ∫ f(x) · x^k · exp(-x²/2) dx = 0 for all k. -/
private lemma integral_f_xpow_gaussian_zero
    (f : ℝ → ℝ) (hf : MemLp f 2 volume)
    (horth : ∀ n, ∫ x, f x * hermiteFunction n x = 0) :
    ∀ k : ℕ, ∫ x, f x * (x ^ k * Real.exp (-(x ^ 2 / 2))) = 0 := by
  have h_sqrt2_pos : (0 : ℝ) < Real.sqrt 2 := Real.sqrt_pos.mpr (by norm_num : (0:ℝ) < 2)
  have h_sqrt2_ne : Real.sqrt 2 ≠ 0 := ne_of_gt h_sqrt2_pos
  have h_sqrt2_sq : Real.sqrt 2 * Real.sqrt 2 = 2 := Real.mul_self_sqrt (by norm_num : (0:ℝ) ≤ 2)
  have h_pG_memLp : ∀ (p : ℝ[X]),
      MemLp (fun x => p.eval x * Real.exp (-(x ^ 2 / 2))) 2 volume := by
    intro p
    obtain ⟨φ, hφ⟩ := poly_mul_gaussian_schwartz p
    exact MemLp.ae_eq (Filter.Eventually.of_forall hφ) (φ.memLp 2 volume)
  have h_integrable : ∀ (p : ℝ[X]),
      Integrable (fun x => f x * (p.eval x * Real.exp (-(x ^ 2 / 2)))) volume := by
    intro p
    have h_int := L2.integrable_inner (𝕜 := ℝ) (MemLp.toLp f hf) (MemLp.toLp _ (h_pG_memLp p))
    refine h_int.congr ?_
    filter_upwards [MemLp.coeFn_toLp hf, MemLp.coeFn_toLp (h_pG_memLp p)] with x hfx hpGx
    rw [hfx, hpGx, real_inner_eq_re_inner ℝ, RCLike.inner_apply', conj_trivial, RCLike.re_to_real]
  have h_mono_integrable : ∀ (c : ℝ) (i : ℕ),
      Integrable (fun x => c * (f x * (x ^ i * Real.exp (-(x ^ 2 / 2))))) volume := by
    intro c i
    have : (fun x : ℝ => c * (f x * (x ^ i * Real.exp (-(x ^ 2 / 2))))) =
        fun x => c * (f x * ((Polynomial.X ^ i).eval x * Real.exp (-(x ^ 2 / 2)))) := by
      ext x; simp [Polynomial.eval_pow, Polynomial.eval_X]
    rw [this]
    exact (h_integrable (Polynomial.X ^ i)).const_mul c
  have h_hermiteR_zero : ∀ n, ∫ x, f x * ((hermiteR n).eval (x * Real.sqrt 2) *
      Real.exp (-(x ^ 2 / 2))) = 0 := integral_f_hermiteR_gaussian_zero f hf horth
  intro k
  induction k using Nat.strongRecOn with
  | ind k ih =>
  set Q := (hermiteR k).comp (Polynomial.C (Real.sqrt 2) * Polynomial.X) with hQ_def
  have hQeval : ∀ x : ℝ, Q.eval x = (hermiteR k).eval (x * Real.sqrt 2) := by
    intro x
    simp [hQ_def, Polynomial.eval_comp, Polynomial.eval_mul, Polynomial.eval_C,
      Polynomial.eval_X, mul_comm x (Real.sqrt 2)]
  have hR_natDeg : (hermiteR k).natDegree = k :=
    ((hermite_monic k).natDegree_map (Int.castRingHom ℝ)).trans natDegree_hermite
  have hR_monic : (hermiteR k).Monic := (hermite_monic k).map (Int.castRingHom ℝ)
  have hCX_natDeg : (Polynomial.C (Real.sqrt 2) * Polynomial.X).natDegree = 1 :=
    Polynomial.natDegree_C_mul_X _ h_sqrt2_ne
  have hCX_lead : (Polynomial.C (Real.sqrt 2) * Polynomial.X).leadingCoeff = Real.sqrt 2 :=
    Polynomial.leadingCoeff_C_mul_X (Real.sqrt 2)
  have hCX_ne : (Polynomial.C (Real.sqrt 2) * Polynomial.X).natDegree ≠ 0 := by
    rw [hCX_natDeg]; exact one_ne_zero
  have hQ_natDeg : Q.natDegree = k := by
    rw [hQ_def, Polynomial.natDegree_comp, hCX_natDeg, mul_one, hR_natDeg]
  have hQ_lead : Q.leadingCoeff = Real.sqrt 2 ^ k := by
    rw [hQ_def, Polynomial.leadingCoeff_comp hCX_ne, hR_monic, one_mul,
      hCX_lead, hR_natDeg]
  have h_sqrt2_pow_ne : Real.sqrt 2 ^ k ≠ 0 := pow_ne_zero k h_sqrt2_ne
  set R := Q - Polynomial.C (Real.sqrt 2 ^ k) * Polynomial.X ^ k with hR_def
  have hQR : ∀ x : ℝ, Q.eval x = Real.sqrt 2 ^ k * x ^ k + R.eval x := by
    intro x
    simp [hR_def, Polynomial.eval_sub, Polynomial.eval_mul, Polynomial.eval_C,
      Polynomial.eval_pow, Polynomial.eval_X]
  have hR_deg : R.natDegree < k ∨ k = 0 := by
    by_cases hk : k = 0
    · right; exact hk
    · left
      rw [hR_def]
      have hmon_deg : (Polynomial.C (Real.sqrt 2 ^ k) * Polynomial.X ^ k).natDegree = k :=
        Polynomial.natDegree_C_mul_X_pow k _ h_sqrt2_pow_ne
      have hmon_lead : (Polynomial.C (Real.sqrt 2 ^ k) * Polynomial.X ^ k).leadingCoeff =
          Real.sqrt 2 ^ k :=
        Polynomial.leadingCoeff_C_mul_X_pow _ _
      have h_cancel : Q.leadingCoeff = (Polynomial.C (Real.sqrt 2 ^ k) * Polynomial.X ^ k).leadingCoeff := by
        rw [hQ_lead, hmon_lead]
      have hQ_ne : Q ≠ 0 := by
        intro h; exact h_sqrt2_pow_ne (hQ_lead ▸ Polynomial.leadingCoeff_eq_zero.mpr h)
      have hmon_ne : Polynomial.C (Real.sqrt 2 ^ k) * Polynomial.X ^ k ≠ 0 := by
        intro h; exact h_sqrt2_pow_ne (hmon_lead.symm.trans (Polynomial.leadingCoeff_eq_zero.mpr h))
      have h_same_deg : Q.degree = (Polynomial.C (Real.sqrt 2 ^ k) * Polynomial.X ^ k).degree := by
        rw [Polynomial.degree_eq_natDegree hQ_ne, Polynomial.degree_eq_natDegree hmon_ne,
          hQ_natDeg, hmon_deg]
      have h_deg_lt := Polynomial.degree_sub_lt h_same_deg hQ_ne h_cancel
      rw [Polynomial.degree_eq_natDegree hQ_ne, hQ_natDeg] at h_deg_lt
      by_cases hR_zero : Q - Polynomial.C (Real.sqrt 2 ^ k) * Polynomial.X ^ k = 0
      · rw [hR_zero]; exact Nat.pos_of_ne_zero hk
      · rwa [Polynomial.degree_eq_natDegree hR_zero, Nat.cast_lt] at h_deg_lt
  have h_xpow : ∀ x : ℝ, x ^ k = (Real.sqrt 2 ^ k)⁻¹ * (Q.eval x - R.eval x) := by
    intro x
    have hsub : Q.eval x - R.eval x = Real.sqrt 2 ^ k * x ^ k := by linarith [hQR x]
    rw [hsub, inv_mul_cancel_left₀ h_sqrt2_pow_ne]
  have h_rewrite : ∀ x : ℝ,
      f x * (x ^ k * Real.exp (-(x ^ 2 / 2))) =
      (Real.sqrt 2 ^ k)⁻¹ * (f x * (Q.eval x * Real.exp (-(x ^ 2 / 2)))) -
      (Real.sqrt 2 ^ k)⁻¹ * (f x * (R.eval x * Real.exp (-(x ^ 2 / 2)))) := by
    intro x; rw [h_xpow x]; ring
  simp_rw [h_rewrite]
  rw [integral_sub ((h_integrable Q).const_mul _) ((h_integrable R).const_mul _),
    integral_const_mul, integral_const_mul]
  have hQ_zero : ∫ x, f x * (Q.eval x * Real.exp (-(x ^ 2 / 2))) = 0 := by
    simp_rw [hQeval]; exact h_hermiteR_zero k
  rw [hQ_zero, mul_zero, zero_sub]
  suffices hR_zero : ∫ x, f x * (R.eval x * Real.exp (-(x ^ 2 / 2))) = 0 by
    rw [hR_zero, mul_zero, neg_zero]
  rcases hR_deg with hR_lt | hk0
  · have hR_eval : ∀ x : ℝ, R.eval x * Real.exp (-(x ^ 2 / 2)) =
        ∑ i ∈ Finset.range (R.natDegree + 1),
          R.coeff i * (x ^ i * Real.exp (-(x ^ 2 / 2))) := by
      intro x; rw [Polynomial.eval_eq_sum_range x, Finset.sum_mul]; congr 1; ext i; ring
    have h_eq_sum : ∫ x, f x * (R.eval x * Real.exp (-(x ^ 2 / 2))) =
        ∑ i ∈ Finset.range (R.natDegree + 1),
          R.coeff i * ∫ x, f x * (x ^ i * Real.exp (-(x ^ 2 / 2))) := by
      simp_rw [hR_eval, Finset.mul_sum]
      rw [integral_finsetSum _ (fun i _ => ?_)]
      · congr 1; ext i
        rw [show (fun x => f x * (R.coeff i * (x ^ i * Real.exp (-(x ^ 2 / 2))))) =
          fun x => R.coeff i * (f x * (x ^ i * Real.exp (-(x ^ 2 / 2)))) from by ext x; ring,
          integral_const_mul]
      · show Integrable (fun x => f x * (R.coeff i * (x ^ i * Real.exp (-(x ^ 2 / 2))))) volume
        rw [show (fun x => f x * (R.coeff i * (x ^ i * Real.exp (-(x ^ 2 / 2))))) =
          fun x => R.coeff i * (f x * (x ^ i * Real.exp (-(x ^ 2 / 2)))) from by ext x; ring]
        exact h_mono_integrable _ _
    rw [h_eq_sum]
    apply Finset.sum_eq_zero
    intro i hi
    rw [ih i (by have := Finset.mem_range.mp hi; omega), mul_zero]
  · subst hk0
    have hQ_const : Q = Polynomial.C 1 := by
      simp [hQ_def, hermiteR, hermite_zero, Polynomial.map_one, Polynomial.one_comp]
    have hR_zero_poly : R = 0 := by simp [hR_def, hQ_const, pow_zero, mul_one]
    simp [hR_zero_poly]

private lemma integral_f_poly_gaussian_zero
    (f : ℝ → ℝ) (hf : MemLp f 2 volume)
    (horth : ∀ n, ∫ x, f x * hermiteFunction n x = 0)
    (P : ℝ[X]) :
    ∫ x, f x * (P.eval x * Real.exp (-(x ^ 2 / 2))) = 0 := by
  induction P using Polynomial.induction_on' with
  | add p q hp hq =>
    have h_split : (fun x => f x * ((p + q).eval x * Real.exp (-(x ^ 2 / 2)))) =
        (fun x => f x * (p.eval x * Real.exp (-(x ^ 2 / 2))) +
                   f x * (q.eval x * Real.exp (-(x ^ 2 / 2)))) := by
      ext x; simp [Polynomial.eval_add, add_mul, mul_add]
    rw [h_split]
    have h_int_poly : ∀ (r : ℝ[X]),
        Integrable (fun x => f x * (r.eval x * Real.exp (-(x ^ 2 / 2)))) volume := by
      intro r
      obtain ⟨φ, hφ⟩ := poly_mul_gaussian_schwartz r
      have hrG : MemLp (fun x => r.eval x * Real.exp (-(x ^ 2 / 2))) 2 volume :=
        MemLp.ae_eq (Filter.Eventually.of_forall hφ) (φ.memLp 2 volume)
      refine (L2.integrable_inner (𝕜 := ℝ) (MemLp.toLp f hf) (MemLp.toLp _ hrG)).congr ?_
      filter_upwards [MemLp.coeFn_toLp hf, MemLp.coeFn_toLp hrG] with x hfx hrGx
      rw [hfx, hrGx, real_inner_eq_re_inner ℝ, RCLike.inner_apply', conj_trivial, RCLike.re_to_real]
    rw [integral_add (h_int_poly p) (h_int_poly q), hp, hq, add_zero]
  | monomial k a =>
    simp only [Polynomial.eval_monomial]
    have h_rw : (fun x => f x * (a * x ^ k * Real.exp (-(x ^ 2 / 2)))) =
        fun x => a * (f x * (x ^ k * Real.exp (-(x ^ 2 / 2)))) := by ext x; ring
    rw [h_rw, integral_const_mul, integral_f_xpow_gaussian_zero f hf horth k, mul_zero]

/-  Phase 2-3: Conclude f = 0 a.e. from moment vanishing. -/

/-- g := f · exp(-x²/2) is integrable when f ∈ L² (by Cauchy-Schwarz). -/
private lemma integrable_f_mul_gaussian
    (f : ℝ → ℝ) (hf : MemLp f 2 volume) :
    Integrable (fun x => f x * Real.exp (-(x ^ 2 / 2))) volume := by
  obtain ⟨φ, hφ⟩ := poly_mul_gaussian_schwartz (1 : ℝ[X])
  have hG : MemLp (fun x => Real.exp (-(x ^ 2 / 2))) 2 volume := by
    have h1 : MemLp (fun x => (1 : ℝ[X]).eval x * Real.exp (-(x ^ 2 / 2))) 2 volume :=
      MemLp.ae_eq (Filter.Eventually.of_forall hφ) (φ.memLp 2 volume)
    exact MemLp.ae_eq (Filter.Eventually.of_forall (fun x => by simp)) h1
  exact (L2.integrable_inner (𝕜 := ℝ) (MemLp.toLp f hf)
    (MemLp.toLp _ hG)).congr
    (by filter_upwards [MemLp.coeFn_toLp hf, MemLp.coeFn_toLp hG] with x hfx hGx
        rw [hfx, hGx, real_inner_eq_re_inner ℝ, RCLike.inner_apply', conj_trivial, RCLike.re_to_real])

/-- |f(x)| * exp(-x²/4) is integrable when f ∈ L², by Cauchy-Schwarz:
    f ∈ L², exp(-x²/4) ∈ L² (since exp(-x²/4)² = exp(-x²/2)). -/
private lemma integrable_f_mul_half_gaussian
    (f : ℝ → ℝ) (hf : MemLp f 2 volume) :
    Integrable (fun x => f x * Real.exp (-(x ^ 2 / 4))) volume := by
  have hG : MemLp (fun x => Real.exp (-(x ^ 2 / 4))) 2 volume := by
    have hmeas : AEStronglyMeasurable (fun x => Real.exp (-(x ^ 2 / 4))) volume :=
      Measurable.aestronglyMeasurable (by fun_prop)
    rw [memLp_two_iff_integrable_sq hmeas]
    have h_sq : (fun x => Real.exp (-(x ^ 2 / 4)) ^ 2) =
        (fun x => Real.exp (-(1/2) * x ^ 2)) := by
      ext x; rw [sq, ← Real.exp_add]; congr 1; ring
    rw [h_sq]
    exact integrable_exp_neg_mul_sq (by positivity)
  exact (L2.integrable_inner (𝕜 := ℝ) (MemLp.toLp f hf)
    (MemLp.toLp _ hG)).congr
    (by filter_upwards [MemLp.coeFn_toLp hf, MemLp.coeFn_toLp hG] with x hfx hGx
        rw [hfx, hGx, real_inner_eq_re_inner ℝ, RCLike.inner_apply', conj_trivial, RCLike.re_to_real])

set_option maxHeartbeats 800000 in
/-- g(x) * exp(c|x|) is integrable, where g(x) = f(x) * exp(-x²/2), by completing the square:
    |g(x)| * exp(c|x|) = |f(x)| * exp(-x²/2 + c|x|) ≤ exp(c²) * |f(x)| * exp(-x²/4). -/
private lemma integrable_g_mul_exp_linear
    (f : ℝ → ℝ) (hf : MemLp f 2 volume) (c : ℝ) :
    Integrable (fun x => ‖f x * Real.exp (-(x ^ 2 / 2))‖ * Real.exp (c * |x|)) volume := by
  have hfg := integrable_f_mul_half_gaussian f hf
  -- The bound: ‖g(x)‖ * exp(c|x|) ≤ exp(c²) * |f(x) * exp(-x²/4)|
  -- Use Integrable.mono' with the nonneg bound
  apply (hfg.norm.const_mul (Real.exp (c ^ 2))).mono'
  · apply AEStronglyMeasurable.mul
    · exact (hf.aestronglyMeasurable.mul
        (Measurable.aestronglyMeasurable (by fun_prop))).norm
    · exact Measurable.aestronglyMeasurable (by fun_prop)
  · filter_upwards with x
    -- Goal: ‖‖f x * exp(-x²/2)‖ * exp(c*|x|)‖ ≤ exp(c²) * ‖f x * exp(-x²/4)‖
    have hexp1 : (0 : ℝ) < Real.exp (-(x ^ 2 / 2)) := Real.exp_pos _
    have hexp2 : (0 : ℝ) < Real.exp (c * |x|) := Real.exp_pos _
    have hexp3 : (0 : ℝ) < Real.exp (-(x ^ 2 / 4)) := Real.exp_pos _
    simp only [norm_mul, Real.norm_eq_abs, abs_of_pos hexp1, abs_of_pos hexp2,
      abs_of_pos hexp3, abs_abs]
    calc |f x| * Real.exp (-(x ^ 2 / 2)) * Real.exp (c * |x|)
        = |f x| * Real.exp (-(x ^ 2 / 2) + c * |x|) := by
          rw [mul_assoc, ← Real.exp_add]
      _ ≤ |f x| * Real.exp (c ^ 2 + -(x ^ 2 / 4)) := by
          apply mul_le_mul_of_nonneg_left _ (abs_nonneg _)
          apply Real.exp_le_exp_of_le
          nlinarith [sq_nonneg (|x| / 2 - c), sq_abs x]
      _ = Real.exp (c ^ 2) * (|f x| * Real.exp (-(x ^ 2 / 4))) := by
          rw [Real.exp_add]; ring

set_option maxHeartbeats 3200000 in
/-- The L^1 Fourier transform of g(x) = f(x) exp(-x^2/2) vanishes when all
    polynomial moments of g are zero. Uses DCT with partial sums of exp. -/
private lemma fourierIntegral_f_mul_gaussian_eq_zero
    (f : ℝ → ℝ) (hf : MemLp f 2 volume)
    (h_moments : ∀ k : ℕ, ∫ x, f x * (x ^ k * Real.exp (-(x ^ 2 / 2))) = 0) :
    ∀ ξ : ℝ, VectorFourier.fourierIntegral Real.fourierChar volume (innerₗ ℝ)
      (fun x => (↑(f x * Real.exp (-(x ^ 2 / 2))) : ℂ)) ξ = 0 := by
  intro ξ
  -- Abbreviations
  set g : ℝ → ℝ := fun x => f x * Real.exp (-(x ^ 2 / 2)) with hg_def
  set g_ℂ : ℝ → ℂ := fun x => (↑(g x) : ℂ) with hg_ℂ_def
  -- Rewrite Fourier integral using exp form
  rw [Real.vector_fourierIntegral_eq_integral_exp_smul]
  -- In ℂ, • is just multiplication
  simp_rw [smul_eq_mul]
  -- Define the complex exponential argument; innerₗ ℝ x ξ = ⟪x,ξ⟫_ℝ
  set z : ℝ → ℂ := fun x => ↑(-2 * π * innerₗ ℝ x ξ) * Complex.I with hz_def
  -- Partial sums: F N x = (∑ k in range N, z(x)^k / k!) * g_ℂ(x)
  let expPartialSum : ℂ → ℕ → ℂ := fun w N =>
    Finset.sum (Finset.range N) fun k => w ^ k / (Nat.factorial k : ℂ)
  let F : ℕ → ℝ → ℂ := fun N x => expPartialSum (z x) N * g_ℂ x
  -- The integrand
  set target : ℝ → ℂ := fun x => Complex.exp (z x) * g_ℂ x with htarget_def
  -- Step 1: F N → target pointwise (from power series of exp)
  have h_pw : ∀ x, Filter.Tendsto (fun N => F N x) Filter.atTop (nhds (target x)) := by
    intro x
    apply Filter.Tendsto.mul_const
    rw [Complex.exp_eq_exp_ℂ]
    exact (NormedSpace.expSeries_div_hasSum_exp (z x)).tendsto_sum_nat
  -- Step 2: Dominating function
  set bound : ℝ → ℝ := fun x => ‖g x‖ * Real.exp (2 * π * |ξ| * |x|) with hbound_def
  -- Helper: innerₗ ℝ x ξ = x * ξ for ℝ
  have inner_eq : ∀ y : ℝ, innerₗ ℝ y ξ = y * ξ := by
    intro y; rw [innerₗ_apply_apply, real_inner_eq_re_inner ℝ, RCLike.inner_apply, conj_trivial,
      RCLike.re_to_real, mul_comm]
  -- Helper: ‖z x‖ = 2π|x||ξ|
  have norm_z : ∀ y : ℝ, ‖z y‖ ≤ 2 * π * |ξ| * |y| := by
    intro y
    simp only [z, Complex.norm_mul, Complex.norm_I, mul_one, Complex.norm_real, inner_eq,
      Real.norm_eq_abs]
    -- Goal: |-2 * π * (y * ξ)| ≤ 2 * π * |ξ| * |y|
    have : |-2 * π * (y * ξ)| = 2 * π * |ξ| * |y| := by
      have h1 : (-2 : ℝ) * π * (y * ξ) = -(2 * π * (y * ξ)) := by ring
      rw [h1, abs_neg, abs_mul, abs_of_pos (by positivity : (0 : ℝ) < 2 * π), abs_mul]
      ring
    linarith
  -- Step 3: ‖F N x‖ ≤ bound x
  have h_bound : ∀ N, ∀ᵐ x ∂volume, ‖F N x‖ ≤ bound x := by
    intro N
    filter_upwards with x
    show ‖expPartialSum (z x) N * g_ℂ x‖ ≤ _
    -- ‖a * b‖ ≤ ‖a‖ * ‖b‖, and ‖g_ℂ x‖ = ‖g x‖
    have hgn : ‖g_ℂ x‖ = ‖g x‖ := by simp [g_ℂ, Complex.norm_real, Real.norm_eq_abs]
    -- Partial sum bound
    have h_ps : ‖expPartialSum (z x) N‖ ≤ Real.exp (2 * π * |ξ| * |x|) :=
      calc ‖expPartialSum (z x) N‖
          = ‖∑ k ∈ Finset.range N, z x ^ k / (Nat.factorial k : ℂ)‖ := rfl
        _ ≤ ∑ k ∈ Finset.range N, ‖z x ^ k / (Nat.factorial k : ℂ)‖ := norm_sum_le _ _
        _ = ∑ k ∈ Finset.range N, ‖z x‖ ^ k / (Nat.factorial k) := by
            congr 1; ext k
            rw [norm_div, norm_pow, Complex.norm_natCast]
        _ ≤ Real.exp ‖z x‖ :=
            Real.sum_le_exp_of_nonneg (norm_nonneg _) N
        _ ≤ Real.exp (2 * π * |ξ| * |x|) :=
            Real.exp_le_exp_of_le (norm_z x)
    simp only [hbound_def]
    calc ‖expPartialSum (z x) N * g_ℂ x‖
        ≤ ‖expPartialSum (z x) N‖ * ‖g_ℂ x‖ := norm_mul_le _ _
      _ = ‖expPartialSum (z x) N‖ * ‖g x‖ := by rw [hgn]
      _ ≤ Real.exp (2 * π * |ξ| * |x|) * ‖g x‖ :=
          mul_le_mul_of_nonneg_right h_ps (norm_nonneg _)
      _ = ‖g x‖ * Real.exp (2 * π * |ξ| * |x|) := mul_comm _ _
  -- Step 4: Bound is integrable
  have h_bound_int : Integrable bound volume := by
    have key := integrable_g_mul_exp_linear f hf (2 * π * |ξ|)
    -- key : Integrable (fun x => ‖f x * exp(-x²/2)‖ * exp((2π|ξ|) * |x|))
    -- bound x = ‖g x‖ * exp(2π|ξ| * |x|)
    -- These are the same since g = f * exp(-x²/2) and mul is assoc
    exact key.congr (by filter_upwards with x; simp [hbound_def, g, mul_assoc])
  -- Step 5: F N is measurable
  have hg_ℂ_meas : AEStronglyMeasurable g_ℂ volume :=
    RCLike.continuous_ofReal.comp_aestronglyMeasurable
      (hf.aestronglyMeasurable.mul (Measurable.aestronglyMeasurable (by fun_prop)))
  have hz_cont : Continuous z := by
    simp only [z, inner_eq]
    fun_prop
  have h_summand_cont : ∀ k : ℕ,
      Continuous (fun x : ℝ => z x ^ k / (Nat.factorial k : ℂ)) :=
    fun k => (hz_cont.pow k).div_const _
  have h_meas : ∀ N, AEStronglyMeasurable (F N) volume := by
    intro N
    show AEStronglyMeasurable (fun x => expPartialSum (z x) N * g_ℂ x) volume
    apply AEStronglyMeasurable.mul _ hg_ℂ_meas
    exact Finset.aestronglyMeasurable_fun_sum _ (fun k _ =>
      (h_summand_cont k).measurable.aestronglyMeasurable)
  -- Step 6: Apply DCT
  have h_DCT := tendsto_integral_of_dominated_convergence bound h_meas h_bound_int h_bound
    (Filter.Eventually.of_forall h_pw)
  -- Factor z(x)^k = c^k * ↑(x^k) where c = ↑(-2πξ) * I
  set c : ℂ := ↑(-2 * π * ξ) * Complex.I with hc_def
  have h_zk : ∀ (k : ℕ) (y : ℝ), z y ^ k = c ^ k * ↑(y ^ k) := by
    intro k y
    simp only [z, innerₗ_apply_apply]
    rw [show @inner ℝ ℝ _ y ξ = y * ξ from by
      rw [real_inner_eq_re_inner ℝ, RCLike.inner_apply, conj_trivial, RCLike.re_to_real, mul_comm]]
    rw [show (↑(-2 * π * (y * ξ)) : ℂ) * Complex.I = c * ↑y from by
      simp only [c]; push_cast; ring]
    rw [mul_pow, Complex.ofReal_pow]
  -- Integrability helper: each monomial-Gaussian product is integrable
  have h_mono_int : ∀ k : ℕ,
      Integrable (fun y => f y * (y ^ k * Real.exp (-(y ^ 2 / 2)))) volume := by
    intro k
    obtain ⟨φ, hφ⟩ := poly_mul_gaussian_schwartz (Polynomial.X ^ k)
    have hpG : MemLp (fun y => y ^ k * Real.exp (-(y ^ 2 / 2))) 2 volume :=
      MemLp.ae_eq (Filter.Eventually.of_forall (fun y => by simp [hφ y, Polynomial.eval_pow,
        Polynomial.eval_X])) (φ.memLp 2 volume)
    exact (L2.integrable_inner (𝕜 := ℝ) (MemLp.toLp f hf) (MemLp.toLp _ hpG)).congr
      (by filter_upwards [MemLp.coeFn_toLp hf, MemLp.coeFn_toLp hpG] with x hfx hGx
          rw [hfx, hGx, real_inner_eq_re_inner ℝ, RCLike.inner_apply', conj_trivial,
            RCLike.re_to_real])
  -- Helper: rewrite summand as constant times moment integrand
  have h_summand_eq : ∀ k : ℕ, (fun y => z y ^ k / (Nat.factorial k : ℂ) * g_ℂ y) =
      fun y => (c ^ k / (Nat.factorial k : ℂ)) *
        (↑(f y * (y ^ k * Real.exp (-(y ^ 2 / 2)))) : ℂ) := by
    intro k; ext y; rw [h_zk]; simp only [g_ℂ, g]; push_cast; ring
  -- Each summand is integrable
  have h_summand_int : ∀ k : ℕ,
      Integrable (fun y => z y ^ k / (Nat.factorial k : ℂ) * g_ℂ y) volume := by
    intro k; rw [h_summand_eq]; exact (h_mono_int k).ofReal.const_mul _
  -- Step 7: Each ∫ F N = 0
  have h_zero : ∀ N, ∫ x, F N x = 0 := by
    intro N
    show ∫ x, expPartialSum (z x) N * g_ℂ x = 0
    simp only [expPartialSum, Finset.sum_mul]
    rw [integral_finsetSum _ (fun k _ => h_summand_int k)]
    apply Finset.sum_eq_zero
    intro k _
    rw [h_summand_eq]
    calc ∫ y, c ^ k / ↑k.factorial * (↑(f y * (y ^ k * Real.exp (-(y ^ 2 / 2)))) : ℂ)
        = c ^ k / ↑k.factorial * ∫ y, (↑(f y * (y ^ k * Real.exp (-(y ^ 2 / 2)))) : ℂ) :=
          integral_const_mul _ _
      _ = c ^ k / ↑k.factorial * ↑(∫ y, f y * (y ^ k * Real.exp (-(y ^ 2 / 2)))) :=
          congr_arg _ integral_ofReal
      _ = 0 := by rw [h_moments k, Complex.ofReal_zero, mul_zero]
  -- Step 8: Limit of zeros is zero
  exact tendsto_nhds_unique h_DCT
    (tendsto_const_nhds.congr (fun N => (h_zero N).symm))

set_option maxHeartbeats 800000 in
/-- If ĝ = 0 and φ is smooth compactly supported, then ∫ φ · g = 0.
    Uses Parseval identity and Fourier inversion for Schwartz functions. -/
private lemma integral_smul_f_mul_gaussian_eq_zero
    (f : ℝ → ℝ) (hf : MemLp f 2 volume)
    (h_moments : ∀ k : ℕ, ∫ x, f x * (x ^ k * Real.exp (-(x ^ 2 / 2))) = 0)
    (φ : ℝ → ℝ) (hφ : ContDiff ℝ ∞ φ) (hφc : HasCompactSupport φ) :
    ∫ x, φ x • (f x * Real.exp (-(x ^ 2 / 2))) = 0 := by
  -- Abbreviations: g(x) = f(x) * exp(-x²/2), g_ℂ = ↑g
  set g : ℝ → ℝ := fun x => f x * Real.exp (-(x ^ 2 / 2)) with hg_def
  set g_ℂ : ℝ → ℂ := fun x => (↑(g x) : ℂ) with hg_ℂ_def
  -- Step 1: Fourier transform of g_ℂ vanishes
  have hFT := fourierIntegral_f_mul_gaussian_eq_zero f hf h_moments
  -- Step 2: g and g_ℂ are integrable
  have hg_int : Integrable g volume := integrable_f_mul_gaussian f hf
  have hg_ℂ_int : Integrable g_ℂ volume := hg_int.ofReal
  -- Step 3: Lift φ to ℂ-valued Schwartz map
  have hφc_ℂ : HasCompactSupport (Complex.ofRealCLM ∘ φ) := hφc.comp_left rfl
  have hφd_ℂ : ContDiff ℝ ∞ (Complex.ofRealCLM ∘ φ) := by fun_prop
  set ψ : SchwartzMap ℝ ℂ := hφc_ℂ.toSchwartzMap hφd_ℂ
  -- Step 4: η = 𝓕⁻¹ ψ (Schwartz), integrable
  set η : SchwartzMap ℝ ℂ := FourierTransform.fourierInv ψ
  have hη_int : Integrable (η : ℝ → ℂ) volume := η.integrable
  -- Step 5: Fourier inversion: 𝓕(𝓕⁻¹ ψ) = ψ
  have hFF : FourierTransform.fourier η = ψ := FourierTransform.fourier_fourierInv_eq ψ
  -- Step 6: The FT of g_ℂ (as used in Parseval) agrees with hFT
  have hFT_g : ∀ ξ, VectorFourier.fourierIntegral Real.fourierChar volume (innerₗ ℝ)
      g_ℂ ξ = 0 := fun ξ => hFT ξ
  -- Step 7: Apply Parseval / self-adjoint identity for L^1 Fourier transform
  have hParseval := VectorFourier.integral_fourierIntegral_smul_eq_flip
    (L := innerₗ ℝ) (e := Real.fourierChar) (μ := volume) (ν := volume)
    Real.continuous_fourierChar continuous_inner hη_int hg_ℂ_int
  -- Step 8: L.flip = L for innerₗ ℝ, and RHS has 𝓕(g_ℂ) = 0
  simp only [flip_innerₗ, hFT_g, smul_zero, integral_zero] at hParseval
  -- hParseval : ∫ (𝓕 η)(ξ) • g_ℂ(ξ) = 0
  -- Step 9: 𝓕(η) = ψ = ofRealCLM ∘ φ as functions
  have h_FT_eta : ∀ ξ, VectorFourier.fourierIntegral Real.fourierChar volume (innerₗ ℝ)
      (η : ℝ → ℂ) ξ = (↑(φ ξ) : ℂ) :=
    fun ξ => congr_fun (congr_arg DFunLike.coe hFF) ξ
  -- Step 10: Rewrite Parseval to get ∫ ↑(φ ξ) • g_ℂ(ξ) = 0
  simp only [h_FT_eta] at hParseval
  -- Step 11: Extract real integral from ℂ integral
  suffices h : (↑(∫ x, φ x • g x) : ℂ) = 0 by exact_mod_cast h
  calc (↑(∫ x, φ x • g x) : ℂ)
      = ∫ x, ((↑(φ x • g x)) : ℂ) := integral_ofReal.symm
    _ = ∫ x, (↑(φ x) : ℂ) • g_ℂ x := by
        congr 1; funext x; simp [hg_ℂ_def, smul_eq_mul, Complex.ofReal_mul]
    _ = 0 := hParseval

private theorem hermiteFunction_complete_proof :
    ∀ f : ℝ → ℝ, MemLp f 2 volume →
    (∀ n, ∫ x, f x * hermiteFunction n x = 0) →
    f =ᵐ[volume] 0 := by
  intro f hf horth
  have h_moments := integral_f_xpow_gaussian_zero f hf horth
  suffices hg : (fun x => f x * Real.exp (-(x ^ 2 / 2))) =ᵐ[volume] 0 by
    filter_upwards [hg] with x hx
    have hexp_pos : Real.exp (-(x ^ 2 / 2)) > 0 := Real.exp_pos _
    exact (mul_eq_zero.mp hx).resolve_right (ne_of_gt hexp_pos)
  have hg_loc : LocallyIntegrable (fun x => f x * Real.exp (-(x ^ 2 / 2))) volume :=
    (integrable_f_mul_gaussian f hf).locallyIntegrable
  exact ae_eq_zero_of_integral_contDiff_smul_eq_zero hg_loc
    (integral_smul_f_mul_gaussian_eq_zero f hf h_moments)

/-- Hermite functions form a complete system in L²(ℝ):
    if ∫ f ψₙ = 0 for all n, then f = 0 a.e. -/
theorem hermiteFunction_complete :
    ∀ f : ℝ → ℝ, MemLp f 2 volume →
    (∀ n, ∫ x, f x * hermiteFunction n x = 0) →
    f =ᵐ[volume] 0 :=
  hermiteFunction_complete_proof

/-! ## Multi-dimensional Hermite Functions

For ℝᵈ, Hermite functions are tensor products:
  Ψ_α(x) = ∏ᵢ ψ_{αᵢ}(xᵢ)  for multi-index α = (α₁, ..., αd)

These form an ONB of L²(ℝᵈ) and lie in 𝓢(ℝᵈ, ℝ). -/

-- Multi-index Hermite function for ℝᵈ
-- (Definition deferred until the 1D properties are proved)

/-! ## Schwartz Hermite Expansion (General D, F)

The 5 Hermite infrastructure definitions and theorems (basis, coefficients, expansion,
seminorm growth, coefficient decay) are now proved in `HermiteTensorProduct.lean` via
multi-dimensional tensor products of 1D Hermite functions.

The previous axiom declarations have been removed. Import `HermiteTensorProduct` to
access `schwartzHermiteBasis`, `schwartzHermiteCoeff`, `schwartz_hermite_expansion`,
`schwartz_hermite_seminorm_growth`, and `schwartz_hermite_coefficient_decay`.
-/

end
