l3lab/ntp-mathlib huggingface dataset:
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
            simp only [(val_eq_ite_valMinAbs _).symm]; 

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)), ↑↑(val (↑a * ↑x))

Next tactic:
simp [Nat.cast_sum]
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
            simp only [(val_eq_ite_valMinAbs _).symm]; simp [Nat.cast_sum]
          _ = ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card +
              (∑ x in Ico 1 (p / 2).succ, (a * x : ZMod p).valMinAbs.natAbs : ℕ) := 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)), ↑(valMinAbs (↑a * ↑x) + ↑(if val (↑a * ↑x) ≤ p / 2 then 0 else p)) =
    ↑(Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))) +
      ↑(∑ x in Ico 1 (succ (p / 2)), Int.natAbs (valMinAbs (↑a * ↑x)))

Next tactic:
simp [add_comm, sum_add_distrib, Finset.sum_ite, hp2, Nat.cast_sum]
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
            simp only [(val_eq_ite_valMinAbs _).symm]; simp [Nat.cast_sum]
          _ = ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card +
              (∑ x in Ico 1 (p / 2).succ, (a * x : ZMod p).valMinAbs.natAbs : ℕ) := by
            simp [add_comm, sum_add_distrib, Finset.sum_ite, hp2, Nat.cast_sum]
          _ = _ := by
            

Proof state:
p : ℕ
inst✝ : Fact (Nat.Prime p)
hp2✝ : Fact (p % 2 = 1)
a : ℕ
hap : ↑a ≠ 0
hp2 : ↑p = ↑1
⊢ ↑(Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))) +
      ↑(∑ x in Ico 1 (succ (p / 2)), Int.natAbs (valMinAbs (↑a * ↑x))) =
    ↑(Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))) + ↑(∑ x in Ico 1 (succ (p / 2)), x)

Next tactic:
rw [Finset.sum_eq_multiset_sum, Ico_map_valMinAbs_natAbs_eq_Ico_map_id p a hap, ←
              Finset.sum_eq_multiset_sum]
theorem eisenstein_lemma_aux (p : ℕ) [Fact p.Prime] [Fact (p % 2 = 1)] {a : ℕ} (ha2 : a % 2 = 1)
    (hap : (a : ZMod p) ≠ 0) :
    ((Ico 1 (p / 2).succ).filter fun x : ℕ => p / 2 < (a * x : ZMod p).val).card ≡
      ∑ x in Ico 1 (p / 2).succ, x * a / p [MOD 2] :=
  have ha2 : (a : ZMod 2) = (1 : ℕ) := (eq_iff_modEq_nat _).2 ha2
  (eq_iff_modEq_nat 2).1 <| sub_eq_zero.1 <| by
    

Proof state:
p : ℕ
inst✝¹ : Fact (Nat.Prime p)
inst✝ : Fact (p % 2 = 1)
a : ℕ
ha2✝ : a % 2 = 1
hap : ↑a ≠ 0
ha2 : ↑a = ↑1
⊢ ↑(Finset.card (filter (fun x => p / 2 < val (↑a * ↑x)) (Ico 1 (succ (p / 2))))) -
      ↑(∑ x in Ico 1 (succ (p / 2)), x * a / p) =
    0

Next tactic:
simpa [add_left_comm, sub_eq_add_neg, Finset.mul_sum.symm, mul_comm, ha2, Nat.cast_sum,
      add_neg_eq_iff_eq_add.symm, neg_eq_self_mod_two, add_assoc] using
      Eq.symm (eisenstein_lemma_aux₁ p hap)
theorem div_eq_filter_card {a b c : ℕ} (hb0 : 0 < b) (hc : a / b ≤ c) :
    a / b = ((Ico 1 c.succ).filter fun x => x * b ≤ a).card :=
  calc
    a / b = (Ico 1 (a / b).succ).card := by 

Proof state:
a b c : ℕ
hb0 : 0 < b
hc : a / b ≤ c
⊢ a / b = Finset.card (Ico 1 (succ (a / b)))

