l3lab/ntp-mathlib huggingface dataset:
private theorem gauss_lemma_aux₁ (p : ℕ) [Fact p.Prime] {a : ℤ}
    (hap : (a : ZMod p) ≠ 0) : (a ^ (p / 2) * (p / 2)! : ZMod p) =
    (-1 : ZMod p) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      ¬(a * x : ZMod p).val ≤ p / 2).card * (p / 2)! :=
  calc
    (a ^ (p / 2) * (p / 2)! : ZMod p) = ∏ x in Ico 1 (p / 2).succ, a * x := by
      rw [prod_mul_distrib, ← prod_natCast, prod_Ico_id_eq_factorial, prod_const, card_Ico,
        Nat.add_one_sub_one]; simp
    _ = ∏ x in Ico 1 (p / 2).succ, ↑((a * x : ZMod p).val) := by simp
    _ = ∏ x in Ico 1 (p / 2).succ, (if (a * x : ZMod p).val ≤ p / 2 then (1 : ZMod p) else -1) *
        (a * x : ZMod p).valMinAbs.natAbs :=
      (prod_congr rfl fun _ _ => by
        simp only [nat_cast_natAbs_valMinAbs]
        split_ifs <;> simp)
    _ = (-1 : ZMod p) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
        ¬(a * x : ZMod p).val ≤ p / 2).card * ∏ x in Ico 1 (p / 2).succ,
          ↑((a * x : ZMod p).valMinAbs.natAbs) := by
      have :
          (∏ x in Ico 1 (p / 2).succ, if (a * x : ZMod p).val ≤ p / 2 then (1 : ZMod p) else -1) =
          ∏ x in (Ico 1 (p / 2).succ).filter fun x : ℕ => ¬(a * x : ZMod p).val ≤ p / 2, -1 :=
        prod_bij_ne_one (fun x _ _ => x)
          (fun x => by split_ifs <;> (dsimp; simp_all))
          (fun _ _ _ _ _ _ => id) (fun b h _ => ⟨b, by simp_all [-not_le]⟩)
          (by intros; split_ifs at * <;> 

Proof state:
case pos
p : ℕ
inst✝ : Fact (Nat.Prime p)
a : ℤ
hap : ↑a ≠ 0
a✝ : ℕ
h₁✝ : a✝ ∈ Ico 1 (succ (p / 2))
h✝ : val (↑a * ↑a✝) ≤ p / 2
h₂✝ : 1 ≠ 1
⊢ 1 = -1

Next tactic:
simp_all
private theorem gauss_lemma_aux₁ (p : ℕ) [Fact p.Prime] {a : ℤ}
    (hap : (a : ZMod p) ≠ 0) : (a ^ (p / 2) * (p / 2)! : ZMod p) =
    (-1 : ZMod p) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      ¬(a * x : ZMod p).val ≤ p / 2).card * (p / 2)! :=
  calc
    (a ^ (p / 2) * (p / 2)! : ZMod p) = ∏ x in Ico 1 (p / 2).succ, a * x := by
      rw [prod_mul_distrib, ← prod_natCast, prod_Ico_id_eq_factorial, prod_const, card_Ico,
        Nat.add_one_sub_one]; simp
    _ = ∏ x in Ico 1 (p / 2).succ, ↑((a * x : ZMod p).val) := by simp
    _ = ∏ x in Ico 1 (p / 2).succ, (if (a * x : ZMod p).val ≤ p / 2 then (1 : ZMod p) else -1) *
        (a * x : ZMod p).valMinAbs.natAbs :=
      (prod_congr rfl fun _ _ => by
        simp only [nat_cast_natAbs_valMinAbs]
        split_ifs <;> simp)
    _ = (-1 : ZMod p) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
        ¬(a * x : ZMod p).val ≤ p / 2).card * ∏ x in Ico 1 (p / 2).succ,
          ↑((a * x : ZMod p).valMinAbs.natAbs) := by
      have :
          (∏ x in Ico 1 (p / 2).succ, if (a * x : ZMod p).val ≤ p / 2 then (1 : ZMod p) else -1) =
          ∏ x in (Ico 1 (p / 2).succ).filter fun x : ℕ => ¬(a * x : ZMod p).val ≤ p / 2, -1 :=
        prod_bij_ne_one (fun x _ _ => x)
          (fun x => by split_ifs <;> (dsimp; simp_all))
          (fun _ _ _ _ _ _ => id) (fun b h _ => ⟨b, by simp_all [-not_le]⟩)
          (by intros; split_ifs at * <;> 

Proof state:
case neg
p : ℕ
inst✝ : Fact (Nat.Prime p)
a : ℤ
hap : ↑a ≠ 0
a✝ : ℕ
h₁✝ : a✝ ∈ Ico 1 (succ (p / 2))
h✝ : ¬val (↑a * ↑a✝) ≤ p / 2
h₂✝ : -1 ≠ 1
⊢ -1 = -1

Next tactic:
simp_all
private theorem gauss_lemma_aux₁ (p : ℕ) [Fact p.Prime] {a : ℤ}
    (hap : (a : ZMod p) ≠ 0) : (a ^ (p / 2) * (p / 2)! : ZMod p) =
    (-1 : ZMod p) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      ¬(a * x : ZMod p).val ≤ p / 2).card * (p / 2)! :=
  calc
    (a ^ (p / 2) * (p / 2)! : ZMod p) = ∏ x in Ico 1 (p / 2).succ, a * x := by
      rw [prod_mul_distrib, ← prod_natCast, prod_Ico_id_eq_factorial, prod_const, card_Ico,
        Nat.add_one_sub_one]; simp
    _ = ∏ x in Ico 1 (p / 2).succ, ↑((a * x : ZMod p).val) := by simp
    _ = ∏ x in Ico 1 (p / 2).succ, (if (a * x : ZMod p).val ≤ p / 2 then (1 : ZMod p) else -1) *
        (a * x : ZMod p).valMinAbs.natAbs :=
      (prod_congr rfl fun _ _ => by
        simp only [nat_cast_natAbs_valMinAbs]
        split_ifs <;> simp)
    _ = (-1 : ZMod p) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
        ¬(a * x : ZMod p).val ≤ p / 2).card * ∏ x in Ico 1 (p / 2).succ,
          ↑((a * x : ZMod p).valMinAbs.natAbs) := by
      have :
          (∏ x in Ico 1 (p / 2).succ, if (a * x : ZMod p).val ≤ p / 2 then (1 : ZMod p) else -1) =
          ∏ x in (Ico 1 (p / 2).succ).filter fun x : ℕ => ¬(a * x : ZMod p).val ≤ p / 2, -1 :=
        prod_bij_ne_one (fun x _ _ => x)
          (fun x => by split_ifs <;> (dsimp; simp_all))
          (fun _ _ _ _ _ _ => id) (fun b h _ => ⟨b, by simp_all [-not_le]⟩)
          (by intros; split_ifs at * <;> simp_all)
      

