l3lab/ntp-mathlib huggingface dataset:
theorem X_pow_card_sub_X_natDegree_eq (hp : 1 < p) : (X ^ p - X : K'[X]).natDegree = p := by
  have h1 : (X : K'[X]).degree < (X ^ p : K'[X]).degree := by
    rw [degree_X_pow, degree_X]
    exact mod_cast hp
  

Proof state:
K : Type u_1
R : Type u_2
inst✝² : Field K
inst✝¹ : Fintype K
K' : Type u_3
inst✝ : Field K'
p n : ℕ
hp : 1 < p
h1 : degree X < degree (X ^ p)
⊢ natDegree (X ^ p - X) = p

Next tactic:
rw [natDegree_eq_of_degree_eq (degree_sub_eq_left_of_degree_lt h1), natDegree_X_pow]
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  

Proof state:
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
⊢ roots (X ^ q - X) = univ.val

Next tactic:
classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      rw [eq_univ_iff_forall]
      intro x
      rw [Multiset.mem_toFinset, mem_roots aux, IsRoot.def, eval_sub, eval_pow, eval_X,
        sub_eq_zero, pow_card]
    rw [← this, Multiset.toFinset_val, eq_comm, Multiset.dedup_eq_self]
    apply nodup_roots
    rw [separable_def]
    convert isCoprime_one_right.neg_right (R := K[X]) using 1
    rw [derivative_sub, derivative_X, derivative_X_pow, CharP.cast_card_eq_zero K, C_0,
      zero_mul, zero_sub]
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    

Proof state:
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
⊢ roots (X ^ q - X) = univ.val

Next tactic:
have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    

Proof state:
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
aux : X ^ q - X ≠ 0
⊢ roots (X ^ q - X) = univ.val

Next tactic:
have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      rw [eq_univ_iff_forall]
      intro x
      rw [Multiset.mem_toFinset, mem_roots aux, IsRoot.def, eval_sub, eval_pow, eval_X,
        sub_eq_zero, pow_card]
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      

Proof state:
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
aux : X ^ q - X ≠ 0
⊢ Multiset.toFinset (roots (X ^ q - X)) = univ

Next tactic:
rw [eq_univ_iff_forall]
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      rw [eq_univ_iff_forall]
      

Proof state:
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
aux : X ^ q - X ≠ 0
⊢ ∀ (x : K), x ∈ Multiset.toFinset (roots (X ^ q - X))

Next tactic:
intro x
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      rw [eq_univ_iff_forall]
      intro x
      

Proof state:
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
aux : X ^ q - X ≠ 0
x : K
⊢ x ∈ Multiset.toFinset (roots (X ^ q - X))

Next tactic:
rw [Multiset.mem_toFinset, mem_roots aux, IsRoot.def, eval_sub, eval_pow, eval_X,
        sub_eq_zero, pow_card]
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      rw [eq_univ_iff_forall]
      intro x
      rw [Multiset.mem_toFinset, mem_roots aux, IsRoot.def, eval_sub, eval_pow, eval_X,
        sub_eq_zero, pow_card]
    

Proof state:
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
aux : X ^ q - X ≠ 0
this : Multiset.toFinset (roots (X ^ q - X)) = univ
⊢ roots (X ^ q - X) = univ.val

Next tactic:
rw [← this, Multiset.toFinset_val, eq_comm, Multiset.dedup_eq_self]
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      rw [eq_univ_iff_forall]
      intro x
      rw [Multiset.mem_toFinset, mem_roots aux, IsRoot.def, eval_sub, eval_pow, eval_X,
        sub_eq_zero, pow_card]
    rw [← this, Multiset.toFinset_val, eq_comm, Multiset.dedup_eq_self]
    

Proof state:
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
aux : X ^ q - X ≠ 0
this : Multiset.toFinset (roots (X ^ q - X)) = univ
⊢ Multiset.Nodup (roots (X ^ q - X))

Next tactic:
apply nodup_roots
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      rw [eq_univ_iff_forall]
      intro x
      rw [Multiset.mem_toFinset, mem_roots aux, IsRoot.def, eval_sub, eval_pow, eval_X,
        sub_eq_zero, pow_card]
    rw [← this, Multiset.toFinset_val, eq_comm, Multiset.dedup_eq_self]
    apply nodup_roots
    

Proof state:
case hsep
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
aux : X ^ q - X ≠ 0
this : Multiset.toFinset (roots (X ^ q - X)) = univ
⊢ Separable (X ^ q - X)

Next tactic:
rw [separable_def]
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      rw [eq_univ_iff_forall]
      intro x
      rw [Multiset.mem_toFinset, mem_roots aux, IsRoot.def, eval_sub, eval_pow, eval_X,
        sub_eq_zero, pow_card]
    rw [← this, Multiset.toFinset_val, eq_comm, Multiset.dedup_eq_self]
    apply nodup_roots
    rw [separable_def]
    

Proof state:
case hsep
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
aux : X ^ q - X ≠ 0
this : Multiset.toFinset (roots (X ^ q - X)) = univ
⊢ IsCoprime (X ^ q - X) (derivative (X ^ q - X))

Next tactic:
convert isCoprime_one_right.neg_right (R := K[X]) using 1
theorem roots_X_pow_card_sub_X : roots (X ^ q - X : K[X]) = Finset.univ.val := by
  classical
    have aux : (X ^ q - X : K[X]) ≠ 0 := X_pow_card_sub_X_ne_zero K Fintype.one_lt_card
    have : (roots (X ^ q - X : K[X])).toFinset = Finset.univ := by
      rw [eq_univ_iff_forall]
      intro x
      rw [Multiset.mem_toFinset, mem_roots aux, IsRoot.def, eval_sub, eval_pow, eval_X,
        sub_eq_zero, pow_card]
    rw [← this, Multiset.toFinset_val, eq_comm, Multiset.dedup_eq_self]
    apply nodup_roots
    rw [separable_def]
    convert isCoprime_one_right.neg_right (R := K[X]) using 1
    

Proof state:
case h.e'_4
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
aux : X ^ q - X ≠ 0
this : Multiset.toFinset (roots (X ^ q - X)) = univ
⊢ derivative (X ^ q - X) = -1

Next tactic:
rw [derivative_sub, derivative_X, derivative_X_pow, CharP.cast_card_eq_zero K, C_0,
      zero_mul, zero_sub]
theorem frobenius_pow {p : ℕ} [Fact p.Prime] [CharP K p] {n : ℕ} (hcard : q = p ^ n) :
    frobenius K p ^ n = 1 := by
  

Proof state:
K : Type u_1
R : Type u_2
inst✝⁵ : Field K
inst✝⁴ : Fintype K
p✝ : ℕ
inst✝³ : Fact (Nat.Prime p✝)
inst✝² : Algebra (ZMod p✝) K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : CharP K p
n : ℕ
hcard : q = p ^ n
⊢ frobenius K p ^ n = 1

Next tactic:
ext x
theorem frobenius_pow {p : ℕ} [Fact p.Prime] [CharP K p] {n : ℕ} (hcard : q = p ^ n) :
    frobenius K p ^ n = 1 := by
  ext x; 

Proof state:
case a
K : Type u_1
R : Type u_2
inst✝⁵ : Field K
inst✝⁴ : Fintype K
p✝ : ℕ
inst✝³ : Fact (Nat.Prime p✝)
inst✝² : Algebra (ZMod p✝) K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : CharP K p
n : ℕ
hcard : q = p ^ n
x : K
⊢ (frobenius K p ^ n) x = 1 x

Next tactic:
conv_rhs => rw [RingHom.one_def, RingHom.id_apply, ← pow_card x, hcard]
theorem frobenius_pow {p : ℕ} [Fact p.Prime] [CharP K p] {n : ℕ} (hcard : q = p ^ n) :
    frobenius K p ^ n = 1 := by
  ext x; conv_rhs => 

Proof state:
K : Type u_1
R : Type u_2
inst✝⁵ : Field K
inst✝⁴ : Fintype K
p✝ : ℕ
inst✝³ : Fact (Nat.Prime p✝)
inst✝² : Algebra (ZMod p✝) K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : CharP K p
n : ℕ
hcard : q = p ^ n
x : K
| 1 x

Next tactic:
rw [RingHom.one_def, RingHom.id_apply, ← pow_card x, hcard]
theorem frobenius_pow {p : ℕ} [Fact p.Prime] [CharP K p] {n : ℕ} (hcard : q = p ^ n) :
    frobenius K p ^ n = 1 := by
  ext x; conv_rhs => rw [RingHom.one_def, RingHom.id_apply, ← pow_card x, hcard]
  

Proof state:
case a
K : Type u_1
R : Type u_2
inst✝⁵ : Field K
inst✝⁴ : Fintype K
p✝ : ℕ
inst✝³ : Fact (Nat.Prime p✝)
inst✝² : Algebra (ZMod p✝) K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : CharP K p
n : ℕ
hcard : q = p ^ n
x : K
⊢ (frobenius K p ^ n) x = x ^ p ^ n

Next tactic:
clear hcard
theorem frobenius_pow {p : ℕ} [Fact p.Prime] [CharP K p] {n : ℕ} (hcard : q = p ^ n) :
    frobenius K p ^ n = 1 := by
  ext x; conv_rhs => rw [RingHom.one_def, RingHom.id_apply, ← pow_card x, hcard]
  clear hcard
  

Proof state:
case a
K : Type u_1
R : Type u_2
inst✝⁵ : Field K
inst✝⁴ : Fintype K
p✝ : ℕ
inst✝³ : Fact (Nat.Prime p✝)
inst✝² : Algebra (ZMod p✝) K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : CharP K p
n : ℕ
x : K
⊢ (frobenius K p ^ n) x = x ^ p ^ n

Next tactic:
induction' n with n hn
theorem frobenius_pow {p : ℕ} [Fact p.Prime] [CharP K p] {n : ℕ} (hcard : q = p ^ n) :
    frobenius K p ^ n = 1 := by
  ext x; conv_rhs => rw [RingHom.one_def, RingHom.id_apply, ← pow_card x, hcard]
  clear hcard
  induction' n with n hn
  · 

Proof state:
case a.zero
K : Type u_1
R : Type u_2
inst✝⁵ : Field K
inst✝⁴ : Fintype K
p✝ : ℕ
inst✝³ : Fact (Nat.Prime p✝)
inst✝² : Algebra (ZMod p✝) K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : CharP K p
x : K
⊢ (frobenius K p ^ Nat.zero) x = x ^ p ^ Nat.zero

Next tactic:
simp
theorem frobenius_pow {p : ℕ} [Fact p.Prime] [CharP K p] {n : ℕ} (hcard : q = p ^ n) :
    frobenius K p ^ n = 1 := by
  ext x; conv_rhs => rw [RingHom.one_def, RingHom.id_apply, ← pow_card x, hcard]
  clear hcard
  induction' n with n hn
  · simp
  · 

Proof state:
case a.succ
K : Type u_1
R : Type u_2
inst✝⁵ : Field K
inst✝⁴ : Fintype K
p✝ : ℕ
inst✝³ : Fact (Nat.Prime p✝)
inst✝² : Algebra (ZMod p✝) K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : CharP K p
x : K
n : ℕ
hn : (frobenius K p ^ n) x = x ^ p ^ n
⊢ (frobenius K p ^ Nat.succ n) x = x ^ p ^ Nat.succ n

Next tactic:
rw [pow_succ, pow_succ', pow_mul, RingHom.mul_def, RingHom.comp_apply, frobenius_def, hn]
theorem expand_card (f : K[X]) : expand K q f = f ^ q := by
  

Proof state:
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Algebra (ZMod p) K
f : K[X]
⊢ (expand K q) f = f ^ q

Next tactic:
cases' CharP.exists K with p hp
theorem expand_card (f : K[X]) : expand K q f = f ^ q := by
  cases' CharP.exists K with p hp
  

Proof state:
case intro
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p✝ : ℕ
inst✝¹ : Fact (Nat.Prime p✝)
inst✝ : Algebra (ZMod p✝) K
f : K[X]
p : ℕ
hp : CharP K p
⊢ (expand K q) f = f ^ q

Next tactic:
letI := hp
theorem expand_card (f : K[X]) : expand K q f = f ^ q := by
  cases' CharP.exists K with p hp
  letI := hp
  

Proof state:
case intro
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p✝ : ℕ
inst✝¹ : Fact (Nat.Prime p✝)
inst✝ : Algebra (ZMod p✝) K
f : K[X]
p : ℕ
hp : CharP K p
this : CharP K p := hp
⊢ (expand K q) f = f ^ q

Next tactic:
rcases FiniteField.card K p with ⟨⟨n, npos⟩, ⟨hp, hn⟩⟩
theorem expand_card (f : K[X]) : expand K q f = f ^ q := by
  cases' CharP.exists K with p hp
  letI := hp
  rcases FiniteField.card K p with ⟨⟨n, npos⟩, ⟨hp, hn⟩⟩
  

Proof state:
case intro.intro.mk.intro
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p✝ : ℕ
inst✝¹ : Fact (Nat.Prime p✝)
inst✝ : Algebra (ZMod p✝) K
f : K[X]
p : ℕ
hp✝ : CharP K p
this : CharP K p := hp✝
n : ℕ
npos : 0 < n
hp : Nat.Prime p
hn : q = p ^ ↑{ val := n, property := npos }
⊢ (expand K q) f = f ^ q

Next tactic:
haveI : Fact p.Prime := ⟨hp⟩
theorem expand_card (f : K[X]) : expand K q f = f ^ q := by
  cases' CharP.exists K with p hp
  letI := hp
  rcases FiniteField.card K p with ⟨⟨n, npos⟩, ⟨hp, hn⟩⟩
  haveI : Fact p.Prime := ⟨hp⟩
  

Proof state:
case intro.intro.mk.intro
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p✝ : ℕ
inst✝¹ : Fact (Nat.Prime p✝)
inst✝ : Algebra (ZMod p✝) K
f : K[X]
p : ℕ
hp✝ : CharP K p
this✝ : CharP K p := hp✝
n : ℕ
npos : 0 < n
hp : Nat.Prime p
hn : q = p ^ ↑{ val := n, property := npos }
this : Fact (Nat.Prime p)
⊢ (expand K q) f = f ^ q

Next tactic:
dsimp at hn
theorem expand_card (f : K[X]) : expand K q f = f ^ q := by
  cases' CharP.exists K with p hp
  letI := hp
  rcases FiniteField.card K p with ⟨⟨n, npos⟩, ⟨hp, hn⟩⟩
  haveI : Fact p.Prime := ⟨hp⟩
  dsimp at hn
  

Proof state:
case intro.intro.mk.intro
K : Type u_1
R : Type u_2
inst✝³ : Field K
inst✝² : Fintype K
p✝ : ℕ
inst✝¹ : Fact (Nat.Prime p✝)
inst✝ : Algebra (ZMod p✝) K
f : K[X]
p : ℕ
hp✝ : CharP K p
this✝ : CharP K p := hp✝
n : ℕ
npos : 0 < n
hp : Nat.Prime p
hn : q = p ^ n
this : Fact (Nat.Prime p)
⊢ (expand K q) f = f ^ q

Next tactic:
rw [hn, ← map_expand_pow_char, frobenius_pow hn, RingHom.one_def, map_id]
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  

Proof state:
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
⊢ ∃ a b, a ^ 2 + b ^ 2 = x

Next tactic:
cases' hp.1.eq_two_or_odd with hp2 hp_odd
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · 

Proof state:
case inl
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
hp2 : p = 2
⊢ ∃ a b, a ^ 2 + b ^ 2 = x

Next tactic:
subst p
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    

Proof state:
case inl
K : Type u_1
R : Type u_2
hp : Fact (Nat.Prime 2)
x : ZMod 2
⊢ ∃ a b, a ^ 2 + b ^ 2 = x

Next tactic:
change Fin 2 at x
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    

Proof state:
case inl
K : Type u_1
R : Type u_2
hp : Fact (Nat.Prime 2)
x : Fin 2
⊢ ∃ a b, a ^ 2 + b ^ 2 = x

Next tactic:
fin_cases x
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · 

Proof state:
case inl.head
K : Type u_1
R : Type u_2
hp : Fact (Nat.Prime 2)
⊢ ∃ a b, a ^ 2 + b ^ 2 = { val := 0, isLt := (_ : 0 < 2) }

Next tactic:
use 0
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; 

Proof state:
case h
K : Type u_1
R : Type u_2
hp : Fact (Nat.Prime 2)
⊢ ∃ b, 0 ^ 2 + b ^ 2 = { val := 0, isLt := (_ : 0 < 2) }

Next tactic:
simp
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · 

Proof state:
case inl.tail.head
K : Type u_1
R : Type u_2
hp : Fact (Nat.Prime 2)
⊢ ∃ a b, a ^ 2 + b ^ 2 = { val := 1, isLt := (_ : (fun a => a < 2) 1) }

Next tactic:
use 0, 1
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · use 0, 1; 

Proof state:
case h
K : Type u_1
R : Type u_2
hp : Fact (Nat.Prime 2)
⊢ 0 ^ 2 + 1 ^ 2 = { val := 1, isLt := (_ : (fun a => a < 2) 1) }

Next tactic:
simp
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · use 0, 1; simp
  

Proof state:
case inr
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
hp_odd : p % 2 = 1
⊢ ∃ a b, a ^ 2 + b ^ 2 = x

Next tactic:
let f : (ZMod p)[X] := X ^ 2
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · use 0, 1; simp
  let f : (ZMod p)[X] := X ^ 2
  

Proof state:
case inr
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
hp_odd : p % 2 = 1
f : (ZMod p)[X] := X ^ 2
⊢ ∃ a b, a ^ 2 + b ^ 2 = x

Next tactic:
let g : (ZMod p)[X] := X ^ 2 - C x
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · use 0, 1; simp
  let f : (ZMod p)[X] := X ^ 2
  let g : (ZMod p)[X] := X ^ 2 - C x
  

Proof state:
case inr
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
hp_odd : p % 2 = 1
f : (ZMod p)[X] := X ^ 2
g : (ZMod p)[X] := X ^ 2 - C x
⊢ ∃ a b, a ^ 2 + b ^ 2 = x

Next tactic:
obtain ⟨a, b, hab⟩ : ∃ a b, f.eval a + g.eval b = 0 :=
    @exists_root_sum_quadratic _ _ _ _ f g (degree_X_pow 2) (degree_X_pow_sub_C (by decide) _)
      (by rw [ZMod.card, hp_odd])
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · use 0, 1; simp
  let f : (ZMod p)[X] := X ^ 2
  let g : (ZMod p)[X] := X ^ 2 - C x
  obtain ⟨a, b, hab⟩ : ∃ a b, f.eval a + g.eval b = 0 :=
    @exists_root_sum_quadratic _ _ _ _ f g (degree_X_pow 2) (degree_X_pow_sub_C (by 

Proof state:
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
hp_odd : p % 2 = 1
f : (ZMod p)[X] := X ^ 2
g : (ZMod p)[X] := X ^ 2 - C x
⊢ 0 < 2

Next tactic:
decide
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · use 0, 1; simp
  let f : (ZMod p)[X] := X ^ 2
  let g : (ZMod p)[X] := X ^ 2 - C x
  obtain ⟨a, b, hab⟩ : ∃ a b, f.eval a + g.eval b = 0 :=
    @exists_root_sum_quadratic _ _ _ _ f g (degree_X_pow 2) (degree_X_pow_sub_C (by decide) _)
      (by 

Proof state:
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
hp_odd : p % 2 = 1
f : (ZMod p)[X] := X ^ 2
g : (ZMod p)[X] := X ^ 2 - C x
⊢ Fintype.card (ZMod p) % 2 = 1

Next tactic:
rw [ZMod.card, hp_odd]
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · use 0, 1; simp
  let f : (ZMod p)[X] := X ^ 2
  let g : (ZMod p)[X] := X ^ 2 - C x
  obtain ⟨a, b, hab⟩ : ∃ a b, f.eval a + g.eval b = 0 :=
    @exists_root_sum_quadratic _ _ _ _ f g (degree_X_pow 2) (degree_X_pow_sub_C (by decide) _)
      (by rw [ZMod.card, hp_odd])
  

Proof state:
case inr.intro.intro
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
hp_odd : p % 2 = 1
f : (ZMod p)[X] := X ^ 2
g : (ZMod p)[X] := X ^ 2 - C x
a b : ZMod p
hab : eval a f + eval b g = 0
⊢ ∃ a b, a ^ 2 + b ^ 2 = x

Next tactic:
refine' ⟨a, b, _⟩
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · use 0, 1; simp
  let f : (ZMod p)[X] := X ^ 2
  let g : (ZMod p)[X] := X ^ 2 - C x
  obtain ⟨a, b, hab⟩ : ∃ a b, f.eval a + g.eval b = 0 :=
    @exists_root_sum_quadratic _ _ _ _ f g (degree_X_pow 2) (degree_X_pow_sub_C (by decide) _)
      (by rw [ZMod.card, hp_odd])
  refine' ⟨a, b, _⟩
  

Proof state:
case inr.intro.intro
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
hp_odd : p % 2 = 1
f : (ZMod p)[X] := X ^ 2
g : (ZMod p)[X] := X ^ 2 - C x
a b : ZMod p
hab : eval a f + eval b g = 0
⊢ a ^ 2 + b ^ 2 = x

Next tactic:
rw [← sub_eq_zero]
theorem sq_add_sq (p : ℕ) [hp : Fact p.Prime] (x : ZMod p) : ∃ a b : ZMod p, a ^ 2 + b ^ 2 = x := by
  cases' hp.1.eq_two_or_odd with hp2 hp_odd
  · subst p
    change Fin 2 at x
    fin_cases x
    · use 0; simp
    · use 0, 1; simp
  let f : (ZMod p)[X] := X ^ 2
  let g : (ZMod p)[X] := X ^ 2 - C x
  obtain ⟨a, b, hab⟩ : ∃ a b, f.eval a + g.eval b = 0 :=
    @exists_root_sum_quadratic _ _ _ _ f g (degree_X_pow 2) (degree_X_pow_sub_C (by decide) _)
      (by rw [ZMod.card, hp_odd])
  refine' ⟨a, b, _⟩
  rw [← sub_eq_zero]
  

Proof state:
case inr.intro.intro
K : Type u_1
R : Type u_2
p : ℕ
hp : Fact (Nat.Prime p)
x : ZMod p
hp_odd : p % 2 = 1
f : (ZMod p)[X] := X ^ 2
g : (ZMod p)[X] := X ^ 2 - C x
a b : ZMod p
hab : eval a f + eval b g = 0
⊢ a ^ 2 + b ^ 2 - x = 0

Next tactic:
simpa only [eval_C, eval_X, eval_pow, eval_sub, ← add_sub_assoc] using hab
/-- If `p` is a prime natural number and `x` is an integer number, then there exist natural numbers
`a ≤ p / 2` and `b ≤ p / 2` such that `a ^ 2 + b ^ 2 ≡ x [ZMOD p]`. This is a version of
`ZMod.sq_add_sq` with estimates on `a` and `b`. -/
theorem Nat.sq_add_sq_zmodEq (p : ℕ) [Fact p.Prime] (x : ℤ) :
    ∃ a b : ℕ, a ≤ p / 2 ∧ b ≤ p / 2 ∧ (a : ℤ) ^ 2 + (b : ℤ) ^ 2 ≡ x [ZMOD p] := by
  

Proof state:
K : Type u_1
R : Type u_2
p : ℕ
inst✝ : Fact (Prime p)
x : ℤ
⊢ ∃ a b, a ≤ p / 2 ∧ b ≤ p / 2 ∧ ↑a ^ 2 + ↑b ^ 2 ≡ x [ZMOD ↑p]

Next tactic:
rcases ZMod.sq_add_sq p x with ⟨a, b, hx⟩
/-- If `p` is a prime natural number and `x` is an integer number, then there exist natural numbers
`a ≤ p / 2` and `b ≤ p / 2` such that `a ^ 2 + b ^ 2 ≡ x [ZMOD p]`. This is a version of
`ZMod.sq_add_sq` with estimates on `a` and `b`. -/
theorem Nat.sq_add_sq_zmodEq (p : ℕ) [Fact p.Prime] (x : ℤ) :
    ∃ a b : ℕ, a ≤ p / 2 ∧ b ≤ p / 2 ∧ (a : ℤ) ^ 2 + (b : ℤ) ^ 2 ≡ x [ZMOD p] := by
  rcases ZMod.sq_add_sq p x with ⟨a, b, hx⟩
  

Proof state:
case intro.intro
K : Type u_1
R : Type u_2
p : ℕ
inst✝ : Fact (Prime p)
x : ℤ
a b : ZMod p
hx : a ^ 2 + b ^ 2 = ↑x
⊢ ∃ a b, a ≤ p / 2 ∧ b ≤ p / 2 ∧ ↑a ^ 2 + ↑b ^ 2 ≡ x [ZMOD ↑p]

Next tactic:
refine ⟨a.valMinAbs.natAbs, b.valMinAbs.natAbs, ZMod.natAbs_valMinAbs_le _,
    ZMod.natAbs_valMinAbs_le _, ?_⟩