Fermat's Last Theorem in Lean 4

← all definition modules

Definitions/Def_EllipticCurve_FunctionFieldPullback.lean

definition module

Generic point and function-field pull-backs for elliptic curves

Throughout, W is a Weierstrass curve over a field R and K an R-algebra which is a field; E = W_{/K} denotes the base-changed affine curve, K[E] its Mathlib coordinate ring K[X][Y]/(\,W\text{-polynomial}) and L = K(E) its function field (the fraction field of K[E]). A first group of results concerns K[E] as a K[X]-algebra: it is finite and integral over K[X], every nonzero prime ideal of it is maximal, and for K algebraically closed every nonzero prime equals \mathrm{XYIdeal}(a, C\,b) = (X-a,\;Y-b) for some solution (a,b) of the affine equation, these ideals being maximal. Point.xc, Point.yc read off the coordinates of an affine point (value 0 at the point at infinity). The generic point is the L-point \gamma whose coordinates genericX, genericY are the images in L of the classes of X and Y; that it satisfies the equation, hence is nonsingular when W is elliptic, is proved here. For a nonzero point Q \in E(L), pointHom Q is the K-algebra map K[E] \to L determined by X \mapsto x(Q), Y \mapsto y(Q); it is injective whenever K is algebraically closed and Q is not the base change of a K-point, and pointPull Q is then its extension L \to L along the fraction field. Specialising Q gives transPull/transEquiv (pull-back along translation by a K-point S, i.e. the map sending \gamma to \gamma + S), negPull/negEquiv (pull-back along inversion), and mulPull W K n, defined from the point n\cdot\gamma when the predicate MulGood W K n holds — namely n\cdot\gamma \ne 0 and n\cdot\gamma is not the base change of any K-point — and as the identity otherwise. The expected identities are proved: additivity and injectivity of S \mapsto \tau_S^{*}, \iota^{*}\circ\iota^{*} = \mathrm{id}, [m]^{*}[n]^{*} = [nm]^{*}, and \tau_S^{*}\circ[n]^{*} = [n]^{*} when nS = 0. placeOf W K P packages (X-x(P),\,Y-y(P)) as a height-one prime of K[E]; it is injective, and surjective onto the height-one spectrum for K algebraically closed and W elliptic, with CoordinateRing.evalAt the evaluation homomorphism K[E] \to K at a solution of the equation. Finally, Valuation.Cong v a b is the relation v(a-b) < 1 — congruence modulo the valuation ideal, not an identity — shown stable under sums, products and powers of elements of valuation \le 1, under inversion and division by elements of valuation 1, and under two-variable polynomial evaluation; from it and a valuation-transfer lemma for a ring map between fraction fields of Dedekind domains, the module deduces that v_P(\mathrm{pointPull}\,Q\,(h)) \le e^{-k} whenever v_{P'}(h) \le e^{-k}, for points whose coordinates are congruent at P, and the corresponding statement for mulPull in terms of polynomials \Phi, \Psi, \omega, \psi expressing the coordinates of n\cdot\gamma and of n\cdot P as rational functions of the coordinates.

Relation to Mathlib

Mathlib supplies the affine Weierstrass curve, its coordinate ring with XClass, YClass, XYIdeal, the function field, the group of affine points and base change of points; the generic point, the homomorphisms pointHom/pointPull and their translation, inversion and multiplication-by-n specialisations, the map from points to the height-one spectrum, and the valuation congruence relation Valuation.Cong are the project's own.

Where it is used

The material sets up the action of translations, inversion and multiplication by n on the function field of an elliptic curve, together with the dictionary between affine points over an algebraically closed field and height-one places of the coordinate ring, and a valuation comparison between a point P and its multiple n\cdot P.

References

  1. J. H. Silverman, The Arithmetic of Elliptic Curves, Graduate Texts in Mathematics 106, Springer, 2nd ed., 2009
  2. J.-P. Serre, Local Fields, Graduate Texts in Mathematics 67, Springer, 1979

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

Imports

  • only Mathlib

Imported by

Declarations

Source

import Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Point ↗
import Mathlib.RingTheory.DedekindDomain.Dvr ↗
import Mathlib.RingTheory.DedekindDomain.AdicValuation ↗
import Mathlib.RingTheory.DiscreteValuationRing.TFAE ↗
import Mathlib.FieldTheory.IsAlgClosed.Basic ↗
import Mathlib.RingTheory.Ideal.GoingUp ↗
import Mathlib.RingTheory.Localization.FractionRing ↗
import Mathlib.Tactic.FieldSimp ↗

open Polynomial
open scoped Polynomial.Bivariate

namespace WeierstrassCurve.Affine.CoordinateRing

variable {K : Type*} [Field K] {W : Affine K}

instance moduleFinite : Module.Finite K[X] W.CoordinateRing :=
  Module.Finite.of_basis (CoordinateRing.basis W)

instance isIntegral : Algebra.IsIntegral K[X] W.CoordinateRing :=
  Algebra.IsIntegral.of_finite K[X] W.CoordinateRing

theorem algebraMap_eq_mk_C (p : K[X]) : algebraMap K[X] W.CoordinateRing p = mk W (C p) := rfl

theorem isMaximal_of_isPrime {P : Ideal W.CoordinateRing} (hP : P ≠ ⊥) [P.IsPrime] :
    P.IsMaximal := by
  have hc : (P.comap (algebraMap K[X] W.CoordinateRing)) ≠ ⊥ := fun h =>
    hP (Ideal.eq_bot_of_comap_eq_bot h)
  haveI : (P.comap (algebraMap K[X] W.CoordinateRing)).IsMaximal :=
    IsPrime.to_maximal_ideal hc
  exact Ideal.isMaximal_of_isIntegral_of_isMaximal_comap (R := K[X]) P inferInstance

theorem exists_comap_eq_span [IsAlgClosed K] {P : Ideal W.CoordinateRing} (hP : P ≠ ⊥)
    [P.IsPrime] :
    ∃ a : K, P.comap (algebraMap K[X] W.CoordinateRing) = Ideal.span {X - C a} := by
  set p := P.comap (algebraMap K[X] W.CoordinateRing) with hp
  have hp0 : p ≠ ⊥ := fun h => hP (Ideal.eq_bot_of_comap_eq_bot h)
  haveI : p.IsPrime := Ideal.comap_isPrime _ P
  haveI hpmax : p.IsMaximal := IsPrime.to_maximal_ideal hp0
  set g := Submodule.IsPrincipal.generator p with hg
  have hgp : Ideal.span {g} = p := Ideal.span_singleton_generator p
  have hgprime : Prime g := Submodule.IsPrincipal.prime_generator_of_isPrime p hp0
  have hdeg : g.degree = 1 := IsAlgClosed.degree_eq_one_of_irreducible K hgprime.irreducible
  obtain ⟨a, ha⟩ := Polynomial.exists_root_of_degree_eq_one hdeg
  refine ⟨a, ?_⟩
  have hle : p ≤ Ideal.span {X - C a} := by
    rw [← hgp, Ideal.span_singleton_le_span_singleton]
    exact ⟨g /ₘ (X - C a), (Polynomial.mul_divByMonic_eq_iff_isRoot.mpr ha).symm⟩
  exact (hpmax.eq_of_le (Ideal.span_singleton_ne_top (Polynomial.not_isUnit_X_sub_C a)) hle).symm
    |>.symm

theorem XClass_mem_of_comap_eq {P : Ideal W.CoordinateRing} {a : K}
    (h : P.comap (algebraMap K[X] W.CoordinateRing) = Ideal.span {X - C a}) : XClass W a ∈ P := by
  have : X - C a ∈ P.comap (algebraMap K[X] W.CoordinateRing) := by
    rw [h]; exact Ideal.mem_span_singleton_self _
  exact this

theorem mk_C_mem_iff {P : Ideal W.CoordinateRing} {a : K}
    (h : P.comap (algebraMap K[X] W.CoordinateRing) = Ideal.span {X - C a}) (q : K[X]) :
    mk W (C q) ∈ P ↔ q.IsRoot a := by
  change q ∈ P.comap (algebraMap K[X] W.CoordinateRing) ↔ _
  rw [h, Ideal.mem_span_singleton, Polynomial.dvd_iff_isRoot]

theorem XYIdeal_isMaximal {a b : K} (h : W.Equation a b) : (XYIdeal W a (C b)).IsMaximal := by
  apply Ideal.Quotient.maximal_of_isField
  exact MulEquiv.isField (Field.toIsField K)
    (quotientXYIdealEquiv (W' := W) (x := a) (y := C b) h).toMulEquiv

theorem algebraMap_quot_apply (P : Ideal W.CoordinateRing) (c : K) :
    algebraMap K (W.CoordinateRing ⧸ P) c = Ideal.Quotient.mk P (mk W (C (C c))) := rfl

theorem algebraMap_quot_eq_comp_eval {P : Ideal W.CoordinateRing} {a : K}
    (h : P.comap (algebraMap K[X] W.CoordinateRing) = Ideal.span {X - C a}) :
    algebraMap K[X] (W.CoordinateRing ⧸ P) =
      (algebraMap K (W.CoordinateRing ⧸ P)).comp (evalRingHom a) := by
  apply Polynomial.ringHom_ext
  · intro c
    simp only [RingHom.coe_comp, Function.comp_apply, coe_evalRingHom, eval_C]
    rfl
  · simp only [RingHom.coe_comp, Function.comp_apply, coe_evalRingHom, eval_X]
    rw [algebraMap_quot_apply, IsScalarTower.algebraMap_apply K[X] W.CoordinateRing (W.CoordinateRing ⧸ P),
      Ideal.Quotient.algebraMap_eq, eq_comm, Ideal.Quotient.mk_eq_mk_iff_sub_mem]
    have : mk W (C (C a)) - algebraMap K[X] W.CoordinateRing X = -XClass W a := by
      simp only [algebraMap_eq_mk_C, XClass, map_sub]; abel
    rw [this]
    exact P.neg_mem (XClass_mem_of_comap_eq h)

theorem exists_eq_XYIdeal [IsAlgClosed K] {P : Ideal W.CoordinateRing} (hP : P ≠ ⊥) [P.IsPrime] :
    ∃ a b : K, W.Equation a b ∧ P = XYIdeal W a (C b) := by
  obtain ⟨a, ha⟩ := exists_comap_eq_span hP
  haveI hPmax : P.IsMaximal := isMaximal_of_isPrime hP
  set L := W.CoordinateRing ⧸ P
  set π := Ideal.Quotient.mk P
  have hθ := algebraMap_quot_eq_comp_eval ha

  haveI : Algebra.IsIntegral K L := ⟨fun z => by
    obtain ⟨m, hm, hm0⟩ := Algebra.IsIntegral.isIntegral (R := K[X]) z
    refine ⟨m.map (evalRingHom a), hm.map _, ?_⟩
    rw [eval₂_map, ← hθ]
    exact hm0⟩

  obtain ⟨b, hb⟩ := (IsAlgClosed.algebraMap_bijective_of_isIntegral (k := K) (K := L)).2 (π (mk W Y))
  have hYmem : YClass W (C b) ∈ P := by
    rw [YClass, map_sub, ← Ideal.Quotient.mk_eq_mk_iff_sub_mem]
    exact hb.symm

  have heq : W.Equation a b := by
    have hψ : π.comp (mk W) = (algebraMap K L).comp (evalEvalRingHom a b) := by
      apply Polynomial.ringHom_ext
      · intro q
        change algebraMap K[X] L q = _
        rw [hθ]
        simp only [RingHom.coe_comp, Function.comp_apply, coe_evalRingHom, eval_C]
        rfl
      · simp only [RingHom.coe_comp, Function.comp_apply, coe_evalRingHom, eval_X, eval_C]
        exact hb.symm
    have h0 : (π.comp (mk W)) W.polynomial = 0 := by
      rw [RingHom.comp_apply, AdjoinRoot.mk_self, map_zero]
    rw [hψ, RingHom.comp_apply, coe_evalEvalRingHom, map_eq_zero] at h0
    exact h0
  refine ⟨a, b, heq, ?_⟩

  have hle : XYIdeal W a (C b) ≤ P := by
    rw [XYIdeal, Ideal.span_le]
    rintro z (rfl | rfl)
    · exact XClass_mem_of_comap_eq ha
    · exact hYmem
  exact ((XYIdeal_isMaximal heq).eq_of_le hPmax.ne_top hle).symm

end WeierstrassCurve.Affine.CoordinateRing

namespace WeierstrassCurve.Affine

namespace Point

variable {S : Type*} [CommRing S] {V : Affine S}

def xc : V.Point → S
  | 0 => 0
  | some x _ _ => x

def yc : V.Point → S
  | 0 => 0
  | some _ y _ => y

@[simp] lemma xc_some {x y : S} (h : V.Nonsingular x y) : (some x y h).xc = x := rfl
@[simp] lemma yc_some {x y : S} (h : V.Nonsingular x y) : (some x y h).yc = y := rfl

lemma nonsingular_xc_yc {P : V.Point} (hP : P ≠ 0) : V.Nonsingular P.xc P.yc := by
  rcases P with _ | ⟨x, y, h⟩
  · exact absurd rfl hP
  · exact h

lemma eq_some_xc_yc {P : V.Point} (hP : P ≠ 0) : P = some P.xc P.yc (nonsingular_xc_yc hP) := by
  rcases P with _ | ⟨x, y, h⟩
  · exact absurd rfl hP
  · rfl

end Point

section Generic

variable {R : Type*} [Field R] (W : WeierstrassCurve R) (K : Type*) [Field K] [Algebra R K]

noncomputable instance instDecidableEqFunctionField : DecidableEq (W⁄K).FunctionField :=
  Classical.decEq _

noncomputable def genericX : (W⁄K).FunctionField :=
  algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField (CoordinateRing.mk (W⁄K) (C X))

noncomputable def genericY : (W⁄K).FunctionField :=
  algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField (CoordinateRing.mk (W⁄K) Y)

theorem evalEval_baseChange_polynomial (x y : (W⁄K).FunctionField) :
    (W⁄(W⁄K).FunctionField).polynomial.evalEval x y =
      (W⁄K).polynomial.eval₂ (aeval (R := K) x).toRingHom y := by
  rw [baseChange_polynomial W (Algebra.ofId K (W⁄K).FunctionField), evalEval, eval_map]
  have h := hom_eval₂ (W⁄K).polynomial (mapRingHom (Algebra.ofId K (W⁄K).FunctionField : K →+* _))
    (evalRingHom x) (C y)
  simp only [coe_evalRingHom, eval_C] at h
  rw [h]
  congr 1
  apply Polynomial.ringHom_ext
  · intro c
    simp only [RingHom.coe_comp, Function.comp_apply, coe_mapRingHom, map_C, coe_evalRingHom,
      eval_C, AlgHom.toRingHom_eq_coe, RingHom.coe_coe, aeval_C]
    rfl
  · simp only [RingHom.coe_comp, Function.comp_apply, coe_mapRingHom, map_X, coe_evalRingHom,
      eval_X, AlgHom.toRingHom_eq_coe, RingHom.coe_coe, aeval_X]

theorem algebraMap_comp_of :
    (algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField).comp (AdjoinRoot.of (W⁄K).polynomial) =
      (aeval (R := K) (genericX W K)).toRingHom := by
  apply Polynomial.ringHom_ext
  · intro c
    simp only [RingHom.coe_comp, Function.comp_apply, AlgHom.toRingHom_eq_coe, RingHom.coe_coe,
      aeval_C]
    rw [← AdjoinRoot.algebraMap_eq, ← IsScalarTower.algebraMap_apply]
    change (algebraMap K[X] (W⁄K).FunctionField) (algebraMap K K[X] c) = _
    exact (IsScalarTower.algebraMap_apply K K[X] (W⁄K).FunctionField c).symm
  · simp only [RingHom.coe_comp, Function.comp_apply, AlgHom.toRingHom_eq_coe, RingHom.coe_coe,
      aeval_X]
    rfl

theorem generic_equation :
    (W⁄(W⁄K).FunctionField).Equation (genericX W K) (genericY W K) := by
  rw [Equation, evalEval_baseChange_polynomial, ← algebraMap_comp_of]
  have h0 := congrArg (algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField)
    (AdjoinRoot.eval₂_root (W⁄K).polynomial)
  rw [map_zero, hom_eval₂] at h0
  exact h0

theorem generic_nonsingular [W.IsElliptic] :
    (W⁄(W⁄K).FunctionField).Nonsingular (genericX W K) (genericY W K) := by
  haveI : (W⁄(W⁄K).FunctionField).IsElliptic := by
    dsimp only [Affine.baseChange, WeierstrassCurve.baseChange]; infer_instance
  exact (equation_iff_nonsingular (W := W⁄(W⁄K).FunctionField)).mp (generic_equation W K)

noncomputable def genericPoint [W.IsElliptic] : (W⁄(W⁄K).FunctionField).Point :=
  .some _ _ (generic_nonsingular W K)

end Generic

section PointHom

variable {R : Type*} [Field R] {W : WeierstrassCurve R} {K : Type*} [Field K] [Algebra R K]

theorem eval₂_polynomial_of_point (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0) :
    (W⁄K).polynomial.eval₂ (aeval (R := K) Q.xc).toRingHom Q.yc = 0 := by
  rw [← evalEval_baseChange_polynomial]
  exact (Point.nonsingular_xc_yc hQ).left

noncomputable def pointHom (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0) :
    (W⁄K).CoordinateRing →ₐ[K] (W⁄K).FunctionField :=
  { AdjoinRoot.lift (aeval (R := K) Q.xc).toRingHom Q.yc (eval₂_polynomial_of_point Q hQ) with
    commutes' := fun c => by
      simp only [RingHom.toMonoidHom_eq_coe, OneHom.toFun_eq_coe, MonoidHom.toOneHom_coe,
        MonoidHom.coe_coe]
      rw [IsScalarTower.algebraMap_apply K (Polynomial K) (W⁄K).CoordinateRing,
        AdjoinRoot.algebraMap_eq, AdjoinRoot.lift_of, AlgHom.toRingHom_eq_coe, RingHom.coe_coe,
        Polynomial.algebraMap_apply, aeval_C, Algebra.algebraMap_self, RingHom.id_apply] }

theorem pointHom_mk (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0) (g : (Polynomial K)[X]) :
    pointHom Q hQ (CoordinateRing.mk (W⁄K) g) = g.eval₂ (aeval (R := K) Q.xc).toRingHom Q.yc :=
  AdjoinRoot.lift_mk (eval₂_polynomial_of_point Q hQ) g

@[simp] theorem pointHom_mk_C_C (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0) (c : K) :
    pointHom Q hQ (CoordinateRing.mk (W⁄K) (C (C c))) = algebraMap K _ c :=
  (pointHom Q hQ).commutes c

@[simp] theorem pointHom_mk_C_X (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0) :
    pointHom Q hQ (CoordinateRing.mk (W⁄K) (C X)) = Q.xc := by
  rw [pointHom_mk, eval₂_C, AlgHom.toRingHom_eq_coe, RingHom.coe_coe, aeval_X]

@[simp] theorem pointHom_mk_Y (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0) :
    pointHom Q hQ (CoordinateRing.mk (W⁄K) Y) = Q.yc := by
  rw [pointHom_mk, eval₂_X]

theorem CoordinateRing.algHom_ext' {A : Type*} [Semiring A] [Algebra K A]
    {f g : (W⁄K).CoordinateRing →ₐ[K] A}
    (hX : f (CoordinateRing.mk (W⁄K) (C X)) = g (CoordinateRing.mk (W⁄K) (C X)))
    (hY : f (CoordinateRing.mk (W⁄K) Y) = g (CoordinateRing.mk (W⁄K) Y)) : f = g := by
  apply AlgHom.coe_ringHom_injective
  apply Ideal.Quotient.ringHom_ext
  apply Polynomial.ringHom_ext
  · intro q
    change f (CoordinateRing.mk (W⁄K) (C q)) = g (CoordinateRing.mk (W⁄K) (C q))
    have hf : (f : (W⁄K).CoordinateRing →+* A).comp ((CoordinateRing.mk (W⁄K)).comp C) =
        (g : (W⁄K).CoordinateRing →+* A).comp ((CoordinateRing.mk (W⁄K)).comp C) := by
      apply Polynomial.ringHom_ext
      · intro c
        change f (algebraMap K _ c) = g (algebraMap K _ c)
        rw [AlgHom.commutes, AlgHom.commutes]
      · exact hX
    exact RingHom.congr_fun hf q
  · exact hY

theorem pointHom_genericPoint [W.IsElliptic] :
    pointHom (genericPoint W K) (Point.some_ne_zero _) =
      IsScalarTower.toAlgHom K (W⁄K).CoordinateRing (W⁄K).FunctionField := by
  apply CoordinateRing.algHom_ext'
  · rw [pointHom_mk_C_X, IsScalarTower.coe_toAlgHom', genericPoint, Point.xc_some]; rfl
  · rw [pointHom_mk_Y, IsScalarTower.coe_toAlgHom', genericPoint, Point.yc_some]; rfl

theorem Point.xc_map {F' L' : Type*} [Field F'] [Field L'] [Algebra R F'] [Algebra K F']
    [IsScalarTower R K F'] [Algebra R L'] [Algebra K L'] [IsScalarTower R K L']
    [DecidableEq F'] [DecidableEq L'] (f : F' →ₐ[K] L') (P : (W⁄F').Point) :
    (Point.map (W' := W) f P).xc = f P.xc := by
  cases P with
  | zero => exact (_root_.map_zero f).symm
  | some x y h => rfl

theorem Point.yc_map {F' L' : Type*} [Field F'] [Field L'] [Algebra R F'] [Algebra K F']
    [IsScalarTower R K F'] [Algebra R L'] [Algebra K L'] [IsScalarTower R K L']
    [DecidableEq F'] [DecidableEq L'] (f : F' →ₐ[K] L') (P : (W⁄F').Point) :
    (Point.map (W' := W) f P).yc = f P.yc := by
  cases P with
  | zero => exact (_root_.map_zero f).symm
  | some x y h => rfl

theorem pointHom_injective_of_ne_baseChange [DecidableEq K] [IsAlgClosed K] [W.IsElliptic]
    (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0)
    (hQK : ∀ P : (W⁄K).Point, Q ≠ Point.baseChange K (W⁄K).FunctionField P) :
    Function.Injective (pointHom Q hQ) := by
  rw [injective_iff_map_eq_zero]
  by_contra hcon
  push Not at hcon
  have hker : RingHom.ker (pointHom Q hQ) ≠ ⊥ := by
    obtain ⟨a, ha, ha0⟩ := hcon
    intro h
    have hm : a ∈ RingHom.ker (pointHom Q hQ) := (RingHom.mem_ker).mpr ha
    rw [h, Ideal.mem_bot] at hm
    exact ha0 hm
  haveI : (RingHom.ker (pointHom Q hQ)).IsPrime := RingHom.ker_isPrime _
  obtain ⟨a, b, hab, hker'⟩ := CoordinateRing.exists_eq_XYIdeal hker
  haveI : (W⁄K).IsElliptic := by
    dsimp only [Affine.baseChange, WeierstrassCurve.baseChange]; infer_instance
  have hns : (W⁄K).Nonsingular a b := (equation_iff_nonsingular (W := W⁄K)).mp hab
  have hx : Q.xc = algebraMap K _ a := by
    have : CoordinateRing.XClass (W⁄K) a ∈ RingHom.ker (pointHom Q hQ) := by
      rw [hker']; exact Ideal.subset_span (Set.mem_insert _ _)
    rw [RingHom.mem_ker, CoordinateRing.XClass, map_sub, map_sub, map_sub, pointHom_mk_C_X,
      pointHom_mk_C_C, sub_eq_zero] at this
    exact this
  have hy : Q.yc = algebraMap K _ b := by
    have : CoordinateRing.YClass (W⁄K) (C b) ∈ RingHom.ker (pointHom Q hQ) := by
      rw [hker']; exact Ideal.subset_span (Set.mem_insert_of_mem _ rfl)
    rw [RingHom.mem_ker, CoordinateRing.YClass, map_sub, map_sub, pointHom_mk_Y, pointHom_mk_C_C,
      sub_eq_zero] at this
    exact this
  apply hQK (.some a b hns)
  rw [Point.eq_some_xc_yc hQ, Point.baseChange, Point.map_some]
  congr 1

noncomputable def pointPull (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0)
    (hinj : Function.Injective (pointHom Q hQ)) :
    (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField :=
  IsFractionRing.liftAlgHom hinj

@[simp] theorem pointPull_algebraMap (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0)
    (hinj : Function.Injective (pointHom Q hQ)) (h : (W⁄K).CoordinateRing) :
    pointPull Q hQ hinj (algebraMap _ _ h) = pointHom Q hQ h := by
  rw [pointPull, IsFractionRing.liftAlgHom_apply]
  exact IsFractionRing.lift_algebraMap hinj h

theorem map_pointPull_genericPoint [W.IsElliptic] (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0)
    (hinj : Function.Injective (pointHom Q hQ)) :
    Point.map (pointPull Q hQ hinj) (genericPoint W K) = Q := by
  conv_rhs => rw [Point.eq_some_xc_yc hQ]
  rw [genericPoint, Point.map_some]
  congr 1
  · rw [genericX, pointPull_algebraMap, pointHom_mk_C_X]
  · rw [genericY, pointPull_algebraMap, pointHom_mk_Y]

theorem pointPull_comp_pointHom (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0)
    (hinj : Function.Injective (pointHom Q hQ)) (P : (W⁄(W⁄K).FunctionField).Point) (hP : P ≠ 0)
    (hP' : Point.map (pointPull Q hQ hinj) P ≠ 0) :
    (pointPull Q hQ hinj).comp (pointHom P hP) = pointHom (Point.map (pointPull Q hQ hinj) P) hP' := by
  apply CoordinateRing.algHom_ext'
  · rw [AlgHom.comp_apply, pointHom_mk_C_X, pointHom_mk_C_X, Point.xc_map]
  · rw [AlgHom.comp_apply, pointHom_mk_Y, pointHom_mk_Y, Point.yc_map]

theorem pointHom_congr {Q Q' : (W⁄(W⁄K).FunctionField).Point} (h : Q = Q') (hQ : Q ≠ 0) (hQ' : Q' ≠ 0) :
    pointHom Q hQ = pointHom Q' hQ' := by subst h; rfl

theorem pointPull_comp_algebraMap (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0)
    (hinj : Function.Injective (pointHom Q hQ)) :
    (pointPull Q hQ hinj : (W⁄K).FunctionField →+* (W⁄K).FunctionField).comp
      (algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField) = pointHom Q hQ :=
  RingHom.ext fun h => pointPull_algebraMap Q hQ hinj h

theorem FunctionField.algHom_ext {φ ψ : (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField}
    (h : (φ : (W⁄K).FunctionField →+* (W⁄K).FunctionField).comp
        (algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField) =
      (ψ : (W⁄K).FunctionField →+* (W⁄K).FunctionField).comp
        (algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField)) : φ = ψ :=
  AlgHom.coe_ringHom_injective
    (IsLocalization.ringHom_ext (nonZeroDivisors (W⁄K).CoordinateRing) h)

theorem exists_eq_baseChange_of_isIntegral_xc [DecidableEq K] [IsAlgClosed K] [W.IsElliptic]
    (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0) (hx : _root_.IsIntegral K Q.xc) :
    ∃ P : (W⁄K).Point, Q = Point.baseChange K (W⁄K).FunctionField P := by
  haveI : (W⁄K).IsElliptic := by
    dsimp only [Affine.baseChange, WeierstrassCurve.baseChange]; infer_instance

  have hdx : (minpoly K Q.xc).degree = 1 :=
    IsAlgClosed.degree_eq_one_of_irreducible K (minpoly.irreducible hx)
  obtain ⟨a, ha⟩ : Q.xc ∈ (algebraMap K (W⁄K).FunctionField).range := minpoly.mem_range_of_degree_eq_one K _ hdx

  have heq := (Point.nonsingular_xc_yc hQ).left
  rw [equation_iff] at heq
  have hy : _root_.IsIntegral K Q.yc := by
    refine ⟨X ^ 2 + C ((W⁄K).a₁ * a + (W⁄K).a₃) * X - C (a ^ 3 + (W⁄K).a₂ * a ^ 2 + (W⁄K).a₄ * a + (W⁄K).a₆),
      ?_, ?_⟩
    · have hlt : (C ((W⁄K).a₁ * a + (W⁄K).a₃) * X -
          C (a ^ 3 + (W⁄K).a₂ * a ^ 2 + (W⁄K).a₄ * a + (W⁄K).a₆) : K[X]).degree < ((X : K[X]) ^ 2).degree := by
        rw [degree_X_pow]
        refine (degree_sub_le _ _).trans_lt (max_lt ((degree_C_mul_X_le _).trans_lt ?_)
          (degree_C_le.trans_lt ?_))
        · exact WithBot.coe_lt_coe.mpr (by norm_num)
        · exact WithBot.coe_lt_coe.mpr (by norm_num)
      rw [add_sub_assoc]
      exact (monic_X_pow 2).add_of_left hlt
    · simp only [eval₂_add, eval₂_sub, eval₂_pow, eval₂_X, eval₂_mul, eval₂_C, map_add, map_mul, map_pow]
      rw [ha]
      have e1 : (algebraMap K (W⁄K).FunctionField) (W⁄K).a₁ = (W⁄(W⁄K).FunctionField).a₁ :=
        (IsScalarTower.algebraMap_apply R K (W⁄K).FunctionField W.a₁).symm
      have e2 : (algebraMap K (W⁄K).FunctionField) (W⁄K).a₂ = (W⁄(W⁄K).FunctionField).a₂ :=
        (IsScalarTower.algebraMap_apply R K (W⁄K).FunctionField W.a₂).symm
      have e3 : (algebraMap K (W⁄K).FunctionField) (W⁄K).a₃ = (W⁄(W⁄K).FunctionField).a₃ :=
        (IsScalarTower.algebraMap_apply R K (W⁄K).FunctionField W.a₃).symm
      have e4 : (algebraMap K (W⁄K).FunctionField) (W⁄K).a₄ = (W⁄(W⁄K).FunctionField).a₄ :=
        (IsScalarTower.algebraMap_apply R K (W⁄K).FunctionField W.a₄).symm
      have e6 : (algebraMap K (W⁄K).FunctionField) (W⁄K).a₆ = (W⁄(W⁄K).FunctionField).a₆ :=
        (IsScalarTower.algebraMap_apply R K (W⁄K).FunctionField W.a₆).symm
      rw [e1, e2, e3, e4, e6]
      linear_combination heq
  have hdy : (minpoly K Q.yc).degree = 1 :=
    IsAlgClosed.degree_eq_one_of_irreducible K (minpoly.irreducible hy)
  obtain ⟨b, hb⟩ : Q.yc ∈ (algebraMap K (W⁄K).FunctionField).range := minpoly.mem_range_of_degree_eq_one K _ hdy
  have hab : (W⁄K).Equation a b := by
    rw [← baseChange_equation W (Algebra.ofId K (W⁄K).FunctionField).injective a b]
    change (W⁄(W⁄K).FunctionField).Equation (algebraMap K _ a) (algebraMap K _ b)
    rw [ha, hb]
    exact (Point.nonsingular_xc_yc hQ).left
  have hns : (W⁄K).Nonsingular a b := (equation_iff_nonsingular (W := W⁄K)).mp hab
  refine ⟨.some a b hns, ?_⟩
  rw [Point.eq_some_xc_yc hQ, Point.baseChange, Point.map_some]
  congr 1 <;> [exact ha.symm; exact hb.symm]

theorem not_isIntegral_xc_of_ne_baseChange [DecidableEq K] [IsAlgClosed K] [W.IsElliptic]
    (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0)
    (hQK : ∀ P : (W⁄K).Point, Q ≠ Point.baseChange K (W⁄K).FunctionField P) :
    ¬ _root_.IsIntegral K Q.xc :=
  fun hx => by obtain ⟨P, hP⟩ := exists_eq_baseChange_of_isIntegral_xc Q hQ hx; exact hQK P hP

end PointHom

section TransMul

variable {R : Type*} [Field R] (W : WeierstrassCurve R) (K : Type*) [Field K] [Algebra R K]
  [DecidableEq K] [IsAlgClosed K] [W.IsElliptic]

omit [IsAlgClosed K] in

theorem genericPoint_ne_baseChange (P : (W⁄K).Point) :
    genericPoint W K ≠ Point.baseChange K (W⁄K).FunctionField P := by
  intro h
  cases P with
  | zero => exact Point.some_ne_zero _ (h.trans (map_zero _))
  | some a b hab =>
    rw [genericPoint, Point.baseChange, Point.map_some, Point.some.injEq] at h
    apply CoordinateRing.XClass_ne_zero (W' := W⁄K) a
    apply IsFractionRing.injective (W⁄K).CoordinateRing (W⁄K).FunctionField
    rw [CoordinateRing.XClass, map_sub, map_sub, map_sub, _root_.map_zero]
    have h1 : algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField (CoordinateRing.mk (W⁄K) (C X)) =
        genericX W K := rfl
    have h2 : algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField (CoordinateRing.mk (W⁄K) (C (C a))) =
        algebraMap K (W⁄K).FunctionField a :=
      (IsScalarTower.algebraMap_apply K (W⁄K).CoordinateRing (W⁄K).FunctionField a).symm
    rw [h1, h2, h.1, sub_eq_zero]
    rfl

omit [IsAlgClosed K] in

theorem genericPoint_add_baseChange_ne_zero (S : (W⁄K).Point) :
    genericPoint W K + Point.baseChange K _ S ≠ 0 := by
  intro h
  apply genericPoint_ne_baseChange W K (-S)
  rw [map_neg]
  exact eq_neg_of_add_eq_zero_left h

omit [IsAlgClosed K] in

theorem genericPoint_add_baseChange_ne_baseChange (S P : (W⁄K).Point) :
    genericPoint W K + Point.baseChange K _ S ≠ Point.baseChange K _ P := by
  intro h
  apply genericPoint_ne_baseChange W K (P - S)
  rw [map_sub, ← h, add_sub_cancel_right]

theorem transHom_injective (S : (W⁄K).Point) :
    Function.Injective (pointHom (genericPoint W K + Point.baseChange K _ S)
      (genericPoint_add_baseChange_ne_zero W K S)) :=
  pointHom_injective_of_ne_baseChange _ _ (genericPoint_add_baseChange_ne_baseChange W K S)

noncomputable def transPull (S : (W⁄K).Point) : (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField :=
  pointPull _ (genericPoint_add_baseChange_ne_zero W K S) (transHom_injective W K S)

theorem map_transPull_genericPoint (S : (W⁄K).Point) :
    Point.map (transPull W K S) (genericPoint W K) = genericPoint W K + Point.baseChange K _ S :=
  map_pointPull_genericPoint _ _ _

theorem map_transPull_genericPoint_add (S T : (W⁄K).Point) :
    Point.map (transPull W K S) (genericPoint W K + Point.baseChange K _ T) =
      genericPoint W K + Point.baseChange K _ (S + T) := by
  rw [map_add, map_transPull_genericPoint, Point.map_baseChange, add_assoc, ← map_add]

theorem transPull_comp_algebraMap (S : (W⁄K).Point) :
    (transPull W K S : (W⁄K).FunctionField →+* (W⁄K).FunctionField).comp
      (algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField) =
      pointHom (genericPoint W K + Point.baseChange K _ S) (genericPoint_add_baseChange_ne_zero W K S) :=
  pointPull_comp_algebraMap _ (genericPoint_add_baseChange_ne_zero W K S) (transHom_injective W K S)

theorem transPull_comp_pointHom (S : (W⁄K).Point) (P : (W⁄(W⁄K).FunctionField).Point) (hP : P ≠ 0)
    (hP' : Point.map (transPull W K S) P ≠ 0) :
    (transPull W K S).comp (pointHom P hP) = pointHom (Point.map (transPull W K S) P) hP' :=
  pointPull_comp_pointHom _ _ _ P hP hP'

theorem transPull_comp_transHom (S T : (W⁄K).Point) :
    (transPull W K S).comp (pointHom (genericPoint W K + Point.baseChange K _ T)
      (genericPoint_add_baseChange_ne_zero W K T)) =
      pointHom (genericPoint W K + Point.baseChange K _ (S + T))
        (genericPoint_add_baseChange_ne_zero W K (S + T)) := by
  apply CoordinateRing.algHom_ext'
  · rw [AlgHom.comp_apply, pointHom_mk_C_X, pointHom_mk_C_X, ← map_transPull_genericPoint_add,
      Point.xc_map]
  · rw [AlgHom.comp_apply, pointHom_mk_Y, pointHom_mk_Y, ← map_transPull_genericPoint_add,
      Point.yc_map]

theorem transPull_comp (S T : (W⁄K).Point) :
    (transPull W K S).comp (transPull W K T) = transPull W K (S + T) := by
  apply FunctionField.algHom_ext
  rw [AlgHom.comp_toRingHom, RingHom.comp_assoc, transPull_comp_algebraMap W K T,
    transPull_comp_algebraMap W K (S + T), ← AlgHom.comp_toRingHom, transPull_comp_transHom]

theorem transPull_zero : transPull W K 0 = AlgHom.id K _ := by
  apply FunctionField.algHom_ext
  rw [transPull_comp_algebraMap]
  have h0 : genericPoint W K + Point.baseChange K (W⁄K).FunctionField (0 : (W⁄K).Point) =
      genericPoint W K := by
    rw [map_zero, add_zero]
  rw [pointHom_congr h0 _ (Point.some_ne_zero _), pointHom_genericPoint]
  exact RingHom.ext fun x => rfl

noncomputable def transEquiv (S : (W⁄K).Point) : (W⁄K).FunctionField ≃ₐ[K] (W⁄K).FunctionField :=
  AlgEquiv.ofAlgHom (transPull W K S) (transPull W K (-S))
    (by rw [transPull_comp, add_neg_cancel, transPull_zero])
    (by rw [transPull_comp, neg_add_cancel, transPull_zero])

theorem transEquiv_apply (S : (W⁄K).Point) (h : (W⁄K).FunctionField) :
    transEquiv W K S h = transPull W K S h := rfl

theorem coe_transEquiv (S : (W⁄K).Point) :
    (transEquiv W K S : (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField) = transPull W K S := rfl

theorem transEquiv_algebraMap (S : (W⁄K).Point) (h : (W⁄K).CoordinateRing) :
    transEquiv W K S (algebraMap _ _ h) =
      pointHom (genericPoint W K + Point.baseChange K _ S) (genericPoint_add_baseChange_ne_zero W K S) h :=
  pointPull_algebraMap _ (genericPoint_add_baseChange_ne_zero W K S) (transHom_injective W K S) h

theorem transEquiv_genericX (S : (W⁄K).Point) :
    transEquiv W K S (genericX W K) = (genericPoint W K + Point.baseChange K _ S).xc := by
  rw [genericX, transEquiv_algebraMap, pointHom_mk_C_X]

theorem transEquiv_genericY (S : (W⁄K).Point) :
    transEquiv W K S (genericY W K) = (genericPoint W K + Point.baseChange K _ S).yc := by
  rw [genericY, transEquiv_algebraMap, pointHom_mk_Y]

theorem map_transEquiv_genericPoint (S : (W⁄K).Point) :
    Point.map (transEquiv W K S : (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField) (genericPoint W K) =
      genericPoint W K + Point.baseChange K _ S := by
  rw [coe_transEquiv, map_transPull_genericPoint]

theorem transEquiv_add (S T : (W⁄K).Point) :
    transEquiv W K (S + T) = (transEquiv W K T).trans (transEquiv W K S) := by
  apply AlgEquiv.ext
  intro h
  rw [AlgEquiv.trans_apply, transEquiv_apply, transEquiv_apply, transEquiv_apply,
    ← AlgHom.comp_apply, transPull_comp]

theorem transEquiv_zero : transEquiv W K 0 = AlgEquiv.refl := by
  apply AlgEquiv.ext
  intro h
  rw [transEquiv_apply, transPull_zero]
  rfl

theorem transEquiv_injective : Function.Injective (transEquiv W K) := by
  intro S T h
  have hx := transEquiv_genericX W K S
  have hy := transEquiv_genericY W K S
  rw [h, transEquiv_genericX] at hx
  rw [h, transEquiv_genericY] at hy
  have hpt : genericPoint W K + Point.baseChange K _ T = genericPoint W K + Point.baseChange K _ S := by
    rw [Point.eq_some_xc_yc (genericPoint_add_baseChange_ne_zero W K T),
      Point.eq_some_xc_yc (genericPoint_add_baseChange_ne_zero W K S)]
    congr 1
  exact (Point.map_injective _ (add_left_cancel hpt)).symm

omit [IsAlgClosed K] in
theorem neg_genericPoint_ne_baseChange (P : (W⁄K).Point) :
    -genericPoint W K ≠ Point.baseChange K (W⁄K).FunctionField P := by
  intro h
  apply genericPoint_ne_baseChange W K (-P)
  rw [map_neg, ← h, neg_neg]

theorem negHom_injective :
    Function.Injective (pointHom (-genericPoint W K) (neg_ne_zero.mpr (Point.some_ne_zero _))) :=
  pointHom_injective_of_ne_baseChange _ _ (neg_genericPoint_ne_baseChange W K)

noncomputable def negPull : (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField :=
  pointPull _ (neg_ne_zero.mpr (Point.some_ne_zero _)) (negHom_injective W K)

theorem map_negPull_genericPoint : Point.map (negPull W K) (genericPoint W K) = -genericPoint W K :=
  map_pointPull_genericPoint _ _ _

theorem negPull_algebraMap (g : (W⁄K).CoordinateRing) :
    negPull W K (algebraMap _ _ g) = pointHom (-genericPoint W K) (neg_ne_zero.mpr (Point.some_ne_zero _)) g :=
  pointPull_algebraMap _ _ _ g

theorem negPull_comp_algebraMap :
    (negPull W K : (W⁄K).FunctionField →+* (W⁄K).FunctionField).comp
      (algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField) =
      pointHom (-genericPoint W K) (neg_ne_zero.mpr (Point.some_ne_zero _)) :=
  pointPull_comp_algebraMap _ _ _

theorem negPull_comp_pointHom (P : (W⁄(W⁄K).FunctionField).Point) (hP : P ≠ 0)
    (hP' : Point.map (negPull W K) P ≠ 0) :
    (negPull W K).comp (pointHom P hP) = pointHom (Point.map (negPull W K) P) hP' :=
  pointPull_comp_pointHom _ _ _ P hP hP'

theorem negPull_comp_negPull : (negPull W K).comp (negPull W K) = AlgHom.id K _ := by
  apply FunctionField.algHom_ext
  have hP' : Point.map (negPull W K) (-genericPoint W K) = genericPoint W K := by
    rw [map_neg, map_negPull_genericPoint, neg_neg]
  rw [AlgHom.comp_toRingHom, RingHom.comp_assoc, negPull_comp_algebraMap, ← AlgHom.comp_toRingHom,
    negPull_comp_pointHom W K _ _ (by rw [hP']; exact Point.some_ne_zero _),
    pointHom_congr hP' _ (Point.some_ne_zero _), pointHom_genericPoint]
  exact RingHom.ext fun x => rfl

noncomputable def negEquiv : (W⁄K).FunctionField ≃ₐ[K] (W⁄K).FunctionField :=
  AlgEquiv.ofAlgHom (negPull W K) (negPull W K) (negPull_comp_negPull W K) (negPull_comp_negPull W K)

theorem coe_negEquiv : (negEquiv W K : (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField) = negPull W K := rfl

theorem negEquiv_apply (h : (W⁄K).FunctionField) : negEquiv W K h = negPull W K h := rfl

theorem map_negEquiv_genericPoint :
    Point.map (negEquiv W K : (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField) (genericPoint W K) =
      -genericPoint W K := by
  rw [coe_negEquiv, map_negPull_genericPoint]

theorem negEquiv_genericX : negEquiv W K (genericX W K) = genericX W K := by
  rw [negEquiv_apply, genericX, negPull_algebraMap, pointHom_mk_C_X]
  simp only [genericPoint, Point.neg_some, Point.xc_some]
  rfl

theorem negEquiv_genericY :
    negEquiv W K (genericY W K) = (W⁄(W⁄K).FunctionField).negY (genericX W K) (genericY W K) := by
  rw [negEquiv_apply, genericY, negPull_algebraMap, pointHom_mk_Y]
  simp only [genericPoint, Point.neg_some, Point.yc_some]
  rfl

theorem negEquiv_symm : (negEquiv W K).symm = negEquiv W K := rfl

def MulGood (n : ℤ) : Prop :=
  n • genericPoint W K ≠ 0
    ∀ P : (W⁄K).Point, n • genericPoint W K ≠ Point.baseChange K (W⁄K).FunctionField P

theorem mulHom_injective {n : ℤ} (h : MulGood W K n) :
    Function.Injective (pointHom (n • genericPoint W K) h.1) :=
  pointHom_injective_of_ne_baseChange _ _ h.2

open Classical in

noncomputable def mulPull (n : ℤ) : (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField :=
  if h : MulGood W K n then pointPull (n • genericPoint W K) h.1 (mulHom_injective W K h)
  else AlgHom.id K _

theorem mulPull_eq {n : ℤ} (h : MulGood W K n) :
    mulPull W K n = pointPull (n • genericPoint W K) h.1 (mulHom_injective W K h) := by
  rw [mulPull, dif_pos h]

theorem mulPull_algebraMap {n : ℤ} (h : MulGood W K n) (g : (W⁄K).CoordinateRing) :
    mulPull W K n (algebraMap _ _ g) = pointHom (n • genericPoint W K) h.1 g := by
  rw [mulPull_eq W K h, pointPull_algebraMap]

theorem mulPull_comp_algebraMap {n : ℤ} (h : MulGood W K n) :
    (mulPull W K n : (W⁄K).FunctionField →+* (W⁄K).FunctionField).comp
      (algebraMap (W⁄K).CoordinateRing (W⁄K).FunctionField) = pointHom (n • genericPoint W K) h.1 :=
  RingHom.ext fun g => mulPull_algebraMap W K h g

theorem mulPull_genericX' {n : ℤ} (h : MulGood W K n) :
    mulPull W K n (genericX W K) = (n • genericPoint W K).xc := by
  rw [genericX, mulPull_algebraMap W K h, pointHom_mk_C_X]

theorem mulPull_genericY' {n : ℤ} (h : MulGood W K n) :
    mulPull W K n (genericY W K) = (n • genericPoint W K).yc := by
  rw [genericY, mulPull_algebraMap W K h, pointHom_mk_Y]

theorem map_mulPull_genericPoint' {n : ℤ} (h : MulGood W K n) :
    Point.map (mulPull W K n) (genericPoint W K) = n • genericPoint W K := by
  rw [mulPull_eq W K h, map_pointPull_genericPoint]

theorem mulPull_comp_pointHom {n : ℤ} (h : MulGood W K n) (P : (W⁄(W⁄K).FunctionField).Point)
    (hP : P ≠ 0) (hP' : Point.map (mulPull W K n) P ≠ 0) :
    (mulPull W K n).comp (pointHom P hP) = pointHom (Point.map (mulPull W K n) P) hP' := by
  have hP'' : Point.map (pointPull (n • genericPoint W K) h.1 (mulHom_injective W K h)) P ≠ 0 := by
    rw [← mulPull_eq W K h]; exact hP'
  rw [pointHom_congr (congrArg (fun φ => Point.map φ P) (mulPull_eq W K h)) hP' hP'', mulPull_eq W K h]
  exact pointPull_comp_pointHom _ _ _ P hP hP''

theorem transEquiv_mulPull' {n : ℤ} (h : MulGood W K n) {S : (W⁄K).Point} (hS : n • S = 0)
    (g : (W⁄K).FunctionField) : transEquiv W K S (mulPull W K n g) = mulPull W K n g := by
  rw [transEquiv_apply, ← AlgHom.comp_apply]
  congr 1
  apply FunctionField.algHom_ext
  have hP' : Point.map (transPull W K S) (n • genericPoint W K) = n • genericPoint W K := by
    rw [map_zsmul, map_transPull_genericPoint, smul_add, ← map_zsmul, hS, map_zero, add_zero]
  rw [AlgHom.comp_toRingHom, RingHom.comp_assoc, mulPull_comp_algebraMap W K h, ← AlgHom.comp_toRingHom,
    transPull_comp_pointHom W K S _ _ (by rw [hP']; exact h.1)]
  exact congrArg _ (pointHom_congr hP' _ _)

theorem mulPull_comp' {m n : ℤ} (hm : MulGood W K m) (hn : MulGood W K n) (hnm : MulGood W K (n * m)) :
    (mulPull W K m).comp (mulPull W K n) = mulPull W K (n * m) := by
  apply FunctionField.algHom_ext
  have hP' : Point.map (mulPull W K m) (n • genericPoint W K) = (n * m) • genericPoint W K := by
    rw [map_zsmul, map_mulPull_genericPoint' W K hm, smul_smul]
  rw [AlgHom.comp_toRingHom, RingHom.comp_assoc, mulPull_comp_algebraMap W K hn,
    mulPull_comp_algebraMap W K hnm, ← AlgHom.comp_toRingHom,
    mulPull_comp_pointHom W K hm _ _ (by rw [hP']; exact hnm.1)]
  exact congrArg _ (pointHom_congr hP' _ _)

omit [DecidableEq K] in

theorem exists_point_xc_eq (x : K) : ∃ P : (W⁄K).Point, P ≠ 0 ∧ P.xc = x := by
  haveI : (W⁄K).IsElliptic := by
    dsimp only [Affine.baseChange, WeierstrassCurve.baseChange]; infer_instance
  set c := (W⁄K).a₁ * x + (W⁄K).a₃ with hc
  set d := -(x ^ 3 + (W⁄K).a₂ * x ^ 2 + (W⁄K).a₄ * x + (W⁄K).a₆) with hd
  have hlt : (C c * X + C d : K[X]).degree < ((X : K[X]) ^ 2).degree := by
    rw [degree_X_pow]
    refine (degree_add_le _ _).trans_lt (max_lt ((degree_C_mul_X_le c).trans_lt ?_)
      (degree_C_le.trans_lt ?_))
    · exact WithBot.coe_lt_coe.mpr (by norm_num)
    · exact WithBot.coe_lt_coe.mpr (by norm_num)
  have hdeg : ((X : K[X]) ^ 2 + (C c * X + C d)).degree = 2 := by
    rw [degree_add_eq_left_of_degree_lt hlt, degree_X_pow]; rfl
  obtain ⟨y, hy⟩ := IsAlgClosed.exists_root ((X : K[X]) ^ 2 + (C c * X + C d))
    (by rw [hdeg]; exact two_ne_zero)
  have hroot : y ^ 2 + (c * y + d) = 0 := by
    have := hy.eq_zero
    simpa only [eval_add, eval_pow, eval_X, eval_mul, eval_C] using this
  have heq : (W⁄K).Equation x y := by
    rw [equation_iff]
    rw [hc, hd] at hroot
    linear_combination hroot
  exact ⟨.some x y ((equation_iff_nonsingular (W := W⁄K)).mp heq), Point.some_ne_zero _, rfl⟩

omit [DecidableEq K] in
theorem infinite_point : Infinite (W⁄K).Point := by
  choose f hf0 hfx using exists_point_xc_eq W K
  exact Infinite.of_injective f fun x y h => by rw [← hfx x, ← hfx y, h]

theorem map_negEquiv_zsmul_genericPoint (n : ℤ) :
    Point.map (negEquiv W K : (W⁄K).FunctionField →ₐ[K] (W⁄K).FunctionField) (n • genericPoint W K) =
      -(n • genericPoint W K) := by
  rw [map_zsmul, map_negEquiv_genericPoint, smul_neg]

theorem negEquiv_mulPull_genericX' {n : ℤ} (h : MulGood W K n) :
    negEquiv W K (mulPull W K n (genericX W K)) = (n • genericPoint W K).xc := by
  rw [mulPull_genericX' W K h, ← AlgEquiv.coe_algHom, ← Point.xc_map (W := W) (K := K),
    map_negEquiv_zsmul_genericPoint, Point.eq_some_xc_yc h.1, Point.neg_some, Point.xc_some, Point.xc_some]

theorem negEquiv_mulPull_genericY' {n : ℤ} (h : MulGood W K n) :
    negEquiv W K (mulPull W K n (genericY W K)) =
      (W⁄(W⁄K).FunctionField).negY (n • genericPoint W K).xc (n • genericPoint W K).yc := by
  rw [mulPull_genericY' W K h, ← AlgEquiv.coe_algHom, ← Point.yc_map (W := W) (K := K),
    map_negEquiv_zsmul_genericPoint]
  conv_lhs => rw [Point.eq_some_xc_yc h.1, Point.neg_some, Point.yc_some]

omit [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] in
theorem mulPull_injective [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] (n : ℤ) :
    Function.Injective (mulPull W K n) := (mulPull W K n).injective

end TransMul

section Places

variable {R : Type*} [Field R] (W : WeierstrassCurve R) (K : Type*) [Field K] [Algebra R K]
  [DecidableEq K]

noncomputable def placeOf (P : (W⁄K).Point) (hP : P ≠ 0) :
    IsDedekindDomain.HeightOneSpectrum (W⁄K).CoordinateRing where
  asIdeal := CoordinateRing.XYIdeal (W⁄K) P.xc (C P.yc)
  isPrime := (CoordinateRing.XYIdeal_isMaximal (Point.nonsingular_xc_yc hP).left).isPrime
  ne_bot h := CoordinateRing.XClass_ne_zero (W' := W⁄K) P.xc
    (by rw [← Ideal.mem_bot, ← h]; exact Ideal.subset_span (Set.mem_insert _ _))

omit [DecidableEq K] in
@[simp] theorem placeOf_asIdeal (P : (W⁄K).Point) (hP : P ≠ 0) :
    (placeOf W K P hP).asIdeal = CoordinateRing.XYIdeal (W⁄K) P.xc (C P.yc) := rfl

omit [DecidableEq K] in

theorem placeOf_surjective [IsAlgClosed K] [W.IsElliptic]
    (v : IsDedekindDomain.HeightOneSpectrum (W⁄K).CoordinateRing) :
    ∃ (P : (W⁄K).Point) (hP : P ≠ 0), placeOf W K P hP = v := by
  haveI : (W⁄K).IsElliptic := by
    dsimp only [Affine.baseChange, WeierstrassCurve.baseChange]; infer_instance
  haveI := v.isPrime
  obtain ⟨a, b, hab, hv⟩ := CoordinateRing.exists_eq_XYIdeal v.ne_bot
  refine ⟨.some a b ((equation_iff_nonsingular (W := W⁄K)).mp hab), Point.some_ne_zero _, ?_⟩
  exact IsDedekindDomain.HeightOneSpectrum.ext hv.symm

noncomputable def CoordinateRing.evalAt {a b : K} (h : (W⁄K).Equation a b) : (W⁄K).CoordinateRing →+* K :=
  AdjoinRoot.evalEval h

omit [DecidableEq K] in
theorem CoordinateRing.evalAt_mk {a b : K} (h : (W⁄K).Equation a b) (g : K[X][Y]) :
    CoordinateRing.evalAt W K h (CoordinateRing.mk (W⁄K) g) = g.evalEval a b :=
  AdjoinRoot.evalEval_mk h g

omit [DecidableEq K] in
theorem CoordinateRing.evalAt_XYIdeal {a b : K} (h : (W⁄K).Equation a b) :
    ∀ g ∈ CoordinateRing.XYIdeal (W⁄K) a (C b), CoordinateRing.evalAt W K h g = 0 := by
  intro g hg
  rw [← RingHom.mem_ker]
  revert hg g
  rw [← SetLike.le_def, CoordinateRing.XYIdeal, Ideal.span_le]
  rintro g (rfl | rfl)
  · rw [SetLike.mem_coe, RingHom.mem_ker, CoordinateRing.XClass, CoordinateRing.evalAt_mk,
      evalEval_C, eval_sub, eval_X, eval_C, sub_self]
  · rw [SetLike.mem_coe, RingHom.mem_ker, CoordinateRing.YClass, CoordinateRing.evalAt_mk,
      evalEval_sub, evalEval_X, evalEval_C, eval_C, sub_self]

omit [DecidableEq K] in
theorem placeOf_injective {P Q : (W⁄K).Point} (hP : P ≠ 0) (hQ : Q ≠ 0)
    (h : placeOf W K P hP = placeOf W K Q hQ) : P = Q := by
  have hI : CoordinateRing.XYIdeal (W⁄K) P.xc (C P.yc) = CoordinateRing.XYIdeal (W⁄K) Q.xc (C Q.yc) :=
    congrArg IsDedekindDomain.HeightOneSpectrum.asIdeal h
  have hev := CoordinateRing.evalAt_XYIdeal W K (Point.nonsingular_xc_yc hP).left
  rw [hI] at hev
  have hx : P.xc = Q.xc := by
    have := hev _ (Ideal.subset_span (Set.mem_insert _ _))
    rw [CoordinateRing.XClass, CoordinateRing.evalAt_mk, evalEval_C, eval_sub, eval_X, eval_C,
      sub_eq_zero] at this
    exact this
  have hy : P.yc = Q.yc := by
    have := hev _ (Ideal.subset_span (Set.mem_insert_of_mem _ rfl))
    rw [CoordinateRing.YClass, CoordinateRing.evalAt_mk, evalEval_sub, evalEval_X, evalEval_C, eval_C,
      sub_eq_zero] at this
    exact this
  rw [Point.eq_some_xc_yc hP, Point.eq_some_xc_yc hQ]
  congr 1

end Places

end WeierstrassCurve.Affine

open Polynomial

namespace Valuation

variable {L : Type*} [CommRing L] {Γ₀ : Type*} [LinearOrderedCommGroupWithZero Γ₀] (v : Valuation L Γ₀)

def Cong (a b : L) : Prop := v (a - b) < 1

variable {v}

theorem cong_iff {a b : L} : v.Cong a b ↔ v (a - b) < 1 := Iff.rfl

@[refl] theorem Cong.refl (a : L) : v.Cong a a := by simp [Cong]

theorem Cong.rfl {a : L} : v.Cong a a := Cong.refl a

@[symm] theorem Cong.symm {a b : L} (h : v.Cong a b) : v.Cong b a := by
  rw [Cong, ← Valuation.map_neg, neg_sub]; exact h

theorem Cong.trans {a b c : L} (h₁ : v.Cong a b) (h₂ : v.Cong b c) : v.Cong a c := by
  have : a - c = (a - b) + (b - c) := by ring
  rw [Cong, this]
  exact (v.map_add _ _).trans_lt (max_lt h₁ h₂)

theorem Cong.add {a b a' b' : L} (h₁ : v.Cong a a') (h₂ : v.Cong b b') : v.Cong (a + b) (a' + b') := by
  have : a + b - (a' + b') = (a - a') + (b - b') := by ring
  rw [Cong, this]
  exact (v.map_add _ _).trans_lt (max_lt h₁ h₂)

theorem Cong.neg {a a' : L} (h : v.Cong a a') : v.Cong (-a) (-a') := by
  have : -a - -a' = -(a - a') := by ring
  rw [Cong, this, Valuation.map_neg]; exact h

theorem Cong.sub {a b a' b' : L} (h₁ : v.Cong a a') (h₂ : v.Cong b b') : v.Cong (a - b) (a' - b') := by
  rw [sub_eq_add_neg, sub_eq_add_neg]; exact h₁.add h₂.neg

theorem Cong.le_one {a a' : L} (h : v.Cong a a') (ha' : v a' ≤ 1) : v a ≤ 1 := by
  have : a = (a - a') + a' := by ring
  rw [this]
  exact (v.map_add _ _).trans (max_le h.le ha')

theorem Cong.mul {a b a' b' : L} (h₁ : v.Cong a a') (h₂ : v.Cong b b') (ha' : v a' ≤ 1)
    (hb' : v b' ≤ 1) : v.Cong (a * b) (a' * b') := by
  have ha : v a ≤ 1 := h₁.le_one ha'
  have : a * b - a' * b' = a * (b - b') + (a - a') * b' := by ring
  rw [Cong, this]
  refine (v.map_add _ _).trans_lt (max_lt ?_ ?_)
  · rw [Valuation.map_mul]
    calc v a * v (b - b') ≤ 1 * v (b - b') := mul_le_mul' ha le_rfl
      _ < 1 := by rw [one_mul]; exact h₂
  · rw [Valuation.map_mul]
    calc v (a - a') * v b' ≤ v (a - a') * 1 := mul_le_mul' le_rfl hb'
      _ < 1 := by rw [mul_one]; exact h₁

theorem Cong.pow {a a' : L} (h : v.Cong a a') (ha' : v a' ≤ 1) (n : ℕ) : v.Cong (a ^ n) (a' ^ n) := by
  induction n with
  | zero => simp only [pow_zero]; exact Cong.rfl
  | succ n ih =>
    rw [pow_succ, pow_succ]
    exact ih.mul h (by rw [Valuation.map_pow]; exact pow_le_one' ha' n) ha'

theorem Cong.eq_of_eq_one {a a' : L} (h : v.Cong a a') (ha' : v a' = 1) : v a = 1 := by
  have : a = a' + (a - a') := by ring
  rw [this, Valuation.map_add_eq_of_lt_left]
  · exact ha'
  · rw [ha']; exact h

theorem Cong.lt_one_iff {a a' : L} (h : v.Cong a a') : v a < 1 ↔ v a' < 1 := by
  constructor
  · intro ha
    have : a' = a - (a - a') := by ring
    rw [this]
    exact (v.map_sub _ _).trans_lt (max_lt ha h)
  · intro ha'
    have : a = (a - a') + a' := by ring
    rw [this]
    exact (v.map_add _ _).trans_lt (max_lt h ha')

theorem cong_zero_iff {a : L} : v.Cong a 0 ↔ v a < 1 := by rw [Cong, sub_zero]

section Field

variable {F : Type*} [Field F] {Γ : Type*} [LinearOrderedCommGroupWithZero Γ] {w : Valuation F Γ}

theorem Cong.inv {d d' : F} (h : w.Cong d d') (hd' : w d' = 1) : w.Cong d⁻¹ d'⁻¹ := by
  have hd : w d = 1 := h.eq_of_eq_one hd'
  have hd0 : d ≠ 0 := fun h0 => by rw [h0, Valuation.map_zero] at hd; exact zero_ne_one hd
  have hd0' : d' ≠ 0 := fun h0 => by rw [h0, Valuation.map_zero] at hd'; exact zero_ne_one hd'
  have : d⁻¹ - d'⁻¹ = -(d - d') * (d⁻¹ * d'⁻¹) := by
    field_simp
    ring
  rw [Cong, this, Valuation.map_mul, Valuation.map_neg, Valuation.map_mul, map_inv₀, map_inv₀, hd, hd',
    inv_one, mul_one, mul_one]
  exact h

theorem Cong.div {a d a' d' : F} (h₁ : w.Cong a a') (h₂ : w.Cong d d') (ha' : w a' ≤ 1)
    (hd' : w d' = 1) : w.Cong (a / d) (a' / d') := by
  rw [div_eq_mul_inv, div_eq_mul_inv]
  exact h₁.mul (h₂.inv hd') ha' (by rw [map_inv₀, hd', inv_one])

end Field

section Eval

variable {K : Type*} [CommRing K] (f : K →+* L)

theorem Cong.eval₂ (hf : ∀ c, v (f c) ≤ 1) {a a' : L} (h : v.Cong a a') (ha' : v a' ≤ 1) (p : K[X]) :
    v.Cong (p.eval₂ f a) (p.eval₂ f a') ∧ v (p.eval₂ f a') ≤ 1 := by
  induction p using Polynomial.induction_on' with
  | add p q hp hq =>
    rw [eval₂_add, eval₂_add]
    exact ⟨hp.1.add hq.1, (v.map_add _ _).trans (max_le hp.2 hq.2)⟩
  | monomial n c =>
    rw [eval₂_monomial, eval₂_monomial]
    have hpow : v (a' ^ n) ≤ 1 := by rw [Valuation.map_pow]; exact pow_le_one' ha' n
    exact ⟨(Cong.refl (f c)).mul (h.pow ha' n) (hf c) hpow,
      by rw [Valuation.map_mul]; exact mul_le_one' (hf c) hpow⟩

theorem Cong.eval₂_eval₂ (hf : ∀ c, v (f c) ≤ 1) {a a' b b' : L} (ha : v.Cong a a') (ha' : v a' ≤ 1)
    (hb : v.Cong b b') (hb' : v b' ≤ 1) (p : K[X][X]) :
    v.Cong (p.eval₂ (eval₂RingHom f a) b) (p.eval₂ (eval₂RingHom f a') b') ∧
      v (p.eval₂ (eval₂RingHom f a') b') ≤ 1 := by
  induction p using Polynomial.induction_on' with
  | add p q hp hq =>
    rw [eval₂_add, eval₂_add]
    exact ⟨hp.1.add hq.1, (v.map_add _ _).trans (max_le hp.2 hq.2)⟩
  | monomial n q =>
    rw [eval₂_monomial, eval₂_monomial, coe_eval₂RingHom, coe_eval₂RingHom]
    have hq := Cong.eval₂ f hf ha ha' q
    have hpow : v (b' ^ n) ≤ 1 := by rw [Valuation.map_pow]; exact pow_le_one' hb' n
    exact ⟨hq.1.mul (hb.pow hb' n) hq.2 hpow, by rw [Valuation.map_mul]; exact mul_le_one' hq.2 hpow⟩

end Eval

end Valuation

open IsDedekindDomain IsDedekindDomain.HeightOneSpectrum WithZero

namespace IsDedekindDomain.HeightOneSpectrum

variable {R : Type*} [CommRing R] [IsDedekindDomain R] {K : Type*} [Field K] [Algebra R K]
  [IsFractionRing R K] {R' : Type*} [CommRing R'] [IsDedekindDomain R'] {K' : Type*} [Field K']
  [Algebra R' K'] [IsFractionRing R' K']

omit [IsDedekindDomain R] [IsFractionRing R K] in

theorem valuation_map_le_exp_neg_of_mem_pow (ψ : K →+* K') (w : HeightOneSpectrum R)
    (v : HeightOneSpectrum R')
    (h0 : ∀ b : R, v.valuation K' (ψ (algebraMap R K b)) ≤ 1)
    (h1 : ∀ b : R, b ∈ w.asIdeal → v.valuation K' (ψ (algebraMap R K b)) ≤ exp (-1 : ℤ))
    (m : ℕ) : ∀ a ∈ w.asIdeal ^ m, v.valuation K' (ψ (algebraMap R K a)) ≤ exp (-(m : ℤ)) := by
  induction m with
  | zero =>
    intro a _
    rw [Nat.cast_zero, neg_zero, exp_zero]
    exact h0 a
  | succ m ih =>
    intro a ha
    rw [pow_succ'] at ha
    refine Submodule.mul_induction_on ha ?_ ?_
    · intro c hc a' ha'
      rw [map_mul, map_mul, map_mul, Nat.cast_succ, neg_add, add_comm, exp_add]
      exact mul_le_mul' (h1 c hc) (ih a' ha')
    · intro z₁ z₂ hz₁ hz₂
      rw [map_add, map_add]
      exact (Valuation.map_add _ _ _).trans (max_le hz₁ hz₂)

theorem valuation_map_le_of_valuation_le (ψ : K →+* K') (w : HeightOneSpectrum R)
    (v : HeightOneSpectrum R')
    (h1 : ∀ b : R, b ∈ w.asIdeal → v.valuation K' (ψ (algebraMap R K b)) ≤ exp (-1 : ℤ))
    (h2 : ∀ b : R, b ∉ w.asIdeal → v.valuation K' (ψ (algebraMap R K b)) = 1)
    (x : K) (k : ℕ) (hx : w.valuation K x ≤ exp (-(k : ℤ))) :
    v.valuation K' (ψ x) ≤ exp (-(k : ℤ)) := by
  have h0 : ∀ b : R, v.valuation K' (ψ (algebraMap R K b)) ≤ 1 := by
    intro b
    by_cases hb : b ∈ w.asIdeal
    · exact (h1 b hb).trans (by rw [← exp_zero, exp_le_exp]; norm_num)
    · exact (h2 b hb).le
  have hx1 : w.valuation K x ≤ 1 :=
    hx.trans (by rw [← exp_zero, exp_le_exp, neg_nonpos]; exact Nat.cast_nonneg k)
  obtain ⟨n, d, hnd⟩ := exists_primeCompl_mul_eq_of_integer (v := w) x hx1
  have hd : (d : R) ∉ w.asIdeal := d.2

  have hwn : w.valuation K (algebraMap R K n) ≤ exp (-(k : ℤ)) := by
    rw [← hnd, map_mul, (valuation_eq_one_iff_notMem (K := K) w).mpr hd, mul_one]
    exact hx
  rw [valuation_of_algebraMap, intValuation_le_pow_iff_mem] at hwn
  have hψn := valuation_map_le_exp_neg_of_mem_pow ψ w v h0 h1 k n hwn

  have := congrArg (fun t => v.valuation K' (ψ t)) hnd
  simp only [map_mul, h2 d hd, mul_one] at this
  rw [this]
  exact hψn

end IsDedekindDomain.HeightOneSpectrum

namespace WeierstrassCurve.Affine

open IsDedekindDomain IsDedekindDomain.HeightOneSpectrum WithZero Polynomial

section Transport

variable {R : Type*} [Field R] (W : WeierstrassCurve R) (K : Type*) [Field K] [Algebra R K]
  [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] [IsDedekindDomain (W⁄K).CoordinateRing]

theorem _root_.WithZero.lt_one_iff_le_exp_neg_one {y : ℤᵐ⁰} : y < 1 ↔ y ≤ exp (-1 : ℤ) := by
  by_cases hy : y = 0
  · subst hy; simp
  · rw [← exp_log hy, ← exp_zero, exp_lt_exp, exp_le_exp]; omega

omit [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] in

theorem valuation_algebraMap_le_one (P : (W⁄K).Point) (hP : P ≠ 0) (c : K) :
    (placeOf W K P hP).valuation (W⁄K).FunctionField (algebraMap K (W⁄K).FunctionField c) ≤ 1 := by
  rw [IsScalarTower.algebraMap_apply K (W⁄K).CoordinateRing (W⁄K).FunctionField]
  exact valuation_le_one _ _

omit [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] in

theorem valuation_algebraMap_eq_one (P : (W⁄K).Point) (hP : P ≠ 0) {c : K} (hc : c ≠ 0) :
    (placeOf W K P hP).valuation (W⁄K).FunctionField (algebraMap K (W⁄K).FunctionField c) = 1 := by
  apply le_antisymm (valuation_algebraMap_le_one W K P hP c)
  have h := valuation_algebraMap_le_one W K P hP c⁻¹
  rw [map_inv₀, map_inv₀, inv_le_one₀] at h
  · exact h
  · rw [Valuation.pos_iff]
    exact (_root_.map_ne_zero _).mpr hc

omit [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] [IsDedekindDomain (W⁄K).CoordinateRing] in

theorem mem_placeOf_iff (P : (W⁄K).Point) (hP : P ≠ 0) (b : (W⁄K).CoordinateRing) :
    b ∈ (placeOf W K P hP).asIdeal ↔ CoordinateRing.evalAt W K (Point.nonsingular_xc_yc hP).left b = 0 := by
  constructor
  · exact CoordinateRing.evalAt_XYIdeal W K _ b
  · intro hb
    have hker : RingHom.ker (CoordinateRing.evalAt W K (Point.nonsingular_xc_yc hP).left) =
        (placeOf W K P hP).asIdeal := by
      refine ((CoordinateRing.XYIdeal_isMaximal (Point.nonsingular_xc_yc hP).left).eq_of_le
        (RingHom.ker_ne_top _) ?_).symm
      intro g hg
      exact (RingHom.mem_ker).mpr (CoordinateRing.evalAt_XYIdeal W K _ g hg)
    rw [← hker]
    exact (RingHom.mem_ker).mpr hb

omit [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] in
theorem cong_genericX (P : (W⁄K).Point) (hP : P ≠ 0) :
    ((placeOf W K P hP).valuation (W⁄K).FunctionField).Cong (genericX W K)
      (algebraMap K (W⁄K).FunctionField P.xc) := by
  rw [Valuation.cong_iff, IsScalarTower.algebraMap_apply K (W⁄K).CoordinateRing (W⁄K).FunctionField, genericX,
    ← map_sub]
  apply (valuation_lt_one_iff_mem _ _).mpr
  rw [placeOf_asIdeal]
  have : CoordinateRing.mk (W⁄K) (C X) - algebraMap K (W⁄K).CoordinateRing P.xc =
      CoordinateRing.XClass (W⁄K) P.xc := by
    rw [CoordinateRing.XClass, map_sub, map_sub]; rfl
  rw [this]
  exact Ideal.subset_span (Set.mem_insert _ _)

omit [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] in
theorem cong_genericY (P : (W⁄K).Point) (hP : P ≠ 0) :
    ((placeOf W K P hP).valuation (W⁄K).FunctionField).Cong (genericY W K)
      (algebraMap K (W⁄K).FunctionField P.yc) := by
  rw [Valuation.cong_iff, IsScalarTower.algebraMap_apply K (W⁄K).CoordinateRing (W⁄K).FunctionField, genericY,
    ← map_sub]
  apply (valuation_lt_one_iff_mem _ _).mpr
  rw [placeOf_asIdeal]
  have : CoordinateRing.mk (W⁄K) Y - algebraMap K (W⁄K).CoordinateRing P.yc =
      CoordinateRing.YClass (W⁄K) (C P.yc) := by
    rw [CoordinateRing.YClass, map_sub]; rfl
  rw [this]
  exact Ideal.subset_span (Set.mem_insert_of_mem _ rfl)

omit [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] [IsDedekindDomain (W⁄K).CoordinateRing] in

theorem algebraMap_evalEval (g : K[X][Y]) (a b : K) :
    algebraMap K (W⁄K).FunctionField (g.evalEval a b) =
      g.eval₂ (eval₂RingHom (algebraMap K (W⁄K).FunctionField) (algebraMap K _ a)) (algebraMap K _ b) := by
  have hφ : (algebraMap K (W⁄K).FunctionField).comp (evalEvalRingHom a b) =
      eval₂RingHom (eval₂RingHom (algebraMap K (W⁄K).FunctionField) (algebraMap K _ a)) (algebraMap K _ b) := by
    apply Polynomial.ringHom_ext
    · intro q
      simp only [RingHom.coe_comp, Function.comp_apply, coe_evalRingHom, eval_C, coe_eval₂RingHom,
        eval₂_C, eval₂_at_apply]
    · simp only [RingHom.coe_comp, Function.comp_apply, coe_evalRingHom, eval_X, eval_C, coe_eval₂RingHom,
        eval₂_X]
  exact RingHom.congr_fun hφ g

omit [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] [IsDedekindDomain (W⁄K).CoordinateRing] in
theorem pointHom_mk_eq_eval₂ (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0) (g : K[X][Y]) :
    pointHom Q hQ (CoordinateRing.mk (W⁄K) g) =
      g.eval₂ (eval₂RingHom (algebraMap K (W⁄K).FunctionField) Q.xc) Q.yc := by
  rw [pointHom_mk]
  congr 1

omit [DecidableEq K] [IsAlgClosed K] [W.IsElliptic] in

theorem valuation_pointPull_le (Q : (W⁄(W⁄K).FunctionField).Point) (hQ : Q ≠ 0)
    (hinj : Function.Injective (pointHom Q hQ)) {P : (W⁄K).Point} (hP : P ≠ 0) {P' : (W⁄K).Point}
    (hP' : P' ≠ 0)
    (hx : ((placeOf W K P hP).valuation (W⁄K).FunctionField).Cong Q.xc (algebraMap K _ P'.xc))
    (hy : ((placeOf W K P hP).valuation (W⁄K).FunctionField).Cong Q.yc (algebraMap K _ P'.yc))
    (h : (W⁄K).FunctionField) (k : ℕ)
    (hh : (placeOf W K P' hP').valuation (W⁄K).FunctionField h ≤ exp (-(k : ℤ))) :
    (placeOf W K P hP).valuation (W⁄K).FunctionField (pointPull Q hQ hinj h) ≤ exp (-(k : ℤ)) := by
  set v := (placeOf W K P hP).valuation (W⁄K).FunctionField with hv
  have hc : ∀ g : K[X][Y], v.Cong (pointHom Q hQ (CoordinateRing.mk (W⁄K) g))
      (algebraMap K _ (g.evalEval P'.xc P'.yc)) := by
    intro g
    rw [pointHom_mk_eq_eval₂, algebraMap_evalEval]
    exact (Valuation.Cong.eval₂_eval₂ (v := v) (algebraMap K _) (valuation_algebraMap_le_one W K P hP)
      hx (valuation_algebraMap_le_one W K P hP _) hy (valuation_algebraMap_le_one W K P hP _) g).1
  refine valuation_map_le_of_valuation_le (pointPull Q hQ hinj : (W⁄K).FunctionField →+* (W⁄K).FunctionField)
    (placeOf W K P' hP') (placeOf W K P hP) ?_ ?_ h k hh
  · intro b hb
    obtain ⟨g, rfl⟩ := Ideal.Quotient.mk_surjective b
    change v (pointPull Q hQ hinj (algebraMap _ _ (CoordinateRing.mk (W⁄K) g))) ≤ _
    rw [pointPull_algebraMap, ← lt_one_iff_le_exp_neg_one, ← Valuation.cong_zero_iff]
    have h0 : g.evalEval P'.xc P'.yc = 0 := by
      rw [← CoordinateRing.evalAt_mk W K (Point.nonsingular_xc_yc hP').left]
      exact (mem_placeOf_iff W K P' hP' _).mp hb
    simpa only [h0, map_zero] using hc g
  · intro b hb
    obtain ⟨g, rfl⟩ := Ideal.Quotient.mk_surjective b
    change v (pointPull Q hQ hinj (algebraMap _ _ (CoordinateRing.mk (W⁄K) g))) = 1
    rw [pointPull_algebraMap]
    have h0 : g.evalEval P'.xc P'.yc ≠ 0 := by
      rw [← CoordinateRing.evalAt_mk W K (Point.nonsingular_xc_yc hP').left]
      exact fun h => hb ((mem_placeOf_iff W K P' hP' _).mpr h)
    exact (hc g).eq_of_eq_one (valuation_algebraMap_eq_one W K P hP h0)

theorem valuation_mulPull_le {n : ℤ} (hgood : MulGood W K n) (Φ Ψ : K[X]) (ω ψ : K[X][Y])
    (hξ : (n • genericPoint W K).xc =
      Φ.eval₂ (algebraMap K (W⁄K).FunctionField) (genericX W K) /
        Ψ.eval₂ (algebraMap K (W⁄K).FunctionField) (genericX W K))
    (hη : (n • genericPoint W K).yc =
      ω.eval₂ (eval₂RingHom (algebraMap K (W⁄K).FunctionField) (genericX W K)) (genericY W K) /
        ψ.eval₂ (eval₂RingHom (algebraMap K (W⁄K).FunctionField) (genericX W K)) (genericY W K) ^ 3)
    {P : (W⁄K).Point} (hP : P ≠ 0) (hnP : n • P ≠ 0)
    (hxP : (n • P).xc = Φ.eval P.xc / Ψ.eval P.xc) (hΨ : Ψ.eval P.xc ≠ 0)
    (hyP : (n • P).yc = ω.evalEval P.xc P.yc / ψ.evalEval P.xc P.yc ^ 3) (hψ : ψ.evalEval P.xc P.yc ≠ 0)
    (h : (W⁄K).FunctionField) (k : ℕ)
    (hh : (placeOf W K (n • P) hnP).valuation (W⁄K).FunctionField h ≤ exp (-(k : ℤ))) :
    (placeOf W K P hP).valuation (W⁄K).FunctionField (mulPull W K n h) ≤ exp (-(k : ℤ)) := by
  set v := (placeOf W K P hP).valuation (W⁄K).FunctionField with hv
  have hc1 := valuation_algebraMap_le_one W K P hP
  have hX := cong_genericX W K P hP
  have hY := cong_genericY W K P hP

  have hx : v.Cong (n • genericPoint W K).xc (algebraMap K _ (n • P).xc) := by
    rw [hξ, hxP, map_div₀, ← eval₂_at_apply, ← eval₂_at_apply]
    have hΦ := Valuation.Cong.eval₂ (v := v) (algebraMap K _) hc1 hX (hc1 _) Φ
    have hΨc := Valuation.Cong.eval₂ (v := v) (algebraMap K _) hc1 hX (hc1 _) Ψ
    refine hΦ.1.div hΨc.1 hΦ.2 ?_
    rw [eval₂_at_apply]
    exact valuation_algebraMap_eq_one W K P hP hΨ

  have hy : v.Cong (n • genericPoint W K).yc (algebraMap K _ (n • P).yc) := by
    rw [hη, hyP, map_div₀, map_pow, algebraMap_evalEval, algebraMap_evalEval]
    have hω := Valuation.Cong.eval₂_eval₂ (v := v) (algebraMap K _) hc1 hX (hc1 _) hY (hc1 _) ω
    have hψc := Valuation.Cong.eval₂_eval₂ (v := v) (algebraMap K _) hc1 hX (hc1 _) hY (hc1 _) ψ
    refine hω.1.div (hψc.1.pow hψc.2 3) hω.2 ?_
    rw [← algebraMap_evalEval, ← map_pow]
    exact valuation_algebraMap_eq_one W K P hP (pow_ne_zero 3 hψ)
  rw [mulPull_eq W K hgood]
  exact valuation_pointPull_le W K _ _ _ hP hnP hx hy h k hh

end Transport

end WeierstrassCurve.Affine

Statements phrased using this module (10)