import Mathlib
import Definitions.Def_ModularCurve_KatzLevelP
import Definitions.Def_ModularCurve_KatzLevelPUniversal
import Definitions.Def_ModularCurve_KatzLevelPClassifyingMaps

set_option autoImplicit false

universe u v

noncomputable section

open Polynomial WeierstrassCurve
open scoped TensorProduct

namespace ModularCurve

namespace LevelP

section Yoneda

variable (p : ℕ) (S : Type u) [CommRing S] {A : Type u} [CommRing A] [Algebra S A]

def sClassify (hpA : IsUnit (p : A)) (W : WeierstrassCurve A) (hW : IsUnit W.Δ) (D : LevelPData A)
    (hD : IsLevelPStructure W p D) : S ⊗[ℤ] UnivBasisRing p →ₐ[S] A :=
  Algebra.TensorProduct.lift (Algebra.ofId S A)
    (UnivBasisRing.classify p W (hpA.mul hW) D hD).toIntAlgHom (fun _ _ => Commute.all _ _)

variable (hpA : IsUnit (p : A)) (W : WeierstrassCurve A) (hW : IsUnit W.Δ) (D : LevelPData A)
  (hD : IsLevelPStructure W p D)

@[simp] theorem sClassify_tmul (s : S) (t : UnivBasisRing p) :
    sClassify p S hpA W hW D hD (s ⊗ₜ t) =
      algebraMap S A s * UnivBasisRing.classify p W (hpA.mul hW) D hD t :=
  Algebra.TensorProduct.lift_tmul _ _ _ _ _

theorem sClassify_comp_univInclude :
    (sClassify p S hpA W hW D hD : S ⊗[ℤ] UnivBasisRing p →+* A).comp
        (KatzLevelPForm.univInclude S p) =
      UnivBasisRing.classify p W (hpA.mul hW) D hD := by
  ext t
  change sClassify p S hpA W hW D hD ((1 : S) ⊗ₜ t) = _
  rw [sClassify_tmul, map_one, one_mul]

theorem univCurveOver_map_sClassify :
    (KatzLevelPForm.univCurveOver S p).map
        (sClassify p S hpA W hW D hD : S ⊗[ℤ] UnivBasisRing p →+* A) = W := by
  rw [KatzLevelPForm.univCurveOver, WeierstrassCurve.map_map, sClassify_comp_univInclude,
    univCurveT_map_classify]

theorem univDataOver_map_sClassify :
    (KatzLevelPForm.univDataOver S p).map
        (sClassify p S hpA W hW D hD : S ⊗[ℤ] UnivBasisRing p →+* A) = D := by
  rw [KatzLevelPForm.univDataOver, LevelPData.map_map, sClassify_comp_univInclude,
    univData_map_classify]