Next tactic:
simp
theorem div_eq_filter_card {a b c : ℕ} (hb0 : 0 < b) (hc : a / b ≤ c) :
    a / b = ((Ico 1 c.succ).filter fun x => x * b ≤ a).card :=
  calc
    a / b = (Ico 1 (a / b).succ).card := by simp
    _ = ((Ico 1 c.succ).filter fun x => x * b ≤ a).card :=
      congr_arg _ <| Finset.ext fun x => by
        

Proof state:
a b c : ℕ
hb0 : 0 < b
hc : a / b ≤ c
x : ℕ
⊢ x ∈ Ico 1 (succ (a / b)) ↔ x ∈ filter (fun x => x * b ≤ a) (Ico 1 (succ c))

Next tactic:
have : x * b ≤ a → x ≤ c := fun h => le_trans (by rwa [le_div_iff_mul_le hb0]) hc
theorem div_eq_filter_card {a b c : ℕ} (hb0 : 0 < b) (hc : a / b ≤ c) :
    a / b = ((Ico 1 c.succ).filter fun x => x * b ≤ a).card :=
  calc
    a / b = (Ico 1 (a / b).succ).card := by simp
    _ = ((Ico 1 c.succ).filter fun x => x * b ≤ a).card :=
      congr_arg _ <| Finset.ext fun x => by
        have : x * b ≤ a → x ≤ c := fun h => le_trans (by 

Proof state:
a b c : ℕ
hb0 : 0 < b
hc : a / b ≤ c
x : ℕ
h : x * b ≤ a
⊢ x ≤ a / b

Next tactic:
rwa [le_div_iff_mul_le hb0]
theorem div_eq_filter_card {a b c : ℕ} (hb0 : 0 < b) (hc : a / b ≤ c) :
    a / b = ((Ico 1 c.succ).filter fun x => x * b ≤ a).card :=
  calc
    a / b = (Ico 1 (a / b).succ).card := by simp
    _ = ((Ico 1 c.succ).filter fun x => x * b ≤ a).card :=
      congr_arg _ <| Finset.ext fun x => by
        have : x * b ≤ a → x ≤ c := fun h => le_trans (by rwa [le_div_iff_mul_le hb0]) hc
        

Proof state:
a b c : ℕ
hb0 : 0 < b
hc : a / b ≤ c
x : ℕ
this : x * b ≤ a → x ≤ c
⊢ x ∈ Ico 1 (succ (a / b)) ↔ x ∈ filter (fun x => x * b ≤ a) (Ico 1 (succ c))

Next tactic:
simp [lt_succ_iff, le_div_iff_mul_le hb0]
theorem div_eq_filter_card {a b c : ℕ} (hb0 : 0 < b) (hc : a / b ≤ c) :
    a / b = ((Ico 1 c.succ).filter fun x => x * b ≤ a).card :=
  calc
    a / b = (Ico 1 (a / b).succ).card := by simp
    _ = ((Ico 1 c.succ).filter fun x => x * b ≤ a).card :=
      congr_arg _ <| Finset.ext fun x => by
        have : x * b ≤ a → x ≤ c := fun h => le_trans (by rwa [le_div_iff_mul_le hb0]) hc
        simp [lt_succ_iff, le_div_iff_mul_le hb0]; 

Proof state:
a b c : ℕ
hb0 : 0 < b
hc : a / b ≤ c
x : ℕ
this : x * b ≤ a → x ≤ c
⊢ x * b ≤ a → 1 ≤ x → x ≤ c

Next tactic:
tauto
/-- The given sum is the number of integer points in the triangle formed by the diagonal of the
  rectangle `(0, p/2) × (0, q/2)`. -/
private theorem sum_Ico_eq_card_lt {p q : ℕ} :
    ∑ a in Ico 1 (p / 2).succ, a * q / p =
    ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
      x.2 * p ≤ x.1 * q).card :=
  if hp0 : p = 0 then by 

