Fermat's Last Theorem in Lean 4

← all definition modules

Definitions/Def_ModularCurve_CuspSpace.lean

definition module

Cusps of : orbit space, denominator, width

Working with \mathbb{P}^1(\mathbb{Q}) realised as OnePoint ℚ, the module first introduces homogeneous integer coordinates: ratPoint a c is \infty when c = 0 and the image of a/c otherwise. Small lemmas record that every point of OnePoint ℚ is ratPoint a c for a coprime pair (a,c) (numerator and denominator of a rational, (1,0) for \infty), that ratPoint is unchanged by scaling both coordinates by a nonzero integer, that the Möbius action of the image of \gamma \in \mathrm{SL}_2(\mathbb{Z}) under mapGL ℚ sends ratPoint a c to ratPoint (γ₀₀a + γ₀₁c) (γ₁₀a + γ₁₁c), and that coprimality of the pair is preserved. Gamma0Q N is the image of \Gamma_0(N) in \mathrm{GL}_2(\mathbb{Q}) under mapGL ℚ, and CuspSpace N is the orbit space of OnePoint ℚ under this subgroup, with quotient map mk; mk N x = mk N y exactly when y is carried to x by some element of \Gamma_0(N). The map fromCoset sends the coset of g in \mathrm{SL}_2(\mathbb{Z})/\Gamma_0(N) to the class of g^{-1}\infty; it is surjective, whence CuspSpace N is finite for N \neq 0, is always nonempty, and is a subsingleton for N = 1.

The denominator invariant is defined on representatives by cuspDenomAux N x = N for x = \infty and \gcd(\mathrm{den}\,q, N) for x = q \in \mathbb{Q}; it is \Gamma_0(N)-invariant, hence descends to cuspDenom N : CuspSpace N → ℕ, equals \gcd(c,N) on ratPoint a c for coprime a, c, divides N, is positive for N \neq 0, and takes the value e at the class of ratPoint 1 e whenever e \mid N. The width is cuspWidthOfDenom N\,d = N/\gcd(d^2,N) and cuspWidth x is this applied to cuspDenom N x; accompanying arithmetic gives \gcd(d^2,N)\cdot N/\gcd(d^2,N) = N, divisibility of the width by N, positivity, the form (N/d)/\gcd(d,N/d) for d \mid N, invariance under replacing d by \gcd(d,N), and the values 1 at d = N and N at d = 1; on cusps, the width of the class of ratPoint a c (a,c coprime) is N/\gcd(|c|^2,N), the width at \infty is 1 for N \neq 0, and the width at 0 is N. Finally two Props package classical statements about this model: NormalFormCriterion N asserts that for all integers a, a' and e \mid N with a, a' coprime to e, the classes of ratPoint a e and ratPoint a' e agree if and only if a \equiv a' \bmod \gcd(e, N/e), and Classification N asserts merely the existence (a Nonempty of an equivalence, not a chosen one) of a bijection between CuspSpace N and \coprod_{d \mid N} (\mathbb{Z}/\gcd(d,N/d))^\times. Also included is the reformulation \gamma \in \Gamma_0(N) \iff N \mid \gamma_{1 0}, stated in the CongruenceSubgroup namespace.

Relation to Mathlib

Built on Mathlib's OnePoint ℚ, CongruenceSubgroup.Gamma0, the homomorphism mapGL into \mathrm{GL}_2(\mathbb{Q}) and MulAction.orbitRel.Quotient; the cusp set of \Gamma_0(N) as an orbit space on \mathbb{P}^1(\mathbb{Q}), together with the denominator and width invariants, is the project's own.

Where it is used

These definitions provide the combinatorial data of the cusps of X_0(N) — their number, their denominators and their widths — used in the numerical computations attached to modular curves of small level in the level-lowering part of the argument.