theorem sAlgHom_ext {φ ψ : S ⊗[ℤ] UnivBasisRing p →ₐ[S] A}
    (hW : (KatzLevelPForm.univCurveOver S p).map (φ : S ⊗[ℤ] UnivBasisRing p →+* A) =
      (KatzLevelPForm.univCurveOver S p).map (ψ : S ⊗[ℤ] UnivBasisRing p →+* A))
    (hD : (KatzLevelPForm.univDataOver S p).map (φ : S ⊗[ℤ] UnivBasisRing p →+* A) =
      (KatzLevelPForm.univDataOver S p).map (ψ : S ⊗[ℤ] UnivBasisRing p →+* A)) : φ = ψ := by
  have h : (φ : S ⊗[ℤ] UnivBasisRing p →+* A).comp (KatzLevelPForm.univInclude S p) =
      (ψ : S ⊗[ℤ] UnivBasisRing p →+* A).comp (KatzLevelPForm.univInclude S p) := by
    refine UnivBasisRing.ringHom_ext p ?_ ?_
    · simpa only [KatzLevelPForm.univCurveOver, WeierstrassCurve.map_map] using hW
    · simpa only [KatzLevelPForm.univDataOver, LevelPData.map_map] using hD
  refine Algebra.TensorProduct.ext' fun s t => ?_
  have hs : s ⊗ₜ[ℤ] t = s • ((1 : S) ⊗ₜ[ℤ] t) := by
    rw [TensorProduct.smul_tmul', smul_eq_mul, mul_one]
  rw [hs, map_smul, map_smul]
  congr 1
  exact RingHom.congr_fun h t

theorem eq_sClassify {φ : S ⊗[ℤ] UnivBasisRing p →ₐ[S] A}
    (hφW : (KatzLevelPForm.univCurveOver S p).map (φ : S ⊗[ℤ] UnivBasisRing p →+* A) = W)
    (hφD : (KatzLevelPForm.univDataOver S p).map (φ : S ⊗[ℤ] UnivBasisRing p →+* A) = D) :
    φ = sClassify p S hpA W hW D hD :=
  sAlgHom_ext p S (by rw [hφW, univCurveOver_map_sClassify])
    (by rw [hφD, univDataOver_map_sClassify])

theorem sClassify_self (hpS : IsUnit (p : S ⊗[ℤ] UnivBasisRing p)) :
    sClassify p S hpS (KatzLevelPForm.univCurveOver S p) (KatzLevelPForm.isUnit_Δ_univCurveOver S p)
      (KatzLevelPForm.univDataOver S p) (KatzLevelPForm.isLevelPStructure_univDataOver S p) =
      AlgHom.id S _ :=
  (eq_sClassify p S _ _ _ _ _ (by exact WeierstrassCurve.map_id _)
    (by exact LevelPData.map_id _)).symm

theorem comp_sClassify {A' : Type u} [CommRing A'] [Algebra S A'] (f : A →ₐ[S] A')
    (hpA' : IsUnit (p : A')) (hW' : IsUnit (W.map (f : A →+* A')).Δ)
    (hD' : IsLevelPStructure (W.map (f : A →+* A')) p (D.map (f : A →+* A'))) :
    f.comp (sClassify p S hpA W hW D hD) =
      sClassify p S hpA' (W.map (f : A →+* A')) hW' (D.map (f : A →+* A')) hD' :=
  eq_sClassify p S _ _ _ _ _
    (by rw [AlgHom.comp_toRingHom, ← WeierstrassCurve.map_map, univCurveOver_map_sClassify])
    (by rw [AlgHom.comp_toRingHom, ← LevelPData.map_map, univDataOver_map_sClassify])

end Yoneda

end LevelP

namespace KatzLevelPForm

open LevelP

variable {p : ℕ} {S : Type u} [CommRing S] {k : ℤ}

theorem toFun_congr (G : KatzLevelPForm S p k) {A : Type u} [CommRing A] [Algebra S A]
    {W W' : WeierstrassCurve A} (hW : IsUnit W.Δ) (hW' : IsUnit W'.Δ) {D D' : LevelPData A}
    (hD : IsLevelPStructure W p D) (hD' : IsLevelPStructure W' p D') (e : W = W') (e' : D = D') :
    G.toFun W hW D hD = G.toFun W' hW' D' hD' := by
  subst e e'
  rfl

theorem map_toFun' (G : KatzLevelPForm S p k) {A B : Type u} [CommRing A] [Algebra S A]
    [CommRing B] [Algebra S B] (f : A →ₐ[S] B) (W : WeierstrassCurve A) (hW : IsUnit W.Δ)
    (D : LevelPData A) (hD : IsLevelPStructure W p D) :
    f (G.toFun W hW D hD) = G.toFun (W.map (f : A →+* B)) (KatzModularForm.isUnit_Δ_map _ hW)
      (D.map (f : A →+* B)) (hD.map _) :=
  (G.map_toFun f W hW _ D hD _).symm

