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

# Circle Restriction: Sampling Smooth Periodic Functions on Lattice Points

Defines `circleRestriction L N`, a continuous linear map that samples a smooth
periodic function at N equally spaced lattice points with the correct
normalization for Riemann sum convergence.

## Main definitions

- `circleRestriction L N` — CLM from `SmoothMap_Circle L ℝ` to `ZMod N → ℝ`

## Mathematical background

The circle restriction map is:

  `(r_N f)(k) = √(L/N) · f(kL/N)`    for k = 0, ..., N-1

The `√(L/N)` normalization ensures that the ℓ² norm of r_N f approximates
the L² norm of f: `Σ_k |r_N f(k)|² ≈ ∫₀ᴸ |f(x)|² dx` as N → ∞.

This is the 1D torus analogue of the lattice embedding from the continuum
limit construction, specialized to periodic functions on [0,L].

## References

- Glimm-Jaffe, *Quantum Physics*, §6.1 (Lattice approximation)
-/

import SmoothCircle.Basic
import Mathlib.Data.ZMod.Basic

noncomputable section

namespace GaussianField

open SmoothMap_Circle

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

/-! ## Circle restriction map -/

/-- Lattice spacing on the circle: `a = L/N`. -/
def circleSpacing (N : ℕ) [NeZero N] : ℝ := L / N

theorem circleSpacing_pos (N : ℕ) [NeZero N] : 0 < circleSpacing L N :=
  div_pos hL.out (Nat.cast_pos.mpr (NeZero.pos N))

/-- Position of the k-th lattice point on the circle: `kL/N`.

Uses `ZMod.val` to convert `ZMod N` to `ℕ`, matching the `FinLatticeSites`
convention where lattice sites are indexed by `ZMod N`. -/
def circlePoint (N : ℕ) [NeZero N] (k : ZMod N) : ℝ :=
  (ZMod.val k : ℝ) * L / N

/-- Evaluation at a single point is continuous on `SmoothMap_Circle L ℝ`.

  `ev_x(f) = f(x)` satisfies `|f(x)| ≤ p_0(f)` (the C⁰ seminorm). -/