References

  1. F. Diamond and J. Shurman, A First Course in Modular Forms, Graduate Texts in Mathematics 228, Springer, 2005, §3.8
  2. G. Shimura, Introduction to the Arithmetic Theory of Automorphic Functions, Publications of the Mathematical Society of Japan 11, Princeton University Press, 1971

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_CuspSpace.lean

Declarations

Source

import Mathlib

open Matrix Matrix.SpecialLinearGroup OnePoint CongruenceSubgroup

open scoped MatrixGroups

namespace ModularCurve

noncomputable section

def ratPoint (a c : ℤ) : OnePoint ℚ :=
  if c = 0 thenelse (((a : ℚ) / (c : ℚ) : ℚ) : OnePoint ℚ)

@[simp] lemma ratPoint_zero_right (a : ℤ) : ratPoint a 0 = ∞ := if_pos rfl

lemma ratPoint_of_ne_zero (a : ℤ) {c : ℤ} (hc : c ≠ 0) :
    ratPoint a c = (((a : ℚ) / (c : ℚ) : ℚ) : OnePoint ℚ) := if_neg hc

@[simp] lemma ratPoint_one_zero : ratPoint 1 0 = ∞ := if_pos rfl

@[simp] lemma ratPoint_zero_one : ratPoint 0 1 = ((0 : ℚ) : OnePoint ℚ) := by
  simp [ratPoint]

lemma ratPoint_eq_infty_iff {a c : ℤ} : ratPoint a c = ∞ ↔ c = 0 := by
  rcases eq_or_ne c 0 with rfl | hc
  · simp
  · simp [ratPoint_of_ne_zero a hc, hc]

lemma ratPoint_mul_left {l : ℤ} (hl : l ≠ 0) (a c : ℤ) :
    ratPoint (l * a) (l * c) = ratPoint a c := by
  rcases eq_or_ne c 0 with rfl | hc
  · simp
  · rw [ratPoint_of_ne_zero _ (mul_ne_zero hl hc), ratPoint_of_ne_zero _ hc]
    push_cast
    rw [mul_div_mul_left _ _ (by exact_mod_cast hl)]

lemma ratPoint_neg_neg (a c : ℤ) : ratPoint (-a) (-c) = ratPoint a c := by
  simpa using ratPoint_mul_left (l := -1) (by norm_num) a c

lemma exists_ratPoint_eq (x : OnePoint ℚ) : ∃ a c : ℤ, IsCoprime a c ∧ ratPoint a c = x := by
  cases x with
  | infty => exact ⟨1, 0, isCoprime_one_left, rfl⟩
  | coe q =>
    refine ⟨q.num, (q.den : ℤ), ?_, ?_⟩
    · rw [Int.isCoprime_iff_gcd_eq_one]
      simpa [Int.gcd] using q.reduced
    · rw [ratPoint_of_ne_zero _ (by exact_mod_cast q.den_nz), OnePoint.coe_eq_coe,
        show ((q.den : ℤ) : ℚ) = (q.den : ℚ) by push_cast; ring, Rat.num_div_den]

@[simp] lemma mapGL_apply_coe (γ : SL(2, ℤ)) (i j : Fin 2) :
    (mapGL ℚ γ) i j = ((γ i j : ℤ) : ℚ) := by
  simp [mapGL]

lemma mapGL_smul_infty (γ : SL(2, ℤ)) :
    mapGL ℚ γ • (∞ : OnePoint ℚ) = ratPoint0 0) (γ 1 0) := by
  rw [smul_infty_eq_ite, mapGL_apply_coe, mapGL_apply_coe]
  rcases eq_or_ne (γ 1 0) 0 with h | h
  · simp [h]
  · rw [if_neg (by exact_mod_cast h), ratPoint_of_ne_zero _ h]

