l3lab/ntp-mathlib huggingface dataset:
@[to_additive]
theorem pi_mem_of_mulSingle_mem_aux [DecidableEq η] (I : Finset η) {H : Subgroup (∀ i, f i)}
    (x : ∀ i, f i) (h1 : ∀ i, i ∉ I → x i = 1) (h2 : ∀ i, i ∈ I → Pi.mulSingle i (x i) ∈ H) :
    x ∈ H := by
  induction' I using Finset.induction_on with i I hnmem ih generalizing x
  · convert one_mem H
    ext i
    exact h1 i (Finset.not_mem_empty i)
  · have : x = Function.update x i 1 * Pi.mulSingle i (x i) := by
      ext j
      by_cases heq : j = i
      · subst heq
        simp
      · simp [heq]
    rw [this]
    clear this
    apply mul_mem
    · apply ih <;> clear ih
      · intro j hj
        by_cases heq : j = i
        · subst heq
          simp
        · simp [heq]
          apply h1 j
          simpa [heq] using hj
      · intro j hj
        have : j ≠ i := by
          rintro rfl
          contradiction
        

Proof state:
case insert.a.h2
G : Type u_1
inst✝³ : Group G
A : Type u_2
inst✝² : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝¹ : (i : η) → Group (f i)
inst✝ : DecidableEq η
H : Subgroup ((i : η) → f i)
i : η
I : Finset η
hnmem : i ∉ I
x : (i : η) → f i
h1 : ∀ i_1 ∉ insert i I, x i_1 = 1
h2 : ∀ i_1 ∈ insert i I, Pi.mulSingle i_1 (x i_1) ∈ H
j : η
hj : j ∈ I
this : j ≠ i
⊢ Pi.mulSingle j (Function.update x i 1 j) ∈ H

Next tactic:
simp only [ne_eq, this, not_false_eq_true, Function.update_noteq]
@[to_additive]
theorem pi_mem_of_mulSingle_mem_aux [DecidableEq η] (I : Finset η) {H : Subgroup (∀ i, f i)}
    (x : ∀ i, f i) (h1 : ∀ i, i ∉ I → x i = 1) (h2 : ∀ i, i ∈ I → Pi.mulSingle i (x i) ∈ H) :
    x ∈ H := by
  induction' I using Finset.induction_on with i I hnmem ih generalizing x
  · convert one_mem H
    ext i
    exact h1 i (Finset.not_mem_empty i)
  · have : x = Function.update x i 1 * Pi.mulSingle i (x i) := by
      ext j
      by_cases heq : j = i
      · subst heq
        simp
      · simp [heq]
    rw [this]
    clear this
    apply mul_mem
    · apply ih <;> clear ih
      · intro j hj
        by_cases heq : j = i
        · subst heq
          simp
        · simp [heq]
          apply h1 j
          simpa [heq] using hj
      · intro j hj
        have : j ≠ i := by
          rintro rfl
          contradiction
        simp only [ne_eq, this, not_false_eq_true, Function.update_noteq]
        

Proof state:
case insert.a.h2
G : Type u_1
inst✝³ : Group G
A : Type u_2
inst✝² : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝¹ : (i : η) → Group (f i)
inst✝ : DecidableEq η
H : Subgroup ((i : η) → f i)
i : η
I : Finset η
hnmem : i ∉ I
x : (i : η) → f i
h1 : ∀ i_1 ∉ insert i I, x i_1 = 1
h2 : ∀ i_1 ∈ insert i I, Pi.mulSingle i_1 (x i_1) ∈ H
j : η
hj : j ∈ I
this : j ≠ i
⊢ Pi.mulSingle j (x j) ∈ H

Next tactic:
exact h2 _ (Finset.mem_insert_of_mem hj)
@[to_additive]
theorem pi_mem_of_mulSingle_mem_aux [DecidableEq η] (I : Finset η) {H : Subgroup (∀ i, f i)}
    (x : ∀ i, f i) (h1 : ∀ i, i ∉ I → x i = 1) (h2 : ∀ i, i ∈ I → Pi.mulSingle i (x i) ∈ H) :
    x ∈ H := by
  induction' I using Finset.induction_on with i I hnmem ih generalizing x
  · convert one_mem H
    ext i
    exact h1 i (Finset.not_mem_empty i)
  · have : x = Function.update x i 1 * Pi.mulSingle i (x i) := by
      ext j
      by_cases heq : j = i
      · subst heq
        simp
      · simp [heq]
    rw [this]
    clear this
    apply mul_mem
    · apply ih <;> clear ih
      · intro j hj
        by_cases heq : j = i
        · subst heq
          simp
        · simp [heq]
          apply h1 j
          simpa [heq] using hj
      · intro j hj
        have : j ≠ i := by
          rintro rfl
          contradiction
        simp only [ne_eq, this, not_false_eq_true, Function.update_noteq]
        exact h2 _ (Finset.mem_insert_of_mem hj)
    · 

