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

# Positive-Time Test Functions on the Cylinder

Defines the submodule of cylinder test functions supported at positive time,
using the 1D Schwartz positive-time submodule from `Cylinder.Symmetry`.

## Main definitions

- `cylinderPositiveTimeSubmodule` — closure of span of pure tensors g ⊗ h
  with h ∈ schwartzPositiveTimeSubmodule
- `cylinderNegativeTimeSubmodule` — closure of span of pure tensors g ⊗ h
  with h ∈ schwartzNegativeTimeSubmodule
- `CylinderPositiveTimeTestFunction` — elements of the positive-time submodule

## Main results

- `cylinderTimeReflection_pos_to_neg` — Θ maps P+ into N− (proved)
- `cylinderPositiveTime_disjoint_reflected` — Θf ∉ P+ for nonzero f ∈ P+ (proved)
- `cylinderPositiveTime_spatialTranslation_closed` — spatial translation preserves P+ (proved)

## Mathematical background

On the cylinder S¹_L × ℝ, "positive time" means the temporal coordinate
t ∈ (0, ∞) ⊂ ℝ. This is the natural half-space for the Osterwalder-Schrader
axioms:

- Time reflection Θ maps t ↦ -t, so support in (0,∞) maps to (-∞,0)
- Positive-time and negative-time supports are disjoint (no wraparound)
- The block-zero property of the mass operator holds automatically:
  if f has support in (0,∞) and Θg has support in (-∞,0), then
  the coupling ⟨f, Q(Θg)⟩ vanishes by locality

This is much cleaner than the torus case, where positive time
means t ∈ (0, L/2) ⊂ ℝ/Lℤ and wraparound must be handled.

## References

- Osterwalder-Schrader (1973), Axiom (A3)
- Glimm-Jaffe, *Quantum Physics*, §6.1
-/

import Cylinder.Symmetry

noncomputable section

namespace GaussianField

open NuclearTensorProduct

variable (L : ℝ) [hL : Fact (0 < L)]

/-! ## Positive-time submodule -/

/-- The set of positive-time pure tensors: `g ⊗ h` where h vanishes on (-∞, 0]. -/
private def positiveTimePureTensors :
    Set (CylinderTestFunction L) :=
  {f | ∃ (g : SmoothMap_Circle L ℝ) (h : SchwartzMap ℝ ℝ),
    h ∈ schwartzPositiveTimeSubmodule ∧ f = pure g h}

/-- Submodule of cylinder test functions supported at positive time t > 0.

Defined as the topological closure of the span of pure tensors `g ⊗ h`
where `h ∈ schwartzPositiveTimeSubmodule` (i.e., h vanishes on (-∞, 0]).

Mathematically, this is the closure (in the nuclear Fréchet topology) of
finite sums `∑ gᵢ ⊗ hᵢ` where each `hᵢ ∈ 𝓢(ℝ)` has support in (0, ∞).

Key property: if f is positive-time supported, then `cylinderTimeReflection L f`
is negative-time supported (support in (-∞, 0)), and these are disjoint.
This separation is what makes OS3 (reflection positivity) work. -/
def cylinderPositiveTimeSubmodule :
    Submodule ℝ (CylinderTestFunction L) :=
  (Submodule.span ℝ (positiveTimePureTensors L)).topologicalClosure

/-- Positive-time test functions on the cylinder.

These are the test functions with temporal support in the open half-line (0, ∞).
For pure tensors g ⊗ h, this means supp(h) ⊂ (0, ∞). -/
abbrev CylinderPositiveTimeTestFunction :=
    cylinderPositiveTimeSubmodule L

/-! ## Negative-time submodule -/

/-- The set of negative-time pure tensors: `g ⊗ h` where h vanishes on [0, ∞). -/
private def negativeTimePureTensors :
    Set (CylinderTestFunction L) :=
  {f | ∃ (g : SmoothMap_Circle L ℝ) (h : SchwartzMap ℝ ℝ),
    h ∈ schwartzNegativeTimeSubmodule ∧ f = pure g h}

/-- Submodule of cylinder test functions supported at negative time t < 0.

Defined as the topological closure of the span of pure tensors `g ⊗ h`
where `h ∈ schwartzNegativeTimeSubmodule` (i.e., h vanishes on [0, ∞)). -/
def cylinderNegativeTimeSubmodule :
    Submodule ℝ (CylinderTestFunction L) :=
  (Submodule.span ℝ (negativeTimePureTensors L)).topologicalClosure