Proof state:
p q : ℕ
hp0 : p = 0
⊢ ∑ a in Ico 1 (succ (p / 2)), a * q / p =
    Finset.card (filter (fun x => x.2 * p ≤ x.1 * q) (Ico 1 (succ (p / 2)) ×ˢ Ico 1 (succ (q / 2))))

Next tactic:
simp [hp0, Finset.ext_iff]
/-- The given sum is the number of integer points in the triangle formed by the diagonal of the
  rectangle `(0, p/2) × (0, q/2)`. -/
private theorem sum_Ico_eq_card_lt {p q : ℕ} :
    ∑ a in Ico 1 (p / 2).succ, a * q / p =
    ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
      x.2 * p ≤ x.1 * q).card :=
  if hp0 : p = 0 then by simp [hp0, Finset.ext_iff]
  else
    calc
      ∑ a in Ico 1 (p / 2).succ, a * q / p =
          ∑ a in Ico 1 (p / 2).succ, ((Ico 1 (q / 2).succ).filter fun x => x * p ≤ a * q).card :=
        Finset.sum_congr rfl fun x hx => div_eq_filter_card (Nat.pos_of_ne_zero hp0)
          (calc
            x * q / p ≤ p / 2 * q / p := Nat.div_le_div_right
              (mul_le_mul_of_nonneg_right (le_of_lt_succ <| (mem_Ico.mp hx).2) (Nat.zero_le _))
            _ ≤ _ := Nat.div_mul_div_le_div _ _ _)
      _ = _ := by
        

Proof state:
p q : ℕ
hp0 : ¬p = 0
⊢ ∑ a in Ico 1 (succ (p / 2)), Finset.card (filter (fun x => x * p ≤ a * q) (Ico 1 (succ (q / 2)))) =
    Finset.card (filter (fun x => x.2 * p ≤ x.1 * q) (Ico 1 (succ (p / 2)) ×ˢ Ico 1 (succ (q / 2))))

Next tactic:
rw [← card_sigma]
/-- The given sum is the number of integer points in the triangle formed by the diagonal of the
  rectangle `(0, p/2) × (0, q/2)`. -/
private theorem sum_Ico_eq_card_lt {p q : ℕ} :
    ∑ a in Ico 1 (p / 2).succ, a * q / p =
    ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
      x.2 * p ≤ x.1 * q).card :=
  if hp0 : p = 0 then by simp [hp0, Finset.ext_iff]
  else
    calc
      ∑ a in Ico 1 (p / 2).succ, a * q / p =
          ∑ a in Ico 1 (p / 2).succ, ((Ico 1 (q / 2).succ).filter fun x => x * p ≤ a * q).card :=
        Finset.sum_congr rfl fun x hx => div_eq_filter_card (Nat.pos_of_ne_zero hp0)
          (calc
            x * q / p ≤ p / 2 * q / p := Nat.div_le_div_right
              (mul_le_mul_of_nonneg_right (le_of_lt_succ <| (mem_Ico.mp hx).2) (Nat.zero_le _))
            _ ≤ _ := Nat.div_mul_div_le_div _ _ _)
      _ = _ := by
        rw [← card_sigma]
        

Proof state:
p q : ℕ
hp0 : ¬p = 0
⊢ Finset.card (Finset.sigma (Ico 1 (succ (p / 2))) fun a => filter (fun x => x * p ≤ a * q) (Ico 1 (succ (q / 2)))) =
    Finset.card (filter (fun x => x.2 * p ≤ x.1 * q) (Ico 1 (succ (p / 2)) ×ˢ Ico 1 (succ (q / 2))))

Next tactic:
exact card_congr (fun a _ => ⟨a.1, a.2⟩) (by
          simp (config := { contextual := true }) only [mem_filter, mem_sigma, and_self_iff,
            forall_true_iff, mem_product])
          (fun ⟨_, _⟩ ⟨_, _⟩ => by
            simp (config := { contextual := true }) only [Prod.mk.inj_iff, eq_self_iff_true,
              and_self_iff, heq_iff_eq, forall_true_iff])
          fun ⟨b₁, b₂⟩ h => ⟨⟨b₁, b₂⟩, by
            revert h
            simp (config := { contextual := true }) only [mem_filter, eq_self_iff_true,
              exists_prop_of_true, mem_sigma, and_self_iff, forall_true_iff, mem_product]⟩