lemma mapGL_smul_ratPoint (γ : SL(2, ℤ)) {a c : ℤ} (h : a ≠ 0 ∨ c ≠ 0) :
    mapGL ℚ γ • ratPoint a c =
      ratPoint0 0 * a + γ 0 1 * c) (γ 1 0 * a + γ 1 1 * c) := by
  rcases eq_or_ne c 0 with rfl | hc
  ·
    replace h : a ≠ 0 := h.resolve_right (by simp)
    rw [ratPoint_zero_right, mapGL_smul_infty]
    simp only [mul_zero, add_zero]
    rw [mul_comm (γ 0 0) a, mul_comm (γ 1 0) a, ratPoint_mul_left h]
  ·
    rw [ratPoint_of_ne_zero _ hc, smul_some_eq_ite]
    have hcQ : (c : ℚ) ≠ 0 := by exact_mod_cast hc
    have hden : (mapGL ℚ γ) 1 0 * ((a : ℚ) / c) + (mapGL ℚ γ) 1 1
        = (((γ 1 0 * a + γ 1 1 * c : ℤ) : ℚ)) / (c : ℚ) := by
      simp only [mapGL_apply_coe]
      field_simp
      push_cast
      ring
    have hnum : (mapGL ℚ γ) 0 0 * ((a : ℚ) / c) + (mapGL ℚ γ) 0 1
        = (((γ 0 0 * a + γ 0 1 * c : ℤ) : ℚ)) / (c : ℚ) := by
      simp only [mapGL_apply_coe]
      field_simp
      push_cast
      ring
    rcases eq_or_ne (γ 1 0 * a + γ 1 1 * c) 0 with h0 | h0
    · rw [if_pos (by rw [hden, h0]; simp), h0, ratPoint_zero_right]
    · rw [if_neg (by rw [hden]; exact div_ne_zero (by exact_mod_cast h0) hcQ),
        ratPoint_of_ne_zero _ h0, hnum, hden, OnePoint.coe_eq_coe,
        div_div_div_cancel_right₀ hcQ]

lemma isCoprime_smul_pair (γ : SL(2, ℤ)) {a c : ℤ} (h : IsCoprime a c) :
    IsCoprime (γ 0 0 * a + γ 0 1 * c) (γ 1 0 * a + γ 1 1 * c) := by
  have := IsCoprime.mulVecSL (v := ![a, c]) (by simpa using h) γ
  simpa [Matrix.mulVec_fin_two] using this

variable (N : ℕ)

def Gamma0Q : Subgroup (GL (Fin 2) ℚ) := (Gamma0 N).map (mapGL ℚ)

lemma mem_Gamma0Q {g : GL (Fin 2) ℚ} :
    g ∈ Gamma0Q N ↔ ∃ γ ∈ Gamma0 N, mapGL ℚ γ = g := Iff.rfl

abbrev CuspSpace : Type := MulAction.orbitRel.Quotient (Gamma0Q N) (OnePoint ℚ)

namespace CuspSpace

def mk (x : OnePoint ℚ) : CuspSpace N := ⟦x⟧

lemma mk_surjective : Function.Surjective (mk N) := by
  rintro ⟨x⟩; exact ⟨x, rfl⟩

variable {N}

lemma mk_eq_mk_iff {x y : OnePoint ℚ} :
    mk N x = mk N y ↔ ∃ γ ∈ Gamma0 N, mapGL ℚ γ • y = x := by
  rw [mk, mk, Quotient.eq, MulAction.orbitRel_apply, MulAction.mem_orbit_iff]
  constructor
  · rintro ⟨⟨g, γ, hγ, rfl⟩, hg⟩
    exact ⟨γ, hγ, hg⟩
  · rintro ⟨γ, hγ, hg⟩
    exact ⟨⟨mapGL ℚ γ, γ, hγ, rfl⟩, hg⟩

lemma mk_smul {γ : SL(2, ℤ)} (hγ : γ ∈ Gamma0 N) (x : OnePoint ℚ) :
    mk N (mapGL ℚ γ • x) = mk N x :=
  mk_eq_mk_iff.mpr ⟨γ, hγ, rfl⟩

variable (N)