/-! ## Time reflection maps P+ to N+ -/

/-- Time reflection maps positive-time pure tensors to negative-time pure tensors:
`Θ(g ⊗ h) = g ⊗ Θh`, and if h vanishes on (-∞, 0] then Θh vanishes on [0, ∞). -/
private theorem timeReflection_maps_pos_to_neg
    {f : CylinderTestFunction L} (hf : f ∈ positiveTimePureTensors L) :
    cylinderTimeReflection L f ∈ negativeTimePureTensors L := by
  obtain ⟨g, h, hh, rfl⟩ := hf
  refine ⟨g, schwartzReflection h, ?_, ?_⟩
  · exact schwartzReflection_positive_to_negative hh
  · show nuclearTensorProduct_mapCLM (ContinuousLinearMap.id ℝ _)
      schwartzReflection (pure g h) = pure g (schwartzReflection h)
    rw [nuclearTensorProduct_mapCLM_pure]
    simp

/-- Time reflection maps the positive-time submodule into the negative-time submodule.

Proof: Θ maps positive-time generators `g ⊗ h` to negative-time generators
`g ⊗ Θh`, hence the span, hence (by continuity) the closure. -/
theorem cylinderTimeReflection_pos_to_neg
    (f : CylinderPositiveTimeTestFunction L) :
    cylinderTimeReflection L f.val ∈ cylinderNegativeTimeSubmodule L := by
  set S := Submodule.span ℝ (positiveTimePureTensors L)
  set Θ := cylinderTimeReflection L
  set N := cylinderNegativeTimeSubmodule L
  suffices h : S.topologicalClosure ≤ N.comap Θ.toLinearMap from
    (h f.property : Θ f.val ∈ N)
  apply Submodule.topologicalClosure_minimal
  · intro x hx
    show Θ x ∈ N
    suffices Θ x ∈ Submodule.span ℝ (negativeTimePureTensors L) from subset_closure this
    induction hx using Submodule.span_induction with
    | mem x hx => exact Submodule.subset_span (timeReflection_maps_pos_to_neg L hx)
    | zero => simp
    | add x y _ _ hTx hTy => rw [map_add]; exact Submodule.add_mem _ hTx hTy
    | smul r x _ hTx => rw [map_smul]; exact Submodule.smul_mem _ r hTx
  · exact (Submodule.isClosed_topologicalClosure _).preimage Θ.continuous

/-! ## Slice extraction CLMs

For fixed `a : ℕ`, we extract the "a-th slice" of a cylinder test function:
the subsequence `b ↦ f.val(Nat.pair a b)`. This maps `RapidDecaySeq → RapidDecaySeq`
and, composed with `schwartzRapidDecayEquiv1D.symm`, gives a CLM to `SchwartzMap ℝ ℝ`.

The key property is that slicing a pure tensor `pure g h` at index `a` yields
`coeff a g • h`, where `coeff a g` is the a-th DM coefficient of `g`. -/

/-- Extract the a-th slice: `b ↦ f.val(Nat.pair a b)`.
This is rapidly decaying because `b ≤ Nat.pair a b`. -/
theorem pair_weight_le (a b : ℕ) (k : ℕ) :
    (1 + (b : ℝ)) ^ k ≤ (1 + (Nat.pair a b : ℝ)) ^ k := by
  apply pow_le_pow_left₀ (by positivity)
  have h : b ≤ Nat.pair a b := Nat.right_le_pair a b
  have : (b : ℝ) ≤ (Nat.pair a b : ℝ) := Nat.cast_le (α := ℝ).mpr h
  linarith

theorem pair_right_injective (a : ℕ) :
    Function.Injective (Nat.pair a) := by
  intro b₁ b₂ h
  exact (Nat.pair_eq_pair.mp h).2