Proof state:
case insert.a
G : Type u_1
inst✝³ : Group G
A : Type u_2
inst✝² : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝¹ : (i : η) → Group (f i)
inst✝ : DecidableEq η
H : Subgroup ((i : η) → f i)
i : η
I : Finset η
hnmem : i ∉ I
ih : ∀ (x : (i : η) → f i), (∀ i ∉ I, x i = 1) → (∀ i ∈ I, Pi.mulSingle i (x i) ∈ H) → x ∈ H
x : (i : η) → f i
h1 : ∀ i_1 ∉ insert i I, x i_1 = 1
h2 : ∀ i_1 ∈ insert i I, Pi.mulSingle i_1 (x i_1) ∈ H
⊢ Pi.mulSingle i (x i) ∈ H

Next tactic:
apply h2
@[to_additive]
theorem pi_mem_of_mulSingle_mem_aux [DecidableEq η] (I : Finset η) {H : Subgroup (∀ i, f i)}
    (x : ∀ i, f i) (h1 : ∀ i, i ∉ I → x i = 1) (h2 : ∀ i, i ∈ I → Pi.mulSingle i (x i) ∈ H) :
    x ∈ H := by
  induction' I using Finset.induction_on with i I hnmem ih generalizing x
  · convert one_mem H
    ext i
    exact h1 i (Finset.not_mem_empty i)
  · have : x = Function.update x i 1 * Pi.mulSingle i (x i) := by
      ext j
      by_cases heq : j = i
      · subst heq
        simp
      · simp [heq]
    rw [this]
    clear this
    apply mul_mem
    · apply ih <;> clear ih
      · intro j hj
        by_cases heq : j = i
        · subst heq
          simp
        · simp [heq]
          apply h1 j
          simpa [heq] using hj
      · intro j hj
        have : j ≠ i := by
          rintro rfl
          contradiction
        simp only [ne_eq, this, not_false_eq_true, Function.update_noteq]
        exact h2 _ (Finset.mem_insert_of_mem hj)
    · apply h2
      

Proof state:
case insert.a.a
G : Type u_1
inst✝³ : Group G
A : Type u_2
inst✝² : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝¹ : (i : η) → Group (f i)
inst✝ : DecidableEq η
H : Subgroup ((i : η) → f i)
i : η
I : Finset η
hnmem : i ∉ I
ih : ∀ (x : (i : η) → f i), (∀ i ∉ I, x i = 1) → (∀ i ∈ I, Pi.mulSingle i (x i) ∈ H) → x ∈ H
x : (i : η) → f i
h1 : ∀ i_1 ∉ insert i I, x i_1 = 1
h2 : ∀ i_1 ∈ insert i I, Pi.mulSingle i_1 (x i_1) ∈ H
⊢ i ∈ insert i I

Next tactic:
simp
@[to_additive]
theorem pi_mem_of_mulSingle_mem [Finite η] [DecidableEq η] {H : Subgroup (∀ i, f i)} (x : ∀ i, f i)
    (h : ∀ i, Pi.mulSingle i (x i) ∈ H) : x ∈ H := by
  

Proof state:
G : Type u_1
inst✝⁴ : Group G
A : Type u_2
inst✝³ : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝² : (i : η) → Group (f i)
inst✝¹ : Finite η
inst✝ : DecidableEq η
H : Subgroup ((i : η) → f i)
x : (i : η) → f i
h : ∀ (i : η), Pi.mulSingle i (x i) ∈ H
⊢ x ∈ H

Next tactic:
cases nonempty_fintype η
@[to_additive]
theorem pi_mem_of_mulSingle_mem [Finite η] [DecidableEq η] {H : Subgroup (∀ i, f i)} (x : ∀ i, f i)
    (h : ∀ i, Pi.mulSingle i (x i) ∈ H) : x ∈ H := by
  cases nonempty_fintype η
  

