Fermat's Last Theorem in Lean 4

← all definition modules

Definitions/Def_ModularCurve_CharLFrobeniusGeomLevel.lean

definition module

Characteristic- Frobenius and Hecke operators on modular function fields

Fix a field K of characteristic \ell (\ell prime), an integer N \neq 0, a datum data : ModularPolynomialData ℓ and a hypothesis hKr : KroneckerCongruence ℓ data. Inside K((q)) the field modularFunctionFieldC K N is the intermediate field generated over K by jqModC K (the Laurent series q^{-1} times the reduction of the integral power series jNum, i.e. the q-expansion of j) and by jqNModC K N, its image under the substitution q \mapsto q^N. The substitution q \mapsto q^\ell, packaged as a K-algebra endomorphism qExpandAlgC K ℓ of K((q)), raises each of these two generators to the \ell-th power, by the identity j(q^\ell) = j(q)^\ell recorded in ModularPolynomialData.frobenius_identity_geom; hence it maps the field into itself, and frobeniusGeomLevel is the resulting injective K-algebra endomorphism, with frobeniusGeomLevelImage its image and frobeniusGeomLevelEquiv the isomorphism onto that image. Every \ell-th power lies in the image, so the extension is purely inseparable of exponent dividing \ell: isFrobeniusEndo_frobeniusGeomLevel verifies both clauses of IsFrobeniusEndo ℓ, namely that each x^\ell is a value and each value is an \ell-th power, given that K is closed under \ell-th roots.

On places, frobOnPlacesGeomLevel sends w to the place whose valuation subring consists of the x with frobeniusGeomLevel x in that of w; it is injective, and surjective under the same perfectness hypothesis, via IsFrobeniusEndo.extendPlace, which pulls a place back along the ring homomorphism frobNormRingHom given by the inverse-image map frobNorm. verOnPlacesGeomLevel is a classically chosen one-sided inverse. Ramification is computed: \mathrm{ord}_w(\mathrm{Frob}\,g) = \ell\,\mathrm{ord}_{\mathrm{Frob}(w)}(g).

On divisors, frobeniusPushforwardGeomLevel transports supports along frobOnPlacesGeomLevel, frobeniusPullbackGeomLevel sends \langle v, n\rangle to \langle \mathrm{Ver}(v), n\ell\rangle, and heckeFibreGeomLevel is their sum. Their composite satisfies \mathrm{Frob}^*\mathrm{Frob}_* = \ell, degrees are multiplied by 1, \ell and \ell+1 when all places have degree one, and all three preserve principality; they therefore descend to degree-zero divisors and to Pic0, where eichlerShimura_special_fibre_pic0_level gives \mathrm{Frob}_*^2 - T\,\mathrm{Frob}_* + \ell = 0. The …OfIsCurveOver variants supply the perfectness and degree-one hypotheses from K algebraically closed together with IsCurveOver K (modularFunctionFieldC K N).

Relation to Mathlib

The framework of places, divisors, degree-zero divisors and Pic0 for a function field, the predicate IsCurveOver, and IsFrobeniusEndo are the project's own; Mathlib supplies the ambient LaurentSeries, IntermediateField, ValuationSubring and Finsupp material. Some general lemmas on valuation subrings and on ramification indices of restricted places are proved here alongside.

Where it is used

These operators model the Frobenius and Verschiebung morphisms, and the Hecke correspondence T_\ell, on the characteristic-\ell fibre of the modular curve of level N, the Eichler–Shimura congruence relation on its Jacobian being what links Hecke eigenvalues to traces of Frobenius in the Galois representations attached to modular forms.

References

  1. F. Diamond and J. Shurman, A First Course in Modular Forms, Graduate Texts in Mathematics 228, Springer, 2005, Chapter 8
  2. H. Stichtenoth, Algebraic Function Fields and Codes, 2nd edition, Springer, 2009
  3. P. Deligne and M. Rapoport, Les schémas de modules de courbes elliptiques, in: Modular Functions of One Variable II, Lecture Notes in Mathematics 349, Springer, 1973, 143–316

References are suggested automatically and have not been individually verified.

English text generated automatically from the Lean source; the Lean statement is authoritative.

Source file: Definitions/Def_ModularCurve_CharLFrobeniusGeomLevel.lean

Imports

Theorems imported by this definition module

Imported by

Declarations

Source

import Mathlib
import Definitions.Def_AlgebraicCurve_IsCurveOver
import Definitions.Def_AlgebraicCurve_DivisorPushPull
import Definitions.Def_AlgebraicCurve_RatFuncPlaces
import Definitions.Def_AlgebraicCurve_FrobeniusEndo
import Definitions.Def_AlgebraicCurve_FrobeniusEndoPic0
import Definitions.Def_ModularCurve_JqCoeff
import Definitions.Def_ModularCurve_FibrePoly
import Definitions.Def_HeckeGalois_EichlerShimura
import Theorems.Thm_ModularCurve_frobenius_identity_geom_unconditional

set_option autoImplicit false

noncomputable section

open AlgebraicCurve Polynomial

set_option synthInstance.maxHeartbeats 400000
set_option maxHeartbeats 800000

namespace ModularCurve

section ConstantEmbedding
variable {R : Type*} [CommRing R]