/-- The given sum is the number of integer points in the triangle formed by the diagonal of the
  rectangle `(0, p/2) × (0, q/2)`. -/
private theorem sum_Ico_eq_card_lt {p q : ℕ} :
    ∑ a in Ico 1 (p / 2).succ, a * q / p =
    ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
      x.2 * p ≤ x.1 * q).card :=
  if hp0 : p = 0 then by simp [hp0, Finset.ext_iff]
  else
    calc
      ∑ a in Ico 1 (p / 2).succ, a * q / p =
          ∑ a in Ico 1 (p / 2).succ, ((Ico 1 (q / 2).succ).filter fun x => x * p ≤ a * q).card :=
        Finset.sum_congr rfl fun x hx => div_eq_filter_card (Nat.pos_of_ne_zero hp0)
          (calc
            x * q / p ≤ p / 2 * q / p := Nat.div_le_div_right
              (mul_le_mul_of_nonneg_right (le_of_lt_succ <| (mem_Ico.mp hx).2) (Nat.zero_le _))
            _ ≤ _ := Nat.div_mul_div_le_div _ _ _)
      _ = _ := by
        rw [← card_sigma]
        exact card_congr (fun a _ => ⟨a.1, a.2⟩) (by
          

Proof state:
p q : ℕ
hp0 : ¬p = 0
⊢ ∀ (a : (_ : ℕ) × ℕ)
    (ha : a ∈ Finset.sigma (Ico 1 (succ (p / 2))) fun a => filter (fun x => x * p ≤ a * q) (Ico 1 (succ (q / 2)))),
    (fun a x => (a.fst, a.snd)) a ha ∈
      filter (fun x => x.2 * p ≤ x.1 * q) (Ico 1 (succ (p / 2)) ×ˢ Ico 1 (succ (q / 2)))

Next tactic:
simp (config := { contextual := true }) only [mem_filter, mem_sigma, and_self_iff,
            forall_true_iff, mem_product]
/-- The given sum is the number of integer points in the triangle formed by the diagonal of the
  rectangle `(0, p/2) × (0, q/2)`. -/
private theorem sum_Ico_eq_card_lt {p q : ℕ} :
    ∑ a in Ico 1 (p / 2).succ, a * q / p =
    ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
      x.2 * p ≤ x.1 * q).card :=
  if hp0 : p = 0 then by simp [hp0, Finset.ext_iff]
  else
    calc
      ∑ a in Ico 1 (p / 2).succ, a * q / p =
          ∑ a in Ico 1 (p / 2).succ, ((Ico 1 (q / 2).succ).filter fun x => x * p ≤ a * q).card :=
        Finset.sum_congr rfl fun x hx => div_eq_filter_card (Nat.pos_of_ne_zero hp0)
          (calc
            x * q / p ≤ p / 2 * q / p := Nat.div_le_div_right
              (mul_le_mul_of_nonneg_right (le_of_lt_succ <| (mem_Ico.mp hx).2) (Nat.zero_le _))
            _ ≤ _ := Nat.div_mul_div_le_div _ _ _)
      _ = _ := by
        rw [← card_sigma]
        exact card_congr (fun a _ => ⟨a.1, a.2⟩) (by
          simp (config := { contextual := true }) only [mem_filter, mem_sigma, and_self_iff,
            forall_true_iff, mem_product])
          (fun ⟨_, _⟩ ⟨_, _⟩ => by
            

Proof state:
p q : ℕ
hp0 : ¬p = 0
x✝¹ x✝ : (_ : ℕ) × ℕ
fst✝¹ snd✝¹ fst✝ snd✝ : ℕ
⊢ ∀
    (ha :
      { fst := fst✝¹, snd := snd✝¹ } ∈
        Finset.sigma (Ico 1 (succ (p / 2))) fun a => filter (fun x => x * p ≤ a * q) (Ico 1 (succ (q / 2))))
    (hb :
      { fst := fst✝, snd := snd✝ } ∈
        Finset.sigma (Ico 1 (succ (p / 2))) fun a => filter (fun x => x * p ≤ a * q) (Ico 1 (succ (q / 2)))),
    (fun a x => (a.fst, a.snd)) { fst := fst✝¹, snd := snd✝¹ } ha =
        (fun a x => (a.fst, a.snd)) { fst := fst✝, snd := snd✝ } hb →
      { fst := fst✝¹, snd := snd✝¹ } = { fst := fst✝, snd := snd✝ }

Next tactic:
simp (config := { contextual := true }) only [Prod.mk.inj_iff, eq_self_iff_true,
              and_self_iff, heq_iff_eq, forall_true_iff]
/-- The given sum is the number of integer points in the triangle formed by the diagonal of the
  rectangle `(0, p/2) × (0, q/2)`. -/
private theorem sum_Ico_eq_card_lt {p q : ℕ} :
    ∑ a in Ico 1 (p / 2).succ, a * q / p =
    ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
      x.2 * p ≤ x.1 * q).card :=
  if hp0 : p = 0 then by simp [hp0, Finset.ext_iff]
  else
    calc
      ∑ a in Ico 1 (p / 2).succ, a * q / p =
          ∑ a in Ico 1 (p / 2).succ, ((Ico 1 (q / 2).succ).filter fun x => x * p ≤ a * q).card :=
        Finset.sum_congr rfl fun x hx => div_eq_filter_card (Nat.pos_of_ne_zero hp0)
          (calc
            x * q / p ≤ p / 2 * q / p := Nat.div_le_div_right
              (mul_le_mul_of_nonneg_right (le_of_lt_succ <| (mem_Ico.mp hx).2) (Nat.zero_le _))
            _ ≤ _ := Nat.div_mul_div_le_div _ _ _)
      _ = _ := by
        rw [← card_sigma]
        exact card_congr (fun a _ => ⟨a.1, a.2⟩) (by
          simp (config := { contextual := true }) only [mem_filter, mem_sigma, and_self_iff,
            forall_true_iff, mem_product])
          (fun ⟨_, _⟩ ⟨_, _⟩ => by
            simp (config := { contextual := true }) only [Prod.mk.inj_iff, eq_self_iff_true,
              and_self_iff, heq_iff_eq, forall_true_iff])
          fun ⟨b₁, b₂⟩ h => ⟨⟨b₁, b₂⟩, by
            