def ntpExtractSliceFun (a : ℕ) (f : RapidDecaySeq) : RapidDecaySeq where
  val b := f.val (Nat.pair a b)
  rapid_decay k := by
    set g : ℕ → ℝ := fun m => |f.val m| * (1 + (m : ℝ)) ^ k
    apply Summable.of_nonneg_of_le
    · intro b; exact mul_nonneg (abs_nonneg _) (RapidDecaySeq.weight_nonneg b k)
    · intro b
      show |f.val (Nat.pair a b)| * (1 + (b : ℝ)) ^ k ≤ g (Nat.pair a b)
      exact mul_le_mul_of_nonneg_left (pair_weight_le a b k) (abs_nonneg _)
    · exact (f.rapid_decay k).comp_injective (pair_right_injective a)

def ntpExtractSliceLM (a : ℕ) : RapidDecaySeq →ₗ[ℝ] RapidDecaySeq where
  toFun := ntpExtractSliceFun a
  map_add' f g := RapidDecaySeq.ext fun b => by
    show (f + g).val (Nat.pair a b) = (ntpExtractSliceFun a f + ntpExtractSliceFun a g).val b
    simp [ntpExtractSliceFun, RapidDecaySeq.add_val]
  map_smul' r f := RapidDecaySeq.ext fun b => by
    show (r • f).val (Nat.pair a b) = (r • ntpExtractSliceFun a f).val b
    simp [ntpExtractSliceFun, RapidDecaySeq.smul_val]

theorem ntpExtractSliceLM_isBounded (a : ℕ) :
    Seminorm.IsBounded RapidDecaySeq.rapidDecaySeminorm
      RapidDecaySeq.rapidDecaySeminorm (ntpExtractSliceLM a) := by
  intro k
  refine ⟨{k}, 1, fun f => ?_⟩
  simp only [Seminorm.comp_apply, Finset.sup_singleton, one_smul]
  show ∑' b, |f.val (Nat.pair a b)| * (1 + (b : ℝ)) ^ k ≤
    ∑' m, |f.val m| * (1 + (m : ℝ)) ^ k
  set h₁ : ℕ → ℝ := fun b => |f.val (Nat.pair a b)| * (1 + (b : ℝ)) ^ k
  set h₂ : ℕ → ℝ := fun b => |f.val (Nat.pair a b)| * (1 + (Nat.pair a b : ℝ)) ^ k
  have h₁_sum : Summable h₁ := (ntpExtractSliceFun a f).rapid_decay k
  have h₂_sum : Summable h₂ := (f.rapid_decay k).comp_injective (pair_right_injective a)
  calc ∑' b, h₁ b
      ≤ ∑' b, h₂ b :=
        h₁_sum.tsum_le_tsum
          (fun b => mul_le_mul_of_nonneg_left (pair_weight_le a b k) (abs_nonneg _)) h₂_sum
    _ ≤ ∑' m, |f.val m| * (1 + (m : ℝ)) ^ k :=
        tsum_comp_le_tsum_of_inj (f.rapid_decay k)
          (fun m => mul_nonneg (abs_nonneg _) (RapidDecaySeq.weight_nonneg m k))
          (pair_right_injective a)

def ntpExtractSlice (a : ℕ) : RapidDecaySeq →L[ℝ] RapidDecaySeq where
  toLinearMap := ntpExtractSliceLM a
  cont := WithSeminorms.continuous_of_isBounded
    RapidDecaySeq.rapidDecay_withSeminorms
    RapidDecaySeq.rapidDecay_withSeminorms
    (ntpExtractSliceLM a) (ntpExtractSliceLM_isBounded a)

@[simp]
theorem ntpExtractSlice_val (a : ℕ) (f : RapidDecaySeq) (b : ℕ) :
    (ntpExtractSlice a f).val b = f.val (Nat.pair a b) := rfl

/-- Compose extraction with `schwartzRapidDecayEquiv1D.symm` to get a CLM
from `CylinderTestFunction L` to `SchwartzMap ℝ ℝ`. -/
def ntpSliceSchwartz (a : ℕ) :
    CylinderTestFunction L →L[ℝ] SchwartzMap ℝ ℝ :=
  schwartzRapidDecayEquiv1D.symm.toContinuousLinearMap.comp (ntpExtractSlice a)

/-! ## Slice of pure tensors -/

/-- Slicing a pure tensor at index `a` yields `coeff_a(g) • h`.

For `pure g h`:
- `(pure g h).val (Nat.pair a b) = coeff a g * coeff b h`
  (by `NuclearTensorProduct.pure_val` and `Nat.unpair_pair`)