theorem continuous_eval_at (x : ℝ) :
    Continuous (fun f : SmoothMap_Circle L ℝ => f x) := by
  -- Enough to show the linear functional f ↦ f(x) is bounded by a seminorm
  set lm : SmoothMap_Circle L ℝ →ₗ[ℝ] ℝ :=
    { toFun := fun f => f x
      map_add' := fun f g => add_apply f g x
      map_smul' := fun r f => smul_apply r f x }
  change Continuous lm
  apply WithSeminorms.continuous_of_isBounded smoothCircle_withSeminorms (norm_withSeminorms ℝ ℝ)
  intro _
  refine ⟨{0}, ⟨1, by norm_num⟩, fun f => ?_⟩
  simp only [Seminorm.comp_apply, Finset.sup_singleton]
  -- Goal: (normSeminorm ℝ ℝ) (lm f) ≤ 1 • sobolevSeminorm 0 f
  show (normSeminorm ℝ ℝ) (lm f) ≤ (1 : ℝ) • (sobolevSeminorm 0) f
  rw [one_smul, coe_normSeminorm, Real.norm_eq_abs]
  calc |f x| = ‖iteratedDeriv 0 (⇑f) x‖ := by
        rw [iteratedDeriv_zero]; exact (Real.norm_eq_abs _).symm
    _ ≤ sobolevSeminorm 0 f := norm_iteratedDeriv_le_sobolevSeminorm' f 0 x


/-- The circle restriction as a linear map (before proving continuity). -/
def circleRestrictionLM (N : ℕ) [NeZero N] :
    SmoothMap_Circle L ℝ →ₗ[ℝ] (ZMod N → ℝ) where
  toFun f k := Real.sqrt (circleSpacing L N) * f (circlePoint L N k)
  map_add' f g := by ext k; simp [SmoothMap_Circle.add_apply, mul_add]
  map_smul' r f := by ext k; simp [SmoothMap_Circle.smul_apply, mul_left_comm]

/-- The circle restriction map: sample a smooth periodic function at N lattice points.

  `(r_N f)(k) = √(L/N) · f(kL/N)`

The √(L/N) normalization makes the ℓ² inner product ⟨r_N f, r_N g⟩ approximate
the L²([0,L]) inner product ∫₀ᴸ f(x)g(x)dx as N → ∞. -/
def circleRestriction (N : ℕ) [NeZero N] :
    SmoothMap_Circle L ℝ →L[ℝ] (ZMod N → ℝ) where
  toLinearMap := circleRestrictionLM L N
  cont := by
    -- The map is continuous because it's a finite product of (scalar * eval_at_point),
    -- each of which is continuous since evaluation is continuous.
    apply continuous_pi
    intro k
    exact continuous_const.mul (continuous_eval_at L (circlePoint L N k))

@[simp] theorem circleRestriction_apply (N : ℕ) [NeZero N]
    (f : SmoothMap_Circle L ℝ) (k : ZMod N) :
    circleRestriction L N f k = Real.sqrt (circleSpacing L N) * f (circlePoint L N k) := rfl

/-- The circle restriction equals √(L/N) times evaluation at kL/N. -/
theorem circleRestriction_eq (N : ℕ) [NeZero N]
    (f : SmoothMap_Circle L ℝ) (k : ZMod N) :
    circleRestriction L N f k = Real.sqrt (L / N) * f ((ZMod.val k : ℝ) * L / N) := by
  simp [circleSpacing, circlePoint]

/-! ## Glimm–Jaffe (Riemann-sum) circle restriction

The **counting-isometric** `circleRestriction` above scales by
`√(L/N)` per coordinate; its squared ℓ²-norm approximates the
continuum L² inner product. Useful when the GFF is built with the
counting inner product on the lattice.

The **Riemann-sum-isometric** `circleRestrictionGJ` below scales by
`(L/N)` per coordinate (i.e., one extra `√(L/N)` factor). It
corresponds to the Glimm–Jaffe-aligned setup where the lattice GFF
has covariance kernel `a^{-d} Q^{-1}`, and the embedding's `(L/N)^d`
factor for d-dim tensor products converts a lattice configuration
`φ : (ℤ/Nℤ)^d → ℝ` to a Riemann-sum approximation
`(ι_GJ φ)(f) = a^d Σ_x φ_x f(x_a)` of the continuum smearing.

The relationship: `circleRestrictionGJ = √(L/N) • circleRestriction`. -/

/-- The Glimm–Jaffe circle restriction: sample a smooth periodic function
at N lattice points, with `(L/N)` weight (the Riemann-sum volume element):

  `(r_N^{GJ} f)(k) = (L/N) · f(kL/N)`

This is the **Riemann-sum-isometric** rescaling of `circleRestriction` —
ie, `circleRestrictionGJ = √(L/N) • circleRestriction`. The squared
ℓ²-norm `Σ_k (r_N^{GJ} f)(k)²` is `(L/N)² Σ_k f²(kL/N) = (L/N) · (Riemann sum of f²)`,
so it scales as `O(L/N) = O(a)` per coordinate. For d-dim tensor
products, the squared norm scales as `O(a^d)`, exactly matching the
`(a^d)⁻¹` factor in the GJ-aligned lattice covariance. -/
noncomputable def circleRestrictionGJ (N : ℕ) [NeZero N] :
    SmoothMap_Circle L ℝ →L[ℝ] (ZMod N → ℝ) :=
  Real.sqrt (circleSpacing L N) • circleRestriction L N

@[simp] theorem circleRestrictionGJ_apply (N : ℕ) [NeZero N]
    (f : SmoothMap_Circle L ℝ) (k : ZMod N) :
    circleRestrictionGJ L N f k =
      circleSpacing L N * f (circlePoint L N k) := by
  unfold circleRestrictionGJ
  rw [_root_.smul_apply, Pi.smul_apply, smul_eq_mul,
    circleRestriction_apply]
  have h_nn : (0 : ℝ) ≤ circleSpacing L N :=
    le_of_lt (circleSpacing_pos L N)
  rw [show Real.sqrt (circleSpacing L N) *
      (Real.sqrt (circleSpacing L N) * f (circlePoint L N k)) =
      Real.sqrt (circleSpacing L N) * Real.sqrt (circleSpacing L N) *
      f (circlePoint L N k) from by ring,
    Real.mul_self_sqrt h_nn]

/-- The GJ circle restriction equals (L/N) times evaluation at kL/N. -/
theorem circleRestrictionGJ_eq (N : ℕ) [NeZero N]
    (f : SmoothMap_Circle L ℝ) (k : ZMod N) :
    circleRestrictionGJ L N f k = (L / N) * f ((ZMod.val k : ℝ) * L / N) := by
  simp [circleSpacing, circlePoint]

/-- Pointwise bound on the GJ circle restriction:
`|circleRestrictionGJ L N f k| ≤ (L/N) · ‖f‖_∞` (in d=1, single coord). -/
theorem circleRestrictionGJ_le (N : ℕ) [NeZero N]
    (f : SmoothMap_Circle L ℝ) (k : ZMod N) :
    |circleRestrictionGJ L N f k| ≤
      circleSpacing L N *
        SmoothMap_Circle.sobolevSeminorm 0 f := by
  rw [circleRestrictionGJ_apply, abs_mul,
    abs_of_nonneg (le_of_lt (circleSpacing_pos L N))]
  apply mul_le_mul_of_nonneg_left _ (le_of_lt (circleSpacing_pos L N))
  calc |(f : ℝ → ℝ) (circlePoint L N k)|
      = ‖iteratedDeriv 0 ((f : ℝ → ℝ)) (circlePoint L N k)‖ := by
        rw [iteratedDeriv_zero, Real.norm_eq_abs]
    _ ≤ SmoothMap_Circle.sobolevSeminorm 0 f :=
        SmoothMap_Circle.norm_iteratedDeriv_le_sobolevSeminorm' _ 0 _

omit [Fact (0 < L)] in
@[simp] theorem circleSpacing_eq (N : ℕ) [NeZero N] :
    circleSpacing L N = L / N := rfl

/-! ## Duality with geometric embedding

The circle restriction is dual to the geometric torus embedding:
- `circlePoint L N k` gives the real coordinate `(ZMod.val k) * L / N`
- `ZMod.toScaledAddCircle L N hL k` gives the same value modulo L in `AddCircle L`

The torus embedding `torusEmbedCLM` (from `Torus/Evaluation.lean`) maps lattice
fields to configurations, while `circleRestriction` samples test functions at
lattice points. These are transposes: `⟨ι_N(φ), f⟩ = ⟨φ, r_N(f)⟩`. -/

/-! ## Riemann sum convergence -/

/-- The ℓ² norm squared of r_N f equals (L/N) times the Riemann sum of f². -/
theorem circleRestriction_sq_sum (N : ℕ) [NeZero N] (f : SmoothMap_Circle L ℝ) :
    ∑ k : ZMod N, (circleRestriction L N f k) ^ 2 =
    (L / ↑N) * ∑ k : ZMod N, (f (circlePoint L N k)) ^ 2 := by
  simp only [circleRestriction_apply, mul_pow, circleSpacing_eq]
  rw [← Finset.mul_sum]
  congr 1
  rw [Real.sq_sqrt (le_of_lt (div_pos hL.out (Nat.cast_pos.mpr (NeZero.pos N))))]

end GaussianField
