[System] You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange).

[User]
Prove the following theorem. The retrieved context below may help.

```lean
lemma variableChange_b₆ : (C • W).b₆ =
    C.u⁻¹ ^ 6 * (W.b₆ + 2 * C.r * W.b₄ + C.r ^ 2 * W.b₂ + 4 * C.r ^ 3)
```

## Retrieved context
-- snippet 1
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.one_def (L75-L76)
lemma one_def : (1 : VariableChange R) = ⟨1, 0, 0, 0⟩ := rfl
```

-- snippet 2
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.mul_def (L85-L90)
lemma mul_def : C * C' = {
    u := C.u * C'.u
r := C.r * C'.u ^ 2 + C'.r
    s := C'.u * C.s + C'.s
    t := C.t * C'.u ^ 3 + C.r * C'.s * C'.u ^ 2 + C'.t } := rfl
```

-- snippet 3
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.inv_def (L99-L104)
lemma inv_def : C⁻¹ = {
    u := C.u⁻¹
r := -C.r * C.u⁻¹ ^ 2
    s := -C.s * C.u⁻¹
    t := (C.r * C.s - C.t) * C.u⁻¹ ^ 3 } := rfl
```

-- snippet 4
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_def (L137-L145)
lemma variableChange_def : C • W = {
    a₁ := C.u⁻¹ * (W.a₁ + 2 * C.s)
a₂ := C.u⁻¹ ^ 2 * (W.a₂ - C.s * W.a₁ + 3 * C.r - C.s ^ 2)
    a₃ := C.u⁻¹ ^ 3 * (W.a₃ + C.r * W.a₁ + 2 * C.t)
    a₄ := C.u⁻¹ ^ 4 * (W.a₄ - C.s * W.a₃ + 2 * C.r * W.a₂ - (C.t + C.r * C.s) * W.a₁ + 3 * C.r ^ 2
      - 2 * C.s * C.t)
    a₆ := C.u⁻¹ ^ 6 * (W.a₆ + C.r * W.a₄ + C.r ^ 2 * W.a₂ + C.r ^ 3 - C.t * W.a₃ - C.t ^ 2
      - C.r * C.t * W.a₁) } := rfl
```

-- snippet 5
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₁ (L177-L178)
lemma variableChange_a₁ : (C • W).a₁ = C.u⁻¹ * (W.a₁ + 2 * C.s) := rfl
```

-- snippet 6
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₂ (L179-L180)
lemma variableChange_a₂ : (C • W).a₂ = C.u⁻¹ ^ 2 * (W.a₂ - C.s * W.a₁ + 3 * C.r - C.s ^ 2) := rfl
```

-- snippet 7
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₃ (L181-L182)
lemma variableChange_a₃ : (C • W).a₃ = C.u⁻¹ ^ 3 * (W.a₃ + C.r * W.a₁ + 2 * C.t) := rfl
```

-- snippet 8
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₄ (L183-L186)
lemma variableChange_a₄ : (C • W).a₄ =
    C.u⁻¹ ^ 4 * (W.a₄ - C.s * W.a₃ + 2 * C.r * W.a₂ - (C.t + C.r * C.s) * W.a₁ + 3 * C.r ^ 2
      - 2 * C.s * C.t) := rfl
```

-- snippet 9
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₆ (L187-L190)
lemma variableChange_a₆ : (C • W).a₆ =
    C.u⁻¹ ^ 6 * (W.a₆ + C.r * W.a₄ + C.r ^ 2 * W.a₂ + C.r ^ 3 - C.t * W.a₃ - C.t ^ 2
      - C.r * C.t * W.a₁) := rfl
```

-- snippet 10
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_b₂ (L191-L194)
lemma variableChange_b₂ : (C • W).b₂ = C.u⁻¹ ^ 2 * (W.b₂ + 12 * C.r) := by
simp only [b₂, variableChange_a₁, variableChange_a₂]
  ring1
```

-- snippet 11
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_b₄ (L195-L198)
lemma variableChange_b₄ : (C • W).b₄ = C.u⁻¹ ^ 4 * (W.b₄ + C.r * W.b₂ + 6 * C.r ^ 2) := by
simp only [b₂, b₄, variableChange_a₁, variableChange_a₃, variableChange_a₄]
  ring1
```

-- snippet 12
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_b₈ (L204-L209)
lemma variableChange_b₈ : (C • W).b₈ = C.u⁻¹ ^ 8 *
    (W.b₈ + 3 * C.r * W.b₆ + 3 * C.r ^ 2 * W.b₄ + C.r ^ 3 * W.b₂ + 3 * C.r ^ 4) := by
simp only [b₂, b₄, b₆, b₈, variableChange_a₁, variableChange_a₂, variableChange_a₃,
    variableChange_a₄, variableChange_a₆]
  ring1
```

-- snippet 13
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_c₄ (L210-L213)
lemma variableChange_c₄ : (C • W).c₄ = C.u⁻¹ ^ 4 * W.c₄ := by
simp only [c₄, variableChange_b₂, variableChange_b₄]
  ring1
```

-- snippet 14
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_c₆ (L214-L217)
lemma variableChange_c₆ : (C • W).c₆ = C.u⁻¹ ^ 6 * W.c₆ := by
simp only [c₆, variableChange_b₂, variableChange_b₄, variableChange_b₆]
  ring1
```

-- snippet 15
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_Δ (L218-L224)
lemma variableChange_Δ : (C • W).Δ = C.u⁻¹ ^ 12 * W.Δ := by
simp only [b₂, b₄, b₆, b₈, Δ, variableChange_a₁, variableChange_a₂, variableChange_a₃,
    variableChange_a₄, variableChange_a₆]
  ring1

variable [W.IsElliptic]
```

-- snippet 16
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_Δ' (L230-L233)
lemma variableChange_Δ' : (C • W).Δ' = C.u⁻¹ ^ 12 * W.Δ' := by
simp_rw [Units.ext_iff, Units.val_mul, coe_Δ', variableChange_Δ, Units.val_pow_eq_pow_val]

set_option linter.docPrime false in
```

-- snippet 17
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.coe_variableChange_Δ' (L234-L237)
lemma coe_variableChange_Δ' : ((C • W).Δ' : R) = C.u⁻¹ ^ 12 * W.Δ' := by
simp_rw [coe_Δ', variableChange_Δ]

set_option linter.docPrime false in
```

-- snippet 18
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.inv_variableChange_Δ' (L238-L241)
lemma inv_variableChange_Δ' : (C • W).Δ'⁻¹ = C.u ^ 12 * W.Δ'⁻¹ := by
rw [variableChange_Δ', mul_inv, inv_pow, inv_inv]

set_option linter.docPrime false in
```

-- snippet 19
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.coe_inv_variableChange_Δ' (L242-L244)
lemma coe_inv_variableChange_Δ' : (↑(C • W).Δ'⁻¹ : R) = C.u ^ 12 * W.Δ'⁻¹ := by
rw [inv_variableChange_Δ', Units.val_mul, Units.val_pow_eq_pow_val]
```

-- snippet 20
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_j (L246-L262)
lemma variableChange_j : (C • W).j = W.j := by
rw [j, coe_inv_variableChange_Δ', variableChange_c₄, j, mul_pow, ← pow_mul, ← mul_assoc,
    mul_right_comm (C.u.val ^ 12), ← mul_pow, C.u.mul_inv, one_pow, one_mul]

end VariableChange

section BaseChange

/-! ## Maps and base changes -/

variable {A : Type v} [CommRing A] (φ : R →+* A)

namespace VariableChange

variable (C : VariableChange R)

/-- The change of variables mapped over a ring homomorphism `φ : R →+* A`. -/
```

-- snippet 21
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.map (L264-L268)
def map : VariableChange A :=
⟨Units.map φ C.u, φ C.r, φ C.s, φ C.t⟩

variable (A) in
/-- The change of variables base changed to an algebra `A` over `R`. -/
```

-- snippet 22
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.baseChange (L269-L271)
abbrev baseChange [Algebra R A] : VariableChange A :=
C.map <| algebraMap R A
```

-- snippet 23
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.map_id (L273-L275)
lemma map_id : C.map (RingHom.id R) = C :=
rfl
```

-- snippet 24
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.map_map (L276-L279)
lemma map_map {A : Type v} [CommRing A] (φ : R →+* A) {B : Type w} [CommRing B] (ψ : A →+* B) :
    (C.map φ).map ψ = C.map (ψ.comp φ) :=
rfl
```

-- snippet 25
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.map_baseChange (L281-L285)
lemma map_baseChange {S : Type s} [CommRing S] [Algebra R S] {A : Type v} [CommRing A] [Algebra R A]
    [Algebra S A] [IsScalarTower R S A] {B : Type w} [CommRing B] [Algebra R B] [Algebra S B]
    [IsScalarTower R S B] (ψ : A →ₐ[S] B) : (C.baseChange A).map ψ = C.baseChange B :=
congr_arg C.map <| ψ.comp_algebraMap_of_tower R
```

-- snippet 26
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_negPolynomial (L396-L400)
lemma map_negPolynomial :
    (W'.map f).toAffine.negPolynomial = W'.negPolynomial.map (mapRingHom f) := by
simp only [negPolynomial]
  map_simp
```

-- snippet 27
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_polynomial (L286-L290)
lemma baseChange_polynomial : (W.baseChange B).toAffine.polynomial =
    (W.baseChange A).toAffine.polynomial.map (mapRingHom f) := by
rw [← map_polynomial, map_baseChange]

variable {x y} in
```

-- snippet 28
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addY_sub_negY_addY (L382-L395)
lemma addY_sub_negY_addY {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂) :
    let x₃ := W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂)
let y₃ := W.addY x₁ x₂ y₁ (W.slope x₁ x₂ y₁ y₂)
    y₃ - W.negY x₃ y₃ =
      ((y₂ - W.negY x₂ y₂) * (x₁ - x₃) - (y₁ - W.negY x₁ y₁) * (x₂ - x₃)) / (x₂ - x₁) := by
  simp_rw [addY, negY, eq_div_iff (sub_ne_zero.mpr hx.symm)]
  linear_combination (norm := ring1) 2 * cyclic_sum_Y_mul_X_sub_X y₁ y₂ hx

end slope

/-! ## Maps and base changes -/

variable (f : R →+* S) (x y x₁ y₁ x₂ y₂ ℓ : R)
```

-- snippet 29
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomial_zero (L141-L147)
lemma evalEval_polynomial_zero : W.polynomial.evalEval 0 0 = -W.a₆ := by
simp only [evalEval_polynomial, zero_add, zero_sub, mul_zero, zero_pow <| Nat.succ_ne_zero _]

variable (W) in
/-- The proposition that an affine point `(x, y)` lies in a Weierstrass curve `W`.

In other words, it satisfies the Weierstrass equation `W(X, Y) = 0`. -/
```

-- snippet 30
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_polynomialY (L273-L277)
lemma map_polynomialY : (W.map f).toAffine.polynomialY = W.polynomialY.map (mapRingHom f) := by
simp only [polynomialY]
  map_simp

variable {f} in
```

-- snippet 31
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.negY_negY (L116-L119)
lemma negY_negY (x y : R) : W'.negY x (W'.negY x y) = y := by
simp only [negY]
  ring1
```

-- snippet 32
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff_variableChange (L163-L177)
lemma equation_iff_variableChange (x y : R) :
    W.Equation x y ↔ (VariableChange.mk 1 x 0 y • W).toAffine.Equation 0 0 := by
rw [equation_iff', ← neg_eq_zero, equation_zero, variableChange_a₆, inv_one, Units.val_one]
  congr! 1
  ring1

/-! ## The nonsingular condition in affine coordinates -/

variable (W) in
/-- The partial derivative `W_X(X, Y)` with respect to `X` of the polynomial `W(X, Y)` associated to
a Weierstrass curve `W` in affine coordinates. -/
-- TODO: define this in terms of `Polynomial.derivative`.
noncomputable def polynomialX : R[X][Y] :=
  C (C W.a₁) * Y - C (C 3 * X ^ 2 + C (2 * W.a₂) * X + C W.a₄)
```

-- snippet 33
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.evalEval_negPolynomial (L120-L123)
lemma evalEval_negPolynomial (x y : R) : W'.negPolynomial.evalEval x y = W'.negY x y := by
rw [negY, sub_sub, negPolynomial]
  eval_simp
