l3lab/ntp-mathlib huggingface dataset:
theorem mem_perms_of_finset_iff :
    ∀ {s : Finset α} {f : Perm α}, f ∈ permsOfFinset s ↔ ∀ {x}, f x ≠ x → x ∈ s := by
  rintro ⟨⟨l⟩, hs⟩ f; 

Proof state:
case mk.mk
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝¹ : DecidableEq α
inst✝ : DecidableEq β
val✝ : Multiset α
l : List α
hs : Multiset.Nodup (Quot.mk Setoid.r l)
f : Equiv.Perm α
⊢ f ∈ permsOfFinset { val := Quot.mk Setoid.r l, nodup := hs } ↔
    ∀ {x : α}, f x ≠ x → x ∈ { val := Quot.mk Setoid.r l, nodup := hs }

Next tactic:
exact mem_permsOfList_iff
theorem card_perms_of_finset : ∀ s : Finset α, (permsOfFinset s).card = s.card ! := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝¹ : DecidableEq α
inst✝ : DecidableEq β
⊢ ∀ (s : Finset α), card (permsOfFinset s) = (card s)!

Next tactic:
rintro ⟨⟨l⟩, hs⟩
theorem card_perms_of_finset : ∀ s : Finset α, (permsOfFinset s).card = s.card ! := by
  rintro ⟨⟨l⟩, hs⟩; 

Proof state:
case mk.mk
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝¹ : DecidableEq α
inst✝ : DecidableEq β
val✝ : Multiset α
l : List α
hs : Multiset.Nodup (Quot.mk Setoid.r l)
⊢ card (permsOfFinset { val := Quot.mk Setoid.r l, nodup := hs }) = (card { val := Quot.mk Setoid.r l, nodup := hs })!

Next tactic:
exact length_permsOfList l
/-- The collection of permutations of a fintype is a fintype. -/
def fintypePerm [Fintype α] : Fintype (Perm α) :=
  ⟨permsOfFinset (@Finset.univ α _), by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
inst✝² : DecidableEq α
inst✝¹ : DecidableEq β
inst✝ : Fintype α
⊢ ∀ (x : Equiv.Perm α), x ∈ permsOfFinset univ

Next tactic:
simp [mem_perms_of_finset_iff]
/-- Box product of simple graphs. It relates `(a₁, b)` and `(a₂, b)` if `G` relates `a₁` and `a₂`,
and `(a, b₁)` and `(a, b₂)` if `H` relates `b₁` and `b₂`. -/
def boxProd (G : SimpleGraph α) (H : SimpleGraph β) : SimpleGraph (α × β) where
  Adj x y := G.Adj x.1 y.1 ∧ x.2 = y.2 ∨ H.Adj x.2 y.2 ∧ x.1 = y.1
  symm x y := by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G✝ : SimpleGraph α
H✝ : SimpleGraph β
G : SimpleGraph α
H : SimpleGraph β
x y : α × β
⊢ (fun x y => Adj G x.1 y.1 ∧ x.2 = y.2 ∨ Adj H x.2 y.2 ∧ x.1 = y.1) x y →
    (fun x y => Adj G x.1 y.1 ∧ x.2 = y.2 ∨ Adj H x.2 y.2 ∧ x.1 = y.1) y x

Next tactic:
simp [and_comm, or_comm, eq_comm, adj_comm]
/-- Box product of simple graphs. It relates `(a₁, b)` and `(a₂, b)` if `G` relates `a₁` and `a₂`,
and `(a, b₁)` and `(a, b₂)` if `H` relates `b₁` and `b₂`. -/
def boxProd (G : SimpleGraph α) (H : SimpleGraph β) : SimpleGraph (α × β) where
  Adj x y := G.Adj x.1 y.1 ∧ x.2 = y.2 ∨ H.Adj x.2 y.2 ∧ x.1 = y.1
  symm x y := by simp [and_comm, or_comm, eq_comm, adj_comm]
  loopless x := by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G✝ : SimpleGraph α
