Fermat's Last Theorem in Lean 4

← all definition modules

Definitions/Def_AlgebraicCurve_DivisorPushPull.lean

definition module

Restriction of places, ramification and inertia, divisor pull-back and push-forward

Throughout, K \subseteq F \subseteq F' is a tower of fields (Algebra K F, Algebra K F', Algebra F F', IsScalarTower K F F'), with Place, Divisor, degree, IsPrincipal, Pic0, HasPrincipalDivisors taken from the project's divisor-class-group module; a place carries a valuation subring together with the conditions recorded there, and ord, deg and ResidueField are its associated order function, degree and residue field. For a place w of F', Place.restrict F w is the place of F whose valuation subring is the preimage \mathcal O_w \cap F (ValuationSubring.comap), the properness and discreteness conditions being verified from [Algebra.IsIntegral F F'] and integral closedness. Place.ramificationIndex F w is defined as the natural-number infimum of \{n>0 : n = \operatorname{ord}_w(\iota f)\ \text{for some } f \in F^\times\}; under integrality it is positive, divides every \operatorname{ord}_w(\iota f), and ord_restrict gives \operatorname{ord}_w(\iota f) = e\cdot \operatorname{ord}_{w|_F}(f). Place.restrictInclusion and Place.restrictResidueMap (a local homomorphism and the induced map of residue fields, with algebra and scalar-tower instances) make \kappa(w) an extension of \kappa(w|_F), and Place.inertiaDeg F w is its finrank, so that deg_restrict_mul_inertiaDeg is the tower formula \deg(w|_F)\cdot f = \deg w. Assuming HasPrincipalDivisors K F', Place.fiber F' v is the finite set of places above v. On divisors: Divisor.mapRestrict is the bare relabelling w \mapsto w|_F; Divisor.pushforward F sends w \mapsto f(w)\, w|_F and preserves degree; Divisor.pullback F' sends v \mapsto \sum_{w \mid v} e(w)\,w, takes \operatorname{div}_F f to \operatorname{div}_{F'}(\iota f) and principal divisors to principal divisors. Two classes state ramification identities as hypotheses: SumRamificationInertia (\sum_{w\mid v} e f = [F':F]) and FundamentalIdentity (\sum_{w\mid v} e \deg w = [F':F]\deg v), the former implying the latter; under the latter, pull-back multiplies degrees by [F':F] and descends to Pic0.pullbackHom. Norm compatibility is the project's proposition Divisor.PushforwardNormFormula K F F': for every f \in (F')^\times, every divisor D with D(w) = \operatorname{ord}_w f for all w, and every v, (\pi_* D)(v) = \operatorname{ord}_v(N_{F'/F} f); it is passed as an explicit argument to Pic0.pushforwardHom (which also needs Module.Finite F F').

Relation to Mathlib

Mathlib's ramification index and inertia degree are defined for ideals in extensions of Dedekind domains (Ideal.ramificationIdx, Ideal.inertiaDeg); here both are redefined directly for the project's own Place, the ramification index as an infimum of positive orders and the inertia degree as a residue-field finrank. The fundamental identity and the norm formula are not proved in this module but appear as the project's hypothesis classes SumRamificationInertia, FundamentalIdentity and the proposition Divisor.PushforwardNormFormula.

Where it is used

These maps supply the functoriality of degree-zero divisor classes along a finite extension of function fields, so that a correspondence between curves acts on \mathrm{Pic}^0 by push-forward after pull-back along its two projections; this is the form in which Hecke operators act on the Jacobians of modular curves in the modularity part of the argument.

References

  1. H. Stichtenoth, Algebraic Function Fields and Codes, Springer, 1993
  2. J.-P. Serre, Local Fields, Graduate Texts in Mathematics 67, Springer, 1979
  3. J.-P. Serre, Groupes algébriques et corps de classes, Hermann, 1959

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

Imports

Imported by

Declarations

Source

import Definitions.Def_AlgebraicCurve_DivisorClassGroup
import Mathlib.RingTheory.Valuation.LocalSubring ↗
import Mathlib.RingTheory.IntegralClosure.IntegrallyClosed ↗
import Mathlib.RingTheory.IntegralClosure.Algebra.Basic ↗
import Mathlib.RingTheory.Norm.Basic ↗
import Mathlib.LinearAlgebra.Dimension.Free ↗

set_option autoImplicit false

noncomputable section

open IsDedekindDomain WithZero IsLocalRing

namespace AlgebraicCurve

namespace Place

section SinglePlace

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

private 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

private 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 _)