Proof state:
p q : ℕ
hp0 : ¬p = 0
x✝ : ℕ × ℕ
b₁ b₂ : ℕ
h : (b₁, b₂) ∈ filter (fun x => x.2 * p ≤ x.1 * q) (Ico 1 (succ (p / 2)) ×ˢ Ico 1 (succ (q / 2)))
⊢ ∃ (ha :
    { fst := b₁, snd := b₂ } ∈
      Finset.sigma (Ico 1 (succ (p / 2))) fun a => filter (fun x => x * p ≤ a * q) (Ico 1 (succ (q / 2)))),
    (fun a x => (a.fst, a.snd)) { fst := b₁, snd := b₂ } ha = (b₁, b₂)

Next tactic:
revert h
/-- The given sum is the number of integer points in the triangle formed by the diagonal of the
  rectangle `(0, p/2) × (0, q/2)`. -/
private theorem sum_Ico_eq_card_lt {p q : ℕ} :
    ∑ a in Ico 1 (p / 2).succ, a * q / p =
    ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
      x.2 * p ≤ x.1 * q).card :=
  if hp0 : p = 0 then by simp [hp0, Finset.ext_iff]
  else
    calc
      ∑ a in Ico 1 (p / 2).succ, a * q / p =
          ∑ a in Ico 1 (p / 2).succ, ((Ico 1 (q / 2).succ).filter fun x => x * p ≤ a * q).card :=
        Finset.sum_congr rfl fun x hx => div_eq_filter_card (Nat.pos_of_ne_zero hp0)
          (calc
            x * q / p ≤ p / 2 * q / p := Nat.div_le_div_right
              (mul_le_mul_of_nonneg_right (le_of_lt_succ <| (mem_Ico.mp hx).2) (Nat.zero_le _))
            _ ≤ _ := Nat.div_mul_div_le_div _ _ _)
      _ = _ := by
        rw [← card_sigma]
        exact card_congr (fun a _ => ⟨a.1, a.2⟩) (by
          simp (config := { contextual := true }) only [mem_filter, mem_sigma, and_self_iff,
            forall_true_iff, mem_product])
          (fun ⟨_, _⟩ ⟨_, _⟩ => by
            simp (config := { contextual := true }) only [Prod.mk.inj_iff, eq_self_iff_true,
              and_self_iff, heq_iff_eq, forall_true_iff])
          fun ⟨b₁, b₂⟩ h => ⟨⟨b₁, b₂⟩, by
            revert h
            