lemma mk_smul_infty_eq_of_mem {a b : SL(2, ℤ)} (hab : a⁻¹ * b ∈ Gamma0 N) :
    mk N (mapGL ℚ a⁻¹ • ∞) = mk N (mapGL ℚ b⁻¹ • ∞) := by
  have key : mapGL ℚ b⁻¹ • (∞ : OnePoint ℚ)
      = mapGL ℚ (a⁻¹ * b)⁻¹ • (mapGL ℚ a⁻¹ • (∞ : OnePoint ℚ)) := by
    have h1 : (a⁻¹ * b)⁻¹ * a⁻¹ = b⁻¹ := by group
    rw [← mul_smul, ← map_mul, h1]
  rw [key]
  exact (mk_smul (inv_mem hab) _).symm

def fromCoset : SL(2, ℤ) ⧸ Gamma0 N → CuspSpace N :=
  Quotient.lift (fun g => mk N (mapGL ℚ g⁻¹ • ∞))
    (fun _ _ hab => mk_smul_infty_eq_of_mem N (QuotientGroup.leftRel_apply.mp hab))

@[simp] lemma fromCoset_mk (g : SL(2, ℤ)) :
    fromCoset N ((g : SL(2, ℤ) ⧸ Gamma0 N)) = mk N (mapGL ℚ g⁻¹ • ∞) := rfl

lemma fromCoset_surjective : Function.Surjective (fromCoset N) := by
  rintro ⟨x⟩
  obtain ⟨g, hg⟩ := OnePoint.exists_mem_SL2 ℤ x
  exact ⟨(g⁻¹ : SL(2, ℤ)), by rw [fromCoset_mk, inv_inv, hg]; rfl⟩

instance instFinite [NeZero N] : Finite (CuspSpace N) :=
  Finite.of_surjective _ (fromCoset_surjective N)

instance instNonempty : Nonempty (CuspSpace N) := ⟨mk N ∞⟩

instance instSubsingletonOfOne : Subsingleton (CuspSpace 1) := by
  constructor
  rintro ⟨x⟩ ⟨y⟩
  obtain ⟨g, hg⟩ := OnePoint.exists_mem_SL2 ℤ x
  obtain ⟨h, hh⟩ := OnePoint.exists_mem_SL2 ℤ y
  show mk 1 x = mk 1 y
  have hmem : ∀ γ : SL(2, ℤ), γ ∈ Gamma0 1 := fun γ => by
    rw [Gamma0_mem]; exact Subsingleton.elim _ _
  rw [← hg, ← hh]
  symm
  rw [show (mapGL ℚ h • (∞ : OnePoint ℚ)) = mapGL ℚ (h * g⁻¹) • (mapGL ℚ g • (∞ : OnePoint ℚ))
    by rw [← mul_smul, ← map_mul, inv_mul_cancel_right]]
  exact mk_smul (hmem _) _

def cuspDenomAux (x : OnePoint ℚ) : ℕ :=
  x.elim N fun q => Nat.gcd q.den N

@[simp] lemma cuspDenomAux_infty : cuspDenomAux N ∞ = N := rfl

@[simp] lemma cuspDenomAux_coe (q : ℚ) : cuspDenomAux N (q : OnePoint ℚ) = Nat.gcd q.den N :=
  rfl

variable {N}

lemma cuspDenomAux_ratPoint {a c : ℤ} (h : IsCoprime a c) :
    cuspDenomAux N (ratPoint a c) = Int.gcd c N := by
  rcases eq_or_ne c 0 with rfl | hc
  · simp [Int.gcd]
  · rw [ratPoint_of_ne_zero _ hc, cuspDenomAux_coe]
    have hcop : Nat.Coprime a.natAbs c.natAbs := Int.isCoprime_iff_gcd_eq_one.mp h
    have hden : (((a : ℚ) / (c : ℚ)).den : ℤ) = (c.natAbs : ℤ) := by
      rcases lt_or_gt_of_ne hc with hneg | hpos
      · have : ((a : ℚ) / (c : ℚ)) = (((-a : ℤ) : ℚ) / ((-c : ℤ) : ℚ)) := by
          push_cast; rw [neg_div_neg_eq]
        rw [this, Rat.den_div_eq_of_coprime (by omega) (by simpa using hcop)]
        omega
      · rw [Rat.den_div_eq_of_coprime hpos hcop]
        omega
    have : ((a : ℚ) / (c : ℚ)).den = c.natAbs := by exact_mod_cast hden
    rw [this]
    rfl