Proof state:
p : ℕ
inst✝ : Fact (Nat.Prime p)
a : ℤ
hap : ↑a ≠ 0
this :
  (∏ x in Ico 1 (succ (p / 2)), if val (↑a * ↑x) ≤ p / 2 then 1 else -1) =
    ∏ x in filter (fun x => ¬val (↑a * ↑x) ≤ p / 2) (Ico 1 (succ (p / 2))), -1
⊢ ∏ x in Ico 1 (succ (p / 2)), (if val (↑a * ↑x) ≤ p / 2 then 1 else -1) * ↑(Int.natAbs (valMinAbs (↑a * ↑x))) =
    (-1) ^ Finset.card (filter (fun x => ¬val (↑a * ↑x) ≤ p / 2) (Ico 1 (succ (p / 2)))) *
      ∏ x in Ico 1 (succ (p / 2)), ↑(Int.natAbs (valMinAbs (↑a * ↑x)))

Next tactic:
rw [prod_mul_distrib, this, prod_const]
private theorem gauss_lemma_aux₁ (p : ℕ) [Fact p.Prime] {a : ℤ}
    (hap : (a : ZMod p) ≠ 0) : (a ^ (p / 2) * (p / 2)! : ZMod p) =
    (-1 : ZMod p) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      ¬(a * x : ZMod p).val ≤ p / 2).card * (p / 2)! :=
  calc
    (a ^ (p / 2) * (p / 2)! : ZMod p) = ∏ x in Ico 1 (p / 2).succ, a * x := by
      rw [prod_mul_distrib, ← prod_natCast, prod_Ico_id_eq_factorial, prod_const, card_Ico,
        Nat.add_one_sub_one]; simp
    _ = ∏ x in Ico 1 (p / 2).succ, ↑((a * x : ZMod p).val) := by simp
    _ = ∏ x in Ico 1 (p / 2).succ, (if (a * x : ZMod p).val ≤ p / 2 then (1 : ZMod p) else -1) *
        (a * x : ZMod p).valMinAbs.natAbs :=
      (prod_congr rfl fun _ _ => by
        simp only [nat_cast_natAbs_valMinAbs]
        split_ifs <;> simp)
    _ = (-1 : ZMod p) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
        ¬(a * x : ZMod p).val ≤ p / 2).card * ∏ x in Ico 1 (p / 2).succ,
          ↑((a * x : ZMod p).valMinAbs.natAbs) := by
      have :
          (∏ x in Ico 1 (p / 2).succ, if (a * x : ZMod p).val ≤ p / 2 then (1 : ZMod p) else -1) =
          ∏ x in (Ico 1 (p / 2).succ).filter fun x : ℕ => ¬(a * x : ZMod p).val ≤ p / 2, -1 :=
        prod_bij_ne_one (fun x _ _ => x)
          (fun x => by split_ifs <;> (dsimp; simp_all))
          (fun _ _ _ _ _ _ => id) (fun b h _ => ⟨b, by simp_all [-not_le]⟩)
          (by intros; split_ifs at * <;> simp_all)
      rw [prod_mul_distrib, this, prod_const]
    _ = (-1 : ZMod p) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
        ¬(a * x : ZMod p).val ≤ p / 2).card * (p / 2)! := by
      