- The extracted slice is `b ↦ coeff_a(g) * coeff_b(h) = coeff_a(g) • (equiv h).val b`
- Applying `equiv.symm` gives `coeff_a(g) • h` -/
theorem ntpSliceSchwartz_pure (a : ℕ)
    (g : SmoothMap_Circle L ℝ) (h : SchwartzMap ℝ ℝ) :
    ntpSliceSchwartz L a (NuclearTensorProduct.pure g h) =
      DyninMityaginSpace.coeff a g • h := by
  -- Step 1: Show the extracted slice equals coeff_a(g) • equiv(h) as RapidDecaySeq
  have h_extract : ntpExtractSlice a (NuclearTensorProduct.pure g h) =
      DyninMityaginSpace.coeff a g • schwartzRapidDecayEquiv1D h := by
    apply RapidDecaySeq.ext; intro b
    change (NuclearTensorProduct.pure g h).val (Nat.pair a b) = _
    rw [NuclearTensorProduct.pure_val, Nat.unpair_pair,
      RapidDecaySeq.smul_val]
    -- coeff b h = (schwartzRapidDecayEquiv1D h).val b by definition of ofRapidDecayEquiv
    rfl
  -- Step 2: Apply equiv.symm
  show schwartzRapidDecayEquiv1D.symm (ntpExtractSlice a (NuclearTensorProduct.pure g h)) =
    DyninMityaginSpace.coeff a g • h
  rw [h_extract, map_smul, ContinuousLinearEquiv.symm_apply_apply]

/-! ## Slices preserve time submodules -/

/-- The slice CLM maps `cylinderPositiveTimeSubmodule` into
`schwartzPositiveTimeSubmodule`. -/
theorem ntpSliceSchwartz_maps_positive (a : ℕ)
    (f : CylinderTestFunction L)
    (hf : f ∈ cylinderPositiveTimeSubmodule L) :
    ntpSliceSchwartz L a f ∈ schwartzPositiveTimeSubmodule := by
  -- Strategy: show preimage of schwartzPositiveTimeSubmodule under the CLM is closed,
  -- contains the generators, hence contains the topological closure.
  set S := Submodule.span ℝ (positiveTimePureTensors L)
  set Φ := ntpSliceSchwartz L a
  set P := schwartzPositiveTimeSubmodule
  -- It suffices to show S.topologicalClosure ≤ P.comap Φ.toLinearMap
  suffices h : S.topologicalClosure ≤ P.comap Φ.toLinearMap from h hf
  apply Submodule.topologicalClosure_minimal
  · -- S ≤ P.comap Φ: the CLM maps generators into P
    intro x hx
    show Φ x ∈ P
    -- Induction on the span
    induction hx using Submodule.span_induction with
    | mem x hx =>
      obtain ⟨g, h, hh, rfl⟩ := hx
      rw [ntpSliceSchwartz_pure]
      exact Submodule.smul_mem P _ hh
    | zero => simp [map_zero, Submodule.zero_mem P]
    | add x y _ _ hΦx hΦy => rw [map_add]; exact Submodule.add_mem P hΦx hΦy
    | smul r x _ hΦx => rw [map_smul]; exact Submodule.smul_mem P r hΦx
  · -- P.comap Φ is closed (continuous preimage of closed set)
    exact schwartzPositiveTimeSubmodule_isClosed.preimage Φ.continuous

/-- The slice CLM maps `cylinderNegativeTimeSubmodule` into
`schwartzNegativeTimeSubmodule`. -/
private theorem ntpSliceSchwartz_maps_negative (a : ℕ)
    (f : CylinderTestFunction L)
    (hf : f ∈ cylinderNegativeTimeSubmodule L) :
    ntpSliceSchwartz L a f ∈ schwartzNegativeTimeSubmodule := by
  set S := Submodule.span ℝ (negativeTimePureTensors L)
  set Φ := ntpSliceSchwartz L a
  set N := schwartzNegativeTimeSubmodule
  suffices h : S.topologicalClosure ≤ N.comap Φ.toLinearMap from h hf
  apply Submodule.topologicalClosure_minimal
  · intro x hx
    show Φ x ∈ N
    induction hx using Submodule.span_induction with
    | mem x hx =>
      obtain ⟨g, h, hh, rfl⟩ := hx
      rw [ntpSliceSchwartz_pure]
      exact Submodule.smul_mem N _ hh
    | zero => simp [map_zero, Submodule.zero_mem N]
    | add x y _ _ hΦx hΦy => rw [map_add]; exact Submodule.add_mem N hΦx hΦy
    | smul r x _ hΦx => rw [map_smul]; exact Submodule.smul_mem N r hΦx
  · exact schwartzNegativeTimeSubmodule_isClosed.preimage Φ.continuous