Proof state:
p q : ℕ
hp0 : ¬p = 0
x✝ : ℕ × ℕ
b₁ b₂ : ℕ
⊢ (b₁, b₂) ∈ filter (fun x => x.2 * p ≤ x.1 * q) (Ico 1 (succ (p / 2)) ×ˢ Ico 1 (succ (q / 2))) →
    ∃ (ha :
      { fst := b₁, snd := b₂ } ∈
        Finset.sigma (Ico 1 (succ (p / 2))) fun a => filter (fun x => x * p ≤ a * q) (Ico 1 (succ (q / 2)))),
      (fun a x => (a.fst, a.snd)) { fst := b₁, snd := b₂ } ha = (b₁, b₂)

Next tactic:
simp (config := { contextual := true }) only [mem_filter, eq_self_iff_true,
              exists_prop_of_true, mem_sigma, and_self_iff, forall_true_iff, mem_product]
/-- Each of the sums in this lemma is the cardinality of the set of integer points in each of the
  two triangles formed by the diagonal of the rectangle `(0, p/2) × (0, q/2)`. Adding them
  gives the number of points in the rectangle. -/
theorem sum_mul_div_add_sum_mul_div_eq_mul (p q : ℕ) [hp : Fact p.Prime] (hq0 : (q : ZMod p) ≠ 0) :
    ∑ a in Ico 1 (p / 2).succ, a * q / p + ∑ a in Ico 1 (q / 2).succ, a * p / q =
    p / 2 * (q / 2) := by
  

Proof state:
p q : ℕ
hp : Fact (Nat.Prime p)
hq0 : ↑q ≠ 0
⊢ ∑ a in Ico 1 (succ (p / 2)), a * q / p + ∑ a in Ico 1 (succ (q / 2)), a * p / q = p / 2 * (q / 2)

Next tactic:
have hswap :
    ((Ico 1 (q / 2).succ ×ˢ Ico 1 (p / 2).succ).filter fun x : ℕ × ℕ => x.2 * q ≤ x.1 * p).card =
      ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
        x.1 * q ≤ x.2 * p).card :=
    card_congr (fun x _ => Prod.swap x)
      (fun ⟨_, _⟩ => by
        simp (config := { contextual := true }) only [mem_filter, and_self_iff, Prod.swap_prod_mk,
          forall_true_iff, mem_product])
      (fun ⟨_, _⟩ ⟨_, _⟩ => by
        simp (config := { contextual := true }) only [Prod.mk.inj_iff, eq_self_iff_true,
          and_self_iff, Prod.swap_prod_mk, forall_true_iff])
      fun ⟨x₁, x₂⟩ h => ⟨⟨x₂, x₁⟩, by
        revert h
        simp (config := { contextual := true }) only [mem_filter, eq_self_iff_true, and_self_iff,
          exists_prop_of_true, Prod.swap_prod_mk, forall_true_iff, mem_product]⟩
/-- Each of the sums in this lemma is the cardinality of the set of integer points in each of the
  two triangles formed by the diagonal of the rectangle `(0, p/2) × (0, q/2)`. Adding them
  gives the number of points in the rectangle. -/