Proof state:
p : ℕ
inst✝ : Fact (Nat.Prime p)
a : ℤ
hap : ↑a ≠ 0
⊢ (-1) ^ Finset.card (filter (fun x => ¬val (↑a * ↑x) ≤ p / 2) (Ico 1 (succ (p / 2)))) *
      ∏ x in Ico 1 (succ (p / 2)), ↑(Int.natAbs (valMinAbs (↑a * ↑x))) =
    (-1) ^ Finset.card (filter (fun x => ¬val (↑a * ↑x) ≤ p / 2) (Ico 1 (succ (p / 2)))) * ↑(p / 2)!

Next tactic:
rw [← prod_natCast, Finset.prod_eq_multiset_prod,
        Ico_map_valMinAbs_natAbs_eq_Ico_map_id p a hap, ← Finset.prod_eq_multiset_prod,
        prod_Ico_id_eq_factorial]
theorem gauss_lemma_aux (p : ℕ) [hp : Fact p.Prime] {a : ℤ}
    (hap : (a : ZMod p) ≠ 0) : (↑a ^ (p / 2) : ZMod p) =
    ((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card :) :=
  (mul_left_inj' (show ((p / 2)! : ZMod p) ≠ 0 by
    

Proof state:
p : ℕ
hp : Fact (Nat.Prime p)
a : ℤ
hap : ↑a ≠ 0
⊢ ↑(p / 2)! ≠ 0

Next tactic:
rw [Ne.def, CharP.cast_eq_zero_iff (ZMod p) p, hp.1.dvd_factorial, not_le]
theorem gauss_lemma_aux (p : ℕ) [hp : Fact p.Prime] {a : ℤ}
    (hap : (a : ZMod p) ≠ 0) : (↑a ^ (p / 2) : ZMod p) =
    ((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card :) :=
  (mul_left_inj' (show ((p / 2)! : ZMod p) ≠ 0 by
    rw [Ne.def, CharP.cast_eq_zero_iff (ZMod p) p, hp.1.dvd_factorial, not_le]
    

Proof state:
p : ℕ
hp : Fact (Nat.Prime p)
a : ℤ
hap : ↑a ≠ 0
⊢ p / 2 < p

Next tactic:
exact Nat.div_lt_self hp.1.pos (by decide)
theorem gauss_lemma_aux (p : ℕ) [hp : Fact p.Prime] {a : ℤ}
    (hap : (a : ZMod p) ≠ 0) : (↑a ^ (p / 2) : ZMod p) =
    ((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card :) :=
  (mul_left_inj' (show ((p / 2)! : ZMod p) ≠ 0 by
    rw [Ne.def, CharP.cast_eq_zero_iff (ZMod p) p, hp.1.dvd_factorial, not_le]
    exact Nat.div_lt_self hp.1.pos (by 

Proof state:
p : ℕ
hp : Fact (Nat.Prime p)
a : ℤ
hap : ↑a ≠ 0
⊢ 1 < 2

Next tactic:
decide
theorem gauss_lemma_aux (p : ℕ) [hp : Fact p.Prime] {a : ℤ}
    (hap : (a : ZMod p) ≠ 0) : (↑a ^ (p / 2) : ZMod p) =
    ((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card :) :=
  (mul_left_inj' (show ((p / 2)! : ZMod p) ≠ 0 by
    rw [Ne.def, CharP.cast_eq_zero_iff (ZMod p) p, hp.1.dvd_factorial, not_le]
    exact Nat.div_lt_self hp.1.pos (by decide))).1 <| by
      

Proof state:
p : ℕ
hp : Fact (Nat.Prime p)
a : ℤ
hap : ↑a ≠ 0
⊢ ↑a ^ (p / 2) * ↑(p / 2)! =
    ↑((-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))) * ↑(p / 2)!

Next tactic:
simpa using gauss_lemma_aux₁ p hap
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  

Proof state:
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
hp : p ≠ 2
ha0 : ↑a ≠ 0
⊢ legendreSym p a = (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))

Next tactic:
replace hp : Odd p := h.out.odd_of_ne_two hp
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  replace hp : Odd p := h.out.odd_of_ne_two hp
  

Proof state:
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
ha0 : ↑a ≠ 0
hp : Odd p
⊢ legendreSym p a = (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))

Next tactic:
have : (legendreSym p a : ZMod p) = (((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card : ℤ) : ZMod p) := by
    rw [legendreSym.eq_pow, gauss_lemma_aux p ha0]
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  replace hp : Odd p := h.out.odd_of_ne_two hp
  have : (legendreSym p a : ZMod p) = (((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card : ℤ) : ZMod p) := by
    

Proof state:
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
ha0 : ↑a ≠ 0
hp : Odd p
⊢ ↑(legendreSym p a) = ↑((-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))))

Next tactic:
rw [legendreSym.eq_pow, gauss_lemma_aux p ha0]
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  replace hp : Odd p := h.out.odd_of_ne_two hp
  have : (legendreSym p a : ZMod p) = (((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card : ℤ) : ZMod p) := by
    rw [legendreSym.eq_pow, gauss_lemma_aux p ha0]
  

Proof state:
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
ha0 : ↑a ≠ 0
hp : Odd p
this : ↑(legendreSym p a) = ↑((-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))))
⊢ legendreSym p a = (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))

Next tactic:
cases legendreSym.eq_one_or_neg_one p ha0
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  replace hp : Odd p := h.out.odd_of_ne_two hp
  have : (legendreSym p a : ZMod p) = (((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card : ℤ) : ZMod p) := by
    rw [legendreSym.eq_pow, gauss_lemma_aux p ha0]
  cases legendreSym.eq_one_or_neg_one p ha0 <;>
  

Proof state:
case inl
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
ha0 : ↑a ≠ 0
hp : Odd p
this : ↑(legendreSym p a) = ↑((-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))))
h✝ : legendreSym p a = 1
⊢ legendreSym p a = (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))