/-! ## 1D disjointness of positive and negative time Schwartz submodules -/

/-- The positive-time and negative-time Schwartz submodules are disjoint:
if h vanishes on (-∞,0] AND on [0,∞) then h = 0. -/
private theorem schwartz_posNeg_disjoint :
    schwartzPositiveTimeSubmodule ⊓ schwartzNegativeTimeSubmodule = ⊥ := by
  rw [Submodule.eq_bot_iff]
  intro h ⟨hpos, hneg⟩
  ext x
  simp only [zero_apply]
  by_cases hx : x ≤ 0
  · exact hpos x hx
  · push Not at hx; exact hneg x (le_of_lt hx)

/-! ## Disjointness of positive-time and negative-time submodules -/

/-- The positive-time and negative-time submodules are disjoint.

This is the fundamental temporal separation property: a nonzero element
cannot simultaneously be in the closure of positive-time and negative-time
pure tensors. It follows from the NTP coefficient structure (Cantor-paired
DM expansion) and the 1D result `schwartzPositiveTime_disjoint_reflected`
that positive-time and negative-time Schwartz functions have trivial
intersection. -/
theorem cylinderPositiveTime_negativeTime_disjoint :
    cylinderPositiveTimeSubmodule L ⊓ cylinderNegativeTimeSubmodule L = ⊥ := by
  rw [Submodule.eq_bot_iff]
  intro f ⟨hf_pos, hf_neg⟩
  -- Each slice of f is in both positive and negative time
  have h_slice_zero : ∀ a, ntpSliceSchwartz L a f = 0 := by
    intro a
    have h_pos := ntpSliceSchwartz_maps_positive L a f hf_pos
    have h_neg := ntpSliceSchwartz_maps_negative L a f hf_neg
    have h_mem : ntpSliceSchwartz L a f ∈
        schwartzPositiveTimeSubmodule ⊓ schwartzNegativeTimeSubmodule :=
      ⟨h_pos, h_neg⟩
    rw [schwartz_posNeg_disjoint] at h_mem
    exact (Submodule.mem_bot ℝ).mp h_mem
  -- All slices zero means all coefficients at paired indices are zero
  have h_val_pair : ∀ a b, f.val (Nat.pair a b) = 0 := by
    intro a b
    have h := h_slice_zero a
    -- ntpSliceSchwartz L a f = equiv.symm (ntpExtractSlice a f) = 0
    -- So ntpExtractSlice a f = equiv 0 = 0
    -- So (ntpExtractSlice a f).val b = 0
    -- i.e. f.val (Nat.pair a b) = 0
    have h2 : ntpExtractSlice a f = 0 := by
      have h1 : schwartzRapidDecayEquiv1D.symm (ntpExtractSlice a f) = 0 := h
      rw [← map_zero schwartzRapidDecayEquiv1D.symm] at h1
      exact schwartzRapidDecayEquiv1D.symm.injective h1
    have h3 := congrArg (fun x : RapidDecaySeq => x.val b) h2
    change f.val (Nat.pair a b) = 0 at h3
    exact h3
  -- Since Nat.pair is surjective, f.val m = 0 for all m
  apply RapidDecaySeq.ext
  intro m
  show f.val m = (0 : RapidDecaySeq).val m
  rw [RapidDecaySeq.zero_val]
  have := h_val_pair (Nat.unpair m).1 (Nat.unpair m).2
  rwa [Nat.pair_unpair] at this

/-- Time reflection maps the positive-time submodule to a disjoint submodule.

If f has temporal support in (0, ∞), then Θf has temporal support in (-∞, 0).
In particular, f and Θf have disjoint temporal supports.

Proof: if Θf were also in P+, then Θf ∈ P+ ∩ N+ = {0} (since we proved
Θf ∈ N+ from f ∈ P+). So Θf = 0, hence f = Θ(Θf) = 0.