theorem sum_mul_div_add_sum_mul_div_eq_mul (p q : ℕ) [hp : Fact p.Prime] (hq0 : (q : ZMod p) ≠ 0) :
    ∑ a in Ico 1 (p / 2).succ, a * q / p + ∑ a in Ico 1 (q / 2).succ, a * p / q =
    p / 2 * (q / 2) := by
  have hswap :
    ((Ico 1 (q / 2).succ ×ˢ Ico 1 (p / 2).succ).filter fun x : ℕ × ℕ => x.2 * q ≤ x.1 * p).card =
      ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
        x.1 * q ≤ x.2 * p).card :=
    card_congr (fun x _ => Prod.swap x)
      (fun ⟨_, _⟩ => by
        

Proof state:
p q : ℕ
hp : Fact (Nat.Prime p)
hq0 : ↑q ≠ 0
x✝ : ℕ × ℕ
fst✝ snd✝ : ℕ
⊢ ∀ (ha : (fst✝, snd✝) ∈ filter (fun x => x.2 * q ≤ x.1 * p) (Ico 1 (succ (q / 2)) ×ˢ Ico 1 (succ (p / 2)))),
    (fun x x_1 => Prod.swap x) (fst✝, snd✝) ha ∈
      filter (fun x => x.1 * q ≤ x.2 * p) (Ico 1 (succ (p / 2)) ×ˢ Ico 1 (succ (q / 2)))

Next tactic:
simp (config := { contextual := true }) only [mem_filter, and_self_iff, Prod.swap_prod_mk,
          forall_true_iff, mem_product]
/-- Each of the sums in this lemma is the cardinality of the set of integer points in each of the
  two triangles formed by the diagonal of the rectangle `(0, p/2) × (0, q/2)`. Adding them
  gives the number of points in the rectangle. -/
theorem sum_mul_div_add_sum_mul_div_eq_mul (p q : ℕ) [hp : Fact p.Prime] (hq0 : (q : ZMod p) ≠ 0) :
    ∑ a in Ico 1 (p / 2).succ, a * q / p + ∑ a in Ico 1 (q / 2).succ, a * p / q =
    p / 2 * (q / 2) := by
  have hswap :
    ((Ico 1 (q / 2).succ ×ˢ Ico 1 (p / 2).succ).filter fun x : ℕ × ℕ => x.2 * q ≤ x.1 * p).card =
      ((Ico 1 (p / 2).succ ×ˢ Ico 1 (q / 2).succ).filter fun x : ℕ × ℕ =>
        x.1 * q ≤ x.2 * p).card :=
    card_congr (fun x _ => Prod.swap x)
      (fun ⟨_, _⟩ => by
        simp (config := { contextual := true }) only [mem_filter, and_self_iff, Prod.swap_prod_mk,
          forall_true_iff, mem_product])
      (fun ⟨_, _⟩ ⟨_, _⟩ => by
        

Proof state:
p q : ℕ
hp : Fact (Nat.Prime p)
hq0 : ↑q ≠ 0
x✝¹ x✝ : ℕ × ℕ
fst✝¹ snd✝¹ fst✝ snd✝ : ℕ
⊢ ∀ (ha : (fst✝¹, snd✝¹) ∈ filter (fun x => x.2 * q ≤ x.1 * p) (Ico 1 (succ (q / 2)) ×ˢ Ico 1 (succ (p / 2))))
    (hb : (fst✝, snd✝) ∈ filter (fun x => x.2 * q ≤ x.1 * p) (Ico 1 (succ (q / 2)) ×ˢ Ico 1 (succ (p / 2)))),
    (fun x x_1 => Prod.swap x) (fst✝¹, snd✝¹) ha = (fun x x_1 => Prod.swap x) (fst✝, snd✝) hb →
      (fst✝¹, snd✝¹) = (fst✝, snd✝)

Next tactic:
simp (config := { contextual := true }) only [Prod.mk.inj_iff, eq_self_iff_true,
          and_self_iff, Prod.swap_prod_mk, forall_true_iff]