theorem algebraMap_laurentSeries_apply_eq_single (c : R) :
    algebraMap R (LaurentSeries R) c = HahnSeries.single 0 c := by
  have h1 : algebraMap R (PowerSeries R) c = PowerSeries.C c := by simp
  rw [HahnSeries.algebraMap_apply', h1, HahnSeries.ofPowerSeries_C]
  rfl
end ConstantEmbedding

section QExpandAlg
variable (K : Type*) [Field K] (N : ℕ) [NeZero N]

def qExpandAlgC : LaurentSeries K →ₐ[K] LaurentSeries K where
  __ := qExpand K N
  commutes' r := by
    show qExpand K N (algebraMap K (LaurentSeries K) r) = algebraMap K (LaurentSeries K) r
    rw [algebraMap_laurentSeries_apply_eq_single, qExpand_single, mul_zero]
@[simp]
theorem qExpandAlgC_apply (f : LaurentSeries K) : qExpandAlgC K N f = qExpand K N f := rfl
end QExpandAlg

section FrobId
variable (K : Type*) [Field K] {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]

theorem ModularPolynomialData.frobenius_identity_geom (_data : ModularPolynomialData ℓ)
    (_hK : KroneckerCongruence ℓ _data) :
    jqNModC K ℓ = (jqModC K) ^ ℓ :=
  frobenius_identity_geom_unconditional K
end FrobId

section DegOneBridge
open AlgebraicCurve
variable (K : Type*) [Field K] (N : ℕ) [NeZero N] [IsAlgClosed K]
    [IsCurveOver K (modularFunctionFieldC K N)]

theorem deg_eq_one_modularFunctionFieldC :
    ∀ w : Place K (modularFunctionFieldC K N), w.deg = 1 :=
  IsCurveOver.forall_deg_eq_one_of_isAlgClosed
end DegOneBridge

end ModularCurve

noncomputable section

open AlgebraicCurve Polynomial

set_option synthInstance.maxHeartbeats 400000
set_option maxHeartbeats 800000

namespace ModularCurve

section FrobeniusEndomorphism

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

theorem qExpand_jqModC_eq_pow : qExpand K ℓ (jqModC K) = (jqModC K) ^ ℓ :=
  data.frobenius_identity_geom K hKr

include data hKr in

theorem qExpand_jqNModC_eq_pow : qExpand K ℓ (jqNModC K N) = (jqNModC K N) ^ ℓ := by
  rw [jqNModC, qExpand_qExpand, qExpand_congr (mul_comm ℓ N), ← qExpand_qExpand,
    qExpand_jqModC_eq_pow K data hKr, map_pow]

include data hKr in

theorem frobeniusGeomLevel_map_le :
    (modularFunctionFieldC K N).map (qExpandAlgC K ℓ) ≤ modularFunctionFieldC K N := by
  show (IntermediateField.adjoin K {jqModC K, jqNModC K N}).map (qExpandAlgC K ℓ) ≤ _
  rw [IntermediateField.adjoin_map, Set.image_pair, IntermediateField.adjoin_le_iff]
  rintro x (rfl | rfl)
  · show qExpandAlgC K ℓ (jqModC K) ∈ modularFunctionFieldC K N
    rw [qExpandAlgC_apply, qExpand_jqModC_eq_pow K data hKr]
    exact pow_mem (jqModC_mem K N) ℓ
  · show qExpandAlgC K ℓ (jqNModC K N) ∈ modularFunctionFieldC K N
    rw [qExpandAlgC_apply, qExpand_jqNModC_eq_pow K N data hKr]
    exact pow_mem (jqNModC_mem K N) ℓ

def frobeniusGeomLevelImage : IntermediateField K (LaurentSeries K) :=
  (modularFunctionFieldC K N).map (qExpandAlgC K ℓ)

def frobeniusGeomLevelEquiv :
    (modularFunctionFieldC K N) ≃ₐ[K] (frobeniusGeomLevelImage K N (ℓ := ℓ)) :=
  (modularFunctionFieldC K N).equivMap (qExpandAlgC K ℓ)

omit [CharP K ℓ] in
@[simp]
theorem coe_frobeniusGeomLevelEquiv_apply (x : modularFunctionFieldC K N) :
    (frobeniusGeomLevelEquiv K N (ℓ := ℓ) x : LaurentSeries K)
      = qExpand K ℓ (x : LaurentSeries K) :=
  IntermediateField.coe_equivMap_apply _ _ x

include data hKr in

def frobeniusGeomLevel : modularFunctionFieldC K N →ₐ[K] modularFunctionFieldC K N :=
  (IntermediateField.inclusion (frobeniusGeomLevel_map_le K N data hKr)).comp
    (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).toAlgHom

@[simp]
theorem frobeniusGeomLevel_apply_coe (x : modularFunctionFieldC K N) :
    (frobeniusGeomLevel K N data hKr x : LaurentSeries K)
      = qExpand K ℓ (x : LaurentSeries K) := by
  refine (IntermediateField.coe_inclusion (frobeniusGeomLevel_map_le K N data hKr)
    _).trans ?_
  exact coe_frobeniusGeomLevelEquiv_apply K N x

theorem frobeniusGeomLevel_jq :
    frobeniusGeomLevel K N data hKr ⟨jqModC K, jqModC_mem K N⟩
      = (⟨jqModC K, jqModC_mem K N⟩ : modularFunctionFieldC K N) ^ ℓ := by
  refine Subtype.ext ?_
  rw [frobeniusGeomLevel_apply_coe]
  push_cast
  exact qExpand_jqModC_eq_pow K data hKr

theorem frobeniusGeomLevel_jqN :
    frobeniusGeomLevel K N data hKr ⟨jqNModC K N, jqNModC_mem K N⟩
      = (⟨jqNModC K N, jqNModC_mem K N⟩ : modularFunctionFieldC K N) ^ ℓ := by
  refine Subtype.ext ?_
  rw [frobeniusGeomLevel_apply_coe]
  push_cast
  exact qExpand_jqNModC_eq_pow K N data hKr

theorem frobeniusGeomLevel_injective :
    Function.Injective (frobeniusGeomLevel K N data hKr) := by
  intro x y h
  have h' := congrArg (fun z : modularFunctionFieldC K N => (z : LaurentSeries K)) h
  simp only [frobeniusGeomLevel_apply_coe] at h'
  exact Subtype.ext (qExpand_injective _ h')

theorem exists_frobeniusGeomLevel_eq_iff (x : modularFunctionFieldC K N) :
    (∃ y, frobeniusGeomLevel K N data hKr y = x)
      ↔ (x : LaurentSeries K) ∈ frobeniusGeomLevelImage K N (ℓ := ℓ) := by
  constructor
  · rintro ⟨y, rfl⟩
    rw [frobeniusGeomLevel_apply_coe]
    exact ⟨(y : LaurentSeries K), y.2, rfl⟩
  · rintro ⟨y, hy, hyx⟩
    refine ⟨⟨y, hy⟩, Subtype.ext ?_⟩
    rw [frobeniusGeomLevel_apply_coe]
    exact hyx

end FrobeniusEndomorphism

section PurelyInseparable

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

omit [Fact ℓ.Prime] in

theorem charP_laurentSeries : CharP (LaurentSeries K) ℓ :=
  charP_of_injective_algebraMap (algebraMap K (LaurentSeries K)).injective ℓ

include data hKr in

theorem pow_mem_frobeniusGeomLevelImage
    {x : LaurentSeries K} (hx : x ∈ modularFunctionFieldC K N) :
    x ^ ℓ ∈ frobeniusGeomLevelImage K N (ℓ := ℓ) := by
  haveI : CharP (LaurentSeries K) ℓ := charP_laurentSeries K
  induction hx using IntermediateField.adjoin_induction with
  | mem y hy =>
    rcases hy with rfl | rfl
    · rw [← qExpand_jqModC_eq_pow K data hKr]
      exact ⟨jqModC K, jqModC_mem K N, rfl⟩
    · rw [← qExpand_jqNModC_eq_pow K N data hKr]
      exact ⟨jqNModC K N, jqNModC_mem K N, rfl⟩
  | algebraMap c =>
    rw [← map_pow]
    exact (frobeniusGeomLevelImage K N (ℓ := ℓ)).algebraMap_mem (c ^ ℓ)
  | add y z _ _ hy hz =>
    rw [add_pow_char]
    exact add_mem hy hz
  | inv y _ hy =>
    rw [inv_pow]
    exact inv_mem hy
  | mul y z _ _ hy hz =>
    rw [mul_pow]
    exact mul_mem hy hz

theorem exists_frobeniusGeomLevel_eq_pow (x : modularFunctionFieldC K N) :
    ∃ y, frobeniusGeomLevel K N data hKr y = x ^ ℓ := by
  rw [exists_frobeniusGeomLevel_eq_iff]
  show (x : LaurentSeries K) ^ ℓ ∈ frobeniusGeomLevelImage K N (ℓ := ℓ)
  exact pow_mem_frobeniusGeomLevelImage K N data hKr x.2

theorem _root_.ValuationSubring.mem_of_pow_mem' {F : Type*} [Field F]
    (A : ValuationSubring F) {x : F} {n : ℕ} (hn : n ≠ 0) (h : x ^ n ∈ A) : x ∈ A := by
  rcases A.mem_or_inv_mem x with hx | hx
  · exact hx
  rcases eq_or_ne x 0 with rfl | hx0
  · exact A.zero_mem
  have hpow : x ^ n = x * x ^ (n - 1) := by
    rw [← pow_succ']
    exact congrArg (x ^ ·) (by omega)
  have hkey : x ^ n * (x⁻¹) ^ (n - 1) = x := by
    rw [hpow, inv_pow, mul_assoc, mul_inv_cancel₀ (pow_ne_zero _ hx0), mul_one]
  rw [← hkey]
  exact A.mul_mem _ _ h (pow_mem hx _)

theorem mem_valuationSubring_iff_pow_mem {F : Type*} [Field F] (A : ValuationSubring F)
    (x : F) : x ∈ A ↔ x ^ ℓ ∈ A :=
fun h => pow_mem h ℓ,
    fun h => ValuationSubring.mem_of_pow_mem' A (Fact.out : ℓ.Prime).pos.ne' h⟩

end PurelyInseparable

section PlaceLevel

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

@[reducible] def frobImageAlgebra :
    Algebra (frobeniusGeomLevelImage K N (ℓ := ℓ)) (modularFunctionFieldC K N) :=
  (IntermediateField.inclusion (frobeniusGeomLevel_map_le K N data hKr)).toRingHom.toAlgebra

theorem frobImageTower :
    letI := frobImageAlgebra K N data hKr
    IsScalarTower K (frobeniusGeomLevelImage K N (ℓ := ℓ)) (modularFunctionFieldC K N) :=
  letI := frobImageAlgebra K N data hKr
  IsScalarTower.of_algebraMap_eq fun _ => Subtype.ext rfl

theorem frobImageIsIntegral :
    letI := frobImageAlgebra K N data hKr
    Algebra.IsIntegral (frobeniusGeomLevelImage K N (ℓ := ℓ))
      (modularFunctionFieldC K N) := by
  letI := frobImageAlgebra K N data hKr
  refine ⟨fun x => ?_⟩
  refine ⟨Polynomial.X ^ ℓ - Polynomial.C
    ⟨(x : LaurentSeries K) ^ ℓ, pow_mem_frobeniusGeomLevelImage K N data hKr x.2⟩,
    Polynomial.monic_X_pow_sub_C _ (Fact.out : ℓ.Prime).pos.ne', ?_⟩
  rw [Polynomial.eval₂_sub, Polynomial.eval₂_X_pow, Polynomial.eval₂_C, sub_eq_zero]
  exact Subtype.ext rfl

include data hKr in

def frobOnPlacesGeomLevel (w : Place K (modularFunctionFieldC K N)) :
    Place K (modularFunctionFieldC K N) :=
  letI := frobImageAlgebra K N data hKr
  letI := frobImageTower K N data hKr
  letI := frobImageIsIntegral K N data hKr
  (Place.congrEquiv (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).toRingEquiv
    (fun a => (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).commutes a)).symm
    (w.restrict (frobeniusGeomLevelImage K N (ℓ := ℓ)))

theorem mem_frobOnPlacesGeomLevel_iff (w : Place K (modularFunctionFieldC K N))
    (x : modularFunctionFieldC K N) :
    x ∈ (frobOnPlacesGeomLevel K N data hKr w).toValuationSubring
frobeniusGeomLevel K N data hKr x ∈ w.toValuationSubring := by
  letI := frobImageAlgebra K N data hKr
  letI := frobImageTower K N data hKr
  letI := frobImageIsIntegral K N data hKr
  rw [show frobOnPlacesGeomLevel K N data hKr w
      = (Place.congrEquiv (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).toRingEquiv
          (fun a => (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).commutes a)).symm
          (w.restrict (frobeniusGeomLevelImage K N (ℓ := ℓ))) from rfl,
    Place.congrEquiv_symm_apply, Place.congrRingEquiv_toValuationSubring,
    ValuationSubring.mem_comap, RingEquiv.symm_symm]
  exact Iff.rfl

theorem frobOnPlacesGeomLevel_injective :
    Function.Injective (frobOnPlacesGeomLevel K N data hKr) := by
  intro w w' h
  ext1
  refine SetLike.ext fun x => ?_
  obtain ⟨y, hy⟩ := exists_frobeniusGeomLevel_eq_pow K N data hKr x
  rw [mem_valuationSubring_iff_pow_mem (ℓ := ℓ) w.toValuationSubring x, ← hy,
mem_frobOnPlacesGeomLevel_iff K N data hKr w y, h,
    mem_frobOnPlacesGeomLevel_iff K N data hKr w' y, hy,
mem_valuationSubring_iff_pow_mem (ℓ := ℓ) w'.toValuationSubring x]

include data hKr in
open Classical in

def verOnPlacesGeomLevel (u : Place K (modularFunctionFieldC K N)) :
    Place K (modularFunctionFieldC K N) :=
  if h : ∃ w, frobOnPlacesGeomLevel K N data hKr w = u then h.choose else u

theorem verOnPlacesGeomLevel_frobOnPlacesGeomLevel
    (w : Place K (modularFunctionFieldC K N)) :
    verOnPlacesGeomLevel K N data hKr (frobOnPlacesGeomLevel K N data hKr w) = w := by
  rw [verOnPlacesGeomLevel, dif_pos ⟨w, rfl⟩]
  exact frobOnPlacesGeomLevel_injective K N data hKr
    (Exists.choose_spec (⟨w, rfl⟩ : ∃ w', frobOnPlacesGeomLevel K N data hKr w'
      = frobOnPlacesGeomLevel K N data hKr w))

end PlaceLevel

section DivisorLevel

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

def frobeniusPushforwardGeomLevel :
    Divisor K (modularFunctionFieldC K N) →+ Divisor K (modularFunctionFieldC K N) :=
  Finsupp.mapDomain.addMonoidHom (frobOnPlacesGeomLevel K N data hKr)

include data hKr in

def frobeniusPullbackGeomLevel :
    Divisor K (modularFunctionFieldC K N) →+ Divisor K (modularFunctionFieldC K N) :=
  Finsupp.liftAddHom fun v =>
    (Finsupp.singleAddHom (verOnPlacesGeomLevel K N data hKr v)).comp
      (AddMonoidHom.mulRight (ℓ : ℤ))

include data hKr in

def heckeFibreGeomLevel :
    Divisor K (modularFunctionFieldC K N) →+ Divisor K (modularFunctionFieldC K N) :=
  frobeniusPushforwardGeomLevel K N data hKr + frobeniusPullbackGeomLevel K N data hKr

@[simp]
theorem frobeniusPushforwardGeomLevel_single (w : Place K (modularFunctionFieldC K N))
    (n : ℤ) :
    frobeniusPushforwardGeomLevel K N data hKr (Finsupp.single w n)
      = Finsupp.single (frobOnPlacesGeomLevel K N data hKr w) n := by
  simp [frobeniusPushforwardGeomLevel, Finsupp.mapDomain.addMonoidHom_apply,
    Finsupp.mapDomain_single]

@[simp]
theorem frobeniusPullbackGeomLevel_single (w : Place K (modularFunctionFieldC K N))
    (n : ℤ) :
    frobeniusPullbackGeomLevel K N data hKr (Finsupp.single w n)
      = Finsupp.single (verOnPlacesGeomLevel K N data hKr w) (n * ℓ) := by
  simp [frobeniusPullbackGeomLevel]

theorem frobeniusPullbackGeomLevel_frobeniusPushforwardGeomLevel
    (D : Divisor K (modularFunctionFieldC K N)) :
    frobeniusPullbackGeomLevel K N data hKr
        (frobeniusPushforwardGeomLevel K N data hKr D) = (ℓ : ℤ) • D := by
  induction D using Finsupp.induction with
  | zero => simp
  | single_add v n D _ _ ih =>
    rw [map_add, map_add, ih, frobeniusPushforwardGeomLevel_single,
      frobeniusPullbackGeomLevel_single, verOnPlacesGeomLevel_frobOnPlacesGeomLevel,
      smul_add, Finsupp.smul_single, smul_eq_mul, mul_comm]

theorem eichlerShimura_special_fibre_geom_level
    (D : Divisor K (modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevel K N data hKr
        (frobeniusPushforwardGeomLevel K N data hKr D)
      - heckeFibreGeomLevel K N data hKr (frobeniusPushforwardGeomLevel K N data hKr D)
      + (ℓ : ℤ) • D = 0 := by
  rw [heckeFibreGeomLevel, AddMonoidHom.add_apply,
    frobeniusPullbackGeomLevel_frobeniusPushforwardGeomLevel]
  abel

end DivisorLevel

section Ramification

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

theorem exists_ramification_frobenius (w : Place K (modularFunctionFieldC K N)) :
    ∃ e : ℕ, 0 < e ∧ (e : ℤ) ∣ (ℓ : ℤ) ∧
      ∀ g : modularFunctionFieldC K N,
        w.ord (frobeniusGeomLevel K N data hKr g)
          = e * (frobOnPlacesGeomLevel K N data hKr w).ord g := by
  letI := frobImageAlgebra K N data hKr
  letI := frobImageTower K N data hKr
  letI := frobImageIsIntegral K N data hKr
  refine ⟨Place.ramificationIndex (F := frobeniusGeomLevelImage K N (ℓ := ℓ)) w,
    Place.ramificationIndex_pos w, ?_, fun g => ?_⟩
  ·

    obtain ⟨π, hπ⟩ := IsDiscreteValuationRing.exists_irreducible w.toValuationSubring
    have hπ0 : (π : modularFunctionFieldC K N) ≠ 0 := by
      simpa [ne_eq, ZeroMemClass.coe_eq_zero] using hπ.ne_zero
    obtain ⟨y, hy⟩ := exists_frobeniusGeomLevel_eq_pow K N data hKr
      (π : modularFunctionFieldC K N)
    have hy0 : (frobeniusGeomLevelEquiv K N (ℓ := ℓ)) y ≠ 0 := by
      intro h0
      have : y = 0 := by
        have := congrArg (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).symm h0
        rwa [AlgEquiv.symm_apply_apply, map_zero] at this
      rw [this, map_zero] at hy
      exact pow_ne_zero ℓ hπ0 hy.symm
    have hdvd := Place.ramificationIndex_dvd_ord
      (F := frobeniusGeomLevelImage K N (ℓ := ℓ)) (w := w) hy0
    rw [show (algebraMap (frobeniusGeomLevelImage K N (ℓ := ℓ))
        (modularFunctionFieldC K N)) ((frobeniusGeomLevelEquiv K N (ℓ := ℓ)) y)
        = frobeniusGeomLevel K N data hKr y from rfl, hy, ← zpow_natCast,
      Place.ord_zpow, Place.ord_coe_irreducible w hπ, mul_one] at hdvd
    exact hdvd
  ·

    rw [show frobeniusGeomLevel K N data hKr g
        = algebraMap (frobeniusGeomLevelImage K N (ℓ := ℓ)) (modularFunctionFieldC K N)
            ((frobeniusGeomLevelEquiv K N (ℓ := ℓ)).toRingEquiv g) from rfl,
      Place.ord_restrict]
    congr 1
    have h2 := Place.ord_congrRingEquiv
      (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).toRingEquiv
      (fun a => (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).commutes a)
      (frobOnPlacesGeomLevel K N data hKr w) g
    rw [show Place.congrRingEquiv (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).toRingEquiv
        (fun a => (frobeniusGeomLevelEquiv K N (ℓ := ℓ)).commutes a)
        (frobOnPlacesGeomLevel K N data hKr w)
        = w.restrict (frobeniusGeomLevelImage K N (ℓ := ℓ)) from
      Equiv.apply_symm_apply (Place.congrEquiv _ _) _] at h2
    exact h2

end Ramification

end ModularCurve

namespace AlgebraicCurve
namespace Place

section SinglePlace
variable {K F : Type*} [Field K] [Field F] [Algebra K F] (v : Place K F)

theorem ord_nonneg_of_mem {f : F} (hf : f ∈ v.toValuationSubring) : 0v.ord f := by
  rcases eq_or_ne f 0 with rfl | hf0
  · simp
  obtain ⟨π, hπ⟩ := IsDiscreteValuationRing.exists_irreducible v.toValuationSubring
  obtain ⟨n, u, hu⟩ :=
    IsDiscreteValuationRing.eq_unit_mul_pow_irreducible
      (x := (⟨f, hf⟩ : v.toValuationSubring)) (by simpa [Subtype.ext_iff] using hf0) hπ
  have hcoe : f = ((u : v.toValuationSubring) : F) * ((π : F) ^ (n : ℤ)) := by
    have h := congrArg (Subtype.val) hu
    push_cast at h
    rw [zpow_natCast]
    exact h
  rw [hcoe, v.ord_unit_smul_zpow u hπ (n : ℤ)]
  exact Int.natCast_nonneg n

theorem mem_of_ord_nonneg {f : F} (hf : f ≠ 0) (h : 0v.ord f) :
    f ∈ v.toValuationSubring := by
  obtain ⟨π, hπ⟩ := IsDiscreteValuationRing.exists_irreducible v.toValuationSubring
  obtain ⟨u, hu⟩ := v.exists_unit_mul_zpow hf hπ
  rw [hu, show v.ord f = (((v.ord f).toNat : ℕ) : ℤ) from (Int.toNat_of_nonneg h).symm,
    zpow_natCast]
  exact mul_mem (u : v.toValuationSubring).2 (pow_mem (π : v.toValuationSubring).2 _)

theorem mem_iff_ord_nonneg {f : F} (hf : f ≠ 0) :
    f ∈ v.toValuationSubring0v.ord f :=
v.ord_nonneg_of_mem, v.mem_of_ord_nonneg hf⟩

theorem exists_ord_pos : ∃ f : F, f ≠ 00 < v.ord f := by
  obtain ⟨π, hπ⟩ := IsDiscreteValuationRing.exists_irreducible v.toValuationSubring
  refine ⟨(π : F), ?_, ?_⟩
  · simpa [ne_eq, ZeroMemClass.coe_eq_zero] using hπ.ne_zero
  · rw [v.ord_coe_irreducible hπ]
    exact one_pos

end SinglePlace

section RestrictPriv
variable {K F F' : Type*} [Field K] [Field F] [Field F']
  [Algebra K F'] [Algebra F F']

theorem algebraMap_ne_zero {f : F} (hf : f ≠ 0) : algebraMap F F' f ≠ 0 := by
  simpa using hf

variable (w : Place K F')

theorem comap_algebraMap_ne_top [Algebra.IsIntegral F F'] :
    w.toValuationSubring.comap (algebraMap F F') ≠ ⊤ := by
  intro htop
  apply w.ne_top'

  have hF : ∀ f : F, algebraMap F F' f ∈ w.toValuationSubring := fun f =>
    ValuationSubring.mem_comap.mp (htop ▸ ValuationSubring.mem_top f)

  refine SetLike.ext fun x => ⟨fun _ => ValuationSubring.mem_top x, fun _ => ?_⟩
  letI : Algebra F w.toValuationSubring :=
    ((algebraMap F F').codRestrict w.toValuationSubring.toSubring hF).toAlgebra
  letI : IsScalarTower F w.toValuationSubring F' :=
    IsScalarTower.of_algebraMap_eq fun f => rfl
  have hx : IsIntegral w.toValuationSubring x :=
    (Algebra.IsIntegral.isIntegral (R := F) x).tower_top
  obtain ⟨y, hy⟩ := IsIntegrallyClosed.isIntegral_iff.mp hx
  exact hy ▸ y.2

variable {w} in

theorem mem_comap_iff_ord_nonneg {f : F} (hf : f ≠ 0) :
    f ∈ w.toValuationSubring.comap (algebraMap F F') ↔
      0 ≤ w.ord (algebraMap F F' f) := by
  rw [ValuationSubring.mem_comap]
  exact w.mem_iff_ord_nonneg (algebraMap_ne_zero hf)

variable {w} in

theorem isUnit_mk_comap_iff {f : F} (hf : f ≠ 0)
    (hmem : f ∈ w.toValuationSubring.comap (algebraMap F F')) :
    IsUnit (⟨f, hmem⟩ : w.toValuationSubring.comap (algebraMap F F')) ↔
      w.ord (algebraMap F F' f) = 0 := by
  constructor
  · rintro h
    obtain ⟨b, hb⟩ := isUnit_iff_exists_inv.mp h
    have hb' : f * (b : F) = 1 := by
      simpa [Subtype.ext_iff] using hb
    have hbne : (b : F) ≠ 0 := by
      intro h0
      rw [h0, mul_zero] at hb'
      exact zero_ne_one hb'
    have hsum : w.ord (algebraMap F F' f) + w.ord (algebraMap F F' (b : F)) = 0 := by
      rw [← w.ord_mul (algebraMap_ne_zero hf) (algebraMap_ne_zero hbne), ← map_mul, hb',
        map_one, w.ord_one]
    have h1 : 0 ≤ w.ord (algebraMap F F' f) := (mem_comap_iff_ord_nonneg hf).mp hmem
    have h2 : 0 ≤ w.ord (algebraMap F F' (b : F)) := (mem_comap_iff_ord_nonneg hbne).mp b.2
    omega
  · intro h0
    have hinv : f⁻¹ ∈ w.toValuationSubring.comap (algebraMap F F') :=
      (mem_comap_iff_ord_nonneg (inv_ne_zero hf)).mpr (by rw [map_inv₀, w.ord_inv]; omega)
    exact ⟨⟨⟨f, hmem⟩, ⟨f⁻¹, hinv⟩, Subtype.ext (mul_inv_cancel₀ hf),
      Subtype.ext (inv_mul_cancel₀ hf)⟩, rfl⟩

theorem exists_ord_algebraMap_pos [Algebra.IsIntegral F F'] :
    ∃ f : F, f ≠ 00 < w.ord (algebraMap F F' f) := by
  have h := w.comap_algebraMap_ne_top (F := F)
  rw [ne_eq, SetLike.ext_iff, not_forall] at h
  obtain ⟨g, hg⟩ := h
  simp only [ValuationSubring.mem_top, iff_true] at hg
  have hg0 : g ≠ 0 := by
    rintro rfl
    exact hg (zero_mem _)
  refine ⟨g⁻¹, inv_ne_zero hg0, ?_⟩
  rw [map_inv₀, w.ord_inv]
  have := (mem_comap_iff_ord_nonneg hg0).not.mp hg
  omega

variable [Algebra.IsIntegral F F']

theorem ramificationIndex_set_nonempty :
    {n : ℕ | 0 < n ∧ ∃ f : F, f ≠ 0 ∧ w.ord (algebraMap F F' f) = n}.Nonempty := by
  obtain ⟨f, hf0, hf⟩ := w.exists_ord_algebraMap_pos (F := F)
  exact ⟨(w.ord (algebraMap F F' f)).toNat, by omega, f, hf0, by omega⟩

theorem isPrincipalIdealRing_comap :
    IsPrincipalIdealRing (w.toValuationSubring.comap (algebraMap F F')) := by
  obtain ⟨g, hg0, hge⟩ := w.exists_ord_eq_ramificationIndex (F := F)
  have hepos : 0 < ramificationIndex (F := F) w := w.ramificationIndex_pos (F := F)
  have hgmem : g ∈ w.toValuationSubring.comap (algebraMap F F') :=
    (mem_comap_iff_ord_nonneg hg0).mpr (by omega)
  refine (IsDiscreteValuationRing.ofHasUnitMulPowIrreducibleFactorization
    ⟨⟨g, hgmem⟩, irreducible_mk_comap w hg0 hgmem hge, ?_⟩).toIsPrincipalIdealRing
  rintro ⟨f, hmem⟩ hx
  have hf : f ≠ 0 := by simpa [Subtype.ext_iff] using hx

  obtain ⟨c, hc⟩ := w.ramificationIndex_dvd_ord (F := F) hf
  have hnonneg : 0 ≤ w.ord (algebraMap F F' f) := (mem_comap_iff_ord_nonneg hf).mp hmem
  have hcnonneg : 0 ≤ c := by
    by_contra hneg
    have hcle : c ≤ -1 := by omega
    have : (ramificationIndex (F := F) w : ℤ) * c ≤ (ramificationIndex (F := F) w : ℤ) * -1 :=
      mul_le_mul_of_nonneg_left hcle (by omega)
    omega
  set n : ℕ := c.toNat with hn
  have hcn : (n : ℤ) = c := Int.toNat_of_nonneg hcnonneg
  refine ⟨n, ?_⟩

  have hgn : g ^ n ≠ 0 := pow_ne_zero _ hg0
  have hdiv0 : f / g ^ n ≠ 0 := div_ne_zero hf hgn
  have hu0 : w.ord (algebraMap F F' (f / g ^ n)) = 0 := by
    have hkey : algebraMap F F' (f / g ^ n)
        = algebraMap F F' f * (algebraMap F F' g) ^ (-(n : ℤ)) := by
      rw [div_eq_mul_inv, map_mul, map_inv₀, map_pow, ← zpow_natCast (algebraMap F F' g) n,
        ← zpow_neg]
    rw [hkey, w.ord_mul (algebraMap_ne_zero hf) (zpow_ne_zero _ (algebraMap_ne_zero hg0)),
      w.ord_zpow, hge, hc, ← hcn]
    ring
  have humem : f / g ^ n ∈ w.toValuationSubring.comap (algebraMap F F') :=
    (mem_comap_iff_ord_nonneg hdiv0).mpr (le_of_eq hu0.symm)
  have hu : IsUnit (⟨f / g ^ n, humem⟩ : w.toValuationSubring.comap (algebraMap F F')) :=
    (isUnit_mk_comap_iff hdiv0 humem).mpr hu0
  refine ⟨hu.unit, ?_⟩
  refine Subtype.ext ?_
  have hcoe : ((hu.unit : w.toValuationSubring.comap (algebraMap F F')) : F) = f / g ^ n := by
    rw [IsUnit.unit_spec]
  push_cast
  rw [hcoe, mul_comm, div_mul_cancel₀]
  exact hgn

end RestrictPriv

end Place
end AlgebraicCurve

noncomputable section

open AlgebraicCurve Polynomial

set_option synthInstance.maxHeartbeats 400000
set_option maxHeartbeats 800000

namespace ModularCurve

section Package

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

theorem exists_pow_eq_frobeniusGeomLevel (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (y : modularFunctionFieldC K N) :
    ∃ x : modularFunctionFieldC K N, frobeniusGeomLevel K N data hKr y = x ^ ℓ := by
  haveI : CharP (LaurentSeries K) ℓ := charP_laurentSeries K

  have key : ∀ z : LaurentSeries K, z ∈ modularFunctionFieldC K N →
      ∃ w : LaurentSeries K, w ∈ modularFunctionFieldC K N ∧ qExpand K ℓ z = w ^ ℓ := by
    intro z hz
    induction hz using IntermediateField.adjoin_induction with
    | mem y hy =>
      rcases hy with rfl | rfl
      · exact ⟨jqModC K, jqModC_mem K N, qExpand_jqModC_eq_pow K data hKr⟩
      · exact ⟨jqNModC K N, jqNModC_mem K N, qExpand_jqNModC_eq_pow K N data hKr⟩
    | algebraMap c =>
      obtain ⟨d, hd⟩ := hperf c
      refine ⟨algebraMap K (LaurentSeries K) d,
        (modularFunctionFieldC K N).algebraMap_mem d, ?_⟩
      rw [show qExpand K ℓ (algebraMap K (LaurentSeries K) c)
            = qExpandAlgC K ℓ (algebraMap K (LaurentSeries K) c) from rfl,
        (qExpandAlgC K ℓ).commutes c, ← map_pow, hd]
    | add y z _ _ hy hz =>
      obtain ⟨w₁, hw₁, he₁⟩ := hy
      obtain ⟨w₂, hw₂, he₂⟩ := hz
      refine ⟨w₁ + w₂, add_mem hw₁ hw₂, ?_⟩
      rw [show qExpand K ℓ (y + z) = qExpandAlgC K ℓ (y + z) from rfl, map_add,
        qExpandAlgC_apply, qExpandAlgC_apply, he₁, he₂, add_pow_char]
    | inv y _ hy =>
      obtain ⟨w, hw, he⟩ := hy
      refine ⟨w⁻¹, inv_mem hw, ?_⟩
      rw [show qExpand K ℓ y⁻¹ = qExpandAlgC K ℓ y⁻¹ from rfl, map_inv₀,
        qExpandAlgC_apply, he, inv_pow]
    | mul y z _ _ hy hz =>
      obtain ⟨w₁, hw₁, he₁⟩ := hy
      obtain ⟨w₂, hw₂, he₂⟩ := hz
      refine ⟨w₁ * w₂, mul_mem hw₁ hw₂, ?_⟩
      rw [show qExpand K ℓ (y * z) = qExpandAlgC K ℓ (y * z) from rfl, map_mul,
        qExpandAlgC_apply, qExpandAlgC_apply, he₁, he₂, mul_pow]
  obtain ⟨w, hw, he⟩ := key (y : LaurentSeries K) y.2
  refine ⟨⟨w, hw⟩, Subtype.ext ?_⟩
  rw [frobeniusGeomLevel_apply_coe]
  push_cast
  exact he

include data hKr in

theorem isFrobeniusEndo_frobeniusGeomLevel (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c) :
    IsFrobeniusEndo ℓ (frobeniusGeomLevel K N data hKr) :=
exists_frobeniusGeomLevel_eq_pow K N data hKr,
    exists_pow_eq_frobeniusGeomLevel K N data hKr hperf⟩

include data hKr in

theorem frobeniusGeomLevel_isIntegral (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c) :
    (frobeniusGeomLevel K N data hKr).toRingHom.IsIntegral :=
  (isFrobeniusEndo_frobeniusGeomLevel K N data hKr hperf).isIntegral
    (Fact.out : ℓ.Prime).pos.ne'

theorem frobeniusGeomLevel_eq_pow_unique {g g' f : modularFunctionFieldC K N}
    (hg : frobeniusGeomLevel K N data hKr g = f ^ ℓ)
    (hg' : frobeniusGeomLevel K N data hKr g' = f ^ ℓ) : g = g' :=
  frobeniusGeomLevel_injective K N data hKr (hg.trans hg'.symm)

end Package

section Identification

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

theorem mem_restrictAlong_iff {F : Type*} [Field F] [Algebra K F] (φ : F →ₐ[K] F)
    (hφ : φ.toRingHom.IsIntegral) (w : Place K F) (x : F) :
    x ∈ (w.restrictAlong φ hφ).toValuationSubring ↔ φ x ∈ w.toValuationSubring := by
  rw [show (w.restrictAlong φ hφ).toValuationSubring
      = w.toValuationSubring.comap φ.toRingHom from rfl, ValuationSubring.mem_comap]
  exact Iff.rfl

include data hKr in

theorem restrictAlong_frobeniusGeomLevel_eq (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (w : Place K (modularFunctionFieldC K N)) :
    w.restrictAlong (frobeniusGeomLevel K N data hKr)
        (frobeniusGeomLevel_isIntegral K N data hKr hperf)
      = frobOnPlacesGeomLevel K N data hKr w := by
  refine Place.ext (SetLike.ext fun x => ?_)
  rw [mem_restrictAlong_iff, mem_frobOnPlacesGeomLevel_iff]

end Identification

section Ramification

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

theorem ramificationIndexAlong_frobeniusGeomLevel (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (w : Place K (modularFunctionFieldC K N)) :
    Place.ramificationIndexAlong (frobeniusGeomLevel K N data hKr) w = ℓ :=
  (isFrobeniusEndo_frobeniusGeomLevel K N data hKr hperf).ramificationIndexAlong_eq
    (Fact.out : ℓ.Prime).pos.ne' w

include data hKr in

theorem ord_frobeniusGeomLevel_eq_mul (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (w : Place K (modularFunctionFieldC K N)) (g : modularFunctionFieldC K N) :
    w.ord (frobeniusGeomLevel K N data hKr g)
      = ℓ * (frobOnPlacesGeomLevel K N data hKr w).ord g := by
  have h := w.ord_restrictAlong (frobeniusGeomLevel K N data hKr)
    (frobeniusGeomLevel_isIntegral K N data hKr hperf) g
  rwa [ramificationIndexAlong_frobeniusGeomLevel K N data hKr hperf,
    restrictAlong_frobeniusGeomLevel_eq K N data hKr hperf] at h

include data hKr in

theorem ord_frobOnPlaces_of_pow_eq (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    {f g : modularFunctionFieldC K N}
    (hg : frobeniusGeomLevel K N data hKr g = f ^ ℓ)
    (w : Place K (modularFunctionFieldC K N)) :
    (frobOnPlacesGeomLevel K N data hKr w).ord g = w.ord f := by
  have hkey : (ℓ : ℤ) * (frobOnPlacesGeomLevel K N data hKr w).ord g
      = (ℓ : ℤ) * w.ord f := by
    rw [← ord_frobeniusGeomLevel_eq_mul K N data hKr hperf w g, hg, ← zpow_natCast,
      Place.ord_zpow]
  exact mul_left_cancel₀ (Int.natCast_ne_zero.mpr (Fact.out : ℓ.Prime).pos.ne') hkey

end Ramification

section NormFormula

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

theorem frobeniusPushforwardGeomLevel_apply_frob
    (D : Divisor K (modularFunctionFieldC K N))
    (w : Place K (modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevel K N data hKr D (frobOnPlacesGeomLevel K N data hKr w)
      = D w := by
  rw [show frobeniusPushforwardGeomLevel K N data hKr D
      = Finsupp.mapDomain (frobOnPlacesGeomLevel K N data hKr) D from rfl]
  exact Finsupp.mapDomain_apply (frobOnPlacesGeomLevel_injective K N data hKr) D w

include data hKr in

theorem frobeniusPushforwardGeomLevel_apply_of_notMem_range
    (D : Divisor K (modularFunctionFieldC K N))
    {v : Place K (modularFunctionFieldC K N)}
    (hv : v ∉ Set.range (frobOnPlacesGeomLevel K N data hKr)) :
    frobeniusPushforwardGeomLevel K N data hKr D v = 0 := by
  rw [show frobeniusPushforwardGeomLevel K N data hKr D
      = Finsupp.mapDomain (frobOnPlacesGeomLevel K N data hKr) D from rfl]
  exact Finsupp.mapDomain_notin_range D v hv

include data hKr in

theorem frobeniusPushforwardGeomLevel_normFormula_apply_frob
    (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    {f g : modularFunctionFieldC K N}
    (hg : frobeniusGeomLevel K N data hKr g = f ^ ℓ)
    {D : Divisor K (modularFunctionFieldC K N)}
    (hD : ∀ w, D w = w.ord f) (w : Place K (modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevel K N data hKr D (frobOnPlacesGeomLevel K N data hKr w)
      = (frobOnPlacesGeomLevel K N data hKr w).ord g := by
  rw [frobeniusPushforwardGeomLevel_apply_frob K N data hKr D w, hD w,
    ord_frobOnPlaces_of_pow_eq K N data hKr hperf hg w]

include data hKr in

theorem frobeniusPushforwardGeomLevel_normFormula
    (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (hsurj : Function.Surjective (frobOnPlacesGeomLevel K N data hKr))
    {f g : modularFunctionFieldC K N}
    (hg : frobeniusGeomLevel K N data hKr g = f ^ ℓ)
    {D : Divisor K (modularFunctionFieldC K N)}
    (hD : ∀ w, D w = w.ord f) (v : Place K (modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevel K N data hKr D v = v.ord g := by
  obtain ⟨w, rfl⟩ := hsurj v
  exact frobeniusPushforwardGeomLevel_normFormula_apply_frob
    K N data hKr hperf hg hD w

include data hKr in

theorem isPrincipal_frobeniusPushforwardGeomLevel
    (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (hsurj : Function.Surjective (frobOnPlacesGeomLevel K N data hKr))
    {D : Divisor K (modularFunctionFieldC K N)} (hD : D.IsPrincipal) :
    (frobeniusPushforwardGeomLevel K N data hKr D).IsPrincipal := by
  obtain ⟨f, hf, hDf⟩ := hD
  obtain ⟨g, hg⟩ := exists_frobeniusGeomLevel_eq_pow K N data hKr f
  refine ⟨g, ?_, fun v => frobeniusPushforwardGeomLevel_normFormula
    K N data hKr hperf hsurj hg hDf v⟩
  intro hg0
  rw [hg0, map_zero] at hg
  exact pow_ne_zero ℓ hf hg.symm

end NormFormula

end ModularCurve

noncomputable section

namespace AlgebraicCurve

section FrobNormHom

variable {K F : Type*} [Field K] [Field F] [Algebra K F]
variable {ℓ : ℕ} {φ : F →ₐ[K] F}

namespace IsFrobeniusEndo

theorem frobNorm_eq_of_map_eq_pow (h : IsFrobeniusEndo ℓ φ) {f g : F}
    (hg : φ g = f ^ ℓ) : h.frobNorm f = g :=
  φ.toRingHom.injective ((h.map_frobNorm f).trans hg.symm)

theorem frobNorm_map (h : IsFrobeniusEndo ℓ φ) (x : F) : h.frobNorm (φ x) = x ^ ℓ :=
  h.frobNorm_eq_of_map_eq_pow (by rw [map_pow])

theorem frobNorm_one (h : IsFrobeniusEndo ℓ φ) : h.frobNorm 1 = 1 :=
  h.frobNorm_eq_of_map_eq_pow (by rw [map_one, one_pow])

theorem frobNorm_mul (h : IsFrobeniusEndo ℓ φ) (a b : F) :
    h.frobNorm (a * b) = h.frobNorm a * h.frobNorm b :=
  h.frobNorm_eq_of_map_eq_pow (by rw [map_mul, h.map_frobNorm, h.map_frobNorm, mul_pow])

theorem frobNorm_algebraMap (h : IsFrobeniusEndo ℓ φ) (c : K) :
    h.frobNorm (algebraMap K F c) = algebraMap K F c ^ ℓ :=
  h.frobNorm_eq_of_map_eq_pow (by rw [map_pow, φ.commutes])

variable [Fact ℓ.Prime] [CharP F ℓ]

omit [CharP F ℓ] in

theorem frobNorm_zero (h : IsFrobeniusEndo ℓ φ) : h.frobNorm 0 = 0 :=
  h.frobNorm_eq_of_map_eq_pow
    (by rw [map_zero, zero_pow (Fact.out : ℓ.Prime).pos.ne'])

theorem frobNorm_add (h : IsFrobeniusEndo ℓ φ) (a b : F) :
    h.frobNorm (a + b) = h.frobNorm a + h.frobNorm b :=
  h.frobNorm_eq_of_map_eq_pow
    (by rw [map_add, h.map_frobNorm, h.map_frobNorm, add_pow_char])

def frobNormRingHom (h : IsFrobeniusEndo ℓ φ) : F →+* F where
  toFun := h.frobNorm
  map_one' := h.frobNorm_one
  map_mul' := h.frobNorm_mul
  map_zero' := h.frobNorm_zero
  map_add' := h.frobNorm_add

@[simp]
theorem frobNormRingHom_apply (h : IsFrobeniusEndo ℓ φ) (f : F) :
    h.frobNormRingHom f = h.frobNorm f := rfl

theorem frobNormRingHom_isIntegral (h : IsFrobeniusEndo ℓ φ) :
    h.frobNormRingHom.IsIntegral := by
  intro x
  refine ⟨Polynomial.X ^ ℓ - Polynomial.C (φ x),
    Polynomial.monic_X_pow_sub_C _ (Fact.out : ℓ.Prime).pos.ne', ?_⟩
  simp [Polynomial.eval₂_sub, frobNormRingHom_apply, h.frobNorm_map x]

end IsFrobeniusEndo

end FrobNormHom

section ExtendPlace

variable {K F : Type*} [Field K] [Field F] [Algebra K F]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP F ℓ] {φ : F →ₐ[K] F}

namespace IsFrobeniusEndo

variable (h : IsFrobeniusEndo ℓ φ)

def extendPlace (v : Place K F) : Place K F where
  toValuationSubring := v.toValuationSubring.comap h.frobNormRingHom
  algebraMap_mem' a := by
    rw [ValuationSubring.mem_comap, frobNormRingHom_apply, h.frobNorm_algebraMap]
    exact pow_mem (v.algebraMap_mem' a) ℓ
  ne_top' := by

    intro htop
    apply v.ne_top'
    refine SetLike.ext fun z => ⟨fun _ => ValuationSubring.mem_top z, fun _ => ?_⟩
    have hz : φ z ∈ v.toValuationSubring.comap h.frobNormRingHom :=
      htop ▸ ValuationSubring.mem_top (φ z)
    rw [ValuationSubring.mem_comap, frobNormRingHom_apply, h.frobNorm_map] at hz
    exact (v.toValuationSubring.pow_mem_iff (Fact.out : ℓ.Prime).pos.ne' z).mp hz
  isPrincipalIdealRing' := by

    letI : Algebra F F := h.frobNormRingHom.toAlgebra
    haveI : Algebra.IsIntegral F F := ⟨h.frobNormRingHom_isIntegral⟩
    exact v.isPrincipalIdealRing_comap (F := F)

theorem mem_extendPlace_iff (v : Place K F) (y : F) :
    y ∈ (h.extendPlace v).toValuationSubring ↔ h.frobNorm y ∈ v.toValuationSubring :=
  ValuationSubring.mem_comap

theorem restrictAlong_extendPlace (hφ : φ.toRingHom.IsIntegral) (v : Place K F) :
    (h.extendPlace v).restrictAlong φ hφ = v := by
  refine Place.ext (SetLike.ext fun x => ?_)
  rw [ModularCurve.mem_restrictAlong_iff (K := K) φ hφ (h.extendPlace v) x,
    h.mem_extendPlace_iff, h.frobNorm_map]
  exact v.toValuationSubring.pow_mem_iff (Fact.out : ℓ.Prime).pos.ne' x

include h in

theorem restrictAlong_surjective (hφ : φ.toRingHom.IsIntegral) :
    Function.Surjective (Place.restrictAlong φ hφ) := fun v =>
  ⟨h.extendPlace v, h.restrictAlong_extendPlace hφ v⟩

theorem extendPlace_restrictAlong (hℓ : ℓ ≠ 0) (hφ : φ.toRingHom.IsIntegral)
    (w : Place K F) :
    h.extendPlace (w.restrictAlong φ hφ) = w :=
  h.restrictAlong_injective hℓ hφ
    (h.restrictAlong_extendPlace hφ (w.restrictAlong φ hφ))

end IsFrobeniusEndo

end ExtendPlace

end AlgebraicCurve

namespace ModularCurve

open AlgebraicCurve

section LevelN

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

omit [Fact ℓ.Prime] in

theorem charP_modularFunctionFieldC : CharP (modularFunctionFieldC K N) ℓ :=
  charP_of_injective_algebraMap
    (algebraMap K (modularFunctionFieldC K N)).injective ℓ

include data hKr in

def extendPlaceGeomLevel (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (v : Place K (modularFunctionFieldC K N)) : Place K (modularFunctionFieldC K N) :=
  haveI : CharP (modularFunctionFieldC K N) ℓ := charP_modularFunctionFieldC K N
  (isFrobeniusEndo_frobeniusGeomLevel K N data hKr hperf).extendPlace v

include data hKr in

theorem frobOnPlacesGeomLevel_extendPlaceGeomLevel (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (v : Place K (modularFunctionFieldC K N)) :
    frobOnPlacesGeomLevel K N data hKr (extendPlaceGeomLevel K N data hKr hperf v) = v := by
  haveI : CharP (modularFunctionFieldC K N) ℓ := charP_modularFunctionFieldC K N
  rw [extendPlaceGeomLevel, ← restrictAlong_frobeniusGeomLevel_eq K N data hKr hperf]
  exact (isFrobeniusEndo_frobeniusGeomLevel K N data hKr hperf).restrictAlong_extendPlace
    (frobeniusGeomLevel_isIntegral K N data hKr hperf) v

include data hKr in

theorem frobOnPlacesGeomLevel_surjective (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c) :
    Function.Surjective (frobOnPlacesGeomLevel K N data hKr) := fun v =>
extendPlaceGeomLevel K N data hKr hperf v,
    frobOnPlacesGeomLevel_extendPlaceGeomLevel K N data hKr hperf v⟩

include data hKr in

theorem frobOnPlacesGeomLevel_bijective (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c) :
    Function.Bijective (frobOnPlacesGeomLevel K N data hKr) :=
frobOnPlacesGeomLevel_injective K N data hKr,
    frobOnPlacesGeomLevel_surjective K N data hKr hperf⟩

include data hKr in

theorem verOnPlacesGeomLevel_eq_extendPlaceGeomLevel (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (u : Place K (modularFunctionFieldC K N)) :
    verOnPlacesGeomLevel K N data hKr u = extendPlaceGeomLevel K N data hKr hperf u := by
  haveI : CharP (modularFunctionFieldC K N) ℓ := charP_modularFunctionFieldC K N
  obtain ⟨w, rfl⟩ := frobOnPlacesGeomLevel_surjective K N data hKr hperf u
  rw [verOnPlacesGeomLevel_frobOnPlacesGeomLevel K N data hKr w, extendPlaceGeomLevel,
restrictAlong_frobeniusGeomLevel_eq K N data hKr hperf]
  exact ((isFrobeniusEndo_frobeniusGeomLevel K N data hKr
    hperf).extendPlace_restrictAlong (Fact.out : ℓ.Prime).pos.ne'
    (frobeniusGeomLevel_isIntegral K N data hKr hperf) w).symm

end LevelN

section Consumers

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

theorem frobeniusPushforwardGeomLevel_normFormula'
    (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    {f g : modularFunctionFieldC K N}
    (hg : frobeniusGeomLevel K N data hKr g = f ^ ℓ)
    {D : Divisor K (modularFunctionFieldC K N)}
    (hD : ∀ w, D w = w.ord f) (v : Place K (modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevel K N data hKr D v = v.ord g :=
  frobeniusPushforwardGeomLevel_normFormula K N data hKr hperf
    (frobOnPlacesGeomLevel_surjective K N data hKr hperf) hg hD v

include data hKr in

theorem isPrincipal_frobeniusPushforwardGeomLevel'
    (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    {D : Divisor K (modularFunctionFieldC K N)} (hD : D.IsPrincipal) :
    (frobeniusPushforwardGeomLevel K N data hKr D).IsPrincipal :=
  isPrincipal_frobeniusPushforwardGeomLevel K N data hKr hperf
    (frobOnPlacesGeomLevel_surjective K N data hKr hperf) hD

end Consumers

end ModularCurve

noncomputable section

open AlgebraicCurve Polynomial

set_option synthInstance.maxHeartbeats 400000
set_option maxHeartbeats 800000

namespace ModularCurve

section PlaceLevel

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

theorem frobOnPlacesGeomLevel_verOnPlacesGeomLevel
    (hsurj : Function.Surjective (frobOnPlacesGeomLevel K N data hKr))
    (v : Place K (modularFunctionFieldC K N)) :
    frobOnPlacesGeomLevel K N data hKr (verOnPlacesGeomLevel K N data hKr v) = v := by
  obtain ⟨u, rfl⟩ := hsurj v
  rw [verOnPlacesGeomLevel_frobOnPlacesGeomLevel K N data hKr u]

include data hKr in

theorem verOnPlacesGeomLevel_eq_iff
    (hsurj : Function.Surjective (frobOnPlacesGeomLevel K N data hKr))
    {v w : Place K (modularFunctionFieldC K N)} :
    verOnPlacesGeomLevel K N data hKr v = w ↔ v = frobOnPlacesGeomLevel K N data hKr w := by
  constructor
  · rintro rfl
    rw [frobOnPlacesGeomLevel_verOnPlacesGeomLevel K N data hKr hsurj v]
  · rintro rfl
    rw [verOnPlacesGeomLevel_frobOnPlacesGeomLevel K N data hKr w]

end PlaceLevel

section Evaluation

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

theorem frobeniusPullbackGeomLevel_apply
    (hsurj : Function.Surjective (frobOnPlacesGeomLevel K N data hKr))
    (D : Divisor K (modularFunctionFieldC K N))
    (w : Place K (modularFunctionFieldC K N)) :
    frobeniusPullbackGeomLevel K N data hKr D w
      = D (frobOnPlacesGeomLevel K N data hKr w) * ℓ := by
  induction D using Finsupp.induction with
  | zero => simp
  | single_add v n D _ _ ih =>
    rw [map_add, Finsupp.add_apply, ih, frobeniusPullbackGeomLevel_single,
      Finsupp.add_apply, add_mul]
    congr 1
    by_cases hvw : v = frobOnPlacesGeomLevel K N data hKr w
    · subst hvw
      rw [verOnPlacesGeomLevel_frobOnPlacesGeomLevel K N data hKr w,
        Finsupp.single_eq_same, Finsupp.single_eq_same]
    · have hne : verOnPlacesGeomLevel K N data hKr v ≠ w :=
        fun h => hvw ((verOnPlacesGeomLevel_eq_iff K N data hKr hsurj).mp h)
      rw [Finsupp.single_eq_of_ne' hne, Finsupp.single_eq_of_ne' hvw, zero_mul]

end Evaluation

section Principal

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

include data hKr in

theorem frobeniusPullbackGeomLevel_pullbackFormula
    (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (hsurj : Function.Surjective (frobOnPlacesGeomLevel K N data hKr))
    {f : modularFunctionFieldC K N}
    {D : Divisor K (modularFunctionFieldC K N)}
    (hD : ∀ w, D w = w.ord f) (w : Place K (modularFunctionFieldC K N)) :
    frobeniusPullbackGeomLevel K N data hKr D w
      = w.ord (frobeniusGeomLevel K N data hKr f) := by
  rw [frobeniusPullbackGeomLevel_apply K N data hKr hsurj D w,
    hD (frobOnPlacesGeomLevel K N data hKr w),
    ord_frobeniusGeomLevel_eq_mul K N data hKr hperf w f, mul_comm]

include data hKr in

theorem isPrincipal_frobeniusPullbackGeomLevel'
    (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (hsurj : Function.Surjective (frobOnPlacesGeomLevel K N data hKr))
    {D : Divisor K (modularFunctionFieldC K N)} (hD : D.IsPrincipal) :
    (frobeniusPullbackGeomLevel K N data hKr D).IsPrincipal := by
  obtain ⟨f, hf, hDf⟩ := hD
  refine ⟨frobeniusGeomLevel K N data hKr f, ?_,
    fun w => frobeniusPullbackGeomLevel_pullbackFormula K N data hKr hperf hsurj hDf w⟩
  intro h0
  exact hf (frobeniusGeomLevel_injective K N data hKr (h0.trans (map_zero _).symm))

include data hKr in

theorem isPrincipal_heckeFibreGeomLevel'
    (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
    (hsurj : Function.Surjective (frobOnPlacesGeomLevel K N data hKr))
    {D : Divisor K (modularFunctionFieldC K N)} (hD : D.IsPrincipal) :
    (heckeFibreGeomLevel K N data hKr D).IsPrincipal := by
  have hpush := isPrincipal_frobeniusPushforwardGeomLevel' K N data hKr hperf hD
  have hpull := isPrincipal_frobeniusPullbackGeomLevel' K N data hKr hperf hsurj hD
  obtain ⟨g, hg, hDg⟩ := hpush
  obtain ⟨h, hh, hDh⟩ := hpull
  refine ⟨g * h, mul_ne_zero hg hh, fun v => ?_⟩
  have key : heckeFibreGeomLevel K N data hKr D
      = frobeniusPushforwardGeomLevel K N data hKr D
        + frobeniusPullbackGeomLevel K N data hKr D := rfl
  rw [key, Finsupp.add_apply, hDg v, hDh v, v.ord_mul hg hh]

end Principal

end ModularCurve

noncomputable section

open AlgebraicCurve

set_option synthInstance.maxHeartbeats 400000
set_option maxHeartbeats 800000

namespace ModularCurve

section DegreePlace

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)
variable (hdeg1 : ∀ w : Place K (modularFunctionFieldC K N), w.deg = 1)

include data hKr hdeg1 in

theorem deg_frobOnPlacesGeomLevel (w : Place K (modularFunctionFieldC K N)) :
    (frobOnPlacesGeomLevel K N data hKr w).deg = w.deg := by
  rw [hdeg1 (frobOnPlacesGeomLevel K N data hKr w), hdeg1 w]

include data hKr hdeg1 in

theorem deg_verOnPlacesGeomLevel (w : Place K (modularFunctionFieldC K N)) :
    (verOnPlacesGeomLevel K N data hKr w).deg = w.deg := by
  rw [hdeg1 (verOnPlacesGeomLevel K N data hKr w), hdeg1 w]

end DegreePlace

section DegreeDivisor

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)
variable (hdeg1 : ∀ w : Place K (modularFunctionFieldC K N), w.deg = 1)

include data hKr hdeg1 in

theorem degree_frobeniusPushforwardGeomLevel (D : Divisor K (modularFunctionFieldC K N)) :
    Divisor.degree (frobeniusPushforwardGeomLevel K N data hKr D) = Divisor.degree D := by
  induction D using Finsupp.induction with
  | zero => simp
  | single_add v n D _ _ ih =>
    rw [map_add, map_add, ih, frobeniusPushforwardGeomLevel_single, map_add,
      Divisor.degree_single, Divisor.degree_single,
      deg_frobOnPlacesGeomLevel K N data hKr hdeg1 v]

include data hKr hdeg1 in

theorem degree_frobeniusPullbackGeomLevel (D : Divisor K (modularFunctionFieldC K N)) :
    Divisor.degree (frobeniusPullbackGeomLevel K N data hKr D) = ℓ * Divisor.degree D := by
  induction D using Finsupp.induction with
  | zero => simp
  | single_add v n D _ _ ih =>
    rw [map_add, map_add, ih, frobeniusPullbackGeomLevel_single, map_add,
      Divisor.degree_single, Divisor.degree_single,
      deg_verOnPlacesGeomLevel K N data hKr hdeg1 v, mul_add]
    ring

include data hKr hdeg1 in

theorem degree_heckeFibreGeomLevel (D : Divisor K (modularFunctionFieldC K N)) :
    Divisor.degree (heckeFibreGeomLevel K N data hKr D) = (ℓ + 1) * Divisor.degree D := by
  rw [show heckeFibreGeomLevel K N data hKr D
      = frobeniusPushforwardGeomLevel K N data hKr D
        + frobeniusPullbackGeomLevel K N data hKr D from rfl,
    map_add, degree_frobeniusPushforwardGeomLevel K N data hKr hdeg1 D,
    degree_frobeniusPullbackGeomLevel K N data hKr hdeg1 D]
  ring

include data hKr hdeg1 in

theorem frobeniusPushforwardGeomLevel_mem_degZero {D : Divisor K (modularFunctionFieldC K N)}
    (hD : D ∈ Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevel K N data hKr D ∈
      Divisor.degZero (K := K) (F := modularFunctionFieldC K N) := by
  rw [Divisor.mem_degZero] at hD ⊢
  rw [degree_frobeniusPushforwardGeomLevel K N data hKr hdeg1 D, hD]

include data hKr hdeg1 in

theorem frobeniusPullbackGeomLevel_mem_degZero {D : Divisor K (modularFunctionFieldC K N)}
    (hD : D ∈ Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    frobeniusPullbackGeomLevel K N data hKr D ∈
      Divisor.degZero (K := K) (F := modularFunctionFieldC K N) := by
  rw [Divisor.mem_degZero] at hD ⊢
  rw [degree_frobeniusPullbackGeomLevel K N data hKr hdeg1 D, hD, mul_zero]

include data hKr hdeg1 in

theorem heckeFibreGeomLevel_mem_degZero {D : Divisor K (modularFunctionFieldC K N)}
    (hD : D ∈ Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    heckeFibreGeomLevel K N data hKr D ∈
      Divisor.degZero (K := K) (F := modularFunctionFieldC K N) := by
  rw [Divisor.mem_degZero] at hD ⊢
  rw [degree_heckeFibreGeomLevel K N data hKr hdeg1 D, hD, mul_zero]

include data hKr hdeg1 in

def frobeniusPushforwardGeomLevelDegZero :
    Divisor.degZero (K := K) (F := modularFunctionFieldC K N)
      →+ Divisor.degZero (K := K) (F := modularFunctionFieldC K N) :=
  ((frobeniusPushforwardGeomLevel K N data hKr).domRestrict
    (Divisor.degZero (K := K) (F := modularFunctionFieldC K N))).codRestrict _
    (fun D => frobeniusPushforwardGeomLevel_mem_degZero K N data hKr hdeg1 D.2)

@[simp]
theorem coe_frobeniusPushforwardGeomLevelDegZero
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    (frobeniusPushforwardGeomLevelDegZero K N data hKr hdeg1 D
        : Divisor K (modularFunctionFieldC K N))
      = frobeniusPushforwardGeomLevel K N data hKr (D : Divisor K (modularFunctionFieldC K N)) :=
  rfl

include data hKr hdeg1 in

def frobeniusPullbackGeomLevelDegZero :
    Divisor.degZero (K := K) (F := modularFunctionFieldC K N)
      →+ Divisor.degZero (K := K) (F := modularFunctionFieldC K N) :=
  ((frobeniusPullbackGeomLevel K N data hKr).domRestrict
    (Divisor.degZero (K := K) (F := modularFunctionFieldC K N))).codRestrict _
    (fun D => frobeniusPullbackGeomLevel_mem_degZero K N data hKr hdeg1 D.2)

@[simp]
theorem coe_frobeniusPullbackGeomLevelDegZero
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    (frobeniusPullbackGeomLevelDegZero K N data hKr hdeg1 D
        : Divisor K (modularFunctionFieldC K N))
      = frobeniusPullbackGeomLevel K N data hKr (D : Divisor K (modularFunctionFieldC K N)) :=
  rfl

include data hKr hdeg1 in

def heckeFibreGeomLevelDegZero :
    Divisor.degZero (K := K) (F := modularFunctionFieldC K N)
      →+ Divisor.degZero (K := K) (F := modularFunctionFieldC K N) :=
  ((heckeFibreGeomLevel K N data hKr).domRestrict
    (Divisor.degZero (K := K) (F := modularFunctionFieldC K N))).codRestrict _
    (fun D => heckeFibreGeomLevel_mem_degZero K N data hKr hdeg1 D.2)

@[simp]
theorem coe_heckeFibreGeomLevelDegZero
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    (heckeFibreGeomLevelDegZero K N data hKr hdeg1 D
        : Divisor K (modularFunctionFieldC K N))
      = heckeFibreGeomLevel K N data hKr (D : Divisor K (modularFunctionFieldC K N)) :=
  rfl

include data hKr hdeg1 in

theorem heckeFibreGeomLevelDegZero_eq_add
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    heckeFibreGeomLevelDegZero K N data hKr hdeg1 D
      = frobeniusPushforwardGeomLevelDegZero K N data hKr hdeg1 D
        + frobeniusPullbackGeomLevelDegZero K N data hKr hdeg1 D :=
  Subtype.ext rfl

end DegreeDivisor

section Pic0Ops

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)
variable (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
variable (hdeg1 : ∀ w : Place K (modularFunctionFieldC K N), w.deg = 1)

include data hKr hperf hdeg1 in

def frobeniusPushforwardGeomLevelPic0 :
    Pic0 K (modularFunctionFieldC K N) →+ Pic0 K (modularFunctionFieldC K N) :=
  QuotientAddGroup.map _ _ (frobeniusPushforwardGeomLevelDegZero K N data hKr hdeg1) (by
    rintro ⟨D, hD0⟩ hD
    simp only [AddSubgroup.mem_addSubgroupOf] at hD ⊢
    exact isPrincipal_frobeniusPushforwardGeomLevel' K N data hKr hperf hD)

@[simp]
theorem frobeniusPushforwardGeomLevelPic0_mk
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevelPic0 K N data hKr hperf hdeg1 (Pic0.mk D)
      = Pic0.mk (frobeniusPushforwardGeomLevelDegZero K N data hKr hdeg1 D) :=
  rfl

include data hKr hperf hdeg1 in

def frobeniusPullbackGeomLevelPic0 :
    Pic0 K (modularFunctionFieldC K N) →+ Pic0 K (modularFunctionFieldC K N) :=
  QuotientAddGroup.map _ _ (frobeniusPullbackGeomLevelDegZero K N data hKr hdeg1) (by
    rintro ⟨D, hD0⟩ hD
    simp only [AddSubgroup.mem_addSubgroupOf] at hD ⊢
    exact isPrincipal_frobeniusPullbackGeomLevel' K N data hKr hperf
      (frobOnPlacesGeomLevel_surjective K N data hKr hperf) hD)

@[simp]
theorem frobeniusPullbackGeomLevelPic0_mk
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    frobeniusPullbackGeomLevelPic0 K N data hKr hperf hdeg1 (Pic0.mk D)
      = Pic0.mk (frobeniusPullbackGeomLevelDegZero K N data hKr hdeg1 D) :=
  rfl

include data hKr hperf hdeg1 in

def heckeFibreGeomLevelPic0 :
    Pic0 K (modularFunctionFieldC K N) →+ Pic0 K (modularFunctionFieldC K N) :=
  QuotientAddGroup.map _ _ (heckeFibreGeomLevelDegZero K N data hKr hdeg1) (by
    rintro ⟨D, hD0⟩ hD
    simp only [AddSubgroup.mem_addSubgroupOf] at hD ⊢
    exact isPrincipal_heckeFibreGeomLevel' K N data hKr hperf
      (frobOnPlacesGeomLevel_surjective K N data hKr hperf) hD)

@[simp]
theorem heckeFibreGeomLevelPic0_mk
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    heckeFibreGeomLevelPic0 K N data hKr hperf hdeg1 (Pic0.mk D)
      = Pic0.mk (heckeFibreGeomLevelDegZero K N data hKr hdeg1 D) :=
  rfl

include data hKr hperf hdeg1 in

theorem heckeFibreGeomLevelPic0_eq_add (x : Pic0 K (modularFunctionFieldC K N)) :
    heckeFibreGeomLevelPic0 K N data hKr hperf hdeg1 x
      = frobeniusPushforwardGeomLevelPic0 K N data hKr hperf hdeg1 x
        + frobeniusPullbackGeomLevelPic0 K N data hKr hperf hdeg1 x := by
  obtain ⟨D, rfl⟩ := Pic0.mk_surjective x
  rw [heckeFibreGeomLevelPic0_mk, frobeniusPushforwardGeomLevelPic0_mk,
    frobeniusPullbackGeomLevelPic0_mk, ← Pic0.mk_add,
    heckeFibreGeomLevelDegZero_eq_add K N data hKr hdeg1 D]

include data hKr hperf hdeg1 in

theorem frobeniusPullbackGeomLevelPic0_frobeniusPushforwardGeomLevelPic0
    (x : Pic0 K (modularFunctionFieldC K N)) :
    frobeniusPullbackGeomLevelPic0 K N data hKr hperf hdeg1
        (frobeniusPushforwardGeomLevelPic0 K N data hKr hperf hdeg1 x)
      = (ℓ : ℤ) • x := by
  obtain ⟨D, rfl⟩ := Pic0.mk_surjective x
  rw [frobeniusPushforwardGeomLevelPic0_mk, frobeniusPullbackGeomLevelPic0_mk,
    show ((ℓ : ℤ) • Pic0.mk D : Pic0 K (modularFunctionFieldC K N))
      = Pic0.mk ((ℓ : ℤ) • D) from rfl]
  refine congrArg Pic0.mk (Subtype.ext ?_)
  exact frobeniusPullbackGeomLevel_frobeniusPushforwardGeomLevel K N data hKr
    (D : Divisor K (modularFunctionFieldC K N))

end Pic0Ops

section Relation

variable (K : Type*) [Field K] (N : ℕ) [NeZero N]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)
variable (hperf : ∀ c : K, ∃ d : K, d ^ ℓ = c)
variable (hdeg1 : ∀ w : Place K (modularFunctionFieldC K N), w.deg = 1)

include data hKr hperf hdeg1 in

theorem eichlerShimura_special_fibre_pic0_level (x : Pic0 K (modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevelPic0 K N data hKr hperf hdeg1
        (frobeniusPushforwardGeomLevelPic0 K N data hKr hperf hdeg1 x)
      - heckeFibreGeomLevelPic0 K N data hKr hperf hdeg1
          (frobeniusPushforwardGeomLevelPic0 K N data hKr hperf hdeg1 x)
      + (ℓ : ℤ) • x = 0 := by
  obtain ⟨D, rfl⟩ := Pic0.mk_surjective x
  rw [frobeniusPushforwardGeomLevelPic0_mk, frobeniusPushforwardGeomLevelPic0_mk,
    heckeFibreGeomLevelPic0_mk,
    show ((ℓ : ℤ) • Pic0.mk D : Pic0 K (modularFunctionFieldC K N))
      = Pic0.mk ((ℓ : ℤ) • D) from rfl,
    show (Pic0.mk _ - Pic0.mk _ + Pic0.mk ((ℓ : ℤ) • D) : Pic0 K (modularFunctionFieldC K N))
      = Pic0.mk (frobeniusPushforwardGeomLevelDegZero K N data hKr hdeg1
          (frobeniusPushforwardGeomLevelDegZero K N data hKr hdeg1 D)
        - heckeFibreGeomLevelDegZero K N data hKr hdeg1
            (frobeniusPushforwardGeomLevelDegZero K N data hKr hdeg1 D)
        + (ℓ : ℤ) • D) from rfl]
  refine congrArg Pic0.mk (Subtype.ext ?_)
  push_cast
  exact eichlerShimura_special_fibre_geom_level K N data hKr
    (D : Divisor K (modularFunctionFieldC K N))

end Relation

end ModularCurve

noncomputable section

open AlgebraicCurve

set_option synthInstance.maxHeartbeats 400000
set_option maxHeartbeats 800000

namespace ModularCurve

variable (K : Type*) [Field K]

theorem perfect_of_isAlgClosed [IsAlgClosed K] {ℓ : ℕ} [hℓ : Fact ℓ.Prime] :
    ∀ c : K, ∃ d : K, d ^ ℓ = c :=
  fun c => IsAlgClosed.exists_pow_nat_eq c hℓ.out.pos

variable (N : ℕ) [NeZero N]
variable [IsAlgClosed K] [IsCurveOver K (modularFunctionFieldC K N)]
variable {ℓ : ℕ} [Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

def frobeniusPushforwardGeomLevelPic0OfIsCurveOver :
    Pic0 K (modularFunctionFieldC K N) →+ Pic0 K (modularFunctionFieldC K N) :=
  frobeniusPushforwardGeomLevelPic0 K N data hKr (perfect_of_isAlgClosed K)
    (deg_eq_one_modularFunctionFieldC K N)

def frobeniusPullbackGeomLevelPic0OfIsCurveOver :
    Pic0 K (modularFunctionFieldC K N) →+ Pic0 K (modularFunctionFieldC K N) :=
  frobeniusPullbackGeomLevelPic0 K N data hKr (perfect_of_isAlgClosed K)
    (deg_eq_one_modularFunctionFieldC K N)

def heckeFibreGeomLevelPic0OfIsCurveOver :
    Pic0 K (modularFunctionFieldC K N) →+ Pic0 K (modularFunctionFieldC K N) :=
  heckeFibreGeomLevelPic0 K N data hKr (perfect_of_isAlgClosed K)
    (deg_eq_one_modularFunctionFieldC K N)

theorem frobeniusPushforwardGeomLevelPic0OfIsCurveOver_eq :
    frobeniusPushforwardGeomLevelPic0OfIsCurveOver K N data hKr
      = frobeniusPushforwardGeomLevelPic0 K N data hKr (perfect_of_isAlgClosed K)
          (deg_eq_one_modularFunctionFieldC K N) :=
  rfl

theorem heckeFibreGeomLevelPic0OfIsCurveOver_eq :
    heckeFibreGeomLevelPic0OfIsCurveOver K N data hKr
      = heckeFibreGeomLevelPic0 K N data hKr (perfect_of_isAlgClosed K)
          (deg_eq_one_modularFunctionFieldC K N) :=
  rfl

@[simp]
theorem frobeniusPushforwardGeomLevelPic0OfIsCurveOver_mk
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevelPic0OfIsCurveOver K N data hKr (Pic0.mk D)
      = Pic0.mk (frobeniusPushforwardGeomLevelDegZero K N data hKr
          (deg_eq_one_modularFunctionFieldC K N) D) :=
  rfl

@[simp]
theorem frobeniusPullbackGeomLevelPic0OfIsCurveOver_mk
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    frobeniusPullbackGeomLevelPic0OfIsCurveOver K N data hKr (Pic0.mk D)
      = Pic0.mk (frobeniusPullbackGeomLevelDegZero K N data hKr
          (deg_eq_one_modularFunctionFieldC K N) D) :=
  rfl

@[simp]
theorem heckeFibreGeomLevelPic0OfIsCurveOver_mk
    (D : Divisor.degZero (K := K) (F := modularFunctionFieldC K N)) :
    heckeFibreGeomLevelPic0OfIsCurveOver K N data hKr (Pic0.mk D)
      = Pic0.mk (heckeFibreGeomLevelDegZero K N data hKr
          (deg_eq_one_modularFunctionFieldC K N) D) :=
  rfl

theorem heckeFibreGeomLevelPic0OfIsCurveOver_eq_add
    (x : Pic0 K (modularFunctionFieldC K N)) :
    heckeFibreGeomLevelPic0OfIsCurveOver K N data hKr x
      = frobeniusPushforwardGeomLevelPic0OfIsCurveOver K N data hKr x
        + frobeniusPullbackGeomLevelPic0OfIsCurveOver K N data hKr x :=
  heckeFibreGeomLevelPic0_eq_add K N data hKr (perfect_of_isAlgClosed K)
    (deg_eq_one_modularFunctionFieldC K N) x

theorem frobeniusPullbackPushforwardGeomLevelPic0OfIsCurveOver
    (x : Pic0 K (modularFunctionFieldC K N)) :
    frobeniusPullbackGeomLevelPic0OfIsCurveOver K N data hKr
        (frobeniusPushforwardGeomLevelPic0OfIsCurveOver K N data hKr x)
      = (ℓ : ℤ) • x :=
  frobeniusPullbackGeomLevelPic0_frobeniusPushforwardGeomLevelPic0 K N data hKr
    (perfect_of_isAlgClosed K) (deg_eq_one_modularFunctionFieldC K N) x

theorem eichlerShimura_special_fibre_pic0_level_of_isCurveOver
    (x : Pic0 K (modularFunctionFieldC K N)) :
    frobeniusPushforwardGeomLevelPic0OfIsCurveOver K N data hKr
        (frobeniusPushforwardGeomLevelPic0OfIsCurveOver K N data hKr x)
      - heckeFibreGeomLevelPic0OfIsCurveOver K N data hKr
          (frobeniusPushforwardGeomLevelPic0OfIsCurveOver K N data hKr x)
      + (ℓ : ℤ) • x = 0 :=
  eichlerShimura_special_fibre_pic0_level K N data hKr (perfect_of_isAlgClosed K)
    (deg_eq_one_modularFunctionFieldC K N) x

end ModularCurve

namespace ModularCurve
open AlgebraicCurve
section FamilyProps

variable (K : Type*) [Field K]

def IsCurveOverModularFunctionFieldCFamily : Prop :=
  ∀ (N : ℕ), [NeZero N] → IsCurveOver K (modularFunctionFieldC K N)

theorem isCurveOverModularFunctionFieldCFamily_iff :
    IsCurveOverModularFunctionFieldCFamily K
      ↔ ∀ (N : ℕ), [NeZero N] → IsCurveOver K (modularFunctionFieldC K N) :=
  Iff.rfl

theorem isCurveOver_modularFunctionFieldC_of_family
    (hfam : IsCurveOverModularFunctionFieldCFamily K) (N : ℕ) [NeZero N] :
    IsCurveOver K (modularFunctionFieldC K N) :=
  hfam N

variable (N : ℕ) [NeZero N]
variable [IsAlgClosed K] [IsCurveOver K (modularFunctionFieldC K N)]
variable {ℓ : ℕ} [hℓ : Fact ℓ.Prime] [CharP K ℓ]
variable (data : ModularPolynomialData ℓ) (hKr : KroneckerCongruence ℓ data)

def SpecialFibreHeckeModuleMatch
    (inst : Module HeckeAlg (Pic0 K (modularFunctionFieldC K N))) : Prop :=
  ∀ x : Pic0 K (modularFunctionFieldC K N),
    letI := inst
    heckeGen ⟨ℓ, hℓ.out⟩ • x = heckeFibreGeomLevelPic0OfIsCurveOver K N data hKr x

theorem specialFibreHeckeModuleMatch_iff
    (inst : Module HeckeAlg (Pic0 K (modularFunctionFieldC K N))) :
    SpecialFibreHeckeModuleMatch K N data hKr inst
      ↔ ∀ x, (letI := inst; heckeGen ⟨ℓ, hℓ.out⟩ • x)
          = heckeFibreGeomLevelPic0OfIsCurveOver K N data hKr x :=
  Iff.rfl

end FamilyProps
end ModularCurve

Statements phrased using this module (20)