lemma _root_.CongruenceSubgroup.Gamma0_mem_iff_dvd {N : ℕ} {γ : SL(2, ℤ)} :
    γ ∈ Gamma0 N ↔ (N : ℤ) ∣ γ 1 0 := by
  rw [Gamma0_mem, ZMod.intCast_zmod_eq_zero_iff_dvd]

lemma cuspDenomAux_dvd_smul {γ : SL(2, ℤ)} (hγ : γ ∈ Gamma0 N) (x : OnePoint ℚ) :
    cuspDenomAux N x ∣ cuspDenomAux N (mapGL ℚ γ • x) := by
  obtain ⟨a, c, hac, rfl⟩ := exists_ratPoint_eq x
  have hac' : a ≠ 0 ∨ c ≠ 0 := hac.ne_zero_or_ne_zero
  rw [mapGL_smul_ratPoint γ hac', cuspDenomAux_ratPoint hac,
    cuspDenomAux_ratPoint (isCoprime_smul_pair γ hac)]

  have h1 : (Int.gcd c N : ℤ) ∣ γ 1 0 * a + γ 1 1 * c := by
    refine dvd_add (Dvd.dvd.mul_right ?_ a) (Dvd.dvd.mul_left (Int.gcd_dvd_left ..) _)
    exact (Int.gcd_dvd_right ..).trans (Gamma0_mem_iff_dvd.mp hγ)
  exact Int.dvd_gcd h1 (Int.gcd_dvd_right ..)

lemma cuspDenomAux_smul {γ : SL(2, ℤ)} (hγ : γ ∈ Gamma0 N) (x : OnePoint ℚ) :
    cuspDenomAux N (mapGL ℚ γ • x) = cuspDenomAux N x := by
  refine Nat.dvd_antisymm ?_ (cuspDenomAux_dvd_smul hγ x)
  have := cuspDenomAux_dvd_smul (inv_mem hγ) (mapGL ℚ γ • x)
  rwa [← mul_smul, ← map_mul, inv_mul_cancel, map_one, one_smul] at this

variable (N)

def cuspDenom : CuspSpace N → ℕ :=
  Quotient.lift (cuspDenomAux N) (by
    rintro x y ⟨⟨g, γ, hγ, rfl⟩, rfl⟩
    exact cuspDenomAux_smul hγ y)

@[simp] lemma cuspDenom_mk (x : OnePoint ℚ) : cuspDenom N (mk N x) = cuspDenomAux N x := rfl

variable {N}

lemma cuspDenom_mk_ratPoint {a c : ℤ} (h : IsCoprime a c) :
    cuspDenom N (mk N (ratPoint a c)) = Int.gcd c N :=
  cuspDenomAux_ratPoint h

@[simp] lemma cuspDenom_mk_infty : cuspDenom N (mk N ∞) = N := rfl

@[simp] lemma cuspDenom_mk_zero : cuspDenom N (mk N ((0 : ℚ) : OnePoint ℚ)) = 1 := by
  rw [← ratPoint_zero_one, cuspDenom_mk_ratPoint (isCoprime_zero_left.mpr isUnit_one)]
  simp [Int.gcd]

lemma cuspDenom_dvd (x : CuspSpace N) : cuspDenom N x ∣ N := by
  obtain ⟨y, rfl⟩ := mk_surjective N x
  obtain ⟨a, c, hac, rfl⟩ := exists_ratPoint_eq y
  rw [cuspDenom_mk_ratPoint hac]
  simpa using Int.gcd_dvd_natAbs_right c (N : ℤ)