Proof state:
case intro
G : Type u_1
inst✝⁴ : Group G
A : Type u_2
inst✝³ : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝² : (i : η) → Group (f i)
inst✝¹ : Finite η
inst✝ : DecidableEq η
H : Subgroup ((i : η) → f i)
x : (i : η) → f i
h : ∀ (i : η), Pi.mulSingle i (x i) ∈ H
val✝ : Fintype η
⊢ x ∈ H

Next tactic:
exact pi_mem_of_mulSingle_mem_aux Finset.univ x (by simp) fun i _ => h i
@[to_additive]
theorem pi_mem_of_mulSingle_mem [Finite η] [DecidableEq η] {H : Subgroup (∀ i, f i)} (x : ∀ i, f i)
    (h : ∀ i, Pi.mulSingle i (x i) ∈ H) : x ∈ H := by
  cases nonempty_fintype η
  exact pi_mem_of_mulSingle_mem_aux Finset.univ x (by 

Proof state:
G : Type u_1
inst✝⁴ : Group G
A : Type u_2
inst✝³ : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝² : (i : η) → Group (f i)
inst✝¹ : Finite η
inst✝ : DecidableEq η
H : Subgroup ((i : η) → f i)
x : (i : η) → f i
h : ∀ (i : η), Pi.mulSingle i (x i) ∈ H
val✝ : Fintype η
⊢ ∀ i ∉ Finset.univ, x i = 1

Next tactic:
simp
/-- For finite index types, the `Subgroup.pi` is generated by the embeddings of the groups.  -/
@[to_additive "For finite index types, the `Subgroup.pi` is generated by the embeddings of the
 additive groups."]
theorem pi_le_iff [DecidableEq η] [Finite η] {H : ∀ i, Subgroup (f i)} {J : Subgroup (∀ i, f i)} :
    pi univ H ≤ J ↔ ∀ i : η, map (MonoidHom.single f i) (H i) ≤ J := by
  

Proof state:
G : Type u_1
inst✝⁴ : Group G
A : Type u_2
inst✝³ : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝² : (i : η) → Group (f i)
inst✝¹ : DecidableEq η
inst✝ : Finite η
H : (i : η) → Subgroup (f i)
J : Subgroup ((i : η) → f i)
⊢ pi univ H ≤ J ↔ ∀ (i : η), map (MonoidHom.single f i) (H i) ≤ J

Next tactic:
constructor
/-- For finite index types, the `Subgroup.pi` is generated by the embeddings of the groups.  -/
@[to_additive "For finite index types, the `Subgroup.pi` is generated by the embeddings of the
 additive groups."]
theorem pi_le_iff [DecidableEq η] [Finite η] {H : ∀ i, Subgroup (f i)} {J : Subgroup (∀ i, f i)} :
    pi univ H ≤ J ↔ ∀ i : η, map (MonoidHom.single f i) (H i) ≤ J := by
  constructor
  · 

Proof state:
case mp
G : Type u_1
inst✝⁴ : Group G
A : Type u_2
inst✝³ : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝² : (i : η) → Group (f i)
inst✝¹ : DecidableEq η
inst✝ : Finite η
H : (i : η) → Subgroup (f i)
J : Subgroup ((i : η) → f i)
⊢ pi univ H ≤ J → ∀ (i : η), map (MonoidHom.single f i) (H i) ≤ J

Next tactic:
rintro h i _ ⟨x, hx, rfl⟩
/-- For finite index types, the `Subgroup.pi` is generated by the embeddings of the groups.  -/
@[to_additive "For finite index types, the `Subgroup.pi` is generated by the embeddings of the
 additive groups."]
theorem pi_le_iff [DecidableEq η] [Finite η] {H : ∀ i, Subgroup (f i)} {J : Subgroup (∀ i, f i)} :
    pi univ H ≤ J ↔ ∀ i : η, map (MonoidHom.single f i) (H i) ≤ J := by
  constructor
  · rintro h i _ ⟨x, hx, rfl⟩
    

Proof state:
case mp.intro.intro
G : Type u_1
inst✝⁴ : Group G
A : Type u_2
inst✝³ : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝² : (i : η) → Group (f i)
inst✝¹ : DecidableEq η
inst✝ : Finite η
H : (i : η) → Subgroup (f i)
J : Subgroup ((i : η) → f i)
h : pi univ H ≤ J
i : η
x : f i
hx : x ∈ ↑(H i)
⊢ (MonoidHom.single f i) x ∈ J

Next tactic:
apply h
/-- For finite index types, the `Subgroup.pi` is generated by the embeddings of the groups.  -/
@[to_additive "For finite index types, the `Subgroup.pi` is generated by the embeddings of the
 additive groups."]
theorem pi_le_iff [DecidableEq η] [Finite η] {H : ∀ i, Subgroup (f i)} {J : Subgroup (∀ i, f i)} :
    pi univ H ≤ J ↔ ∀ i : η, map (MonoidHom.single f i) (H i) ≤ J := by
  constructor
  · rintro h i _ ⟨x, hx, rfl⟩
    apply h
    

Proof state:
case mp.intro.intro.a
G : Type u_1
inst✝⁴ : Group G
A : Type u_2
inst✝³ : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝² : (i : η) → Group (f i)
inst✝¹ : DecidableEq η
inst✝ : Finite η
H : (i : η) → Subgroup (f i)
J : Subgroup ((i : η) → f i)
h : pi univ H ≤ J
i : η
x : f i
hx : x ∈ ↑(H i)
⊢ (MonoidHom.single f i) x ∈ pi univ H

Next tactic:
simpa using hx
/-- For finite index types, the `Subgroup.pi` is generated by the embeddings of the groups.  -/
@[to_additive "For finite index types, the `Subgroup.pi` is generated by the embeddings of the
 additive groups."]
theorem pi_le_iff [DecidableEq η] [Finite η] {H : ∀ i, Subgroup (f i)} {J : Subgroup (∀ i, f i)} :
    pi univ H ≤ J ↔ ∀ i : η, map (MonoidHom.single f i) (H i) ≤ J := by
  constructor
  · rintro h i _ ⟨x, hx, rfl⟩
    apply h
    simpa using hx
  · 

Proof state:
case mpr
G : Type u_1
inst✝⁴ : Group G
A : Type u_2
inst✝³ : AddGroup A
η : Type u_3
f : η → Type u_4
inst✝² : (i : η) → Group (f i)
inst✝¹ : DecidableEq η
inst✝ : Finite η
H : (i : η) → Subgroup (f i)
J : Subgroup ((i : η) → f i)
⊢ (∀ (i : η), map (MonoidHom.single f i) (H i) ≤ J) → pi univ H ≤ J

Next tactic:
exact fun h x hx => pi_mem_of_mulSingle_mem x fun i => h i (mem_map_of_mem _ (hx i trivial))
theorem mem_normalizer_fintype {S : Set G} [Finite S] {x : G} (h : ∀ n, n ∈ S → x * n * x⁻¹ ∈ S) :
    x ∈ Subgroup.setNormalizer S := by
  

Proof state:
G : Type u_1
inst✝² : Group G
A : Type u_2
inst✝¹ : AddGroup A
S : Set G
inst✝ : Finite ↑S
x : G
h : ∀ n ∈ S, x * n * x⁻¹ ∈ S
⊢ x ∈ setNormalizer S

Next tactic:
haveI := Classical.propDecidable
theorem mem_normalizer_fintype {S : Set G} [Finite S] {x : G} (h : ∀ n, n ∈ S → x * n * x⁻¹ ∈ S) :
    x ∈ Subgroup.setNormalizer S := by
  haveI := Classical.propDecidable; 

Proof state:
G : Type u_1
inst✝² : Group G
A : Type u_2
inst✝¹ : AddGroup A
S : Set G
inst✝ : Finite ↑S
x : G
h : ∀ n ∈ S, x * n * x⁻¹ ∈ S
this : (a : Prop) → Decidable a
⊢ x ∈ setNormalizer S

Next tactic:
cases nonempty_fintype S
theorem mem_normalizer_fintype {S : Set G} [Finite S] {x : G} (h : ∀ n, n ∈ S → x * n * x⁻¹ ∈ S) :
    x ∈ Subgroup.setNormalizer S := by
  haveI := Classical.propDecidable; cases nonempty_fintype S;
      

Proof state:
case intro
G : Type u_1
inst✝² : Group G
A : Type u_2
inst✝¹ : AddGroup A
S : Set G
inst✝ : Finite ↑S
x : G
h : ∀ n ∈ S, x * n * x⁻¹ ∈ S
this : (a : Prop) → Decidable a
val✝ : Fintype ↑S
⊢ x ∈ setNormalizer S

Next tactic:
haveI := Set.fintypeImage S fun n => x * n * x⁻¹
theorem mem_normalizer_fintype {S : Set G} [Finite S] {x : G} (h : ∀ n, n ∈ S → x * n * x⁻¹ ∈ S) :
    x ∈ Subgroup.setNormalizer S := by
  haveI := Classical.propDecidable; cases nonempty_fintype S;
      haveI := Set.fintypeImage S fun n => x * n * x⁻¹;
    

Proof state:
case intro
G : Type u_1
inst✝² : Group G
A : Type u_2
inst✝¹ : AddGroup A
S : Set G
inst✝ : Finite ↑S
x : G
h : ∀ n ∈ S, x * n * x⁻¹ ∈ S
this✝ : (a : Prop) → Decidable a
val✝ : Fintype ↑S
this : Fintype ↑((fun n => x * n * x⁻¹) '' S)
⊢ x ∈ setNormalizer S

Next tactic:
exact fun n =>
      ⟨h n, fun h₁ =>
        have heq : (fun n => x * n * x⁻¹) '' S = S :=
          Set.eq_of_subset_of_card_le (fun n ⟨y, hy⟩ => hy.2 ▸ h y hy.1)
            (by rw [Set.card_image_of_injective S conj_injective])
        have : x * n * x⁻¹ ∈ (fun n => x * n * x⁻¹) '' S := heq.symm ▸ h₁
        let ⟨y, hy⟩ := this
        conj_injective hy.2 ▸ hy.1⟩
theorem mem_normalizer_fintype {S : Set G} [Finite S] {x : G} (h : ∀ n, n ∈ S → x * n * x⁻¹ ∈ S) :
    x ∈ Subgroup.setNormalizer S := by
  haveI := Classical.propDecidable; cases nonempty_fintype S;
      haveI := Set.fintypeImage S fun n => x * n * x⁻¹;
    exact fun n =>
      ⟨h n, fun h₁ =>
        have heq : (fun n => x * n * x⁻¹) '' S = S :=
          Set.eq_of_subset_of_card_le (fun n ⟨y, hy⟩ => hy.2 ▸ h y hy.1)
            (by 

Proof state:
G : Type u_1
inst✝² : Group G
A : Type u_2
inst✝¹ : AddGroup A
S : Set G
inst✝ : Finite ↑S
x : G
h : ∀ n ∈ S, x * n * x⁻¹ ∈ S
this✝ : (a : Prop) → Decidable a
val✝ : Fintype ↑S
this : Fintype ↑((fun n => x * n * x⁻¹) '' S)
n : G
h₁ : x * n * x⁻¹ ∈ S
⊢ Fintype.card ↑S ≤ Fintype.card ↑((fun n => x * n * x⁻¹) '' S)

Next tactic:
rw [Set.card_image_of_injective S conj_injective]
theorem bex_cons (p : α → Prop) (a : α) (l : List α) : (∃ x ∈ a :: l, p x) ↔ p a ∨ ∃ x ∈ l, p x :=
  ⟨fun ⟨x, h, px⟩ => by
    

Proof state:
α : Type u
β : Type v
γ : Type w
p : α → Prop
a : α
l : List α
x✝ : ∃ x, x ∈ a :: l ∧ p x
x : α
h : x ∈ a :: l
px : p x
⊢ p a ∨ ∃ x, x ∈ l ∧ p x

Next tactic:
simp only [find?, mem_cons] at h
theorem bex_cons (p : α → Prop) (a : α) (l : List α) : (∃ x ∈ a :: l, p x) ↔ p a ∨ ∃ x ∈ l, p x :=
  ⟨fun ⟨x, h, px⟩ => by
    simp only [find?, mem_cons] at h
    

Proof state:
α : Type u
β : Type v
γ : Type w
p : α → Prop
a : α
l : List α
x✝ : ∃ x, x ∈ a :: l ∧ p x
x : α
px : p x
h : x = a ∨ x ∈ l
⊢ p a ∨ ∃ x, x ∈ l ∧ p x

Next tactic:
cases' h with h h
theorem bex_cons (p : α → Prop) (a : α) (l : List α) : (∃ x ∈ a :: l, p x) ↔ p a ∨ ∃ x ∈ l, p x :=
  ⟨fun ⟨x, h, px⟩ => by
    simp only [find?, mem_cons] at h
    cases' h with h h
    · 

Proof state:
case inl
α : Type u
β : Type v
γ : Type w
p : α → Prop
a : α
l : List α
x✝ : ∃ x, x ∈ a :: l ∧ p x
x : α
px : p x
h : x = a
⊢ p a ∨ ∃ x, x ∈ l ∧ p x

Next tactic:
cases h
theorem bex_cons (p : α → Prop) (a : α) (l : List α) : (∃ x ∈ a :: l, p x) ↔ p a ∨ ∃ x ∈ l, p x :=
  ⟨fun ⟨x, h, px⟩ => by
    simp only [find?, mem_cons] at h
    cases' h with h h
    · cases h; 

Proof state:
case inl.refl
α : Type u
β : Type v
γ : Type w
p : α → Prop
a : α
l : List α
x✝ : ∃ x, x ∈ a :: l ∧ p x
px : p a
⊢ p a ∨ ∃ x, x ∈ l ∧ p x

Next tactic:
exact Or.inl px
theorem bex_cons (p : α → Prop) (a : α) (l : List α) : (∃ x ∈ a :: l, p x) ↔ p a ∨ ∃ x ∈ l, p x :=
  ⟨fun ⟨x, h, px⟩ => by
    simp only [find?, mem_cons] at h
    cases' h with h h
    · cases h; exact Or.inl px;
    · 

Proof state:
case inr
α : Type u
β : Type v
γ : Type w
p : α → Prop
a : α
l : List α
x✝ : ∃ x, x ∈ a :: l ∧ p x
x : α
px : p x
h : x ∈ l
⊢ p a ∨ ∃ x, x ∈ l ∧ p x

Next tactic:
exact Or.inr ⟨x, h, px⟩
/-- ## The chain complex relation `d ≫ d`
-/
theorem d_squared (n : ℕ) : objD X (n + 1) ≫ objD X n = 0 := by
  -- we start by expanding d ≫ d as a double sum
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Preadditive C
X Y : SimplicialObject C
n : ℕ
⊢ objD X (n + 1) ≫ objD X n = 0

Next tactic:
dsimp
/-- ## The chain complex relation `d ≫ d`
-/
theorem d_squared (n : ℕ) : objD X (n + 1) ≫ objD X n = 0 := by
  -- we start by expanding d ≫ d as a double sum
  dsimp
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Preadditive C
X Y : SimplicialObject C
n : ℕ
⊢ (∑ i : Fin (n + 1 + 2), (-1) ^ ↑i • SimplicialObject.δ X i) ≫ ∑ i : Fin (n + 2), (-1) ^ ↑i • SimplicialObject.δ X i =
    0

Next tactic:
simp only [comp_sum, sum_comp, ← Finset.sum_product']
/-- ## The chain complex relation `d ≫ d`
-/
theorem d_squared (n : ℕ) : objD X (n + 1) ≫ objD X n = 0 := by
  -- we start by expanding d ≫ d as a double sum
  dsimp
  simp only [comp_sum, sum_comp, ← Finset.sum_product']
  -- then, we decompose the index set P into a subset S and its complement Sᶜ
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Preadditive C
X Y : SimplicialObject C
n : ℕ
⊢ ∑ x in Finset.univ ×ˢ Finset.univ,
      ((-1) ^ ↑x.2 • SimplicialObject.δ X x.2) ≫ ((-1) ^ ↑x.1 • SimplicialObject.δ X x.1) =
    0

Next tactic:
let P := Fin (n + 2) × Fin (n + 3)
/-- ## The chain complex relation `d ≫ d`
-/
theorem d_squared (n : ℕ) : objD X (n + 1) ≫ objD X n = 0 := by
  -- we start by expanding d ≫ d as a double sum
  dsimp
  simp only [comp_sum, sum_comp, ← Finset.sum_product']
  -- then, we decompose the index set P into a subset S and its complement Sᶜ
  let P := Fin (n + 2) × Fin (n + 3)
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Preadditive C
X Y : SimplicialObject C
n : ℕ
P : Type := Fin (n + 2) × Fin (n + 3)
⊢ ∑ x in Finset.univ ×ˢ Finset.univ,
      ((-1) ^ ↑x.2 • SimplicialObject.δ X x.2) ≫ ((-1) ^ ↑x.1 • SimplicialObject.δ X x.1) =
    0

Next tactic:
let S := Finset.univ.filter fun ij : P => (ij.2 : ℕ) ≤ (ij.1 : ℕ)
/-- ## The chain complex relation `d ≫ d`
-/
theorem d_squared (n : ℕ) : objD X (n + 1) ≫ objD X n = 0 := by
  -- we start by expanding d ≫ d as a double sum
  dsimp
  simp only [comp_sum, sum_comp, ← Finset.sum_product']
  -- then, we decompose the index set P into a subset S and its complement Sᶜ
  let P := Fin (n + 2) × Fin (n + 3)
  let S := Finset.univ.filter fun ij : P => (ij.2 : ℕ) ≤ (ij.1 : ℕ)
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Preadditive C
X Y : SimplicialObject C
n : ℕ
P : Type := Fin (n + 2) × Fin (n + 3)
S : Finset P := Finset.filter (fun ij => ↑ij.2 ≤ ↑ij.1) Finset.univ
⊢ ∑ x in Finset.univ ×ˢ Finset.univ,
      ((-1) ^ ↑x.2 • SimplicialObject.δ X x.2) ≫ ((-1) ^ ↑x.1 • SimplicialObject.δ X x.1) =
    0

Next tactic:
erw [← Finset.sum_add_sum_compl S, ← eq_neg_iff_add_eq_zero, ← Finset.sum_neg_distrib]
/-- ## The chain complex relation `d ≫ d`
-/
theorem d_squared (n : ℕ) : objD X (n + 1) ≫ objD X n = 0 := by
  -- we start by expanding d ≫ d as a double sum
  dsimp
  simp only [comp_sum, sum_comp, ← Finset.sum_product']
  -- then, we decompose the index set P into a subset S and its complement Sᶜ
  let P := Fin (n + 2) × Fin (n + 3)
  let S := Finset.univ.filter fun ij : P => (ij.2 : ℕ) ≤ (ij.1 : ℕ)
  erw [← Finset.sum_add_sum_compl S, ← eq_neg_iff_add_eq_zero, ← Finset.sum_neg_distrib]
  /- we are reduced to showing that two sums are equal, and this is obtained
    by constructing a bijection φ : S -> Sᶜ, which maps (i,j) to (j,i+1),
    and by comparing the terms -/
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Preadditive C
X Y : SimplicialObject C
n : ℕ
P : Type := Fin (n + 2) × Fin (n + 3)
S : Finset P := Finset.filter (fun ij => ↑ij.2 ≤ ↑ij.1) Finset.univ
⊢ ∑ i in S, ((-1) ^ ↑i.2 • SimplicialObject.δ X i.2) ≫ ((-1) ^ ↑i.1 • SimplicialObject.δ X i.1) =
    ∑ x in Sᶜ, -((-1) ^ ↑x.2 • SimplicialObject.δ X x.2) ≫ ((-1) ^ ↑x.1 • SimplicialObject.δ X x.1)

Next tactic:
let φ : ∀ ij : P, ij ∈ S → P := fun ij hij =>
    (Fin.castLT ij.2 (lt_of_le_of_lt (Finset.mem_filter.mp hij).right (Fin.is_lt ij.1)), ij.1.succ)
/-- ## The chain complex relation `d ≫ d`
-/
theorem d_squared (n : ℕ) : objD X (n + 1) ≫ objD X n = 0 := by
  -- we start by expanding d ≫ d as a double sum
  dsimp
  simp only [comp_sum, sum_comp, ← Finset.sum_product']
  -- then, we decompose the index set P into a subset S and its complement Sᶜ
  let P := Fin (n + 2) × Fin (n + 3)
  let S := Finset.univ.filter fun ij : P => (ij.2 : ℕ) ≤ (ij.1 : ℕ)
  erw [← Finset.sum_add_sum_compl S, ← eq_neg_iff_add_eq_zero, ← Finset.sum_neg_distrib]
  /- we are reduced to showing that two sums are equal, and this is obtained
    by constructing a bijection φ : S -> Sᶜ, which maps (i,j) to (j,i+1),
    and by comparing the terms -/
  let φ : ∀ ij : P, ij ∈ S → P := fun ij hij =>
    (Fin.castLT ij.2 (lt_of_le_of_lt (Finset.mem_filter.mp hij).right (Fin.is_lt ij.1)), ij.1.succ)
  

Proof state:
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Preadditive C
X Y : SimplicialObject C
n : ℕ
P : Type := Fin (n + 2) × Fin (n + 3)
S : Finset P := Finset.filter (fun ij => ↑ij.2 ≤ ↑ij.1) Finset.univ
φ : (ij : P) → ij ∈ S → P := fun ij hij => (Fin.castLT ij.2 (_ : ↑ij.2 < n + 2), Fin.succ ij.1)
⊢ ∑ i in S, ((-1) ^ ↑i.2 • SimplicialObject.δ X i.2) ≫ ((-1) ^ ↑i.1 • SimplicialObject.δ X i.1) =
    ∑ x in Sᶜ, -((-1) ^ ↑x.2 • SimplicialObject.δ X x.2) ≫ ((-1) ^ ↑x.1 • SimplicialObject.δ X x.1)

Next tactic:
apply Finset.sum_bij φ
/-- ## The chain complex relation `d ≫ d`
-/
theorem d_squared (n : ℕ) : objD X (n + 1) ≫ objD X n = 0 := by
  -- we start by expanding d ≫ d as a double sum
  dsimp
  simp only [comp_sum, sum_comp, ← Finset.sum_product']
  -- then, we decompose the index set P into a subset S and its complement Sᶜ
  let P := Fin (n + 2) × Fin (n + 3)
  let S := Finset.univ.filter fun ij : P => (ij.2 : ℕ) ≤ (ij.1 : ℕ)
  erw [← Finset.sum_add_sum_compl S, ← eq_neg_iff_add_eq_zero, ← Finset.sum_neg_distrib]
  /- we are reduced to showing that two sums are equal, and this is obtained
    by constructing a bijection φ : S -> Sᶜ, which maps (i,j) to (j,i+1),
    and by comparing the terms -/
  let φ : ∀ ij : P, ij ∈ S → P := fun ij hij =>
    (Fin.castLT ij.2 (lt_of_le_of_lt (Finset.mem_filter.mp hij).right (Fin.is_lt ij.1)), ij.1.succ)
  apply Finset.sum_bij φ
  · -- φ(S) is contained in Sᶜ
    

Proof state:
case hi
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Preadditive C
X Y : SimplicialObject C
n : ℕ
P : Type := Fin (n + 2) × Fin (n + 3)
S : Finset P := Finset.filter (fun ij => ↑ij.2 ≤ ↑ij.1) Finset.univ
φ : (ij : P) → ij ∈ S → P := fun ij hij => (Fin.castLT ij.2 (_ : ↑ij.2 < n + 2), Fin.succ ij.1)
⊢ ∀ (a : P) (ha : a ∈ S), φ a ha ∈ Sᶜ

Next tactic:
intro ij hij
/-- ## The chain complex relation `d ≫ d`
-/
theorem d_squared (n : ℕ) : objD X (n + 1) ≫ objD X n = 0 := by
  -- we start by expanding d ≫ d as a double sum
  dsimp
  simp only [comp_sum, sum_comp, ← Finset.sum_product']
  -- then, we decompose the index set P into a subset S and its complement Sᶜ
  let P := Fin (n + 2) × Fin (n + 3)
  let S := Finset.univ.filter fun ij : P => (ij.2 : ℕ) ≤ (ij.1 : ℕ)
  erw [← Finset.sum_add_sum_compl S, ← eq_neg_iff_add_eq_zero, ← Finset.sum_neg_distrib]
  /- we are reduced to showing that two sums are equal, and this is obtained
    by constructing a bijection φ : S -> Sᶜ, which maps (i,j) to (j,i+1),
    and by comparing the terms -/
  let φ : ∀ ij : P, ij ∈ S → P := fun ij hij =>
    (Fin.castLT ij.2 (lt_of_le_of_lt (Finset.mem_filter.mp hij).right (Fin.is_lt ij.1)), ij.1.succ)
  apply Finset.sum_bij φ
  · -- φ(S) is contained in Sᶜ
    intro ij hij
    

Proof state:
case hi
C : Type u_1
inst✝¹ : Category.{u_2, u_1} C
inst✝ : Preadditive C
X Y : SimplicialObject C
n : ℕ
P : Type := Fin (n + 2) × Fin (n + 3)
S : Finset P := Finset.filter (fun ij => ↑ij.2 ≤ ↑ij.1) Finset.univ
φ : (ij : P) → ij ∈ S → P := fun ij hij => (Fin.castLT ij.2 (_ : ↑ij.2 < n + 2), Fin.succ ij.1)
ij : P
hij : ij ∈ S
⊢ φ ij hij ∈ Sᶜ

Next tactic:
simp only [Finset.mem_univ, Finset.compl_filter, Finset.mem_filter, true_and_iff, Fin.val_succ,
      Fin.coe_castLT] at hij ⊢