private 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⟩

private 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 Restrict

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

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

variable (w : Place K F')

private 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

private 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

private 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⟩

private 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

def ramificationIndex (F : Type*) [Field F] [Algebra F F'] : ℕ :=
  sInf {n : ℕ | 0 < n ∧ ∃ f : F, f ≠ 0 ∧ w.ord (algebraMap F F' f) = n}

theorem ramificationIndex_le_ord {f : F} (hf : f ≠ 0)
    (hpos : 0 < w.ord (algebraMap F F' f)) :
    (ramificationIndex (F := F) w : ℤ) ≤ w.ord (algebraMap F F' f) := by
  have h := Nat.sInf_le
    (s := {n : ℕ | 0 < n ∧ ∃ f : F, f ≠ 0 ∧ w.ord (algebraMap F F' f) = n})
    (m := (w.ord (algebraMap F F' f)).toNat) ⟨by omega, f, hf, by omega⟩
  rw [ramificationIndex]
  omega

variable [Algebra.IsIntegral F F']

private 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 ramificationIndex_pos : 0 < ramificationIndex (F := F) w :=
  (Nat.sInf_mem (w.ramificationIndex_set_nonempty (F := F))).1

theorem exists_ord_eq_ramificationIndex :
    ∃ f : F, f ≠ 0 ∧ w.ord (algebraMap F F' f) = ramificationIndex (F := F) w :=
  (Nat.sInf_mem (w.ramificationIndex_set_nonempty (F := F))).2

theorem ramificationIndex_dvd_ord {f : F} (hf : f ≠ 0) :
    (ramificationIndex (F := F) w : ℤ) ∣ w.ord (algebraMap F F' f) := by
  obtain ⟨g, hg0, hge⟩ := w.exists_ord_eq_ramificationIndex (F := F)
  set e : ℤ := (ramificationIndex (F := F) w : ℤ) with he
  have hepos : 0 < e := by
    have := w.ramificationIndex_pos (F := F)
    omega
  set m : ℤ := w.ord (algebraMap F F' f) with hm
  set q : ℤ := m / e with hq

  have hgq : algebraMap F F' (g ^ (-q)) = (algebraMap F F' g) ^ (-q) := map_zpow₀ _ _ _
  have hr : w.ord (algebraMap F F' (f * g ^ (-q))) = m - e * q := by
    rw [map_mul, w.ord_mul (algebraMap_ne_zero hf)
      (by rw [hgq]; exact zpow_ne_zero _ (algebraMap_ne_zero hg0)), hgq, w.ord_zpow, hge,
      ← hm]
    ring
  have hmod := Int.emod_nonneg m (by omega : e ≠ 0)
  have hmod' := Int.emod_lt_of_pos m hepos
  have hbridge : m % e = m - e * q := by
    rw [hq]
    exact Int.emod_def m e

  rcases eq_or_lt_of_le (show (0 : ℤ) ≤ m - e * q by omega) with heq | hlt
  · exact ⟨q, by omega⟩
  · exfalso
    have hfg : f * g ^ (-q) ≠ 0 := mul_ne_zero hf (zpow_ne_zero _ hg0)
    have hle := w.ramificationIndex_le_ord (F := F) hfg (by omega)
    rw [hr, ← he] at hle
    omega

theorem irreducible_mk_comap {g : F} (hg0 : g ≠ 0)
    (hmem : g ∈ w.toValuationSubring.comap (algebraMap F F'))
    (hge : w.ord (algebraMap F F' g) = ramificationIndex (F := F) w) :
    Irreducible (⟨g, hmem⟩ : w.toValuationSubring.comap (algebraMap F F')) := by
  have hepos : 0 < ramificationIndex (F := F) w := w.ramificationIndex_pos (F := F)
  constructor
  ·
    rw [isUnit_mk_comap_iff hg0 hmem, hge]
    omega
  ·

    rintro ⟨a, ha⟩ ⟨b, hb⟩ hab
    have hab' : g = a * b := by simpa [Subtype.ext_iff] using hab
    have ha0 : a ≠ 0 := by
      rintro rfl
      exact hg0 (by simpa using hab')
    have hb0 : b ≠ 0 := by
      rintro rfl
      exact hg0 (by simpa using hab')
    have hsum : w.ord (algebraMap F F' a) + w.ord (algebraMap F F' b)
        = ramificationIndex (F := F) w := by
      rw [← w.ord_mul (algebraMap_ne_zero ha0) (algebraMap_ne_zero hb0), ← map_mul, ← hab',
        hge]
    have ha' : 0 ≤ w.ord (algebraMap F F' a) := (mem_comap_iff_ord_nonneg ha0).mp ha
    have hb' : 0 ≤ w.ord (algebraMap F F' b) := (mem_comap_iff_ord_nonneg hb0).mp hb

    rcases eq_or_lt_of_le ha' with ha0' | hapos
    · exact Or.inl ((isUnit_mk_comap_iff ha0 ha).mpr ha0'.symm)
    rcases eq_or_lt_of_le hb' with hb0' | hbpos
    · exact Or.inr ((isUnit_mk_comap_iff hb0 hb).mpr hb0'.symm)
    exfalso
    have h1 := w.ramificationIndex_le_ord (F := F) ha0 hapos
    have h2 := w.ramificationIndex_le_ord (F := F) hb0 hbpos
    omega

private 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

section RestrictDef

variable [Algebra K F] [IsScalarTower K F F']

variable (F) in

def restrict : Place K F where
  toValuationSubring := w.toValuationSubring.comap (algebraMap F F')
  algebraMap_mem' a := by
    rw [ValuationSubring.mem_comap, ← IsScalarTower.algebraMap_apply]
    exact w.algebraMap_mem' a
  ne_top' := w.comap_algebraMap_ne_top
  isPrincipalIdealRing' := w.isPrincipalIdealRing_comap

@[simp]
theorem restrict_toValuationSubring :
    (w.restrict F).toValuationSubring = w.toValuationSubring.comap (algebraMap F F') := rfl

theorem mem_restrict_iff {f : F} :
    f ∈ (w.restrict F).toValuationSubring ↔ algebraMap F F' f ∈ w.toValuationSubring :=
  Iff.rfl

theorem ord_restrict (f : F) :
    w.ord (algebraMap F F' f) = ramificationIndex (F := F) w * (w.restrict F).ord f := by
  rcases eq_or_ne f 0 with rfl | hf
  · simp
  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)

  obtain ⟨u, hu⟩ := (w.restrict F).exists_unit_mul_zpow hf
    (π := ⟨g, hgmem⟩) (irreducible_mk_comap w hg0 hgmem hge)
  set n : ℤ := (w.restrict F).ord f with hn

  have hune : ((u : (w.restrict F).toValuationSubring) : F) ≠ 0 := by
    intro h0
    have := u.mul_inv
    rw [Subtype.ext_iff] at this
    push_cast at this
    rw [h0, zero_mul] at this
    exact zero_ne_one this
  have huord : w.ord (algebraMap F F' ((u : (w.restrict F).toValuationSubring) : F)) = 0 :=
    (isUnit_mk_comap_iff hune (u : (w.restrict F).toValuationSubring).2).mp u.isUnit

  have hgz : (algebraMap F F' g) ^ n ≠ 0 := zpow_ne_zero _ (algebraMap_ne_zero hg0)
  calc w.ord (algebraMap F F' f)
      = w.ord (algebraMap F F' (((u : (w.restrict F).toValuationSubring) : F) * g ^ n)) := by
        rw [← hu]
    _ = w.ord (algebraMap F F' ((u : (w.restrict F).toValuationSubring) : F))
          + w.ord ((algebraMap F F' g) ^ n) := by
        rw [map_mul, map_zpow₀]
        exact w.ord_mul (algebraMap_ne_zero hune) hgz
    _ = ramificationIndex (F := F) w * n := by
        rw [huord, w.ord_zpow, hge, zero_add, mul_comm]

theorem ord_algebraMap_ne_zero_of_restrict_eq {v : Place K F} {f : F}
    (hford : v.ord f ≠ 0) (hw : w.restrict F = v) :
    w.ord (algebraMap F F' f) ≠ 0 := by
  rw [w.ord_restrict f, hw]
  have hepos : 0 < ramificationIndex (F := F) w := w.ramificationIndex_pos (F := F)
  exact mul_ne_zero (by omega) hford

theorem restrict_fiber_finite [HasPrincipalDivisors K F'] (v : Place K F) :
    {w : Place K F' | w.restrict F = v}.Finite := by
  obtain ⟨f, hf0, hford⟩ := v.exists_ord_pos
  obtain ⟨D, hD, -⟩ := HasPrincipalDivisors.exists_divisor (K := K)
    (algebraMap F F' f) (algebraMap_ne_zero hf0)
  apply Set.Finite.subset D.support.finite_toSet
  intro w hw
  simp only [Finset.mem_coe, Finsupp.mem_support_iff, hD w]
  exact w.ord_algebraMap_ne_zero_of_restrict_eq (by omega) hw

end RestrictDef

end Restrict

end Place

end AlgebraicCurve

set_option autoImplicit false

noncomputable section

open IsDedekindDomain WithZero IsLocalRing

namespace AlgebraicCurve

variable {K F F' : Type*} [Field K] [Field F] [Field F']
  [Algebra K F] [Algebra K F'] [Algebra F F'] [IsScalarTower K F F']
  [Algebra.IsIntegral F F']

namespace Place

variable (w : Place K F')

variable (F) in

def restrictInclusion : (w.restrict F).toValuationSubring →+* w.toValuationSubring where
  toFun a := ⟨algebraMap F F' (a : F), ValuationSubring.mem_comap.mp a.2
  map_one' := Subtype.ext (map_one (algebraMap F F'))
  map_mul' a b := Subtype.ext (map_mul (algebraMap F F') (a : F) (b : F))
  map_zero' := Subtype.ext (map_zero (algebraMap F F'))
  map_add' a b := Subtype.ext (map_add (algebraMap F F') (a : F) (b : F))

@[simp]
theorem coe_restrictInclusion (a : (w.restrict F).toValuationSubring) :
    ((restrictInclusion F w a : w.toValuationSubring) : F') = algebraMap F F' (a : F) := rfl

instance instIsLocalHomRestrictInclusion : IsLocalHom (restrictInclusion F w) where
  map_nonunit a ha := by

    have hord : w.ord (algebraMap F F' (a : F)) = 0 := by
      have h := w.ord_coe_unit ha.unit
      rwa [IsUnit.unit_spec, coe_restrictInclusion] at h

    have ha0 : (a : F) ≠ 0 := by
      rintro h0
      obtain ⟨b, hb⟩ := isUnit_iff_exists_inv.mp ha
      have hb' : algebraMap F F' (a : F) * (b : F') = 1 := congrArg Subtype.val hb
      rw [h0, map_zero, zero_mul] at hb'
      exact zero_ne_one hb'
    exact (isUnit_mk_comap_iff ha0 a.2).mpr hord

variable (F) in

def restrictResidueMap : (w.restrict F).ResidueField →+* w.ResidueField :=
  IsLocalRing.ResidueField.map (restrictInclusion F w)

@[simp]
theorem restrictResidueMap_residue (a : (w.restrict F).toValuationSubring) :
    restrictResidueMap F w (IsLocalRing.residue _ a) =
      IsLocalRing.residue _ (restrictInclusion F w a) :=
  IsLocalRing.ResidueField.map_residue _ _

instance instAlgebraResidueFieldRestrictPushforward :
    Algebra (w.restrict F).ResidueField w.ResidueField :=
  (restrictResidueMap F w).toAlgebra

theorem algebraMap_residueField_eq :
    algebraMap (w.restrict F).ResidueField w.ResidueField = restrictResidueMap F w := rfl

instance instIsScalarTowerResidueFieldRestrictPushforward :
    IsScalarTower K (w.restrict F).ResidueField w.ResidueField := by
  refine IsScalarTower.of_algebraMap_eq fun a => ?_
  show IsLocalRing.residue _ (algebraMap K w.toValuationSubring a) =
    restrictResidueMap F w
      (IsLocalRing.residue _ (algebraMap K (w.restrict F).toValuationSubring a))
  rw [restrictResidueMap_residue]
  refine congrArg _ (Subtype.ext ?_)
  show algebraMap K F' a = algebraMap F F' (algebraMap K F a)
  rw [← IsScalarTower.algebraMap_apply]

variable (F) in

def inertiaDeg : ℕ := Module.finrank (w.restrict F).ResidueField w.ResidueField

theorem deg_restrict_mul_inertiaDeg : (w.restrict F).deg * w.inertiaDeg F = w.deg :=
  Module.finrank_mul_finrank K (w.restrict F).ResidueField w.ResidueField

end Place

namespace Divisor

variable (F) in

def mapRestrict : Divisor K F' →+ Divisor K F :=
  Finsupp.liftAddHom fun w => Finsupp.singleAddHom (w.restrict F)

@[simp]
theorem mapRestrict_single (w : Place K F') (n : ℤ) :
    mapRestrict F (Finsupp.single w n) = Finsupp.single (w.restrict F) n := by
  simp [mapRestrict]

variable (F) in

def pushforward : Divisor K F' →+ Divisor K F :=
  Finsupp.liftAddHom fun w =>
    (Finsupp.singleAddHom (w.restrict F)).comp (AddMonoidHom.mulRight (w.inertiaDeg F : ℤ))

@[simp]
theorem pushforward_single (w : Place K F') (n : ℤ) :
    pushforward F (Finsupp.single w n) =
      Finsupp.single (w.restrict F) (n * w.inertiaDeg F) := by
  simp [pushforward]

@[simp]
theorem degree_pushforward (D : Divisor K F') :
    degree (pushforward F D) = degree D := by
  induction D using Finsupp.induction with
  | zero => simp
  | single_add w n D hw hn ih =>
    simp only [map_add, pushforward_single, degree_single, ih]
    have h := w.deg_restrict_mul_inertiaDeg (F := F)
    push_cast [← h]
    ring

theorem pushforward_mem_degZero {D : Divisor K F'} (hD : D ∈ degZero (K := K) (F := F')) :
    pushforward F D ∈ degZero (K := K) (F := F) := by
  rwa [mem_degZero, degree_pushforward]

theorem pushforward_apply [DecidableEq (Place K F)] (D : Divisor K F') (v : Place K F) :
    pushforward F D v =
      ∑ w ∈ D.support, if w.restrict F = v then D w * (w.inertiaDeg F : ℤ) else 0 := by
  classical
  rw [pushforward, Finsupp.liftAddHom_apply, Finsupp.sum_apply, Finsupp.sum]
  refine Finset.sum_congr rfl fun w _ => ?_
  simp [Finsupp.single_apply]

variable (K F F') in

def PushforwardNormFormula [Module.Finite F F'] : Prop :=
  ∀ (f : F'), f ≠ 0 → ∀ D : Divisor K F', (∀ w, D w = w.ord f) →
    ∀ v : Place K F, pushforward F D v = v.ord (Algebra.norm F f)

theorem pushforward_eq_of_normFormula [Module.Finite F F']
    (H : PushforwardNormFormula K F F') {f : F'} (hf : f ≠ 0)
    {D : Divisor K F'} (hD : ∀ w, D w = w.ord f)
    {E : Divisor K F} (hE : ∀ v, E v = v.ord (Algebra.norm F f)) :
    pushforward F D = E :=
  Finsupp.ext fun v => (H f hf D hD v).trans (hE v).symm

theorem isPrincipal_pushforward_of_normFormula [Module.Finite F F']
    (H : PushforwardNormFormula K F F') {D : Divisor K F'} (hD : IsPrincipal D) :
    IsPrincipal (pushforward F D) := by
  obtain ⟨f, hf, hDf⟩ := hD
  exact ⟨Algebra.norm F f, Algebra.norm_ne_zero_iff.mpr hf, fun v => H f hf D hDf v⟩

end Divisor

end AlgebraicCurve

set_option autoImplicit false

noncomputable section

namespace AlgebraicCurve

variable {K F F' : Type*} [Field K] [Field F] [Field F']
  [Algebra K F] [Algebra K F'] [Algebra F F'] [IsScalarTower K F F']
  [Algebra.IsIntegral F F'] [HasPrincipalDivisors K F']

namespace Place

variable (F') in

def fiber (v : Place K F) : Finset (Place K F') :=
  (restrict_fiber_finite v).toFinset

@[simp]
theorem mem_fiber {v : Place K F} {w : Place K F'} :
    w ∈ v.fiber F' ↔ w.restrict F = v := by
  simp only [fiber, Set.Finite.mem_toFinset, Set.mem_setOf_eq]

theorem restrict_mem_fiber (w : Place K F') : w ∈ (w.restrict F).fiber F' :=
  mem_fiber.mpr rfl

end Place

namespace Divisor

variable (F') in

def pullbackSingleHom (v : Place K F) : ℤ →+ Divisor K F' where
  toFun n := ∑ w ∈ v.fiber F', Finsupp.single w (n * w.ramificationIndex F)
  map_zero' := by simp
  map_add' m n := by
    rw [← Finset.sum_add_distrib]
    exact Finset.sum_congr rfl fun w _ => by rw [add_mul, Finsupp.single_add]

theorem pullbackSingleHom_apply (v : Place K F) (n : ℤ) :
    pullbackSingleHom F' v n
      = ∑ w ∈ v.fiber F', Finsupp.single w (n * w.ramificationIndex F) :=
  rfl

variable (F') in

def pullback : Divisor K F →+ Divisor K F' :=
  Finsupp.liftAddHom (pullbackSingleHom F')

theorem pullback_single (v : Place K F) (n : ℤ) :
    pullback F' (Finsupp.single v n)
      = ∑ w ∈ v.fiber F', Finsupp.single w (n * w.ramificationIndex F) :=
  Finsupp.liftAddHom_apply_single _ _ _

theorem pullback_single_apply_of_restrict_eq {v : Place K F} {w : Place K F'}
    (hw : w.restrict F = v) (n : ℤ) :
    pullback F' (Finsupp.single v n) w = n * w.ramificationIndex F := by
  rw [pullback_single, Finset.sum_apply']
  trans (Finsupp.single w (n * (w.ramificationIndex F : ℤ))) w
  · refine Finset.sum_eq_single_of_mem w (Place.mem_fiber.mpr hw) ?_
    intro b _ hb
    exact Finsupp.single_eq_of_ne' hb
  · exact Finsupp.single_eq_same

theorem pullback_single_apply_of_restrict_ne {v : Place K F} {w : Place K F'}
    (hw : w.restrict F ≠ v) (n : ℤ) :
    pullback F' (Finsupp.single v n) w = 0 := by
  rw [pullback_single, Finset.sum_apply']
  exact Finset.sum_eq_zero fun b hb =>
    Finsupp.single_eq_of_ne fun h => hw (h ▸ Place.mem_fiber.mp hb)

theorem pullback_apply (D : Divisor K F) (w : Place K F') :
    pullback F' D w = w.ramificationIndex F * D (w.restrict F) := by
  induction D using Finsupp.induction with
  | zero => simp
  | single_add v n D _ _ ih =>
    rw [map_add, Finsupp.add_apply, ih, Finsupp.add_apply, mul_add]
    congr 1
    by_cases hw : w.restrict F = v
    · subst hw
      rw [pullback_single_apply_of_restrict_eq rfl, Finsupp.single_eq_same, mul_comm]
    · rw [pullback_single_apply_of_restrict_ne hw,
        Finsupp.single_eq_of_ne hw, mul_zero]

theorem restrict_mem_support_of_mem_support_pullback {D : Divisor K F} {w : Place K F'}
    (hw : w ∈ (pullback F' D).support) : w.restrict F ∈ D.support := by
  rw [Finsupp.mem_support_iff] at hw ⊢
  intro h
  exact hw (by rw [pullback_apply, h, mul_zero])

theorem pullback_apply_eq_ord {f : F} {D : Divisor K F}
    (hD : ∀ v : Place K F, D v = v.ord f) (w : Place K F') :
    pullback F' D w = w.ord (algebraMap F F' f) := by
  rw [pullback_apply, hD, ← Place.ord_restrict]

theorem isPrincipal_pullback {D : Divisor K F} (hD : D.IsPrincipal) :
    (pullback F' D).IsPrincipal := by
  obtain ⟨f, hf, hDf⟩ := hD
  exact ⟨algebraMap F F' f, by simpa using hf, fun w => pullback_apply_eq_ord hDf w⟩

theorem pullback_mem_principal {D : Divisor K F} (hD : D ∈ principal (K := K) (F := F)) :
    pullback F' D ∈ principal (K := K) (F := F') :=
  isPrincipal_pullback hD

end Divisor

variable (K F F') in

class FundamentalIdentity : Prop where
  sum_ramificationIndex_mul_deg : ∀ v : Place K F,
    ∑ w ∈ v.fiber F', (w.ramificationIndex F : ℤ) * (w.deg : ℤ)
      = (Module.finrank F F' : ℤ) * (v.deg : ℤ)

namespace Divisor

theorem degree_pullback_single [FundamentalIdentity K F F'] (v : Place K F) (n : ℤ) :
    degree (pullback F' (Finsupp.single v n))
      = (Module.finrank F F' : ℤ) * degree (Finsupp.single v n) := by
  rw [pullback_single, map_sum, degree_single]
  simp_rw [degree_single, mul_assoc]
  rw [← Finset.mul_sum,
    FundamentalIdentity.sum_ramificationIndex_mul_deg (K := K) (F := F) (F' := F') v]
  ring

theorem degree_pullback [FundamentalIdentity K F F'] (D : Divisor K F) :
    degree (pullback F' D) = (Module.finrank F F' : ℤ) * degree D := by
  induction D using Finsupp.induction with
  | zero => simp
  | single_add v n D _ _ ih =>
    rw [map_add, map_add, map_add, mul_add, ih, degree_pullback_single]

theorem pullback_mem_degZero [FundamentalIdentity K F F'] {D : Divisor K F}
    (hD : D ∈ degZero (K := K) (F := F)) :
    pullback F' D ∈ degZero (K := K) (F := F') := by
  rw [mem_degZero] at hD ⊢
  rw [degree_pullback, hD, mul_zero]

end Divisor

end AlgebraicCurve

set_option autoImplicit false

noncomputable section

namespace AlgebraicCurve

variable {K F F' : Type*} [Field K] [Field F] [Field F']
  [Algebra K F] [Algebra K F'] [Algebra F F'] [IsScalarTower K F F']
  [Algebra.IsIntegral F F'] [HasPrincipalDivisors K F']

variable (K F F') in

class SumRamificationInertia : Prop where
  sum_ramificationIndex_mul_inertiaDeg : ∀ v : Place K F,
    ∑ w ∈ v.fiber F', (w.ramificationIndex F : ℤ) * (w.inertiaDeg F : ℤ)
      = (Module.finrank F F' : ℤ)

instance (priority := 100) instFundamentalIdentityOfSumRamificationInertia
    [SumRamificationInertia K F F'] : FundamentalIdentity K F F' where
  sum_ramificationIndex_mul_deg v := by
    have key := SumRamificationInertia.sum_ramificationIndex_mul_inertiaDeg
      (K := K) (F := F) (F' := F') v
    calc
      ∑ w ∈ v.fiber F', (w.ramificationIndex F : ℤ) * (w.deg : ℤ)
          = ∑ w ∈ v.fiber F',
              (v.deg : ℤ) * ((w.ramificationIndex F : ℤ) * (w.inertiaDeg F : ℤ)) := by
            refine Finset.sum_congr rfl fun w hw => ?_
            have htower : ((w.restrict F).deg : ℤ) * (w.inertiaDeg F : ℤ) = (w.deg : ℤ) := by
              exact_mod_cast congrArg (Nat.cast (R := ℤ))
                (w.deg_restrict_mul_inertiaDeg (F := F))
            rw [Place.mem_fiber.mp hw] at htower
            rw [← htower]; ring
      _ = (v.deg : ℤ) * ∑ w ∈ v.fiber F',
              (w.ramificationIndex F : ℤ) * (w.inertiaDeg F : ℤ) := by
            rw [Finset.mul_sum]
      _ = (Module.finrank F F' : ℤ) * (v.deg : ℤ) := by rw [key]; ring

namespace Pic0

variable (F') in

def pullbackDegZeroHom [FundamentalIdentity K F F'] :
    Divisor.degZero (K := K) (F := F) →+ Divisor.degZero (K := K) (F := F') :=
  ((Divisor.pullback F').domRestrict (Divisor.degZero (K := K) (F := F))).codRestrict _
    fun D => Divisor.pullback_mem_degZero D.2

@[simp]
theorem coe_pullbackDegZeroHom [FundamentalIdentity K F F']
    (D : Divisor.degZero (K := K) (F := F)) :
    (pullbackDegZeroHom F' D : Divisor K F') = Divisor.pullback F' (D : Divisor K F) := rfl

variable (F') in

def pullbackHom [FundamentalIdentity K F F'] : Pic0 K F →+ Pic0 K F' :=
  QuotientAddGroup.map _ _ (pullbackDegZeroHom F') (by
    rintro ⟨D, hD0⟩ hD
    simp only [AddSubgroup.mem_addSubgroupOf] at hD ⊢
    exact Divisor.pullback_mem_principal hD)

theorem pullbackHom_mk [FundamentalIdentity K F F']
    (D : Divisor.degZero (K := K) (F := F)) :
    pullbackHom F' (mk D) = mk (pullbackDegZeroHom F' D) := rfl

variable (F) in

def pushforwardDegZeroHom :
    Divisor.degZero (K := K) (F := F') →+ Divisor.degZero (K := K) (F := F) :=
  ((Divisor.pushforward F).domRestrict (Divisor.degZero (K := K) (F := F'))).codRestrict _
    fun D => Divisor.pushforward_mem_degZero D.2

omit [HasPrincipalDivisors K F'] in
@[simp]
theorem coe_pushforwardDegZeroHom (D : Divisor.degZero (K := K) (F := F')) :
    (pushforwardDegZeroHom F D : Divisor K F) = Divisor.pushforward F (D : Divisor K F') := rfl

variable (K F F') in

def pushforwardHom [Module.Finite F F'] (H : Divisor.PushforwardNormFormula K F F') :
    Pic0 K F' →+ Pic0 K F :=
  QuotientAddGroup.map _ _ (pushforwardDegZeroHom F) (by
    rintro ⟨D, hD0⟩ hD
    simp only [AddSubgroup.mem_addSubgroupOf] at hD ⊢
    exact Divisor.isPrincipal_pushforward_of_normFormula H hD)

omit [HasPrincipalDivisors K F'] in
theorem pushforwardHom_mk [Module.Finite F F'] (H : Divisor.PushforwardNormFormula K F F')
    (D : Divisor.degZero (K := K) (F := F')) :
    pushforwardHom K F F' H (mk D) = mk (pushforwardDegZeroHom F D) := rfl

end Pic0

end AlgebraicCurve

Statements phrased using this module (130)