Next tactic:
cases neg_one_pow_eq_or ℤ
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  replace hp : Odd p := h.out.odd_of_ne_two hp
  have : (legendreSym p a : ZMod p) = (((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card : ℤ) : ZMod p) := by
    rw [legendreSym.eq_pow, gauss_lemma_aux p ha0]
  cases legendreSym.eq_one_or_neg_one p ha0 <;>
  

Proof state:
case inr
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
ha0 : ↑a ≠ 0
hp : Odd p
this : ↑(legendreSym p a) = ↑((-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))))
h✝ : legendreSym p a = -1
⊢ legendreSym p a = (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))

Next tactic:
cases neg_one_pow_eq_or ℤ
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  replace hp : Odd p := h.out.odd_of_ne_two hp
  have : (legendreSym p a : ZMod p) = (((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card : ℤ) : ZMod p) := by
    rw [legendreSym.eq_pow, gauss_lemma_aux p ha0]
  cases legendreSym.eq_one_or_neg_one p ha0 <;>
  cases neg_one_pow_eq_or ℤ
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card <;>
  

Proof state:
case inl.inl
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
ha0 : ↑a ≠ 0
hp : Odd p
this : ↑(legendreSym p a) = ↑((-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))))
h✝¹ : legendreSym p a = 1
h✝ : (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))) = 1
⊢ legendreSym p a = (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))

