l3lab/ntp-mathlib huggingface dataset:
theorem smallSets_eq_generate {f : Filter α} : f.smallSets = generate (powerset '' f.sets) := by
  simp_rw [generate_eq_biInf, smallSets, iInf_image]
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
f : Filter α
⊢ Filter.lift' f powerset = ⨅ b ∈ f.sets, 𝓟 (𝒫 b)

Next tactic:
rfl
@[simp]
lemma tendsto_image_smallSets {f : α → β} :
    Tendsto (f '' ·) la.smallSets lb.smallSets ↔ Tendsto f la lb := by
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
f : α → β
⊢ Tendsto (fun x => f '' x) (smallSets la) (smallSets lb) ↔ Tendsto f la lb

Next tactic:
rw [tendsto_smallSets_iff]
@[simp]
lemma tendsto_image_smallSets {f : α → β} :
    Tendsto (f '' ·) la.smallSets lb.smallSets ↔ Tendsto f la lb := by
  rw [tendsto_smallSets_iff]
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
f : α → β
⊢ (∀ t ∈ lb, ∀ᶠ (x : Set α) in smallSets la, f '' x ⊆ t) ↔ Tendsto f la lb

Next tactic:
refine forall₂_congr fun u hu ↦ ?_
@[simp]
lemma tendsto_image_smallSets {f : α → β} :
    Tendsto (f '' ·) la.smallSets lb.smallSets ↔ Tendsto f la lb := by
  rw [tendsto_smallSets_iff]
  refine forall₂_congr fun u hu ↦ ?_
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
f : α → β
u : Set β
hu : u ∈ lb
⊢ (∀ᶠ (x : Set α) in smallSets la, f '' x ⊆ u) ↔ u ∈ map f la