```

-- snippet 34
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addX_eq_addX_negY_sub (L360-L372)
lemma addX_eq_addX_negY_sub {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂) :
    W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂) = W.addX x₁ x₂ (W.slope x₁ x₂ y₁ <| W.negY x₂ y₂) -
      (y₁ - W.negY x₁ y₁) * (y₂ - W.negY x₂ y₂) / (x₂ - x₁) ^ 2 := by
simp_rw [slope_of_X_ne hx, addX, negY, ← neg_sub x₁, neg_sq]
  simp [field]
  ring1

-- Non-terminal simp, used to be field_simp
set_option linter.flexible false in
-- see https://github.com/leanprover-community/mathlib4/issues/29041
set_option linter.unusedSimpArgs false in
/-- The formula `y(P₁)(x(P₂) - x(P₃)) + y(P₂)(x(P₃) - x(P₁)) + y(P₃)(x(P₁) - x(P₂)) = 0`,
assuming that `P₁ + P₂ + P₃ = O`. -/
```

-- snippet 35
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_negY (L401-L404)
lemma map_negY : (W'.map f).toAffine.negY (f x) (f y) = f (W'.negY x y) := by
simp only [negY]
  map_simp
```

-- snippet 36
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addPolynomial_slope (L265-L287)
lemma addPolynomial_slope {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : W.addPolynomial x₁ y₁ (W.slope x₁ x₂ y₁ y₂) =
      -((X - C x₁) * (X - C x₂) * (X - C (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂))) := by
rw [addPolynomial_eq, neg_inj, Cubic.prod_X_sub_C_eq, Cubic.toPoly_injective]
  by_cases hx : x₁ = x₂
  · have hy : y₁ ≠ W.negY x₂ y₂ := fun h => hxy ⟨hx, h⟩
    rcases hx, Y_eq_of_Y_ne h₁ h₂ hx hy with ⟨rfl, rfl⟩
    rw [equation_iff] at h₁ h₂
    rw [slope_of_Y_ne rfl hy]
    rw [negY, ← sub_ne_zero] at hy
    replace hy : y₁ - (-y₁ - x₁ * W.a₁ - W.a₃) ≠ 0 := by convert hy using 1; ring
    ext
    · rfl
    · simp only [addX]
      ring1
    · simp [field]
      ring1
    · linear_combination (norm := (simp [field]; ring1)) -h₁
  · rw [equation_iff] at h₁ h₂
    rw [slope_of_X_ne hx]
    simp only [addX]
    grind
```

-- snippet 37
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.equation_neg (L134-L138)
lemma equation_neg (x y : R) : W'.Equation x (W'.negY x y) ↔ W'.Equation x y := by
rw [equation_iff, equation_iff, negY]
  congr! 1
  ring1
```

-- snippet 38
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.Nonsingular (L210-L212)
def Nonsingular (x y : R) : Prop :=
W.Equation x y ∧ (W.polynomialX.evalEval x y ≠ 0 ∨ W.polynomialY.evalEval x y ≠ 0)
```

-- snippet 39
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_equation (L297-L300)
lemma baseChange_equation (hf : Function.Injective f) :
    (W.baseChange B).toAffine.Equation (f x) (f y) ↔ (W.baseChange A).toAffine.Equation x y := by
rw [← map_equation _ _ hf, AlgHom.toRingHom_eq_coe, map_baseChange, RingHom.coe_coe]
```

-- snippet 40
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.nonsingular_zero (L224-L227)
lemma nonsingular_zero : W.Nonsingular 0 0 ↔ W.a₆ = 0 ∧ (W.a₃ ≠ 0 ∨ W.a₄ ≠ 0) := by
rw [Nonsingular, equation_zero, evalEval_polynomialX_zero, neg_ne_zero, evalEval_polynomialY_zero,
    or_comm]
```

-- snippet 41
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_polynomial (L256-L260)
lemma map_polynomial : (W.map f).toAffine.polynomial = W.polynomial.map (mapRingHom f) := by
simp only [polynomial]
  map_simp

variable {x y} in
```

-- snippet 42
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.polynomial_ne_zero (L105-L108)
lemma polynomial_ne_zero [Nontrivial R] : W.polynomial ≠ 0 := by
rw [polynomial_eq]
  exact Cubic.ne_zero_of_b_ne_zero one_ne_zero
```

-- snippet 43
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.derivative_addPolynomial_slope (L294-L302)
lemma derivative_addPolynomial_slope {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁)
    (h₂ : W.Equation x₂ y₂) (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) :
    derivative (W.addPolynomial x₁ y₁ <| W.slope x₁ x₂ y₁ y₂) =
      -((X - C x₁) * (X - C x₂) + (X - C x₁) * (X - C (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂)) +
          (X - C x₂) * (X - C (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂))) := by
rw [addPolynomial_slope h₁ h₂ hxy]
  derivative_simp
  ring1
```

-- snippet 44
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_equation (L265-L268)
lemma map_equation (hf : Function.Injective f) :
    (W.map f).toAffine.Equation (f x) (f y) ↔ W.Equation x y := by
simp only [Equation, map_polynomial, map_mapRingHom_evalEval, map_eq_zero_iff f hf]
```

-- snippet 45
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.cyclic_sum_Y_mul_X_sub_X (L373-L381)
lemma cyclic_sum_Y_mul_X_sub_X {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂) :
    let x₃ := W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂)
y₁ * (x₂ - x₃) + y₂ * (x₃ - x₁) + W.negAddY x₁ x₂ y₁ (W.slope x₁ x₂ y₁ y₂) * (x₁ - x₂) = 0 := by
  simp_rw [slope_of_X_ne hx, negAddY, addX]
  simp [field, sub_ne_zero.mpr hx]
  ring1

/-- The formula `ψ(P₁ + P₂) = (ψ(P₂)(x(P₁) - x(P₃)) - ψ(P₁)(x(P₂) - x(P₃))) / (x(P₂) - x(P₁))`,
where `ψ(x,y) = 2y + a₁x + a₃`. -/
```

-- snippet 46
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.monic_polynomial (L119-L121)
lemma monic_polynomial : W.polynomial.Monic := by
simpa only [polynomial_eq] using Cubic.monic_of_b_eq_one'
```

-- snippet 47
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.equation_add (L329-L333)
lemma equation_add {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) :
    W.Equation (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂) (W.addY x₁ x₂ y₁ <| W.slope x₁ x₂ y₁ y₂) :=
(equation_neg ..).mpr <| equation_negAdd h₁ h₂ hxy
```

-- snippet 48
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.nonsingular_negAdd (L334-L350)
lemma nonsingular_negAdd {x₁ x₂ y₁ y₂ : F} (h₁ : W.Nonsingular x₁ y₁) (h₂ : W.Nonsingular x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : W.Nonsingular
      (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂) (W.negAddY x₁ x₂ y₁ <| W.slope x₁ x₂ y₁ y₂) := by
by_cases hx₁ : W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂) = x₁
  · rwa [negAddY, hx₁, sub_self, mul_zero, zero_add]
  · by_cases hx₂ : W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂) = x₂
    · by_cases hx : x₁ = x₂
      · subst hx
        contradiction
      · rwa [negAddY, ← neg_sub, mul_neg, hx₂, slope_of_X_ne hx,
          div_mul_cancel₀ _ <| sub_ne_zero_of_ne hx, neg_sub, sub_add_cancel]
    · apply nonsingular_negAdd_of_eval_derivative_ne_zero <| equation_negAdd h₁.left h₂.left hxy
      rw [derivative_addPolynomial_slope h₁.left h₂.left hxy]
      eval_simp
      simp only [neg_ne_zero, sub_self, mul_zero, add_zero]
      exact mul_ne_zero (sub_ne_zero_of_ne hx₁) (sub_ne_zero_of_ne hx₂)
```

-- snippet 49
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff_nonsingular (L248-L255)
lemma equation_iff_nonsingular [Nontrivial R] [W.IsElliptic] {x y : R} :
    W.toAffine.Equation x y ↔ W.toAffine.Nonsingular x y :=
W.toAffine.equation_iff_nonsingular_of_Δ_ne_zero <| W.coe_Δ' ▸ W.Δ'.ne_zero

/-! ## Maps and base changes -/

variable (f : R →+* S) (x y : R)
```

-- snippet 50
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.negY (L113-L115)
def negY (x y : R) : R :=
-y - W'.a₁ * x - W'.a₃
```

-- snippet 51
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.nonsingular_iff' (L213-L216)
lemma nonsingular_iff' (x y : R) : W.Nonsingular x y ↔ W.Equation x y ∧
    (W.a₁ * y - (3 * x ^ 2 + 2 * W.a₂ * x + W.a₄) ≠ 0 ∨ 2 * y + W.a₁ * x + W.a₃ ≠ 0) := by