Next tactic:
simp_all [ne_neg_self hp one_ne_zero, (ne_neg_self hp one_ne_zero).symm]
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  replace hp : Odd p := h.out.odd_of_ne_two hp
  have : (legendreSym p a : ZMod p) = (((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card : ℤ) : ZMod p) := by
    rw [legendreSym.eq_pow, gauss_lemma_aux p ha0]
  cases legendreSym.eq_one_or_neg_one p ha0 <;>
  cases neg_one_pow_eq_or ℤ
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card <;>
  

Proof state:
case inl.inr
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
ha0 : ↑a ≠ 0
hp : Odd p
this : ↑(legendreSym p a) = ↑((-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))))
h✝¹ : legendreSym p a = 1
h✝ : (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))) = -1
⊢ legendreSym p a = (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))

Next tactic:
simp_all [ne_neg_self hp one_ne_zero, (ne_neg_self hp one_ne_zero).symm]
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  replace hp : Odd p := h.out.odd_of_ne_two hp
  have : (legendreSym p a : ZMod p) = (((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card : ℤ) : ZMod p) := by
    rw [legendreSym.eq_pow, gauss_lemma_aux p ha0]
  cases legendreSym.eq_one_or_neg_one p ha0 <;>
  cases neg_one_pow_eq_or ℤ
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card <;>
  

Proof state:
case inr.inl
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
ha0 : ↑a ≠ 0
hp : Odd p
this : ↑(legendreSym p a) = ↑((-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))))
h✝¹ : legendreSym p a = -1
h✝ : (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))) = 1
⊢ legendreSym p a = (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))

Next tactic:
simp_all [ne_neg_self hp one_ne_zero, (ne_neg_self hp one_ne_zero).symm]
/-- **Gauss' lemma**. The Legendre symbol can be computed by considering the number of naturals less
  than `p/2` such that `(a * x) % p > p / 2`. -/