lemma cuspDenom_pos [NeZero N] (x : CuspSpace N) : 0 < cuspDenom N x :=
  Nat.pos_of_dvd_of_pos (cuspDenom_dvd x) (Nat.pos_of_ne_zero (NeZero.ne N))

lemma cuspDenom_mk_ratPoint_one_dvd {e : ℕ} (he : e ∣ N) :
    cuspDenom N (mk N (ratPoint 1 e)) = e := by
  rw [cuspDenom_mk_ratPoint isCoprime_one_left, Int.gcd_natCast_natCast, Nat.gcd_eq_left he]

def NormalFormCriterion (N : ℕ) : Prop :=
  ∀ (a a' : ℤ) (e : ℕ), e ∣ N → IsCoprime a (e : ℤ) → IsCoprime a' (e : ℤ) →
    (mk N (ratPoint a e) = mk N (ratPoint a' e) ↔
      (a : ZMod (Nat.gcd e (N / e))) = (a' : ZMod (Nat.gcd e (N / e))))

def Classification (N : ℕ) : Prop :=
  Nonempty (CuspSpace N ≃ (d : N.divisors) × (ZMod (Nat.gcd d (N / (d : ℕ))))ˣ)

end CuspSpace

end

end ModularCurve

open Matrix Matrix.SpecialLinearGroup OnePoint CongruenceSubgroup

open scoped MatrixGroups

namespace ModularCurve

noncomputable section

theorem gcd_sq_eq_mul_gcd_div {N d : ℕ} (hd : d ∣ N) :
    Nat.gcd (d ^ 2) N = d * Nat.gcd d (N / d) := by
  conv_lhs => rw [pow_two, ← Nat.mul_div_cancel' hd]
  exact Nat.gcd_mul_left d d (N / d)

theorem gcd_sq_gcd_left (c N : ℕ) : Nat.gcd (Nat.gcd c N ^ 2) N = Nat.gcd (c ^ 2) N := by
  rcases Nat.eq_zero_or_pos N with rfl | hN
  · simp [Nat.gcd_zero_right]
  rcases Nat.eq_zero_or_pos c with rfl | hc
  · rw [Nat.gcd_zero_left, zero_pow two_ne_zero, Nat.gcd_zero_left,
      Nat.gcd_eq_right (dvd_pow_self N two_ne_zero)]

  set g : ℕ := Nat.gcd c N with hg
  have hg0 : 0 < g := Nat.gcd_pos_of_pos_left N hc
  have hgc : g ∣ c := Nat.gcd_dvd_left c N
  have hgN : g ∣ N := Nat.gcd_dvd_right c N
  have hcop : Nat.Coprime (c / g) (N / g) := Nat.coprime_div_gcd_div_gcd hg0

  have hL : Nat.gcd (g ^ 2) N = g * Nat.gcd g (N / g) := gcd_sq_eq_mul_gcd_div hgN

  have hR : Nat.gcd (c ^ 2) N = g * Nat.gcd g (N / g) := by
    have h1 : c ^ 2 = g * (c / g * c) := by
      rw [← mul_assoc, Nat.mul_div_cancel' hgc, ← pow_two]
    have h2 : N = g * (N / g) := (Nat.mul_div_cancel' hgN).symm
    rw [h1]
    conv_lhs => rw [h2]
    rw [Nat.gcd_mul_left]
    congr 1
    rw [Nat.Coprime.gcd_mul_left_cancel c hcop]
    conv_lhs => rw [← Nat.div_mul_cancel hgc]
    rw [Nat.Coprime.gcd_mul_left_cancel g hcop]
  rw [hL, hR]

def cuspWidthOfDenom (N d : ℕ) : ℕ := N / Nat.gcd (d ^ 2) N

theorem gcd_mul_cuspWidthOfDenom (N d : ℕ) :
    Nat.gcd (d ^ 2) N * cuspWidthOfDenom N d = N :=
  Nat.mul_div_cancel' (Nat.gcd_dvd_right _ _)

theorem cuspWidthOfDenom_dvd (N d : ℕ) : cuspWidthOfDenom N d ∣ N :=
  Nat.div_dvd_of_dvd (Nat.gcd_dvd_right _ _)

theorem cuspWidthOfDenom_pos {N : ℕ} (hN : N ≠ 0) (d : ℕ) : 0 < cuspWidthOfDenom N d :=
  Nat.div_pos (Nat.le_of_dvd (Nat.pos_of_ne_zero hN) (Nat.gcd_dvd_right _ _))
    (Nat.gcd_pos_of_pos_right _ (Nat.pos_of_ne_zero hN))

theorem cuspWidthOfDenom_eq_div_div {N d : ℕ} (hd : d ∣ N) :
    cuspWidthOfDenom N d = N / d / Nat.gcd d (N / d) := by
  unfold cuspWidthOfDenom
  rw [gcd_sq_eq_mul_gcd_div hd, Nat.div_div_eq_div_mul]

theorem cuspWidthOfDenom_gcd (N d : ℕ) :
    cuspWidthOfDenom N (Nat.gcd d N) = cuspWidthOfDenom N d := by
  unfold cuspWidthOfDenom
  rw [gcd_sq_gcd_left]

theorem cuspWidthOfDenom_self {N : ℕ} (hN : N ≠ 0) : cuspWidthOfDenom N N = 1 := by
  unfold cuspWidthOfDenom
  rw [Nat.gcd_eq_right (dvd_pow_self N two_ne_zero), Nat.div_self (Nat.pos_of_ne_zero hN)]

theorem cuspWidthOfDenom_one (N : ℕ) : cuspWidthOfDenom N 1 = N := by
  unfold cuspWidthOfDenom
  rw [one_pow, Nat.gcd_one_left, Nat.div_one]

namespace CuspSpace

variable {N : ℕ}

def cuspWidth (x : CuspSpace N) : ℕ := cuspWidthOfDenom N (cuspDenom N x)

theorem cuspWidth_mk_ratPoint {a c : ℤ} (h : IsCoprime a c) :
    cuspWidth (mk N (ratPoint a c)) = N / Nat.gcd (c.natAbs ^ 2) N := by
  have h1 : cuspDenom N (mk N (ratPoint a c)) = Nat.gcd c.natAbs N := by
    rw [cuspDenom_mk_ratPoint h]
    show Nat.gcd c.natAbs (N : ℤ).natAbs = _
    rw [Int.natAbs_natCast]
  show cuspWidthOfDenom N (cuspDenom N (mk N (ratPoint a c))) = _
  rw [h1]
  show N / Nat.gcd (Nat.gcd c.natAbs N ^ 2) N = _
  rw [gcd_sq_gcd_left]

theorem cuspWidth_mk_infty (hN : N ≠ 0) : cuspWidth (mk N ∞) = 1 := by
  show cuspWidthOfDenom N (cuspDenom N (mk N ∞)) = 1
  rw [cuspDenom_mk_infty]
  exact cuspWidthOfDenom_self hN

theorem cuspWidth_mk_zero : cuspWidth (mk N ((0 : ℚ) : OnePoint ℚ)) = N := by
  show cuspWidthOfDenom N (cuspDenom N (mk N ((0 : ℚ) : OnePoint ℚ))) = N
  rw [cuspDenom_mk_zero]
  exact cuspWidthOfDenom_one N

theorem cuspWidth_dvd (x : CuspSpace N) : cuspWidth x ∣ N := cuspWidthOfDenom_dvd _ _

theorem cuspWidth_pos (hN : N ≠ 0) (x : CuspSpace N) : 0 < cuspWidth x :=
  cuspWidthOfDenom_pos hN _

end CuspSpace

end

end ModularCurve

Statements phrased using this module (10)