H✝ : SimpleGraph β
G : SimpleGraph α
H : SimpleGraph β
x : α × β
⊢ ¬(fun x y => Adj G x.1 y.1 ∧ x.2 = y.2 ∨ Adj H x.2 y.2 ∧ x.1 = y.1) x x

Next tactic:
simp
theorem boxProd_adj_left : (G □ H).Adj (a₁, b) (a₂, b) ↔ G.Adj a₁ a₂ := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
a₁ : α
b : β
a₂ : α
⊢ Adj (G □ H) (a₁, b) (a₂, b) ↔ Adj G a₁ a₂

Next tactic:
simp only [boxProd_adj, and_true, SimpleGraph.irrefl, false_and, or_false]
theorem boxProd_adj_right : (G □ H).Adj (a, b₁) (a, b₂) ↔ H.Adj b₁ b₂ := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
a : α
b₁ b₂ : β
⊢ Adj (G □ H) (a, b₁) (a, b₂) ↔ Adj H b₁ b₂

Next tactic:
simp only [boxProd_adj, SimpleGraph.irrefl, false_and, and_true, false_or]
theorem boxProd_neighborSet (x : α × β) :
    (G □ H).neighborSet x = G.neighborSet x.1 ×ˢ {x.2} ∪ {x.1} ×ˢ H.neighborSet x.2 := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
x : α × β
⊢ neighborSet (G □ H) x = neighborSet G x.1 ×ˢ {x.2} ∪ {x.1} ×ˢ neighborSet H x.2

Next tactic:
ext ⟨a', b'⟩
theorem boxProd_neighborSet (x : α × β) :
    (G □ H).neighborSet x = G.neighborSet x.1 ×ˢ {x.2} ∪ {x.1} ×ˢ H.neighborSet x.2 := by
  ext ⟨a', b'⟩
  

Proof state:
case h.mk
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
x : α × β
a' : α
b' : β
⊢ (a', b') ∈ neighborSet (G □ H) x ↔ (a', b') ∈ neighborSet G x.1 ×ˢ {x.2} ∪ {x.1} ×ˢ neighborSet H x.2

Next tactic:
simp only [mem_neighborSet, Set.mem_union, boxProd_adj, Set.mem_prod, Set.mem_singleton_iff]
theorem boxProd_neighborSet (x : α × β) :
    (G □ H).neighborSet x = G.neighborSet x.1 ×ˢ {x.2} ∪ {x.1} ×ˢ H.neighborSet x.2 := by
  ext ⟨a', b'⟩
  simp only [mem_neighborSet, Set.mem_union, boxProd_adj, Set.mem_prod, Set.mem_singleton_iff]
  

Proof state:
case h.mk
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
x : α × β
a' : α
b' : β
⊢ Adj G x.1 a' ∧ x.2 = b' ∨ Adj H x.2 b' ∧ x.1 = a' ↔ Adj G x.1 a' ∧ b' = x.2 ∨ a' = x.1 ∧ Adj H x.2 b'

Next tactic:
simp only [eq_comm, and_comm]
/-- The box product is associative up to isomorphism. `Equiv.prodAssoc` as a graph isomorphism. -/
@[simps!]
def boxProdAssoc (I : SimpleGraph γ) : G □ H □ I ≃g G □ (H □ I) :=
  ⟨Equiv.prodAssoc _ _ _, fun {x y} => by
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
I : SimpleGraph γ
x y : (α × β) × γ
⊢ Adj (G □ (H □ I)) ((Equiv.prodAssoc α β γ) x) ((Equiv.prodAssoc α β γ) y) ↔ Adj (G □ H □ I) x y

Next tactic:
simp only [boxProd_adj, Equiv.prodAssoc_apply, or_and_right, or_assoc, Prod.ext_iff,
      and_assoc, @and_comm (x.fst.fst = _)]