theorem gauss_lemma {p : ℕ} [h : Fact p.Prime] {a : ℤ} (hp : p ≠ 2) (ha0 : (a : ZMod p) ≠ 0) :
    legendreSym p a = (-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card := by
  replace hp : Odd p := h.out.odd_of_ne_two hp
  have : (legendreSym p a : ZMod p) = (((-1) ^ ((Ico 1 (p / 2).succ).filter fun x : ℕ =>
      p / 2 < (a * x : ZMod p).val).card : ℤ) : ZMod p) := by
    rw [legendreSym.eq_pow, gauss_lemma_aux p ha0]
  cases legendreSym.eq_one_or_neg_one p ha0 <;>
  cases neg_one_pow_eq_or ℤ
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card <;>
  

Proof state:
case inr.inr
p : ℕ
h : Fact (Nat.Prime p)
a : ℤ
ha0 : ↑a ≠ 0
hp : Odd p
this : ↑(legendreSym p a) = ↑((-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))))
h✝¹ : legendreSym p a = -1
h✝ : (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2)))) = -1
⊢ legendreSym p a = (-1) ^ Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))

Next tactic:
simp_all [ne_neg_self hp one_ne_zero, (ne_neg_self hp one_ne_zero).symm]
private theorem eisenstein_lemma_aux₁ (p : ℕ) [Fact p.Prime] [hp2 : Fact (p % 2 = 1)] {a : ℕ}
    (hap : (a : ZMod p) ≠ 0) : ((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : ZMod 2) =
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card +
      ∑ x in Ico 1 (p / 2).succ, x + (∑ x in Ico 1 (p / 2).succ, a * x / p : ℕ) :=
  have hp2 : (p : ZMod 2) = (1 : ℕ) := (eq_iff_modEq_nat _).2 hp2.1
  calc
    ((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : ZMod 2) =
        ((∑ x in Ico 1 (p / 2).succ, (a * x % p + p * (a * x / p)) : ℕ) : ZMod 2) := by
      

Proof state:
p : ℕ
inst✝ : Fact (Nat.Prime p)
hp2✝ : Fact (p % 2 = 1)
a : ℕ
hap : ↑a ≠ 0
hp2 : ↑p = ↑1
⊢ ↑(∑ x in Ico 1 (succ (p / 2)), a * x) = ↑(∑ x in Ico 1 (succ (p / 2)), (a * x % p + p * (a * x / p)))

Next tactic:
simp only [mod_add_div]
private theorem eisenstein_lemma_aux₁ (p : ℕ) [Fact p.Prime] [hp2 : Fact (p % 2 = 1)] {a : ℕ}
    (hap : (a : ZMod p) ≠ 0) : ((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : ZMod 2) =
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card +
      ∑ x in Ico 1 (p / 2).succ, x + (∑ x in Ico 1 (p / 2).succ, a * x / p : ℕ) :=
  have hp2 : (p : ZMod 2) = (1 : ℕ) := (eq_iff_modEq_nat _).2 hp2.1
  calc
    ((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : ZMod 2) =
        ((∑ x in Ico 1 (p / 2).succ, (a * x % p + p * (a * x / p)) : ℕ) : ZMod 2) := by
      simp only [mod_add_div]
    _ = (∑ x in Ico 1 (p / 2).succ, ((a * x : ℕ) : ZMod p).val : ℕ) +
        (∑ x in Ico 1 (p / 2).succ, a * x / p : ℕ) := by
      

Proof state:
p : ℕ
inst✝ : Fact (Nat.Prime p)
hp2✝ : Fact (p % 2 = 1)
a : ℕ
hap : ↑a ≠ 0
hp2 : ↑p = ↑1
⊢ ↑(∑ x in Ico 1 (succ (p / 2)), (a * x % p + p * (a * x / p))) =
    ↑(∑ x in Ico 1 (succ (p / 2)), val ↑(a * x)) + ↑(∑ x in Ico 1 (succ (p / 2)), a * x / p)

Next tactic:
simp only [val_nat_cast]
private theorem eisenstein_lemma_aux₁ (p : ℕ) [Fact p.Prime] [hp2 : Fact (p % 2 = 1)] {a : ℕ}
    (hap : (a : ZMod p) ≠ 0) : ((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : ZMod 2) =
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card +
      ∑ x in Ico 1 (p / 2).succ, x + (∑ x in Ico 1 (p / 2).succ, a * x / p : ℕ) :=
  have hp2 : (p : ZMod 2) = (1 : ℕ) := (eq_iff_modEq_nat _).2 hp2.1
  calc
    ((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : ZMod 2) =
        ((∑ x in Ico 1 (p / 2).succ, (a * x % p + p * (a * x / p)) : ℕ) : ZMod 2) := by
      simp only [mod_add_div]
    _ = (∑ x in Ico 1 (p / 2).succ, ((a * x : ℕ) : ZMod p).val : ℕ) +
        (∑ x in Ico 1 (p / 2).succ, a * x / p : ℕ) := by
      simp only [val_nat_cast]
      

Proof state:
p : ℕ
inst✝ : Fact (Nat.Prime p)
hp2✝ : Fact (p % 2 = 1)
a : ℕ
hap : ↑a ≠ 0
hp2 : ↑p = ↑1
⊢ ↑(∑ x in Ico 1 (succ (p / 2)), (a * x % p + p * (a * x / p))) =
    ↑(∑ x in Ico 1 (succ (p / 2)), a * x % p) + ↑(∑ x in Ico 1 (succ (p / 2)), a * x / p)

Next tactic:
simp [sum_add_distrib, mul_sum.symm, Nat.cast_add, Nat.cast_mul, Nat.cast_sum, hp2]
private theorem eisenstein_lemma_aux₁ (p : ℕ) [Fact p.Prime] [hp2 : Fact (p % 2 = 1)] {a : ℕ}
    (hap : (a : ZMod p) ≠ 0) : ((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : ZMod 2) =
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card +
      ∑ x in Ico 1 (p / 2).succ, x + (∑ x in Ico 1 (p / 2).succ, a * x / p : ℕ) :=
  have hp2 : (p : ZMod 2) = (1 : ℕ) := (eq_iff_modEq_nat _).2 hp2.1
  calc
    ((∑ x in Ico 1 (p / 2).succ, a * x : ℕ) : ZMod 2) =
        ((∑ x in Ico 1 (p / 2).succ, (a * x % p + p * (a * x / p)) : ℕ) : ZMod 2) := by
      simp only [mod_add_div]
    _ = (∑ x in Ico 1 (p / 2).succ, ((a * x : ℕ) : ZMod p).val : ℕ) +
        (∑ x in Ico 1 (p / 2).succ, a * x / p : ℕ) := by
      simp only [val_nat_cast]
      simp [sum_add_distrib, mul_sum.symm, Nat.cast_add, Nat.cast_mul, Nat.cast_sum, hp2]
    _ = _ :=
      congr_arg₂ (· + ·)
        (calc
          ((∑ x in Ico 1 (p / 2).succ, ((a * x : ℕ) : ZMod p).val : ℕ) : ZMod 2) =
              ∑ x in Ico 1 (p / 2).succ, (((a * x : ZMod p).valMinAbs +
                if (a * x : ZMod p).val ≤ p / 2 then 0 else p : ℤ) : ZMod 2) := by
            

Proof state:
p : ℕ
inst✝ : Fact (Nat.Prime p)
hp2✝ : Fact (p % 2 = 1)
a : ℕ
hap : ↑a ≠ 0
hp2 : ↑p = ↑1
⊢ ↑(∑ x in Ico 1 (succ (p / 2)), val ↑(a * x)) =
    ∑ x in Ico 1 (succ (p / 2)), ↑(valMinAbs (↑a * ↑x) + ↑(if val (↑a * ↑x) ≤ p / 2 then 0 else p))

Next tactic:
simp only [(val_eq_ite_valMinAbs _).symm]