theorem toFun_eq_sClassify_evalUniv (G : KatzLevelPForm S p k) {A : Type u} [CommRing A]
    [Algebra S A] (hpA : IsUnit (p : A)) (W : WeierstrassCurve A) (hW : IsUnit W.Δ)
    (D : LevelPData A) (hD : IsLevelPStructure W p D) :
    G.toFun W hW D hD = sClassify p S hpA W hW D hD G.evalUniv := by
  rw [KatzLevelPForm.evalUniv, map_toFun' G (sClassify p S hpA W hW D hD)]
  exact (toFun_congr G _ _ _ _ (univCurveOver_map_sClassify p S hpA W hW D hD)
    (univDataOver_map_sClassify p S hpA W hW D hD)).symm

end KatzLevelPForm

namespace LevelP

section Forms

variable (p : ℕ)

theorem isUnit_pΔ_vcCurve' : IsUnit ((p : VCRing p) * (univVC p • vcCurve p).Δ) := by
  refine (isUnit_natCast_vcRing p).mul ?_
  rw [variableChange_Δ]
  exact ((Units.isUnit _).pow 12).mul (isUnit_Δ_vcCurve p)

variable (hV : IsLevelPStructure (univVC p • vcCurve p) p ((vcData p).variableChange (univVC p)))

def vcClassify : UnivBasisRing p →+* VCRing p :=
  UnivBasisRing.classify p (univVC p • vcCurve p) (isUnit_pΔ_vcCurve' p) _ hV

theorem univCurveT_map_vcClassify : (univCurveT p).map (vcClassify p hV) = univVC p • vcCurve p :=
  univCurveT_map_classify p _ _ _ _

theorem univData_map_vcClassify :
    (univData p).map (vcClassify p hV) = (vcData p).variableChange (univVC p) :=
  univData_map_classify p _ _ _ _

def vcForm (k : ℤ) : UnivBasisRing p →ₗ[ℤ] VCRing p where
  toFun t := vcClassify p hV t - (((univVC p).u⁻¹ ^ k : (VCRing p)ˣ) : VCRing p) * VCRing.ofUniv p t
  map_add' x y := by simp only [map_add]; ring
  map_smul' n x := by
    simp only [zsmul_eq_mul, eq_intCast, Int.cast_id, map_mul, map_intCast]; ring

theorem vcForm_apply (k : ℤ) (t : UnivBasisRing p) :
    vcForm p hV k t =
      vcClassify p hV t - (((univVC p).u⁻¹ ^ k : (VCRing p)ˣ) : VCRing p) * VCRing.ofUniv p t :=
  rfl

theorem isUnit_pΔ_borelCurve (a : ℕ) : IsUnit ((p : BorelRing p a) * (borelCurve p a).Δ) :=
  (isUnit_natCast_borelRing p a).mul (isUnit_Δ_borelCurve p a)

variable {a : ℕ} (hB : IsLevelPStructure (borelCurve p a) p (borelData' p a))

def borelClassify : UnivBasisRing p →+* BorelRing p a :=
  UnivBasisRing.classify p (borelCurve p a) (isUnit_pΔ_borelCurve p a) (borelData' p a) hB

theorem univCurveT_map_borelClassify :
    (univCurveT p).map (borelClassify p hB) = borelCurve p a :=
  univCurveT_map_classify p _ _ _ _

theorem univData_map_borelClassify : (univData p).map (borelClassify p hB) = borelData' p a :=
  univData_map_classify p _ _ _ _

def borelForm : UnivBasisRing p →ₗ[ℤ] BorelRing p a where
  toFun t := borelClassify p hB t - BorelRing.ofUniv p a t
  map_add' x y := by simp only [map_add]; ring
  map_smul' n x := by
    simp only [zsmul_eq_mul, eq_intCast, Int.cast_id, map_mul, map_intCast]; ring

theorem borelForm_apply (t : UnivBasisRing p) :
    borelForm p hB t = borelClassify p hB t - BorelRing.ofUniv p a t := rfl

end Forms

section Targets

variable (p : ℕ) (S : Type u) [CommRing S] {A : Type u} [CommRing A] [Algebra S A]
  (hpA : IsUnit (p : A))

def sLift {N : Type} [CommRing N] (θ : N →+* A) : S ⊗[ℤ] N →ₐ[S] A :=
  Algebra.TensorProduct.lift (Algebra.ofId S A) θ.toIntAlgHom (fun _ _ => Commute.all _ _)

@[simp] theorem sLift_tmul {N : Type} [CommRing N] (θ : N →+* A) (s : S) (n : N) :
    sLift S θ (s ⊗ₜ n) = algebraMap S A s * θ n :=
  Algebra.TensorProduct.lift_tmul _ _ _ _ _

theorem sLift_lTensor {N : Type} [CommRing N] (β ι : UnivBasisRing p →+* N) (c : N)
    (form : UnivBasisRing p →ₗ[ℤ] N) (hform : ∀ t, form t = β t - c * ι t) (θ : N →+* A)
    (W : WeierstrassCurve A) (hW : IsUnit W.Δ) (D : LevelPData A) (hD : IsLevelPStructure W p D)
    (W' : WeierstrassCurve A) (hW' : IsUnit W'.Δ) (D' : LevelPData A)
    (hD' : IsLevelPStructure W' p D')
    (hι : θ.comp ι = UnivBasisRing.classify p W (hpA.mul hW) D hD)
    (hβ : θ.comp β = UnivBasisRing.classify p W' (hpA.mul hW') D' hD')
    (g : S ⊗[ℤ] UnivBasisRing p) :
    sLift S θ (LinearMap.lTensor S form g) =
      sClassify p S hpA W' hW' D' hD' g - θ c * sClassify p S hpA W hW D hD g := by
  induction g using TensorProduct.induction_on with
  | zero => simp
  | tmul s t =>
    rw [LinearMap.lTensor_tmul, sLift_tmul, hform, sClassify_tmul, sClassify_tmul, ← hι, ← hβ,
      RingHom.comp_apply, RingHom.comp_apply, map_sub, map_mul]
    ring
  | add x y hx hy => rw [map_add, map_add, hx, hy, map_add, map_add]; ring

section VCTarget

variable (W : WeierstrassCurve A) (hW : IsUnit W.Δ) (D : LevelPData A)
  (hD : IsLevelPStructure W p D) (C : VariableChange A)

def vcTheta : VCRing p →+* A := VCRing.lift p (UnivBasisRing.classify p W (hpA.mul hW) D hD) C

theorem vcTheta_comp_ofUniv : (vcTheta p hpA W hW D hD C).comp (VCRing.ofUniv p) =
    UnivBasisRing.classify p W (hpA.mul hW) D hD :=
  VCRing.lift_comp_ofUniv p _ C

theorem univVC_map_vcTheta : (univVC p).map (vcTheta p hpA W hW D hD C) = C :=
  univVC_map_lift p _ C

theorem vcTheta_units (k : ℤ) :
    vcTheta p hpA W hW D hD C (((univVC p).u⁻¹ ^ k : (VCRing p)ˣ) : VCRing p) =
      ((C.u⁻¹ ^ k : Aˣ) : A) := by
  have hu : Units.map (vcTheta p hpA W hW D hD C : VCRing p →* A) (univVC p).u = C.u :=
    congrArg VariableChange.u (univVC_map_vcTheta p hpA W hW D hD C)
  change (vcTheta p hpA W hW D hD C : VCRing p →* A)
    (((univVC p).u⁻¹ ^ k : (VCRing p)ˣ) : VCRing p) = _
  rw [← Units.coe_map, map_zpow, map_inv, hu]

variable (hV : IsLevelPStructure (univVC p • vcCurve p) p ((vcData p).variableChange (univVC p)))
  (hW' : IsUnit (C • W).Δ) (hD' : IsLevelPStructure (C • W) p (D.variableChange C))

theorem vcTheta_comp_vcClassify : (vcTheta p hpA W hW D hD C).comp (vcClassify p hV) =
    UnivBasisRing.classify p (C • W) (hpA.mul hW') (D.variableChange C) hD' := by
  have hDmap : ∀ {T T' : Type _} [CommRing T] [CommRing T'] (φ : T →+* T') (C₀ : VariableChange T)
      (D₀ : LevelPData T),
      (D₀.variableChange C₀).map φ = (D₀.map φ).variableChange (C₀.map φ) := by
    intro T T' _ _ φ C₀ D₀
    have hu : φ ((C₀.u⁻¹ : Tˣ) : T) = (((C₀.map φ).u⁻¹ : T'ˣ) : T') :=
      (Units.coe_map_inv (φ : T →* T') C₀.u).symm
    refine LevelPData.ext ?_ ?_ ?_ ?_ <;>
      simp only [LevelPData.map_xP, LevelPData.map_yP, LevelPData.map_xQ, LevelPData.map_yQ,
        LevelPData.variableChange_xP, LevelPData.variableChange_yP,
        LevelPData.variableChange_xQ, LevelPData.variableChange_yQ, map_mul, map_sub, map_pow,
        hu, VariableChange.map]
  refine UnivBasisRing.eq_classify p _ _ _ _ ?_ ?_
  · rw [← WeierstrassCurve.map_map, univCurveT_map_vcClassify,
      ← WeierstrassCurve.map_variableChange, univVC_map_vcTheta, vcCurve, WeierstrassCurve.map_map,
      vcTheta_comp_ofUniv, univCurveT_map_classify]
  · rw [← LevelPData.map_map, univData_map_vcClassify, hDmap, univVC_map_vcTheta, vcData,
      LevelPData.map_map, vcTheta_comp_ofUniv, univData_map_classify]

theorem vc_eval (k : ℤ) (g : S ⊗[ℤ] UnivBasisRing p) :
    sClassify p S hpA (C • W) hW' (D.variableChange C) hD' g -
        ((C.u⁻¹ ^ k : Aˣ) : A) * sClassify p S hpA W hW D hD g =
      sLift S (vcTheta p hpA W hW D hD C) (LinearMap.lTensor S (vcForm p hV k) g) := by
  rw [sLift_lTensor p S hpA (vcClassify p hV) (VCRing.ofUniv p) _ (vcForm p hV k)
    (vcForm_apply p hV k) (vcTheta p hpA W hW D hD C) W hW D hD (C • W) hW' _ hD'
    (vcTheta_comp_ofUniv p hpA W hW D hD C)
    (vcTheta_comp_vcClassify p hpA W hW D hD C hV hW' hD') g, vcTheta_units]

end VCTarget

section BorelTarget

variable (W : WeierstrassCurve A) (hW : IsUnit W.Δ) (D D' : LevelPData A)
  (hD : IsLevelPStructure W p D) (hD' : IsLevelPStructure W p D') {a : ℕ}
  (hu : IsUnit (((univCurveT p).ΨSq a).eval (univData p).xQ))
  (heq : D'.xQ * (W.ΨSq a).eval D.xQ = (W.Φ a).eval D.xQ)

include hu heq in

theorem xQ_eq_classify_borelX :
    D'.xQ = UnivBasisRing.classify p W (hpA.mul hW) D hD (borelX p a) := by
  set φ := UnivBasisRing.classify p W (hpA.mul hW) D hD with hφ
  obtain ⟨v, hv⟩ := hu
  have hWφ : (univCurveT p).map φ = W := univCurveT_map_classify p _ _ _ _
  have hxQ : φ (univData p).xQ = D.xQ :=
    congrArg LevelPData.xQ (univData_map_classify p W (hpA.mul hW) D hD)
  have hΨ : φ (((univCurveT p).ΨSq a).eval (univData p).xQ) = (W.ΨSq a).eval D.xQ := by
    rw [← hxQ, ← Polynomial.eval₂_at_apply, ← Polynomial.eval_map, ← WeierstrassCurve.map_ΨSq, hWφ]
  have hΦ : φ (((univCurveT p).Φ a).eval (univData p).xQ) = (W.Φ a).eval D.xQ := by
    rw [← hxQ, ← Polynomial.eval₂_at_apply, ← Polynomial.eval_map, ← WeierstrassCurve.map_Φ, hWφ]
  rw [borelX, map_mul, hΦ, ← hv, Ring.inverse_unit]
  set vA : Aˣ := Units.map (φ : UnivBasisRing p →* A) v with hvA
  have hinv : φ ((v⁻¹ : (UnivBasisRing p)ˣ) : UnivBasisRing p) = ((vA⁻¹ : Aˣ) : A) :=
    (Units.coe_map_inv (φ : UnivBasisRing p →* A) v).symm
  have hvA' : (vA : A) = (W.ΨSq a).eval D.xQ := by
    rw [hvA, Units.coe_map, MonoidHom.coe_coe, hv, hΨ]
  have heq' : D'.xQ * (vA : A) = (W.Φ a).eval D.xQ := by rw [hvA']; exact heq
  rw [hinv, ← heq', Units.mul_inv_cancel_right]

include hD' in
theorem isLevelPStructure_map_classify' :
    IsLevelPStructure ((univCurveT p).map (UnivBasisRing.classify p W (hpA.mul hW) D hD)) p D' := by
  rwa [univCurveT_map_classify]

def borelTheta : BorelRing p a →+* A :=
  BorelRing.lift p a (UnivBasisRing.classify p W (hpA.mul hW) D hD) D'
    (isLevelPStructure_map_classify' p hpA W hW D D' hD hD')
    (xQ_eq_classify_borelX p hpA W hW D D' hD hu heq)

theorem borelTheta_comp_ofUniv :
    (borelTheta p hpA W hW D D' hD hD' hu heq).comp (BorelRing.ofUniv p a) =
      UnivBasisRing.classify p W (hpA.mul hW) D hD :=
  BorelRing.lift_comp_ofUniv p a _ _ _ _

variable (hB : IsLevelPStructure (borelCurve p a) p (borelData' p a))

theorem borelTheta_comp_borelClassify :
    (borelTheta p hpA W hW D D' hD hD' hu heq).comp (borelClassify p hB) =
      UnivBasisRing.classify p W (hpA.mul hW) D' hD' := by
  refine UnivBasisRing.eq_classify p _ _ _ _ ?_ ?_
  · rw [← WeierstrassCurve.map_map, univCurveT_map_borelClassify, borelTheta, borelCurve_map_lift,
      univCurveT_map_classify]
  · rw [← LevelPData.map_map, univData_map_borelClassify, borelTheta, borelData'_map_lift]

theorem borel_eval (g : S ⊗[ℤ] UnivBasisRing p) :
    sClassify p S hpA W hW D' hD' g - sClassify p S hpA W hW D hD g =
      sLift S (borelTheta p hpA W hW D D' hD hD' hu heq)
        (LinearMap.lTensor S (borelForm p hB) g) := by
  rw [sLift_lTensor p S hpA (borelClassify p hB) (BorelRing.ofUniv p a) 1 (borelForm p hB)
    (fun t => by rw [borelForm_apply, one_mul]) _ W hW D hD W hW D' hD'
    (borelTheta_comp_ofUniv p hpA W hW D D' hD hD' hu heq)
    (borelTheta_comp_borelClassify p hpA W hW D D' hD hD' hu heq hB) g, map_one, one_mul]

end BorelTarget

end Targets

end LevelP

namespace KatzLevelPForm

open LevelP

variable (p : ℕ) (S : Type u) [CommRing S] (hpS : IsUnit (p : S)) (k : ℤ)

include hpS in
theorem isUnit_natCast_algebra (A : Type u) [CommRing A] [Algebra S A] : IsUnit (p : A) := by
  simpa only [map_natCast] using hpS.map (algebraMap S A)

variable (hV : IsLevelPStructure (univVC p • vcCurve p) p ((vcData p).variableChange (univVC p)))
  (g : S ⊗[ℤ] UnivBasisRing p) (hg : LinearMap.lTensor S (vcForm p hV k) g = 0)

def ofUniv : KatzLevelPForm S p k where
  toFun A _ _ W hW D hD := sClassify p S (isUnit_natCast_algebra p S hpS A) W hW D hD g
  map_toFun A B _ _ _ _ f W hW hW' D hD hD' := by
    rw [← AlgHom.comp_apply,
      comp_sClassify p S _ W hW D hD f (isUnit_natCast_algebra p S hpS B) hW' hD']
  toFun_variableChange A _ _ C W hW hW' D hD hD' := by
    have h := vc_eval p S (isUnit_natCast_algebra p S hpS A) W hW D hD C hV hW' hD' k g
    rw [hg, map_zero, sub_eq_zero] at h
    exact h

theorem ofUniv_toFun {A : Type u} [CommRing A] [Algebra S A] (W : WeierstrassCurve A)
    (hW : IsUnit W.Δ) (D : LevelPData A) (hD : IsLevelPStructure W p D) :
    (ofUniv p S hpS k hV g hg).toFun W hW D hD =
      sClassify p S (isUnit_natCast_algebra p S hpS A) W hW D hD g :=
  rfl

theorem evalUniv_ofUniv : (ofUniv p S hpS k hV g hg).evalUniv = g := by
  rw [KatzLevelPForm.evalUniv, ofUniv_toFun, sClassify_self]
  rfl

variable {p S k} in
include hpS in

theorem ofUniv_evalUniv (F : KatzLevelPForm S p k)
    (hF : LinearMap.lTensor S (vcForm p hV k) F.evalUniv = 0) :
    ofUniv p S hpS k hV F.evalUniv hF = F :=
  KatzLevelPForm.ext fun A _ _ W hW D hD => by
    rw [ofUniv_toFun, ← toFun_eq_sClassify_evalUniv]

theorem dependsOnlyOnSndLine_ofUniv
    (hB : ∀ (a : ℕ), 1 ≤ a → a ≤ (p - 1) / 2 → IsLevelPStructure (borelCurve p a) p (borelData' p a))
    (hu : ∀ (a : ℕ), 1 ≤ a → a ≤ (p - 1) / 2 →
      IsUnit (((univCurveT p).ΨSq a).eval (univData p).xQ))
    (hb : ∀ (a : ℕ) (ha : 1 ≤ a) (ha' : a ≤ (p - 1) / 2),
      LinearMap.lTensor S (borelForm p (hB a ha ha')) g = 0) :
    (ofUniv p S hpS k hV g hg).DependsOnlyOnSndLine := by
  intro A _ _ W hW D D' hD hD' hin
  obtain ⟨a, ha, ha', heq⟩ := hin
  have h := borel_eval p S (isUnit_natCast_algebra p S hpS A) W hW D D' hD hD' (hu a ha ha') heq
    (hB a ha ha') g
  rw [hb a ha ha', map_zero, sub_eq_zero] at h
  exact h

end KatzLevelPForm

end ModularCurve

end