Note: this requires f ≠ 0 since Θ0 = 0 is in every submodule. -/
theorem cylinderPositiveTime_disjoint_reflected
    (f : CylinderPositiveTimeTestFunction L) (hf : f.val ≠ 0) :
    cylinderTimeReflection L f.val ∉ cylinderPositiveTimeSubmodule L := by
  intro hΘf_pos
  apply hf
  -- Θf ∈ N+ (from P+ → N+ theorem)
  have hΘf_neg := cylinderTimeReflection_pos_to_neg L f
  -- Θf ∈ P+ ∩ N+ = {0}, so Θf = 0
  have hΘf_zero : cylinderTimeReflection L f.val = 0 := by
    have hmem : cylinderTimeReflection L f.val ∈
        cylinderPositiveTimeSubmodule L ⊓ cylinderNegativeTimeSubmodule L :=
      ⟨hΘf_pos, hΘf_neg⟩
    rw [cylinderPositiveTime_negativeTime_disjoint L] at hmem
    exact (Submodule.mem_bot ℝ).mp hmem
  -- f = Θ(Θf) = Θ(0) = 0
  have hinv := ContinuousLinearMap.ext_iff.mp (cylinderTimeReflection_involution L) f.val
  simp [ContinuousLinearMap.comp_apply] at hinv
  rw [← hinv, hΘf_zero, map_zero]

/-! ## Positive-time support under translation

Time translation by τ > 0 maps positive-time functions to positive-time
functions (shifts support further into the future). Time translation by
τ < 0 can move support to include t ≤ 0, breaking the positive-time property.

Spatial translation preserves the positive-time property since it acts
only on the S¹ factor and leaves the temporal support unchanged. -/

/-- Spatial translation maps positive-time pure tensors to positive-time
pure tensors: `T_v(g ⊗ h) = (T_v g) ⊗ h`, preserving the temporal factor. -/
private theorem spatialTranslation_maps_generators (v : ℝ)
    {f : CylinderTestFunction L} (hf : f ∈ positiveTimePureTensors L) :
    cylinderSpatialTranslation L v f ∈ positiveTimePureTensors L := by
  obtain ⟨g, h, hh, rfl⟩ := hf
  refine ⟨circleTranslation L v g, h, hh, ?_⟩
  show nuclearTensorProduct_mapCLM (circleTranslation L v)
    (ContinuousLinearMap.id ℝ _) (pure g h) = pure (circleTranslation L v g) h
  rw [nuclearTensorProduct_mapCLM_pure]
  simp

/-- Spatial translation preserves positive-time support.

Proof: spatial translation `T_v ⊗ id` maps pure tensors `g ⊗ h ↦ (T_v g) ⊗ h`,
preserving the temporal factor h. So it maps the generating set into itself,
hence the span, hence (by continuity) the closure. -/
theorem cylinderPositiveTime_spatialTranslation_closed (v : ℝ)
    (f : CylinderPositiveTimeTestFunction L) :
    cylinderSpatialTranslation L v f.val ∈ cylinderPositiveTimeSubmodule L := by
  -- Strategy: show the CLM maps the closure into itself via topologicalClosure_minimal
  set S := Submodule.span ℝ (positiveTimePureTensors L)
  set T := cylinderSpatialTranslation L v
  -- The comap of the closure under T is a closed submodule containing S
  set M := cylinderPositiveTimeSubmodule L  -- = S.topologicalClosure
  suffices h : S.topologicalClosure ≤ M.comap T.toLinearMap from
    (h f.property : T f.val ∈ M)
  apply Submodule.topologicalClosure_minimal
  · -- S ≤ M.comap T, i.e., T maps span of generators into M
    intro x hx
    show T x ∈ M
    -- It suffices to show T maps span of generators into span of generators
    suffices T x ∈ S from subset_closure this
    -- Induction on the span: T maps generators to generators, rest by linearity
    induction hx using Submodule.span_induction with
    | mem x hx => exact Submodule.subset_span (spatialTranslation_maps_generators L v hx)
    | zero => simp
    | add x y _ _ hTx hTy => rw [map_add]; exact Submodule.add_mem S hTx hTy
    | smul r x _ hTx => rw [map_smul]; exact Submodule.smul_mem S r hTx
  · -- M.comap T is closed (continuous preimage of closed set)
    exact (Submodule.isClosed_topologicalClosure S).preimage T.continuous

end GaussianField