Next tactic:
rw [eventually_smallSets' fun s t hst ht ↦ (image_subset _ hst).trans ht]
@[simp]
lemma tendsto_image_smallSets {f : α → β} :
    Tendsto (f '' ·) la.smallSets lb.smallSets ↔ Tendsto f la lb := by
  rw [tendsto_smallSets_iff]
  refine forall₂_congr fun u hu ↦ ?_
  rw [eventually_smallSets' fun s t hst ht ↦ (image_subset _ hst).trans ht]
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
f : α → β
u : Set β
hu : u ∈ lb
⊢ (∃ s ∈ la, f '' s ⊆ u) ↔ u ∈ map f la

Next tactic:
simp only [image_subset_iff, exists_mem_subset_iff, mem_map]
@[simp]
theorem smallSets_bot : (⊥ : Filter α).smallSets = pure ∅ := by
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
⊢ smallSets ⊥ = pure ∅

Next tactic:
rw [smallSets, lift'_bot, powerset_empty, principal_singleton]
@[simp]
theorem smallSets_bot : (⊥ : Filter α).smallSets = pure ∅ := by
  rw [smallSets, lift'_bot, powerset_empty, principal_singleton]
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
⊢ Monotone powerset

Next tactic:
exact monotone_powerset
@[simp]
theorem smallSets_top : (⊤ : Filter α).smallSets = ⊤ := by
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
⊢ smallSets ⊤ = ⊤

Next tactic:
rw [smallSets, lift'_top, powerset_univ, principal_univ]
instance smallSets_neBot (l : Filter α) : NeBot l.smallSets := by
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l✝ l' la : Filter α
lb : Filter β
l : Filter α
⊢ NeBot (smallSets l)

Next tactic:
refine' (lift'_neBot_iff _).2 fun _ _ => powerset_nonempty
instance smallSets_neBot (l : Filter α) : NeBot l.smallSets := by
  refine' (lift'_neBot_iff _).2 fun _ _ => powerset_nonempty
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l✝ l' la : Filter α
lb : Filter β
l : Filter α
⊢ Monotone powerset

Next tactic:
exact monotone_powerset
theorem Tendsto.smallSets_mono {s t : α → Set β} (ht : Tendsto t la lb.smallSets)
    (hst : ∀ᶠ x in la, s x ⊆ t x) : Tendsto s la lb.smallSets := by
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
s t : α → Set β
ht : Tendsto t la (smallSets lb)
hst : ∀ᶠ (x : α) in la, s x ⊆ t x
⊢ Tendsto s la (smallSets lb)

Next tactic:
rw [tendsto_smallSets_iff] at ht ⊢
theorem Tendsto.smallSets_mono {s t : α → Set β} (ht : Tendsto t la lb.smallSets)
    (hst : ∀ᶠ x in la, s x ⊆ t x) : Tendsto s la lb.smallSets := by
  rw [tendsto_smallSets_iff] at ht ⊢
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
s t : α → Set β
ht : ∀ t_1 ∈ lb, ∀ᶠ (x : α) in la, t x ⊆ t_1
hst : ∀ᶠ (x : α) in la, s x ⊆ t x
⊢ ∀ t ∈ lb, ∀ᶠ (x : α) in la, s x ⊆ t

Next tactic:
exact fun u hu => (ht u hu).mp (hst.mono fun _ hst ht => hst.trans ht)
@[simp]
theorem eventually_smallSets_eventually {p : α → Prop} :
    (∀ᶠ s in l.smallSets, ∀ᶠ x in l', x ∈ s → p x) ↔ ∀ᶠ x in l ⊓ l', p x :=
  calc
    _ ↔ ∃ s ∈ l, ∀ᶠ x in l', x ∈ s → p x :=
      eventually_smallSets' fun s t hst ht => ht.mono fun x hx hs => hx (hst hs)
    _ ↔ ∃ s ∈ l, ∃ t ∈ l', ∀ x, x ∈ t → x ∈ s → p x := by 

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
p : α → Prop
⊢ (∃ s ∈ l, ∀ᶠ (x : α) in l', x ∈ s → p x) ↔ ∃ s ∈ l, ∃ t ∈ l', ∀ x ∈ t, x ∈ s → p x

Next tactic:
simp only [eventually_iff_exists_mem]
@[simp]
theorem eventually_smallSets_eventually {p : α → Prop} :
    (∀ᶠ s in l.smallSets, ∀ᶠ x in l', x ∈ s → p x) ↔ ∀ᶠ x in l ⊓ l', p x :=
  calc
    _ ↔ ∃ s ∈ l, ∀ᶠ x in l', x ∈ s → p x :=
      eventually_smallSets' fun s t hst ht => ht.mono fun x hx hs => hx (hst hs)
    _ ↔ ∃ s ∈ l, ∃ t ∈ l', ∀ x, x ∈ t → x ∈ s → p x := by simp only [eventually_iff_exists_mem]
    _ ↔ ∀ᶠ x in l ⊓ l', p x := by 

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
p : α → Prop
⊢ (∃ s ∈ l, ∃ t ∈ l', ∀ x ∈ t, x ∈ s → p x) ↔ ∀ᶠ (x : α) in l ⊓ l', p x

Next tactic:
simp only [eventually_inf, and_comm, mem_inter_iff, ← and_imp]
@[simp]
theorem eventually_smallSets_forall {p : α → Prop} :
    (∀ᶠ s in l.smallSets, ∀ x ∈ s, p x) ↔ ∀ᶠ x in l, p x := by
  

Proof state:
α : Type u_1
β : Type u_2
ι : Sort u_3
l l' la : Filter α
lb : Filter β
p : α → Prop
⊢ (∀ᶠ (s : Set α) in smallSets l, ∀ x ∈ s, p x) ↔ ∀ᶠ (x : α) in l, p x

Next tactic:
simpa only [inf_top_eq, eventually_top] using @eventually_smallSets_eventually α l ⊤ p
@[symm]
theorem Rel.symm {x y : α × α} : Rel α x y → Rel α y x := by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
x y : α × α
⊢ Rel α x y → Rel α y x

Next tactic:
aesop (rule_sets [Sym2])
@[trans]
theorem Rel.trans {x y z : α × α} (a : Rel α x y) (b : Rel α y z) : Rel α x z := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
x y z : α × α
a : Rel α x y
b : Rel α y z
⊢ Rel α x z

Next tactic:
aesop (rule_sets [Sym2])
@[simp]
theorem rel_iff {x y z w : α} : (x, y) ≈ (z, w) ↔ x = z ∧ y = w ∨ x = w ∧ y = z :=
  show Rel _ _ _ ↔ _ by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
x y z w : α
⊢ Rel α (x, y) (z, w) ↔ x = z ∧ y = w ∨ x = w ∧ y = z

Next tactic:
aesop (rule_sets [Sym2])
@[elab_as_elim]
protected theorem inductionOn₂ {f : Sym2 α → Sym2 β → Prop} (i : Sym2 α) (j : Sym2 β)
    (hf : ∀ a₁ a₂ b₁ b₂, f ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) : f i j :=
  Quotient.inductionOn₂ i j <| by
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : Sym2 α → Sym2 β → Prop
i : Sym2 α
j : Sym2 β
hf : ∀ (a₁ a₂ : α) (b₁ b₂ : β), f ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧
⊢ ∀ (a : α × α) (b : β × β), f ⟦a⟧ ⟦b⟧

Next tactic:
intro ⟨a₁, a₂⟩ ⟨b₁, b₂⟩
@[elab_as_elim]
protected theorem inductionOn₂ {f : Sym2 α → Sym2 β → Prop} (i : Sym2 α) (j : Sym2 β)
    (hf : ∀ a₁ a₂ b₁ b₂, f ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) : f i j :=
  Quotient.inductionOn₂ i j <| by
    intro ⟨a₁, a₂⟩ ⟨b₁, b₂⟩
    

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : Sym2 α → Sym2 β → Prop
i : Sym2 α
j : Sym2 β
hf : ∀ (a₁ a₂ : α) (b₁ b₂ : β), f ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧
a₁ a₂ : α
b₁ b₂ : β
⊢ f ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧

Next tactic:
exact hf _ _ _ _
theorem eq_swap {a b : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(b, a)⟧ := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
a b : α
⊢ ⟦(a, b)⟧ = ⟦(b, a)⟧

Next tactic:
rw [Quotient.eq]
theorem eq_swap {a b : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(b, a)⟧ := by
  rw [Quotient.eq]
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
a b : α
⊢ (a, b) ≈ (b, a)

Next tactic:
apply Rel.swap
@[simp]
theorem mk''_prod_swap_eq {p : α × α} : Eq (α := Sym2 α) ⟦p.swap⟧ ⟦p⟧ := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
p : α × α
⊢ ⟦Prod.swap p⟧ = ⟦p⟧

Next tactic:
cases p
@[simp]
theorem mk''_prod_swap_eq {p : α × α} : Eq (α := Sym2 α) ⟦p.swap⟧ ⟦p⟧ := by
  cases p
  

Proof state:
case mk
α : Type u_1
β : Type u_2
γ : Type u_3
fst✝ snd✝ : α
⊢ ⟦Prod.swap (fst✝, snd✝)⟧ = ⟦(fst✝, snd✝)⟧

Next tactic:
exact eq_swap
theorem congr_right {a b c : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(a, c)⟧ ↔ b = c := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
⊢ ⟦(a, b)⟧ = ⟦(a, c)⟧ ↔ b = c

Next tactic:
constructor
theorem congr_right {a b c : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(a, c)⟧ ↔ b = c := by
  constructor <;> 

Proof state:
case mp
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
⊢ ⟦(a, b)⟧ = ⟦(a, c)⟧ → b = c

Next tactic:
intro h
theorem congr_right {a b c : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(a, c)⟧ ↔ b = c := by
  constructor <;> 

Proof state:
case mpr
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
⊢ b = c → ⟦(a, b)⟧ = ⟦(a, c)⟧

Next tactic:
intro h
theorem congr_right {a b c : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(a, c)⟧ ↔ b = c := by
  constructor <;> intro h
  · 

Proof state:
case mp
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
h : ⟦(a, b)⟧ = ⟦(a, c)⟧
⊢ b = c

Next tactic:
rw [Quotient.eq] at h
theorem congr_right {a b c : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(a, c)⟧ ↔ b = c := by
  constructor <;> intro h
  · rw [Quotient.eq] at h
    

Proof state:
case mp
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
h : (a, b) ≈ (a, c)
⊢ b = c

Next tactic:
cases h
theorem congr_right {a b c : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(a, c)⟧ ↔ b = c := by
  constructor <;> intro h
  · rw [Quotient.eq] at h
    cases h <;> 

Proof state:
case mp.refl
α : Type u_1
β : Type u_2
γ : Type u_3
a b : α
⊢ b = b

Next tactic:
rfl
theorem congr_right {a b c : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(a, c)⟧ ↔ b = c := by
  constructor <;> intro h
  · rw [Quotient.eq] at h
    cases h <;> 

Proof state:
case mp.swap
α : Type u_1
β : Type u_2
γ : Type u_3
a : α
⊢ a = a

Next tactic:
rfl
theorem congr_right {a b c : α} : Eq (α := Sym2 α) ⟦(a, b)⟧ ⟦(a, c)⟧ ↔ b = c := by
  constructor <;> intro h
  · rw [Quotient.eq] at h
    cases h <;> rfl
  

Proof state:
case mpr
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
h : b = c
⊢ ⟦(a, b)⟧ = ⟦(a, c)⟧

Next tactic:
rw [h]
theorem congr_left {a b c : α} : Eq (α := Sym2 α) ⟦(b, a)⟧ ⟦(c, a)⟧ ↔ b = c := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
⊢ ⟦(b, a)⟧ = ⟦(c, a)⟧ ↔ b = c

Next tactic:
constructor
theorem congr_left {a b c : α} : Eq (α := Sym2 α) ⟦(b, a)⟧ ⟦(c, a)⟧ ↔ b = c := by
  constructor <;> 

Proof state:
case mp
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
⊢ ⟦(b, a)⟧ = ⟦(c, a)⟧ → b = c

Next tactic:
intro h
theorem congr_left {a b c : α} : Eq (α := Sym2 α) ⟦(b, a)⟧ ⟦(c, a)⟧ ↔ b = c := by
  constructor <;> 

Proof state:
case mpr
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
⊢ b = c → ⟦(b, a)⟧ = ⟦(c, a)⟧

Next tactic:
intro h
theorem congr_left {a b c : α} : Eq (α := Sym2 α) ⟦(b, a)⟧ ⟦(c, a)⟧ ↔ b = c := by
  constructor <;> intro h
  · 

Proof state:
case mp
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
h : ⟦(b, a)⟧ = ⟦(c, a)⟧
⊢ b = c

Next tactic:
rw [Quotient.eq] at h
theorem congr_left {a b c : α} : Eq (α := Sym2 α) ⟦(b, a)⟧ ⟦(c, a)⟧ ↔ b = c := by
  constructor <;> intro h
  · rw [Quotient.eq] at h
    

Proof state:
case mp
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
h : (b, a) ≈ (c, a)
⊢ b = c

Next tactic:
cases h
theorem congr_left {a b c : α} : Eq (α := Sym2 α) ⟦(b, a)⟧ ⟦(c, a)⟧ ↔ b = c := by
  constructor <;> intro h
  · rw [Quotient.eq] at h
    cases h <;> 

Proof state:
case mp.refl
α : Type u_1
β : Type u_2
γ : Type u_3
a b : α
⊢ b = b

Next tactic:
rfl
theorem congr_left {a b c : α} : Eq (α := Sym2 α) ⟦(b, a)⟧ ⟦(c, a)⟧ ↔ b = c := by
  constructor <;> intro h
  · rw [Quotient.eq] at h
    cases h <;> 

Proof state:
case mp.swap
α : Type u_1
β : Type u_2
γ : Type u_3
a : α
⊢ a = a

Next tactic:
rfl
theorem congr_left {a b c : α} : Eq (α := Sym2 α) ⟦(b, a)⟧ ⟦(c, a)⟧ ↔ b = c := by
  constructor <;> intro h
  · rw [Quotient.eq] at h
    cases h <;> rfl
  

Proof state:
case mpr
α : Type u_1
β : Type u_2
γ : Type u_3
a b c : α
h : b = c
⊢ ⟦(b, a)⟧ = ⟦(c, a)⟧

Next tactic:
rw [h]
theorem eq_iff {x y z w : α} : Eq (α := Sym2 α) ⟦(x, y)⟧ ⟦(z, w)⟧ ↔ x = z ∧ y = w ∨ x = w ∧ y = z :=
  by 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
x y z w : α
⊢ ⟦(x, y)⟧ = ⟦(z, w)⟧ ↔ x = z ∧ y = w ∨ x = w ∧ y = z

Next tactic:
simp
theorem mk''_eq_mk''_iff {p q : α × α} : Eq (α := Sym2 α) ⟦p⟧ ⟦q⟧ ↔ p = q ∨ p = q.swap := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
p q : α × α
⊢ ⟦p⟧ = ⟦q⟧ ↔ p = q ∨ p = Prod.swap q

Next tactic:
cases p
theorem mk''_eq_mk''_iff {p q : α × α} : Eq (α := Sym2 α) ⟦p⟧ ⟦q⟧ ↔ p = q ∨ p = q.swap := by
  cases p
  

Proof state:
case mk
α : Type u_1
β : Type u_2
γ : Type u_3
q : α × α
fst✝ snd✝ : α
⊢ ⟦(fst✝, snd✝)⟧ = ⟦q⟧ ↔ (fst✝, snd✝) = q ∨ (fst✝, snd✝) = Prod.swap q

Next tactic:
cases q
theorem mk''_eq_mk''_iff {p q : α × α} : Eq (α := Sym2 α) ⟦p⟧ ⟦q⟧ ↔ p = q ∨ p = q.swap := by
  cases p
  cases q
  

Proof state:
case mk.mk
α : Type u_1
β : Type u_2
γ : Type u_3
fst✝¹ snd✝¹ fst✝ snd✝ : α
⊢ ⟦(fst✝¹, snd✝¹)⟧ = ⟦(fst✝, snd✝)⟧ ↔ (fst✝¹, snd✝¹) = (fst✝, snd✝) ∨ (fst✝¹, snd✝¹) = Prod.swap (fst✝, snd✝)

Next tactic:
simp only [eq_iff, Prod.mk.inj_iff, Prod.swap_prod_mk]
/-- The universal property of `Sym2`; symmetric functions of two arguments are equivalent to
functions from `Sym2`. Note that when `β` is `Prop`, it can sometimes be more convenient to use
`Sym2.fromRel` instead. -/
def lift : { f : α → α → β // ∀ a₁ a₂, f a₁ a₂ = f a₂ a₁ } ≃ (Sym2 α → β)
    where
  toFun f :=
    Quotient.lift (uncurry ↑f) <| by
      

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : { f // ∀ (a₁ a₂ : α), f a₁ a₂ = f a₂ a₁ }
⊢ ∀ (a b : α × α), a ≈ b → uncurry (↑f) a = uncurry (↑f) b

Next tactic:
rintro _ _ ⟨⟩
/-- The universal property of `Sym2`; symmetric functions of two arguments are equivalent to
functions from `Sym2`. Note that when `β` is `Prop`, it can sometimes be more convenient to use
`Sym2.fromRel` instead. -/
def lift : { f : α → α → β // ∀ a₁ a₂, f a₁ a₂ = f a₂ a₁ } ≃ (Sym2 α → β)
    where
  toFun f :=
    Quotient.lift (uncurry ↑f) <| by
      rintro _ _ ⟨⟩
      

Proof state:
case refl
α : Type u_1
β : Type u_2
γ : Type u_3
f : { f // ∀ (a₁ a₂ : α), f a₁ a₂ = f a₂ a₁ }
x✝ y✝ : α
⊢ uncurry ↑f (x✝, y✝) = uncurry ↑f (x✝, y✝)
case swap
α : Type u_1
β : Type u_2
γ : Type u_3
f : { f // ∀ (a₁ a₂ : α), f a₁ a₂ = f a₂ a₁ }
x✝ y✝ : α
⊢ uncurry ↑f (x✝, y✝) = uncurry ↑f (y✝, x✝)

Next tactic:
exacts [rfl, f.prop _ _]
/-- A two-argument version of `Sym2.lift`. -/
def lift₂ :
    { f : α → α → β → β → γ //
        ∀ a₁ a₂ b₁ b₂, f a₁ a₂ b₁ b₂ = f a₂ a₁ b₁ b₂ ∧ f a₁ a₂ b₁ b₂ = f a₁ a₂ b₂ b₁ } ≃
      (Sym2 α → Sym2 β → γ)
    where
  toFun f :=
    Quotient.lift₂ (fun (a : α × α) (b : β × β) => f.1 a.1 a.2 b.1 b.2)
      (by
        

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : { f // ∀ (a₁ a₂ : α) (b₁ b₂ : β), f a₁ a₂ b₁ b₂ = f a₂ a₁ b₁ b₂ ∧ f a₁ a₂ b₁ b₂ = f a₁ a₂ b₂ b₁ }
⊢ ∀ (a₁ : α × α) (b₁ : β × β) (a₂ : α × α) (b₂ : β × β),
    a₁ ≈ a₂ → b₁ ≈ b₂ → (fun a b => ↑f a.1 a.2 b.1 b.2) a₁ b₁ = (fun a b => ↑f a.1 a.2 b.1 b.2) a₂ b₂

Next tactic:
rintro _ _ _ _ ⟨⟩ ⟨⟩
/-- A two-argument version of `Sym2.lift`. -/
def lift₂ :
    { f : α → α → β → β → γ //
        ∀ a₁ a₂ b₁ b₂, f a₁ a₂ b₁ b₂ = f a₂ a₁ b₁ b₂ ∧ f a₁ a₂ b₁ b₂ = f a₁ a₂ b₂ b₁ } ≃
      (Sym2 α → Sym2 β → γ)
    where
  toFun f :=
    Quotient.lift₂ (fun (a : α × α) (b : β × β) => f.1 a.1 a.2 b.1 b.2)
      (by
        rintro _ _ _ _ ⟨⟩ ⟨⟩
        

Proof state:
case refl.refl
α : Type u_1
β : Type u_2
γ : Type u_3
f : { f // ∀ (a₁ a₂ : α) (b₁ b₂ : β), f a₁ a₂ b₁ b₂ = f a₂ a₁ b₁ b₂ ∧ f a₁ a₂ b₁ b₂ = f a₁ a₂ b₂ b₁ }
x✝¹ y✝¹ : α
x✝ y✝ : β
⊢ (fun a b => ↑f a.1 a.2 b.1 b.2) (x✝¹, y✝¹) (x✝, y✝) = (fun a b => ↑f a.1 a.2 b.1 b.2) (x✝¹, y✝¹) (x✝, y✝)
case refl.swap
α : Type u_1
β : Type u_2
γ : Type u_3
f : { f // ∀ (a₁ a₂ : α) (b₁ b₂ : β), f a₁ a₂ b₁ b₂ = f a₂ a₁ b₁ b₂ ∧ f a₁ a₂ b₁ b₂ = f a₁ a₂ b₂ b₁ }
x✝¹ y✝¹ : α
x✝ y✝ : β
⊢ (fun a b => ↑f a.1 a.2 b.1 b.2) (x✝¹, y✝¹) (x✝, y✝) = (fun a b => ↑f a.1 a.2 b.1 b.2) (x✝¹, y✝¹) (y✝, x✝)
case swap.refl
α : Type u_1
β : Type u_2
γ : Type u_3
f : { f // ∀ (a₁ a₂ : α) (b₁ b₂ : β), f a₁ a₂ b₁ b₂ = f a₂ a₁ b₁ b₂ ∧ f a₁ a₂ b₁ b₂ = f a₁ a₂ b₂ b₁ }
x✝¹ y✝¹ : α
x✝ y✝ : β
⊢ (fun a b => ↑f a.1 a.2 b.1 b.2) (x✝¹, y✝¹) (x✝, y✝) = (fun a b => ↑f a.1 a.2 b.1 b.2) (y✝¹, x✝¹) (x✝, y✝)
case swap.swap
α : Type u_1
β : Type u_2
γ : Type u_3
f : { f // ∀ (a₁ a₂ : α) (b₁ b₂ : β), f a₁ a₂ b₁ b₂ = f a₂ a₁ b₁ b₂ ∧ f a₁ a₂ b₁ b₂ = f a₁ a₂ b₂ b₁ }
x✝¹ y✝¹ : α
x✝ y✝ : β
⊢ (fun a b => ↑f a.1 a.2 b.1 b.2) (x✝¹, y✝¹) (x✝, y✝) = (fun a b => ↑f a.1 a.2 b.1 b.2) (y✝¹, x✝¹) (y✝, x✝)

Next tactic:
exacts [rfl, (f.2 _ _ _ _).2, (f.2 _ _ _ _).1, (f.2 _ _ _ _).1.trans (f.2 _ _ _ _).2]
/-- A two-argument version of `Sym2.lift`. -/
def lift₂ :
    { f : α → α → β → β → γ //
        ∀ a₁ a₂ b₁ b₂, f a₁ a₂ b₁ b₂ = f a₂ a₁ b₁ b₂ ∧ f a₁ a₂ b₁ b₂ = f a₁ a₂ b₂ b₁ } ≃
      (Sym2 α → Sym2 β → γ)
    where
  toFun f :=
    Quotient.lift₂ (fun (a : α × α) (b : β × β) => f.1 a.1 a.2 b.1 b.2)
      (by
        rintro _ _ _ _ ⟨⟩ ⟨⟩
        exacts [rfl, (f.2 _ _ _ _).2, (f.2 _ _ _ _).1, (f.2 _ _ _ _).1.trans (f.2 _ _ _ _).2])
  invFun F :=
    ⟨fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧, fun a₁ a₂ b₁ b₂ => by
      

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
F : Sym2 α → Sym2 β → γ
a₁ a₂ : α
b₁ b₂ : β
⊢ (fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) a₁ a₂ b₁ b₂ = (fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) a₂ a₁ b₁ b₂ ∧
    (fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) a₁ a₂ b₁ b₂ = (fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) a₁ a₂ b₂ b₁

Next tactic:
constructor
/-- A two-argument version of `Sym2.lift`. -/
def lift₂ :
    { f : α → α → β → β → γ //
        ∀ a₁ a₂ b₁ b₂, f a₁ a₂ b₁ b₂ = f a₂ a₁ b₁ b₂ ∧ f a₁ a₂ b₁ b₂ = f a₁ a₂ b₂ b₁ } ≃
      (Sym2 α → Sym2 β → γ)
    where
  toFun f :=
    Quotient.lift₂ (fun (a : α × α) (b : β × β) => f.1 a.1 a.2 b.1 b.2)
      (by
        rintro _ _ _ _ ⟨⟩ ⟨⟩
        exacts [rfl, (f.2 _ _ _ _).2, (f.2 _ _ _ _).1, (f.2 _ _ _ _).1.trans (f.2 _ _ _ _).2])
  invFun F :=
    ⟨fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧, fun a₁ a₂ b₁ b₂ => by
      constructor
      

Proof state:
case left
α : Type u_1
β : Type u_2
γ : Type u_3
F : Sym2 α → Sym2 β → γ
a₁ a₂ : α
b₁ b₂ : β
⊢ (fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) a₁ a₂ b₁ b₂ = (fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) a₂ a₁ b₁ b₂
case right
α : Type u_1
β : Type u_2
γ : Type u_3
F : Sym2 α → Sym2 β → γ
a₁ a₂ : α
b₁ b₂ : β
⊢ (fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) a₁ a₂ b₁ b₂ = (fun a₁ a₂ b₁ b₂ => F ⟦(a₁, a₂)⟧ ⟦(b₁, b₂)⟧) a₁ a₂ b₂ b₁

Next tactic:
exacts [congr_arg₂ F eq_swap rfl, congr_arg₂ F rfl eq_swap]
/-- The functor `Sym2` is functorial, and this function constructs the induced maps.
-/
def map (f : α → β) : Sym2 α → Sym2 β :=
  Quotient.map (Prod.map f f)
    (by
      

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
⊢ ((fun x x_1 => x ≈ x_1) ⇒ fun x x_1 => x ≈ x_1) (Prod.map f f) (Prod.map f f)

Next tactic:
intro _ _ h
/-- The functor `Sym2` is functorial, and this function constructs the induced maps.
-/
def map (f : α → β) : Sym2 α → Sym2 β :=
  Quotient.map (Prod.map f f)
    (by
      intro _ _ h
      

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
a✝ b✝ : α × α
h : a✝ ≈ b✝
⊢ Prod.map f f a✝ ≈ Prod.map f f b✝

Next tactic:
cases h
/-- The functor `Sym2` is functorial, and this function constructs the induced maps.
-/
def map (f : α → β) : Sym2 α → Sym2 β :=
  Quotient.map (Prod.map f f)
    (by
      intro _ _ h
      cases h
      · 

Proof state:
case refl
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
x✝ y✝ : α
⊢ Prod.map f f (x✝, y✝) ≈ Prod.map f f (x✝, y✝)

Next tactic:
constructor
/-- The functor `Sym2` is functorial, and this function constructs the induced maps.
-/
def map (f : α → β) : Sym2 α → Sym2 β :=
  Quotient.map (Prod.map f f)
    (by
      intro _ _ h
      cases h
      · constructor
      

Proof state:
case swap
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
x✝ y✝ : α
⊢ Prod.map f f (x✝, y✝) ≈ Prod.map f f (y✝, x✝)

Next tactic:
apply Rel.swap
@[simp]
theorem map_id : map (@id α) = id := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
⊢ map id = id

Next tactic:
ext ⟨⟨x, y⟩⟩
@[simp]
theorem map_id : map (@id α) = id := by
  ext ⟨⟨x, y⟩⟩
  

Proof state:
case h.mk.mk
α : Type u_1
β : Type u_2
γ : Type u_3
x✝ : Sym2 α
x y : α
⊢ map id (Quot.mk Setoid.r (x, y)) = id (Quot.mk Setoid.r (x, y))

Next tactic:
rfl
theorem map_comp {g : β → γ} {f : α → β} : Sym2.map (g ∘ f) = Sym2.map g ∘ Sym2.map f := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
g : β → γ
f : α → β
⊢ map (g ∘ f) = map g ∘ map f

Next tactic:
ext ⟨⟨x, y⟩⟩
theorem map_comp {g : β → γ} {f : α → β} : Sym2.map (g ∘ f) = Sym2.map g ∘ Sym2.map f := by
  ext ⟨⟨x, y⟩⟩
  

Proof state:
case h.mk.mk
α : Type u_1
β : Type u_2
γ : Type u_3
g : β → γ
f : α → β
x✝ : Sym2 α
x y : α
⊢ map (g ∘ f) (Quot.mk Setoid.r (x, y)) = (map g ∘ map f) (Quot.mk Setoid.r (x, y))

Next tactic:
rfl
theorem map_map {g : β → γ} {f : α → β} (x : Sym2 α) : map g (map f x) = map (g ∘ f) x := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
g : β → γ
f : α → β
x : Sym2 α
⊢ map g (map f x) = map (g ∘ f) x

Next tactic:
revert x
theorem map_map {g : β → γ} {f : α → β} (x : Sym2 α) : map g (map f x) = map (g ∘ f) x := by
  revert x; 

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
g : β → γ
f : α → β
⊢ ∀ (x : Sym2 α), map g (map f x) = map (g ∘ f) x

Next tactic:
apply Sym2.ind
theorem map_map {g : β → γ} {f : α → β} (x : Sym2 α) : map g (map f x) = map (g ∘ f) x := by
  revert x; apply Sym2.ind; 

Proof state:
case h
α : Type u_1
β : Type u_2
γ : Type u_3
g : β → γ
f : α → β
⊢ ∀ (x y : α), map g (map f ⟦(x, y)⟧) = map (g ∘ f) ⟦(x, y)⟧

Next tactic:
aesop
theorem map.injective {f : α → β} (hinj : Injective f) : Injective (map f) := by
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
hinj : Injective f
⊢ Injective (map f)

Next tactic:
intro z z'
theorem map.injective {f : α → β} (hinj : Injective f) : Injective (map f) := by
  intro z z'
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
hinj : Injective f
z z' : Sym2 α
⊢ map f z = map f z' → z = z'

Next tactic:
refine' Quotient.ind₂ (fun z z' => _) z z'
theorem map.injective {f : α → β} (hinj : Injective f) : Injective (map f) := by
  intro z z'
  refine' Quotient.ind₂ (fun z z' => _) z z'
  

Proof state:
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
hinj : Injective f
z✝ z'✝ : Sym2 α
z z' : α × α
⊢ map f ⟦z⟧ = map f ⟦z'⟧ → ⟦z⟧ = ⟦z'⟧

Next tactic:
cases' z with x y
theorem map.injective {f : α → β} (hinj : Injective f) : Injective (map f) := by
  intro z z'
  refine' Quotient.ind₂ (fun z z' => _) z z'
  cases' z with x y
  

Proof state:
case mk
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
hinj : Injective f
z z'✝ : Sym2 α
z' : α × α
x y : α
⊢ map f ⟦(x, y)⟧ = map f ⟦z'⟧ → ⟦(x, y)⟧ = ⟦z'⟧

Next tactic:
cases' z' with x' y'
theorem map.injective {f : α → β} (hinj : Injective f) : Injective (map f) := by
  intro z z'
  refine' Quotient.ind₂ (fun z z' => _) z z'
  cases' z with x y
  cases' z' with x' y'
  

Proof state:
case mk.mk
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
hinj : Injective f
z z' : Sym2 α
x y x' y' : α
⊢ map f ⟦(x, y)⟧ = map f ⟦(x', y')⟧ → ⟦(x, y)⟧ = ⟦(x', y')⟧

Next tactic:
repeat' rw [map_pair_eq, eq_iff]
theorem map.injective {f : α → β} (hinj : Injective f) : Injective (map f) := by
  intro z z'
  refine' Quotient.ind₂ (fun z z' => _) z z'
  cases' z with x y
  cases' z' with x' y'
  repeat' 

Proof state:
case mk.mk
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
hinj : Injective f
z z' : Sym2 α
x y x' y' : α
⊢ map f ⟦(x, y)⟧ = map f ⟦(x', y')⟧ → ⟦(x, y)⟧ = ⟦(x', y')⟧

Next tactic:
rw [map_pair_eq, eq_iff]
theorem map.injective {f : α → β} (hinj : Injective f) : Injective (map f) := by
  intro z z'
  refine' Quotient.ind₂ (fun z z' => _) z z'
  cases' z with x y
  cases' z' with x' y'
  repeat' 

Proof state:
case mk.mk
α : Type u_1
β : Type u_2
γ : Type u_3
f : α → β
hinj : Injective f
z z' : Sym2 α
x y x' y' : α
⊢ ⟦(f x, f y)⟧ = map f ⟦(x', y')⟧ → x = x' ∧ y = y' ∨ x = y' ∧ y = x'

Next tactic:
rw [map_pair_eq, eq_iff]