rw [Nonsingular, equation_iff', evalEval_polynomialX, evalEval_polynomialY]
```

-- snippet 52
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.baseChange_addPolynomial (L450-L453)
lemma baseChange_addPolynomial : (W'.baseChange B).toAffine.addPolynomial (f x) (f y) (f ℓ) =
    ((W'.baseChange A).toAffine.addPolynomial x y ℓ).map f := by
rw [← RingHom.coe_coe, ← map_addPolynomial, map_baseChange]
```

-- snippet 53
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.equation_negAdd (L322-L328)
lemma equation_negAdd {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : W.Equation
      (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂) (W.negAddY x₁ x₂ y₁ <| W.slope x₁ x₂ y₁ y₂) := by
rw [equation_add_iff, addPolynomial_slope h₁ h₂ hxy]
  eval_simp
  rw [neg_eq_zero, sub_self, mul_zero]
```

-- snippet 54
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.Y_sub_polynomialY (L99-L103)
lemma Y_sub_polynomialY : Y - W'.polynomialY = W'.negPolynomial := by
rw [polynomialY, negPolynomial]
  C_simp
  ring1
```

-- snippet 55
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_Y_ne (L184-L188)
lemma slope_of_Y_ne {x₁ x₂ y₁ y₂ : F} (hx : x₁ = x₂) (hy : y₁ ≠ W.negY x₂ y₂) :
    W.slope x₁ x₂ y₁ y₂ =
      (3 * x₁ ^ 2 + 2 * W.a₂ * x₁ + W.a₄ - W.a₁ * y₁) / (y₁ - W.negY x₁ y₁) := by
simp_all
```

-- snippet 56
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_X_ne (L190-L193)
lemma slope_of_X_ne {x₁ x₂ y₁ y₂ : F} (hx : x₁ ≠ x₂) :
    W.slope x₁ x₂ y₁ y₂ = (y₁ - y₂) / (x₁ - x₂) := by
rw [slope, if_neg hx]
```

-- snippet 57
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.nonsingular_neg (L139-L167)
lemma nonsingular_neg (x y : R) : W'.Nonsingular x (W'.negY x y) ↔ W'.Nonsingular x y := by
rw [nonsingular_iff, equation_neg, ← negY, negY_negY, ← @ne_comm _ y, nonsingular_iff]
  exact and_congr_right' <| (iff_congr not_and_or.symm not_and_or.symm).mpr <|
    not_congr <| and_congr_left fun h => by rw [← h]

/-! ## Slope formulae in affine coordinates -/

variable (W') in
/-- The line polynomial `ℓ(X - x) + y` associated to the line `Y = ℓ(X - x) + y` that passes through
a nonsingular affine point `(x, y)` on a Weierstrass curve `W` with a slope of `ℓ`.

This does not depend on `W`, and has argument order: `x`, `y`, `ℓ`. -/
noncomputable def linePolynomial (x y ℓ : R) : R[X] :=
  C ℓ * (X - C x) + C y

section slope

variable [DecidableEq F]

variable (W) in
/-- The slope of the line through two nonsingular affine points `(x₁, y₁)` and `(x₂, y₂)` on a
Weierstrass curve `W`.

If `x₁ ≠ x₂`, then this line is the secant of `W` through `(x₁, y₁)` and `(x₂, y₂)`, and has slope
`(y₁ - y₂) / (x₁ - x₂)`. Otherwise, if `y₁ ≠ -y₁ - a₁x₁ - a₃`, then this line is the tangent of `W`
at `(x₁, y₁) = (x₂, y₂)`, and has slope `(3x₁² + 2a₂x₁ + a₄ - a₁y₁) / (2y₁ + a₁x₁ + a₃)`. Otherwise,
this line is vertical, in which case this returns the value `0`.

This depends on `W`, and has argument order: `x₁`, `x₂`, `y₁`, `y₂`. -/
```

-- snippet 58
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addX (L238-L245)
def addX (x₁ x₂ ℓ : R) : R :=
ℓ ^ 2 + W'.a₁ * ℓ - W'.a₂ - x₁ - x₂

variable (W') in
/-- The `Y`-coordinate of `-((x₁, y₁) + (x₂, y₂))` for two nonsingular affine points `(x₁, y₁)` and
`(x₂, y₂)` on a Weierstrass curve `W`, where the line through them has a slope of `ℓ`.

This depends on `W`, and has argument order: `x₁`, `x₂`, `y₁`, `ℓ`. -/
```

-- snippet 59
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.nonsingular_negAdd_of_eval_derivative_ne_zero (L303-L316)
lemma nonsingular_negAdd_of_eval_derivative_ne_zero {x₁ x₂ y₁ ℓ : R}
    (hx' : W'.Equation (W'.addX x₁ x₂ ℓ) (W'.negAddY x₁ x₂ y₁ ℓ))
    (hx : (W'.addPolynomial x₁ y₁ ℓ).derivative.eval (W'.addX x₁ x₂ ℓ) ≠ 0) :
    W'.Nonsingular (W'.addX x₁ x₂ ℓ) (W'.negAddY x₁ x₂ y₁ ℓ) := by
rw [Nonsingular, and_iff_right hx', negAddY, polynomialX, polynomialY]
  eval_simp
  contrapose! hx
  rw [addPolynomial, linePolynomial, polynomial]
  eval_simp
  derivative_simp
  simp only [zero_add, add_zero, sub_zero, zero_mul, mul_one]
  eval_simp
  linear_combination (norm := (norm_num1; ring1)) hx.left + ℓ * hx.right
```

-- snippet 60
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomialY_zero (L200-L209)
lemma evalEval_polynomialY_zero : W.polynomialY.evalEval 0 0 = W.a₃ := by
simp only [evalEval_polynomialY, zero_add, mul_zero]

variable (W) in
/-- The proposition that an affine point `(x, y)` on a Weierstrass curve `W` is nonsingular.

In other words, either `W_X(x, y) ≠ 0` or `W_Y(x, y) ≠ 0`.

Note that this definition is only mathematically accurate for fields. -/
-- TODO: generalise this definition to be mathematically accurate for a larger class of rings.
```

-- snippet 61
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_addX (L415-L418)
lemma map_addX : (W'.map f).toAffine.addX (f x₁) (f x₂) (f ℓ) = f (W'.addX x₁ x₂ ℓ) := by
simp only [addX]
  map_simp
```

-- snippet 62
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.Y_eq_of_Y_ne (L130-L133)
lemma Y_eq_of_Y_ne {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂) (hx : x₁ = x₂)
    (hy : y₁ ≠ W.negY x₂ y₂) : y₁ = y₂ :=
(Y_eq_of_X_eq h₁ h₂ hx).resolve_right hy
```

-- snippet 63
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.Y_sub_negPolynomial (L104-L111)
lemma Y_sub_negPolynomial : Y - W'.negPolynomial = W'.polynomialY := by
rw [← Y_sub_polynomialY, sub_sub_cancel]

variable (W') in
/-- The `Y`-coordinate of `-(x, y)` for a nonsingular affine point `(x, y)` on a Weierstrass curve
`W`.

This depends on `W`, and has argument order: `x`, `y`. -/
```

-- snippet 64
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_polynomialY (L305-L309)
lemma baseChange_polynomialY : (W.baseChange B).toAffine.polynomialY =
    (W.baseChange A).toAffine.polynomialY.map (mapRingHom f) := by
rw [← map_polynomialY, map_baseChange]

variable {f} in
```

-- snippet 65
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.Equation (L148-L150)
def Equation (x y : R) : Prop :=
W.polynomial.evalEval x y = 0
```

-- snippet 66
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope (L168-L172)
def slope (x₁ x₂ y₁ y₂ : F) : F :=
if x₁ = x₂ then if y₁ = W.negY x₂ y₂ then 0
    else (3 * x₁ ^ 2 + 2 * W.a₂ * x₁ + W.a₄ - W.a₁ * y₁) / (y₁ - W.negY x₁ y₁)
  else (y₁ - y₂) / (x₁ - x₂)
```

-- snippet 67
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_zero (L160-L162)
lemma equation_zero : W.Equation 0 0 ↔ W.a₆ = 0 := by
rw [Equation, evalEval_polynomial_zero, neg_eq_zero]
```

-- snippet 68
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.nonsingular_iff_variableChange (L228-L241)
lemma nonsingular_iff_variableChange (x y : R) :
    W.Nonsingular x y ↔ (VariableChange.mk 1 x 0 y • W).toAffine.Nonsingular 0 0 := by
rw [nonsingular_iff', equation_iff_variableChange, equation_zero, ← neg_ne_zero, or_comm,
    nonsingular_zero, variableChange_a₃, variableChange_a₄, inv_one, Units.val_one]
  simp only [variableChange_def]
  congr! 3 <;> ring1

private lemma equation_zero_iff_nonsingular_zero_of_Δ_ne_zero (hΔ : W.Δ ≠ 0) :
    W.Equation 0 0 ↔ W.Nonsingular 0 0 := by
  simp only [equation_zero, nonsingular_zero, iff_self_and]
  contrapose! hΔ
  simp only [b₂, b₄, b₆, b₈, Δ, hΔ]
  ring1
```

-- snippet 69
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_polynomialX (L269-L272)
lemma map_polynomialX : (W.map f).toAffine.polynomialX = W.polynomialX.map (mapRingHom f) := by
simp only [polynomialX]
  map_simp
```

-- snippet 70
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_slope (L428-L441)
lemma map_slope [DecidableEq F] [DecidableEq K] (f : F →+* K) (x₁ x₂ y₁ y₂ : F) :
    (W.map f).toAffine.slope (f x₁) (f x₂) (f y₁) (f y₂) = f (W.slope x₁ x₂ y₁ y₂) := by
by_cases hx : x₁ = x₂
  · by_cases hy : y₁ = W.negY x₂ y₂
    · rw [slope_of_Y_eq (congr_arg f hx) <| by rw [hy, map_negY], slope_of_Y_eq hx hy, map_zero]
    · rw [slope_of_Y_ne (congr_arg f hx) <| map_negY f x₂ y₂ ▸ fun h => hy <| f.injective h,
        map_negY, slope_of_Y_ne hx hy]
      map_simp
  · rw [slope_of_X_ne fun h => hx <| f.injective h, slope_of_X_ne hx]
    map_simp

variable [Algebra R S] [Algebra R A] [Algebra S A] [IsScalarTower R S A] [Algebra R B] [Algebra S B]
  [IsScalarTower R S B] (f : A →ₐ[S] B) (x y x₁ y₁ x₂ y₂ ℓ : A)
```

-- snippet 71
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff (L155-L158)
lemma equation_iff (x y : R) : W.Equation x y ↔
    y ^ 2 + W.a₁ * x * y + W.a₃ * y = x ^ 3 + W.a₂ * x ^ 2 + W.a₄ * x + W.a₆ := by
rw [equation_iff', sub_eq_zero]
```

-- snippet 72
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.baseChange_addX (L454-L457)
lemma baseChange_addX : (W'.baseChange B).toAffine.addX (f x₁) (f x₂) (f ℓ) =
    f ((W'.baseChange A).toAffine.addX x₁ x₂ ℓ) := by
rw [← RingHom.coe_coe, ← map_addX, map_baseChange]
```

-- snippet 73
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff' (L151-L154)
lemma equation_iff' (x y : R) : W.Equation x y ↔
    y ^ 2 + W.a₁ * x * y + W.a₃ * y - (x ^ 3 + W.a₂ * x ^ 2 + W.a₄ * x + W.a₆) = 0 := by
rw [Equation, evalEval_polynomial]
```

-- snippet 74
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.Affine (L74-L77)
abbrev Affine : Type r :=
WeierstrassCurve R

/-- The conversion from a Weierstrass curve to affine coordinates. -/
```

-- snippet 75
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomialX (L178-L182)
lemma evalEval_polynomialX (x y : R) :
    W.polynomialX.evalEval x y = W.a₁ * y - (3 * x ^ 2 + 2 * W.a₂ * x + W.a₄) := by
simp only [polynomialX]
  eval_simp
```

-- snippet 76
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_addPolynomial (L409-L414)
lemma map_addPolynomial :
    (W'.map f).toAffine.addPolynomial (f x) (f y) (f ℓ) = (W'.addPolynomial x y ℓ).map f := by
rw [addPolynomial, map_polynomial, eval_map, linePolynomial, addPolynomial, ← coe_mapRingHom,
    ← eval₂_hom, linePolynomial]
  simp
```

-- snippet 77
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.C_addPolynomial_slope (L288-L293)
lemma C_addPolynomial_slope {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : C (W.addPolynomial x₁ y₁ <| W.slope x₁ x₂ y₁ y₂) =
      -(C (X - C x₁) * C (X - C x₂) * C (X - C (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂))) := by
rw [addPolynomial_slope h₁ h₂ hxy]
  simp
```

-- snippet 78
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.baseChange_negY (L446-L449)
lemma baseChange_negY :
    (W'.baseChange B).toAffine.negY (f x) (f y) = f ((W'.baseChange A).toAffine.negY x y) := by
rw [← RingHom.coe_coe, ← map_negY, map_baseChange]
```

-- snippet 79
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.Y_eq_of_X_eq (L124-L129)
lemma Y_eq_of_X_eq {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hx : x₁ = x₂) : y₁ = y₂ ∨ y₁ = W.negY x₂ y₂ := by
rw [equation_iff] at h₁ h₂
  rw [← sub_eq_zero, ← sub_eq_zero (a := y₁), ← mul_eq_zero, negY]
  linear_combination (norm := (rw [hx]; ring1)) h₁ - h₂
```

-- snippet 80
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_nonsingular (L278-L285)
lemma map_nonsingular (hf : Function.Injective f) :
    (W.map f).toAffine.Nonsingular (f x) (f y) ↔ W.Nonsingular x y := by
simp only [Nonsingular, evalEval, map_equation _ _ hf, map_polynomialX, map_polynomialY,
    map_mapRingHom_evalEval, map_ne_zero_iff f hf]

variable [Algebra R S] [Algebra R A] [Algebra S A] [IsScalarTower R S A] [Algebra R B] [Algebra S B]
  [IsScalarTower R S B] (f : A →ₐ[S] B) (x y : A)
```

-- snippet 81
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.nonsingular_iff (L217-L222)
lemma nonsingular_iff (x y : R) : W.Nonsingular x y ↔ W.Equation x y ∧
    (W.a₁ * y ≠ 3 * x ^ 2 + 2 * W.a₂ * x + W.a₄ ∨ y ≠ -y - W.a₁ * x - W.a₃) := by
rw [nonsingular_iff', sub_ne_zero, ← sub_ne_zero (a := y)]
  congr! 3
  ring1
```

-- snippet 82
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_addY (L424-L427)
lemma map_addY :
    (W'.map f).toAffine.addY (f x₁) (f x₂) (f y₁) (f ℓ) = f (W'.toAffine.addY x₁ x₂ y₁ ℓ) := by
simp only [addY, map_negAddY, map_addX, map_negY]
```

-- snippet 83
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff_nonsingular_of_Δ_ne_zero (L242-L247)
lemma equation_iff_nonsingular_of_Δ_ne_zero {x y : R} (hΔ : W.Δ ≠ 0) :
    W.Equation x y ↔ W.Nonsingular x y := by
rw [equation_iff_variableChange, nonsingular_iff_variableChange,
    equation_zero_iff_nonsingular_zero_of_Δ_ne_zero <| by
      rwa [variableChange_Δ, inv_one, Units.val_one, one_pow, one_mul]]
```

-- snippet 84
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.baseChange_negPolynomial (L442-L445)
lemma baseChange_negPolynomial : (W'.baseChange B).toAffine.negPolynomial =
    (W'.baseChange A).toAffine.negPolynomial.map (mapRingHom f) := by
rw [← map_negPolynomial, map_baseChange]
```

-- snippet 85
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.irreducible_polynomial (L122-L133)
lemma irreducible_polynomial [IsDomain R] : Irreducible W.polynomial := by
by_contra h
  rcases (monic_polynomial.not_irreducible_iff_exists_add_mul_eq_coeff natDegree_polynomial).mp h
    with ⟨f, g, h0, h1⟩
  simp only [polynomial_eq, Cubic.coeff_eq_c, Cubic.coeff_eq_d] at h0 h1
  apply_fun degree at h0 h1
  rw [Cubic.degree_of_a_ne_zero' <| neg_ne_zero.mpr <| one_ne_zero' R, degree_mul] at h0
  apply (h1.symm.le.trans Cubic.degree_of_b_eq_zero').not_gt
  rcases Nat.WithBot.add_eq_three_iff.mp h0.symm with h | h | h | h
  iterate 2 rw [degree_add_eq_right_of_degree_lt] <;> simp only [h] <;> decide
  iterate 2 rw [degree_add_eq_left_of_degree_lt] <;> simp only [h] <;> decide
```

-- snippet 86
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_linePolynomial (L405-L408)
lemma map_linePolynomial : linePolynomial (f x) (f y) (f ℓ) = (linePolynomial x y ℓ).map f := by
simp only [linePolynomial]
  map_simp
```

-- snippet 87
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_Y_eq (L174-L177)
lemma slope_of_Y_eq {x₁ x₂ y₁ y₂ : F} (hx : x₁ = x₂) (hy : y₁ = W.negY x₂ y₂) :
    W.slope x₁ x₂ y₁ y₂ = 0 := by
rw [slope, if_pos hx, if_pos hy]
```

-- snippet 88
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_polynomialX (L301-L304)
lemma baseChange_polynomialX : (W.baseChange B).toAffine.polynomialX =
    (W.baseChange A).toAffine.polynomialX.map (mapRingHom f) := by
rw [← map_polynomialX, map_baseChange]
```

-- snippet 89
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.C_addPolynomial (L215-L222)
lemma C_addPolynomial (x y ℓ : R) : C (W'.addPolynomial x y ℓ) =
    (Y - C (linePolynomial x y ℓ)) * (W'.negPolynomial - C (linePolynomial x y ℓ)) +
      W'.polynomial := by
rw [addPolynomial, linePolynomial, polynomial, negPolynomial]
  eval_simp
  C_simp
  ring1
```

-- snippet 90
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_Y_ne' (L179-L183)
lemma slope_of_Y_ne' {x₂ y₁ y₂ : F} (hy : ¬y₁ = -y₂ - W.a₁ * x₂ - W.a₃) :
    W.slope x₂ x₂ y₁ y₂ =
      (3 * x₂ ^ 2 + 2 * W.a₂ * x₂ + W.a₄ - W.a₁ * y₁) / (y₁ - (-y₁ - W.a₁ * x₂ - W.a₃)) := by
simp [slope, hy]
```

-- snippet 91
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.natDegree_polynomial (L115-L118)
lemma natDegree_polynomial [Nontrivial R] : W.polynomial.natDegree = 2 := by
rw [polynomial_eq]
  exact Cubic.natDegree_of_b_ne_zero' one_ne_zero
```

-- snippet 92
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.polynomial_eq (L98-L104)
lemma polynomial_eq : W.polynomial = Cubic.toPoly
    ⟨0, 1, Cubic.toPoly ⟨0, 0, W.a₁, W.a₃⟩, Cubic.toPoly ⟨-1, -W.a₂, -W.a₄, -W.a₆⟩⟩ := by
simp_rw [polynomial, Cubic.toPoly]
  map_simp
  simp only [C_0, C_1]
  ring1
```

-- snippet 93
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_nonsingular (L310-L317)
lemma baseChange_nonsingular (hf : Function.Injective f) :
    (W.baseChange B).toAffine.Nonsingular (f x) (f y) ↔
      (W.baseChange A).toAffine.Nonsingular x y := by
rw [← map_nonsingular _ _ hf, AlgHom.toRingHom_eq_coe, map_baseChange, RingHom.coe_coe]

end Affine

end WeierstrassCurve
```

-- snippet 94
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addY (L256-L264)
def addY (x₁ x₂ y₁ ℓ : R) : R :=
W'.negY (W'.addX x₁ x₂ ℓ) (W'.negAddY x₁ x₂ y₁ ℓ)

section slope

variable [DecidableEq F]

-- Non-terminal simps, used to be field_simp
set_option linter.flexible false in
```

-- snippet 95
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.degree_polynomial (L110-L113)
lemma degree_polynomial [Nontrivial R] : W.polynomial.degree = 2 := by
rw [polynomial_eq]
  exact Cubic.degree_of_b_ne_zero' one_ne_zero
```

-- snippet 96
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.negAddY (L247-L254)
def negAddY (x₁ x₂ y₁ ℓ : R) : R :=
ℓ * (W'.addX x₁ x₂ ℓ - x₁) + y₁

variable (W') in
/-- The `Y`-coordinate of `(x₁, y₁) + (x₂, y₂)` for two nonsingular affine points `(x₁, y₁)` and
`(x₂, y₂)` on a Weierstrass curve `W`, where the line through them has a slope of `ℓ`.

This depends on `W`, and has argument order: `x₁`, `x₂`, `y₁`, `ℓ`. -/
```

-- snippet 97
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomialX_zero (L184-L193)
lemma evalEval_polynomialX_zero : W.polynomialX.evalEval 0 0 = -W.a₄ := by
simp only [evalEval_polynomialX, zero_add, zero_sub, mul_zero, zero_pow <| Nat.succ_ne_zero _]

variable (W) in
/-- The partial derivative `W_Y(X, Y)` with respect to `Y` of the polynomial `W(X, Y)` associated to
a Weierstrass curve `W` in affine coordinates. -/
-- TODO: define this in terms of `Polynomial.derivative`.
noncomputable def polynomialY : R[X][Y] :=
  C (C 2) * Y + C (C W.a₁ * X + C W.a₃)
```

-- snippet 98
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_Y_ne_eq_evalEval (L194-L214)
lemma slope_of_Y_ne_eq_evalEval {x₁ x₂ y₁ y₂ : F} (hx : x₁ = x₂) (hy : y₁ ≠ W.negY x₂ y₂) :
    W.slope x₁ x₂ y₁ y₂ = -W.polynomialX.evalEval x₁ y₁ / W.polynomialY.evalEval x₁ y₁ := by
rw [slope_of_Y_ne hx hy, evalEval_polynomialX, neg_sub]
  congr 1
  rw [negY, evalEval_polynomialY]
  ring1

end slope

/-! ## Addition formulae in affine coordinates -/

variable (W') in
/-- The addition polynomial obtained by substituting the line `Y = ℓ(X - x) + y` into the polynomial
`W(X, Y)` associated to a Weierstrass curve `W`. If such a line intersects `W` at another
nonsingular affine point `(x', y')` on `W`, then the roots of this polynomial are precisely `x`,
`x'`, and the `X`-coordinate of the addition of `(x, y)` and `(x', y')`.

This depends on `W`, and has argument order: `x`, `y`, `ℓ`. -/
noncomputable def addPolynomial (x y ℓ : R) : R[X] :=
  W'.polynomial.eval <| linePolynomial x y ℓ
```

-- snippet 99
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomial (L134-L139)
lemma evalEval_polynomial (x y : R) : W.polynomial.evalEval x y =
    y ^ 2 + W.a₁ * x * y + W.a₃ * y - (x ^ 3 + W.a₂ * x ^ 2 + W.a₄ * x + W.a₆) := by
simp only [polynomial]
  eval_simp
  rw [add_mul, ← add_assoc]
```

-- snippet 100
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_negAddY (L419-L423)
lemma map_negAddY :
    (W'.map f).toAffine.negAddY (f x₁) (f x₂) (f y₁) (f ℓ) = f (W'.negAddY x₁ x₂ y₁ ℓ) := by
simp only [negAddY, map_addX]
  map_simp
```

-- snippet 101
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.nonsingular_add (L351-L359)
lemma nonsingular_add {x₁ x₂ y₁ y₂ : F} (h₁ : W.Nonsingular x₁ y₁) (h₂ : W.Nonsingular x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) :
    W.Nonsingular (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂) (W.addY x₁ x₂ y₁ <| W.slope x₁ x₂ y₁ y₂) :=
(nonsingular_neg ..).mpr <| nonsingular_negAdd h₁ h₂ hxy

-- Non-terminal simp, used to be field_simp
set_option linter.flexible false in
/-- The formula `x(P₁ + P₂) = x(P₁ - P₂) - ψ(P₁)ψ(P₂) / (x(P₂) - x(P₁))²`,
where `ψ(x,y) = 2y + a₁x + a₃`. -/
```

-- snippet 102
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.toAffine (L78-L97)
abbrev toAffine (W : WeierstrassCurve R) : Affine R :=
W

variable [CommRing R] [CommRing S] [CommRing A] [CommRing B] {W : Affine R}

namespace Affine

/-! ## Weierstrass equations in affine coordinates -/

variable (W) in
/-- The polynomial `W(X, Y) := Y² + a₁XY + a₃Y - (X³ + a₂X² + a₄X + a₆)` associated to a Weierstrass
curve `W` over a ring `R` in affine coordinates.

For ease of polynomial manipulation, this is represented as a term of type `R[X][X]`, where the
inner variable represents `X` and the outer variable represents `Y`. For clarity, the alternative
notations `Y` and `R[X][Y]` are provided in the `Polynomial.Bivariate` scope to represent the outer
variable and the bivariate polynomial ring `R[X][X]` respectively. -/
noncomputable def polynomial : R[X][Y] :=
  Y ^ 2 + C (C W.a₁ * X + C W.a₃) * Y - C (X ^ 3 + C W.a₂ * X ^ 2 + C W.a₄ * X + C W.a₆)
```

-- snippet 103
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addPolynomial_eq (L223-L236)
lemma addPolynomial_eq (x y ℓ : R) : W'.addPolynomial x y ℓ = -Cubic.toPoly
    ⟨1, -ℓ ^ 2 - W'.a₁ * ℓ + W'.a₂,
      2 * x * ℓ ^ 2 + (W'.a₁ * x - 2 * y - W'.a₃) * ℓ + (-W'.a₁ * y + W'.a₄),
      -x ^ 2 * ℓ ^ 2 + (2 * x * y + W'.a₃ * x) * ℓ - (y ^ 2 + W'.a₃ * y - W'.a₆)⟩ := by
rw [addPolynomial, linePolynomial, polynomial, Cubic.toPoly]
  eval_simp
  C_simp
  ring1

variable (W') in
/-- The `X`-coordinate of `(x₁, y₁) + (x₂, y₂)` for two nonsingular affine points `(x₁, y₁)` and
`(x₂, y₂)` on a Weierstrass curve `W`, where the line through them has a slope of `ℓ`.

This depends on `W`, and has argument order: `x₁`, `x₂`, `ℓ`. -/
```

-- snippet 104
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomialY (L194-L198)
lemma evalEval_polynomialY (x y : R) : W.polynomialY.evalEval x y = 2 * y + W.a₁ * x + W.a₃ := by
simp only [polynomialY]
  eval_simp
  rw [← add_assoc]
```

-- snippet 105
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.equation_add_iff (L317-L321)
lemma equation_add_iff (x₁ x₂ y₁ ℓ : R) : W'.Equation (W'.addX x₁ x₂ ℓ) (W'.negAddY x₁ x₂ y₁ ℓ) ↔
    (W'.addPolynomial x₁ y₁ ℓ).eval (W'.addX x₁ x₂ ℓ) = 0 := by
rw [Equation, negAddY, addPolynomial, linePolynomial, polynomial]
  eval_simp
```

<s>[SYSTEM_PROMPT]You are Leanstral, a Lean 4 proof assistant. Complete the task below using the provided Lean source context. The target mathlib commit is 5e932f97dd25535344f80f9dd8da3aab83df0fe6 (module Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange).[/SYSTEM_PROMPT][MODEL_SETTINGS]{"reasoning_effort": "none"}[/MODEL_SETTINGS][INST]Prove the following theorem. The retrieved context below may help.

```lean
lemma variableChange_b₆ : (C • W).b₆ =
    C.u⁻¹ ^ 6 * (W.b₆ + 2 * C.r * W.b₄ + C.r ^ 2 * W.b₂ + 4 * C.r ^ 3)
```

## Retrieved context
-- snippet 1
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.one_def (L75-L76)
lemma one_def : (1 : VariableChange R) = ⟨1, 0, 0, 0⟩ := rfl
```

-- snippet 2
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.mul_def (L85-L90)
lemma mul_def : C * C' = {
    u := C.u * C'.u
r := C.r * C'.u ^ 2 + C'.r
    s := C'.u * C.s + C'.s
    t := C.t * C'.u ^ 3 + C.r * C'.s * C'.u ^ 2 + C'.t } := rfl
```

-- snippet 3
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.inv_def (L99-L104)
lemma inv_def : C⁻¹ = {
    u := C.u⁻¹
r := -C.r * C.u⁻¹ ^ 2
    s := -C.s * C.u⁻¹
    t := (C.r * C.s - C.t) * C.u⁻¹ ^ 3 } := rfl
```

-- snippet 4
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_def (L137-L145)
lemma variableChange_def : C • W = {
    a₁ := C.u⁻¹ * (W.a₁ + 2 * C.s)
a₂ := C.u⁻¹ ^ 2 * (W.a₂ - C.s * W.a₁ + 3 * C.r - C.s ^ 2)
    a₃ := C.u⁻¹ ^ 3 * (W.a₃ + C.r * W.a₁ + 2 * C.t)
    a₄ := C.u⁻¹ ^ 4 * (W.a₄ - C.s * W.a₃ + 2 * C.r * W.a₂ - (C.t + C.r * C.s) * W.a₁ + 3 * C.r ^ 2
      - 2 * C.s * C.t)
    a₆ := C.u⁻¹ ^ 6 * (W.a₆ + C.r * W.a₄ + C.r ^ 2 * W.a₂ + C.r ^ 3 - C.t * W.a₃ - C.t ^ 2
      - C.r * C.t * W.a₁) } := rfl
```

-- snippet 5
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₁ (L177-L178)
lemma variableChange_a₁ : (C • W).a₁ = C.u⁻¹ * (W.a₁ + 2 * C.s) := rfl
```

-- snippet 6
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₂ (L179-L180)
lemma variableChange_a₂ : (C • W).a₂ = C.u⁻¹ ^ 2 * (W.a₂ - C.s * W.a₁ + 3 * C.r - C.s ^ 2) := rfl
```

-- snippet 7
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₃ (L181-L182)
lemma variableChange_a₃ : (C • W).a₃ = C.u⁻¹ ^ 3 * (W.a₃ + C.r * W.a₁ + 2 * C.t) := rfl
```

-- snippet 8
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₄ (L183-L186)
lemma variableChange_a₄ : (C • W).a₄ =
    C.u⁻¹ ^ 4 * (W.a₄ - C.s * W.a₃ + 2 * C.r * W.a₂ - (C.t + C.r * C.s) * W.a₁ + 3 * C.r ^ 2
      - 2 * C.s * C.t) := rfl
```

-- snippet 9
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_a₆ (L187-L190)
lemma variableChange_a₆ : (C • W).a₆ =
    C.u⁻¹ ^ 6 * (W.a₆ + C.r * W.a₄ + C.r ^ 2 * W.a₂ + C.r ^ 3 - C.t * W.a₃ - C.t ^ 2
      - C.r * C.t * W.a₁) := rfl
```

-- snippet 10
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_b₂ (L191-L194)
lemma variableChange_b₂ : (C • W).b₂ = C.u⁻¹ ^ 2 * (W.b₂ + 12 * C.r) := by
simp only [b₂, variableChange_a₁, variableChange_a₂]
  ring1
```

-- snippet 11
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_b₄ (L195-L198)
lemma variableChange_b₄ : (C • W).b₄ = C.u⁻¹ ^ 4 * (W.b₄ + C.r * W.b₂ + 6 * C.r ^ 2) := by
simp only [b₂, b₄, variableChange_a₁, variableChange_a₃, variableChange_a₄]
  ring1
```

-- snippet 12
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_b₈ (L204-L209)
lemma variableChange_b₈ : (C • W).b₈ = C.u⁻¹ ^ 8 *
    (W.b₈ + 3 * C.r * W.b₆ + 3 * C.r ^ 2 * W.b₄ + C.r ^ 3 * W.b₂ + 3 * C.r ^ 4) := by
simp only [b₂, b₄, b₆, b₈, variableChange_a₁, variableChange_a₂, variableChange_a₃,
    variableChange_a₄, variableChange_a₆]
  ring1
```

-- snippet 13
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_c₄ (L210-L213)
lemma variableChange_c₄ : (C • W).c₄ = C.u⁻¹ ^ 4 * W.c₄ := by
simp only [c₄, variableChange_b₂, variableChange_b₄]
  ring1
```

-- snippet 14
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_c₆ (L214-L217)
lemma variableChange_c₆ : (C • W).c₆ = C.u⁻¹ ^ 6 * W.c₆ := by
simp only [c₆, variableChange_b₂, variableChange_b₄, variableChange_b₆]
  ring1
```

-- snippet 15
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_Δ (L218-L224)
lemma variableChange_Δ : (C • W).Δ = C.u⁻¹ ^ 12 * W.Δ := by
simp only [b₂, b₄, b₆, b₈, Δ, variableChange_a₁, variableChange_a₂, variableChange_a₃,
    variableChange_a₄, variableChange_a₆]
  ring1

variable [W.IsElliptic]
```

-- snippet 16
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_Δ' (L230-L233)
lemma variableChange_Δ' : (C • W).Δ' = C.u⁻¹ ^ 12 * W.Δ' := by
simp_rw [Units.ext_iff, Units.val_mul, coe_Δ', variableChange_Δ, Units.val_pow_eq_pow_val]

set_option linter.docPrime false in
```

-- snippet 17
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.coe_variableChange_Δ' (L234-L237)
lemma coe_variableChange_Δ' : ((C • W).Δ' : R) = C.u⁻¹ ^ 12 * W.Δ' := by
simp_rw [coe_Δ', variableChange_Δ]

set_option linter.docPrime false in
```

-- snippet 18
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.inv_variableChange_Δ' (L238-L241)
lemma inv_variableChange_Δ' : (C • W).Δ'⁻¹ = C.u ^ 12 * W.Δ'⁻¹ := by
rw [variableChange_Δ', mul_inv, inv_pow, inv_inv]

set_option linter.docPrime false in
```

-- snippet 19
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.coe_inv_variableChange_Δ' (L242-L244)
lemma coe_inv_variableChange_Δ' : (↑(C • W).Δ'⁻¹ : R) = C.u ^ 12 * W.Δ'⁻¹ := by
rw [inv_variableChange_Δ', Units.val_mul, Units.val_pow_eq_pow_val]
```

-- snippet 20
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.variableChange_j (L246-L262)
lemma variableChange_j : (C • W).j = W.j := by
rw [j, coe_inv_variableChange_Δ', variableChange_c₄, j, mul_pow, ← pow_mul, ← mul_assoc,
    mul_right_comm (C.u.val ^ 12), ← mul_pow, C.u.mul_inv, one_pow, one_mul]

end VariableChange

section BaseChange

/-! ## Maps and base changes -/

variable {A : Type v} [CommRing A] (φ : R →+* A)

namespace VariableChange

variable (C : VariableChange R)

/-- The change of variables mapped over a ring homomorphism `φ : R →+* A`. -/
```

-- snippet 21
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.map (L264-L268)
def map : VariableChange A :=
⟨Units.map φ C.u, φ C.r, φ C.s, φ C.t⟩

variable (A) in
/-- The change of variables base changed to an algebra `A` over `R`. -/
```

-- snippet 22
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.baseChange (L269-L271)
abbrev baseChange [Algebra R A] : VariableChange A :=
C.map <| algebraMap R A
```

-- snippet 23
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.map_id (L273-L275)
lemma map_id : C.map (RingHom.id R) = C :=
rfl
```

-- snippet 24
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.map_map (L276-L279)
lemma map_map {A : Type v} [CommRing A] (φ : R →+* A) {B : Type w} [CommRing B] (ψ : A →+* B) :
    (C.map φ).map ψ = C.map (ψ.comp φ) :=
rfl
```

-- snippet 25
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.VariableChange.map_baseChange (L281-L285)
lemma map_baseChange {S : Type s} [CommRing S] [Algebra R S] {A : Type v} [CommRing A] [Algebra R A]
    [Algebra S A] [IsScalarTower R S A] {B : Type w} [CommRing B] [Algebra R B] [Algebra S B]
    [IsScalarTower R S B] (ψ : A →ₐ[S] B) : (C.baseChange A).map ψ = C.baseChange B :=
congr_arg C.map <| ψ.comp_algebraMap_of_tower R
```

-- snippet 26
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_negPolynomial (L396-L400)
lemma map_negPolynomial :
    (W'.map f).toAffine.negPolynomial = W'.negPolynomial.map (mapRingHom f) := by
simp only [negPolynomial]
  map_simp
```

-- snippet 27
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_polynomial (L286-L290)
lemma baseChange_polynomial : (W.baseChange B).toAffine.polynomial =
    (W.baseChange A).toAffine.polynomial.map (mapRingHom f) := by
rw [← map_polynomial, map_baseChange]

variable {x y} in
```

-- snippet 28
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addY_sub_negY_addY (L382-L395)
lemma addY_sub_negY_addY {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂) :
    let x₃ := W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂)
let y₃ := W.addY x₁ x₂ y₁ (W.slope x₁ x₂ y₁ y₂)
    y₃ - W.negY x₃ y₃ =
      ((y₂ - W.negY x₂ y₂) * (x₁ - x₃) - (y₁ - W.negY x₁ y₁) * (x₂ - x₃)) / (x₂ - x₁) := by
  simp_rw [addY, negY, eq_div_iff (sub_ne_zero.mpr hx.symm)]
  linear_combination (norm := ring1) 2 * cyclic_sum_Y_mul_X_sub_X y₁ y₂ hx

end slope

/-! ## Maps and base changes -/

variable (f : R →+* S) (x y x₁ y₁ x₂ y₂ ℓ : R)
```

-- snippet 29
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomial_zero (L141-L147)
lemma evalEval_polynomial_zero : W.polynomial.evalEval 0 0 = -W.a₆ := by
simp only [evalEval_polynomial, zero_add, zero_sub, mul_zero, zero_pow <| Nat.succ_ne_zero _]

variable (W) in
/-- The proposition that an affine point `(x, y)` lies in a Weierstrass curve `W`.

In other words, it satisfies the Weierstrass equation `W(X, Y) = 0`. -/
```

-- snippet 30
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_polynomialY (L273-L277)
lemma map_polynomialY : (W.map f).toAffine.polynomialY = W.polynomialY.map (mapRingHom f) := by
simp only [polynomialY]
  map_simp

variable {f} in
```

-- snippet 31
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.negY_negY (L116-L119)
lemma negY_negY (x y : R) : W'.negY x (W'.negY x y) = y := by
simp only [negY]
  ring1
```

-- snippet 32
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff_variableChange (L163-L177)
lemma equation_iff_variableChange (x y : R) :
    W.Equation x y ↔ (VariableChange.mk 1 x 0 y • W).toAffine.Equation 0 0 := by
rw [equation_iff', ← neg_eq_zero, equation_zero, variableChange_a₆, inv_one, Units.val_one]
  congr! 1
  ring1

/-! ## The nonsingular condition in affine coordinates -/

variable (W) in
/-- The partial derivative `W_X(X, Y)` with respect to `X` of the polynomial `W(X, Y)` associated to
a Weierstrass curve `W` in affine coordinates. -/
-- TODO: define this in terms of `Polynomial.derivative`.
noncomputable def polynomialX : R[X][Y] :=
  C (C W.a₁) * Y - C (C 3 * X ^ 2 + C (2 * W.a₂) * X + C W.a₄)
```

-- snippet 33
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.evalEval_negPolynomial (L120-L123)
lemma evalEval_negPolynomial (x y : R) : W'.negPolynomial.evalEval x y = W'.negY x y := by
rw [negY, sub_sub, negPolynomial]
  eval_simp
```

-- snippet 34
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addX_eq_addX_negY_sub (L360-L372)
lemma addX_eq_addX_negY_sub {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂) :
    W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂) = W.addX x₁ x₂ (W.slope x₁ x₂ y₁ <| W.negY x₂ y₂) -
      (y₁ - W.negY x₁ y₁) * (y₂ - W.negY x₂ y₂) / (x₂ - x₁) ^ 2 := by
simp_rw [slope_of_X_ne hx, addX, negY, ← neg_sub x₁, neg_sq]
  simp [field]
  ring1

-- Non-terminal simp, used to be field_simp
set_option linter.flexible false in
-- see https://github.com/leanprover-community/mathlib4/issues/29041
set_option linter.unusedSimpArgs false in
/-- The formula `y(P₁)(x(P₂) - x(P₃)) + y(P₂)(x(P₃) - x(P₁)) + y(P₃)(x(P₁) - x(P₂)) = 0`,
assuming that `P₁ + P₂ + P₃ = O`. -/
```

-- snippet 35
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_negY (L401-L404)
lemma map_negY : (W'.map f).toAffine.negY (f x) (f y) = f (W'.negY x y) := by
simp only [negY]
  map_simp
```

-- snippet 36
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addPolynomial_slope (L265-L287)
lemma addPolynomial_slope {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : W.addPolynomial x₁ y₁ (W.slope x₁ x₂ y₁ y₂) =
      -((X - C x₁) * (X - C x₂) * (X - C (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂))) := by
rw [addPolynomial_eq, neg_inj, Cubic.prod_X_sub_C_eq, Cubic.toPoly_injective]
  by_cases hx : x₁ = x₂
  · have hy : y₁ ≠ W.negY x₂ y₂ := fun h => hxy ⟨hx, h⟩
    rcases hx, Y_eq_of_Y_ne h₁ h₂ hx hy with ⟨rfl, rfl⟩
    rw [equation_iff] at h₁ h₂
    rw [slope_of_Y_ne rfl hy]
    rw [negY, ← sub_ne_zero] at hy
    replace hy : y₁ - (-y₁ - x₁ * W.a₁ - W.a₃) ≠ 0 := by convert hy using 1; ring
    ext
    · rfl
    · simp only [addX]
      ring1
    · simp [field]
      ring1
    · linear_combination (norm := (simp [field]; ring1)) -h₁
  · rw [equation_iff] at h₁ h₂
    rw [slope_of_X_ne hx]
    simp only [addX]
    grind
```

-- snippet 37
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.equation_neg (L134-L138)
lemma equation_neg (x y : R) : W'.Equation x (W'.negY x y) ↔ W'.Equation x y := by
rw [equation_iff, equation_iff, negY]
  congr! 1
  ring1
```

-- snippet 38
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.Nonsingular (L210-L212)
def Nonsingular (x y : R) : Prop :=
W.Equation x y ∧ (W.polynomialX.evalEval x y ≠ 0 ∨ W.polynomialY.evalEval x y ≠ 0)
```

-- snippet 39
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_equation (L297-L300)
lemma baseChange_equation (hf : Function.Injective f) :
    (W.baseChange B).toAffine.Equation (f x) (f y) ↔ (W.baseChange A).toAffine.Equation x y := by
rw [← map_equation _ _ hf, AlgHom.toRingHom_eq_coe, map_baseChange, RingHom.coe_coe]
```

-- snippet 40
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.nonsingular_zero (L224-L227)
lemma nonsingular_zero : W.Nonsingular 0 0 ↔ W.a₆ = 0 ∧ (W.a₃ ≠ 0 ∨ W.a₄ ≠ 0) := by
rw [Nonsingular, equation_zero, evalEval_polynomialX_zero, neg_ne_zero, evalEval_polynomialY_zero,
    or_comm]
```

-- snippet 41
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_polynomial (L256-L260)
lemma map_polynomial : (W.map f).toAffine.polynomial = W.polynomial.map (mapRingHom f) := by
simp only [polynomial]
  map_simp

variable {x y} in
```

-- snippet 42
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.polynomial_ne_zero (L105-L108)
lemma polynomial_ne_zero [Nontrivial R] : W.polynomial ≠ 0 := by
rw [polynomial_eq]
  exact Cubic.ne_zero_of_b_ne_zero one_ne_zero
```

-- snippet 43
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.derivative_addPolynomial_slope (L294-L302)
lemma derivative_addPolynomial_slope {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁)
    (h₂ : W.Equation x₂ y₂) (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) :
    derivative (W.addPolynomial x₁ y₁ <| W.slope x₁ x₂ y₁ y₂) =
      -((X - C x₁) * (X - C x₂) + (X - C x₁) * (X - C (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂)) +
          (X - C x₂) * (X - C (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂))) := by
rw [addPolynomial_slope h₁ h₂ hxy]
  derivative_simp
  ring1
```

-- snippet 44
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_equation (L265-L268)
lemma map_equation (hf : Function.Injective f) :
    (W.map f).toAffine.Equation (f x) (f y) ↔ W.Equation x y := by
simp only [Equation, map_polynomial, map_mapRingHom_evalEval, map_eq_zero_iff f hf]
```

-- snippet 45
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.cyclic_sum_Y_mul_X_sub_X (L373-L381)
lemma cyclic_sum_Y_mul_X_sub_X {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂) :
    let x₃ := W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂)
y₁ * (x₂ - x₃) + y₂ * (x₃ - x₁) + W.negAddY x₁ x₂ y₁ (W.slope x₁ x₂ y₁ y₂) * (x₁ - x₂) = 0 := by
  simp_rw [slope_of_X_ne hx, negAddY, addX]
  simp [field, sub_ne_zero.mpr hx]
  ring1

/-- The formula `ψ(P₁ + P₂) = (ψ(P₂)(x(P₁) - x(P₃)) - ψ(P₁)(x(P₂) - x(P₃))) / (x(P₂) - x(P₁))`,
where `ψ(x,y) = 2y + a₁x + a₃`. -/
```

-- snippet 46
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.monic_polynomial (L119-L121)
lemma monic_polynomial : W.polynomial.Monic := by
simpa only [polynomial_eq] using Cubic.monic_of_b_eq_one'
```

-- snippet 47
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.equation_add (L329-L333)
lemma equation_add {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) :
    W.Equation (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂) (W.addY x₁ x₂ y₁ <| W.slope x₁ x₂ y₁ y₂) :=
(equation_neg ..).mpr <| equation_negAdd h₁ h₂ hxy
```

-- snippet 48
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.nonsingular_negAdd (L334-L350)
lemma nonsingular_negAdd {x₁ x₂ y₁ y₂ : F} (h₁ : W.Nonsingular x₁ y₁) (h₂ : W.Nonsingular x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : W.Nonsingular
      (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂) (W.negAddY x₁ x₂ y₁ <| W.slope x₁ x₂ y₁ y₂) := by
by_cases hx₁ : W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂) = x₁
  · rwa [negAddY, hx₁, sub_self, mul_zero, zero_add]
  · by_cases hx₂ : W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂) = x₂
    · by_cases hx : x₁ = x₂
      · subst hx
        contradiction
      · rwa [negAddY, ← neg_sub, mul_neg, hx₂, slope_of_X_ne hx,
          div_mul_cancel₀ _ <| sub_ne_zero_of_ne hx, neg_sub, sub_add_cancel]
    · apply nonsingular_negAdd_of_eval_derivative_ne_zero <| equation_negAdd h₁.left h₂.left hxy
      rw [derivative_addPolynomial_slope h₁.left h₂.left hxy]
      eval_simp
      simp only [neg_ne_zero, sub_self, mul_zero, add_zero]
      exact mul_ne_zero (sub_ne_zero_of_ne hx₁) (sub_ne_zero_of_ne hx₂)
```

-- snippet 49
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff_nonsingular (L248-L255)
lemma equation_iff_nonsingular [Nontrivial R] [W.IsElliptic] {x y : R} :
    W.toAffine.Equation x y ↔ W.toAffine.Nonsingular x y :=
W.toAffine.equation_iff_nonsingular_of_Δ_ne_zero <| W.coe_Δ' ▸ W.Δ'.ne_zero

/-! ## Maps and base changes -/

variable (f : R →+* S) (x y : R)
```

-- snippet 50
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.negY (L113-L115)
def negY (x y : R) : R :=
-y - W'.a₁ * x - W'.a₃
```

-- snippet 51
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.nonsingular_iff' (L213-L216)
lemma nonsingular_iff' (x y : R) : W.Nonsingular x y ↔ W.Equation x y ∧
    (W.a₁ * y - (3 * x ^ 2 + 2 * W.a₂ * x + W.a₄) ≠ 0 ∨ 2 * y + W.a₁ * x + W.a₃ ≠ 0) := by
rw [Nonsingular, equation_iff', evalEval_polynomialX, evalEval_polynomialY]
```

-- snippet 52
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.baseChange_addPolynomial (L450-L453)
lemma baseChange_addPolynomial : (W'.baseChange B).toAffine.addPolynomial (f x) (f y) (f ℓ) =
    ((W'.baseChange A).toAffine.addPolynomial x y ℓ).map f := by
rw [← RingHom.coe_coe, ← map_addPolynomial, map_baseChange]
```

-- snippet 53
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.equation_negAdd (L322-L328)
lemma equation_negAdd {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : W.Equation
      (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂) (W.negAddY x₁ x₂ y₁ <| W.slope x₁ x₂ y₁ y₂) := by
rw [equation_add_iff, addPolynomial_slope h₁ h₂ hxy]
  eval_simp
  rw [neg_eq_zero, sub_self, mul_zero]
```

-- snippet 54
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.Y_sub_polynomialY (L99-L103)
lemma Y_sub_polynomialY : Y - W'.polynomialY = W'.negPolynomial := by
rw [polynomialY, negPolynomial]
  C_simp
  ring1
```

-- snippet 55
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_Y_ne (L184-L188)
lemma slope_of_Y_ne {x₁ x₂ y₁ y₂ : F} (hx : x₁ = x₂) (hy : y₁ ≠ W.negY x₂ y₂) :
    W.slope x₁ x₂ y₁ y₂ =
      (3 * x₁ ^ 2 + 2 * W.a₂ * x₁ + W.a₄ - W.a₁ * y₁) / (y₁ - W.negY x₁ y₁) := by
simp_all
```

-- snippet 56
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_X_ne (L190-L193)
lemma slope_of_X_ne {x₁ x₂ y₁ y₂ : F} (hx : x₁ ≠ x₂) :
    W.slope x₁ x₂ y₁ y₂ = (y₁ - y₂) / (x₁ - x₂) := by
rw [slope, if_neg hx]
```

-- snippet 57
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.nonsingular_neg (L139-L167)
lemma nonsingular_neg (x y : R) : W'.Nonsingular x (W'.negY x y) ↔ W'.Nonsingular x y := by
rw [nonsingular_iff, equation_neg, ← negY, negY_negY, ← @ne_comm _ y, nonsingular_iff]
  exact and_congr_right' <| (iff_congr not_and_or.symm not_and_or.symm).mpr <|
    not_congr <| and_congr_left fun h => by rw [← h]

/-! ## Slope formulae in affine coordinates -/

variable (W') in
/-- The line polynomial `ℓ(X - x) + y` associated to the line `Y = ℓ(X - x) + y` that passes through
a nonsingular affine point `(x, y)` on a Weierstrass curve `W` with a slope of `ℓ`.

This does not depend on `W`, and has argument order: `x`, `y`, `ℓ`. -/
noncomputable def linePolynomial (x y ℓ : R) : R[X] :=
  C ℓ * (X - C x) + C y

section slope

variable [DecidableEq F]

variable (W) in
/-- The slope of the line through two nonsingular affine points `(x₁, y₁)` and `(x₂, y₂)` on a
Weierstrass curve `W`.

If `x₁ ≠ x₂`, then this line is the secant of `W` through `(x₁, y₁)` and `(x₂, y₂)`, and has slope
`(y₁ - y₂) / (x₁ - x₂)`. Otherwise, if `y₁ ≠ -y₁ - a₁x₁ - a₃`, then this line is the tangent of `W`
at `(x₁, y₁) = (x₂, y₂)`, and has slope `(3x₁² + 2a₂x₁ + a₄ - a₁y₁) / (2y₁ + a₁x₁ + a₃)`. Otherwise,
this line is vertical, in which case this returns the value `0`.

This depends on `W`, and has argument order: `x₁`, `x₂`, `y₁`, `y₂`. -/
```

-- snippet 58
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addX (L238-L245)
def addX (x₁ x₂ ℓ : R) : R :=
ℓ ^ 2 + W'.a₁ * ℓ - W'.a₂ - x₁ - x₂

variable (W') in
/-- The `Y`-coordinate of `-((x₁, y₁) + (x₂, y₂))` for two nonsingular affine points `(x₁, y₁)` and
`(x₂, y₂)` on a Weierstrass curve `W`, where the line through them has a slope of `ℓ`.

This depends on `W`, and has argument order: `x₁`, `x₂`, `y₁`, `ℓ`. -/
```

-- snippet 59
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.nonsingular_negAdd_of_eval_derivative_ne_zero (L303-L316)
lemma nonsingular_negAdd_of_eval_derivative_ne_zero {x₁ x₂ y₁ ℓ : R}
    (hx' : W'.Equation (W'.addX x₁ x₂ ℓ) (W'.negAddY x₁ x₂ y₁ ℓ))
    (hx : (W'.addPolynomial x₁ y₁ ℓ).derivative.eval (W'.addX x₁ x₂ ℓ) ≠ 0) :
    W'.Nonsingular (W'.addX x₁ x₂ ℓ) (W'.negAddY x₁ x₂ y₁ ℓ) := by
rw [Nonsingular, and_iff_right hx', negAddY, polynomialX, polynomialY]
  eval_simp
  contrapose! hx
  rw [addPolynomial, linePolynomial, polynomial]
  eval_simp
  derivative_simp
  simp only [zero_add, add_zero, sub_zero, zero_mul, mul_one]
  eval_simp
  linear_combination (norm := (norm_num1; ring1)) hx.left + ℓ * hx.right
```

-- snippet 60
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomialY_zero (L200-L209)
lemma evalEval_polynomialY_zero : W.polynomialY.evalEval 0 0 = W.a₃ := by
simp only [evalEval_polynomialY, zero_add, mul_zero]

variable (W) in
/-- The proposition that an affine point `(x, y)` on a Weierstrass curve `W` is nonsingular.

In other words, either `W_X(x, y) ≠ 0` or `W_Y(x, y) ≠ 0`.

Note that this definition is only mathematically accurate for fields. -/
-- TODO: generalise this definition to be mathematically accurate for a larger class of rings.
```

-- snippet 61
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_addX (L415-L418)
lemma map_addX : (W'.map f).toAffine.addX (f x₁) (f x₂) (f ℓ) = f (W'.addX x₁ x₂ ℓ) := by
simp only [addX]
  map_simp
```

-- snippet 62
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.Y_eq_of_Y_ne (L130-L133)
lemma Y_eq_of_Y_ne {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂) (hx : x₁ = x₂)
    (hy : y₁ ≠ W.negY x₂ y₂) : y₁ = y₂ :=
(Y_eq_of_X_eq h₁ h₂ hx).resolve_right hy
```

-- snippet 63
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.Y_sub_negPolynomial (L104-L111)
lemma Y_sub_negPolynomial : Y - W'.negPolynomial = W'.polynomialY := by
rw [← Y_sub_polynomialY, sub_sub_cancel]

variable (W') in
/-- The `Y`-coordinate of `-(x, y)` for a nonsingular affine point `(x, y)` on a Weierstrass curve
`W`.

This depends on `W`, and has argument order: `x`, `y`. -/
```

-- snippet 64
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_polynomialY (L305-L309)
lemma baseChange_polynomialY : (W.baseChange B).toAffine.polynomialY =
    (W.baseChange A).toAffine.polynomialY.map (mapRingHom f) := by
rw [← map_polynomialY, map_baseChange]

variable {f} in
```

-- snippet 65
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.Equation (L148-L150)
def Equation (x y : R) : Prop :=
W.polynomial.evalEval x y = 0
```

-- snippet 66
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope (L168-L172)
def slope (x₁ x₂ y₁ y₂ : F) : F :=
if x₁ = x₂ then if y₁ = W.negY x₂ y₂ then 0
    else (3 * x₁ ^ 2 + 2 * W.a₂ * x₁ + W.a₄ - W.a₁ * y₁) / (y₁ - W.negY x₁ y₁)
  else (y₁ - y₂) / (x₁ - x₂)
```

-- snippet 67
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_zero (L160-L162)
lemma equation_zero : W.Equation 0 0 ↔ W.a₆ = 0 := by
rw [Equation, evalEval_polynomial_zero, neg_eq_zero]
```

-- snippet 68
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.nonsingular_iff_variableChange (L228-L241)
lemma nonsingular_iff_variableChange (x y : R) :
    W.Nonsingular x y ↔ (VariableChange.mk 1 x 0 y • W).toAffine.Nonsingular 0 0 := by
rw [nonsingular_iff', equation_iff_variableChange, equation_zero, ← neg_ne_zero, or_comm,
    nonsingular_zero, variableChange_a₃, variableChange_a₄, inv_one, Units.val_one]
  simp only [variableChange_def]
  congr! 3 <;> ring1

private lemma equation_zero_iff_nonsingular_zero_of_Δ_ne_zero (hΔ : W.Δ ≠ 0) :
    W.Equation 0 0 ↔ W.Nonsingular 0 0 := by
  simp only [equation_zero, nonsingular_zero, iff_self_and]
  contrapose! hΔ
  simp only [b₂, b₄, b₆, b₈, Δ, hΔ]
  ring1
```

-- snippet 69
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_polynomialX (L269-L272)
lemma map_polynomialX : (W.map f).toAffine.polynomialX = W.polynomialX.map (mapRingHom f) := by
simp only [polynomialX]
  map_simp
```

-- snippet 70
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_slope (L428-L441)
lemma map_slope [DecidableEq F] [DecidableEq K] (f : F →+* K) (x₁ x₂ y₁ y₂ : F) :
    (W.map f).toAffine.slope (f x₁) (f x₂) (f y₁) (f y₂) = f (W.slope x₁ x₂ y₁ y₂) := by
by_cases hx : x₁ = x₂
  · by_cases hy : y₁ = W.negY x₂ y₂
    · rw [slope_of_Y_eq (congr_arg f hx) <| by rw [hy, map_negY], slope_of_Y_eq hx hy, map_zero]
    · rw [slope_of_Y_ne (congr_arg f hx) <| map_negY f x₂ y₂ ▸ fun h => hy <| f.injective h,
        map_negY, slope_of_Y_ne hx hy]
      map_simp
  · rw [slope_of_X_ne fun h => hx <| f.injective h, slope_of_X_ne hx]
    map_simp

variable [Algebra R S] [Algebra R A] [Algebra S A] [IsScalarTower R S A] [Algebra R B] [Algebra S B]
  [IsScalarTower R S B] (f : A →ₐ[S] B) (x y x₁ y₁ x₂ y₂ ℓ : A)
```

-- snippet 71
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff (L155-L158)
lemma equation_iff (x y : R) : W.Equation x y ↔
    y ^ 2 + W.a₁ * x * y + W.a₃ * y = x ^ 3 + W.a₂ * x ^ 2 + W.a₄ * x + W.a₆ := by
rw [equation_iff', sub_eq_zero]
```

-- snippet 72
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.baseChange_addX (L454-L457)
lemma baseChange_addX : (W'.baseChange B).toAffine.addX (f x₁) (f x₂) (f ℓ) =
    f ((W'.baseChange A).toAffine.addX x₁ x₂ ℓ) := by
rw [← RingHom.coe_coe, ← map_addX, map_baseChange]
```

-- snippet 73
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff' (L151-L154)
lemma equation_iff' (x y : R) : W.Equation x y ↔
    y ^ 2 + W.a₁ * x * y + W.a₃ * y - (x ^ 3 + W.a₂ * x ^ 2 + W.a₄ * x + W.a₆) = 0 := by
rw [Equation, evalEval_polynomial]
```

-- snippet 74
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.Affine (L74-L77)
abbrev Affine : Type r :=
WeierstrassCurve R

/-- The conversion from a Weierstrass curve to affine coordinates. -/
```

-- snippet 75
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomialX (L178-L182)
lemma evalEval_polynomialX (x y : R) :
    W.polynomialX.evalEval x y = W.a₁ * y - (3 * x ^ 2 + 2 * W.a₂ * x + W.a₄) := by
simp only [polynomialX]
  eval_simp
```

-- snippet 76
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_addPolynomial (L409-L414)
lemma map_addPolynomial :
    (W'.map f).toAffine.addPolynomial (f x) (f y) (f ℓ) = (W'.addPolynomial x y ℓ).map f := by
rw [addPolynomial, map_polynomial, eval_map, linePolynomial, addPolynomial, ← coe_mapRingHom,
    ← eval₂_hom, linePolynomial]
  simp
```

-- snippet 77
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.C_addPolynomial_slope (L288-L293)
lemma C_addPolynomial_slope {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : C (W.addPolynomial x₁ y₁ <| W.slope x₁ x₂ y₁ y₂) =
      -(C (X - C x₁) * C (X - C x₂) * C (X - C (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂))) := by
rw [addPolynomial_slope h₁ h₂ hxy]
  simp
```

-- snippet 78
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.baseChange_negY (L446-L449)
lemma baseChange_negY :
    (W'.baseChange B).toAffine.negY (f x) (f y) = f ((W'.baseChange A).toAffine.negY x y) := by
rw [← RingHom.coe_coe, ← map_negY, map_baseChange]
```

-- snippet 79
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.Y_eq_of_X_eq (L124-L129)
lemma Y_eq_of_X_eq {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂)
    (hx : x₁ = x₂) : y₁ = y₂ ∨ y₁ = W.negY x₂ y₂ := by
rw [equation_iff] at h₁ h₂
  rw [← sub_eq_zero, ← sub_eq_zero (a := y₁), ← mul_eq_zero, negY]
  linear_combination (norm := (rw [hx]; ring1)) h₁ - h₂
```

-- snippet 80
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.map_nonsingular (L278-L285)
lemma map_nonsingular (hf : Function.Injective f) :
    (W.map f).toAffine.Nonsingular (f x) (f y) ↔ W.Nonsingular x y := by
simp only [Nonsingular, evalEval, map_equation _ _ hf, map_polynomialX, map_polynomialY,
    map_mapRingHom_evalEval, map_ne_zero_iff f hf]

variable [Algebra R S] [Algebra R A] [Algebra S A] [IsScalarTower R S A] [Algebra R B] [Algebra S B]
  [IsScalarTower R S B] (f : A →ₐ[S] B) (x y : A)
```

-- snippet 81
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.nonsingular_iff (L217-L222)
lemma nonsingular_iff (x y : R) : W.Nonsingular x y ↔ W.Equation x y ∧
    (W.a₁ * y ≠ 3 * x ^ 2 + 2 * W.a₂ * x + W.a₄ ∨ y ≠ -y - W.a₁ * x - W.a₃) := by
rw [nonsingular_iff', sub_ne_zero, ← sub_ne_zero (a := y)]
  congr! 3
  ring1
```

-- snippet 82
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_addY (L424-L427)
lemma map_addY :
    (W'.map f).toAffine.addY (f x₁) (f x₂) (f y₁) (f ℓ) = f (W'.toAffine.addY x₁ x₂ y₁ ℓ) := by
simp only [addY, map_negAddY, map_addX, map_negY]
```

-- snippet 83
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.equation_iff_nonsingular_of_Δ_ne_zero (L242-L247)
lemma equation_iff_nonsingular_of_Δ_ne_zero {x y : R} (hΔ : W.Δ ≠ 0) :
    W.Equation x y ↔ W.Nonsingular x y := by
rw [equation_iff_variableChange, nonsingular_iff_variableChange,
    equation_zero_iff_nonsingular_zero_of_Δ_ne_zero <| by
      rwa [variableChange_Δ, inv_one, Units.val_one, one_pow, one_mul]]
```

-- snippet 84
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.baseChange_negPolynomial (L442-L445)
lemma baseChange_negPolynomial : (W'.baseChange B).toAffine.negPolynomial =
    (W'.baseChange A).toAffine.negPolynomial.map (mapRingHom f) := by
rw [← map_negPolynomial, map_baseChange]
```

-- snippet 85
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.irreducible_polynomial (L122-L133)
lemma irreducible_polynomial [IsDomain R] : Irreducible W.polynomial := by
by_contra h
  rcases (monic_polynomial.not_irreducible_iff_exists_add_mul_eq_coeff natDegree_polynomial).mp h
    with ⟨f, g, h0, h1⟩
  simp only [polynomial_eq, Cubic.coeff_eq_c, Cubic.coeff_eq_d] at h0 h1
  apply_fun degree at h0 h1
  rw [Cubic.degree_of_a_ne_zero' <| neg_ne_zero.mpr <| one_ne_zero' R, degree_mul] at h0
  apply (h1.symm.le.trans Cubic.degree_of_b_eq_zero').not_gt
  rcases Nat.WithBot.add_eq_three_iff.mp h0.symm with h | h | h | h
  iterate 2 rw [degree_add_eq_right_of_degree_lt] <;> simp only [h] <;> decide
  iterate 2 rw [degree_add_eq_left_of_degree_lt] <;> simp only [h] <;> decide
```

-- snippet 86
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_linePolynomial (L405-L408)
lemma map_linePolynomial : linePolynomial (f x) (f y) (f ℓ) = (linePolynomial x y ℓ).map f := by
simp only [linePolynomial]
  map_simp
```

-- snippet 87
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_Y_eq (L174-L177)
lemma slope_of_Y_eq {x₁ x₂ y₁ y₂ : F} (hx : x₁ = x₂) (hy : y₁ = W.negY x₂ y₂) :
    W.slope x₁ x₂ y₁ y₂ = 0 := by
rw [slope, if_pos hx, if_pos hy]
```

-- snippet 88
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_polynomialX (L301-L304)
lemma baseChange_polynomialX : (W.baseChange B).toAffine.polynomialX =
    (W.baseChange A).toAffine.polynomialX.map (mapRingHom f) := by
rw [← map_polynomialX, map_baseChange]
```

-- snippet 89
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.C_addPolynomial (L215-L222)
lemma C_addPolynomial (x y ℓ : R) : C (W'.addPolynomial x y ℓ) =
    (Y - C (linePolynomial x y ℓ)) * (W'.negPolynomial - C (linePolynomial x y ℓ)) +
      W'.polynomial := by
rw [addPolynomial, linePolynomial, polynomial, negPolynomial]
  eval_simp
  C_simp
  ring1
```

-- snippet 90
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_Y_ne' (L179-L183)
lemma slope_of_Y_ne' {x₂ y₁ y₂ : F} (hy : ¬y₁ = -y₂ - W.a₁ * x₂ - W.a₃) :
    W.slope x₂ x₂ y₁ y₂ =
      (3 * x₂ ^ 2 + 2 * W.a₂ * x₂ + W.a₄ - W.a₁ * y₁) / (y₁ - (-y₁ - W.a₁ * x₂ - W.a₃)) := by
simp [slope, hy]
```

-- snippet 91
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.natDegree_polynomial (L115-L118)
lemma natDegree_polynomial [Nontrivial R] : W.polynomial.natDegree = 2 := by
rw [polynomial_eq]
  exact Cubic.natDegree_of_b_ne_zero' one_ne_zero
```

-- snippet 92
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.polynomial_eq (L98-L104)
lemma polynomial_eq : W.polynomial = Cubic.toPoly
    ⟨0, 1, Cubic.toPoly ⟨0, 0, W.a₁, W.a₃⟩, Cubic.toPoly ⟨-1, -W.a₂, -W.a₄, -W.a₆⟩⟩ := by
simp_rw [polynomial, Cubic.toPoly]
  map_simp
  simp only [C_0, C_1]
  ring1
```

-- snippet 93
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.baseChange_nonsingular (L310-L317)
lemma baseChange_nonsingular (hf : Function.Injective f) :
    (W.baseChange B).toAffine.Nonsingular (f x) (f y) ↔
      (W.baseChange A).toAffine.Nonsingular x y := by
rw [← map_nonsingular _ _ hf, AlgHom.toRingHom_eq_coe, map_baseChange, RingHom.coe_coe]

end Affine

end WeierstrassCurve
```

-- snippet 94
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addY (L256-L264)
def addY (x₁ x₂ y₁ ℓ : R) : R :=
W'.negY (W'.addX x₁ x₂ ℓ) (W'.negAddY x₁ x₂ y₁ ℓ)

section slope

variable [DecidableEq F]

-- Non-terminal simps, used to be field_simp
set_option linter.flexible false in
```

-- snippet 95
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.degree_polynomial (L110-L113)
lemma degree_polynomial [Nontrivial R] : W.polynomial.degree = 2 := by
rw [polynomial_eq]
  exact Cubic.degree_of_b_ne_zero' one_ne_zero
```

-- snippet 96
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.negAddY (L247-L254)
def negAddY (x₁ x₂ y₁ ℓ : R) : R :=
ℓ * (W'.addX x₁ x₂ ℓ - x₁) + y₁

variable (W') in
/-- The `Y`-coordinate of `(x₁, y₁) + (x₂, y₂)` for two nonsingular affine points `(x₁, y₁)` and
`(x₂, y₂)` on a Weierstrass curve `W`, where the line through them has a slope of `ℓ`.

This depends on `W`, and has argument order: `x₁`, `x₂`, `y₁`, `ℓ`. -/
```

-- snippet 97
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomialX_zero (L184-L193)
lemma evalEval_polynomialX_zero : W.polynomialX.evalEval 0 0 = -W.a₄ := by
simp only [evalEval_polynomialX, zero_add, zero_sub, mul_zero, zero_pow <| Nat.succ_ne_zero _]

variable (W) in
/-- The partial derivative `W_Y(X, Y)` with respect to `Y` of the polynomial `W(X, Y)` associated to
a Weierstrass curve `W` in affine coordinates. -/
-- TODO: define this in terms of `Polynomial.derivative`.
noncomputable def polynomialY : R[X][Y] :=
  C (C 2) * Y + C (C W.a₁ * X + C W.a₃)
```

-- snippet 98
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.slope_of_Y_ne_eq_evalEval (L194-L214)
lemma slope_of_Y_ne_eq_evalEval {x₁ x₂ y₁ y₂ : F} (hx : x₁ = x₂) (hy : y₁ ≠ W.negY x₂ y₂) :
    W.slope x₁ x₂ y₁ y₂ = -W.polynomialX.evalEval x₁ y₁ / W.polynomialY.evalEval x₁ y₁ := by
rw [slope_of_Y_ne hx hy, evalEval_polynomialX, neg_sub]
  congr 1
  rw [negY, evalEval_polynomialY]
  ring1

end slope

/-! ## Addition formulae in affine coordinates -/

variable (W') in
/-- The addition polynomial obtained by substituting the line `Y = ℓ(X - x) + y` into the polynomial
`W(X, Y)` associated to a Weierstrass curve `W`. If such a line intersects `W` at another
nonsingular affine point `(x', y')` on `W`, then the roots of this polynomial are precisely `x`,
`x'`, and the `X`-coordinate of the addition of `(x, y)` and `(x', y')`.

This depends on `W`, and has argument order: `x`, `y`, `ℓ`. -/
noncomputable def addPolynomial (x y ℓ : R) : R[X] :=
  W'.polynomial.eval <| linePolynomial x y ℓ
```

-- snippet 99
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomial (L134-L139)
lemma evalEval_polynomial (x y : R) : W.polynomial.evalEval x y =
    y ^ 2 + W.a₁ * x * y + W.a₃ * y - (x ^ 3 + W.a₂ * x ^ 2 + W.a₄ * x + W.a₆) := by
simp only [polynomial]
  eval_simp
  rw [add_mul, ← add_assoc]
```

-- snippet 100
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.map_negAddY (L419-L423)
lemma map_negAddY :
    (W'.map f).toAffine.negAddY (f x₁) (f x₂) (f y₁) (f ℓ) = f (W'.negAddY x₁ x₂ y₁ ℓ) := by
simp only [negAddY, map_addX]
  map_simp
```

-- snippet 101
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.nonsingular_add (L351-L359)
lemma nonsingular_add {x₁ x₂ y₁ y₂ : F} (h₁ : W.Nonsingular x₁ y₁) (h₂ : W.Nonsingular x₂ y₂)
    (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) :
    W.Nonsingular (W.addX x₁ x₂ <| W.slope x₁ x₂ y₁ y₂) (W.addY x₁ x₂ y₁ <| W.slope x₁ x₂ y₁ y₂) :=
(nonsingular_neg ..).mpr <| nonsingular_negAdd h₁ h₂ hxy

-- Non-terminal simp, used to be field_simp
set_option linter.flexible false in
/-- The formula `x(P₁ + P₂) = x(P₁ - P₂) - ψ(P₁)ψ(P₂) / (x(P₂) - x(P₁))²`,
where `ψ(x,y) = 2y + a₁x + a₃`. -/
```

-- snippet 102
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.toAffine (L78-L97)
abbrev toAffine (W : WeierstrassCurve R) : Affine R :=
W

variable [CommRing R] [CommRing S] [CommRing A] [CommRing B] {W : Affine R}

namespace Affine

/-! ## Weierstrass equations in affine coordinates -/

variable (W) in
/-- The polynomial `W(X, Y) := Y² + a₁XY + a₃Y - (X³ + a₂X² + a₄X + a₆)` associated to a Weierstrass
curve `W` over a ring `R` in affine coordinates.

For ease of polynomial manipulation, this is represented as a term of type `R[X][X]`, where the
inner variable represents `X` and the outer variable represents `Y`. For clarity, the alternative
notations `Y` and `R[X][Y]` are provided in the `Polynomial.Bivariate` scope to represent the outer
variable and the bivariate polynomial ring `R[X][X]` respectively. -/
noncomputable def polynomial : R[X][Y] :=
  Y ^ 2 + C (C W.a₁ * X + C W.a₃) * Y - C (X ^ 3 + C W.a₂ * X ^ 2 + C W.a₄ * X + C W.a₆)
```

-- snippet 103
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.addPolynomial_eq (L223-L236)
lemma addPolynomial_eq (x y ℓ : R) : W'.addPolynomial x y ℓ = -Cubic.toPoly
    ⟨1, -ℓ ^ 2 - W'.a₁ * ℓ + W'.a₂,
      2 * x * ℓ ^ 2 + (W'.a₁ * x - 2 * y - W'.a₃) * ℓ + (-W'.a₁ * y + W'.a₄),
      -x ^ 2 * ℓ ^ 2 + (2 * x * y + W'.a₃ * x) * ℓ - (y ^ 2 + W'.a₃ * y - W'.a₆)⟩ := by
rw [addPolynomial, linePolynomial, polynomial, Cubic.toPoly]
  eval_simp
  C_simp
  ring1

variable (W') in
/-- The `X`-coordinate of `(x₁, y₁) + (x₂, y₂)` for two nonsingular affine points `(x₁, y₁)` and
`(x₂, y₂)` on a Weierstrass curve `W`, where the line through them has a slope of `ℓ`.

This depends on `W`, and has argument order: `x₁`, `x₂`, `ℓ`. -/
```

-- snippet 104
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Basic.evalEval_polynomialY (L194-L198)
lemma evalEval_polynomialY (x y : R) : W.polynomialY.evalEval x y = 2 * y + W.a₁ * x + W.a₃ := by
simp only [polynomialY]
  eval_simp
  rw [← add_assoc]
```

-- snippet 105
```lean
-- Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Formula.equation_add_iff (L317-L321)
lemma equation_add_iff (x₁ x₂ y₁ ℓ : R) : W'.Equation (W'.addX x₁ x₂ ℓ) (W'.negAddY x₁ x₂ y₁ ℓ) ↔
    (W'.addPolynomial x₁ y₁ ℓ).eval (W'.addX x₁ x₂ ℓ) = 0 := by
rw [Equation, negAddY, addPolynomial, linePolynomial, polynomial]
  eval_simp
```
[/INST]