@[simp]
theorem ofBoxProdLeft_boxProdLeft [DecidableEq β] [DecidableRel G.Adj] {a₁ a₂ : α} :
    ∀ (w : G.Walk a₁ a₂), (w.boxProdLeft H b).ofBoxProdLeft = w
  | nil => rfl
  | cons' x y z h w => by
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
b : β
inst✝¹ : DecidableEq β
inst✝ : DecidableRel G.Adj
a₁ a₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ ofBoxProdLeft (Walk.boxProdLeft H b (cons' x y z h w)) = cons' x y z h w

Next tactic:
rw [Walk.boxProdLeft, map_cons, ofBoxProdLeft, Or.by_cases, dif_pos, ← Walk.boxProdLeft]
@[simp]
theorem ofBoxProdLeft_boxProdLeft [DecidableEq β] [DecidableRel G.Adj] {a₁ a₂ : α} :
    ∀ (w : G.Walk a₁ a₂), (w.boxProdLeft H b).ofBoxProdLeft = w
  | nil => rfl
  | cons' x y z h w => by
    rw [Walk.boxProdLeft, map_cons, ofBoxProdLeft, Or.by_cases, dif_pos, ← Walk.boxProdLeft]
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
b : β
inst✝¹ : DecidableEq β
inst✝ : DecidableRel G.Adj
a₁ a₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ cons (_ : Adj G (x, b).1 ((Embedding.toHom (boxProdLeft G H b)) y).1) (ofBoxProdLeft (Walk.boxProdLeft H b w)) =
    cons' x y z h w
case hc
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
b : β
inst✝¹ : DecidableEq β
inst✝ : DecidableRel G.Adj
a₁ a₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ Adj G (x, b).1 ((Embedding.toHom (boxProdLeft G H b)) y).1 ∧ (x, b).2 = ((Embedding.toHom (boxProdLeft G H b)) y).2
case hc
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
b : β
inst✝¹ : DecidableEq β
inst✝ : DecidableRel G.Adj
a₁ a₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ Adj G (x, b).1 ((Embedding.toHom (boxProdLeft G H b)) y).1 ∧ (x, b).2 = ((Embedding.toHom (boxProdLeft G H b)) y).2

Next tactic:
simp [ofBoxProdLeft_boxProdLeft]
@[simp]
theorem ofBoxProdLeft_boxProdLeft [DecidableEq β] [DecidableRel G.Adj] {a₁ a₂ : α} :
    ∀ (w : G.Walk a₁ a₂), (w.boxProdLeft H b).ofBoxProdLeft = w
  | nil => rfl
  | cons' x y z h w => by
    rw [Walk.boxProdLeft, map_cons, ofBoxProdLeft, Or.by_cases, dif_pos, ← Walk.boxProdLeft]
    simp [ofBoxProdLeft_boxProdLeft]
    

Proof state:
case hc
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
b : β
inst✝¹ : DecidableEq β
inst✝ : DecidableRel G.Adj
a₁ a₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ Adj G (x, b).1 ((Embedding.toHom (boxProdLeft G H b)) y).1 ∧ (x, b).2 = ((Embedding.toHom (boxProdLeft G H b)) y).2
case hc
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
b : β
inst✝¹ : DecidableEq β
inst✝ : DecidableRel G.Adj
a₁ a₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ Adj G (x, b).1 ((Embedding.toHom (boxProdLeft G H b)) y).1 ∧ (x, b).2 = ((Embedding.toHom (boxProdLeft G H b)) y).2

Next tactic:
exact ⟨h, rfl⟩
@[simp]
theorem ofBoxProdLeft_boxProdRight [DecidableEq α] [DecidableRel G.Adj] {b₁ b₂ : α} :
    ∀ (w : G.Walk b₁ b₂), (w.boxProdRight G a).ofBoxProdRight = w
  | nil => rfl
  | cons' x y z h w => by
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
a : α
inst✝¹ : DecidableEq α
inst✝ : DecidableRel G.Adj
b₁ b₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ ofBoxProdRight (Walk.boxProdRight G a (cons' x y z h w)) = cons' x y z h w

Next tactic:
rw [Walk.boxProdRight, map_cons, ofBoxProdRight, Or.by_cases, dif_pos, ←
      Walk.boxProdRight]
@[simp]
theorem ofBoxProdLeft_boxProdRight [DecidableEq α] [DecidableRel G.Adj] {b₁ b₂ : α} :
    ∀ (w : G.Walk b₁ b₂), (w.boxProdRight G a).ofBoxProdRight = w
  | nil => rfl
  | cons' x y z h w => by
    rw [Walk.boxProdRight, map_cons, ofBoxProdRight, Or.by_cases, dif_pos, ←
      Walk.boxProdRight]
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
a : α
inst✝¹ : DecidableEq α
inst✝ : DecidableRel G.Adj
b₁ b₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ cons (_ : Adj G (a, x).2 ((Embedding.toHom (boxProdRight G G a)) y).2) (ofBoxProdRight (Walk.boxProdRight G a w)) =
    cons' x y z h w
case hc
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
a : α
inst✝¹ : DecidableEq α
inst✝ : DecidableRel G.Adj
b₁ b₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ Adj G (a, x).2 ((Embedding.toHom (boxProdRight G G a)) y).2 ∧ (a, x).1 = ((Embedding.toHom (boxProdRight G G a)) y).1
case hc
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
a : α
inst✝¹ : DecidableEq α
inst✝ : DecidableRel G.Adj
b₁ b₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ Adj G (a, x).2 ((Embedding.toHom (boxProdRight G G a)) y).2 ∧ (a, x).1 = ((Embedding.toHom (boxProdRight G G a)) y).1

Next tactic:
simp [ofBoxProdLeft_boxProdRight]
@[simp]
theorem ofBoxProdLeft_boxProdRight [DecidableEq α] [DecidableRel G.Adj] {b₁ b₂ : α} :
    ∀ (w : G.Walk b₁ b₂), (w.boxProdRight G a).ofBoxProdRight = w
  | nil => rfl
  | cons' x y z h w => by
    rw [Walk.boxProdRight, map_cons, ofBoxProdRight, Or.by_cases, dif_pos, ←
      Walk.boxProdRight]
    simp [ofBoxProdLeft_boxProdRight]
    

Proof state:
case hc
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
a : α
inst✝¹ : DecidableEq α
inst✝ : DecidableRel G.Adj
b₁ b₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ Adj G (a, x).2 ((Embedding.toHom (boxProdRight G G a)) y).2 ∧ (a, x).1 = ((Embedding.toHom (boxProdRight G G a)) y).1
case hc
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
a : α
inst✝¹ : DecidableEq α
inst✝ : DecidableRel G.Adj
b₁ b₂ x z y : α
h : Adj G x y
w : Walk G y z
⊢ Adj G (a, x).2 ((Embedding.toHom (boxProdRight G G a)) y).2 ∧ (a, x).1 = ((Embedding.toHom (boxProdRight G G a)) y).1

Next tactic:
exact ⟨h, rfl⟩
protected theorem Preconnected.boxProd (hG : G.Preconnected) (hH : H.Preconnected) :
    (G □ H).Preconnected := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
hG : Preconnected G
hH : Preconnected H
⊢ Preconnected (G □ H)

Next tactic:
rintro x y
protected theorem Preconnected.boxProd (hG : G.Preconnected) (hH : H.Preconnected) :
    (G □ H).Preconnected := by
  rintro x y
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
hG : Preconnected G
hH : Preconnected H
x y : α × β
⊢ Reachable (G □ H) x y

Next tactic:
obtain ⟨w₁⟩ := hG x.1 y.1
protected theorem Preconnected.boxProd (hG : G.Preconnected) (hH : H.Preconnected) :
    (G □ H).Preconnected := by
  rintro x y
  obtain ⟨w₁⟩ := hG x.1 y.1
  

Proof state:
case intro
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
hG : Preconnected G
hH : Preconnected H
x y : α × β
w₁ : Walk G x.1 y.1
⊢ Reachable (G □ H) x y

Next tactic:
obtain ⟨w₂⟩ := hH x.2 y.2
protected theorem Preconnected.boxProd (hG : G.Preconnected) (hH : H.Preconnected) :
    (G □ H).Preconnected := by
  rintro x y
  obtain ⟨w₁⟩ := hG x.1 y.1
  obtain ⟨w₂⟩ := hH x.2 y.2
  

Proof state:
case intro.intro
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
hG : Preconnected G
hH : Preconnected H
x y : α × β
w₁ : Walk G x.1 y.1
w₂ : Walk H x.2 y.2
⊢ Reachable (G □ H) x y

Next tactic:
exact ⟨(w₁.boxProdLeft _ _).append (w₂.boxProdRight _ _)⟩
protected theorem Preconnected.ofBoxProdLeft [Nonempty β] (h : (G □ H).Preconnected) :
    G.Preconnected := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
inst✝ : Nonempty β
h : Preconnected (G □ H)
⊢ Preconnected G

Next tactic:
classical
  rintro a₁ a₂
  obtain ⟨w⟩ := h (a₁, Classical.arbitrary _) (a₂, Classical.arbitrary _)
  exact ⟨w.ofBoxProdLeft⟩
protected theorem Preconnected.ofBoxProdLeft [Nonempty β] (h : (G □ H).Preconnected) :
    G.Preconnected := by
  classical
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
inst✝ : Nonempty β
h : Preconnected (G □ H)
⊢ Preconnected G

Next tactic:
rintro a₁ a₂
protected theorem Preconnected.ofBoxProdLeft [Nonempty β] (h : (G □ H).Preconnected) :
    G.Preconnected := by
  classical
  rintro a₁ a₂
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
inst✝ : Nonempty β
h : Preconnected (G □ H)
a₁ a₂ : α
⊢ Reachable G a₁ a₂

Next tactic:
obtain ⟨w⟩ := h (a₁, Classical.arbitrary _) (a₂, Classical.arbitrary _)
protected theorem Preconnected.ofBoxProdLeft [Nonempty β] (h : (G □ H).Preconnected) :
    G.Preconnected := by
  classical
  rintro a₁ a₂
  obtain ⟨w⟩ := h (a₁, Classical.arbitrary _) (a₂, Classical.arbitrary _)
  

Proof state:
case intro
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
inst✝ : Nonempty β
h : Preconnected (G □ H)
a₁ a₂ : α
w : Walk (G □ H) (a₁, Classical.arbitrary β) (a₂, Classical.arbitrary β)
⊢ Reachable G a₁ a₂

Next tactic:
exact ⟨w.ofBoxProdLeft⟩
protected theorem Preconnected.ofBoxProdRight [Nonempty α] (h : (G □ H).Preconnected) :
    H.Preconnected := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
inst✝ : Nonempty α
h : Preconnected (G □ H)
⊢ Preconnected H

Next tactic:
classical
  rintro b₁ b₂
  obtain ⟨w⟩ := h (Classical.arbitrary _, b₁) (Classical.arbitrary _, b₂)
  exact ⟨w.ofBoxProdRight⟩
protected theorem Preconnected.ofBoxProdRight [Nonempty α] (h : (G □ H).Preconnected) :
    H.Preconnected := by
  classical
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
inst✝ : Nonempty α
h : Preconnected (G □ H)
⊢ Preconnected H

Next tactic:
rintro b₁ b₂
protected theorem Preconnected.ofBoxProdRight [Nonempty α] (h : (G □ H).Preconnected) :
    H.Preconnected := by
  classical
  rintro b₁ b₂
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
inst✝ : Nonempty α
h : Preconnected (G □ H)
b₁ b₂ : β
⊢ Reachable H b₁ b₂

Next tactic:
obtain ⟨w⟩ := h (Classical.arbitrary _, b₁) (Classical.arbitrary _, b₂)
protected theorem Preconnected.ofBoxProdRight [Nonempty α] (h : (G □ H).Preconnected) :
    H.Preconnected := by
  classical
  rintro b₁ b₂
  obtain ⟨w⟩ := h (Classical.arbitrary _, b₁) (Classical.arbitrary _, b₂)
  

Proof state:
case intro
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
inst✝ : Nonempty α
h : Preconnected (G □ H)
b₁ b₂ : β
w : Walk (G □ H) (Classical.arbitrary α, b₁) (Classical.arbitrary α, b₂)
⊢ Reachable H b₁ b₂

Next tactic:
exact ⟨w.ofBoxProdRight⟩
protected theorem Connected.boxProd (hG : G.Connected) (hH : H.Connected) : (G □ H).Connected := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
hG : Connected G
hH : Connected H
⊢ Connected (G □ H)

Next tactic:
haveI := hG.nonempty
protected theorem Connected.boxProd (hG : G.Connected) (hH : H.Connected) : (G □ H).Connected := by
  haveI := hG.nonempty
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
hG : Connected G
hH : Connected H
this : Nonempty α
⊢ Connected (G □ H)

Next tactic:
haveI := hH.nonempty
protected theorem Connected.boxProd (hG : G.Connected) (hH : H.Connected) : (G □ H).Connected := by
  haveI := hG.nonempty
  haveI := hH.nonempty
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
hG : Connected G
hH : Connected H
this✝ : Nonempty α
this : Nonempty β
⊢ Connected (G □ H)

Next tactic:
exact ⟨hG.preconnected.boxProd hH.preconnected⟩
protected theorem Connected.ofBoxProdLeft (h : (G □ H).Connected) : G.Connected := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
h : Connected (G □ H)
⊢ Connected G

Next tactic:
haveI := (nonempty_prod.1 h.nonempty).1
protected theorem Connected.ofBoxProdLeft (h : (G □ H).Connected) : G.Connected := by
  haveI := (nonempty_prod.1 h.nonempty).1
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
h : Connected (G □ H)
this : Nonempty α
⊢ Connected G

Next tactic:
haveI := (nonempty_prod.1 h.nonempty).2
protected theorem Connected.ofBoxProdLeft (h : (G □ H).Connected) : G.Connected := by
  haveI := (nonempty_prod.1 h.nonempty).1
  haveI := (nonempty_prod.1 h.nonempty).2
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
h : Connected (G □ H)
this✝ : Nonempty α
this : Nonempty β
⊢ Connected G

Next tactic:
exact ⟨h.preconnected.ofBoxProdLeft⟩
protected theorem Connected.ofBoxProdRight (h : (G □ H).Connected) : H.Connected := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
h : Connected (G □ H)
⊢ Connected H

Next tactic:
haveI := (nonempty_prod.1 h.nonempty).1
protected theorem Connected.ofBoxProdRight (h : (G □ H).Connected) : H.Connected := by
  haveI := (nonempty_prod.1 h.nonempty).1
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
h : Connected (G □ H)
this : Nonempty α
⊢ Connected H

Next tactic:
haveI := (nonempty_prod.1 h.nonempty).2
protected theorem Connected.ofBoxProdRight (h : (G □ H).Connected) : H.Connected := by
  haveI := (nonempty_prod.1 h.nonempty).1
  haveI := (nonempty_prod.1 h.nonempty).2
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
h : Connected (G □ H)
this✝ : Nonempty α
this : Nonempty β
⊢ Connected H

Next tactic:
exact ⟨h.preconnected.ofBoxProdRight⟩
instance boxProdFintypeNeighborSet (x : α × β)
    [Fintype (G.neighborSet x.1)] [Fintype (H.neighborSet x.2)] :
    Fintype ((G □ H).neighborSet x) :=
  Fintype.ofEquiv
    ((G.neighborFinset x.1 ×ˢ {x.2}).disjUnion ({x.1} ×ˢ H.neighborFinset x.2) <|
        Finset.disjoint_product.mpr <| Or.inl <| neighborFinset_disjoint_singleton _ _)
    ((Equiv.refl _).subtypeEquiv fun y => by
      

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
x : α × β
inst✝¹ : Fintype ↑(neighborSet G x.1)
inst✝ : Fintype ↑(neighborSet H x.2)
y : α × β
⊢ y ∈
      Finset.disjUnion (neighborFinset G x.1 ×ˢ {x.2}) ({x.1} ×ˢ neighborFinset H x.2)
        (_ : Disjoint (neighborFinset G x.1 ×ˢ {x.2}) ({x.1} ×ˢ neighborFinset H x.2)) ↔
    (Equiv.refl (α × β)) y ∈ neighborSet (G □ H) x

Next tactic:
simp_rw [Finset.mem_disjUnion, Finset.mem_product, Finset.mem_singleton, mem_neighborFinset,
        mem_neighborSet, Equiv.refl_apply, boxProd_adj]
instance boxProdFintypeNeighborSet (x : α × β)
    [Fintype (G.neighborSet x.1)] [Fintype (H.neighborSet x.2)] :
    Fintype ((G □ H).neighborSet x) :=
  Fintype.ofEquiv
    ((G.neighborFinset x.1 ×ˢ {x.2}).disjUnion ({x.1} ×ˢ H.neighborFinset x.2) <|
        Finset.disjoint_product.mpr <| Or.inl <| neighborFinset_disjoint_singleton _ _)
    ((Equiv.refl _).subtypeEquiv fun y => by
      simp_rw [Finset.mem_disjUnion, Finset.mem_product, Finset.mem_singleton, mem_neighborFinset,
        mem_neighborSet, Equiv.refl_apply, boxProd_adj]
      

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
x : α × β
inst✝¹ : Fintype ↑(neighborSet G x.1)
inst✝ : Fintype ↑(neighborSet H x.2)
y : α × β
⊢ Adj G x.1 y.1 ∧ y.2 = x.2 ∨ y.1 = x.1 ∧ Adj H x.2 y.2 ↔ Adj G x.1 y.1 ∧ x.2 = y.2 ∨ Adj H x.2 y.2 ∧ x.1 = y.1

Next tactic:
simp only [eq_comm, and_comm]
theorem boxProd_neighborFinset (x : α × β)
    [Fintype (G.neighborSet x.1)] [Fintype (H.neighborSet x.2)] [Fintype ((G □ H).neighborSet x)] :
    (G □ H).neighborFinset x =
      (G.neighborFinset x.1 ×ˢ {x.2}).disjUnion ({x.1} ×ˢ H.neighborFinset x.2)
        (Finset.disjoint_product.mpr <| Or.inl <| neighborFinset_disjoint_singleton _ _) := by
  -- swap out the fintype instance for the canonical one
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
x : α × β
inst✝² : Fintype ↑(neighborSet G x.1)
inst✝¹ : Fintype ↑(neighborSet H x.2)
inst✝ : Fintype ↑(neighborSet (G □ H) x)
⊢ neighborFinset (G □ H) x =
    Finset.disjUnion (neighborFinset G x.1 ×ˢ {x.2}) ({x.1} ×ˢ neighborFinset H x.2)
      (_ : Disjoint (neighborFinset G x.1 ×ˢ {x.2}) ({x.1} ×ˢ neighborFinset H x.2))

Next tactic:
letI : Fintype ((G □ H).neighborSet x) := SimpleGraph.boxProdFintypeNeighborSet _
theorem boxProd_neighborFinset (x : α × β)
    [Fintype (G.neighborSet x.1)] [Fintype (H.neighborSet x.2)] [Fintype ((G □ H).neighborSet x)] :
    (G □ H).neighborFinset x =
      (G.neighborFinset x.1 ×ˢ {x.2}).disjUnion ({x.1} ×ˢ H.neighborFinset x.2)
        (Finset.disjoint_product.mpr <| Or.inl <| neighborFinset_disjoint_singleton _ _) := by
  -- swap out the fintype instance for the canonical one
  letI : Fintype ((G □ H).neighborSet x) := SimpleGraph.boxProdFintypeNeighborSet _
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
x : α × β
inst✝² : Fintype ↑(neighborSet G x.1)
inst✝¹ : Fintype ↑(neighborSet H x.2)
inst✝ : Fintype ↑(neighborSet (G □ H) x)
this : Fintype ↑(neighborSet (G □ H) x) := boxProdFintypeNeighborSet x
⊢ neighborFinset (G □ H) x =
    Finset.disjUnion (neighborFinset G x.1 ×ˢ {x.2}) ({x.1} ×ˢ neighborFinset H x.2)
      (_ : Disjoint (neighborFinset G x.1 ×ˢ {x.2}) ({x.1} ×ˢ neighborFinset H x.2))

Next tactic:
convert_to (G □ H).neighborFinset x = _ using 2
theorem boxProd_neighborFinset (x : α × β)
    [Fintype (G.neighborSet x.1)] [Fintype (H.neighborSet x.2)] [Fintype ((G □ H).neighborSet x)] :
    (G □ H).neighborFinset x =
      (G.neighborFinset x.1 ×ˢ {x.2}).disjUnion ({x.1} ×ˢ H.neighborFinset x.2)
        (Finset.disjoint_product.mpr <| Or.inl <| neighborFinset_disjoint_singleton _ _) := by
  -- swap out the fintype instance for the canonical one
  letI : Fintype ((G □ H).neighborSet x) := SimpleGraph.boxProdFintypeNeighborSet _
  convert_to (G □ H).neighborFinset x = _ using 2
  

Proof state:
case convert_2
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
x : α × β
inst✝² : Fintype ↑(neighborSet G x.1)
inst✝¹ : Fintype ↑(neighborSet H x.2)
inst✝ : Fintype ↑(neighborSet (G □ H) x)
this : Fintype ↑(neighborSet (G □ H) x) := boxProdFintypeNeighborSet x
⊢ neighborFinset (G □ H) x =
    Finset.disjUnion (neighborFinset G x.1 ×ˢ {x.2}) ({x.1} ×ˢ neighborFinset H x.2)
      (_ : Disjoint (neighborFinset G x.1 ×ˢ {x.2}) ({x.1} ×ˢ neighborFinset H x.2))

Next tactic:
exact Eq.trans (Finset.map_map _ _ _) Finset.attach_map_val
theorem boxProd_degree (x : α × β)
    [Fintype (G.neighborSet x.1)] [Fintype (H.neighborSet x.2)] [Fintype ((G □ H).neighborSet x)] :
    (G □ H).degree x = G.degree x.1 + H.degree x.2 := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
G : SimpleGraph α
H : SimpleGraph β
x : α × β
inst✝² : Fintype ↑(neighborSet G x.1)
inst✝¹ : Fintype ↑(neighborSet H x.2)
inst✝ : Fintype ↑(neighborSet (G □ H) x)
⊢ degree (G □ H) x = degree G x.1 + degree H x.2

Next tactic:
rw [degree, degree, degree, boxProd_neighborFinset, Finset.card_disjUnion]