l3lab/ntp-mathlib huggingface dataset:
/-- `PSum` is equivalent to `Sum`. -/
def psumEquivSum (α β) : PSum α β ≃ Sum α β where
  toFun s := PSum.casesOn s inl inr
  invFun := Sum.elim PSum.inl PSum.inr
  left_inv s := by cases s <;> 

Proof state:
case inl
α : Type ?u.12316
β : Type ?u.12315
val✝ : α
⊢ Sum.elim PSum.inl PSum.inr ((fun s => PSum.casesOn s inl inr) (PSum.inl val✝)) = PSum.inl val✝

Next tactic:
rfl
/-- `PSum` is equivalent to `Sum`. -/
def psumEquivSum (α β) : PSum α β ≃ Sum α β where
  toFun s := PSum.casesOn s inl inr
  invFun := Sum.elim PSum.inl PSum.inr
  left_inv s := by cases s <;> 

Proof state:
case inr
α : Type ?u.12316
β : Type ?u.12315
val✝ : β
⊢ Sum.elim PSum.inl PSum.inr ((fun s => PSum.casesOn s inl inr) (PSum.inr val✝)) = PSum.inr val✝

Next tactic:
rfl
/-- `PSum` is equivalent to `Sum`. -/
def psumEquivSum (α β) : PSum α β ≃ Sum α β where
  toFun s := PSum.casesOn s inl inr
  invFun := Sum.elim PSum.inl PSum.inr
  left_inv s := by cases s <;> rfl
  right_inv s := by 

Proof state:
α : Type ?u.12316
β : Type ?u.12315
s : α ⊕ β
⊢ (fun s => PSum.casesOn s inl inr) (Sum.elim PSum.inl PSum.inr s) = s

Next tactic:
cases s
/-- `PSum` is equivalent to `Sum`. -/
def psumEquivSum (α β) : PSum α β ≃ Sum α β where
  toFun s := PSum.casesOn s inl inr
  invFun := Sum.elim PSum.inl PSum.inr
  left_inv s := by cases s <;> rfl
  right_inv s := by cases s <;> 

Proof state:
case inl
α : Type ?u.12316
β : Type ?u.12315
val✝ : α
⊢ (fun s => PSum.casesOn s inl inr) (Sum.elim PSum.inl PSum.inr (inl val✝)) = inl val✝

Next tactic:
rfl
/-- `PSum` is equivalent to `Sum`. -/
def psumEquivSum (α β) : PSum α β ≃ Sum α β where
  toFun s := PSum.casesOn s inl inr
  invFun := Sum.elim PSum.inl PSum.inr
  left_inv s := by cases s <;> rfl
  right_inv s := by cases s <;> 

Proof state:
case inr
α : Type ?u.12316
β : Type ?u.12315
val✝ : β
⊢ (fun s => PSum.casesOn s inl inr) (Sum.elim PSum.inl PSum.inr (inr val✝)) = inr val✝

Next tactic:
rfl
/-- If `α ≃ α'` and `β ≃ β'`, then `α ⊕ β ≃ α' ⊕ β'`. This is `Sum.map` as an equivalence. -/
@[simps apply]
def sumCongr (ea : α₁ ≃ α₂) (eb : β₁ ≃ β₂) : Sum α₁ β₁ ≃ Sum α₂ β₂ :=
  ⟨Sum.map ea eb, Sum.map ea.symm eb.symm, fun x => by 

Proof state:
α₁ : Type ?u.12723
α₂ : Type ?u.12725
β₁ : Type ?u.12722
β₂ : Type ?u.12724
ea : α₁ ≃ α₂
eb : β₁ ≃ β₂
x : α₁ ⊕ β₁
⊢ Sum.map (⇑ea.symm) (⇑eb.symm) (Sum.map (⇑ea) (⇑eb) x) = x

Next tactic:
simp
/-- If `α ≃ α'` and `β ≃ β'`, then `α ⊕ β ≃ α' ⊕ β'`. This is `Sum.map` as an equivalence. -/
@[simps apply]
def sumCongr (ea : α₁ ≃ α₂) (eb : β₁ ≃ β₂) : Sum α₁ β₁ ≃ Sum α₂ β₂ :=
  ⟨Sum.map ea eb, Sum.map ea.symm eb.symm, fun x => by simp, fun x => by 

Proof state:
α₁ : Type ?u.12723
α₂ : Type ?u.12725
β₁ : Type ?u.12722
β₂ : Type ?u.12724
ea : α₁ ≃ α₂
eb : β₁ ≃ β₂
x : α₂ ⊕ β₂
⊢ Sum.map (⇑ea) (⇑eb) (Sum.map (⇑ea.symm) (⇑eb.symm) x) = x

Next tactic:
simp
/-- If `α ≃ α'` and `β ≃ β'`, then `PSum α β ≃ PSum α' β'`. -/
def psumCongr (e₁ : α ≃ β) (e₂ : γ ≃ δ) : PSum α γ ≃ PSum β δ where
  toFun x := PSum.casesOn x (PSum.inl ∘ e₁) (PSum.inr ∘ e₂)
  invFun x := PSum.casesOn x (PSum.inl ∘ e₁.symm) (PSum.inr ∘ e₂.symm)
  left_inv := by 

Proof state:
α : Sort ?u.14056
β : Sort ?u.14055
γ : Sort ?u.14060
δ : Sort ?u.14059
e₁ : α ≃ β
e₂ : γ ≃ δ
⊢ LeftInverse (fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁.symm) (PSum.inr ∘ ⇑e₂.symm)) fun x =>
    PSum.casesOn x (PSum.inl ∘ ⇑e₁) (PSum.inr ∘ ⇑e₂)

Next tactic:
rintro (x | x)
/-- If `α ≃ α'` and `β ≃ β'`, then `PSum α β ≃ PSum α' β'`. -/
def psumCongr (e₁ : α ≃ β) (e₂ : γ ≃ δ) : PSum α γ ≃ PSum β δ where
  toFun x := PSum.casesOn x (PSum.inl ∘ e₁) (PSum.inr ∘ e₂)
  invFun x := PSum.casesOn x (PSum.inl ∘ e₁.symm) (PSum.inr ∘ e₂.symm)
  left_inv := by rintro (x | x) <;> 

Proof state:
case inl
α : Sort ?u.14056
β : Sort ?u.14055
γ : Sort ?u.14060
δ : Sort ?u.14059
e₁ : α ≃ β
e₂ : γ ≃ δ
x : α
⊢ (fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁.symm) (PSum.inr ∘ ⇑e₂.symm))
      ((fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁) (PSum.inr ∘ ⇑e₂)) (PSum.inl x)) =
    PSum.inl x

Next tactic:
simp
/-- If `α ≃ α'` and `β ≃ β'`, then `PSum α β ≃ PSum α' β'`. -/
def psumCongr (e₁ : α ≃ β) (e₂ : γ ≃ δ) : PSum α γ ≃ PSum β δ where
  toFun x := PSum.casesOn x (PSum.inl ∘ e₁) (PSum.inr ∘ e₂)
  invFun x := PSum.casesOn x (PSum.inl ∘ e₁.symm) (PSum.inr ∘ e₂.symm)
  left_inv := by rintro (x | x) <;> 

Proof state:
case inr
α : Sort ?u.14056
β : Sort ?u.14055
γ : Sort ?u.14060
δ : Sort ?u.14059
e₁ : α ≃ β
e₂ : γ ≃ δ
x : γ
⊢ (fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁.symm) (PSum.inr ∘ ⇑e₂.symm))
      ((fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁) (PSum.inr ∘ ⇑e₂)) (PSum.inr x)) =
    PSum.inr x

Next tactic:
simp
/-- If `α ≃ α'` and `β ≃ β'`, then `PSum α β ≃ PSum α' β'`. -/
def psumCongr (e₁ : α ≃ β) (e₂ : γ ≃ δ) : PSum α γ ≃ PSum β δ where
  toFun x := PSum.casesOn x (PSum.inl ∘ e₁) (PSum.inr ∘ e₂)
  invFun x := PSum.casesOn x (PSum.inl ∘ e₁.symm) (PSum.inr ∘ e₂.symm)
  left_inv := by rintro (x | x) <;> simp
  right_inv := by 

Proof state:
α : Sort ?u.14056
β : Sort ?u.14055
γ : Sort ?u.14060
δ : Sort ?u.14059
e₁ : α ≃ β
e₂ : γ ≃ δ
⊢ Function.RightInverse (fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁.symm) (PSum.inr ∘ ⇑e₂.symm)) fun x =>
    PSum.casesOn x (PSum.inl ∘ ⇑e₁) (PSum.inr ∘ ⇑e₂)

Next tactic:
rintro (x | x)
/-- If `α ≃ α'` and `β ≃ β'`, then `PSum α β ≃ PSum α' β'`. -/
def psumCongr (e₁ : α ≃ β) (e₂ : γ ≃ δ) : PSum α γ ≃ PSum β δ where
  toFun x := PSum.casesOn x (PSum.inl ∘ e₁) (PSum.inr ∘ e₂)
  invFun x := PSum.casesOn x (PSum.inl ∘ e₁.symm) (PSum.inr ∘ e₂.symm)
  left_inv := by rintro (x | x) <;> simp
  right_inv := by rintro (x | x) <;> 

Proof state:
case inl
α : Sort ?u.14056
β : Sort ?u.14055
γ : Sort ?u.14060
δ : Sort ?u.14059
e₁ : α ≃ β
e₂ : γ ≃ δ
x : β
⊢ (fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁) (PSum.inr ∘ ⇑e₂))
      ((fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁.symm) (PSum.inr ∘ ⇑e₂.symm)) (PSum.inl x)) =
    PSum.inl x

Next tactic:
simp
/-- If `α ≃ α'` and `β ≃ β'`, then `PSum α β ≃ PSum α' β'`. -/
def psumCongr (e₁ : α ≃ β) (e₂ : γ ≃ δ) : PSum α γ ≃ PSum β δ where
  toFun x := PSum.casesOn x (PSum.inl ∘ e₁) (PSum.inr ∘ e₂)
  invFun x := PSum.casesOn x (PSum.inl ∘ e₁.symm) (PSum.inr ∘ e₂.symm)
  left_inv := by rintro (x | x) <;> simp
  right_inv := by rintro (x | x) <;> 

Proof state:
case inr
α : Sort ?u.14056
β : Sort ?u.14055
γ : Sort ?u.14060
δ : Sort ?u.14059
e₁ : α ≃ β
e₂ : γ ≃ δ
x : δ
⊢ (fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁) (PSum.inr ∘ ⇑e₂))
      ((fun x => PSum.casesOn x (PSum.inl ∘ ⇑e₁.symm) (PSum.inr ∘ ⇑e₂.symm)) (PSum.inr x)) =
    PSum.inr x

Next tactic:
simp
@[simp]
theorem sumCongr_trans (e : α₁ ≃ β₁) (f : α₂ ≃ β₂) (g : β₁ ≃ γ₁) (h : β₂ ≃ γ₂) :
    (Equiv.sumCongr e f).trans (Equiv.sumCongr g h) = Equiv.sumCongr (e.trans g) (f.trans h) := by
  

Proof state:
α₁ : Type u_1
β₁ : Type u_2
α₂ : Type u_3
β₂ : Type u_4
γ₁ : Type u_5
γ₂ : Type u_6
e : α₁ ≃ β₁
f : α₂ ≃ β₂
g : β₁ ≃ γ₁
h : β₂ ≃ γ₂
⊢ (sumCongr e f).trans (sumCongr g h) = sumCongr (e.trans g) (f.trans h)

Next tactic:
ext i
@[simp]
theorem sumCongr_trans (e : α₁ ≃ β₁) (f : α₂ ≃ β₂) (g : β₁ ≃ γ₁) (h : β₂ ≃ γ₂) :
    (Equiv.sumCongr e f).trans (Equiv.sumCongr g h) = Equiv.sumCongr (e.trans g) (f.trans h) := by
  ext i
  

Proof state:
case H
α₁ : Type u_1
β₁ : Type u_2
α₂ : Type u_3
β₂ : Type u_4
γ₁ : Type u_5
γ₂ : Type u_6
e : α₁ ≃ β₁
f : α₂ ≃ β₂
g : β₁ ≃ γ₁
h : β₂ ≃ γ₂
i : α₁ ⊕ α₂
⊢ ((sumCongr e f).trans (sumCongr g h)) i = (sumCongr (e.trans g) (f.trans h)) i

Next tactic:
cases i
@[simp]
theorem sumCongr_trans (e : α₁ ≃ β₁) (f : α₂ ≃ β₂) (g : β₁ ≃ γ₁) (h : β₂ ≃ γ₂) :
    (Equiv.sumCongr e f).trans (Equiv.sumCongr g h) = Equiv.sumCongr (e.trans g) (f.trans h) := by
  ext i
  cases i <;> 

Proof state:
case H.inl
α₁ : Type u_1
β₁ : Type u_2
α₂ : Type u_3
β₂ : Type u_4
γ₁ : Type u_5
γ₂ : Type u_6
e : α₁ ≃ β₁
f : α₂ ≃ β₂
g : β₁ ≃ γ₁
h : β₂ ≃ γ₂
val✝ : α₁
⊢ ((sumCongr e f).trans (sumCongr g h)) (inl val✝) = (sumCongr (e.trans g) (f.trans h)) (inl val✝)

Next tactic:
rfl
@[simp]
theorem sumCongr_trans (e : α₁ ≃ β₁) (f : α₂ ≃ β₂) (g : β₁ ≃ γ₁) (h : β₂ ≃ γ₂) :
    (Equiv.sumCongr e f).trans (Equiv.sumCongr g h) = Equiv.sumCongr (e.trans g) (f.trans h) := by
  ext i
  cases i <;> 

Proof state:
case H.inr
α₁ : Type u_1
β₁ : Type u_2
α₂ : Type u_3
β₂ : Type u_4
γ₁ : Type u_5
γ₂ : Type u_6
e : α₁ ≃ β₁
f : α₂ ≃ β₂
g : β₁ ≃ γ₁
h : β₂ ≃ γ₂
val✝ : α₂
⊢ ((sumCongr e f).trans (sumCongr g h)) (inr val✝) = (sumCongr (e.trans g) (f.trans h)) (inr val✝)

Next tactic:
rfl
@[simp]
theorem sumCongr_refl : Equiv.sumCongr (Equiv.refl α) (Equiv.refl β) = Equiv.refl (Sum α β) := by
  

Proof state:
α : Type u_1
β : Type u_2
⊢ sumCongr (Equiv.refl α) (Equiv.refl β) = Equiv.refl (α ⊕ β)

Next tactic:
ext i
@[simp]
theorem sumCongr_refl : Equiv.sumCongr (Equiv.refl α) (Equiv.refl β) = Equiv.refl (Sum α β) := by
  ext i
  

Proof state:
case H
α : Type u_1
β : Type u_2
i : α ⊕ β
⊢ (sumCongr (Equiv.refl α) (Equiv.refl β)) i = (Equiv.refl (α ⊕ β)) i

Next tactic:
cases i
@[simp]
theorem sumCongr_refl : Equiv.sumCongr (Equiv.refl α) (Equiv.refl β) = Equiv.refl (Sum α β) := by
  ext i
  cases i <;> 

Proof state:
case H.inl
α : Type u_1
β : Type u_2
val✝ : α
⊢ (sumCongr (Equiv.refl α) (Equiv.refl β)) (inl val✝) = (Equiv.refl (α ⊕ β)) (inl val✝)

Next tactic:
rfl
@[simp]
theorem sumCongr_refl : Equiv.sumCongr (Equiv.refl α) (Equiv.refl β) = Equiv.refl (Sum α β) := by
  ext i
  cases i <;> 

Proof state:
case H.inr
α : Type u_1
β : Type u_2
val✝ : β
⊢ (sumCongr (Equiv.refl α) (Equiv.refl β)) (inr val✝) = (Equiv.refl (α ⊕ β)) (inr val✝)

Next tactic:
rfl
/-- A subtype of a sum is equivalent to a sum of subtypes. -/
def subtypeSum {p : α ⊕ β → Prop} : {c // p c} ≃ {a // p (Sum.inl a)} ⊕ {b // p (Sum.inr b)} where
  toFun c := match h : c.1 with
    | Sum.inl a => Sum.inl ⟨a, h ▸ c.2⟩
    | Sum.inr b => Sum.inr ⟨b, h ▸ c.2⟩
  invFun c := match c with
    | Sum.inl a => ⟨Sum.inl a, a.2⟩
    | Sum.inr b => ⟨Sum.inr b, b.2⟩
  left_inv := by 

Proof state:
α : Type ?u.19399
β : Type ?u.19398
p : α ⊕ β → Prop
⊢ LeftInverse
    (fun c =>
      match c with
      | inl a => { val := inl ↑a, property := (_ : p (inl ↑a)) }
      | inr b => { val := inr ↑b, property := (_ : p (inr ↑b)) })
    fun c =>
    match h : ↑c with
    | inl a => inl { val := a, property := (_ : p (inl a)) }
    | inr b => inr { val := b, property := (_ : p (inr b)) }

Next tactic:
rintro ⟨a | b, h⟩
/-- A subtype of a sum is equivalent to a sum of subtypes. -/
def subtypeSum {p : α ⊕ β → Prop} : {c // p c} ≃ {a // p (Sum.inl a)} ⊕ {b // p (Sum.inr b)} where
  toFun c := match h : c.1 with
    | Sum.inl a => Sum.inl ⟨a, h ▸ c.2⟩
    | Sum.inr b => Sum.inr ⟨b, h ▸ c.2⟩
  invFun c := match c with
    | Sum.inl a => ⟨Sum.inl a, a.2⟩
    | Sum.inr b => ⟨Sum.inr b, b.2⟩
  left_inv := by rintro ⟨a | b, h⟩ <;> 

Proof state:
case mk.inl
α : Type ?u.19399
β : Type ?u.19398
p : α ⊕ β → Prop
a : α
h : p (inl a)
⊢ (fun c =>
        match c with
        | inl a => { val := inl ↑a, property := (_ : p (inl ↑a)) }
        | inr b => { val := inr ↑b, property := (_ : p (inr ↑b)) })
      ((fun c =>
          match h : ↑c with
          | inl a => inl { val := a, property := (_ : p (inl a)) }
          | inr b => inr { val := b, property := (_ : p (inr b)) })
        { val := inl a, property := h }) =
    { val := inl a, property := h }

Next tactic:
rfl
/-- A subtype of a sum is equivalent to a sum of subtypes. -/
def subtypeSum {p : α ⊕ β → Prop} : {c // p c} ≃ {a // p (Sum.inl a)} ⊕ {b // p (Sum.inr b)} where
  toFun c := match h : c.1 with
    | Sum.inl a => Sum.inl ⟨a, h ▸ c.2⟩
    | Sum.inr b => Sum.inr ⟨b, h ▸ c.2⟩
  invFun c := match c with
    | Sum.inl a => ⟨Sum.inl a, a.2⟩
    | Sum.inr b => ⟨Sum.inr b, b.2⟩
  left_inv := by rintro ⟨a | b, h⟩ <;> 

Proof state:
case mk.inr
α : Type ?u.19399
β : Type ?u.19398
p : α ⊕ β → Prop
b : β
h : p (inr b)
⊢ (fun c =>
        match c with
        | inl a => { val := inl ↑a, property := (_ : p (inl ↑a)) }
        | inr b => { val := inr ↑b, property := (_ : p (inr ↑b)) })
      ((fun c =>
          match h : ↑c with
          | inl a => inl { val := a, property := (_ : p (inl a)) }
          | inr b => inr { val := b, property := (_ : p (inr b)) })
        { val := inr b, property := h }) =
    { val := inr b, property := h }

Next tactic:
rfl
/-- A subtype of a sum is equivalent to a sum of subtypes. -/
def subtypeSum {p : α ⊕ β → Prop} : {c // p c} ≃ {a // p (Sum.inl a)} ⊕ {b // p (Sum.inr b)} where
  toFun c := match h : c.1 with
    | Sum.inl a => Sum.inl ⟨a, h ▸ c.2⟩
    | Sum.inr b => Sum.inr ⟨b, h ▸ c.2⟩
  invFun c := match c with
    | Sum.inl a => ⟨Sum.inl a, a.2⟩
    | Sum.inr b => ⟨Sum.inr b, b.2⟩
  left_inv := by rintro ⟨a | b, h⟩ <;> rfl
  right_inv := by 

Proof state:
α : Type ?u.19399
β : Type ?u.19398
p : α ⊕ β → Prop
⊢ Function.RightInverse
    (fun c =>
      match c with
      | inl a => { val := inl ↑a, property := (_ : p (inl ↑a)) }
      | inr b => { val := inr ↑b, property := (_ : p (inr ↑b)) })
    fun c =>
    match h : ↑c with
    | inl a => inl { val := a, property := (_ : p (inl a)) }
    | inr b => inr { val := b, property := (_ : p (inr b)) }

Next tactic:
rintro (a | b)
/-- A subtype of a sum is equivalent to a sum of subtypes. -/
def subtypeSum {p : α ⊕ β → Prop} : {c // p c} ≃ {a // p (Sum.inl a)} ⊕ {b // p (Sum.inr b)} where
  toFun c := match h : c.1 with
    | Sum.inl a => Sum.inl ⟨a, h ▸ c.2⟩
    | Sum.inr b => Sum.inr ⟨b, h ▸ c.2⟩
  invFun c := match c with
    | Sum.inl a => ⟨Sum.inl a, a.2⟩
    | Sum.inr b => ⟨Sum.inr b, b.2⟩
  left_inv := by rintro ⟨a | b, h⟩ <;> rfl
  right_inv := by rintro (a | b) <;> 

Proof state:
case inl
α : Type ?u.19399
β : Type ?u.19398
p : α ⊕ β → Prop
a : { a // p (inl a) }
⊢ (fun c =>
        match h : ↑c with
        | inl a => inl { val := a, property := (_ : p (inl a)) }
        | inr b => inr { val := b, property := (_ : p (inr b)) })
      ((fun c =>
          match c with
          | inl a => { val := inl ↑a, property := (_ : p (inl ↑a)) }
          | inr b => { val := inr ↑b, property := (_ : p (inr ↑b)) })
        (inl a)) =
    inl a

Next tactic:
rfl
/-- A subtype of a sum is equivalent to a sum of subtypes. -/
def subtypeSum {p : α ⊕ β → Prop} : {c // p c} ≃ {a // p (Sum.inl a)} ⊕ {b // p (Sum.inr b)} where
  toFun c := match h : c.1 with
    | Sum.inl a => Sum.inl ⟨a, h ▸ c.2⟩
    | Sum.inr b => Sum.inr ⟨b, h ▸ c.2⟩
  invFun c := match c with
    | Sum.inl a => ⟨Sum.inl a, a.2⟩
    | Sum.inr b => ⟨Sum.inr b, b.2⟩
  left_inv := by rintro ⟨a | b, h⟩ <;> rfl
  right_inv := by rintro (a | b) <;> 

Proof state:
case inr
α : Type ?u.19399
β : Type ?u.19398
p : α ⊕ β → Prop
b : { b // p (inr b) }
⊢ (fun c =>
        match h : ↑c with
        | inl a => inl { val := a, property := (_ : p (inl a)) }
        | inr b => inr { val := b, property := (_ : p (inr b)) })
      ((fun c =>
          match c with
          | inl a => { val := inl ↑a, property := (_ : p (inl ↑a)) }
          | inr b => { val := inr ↑b, property := (_ : p (inr ↑b)) })
        (inr b)) =
    inr b

Next tactic:
rfl
/-- `Bool` is equivalent the sum of two `PUnit`s. -/
def boolEquivPUnitSumPUnit : Bool ≃ Sum PUnit.{u + 1} PUnit.{v + 1} :=
  ⟨fun b => b.casesOn (inl PUnit.unit) (inr PUnit.unit) , Sum.elim (fun _ => false) fun _ => true,
    fun b => by 

Proof state:
b : Bool
⊢ Sum.elim (fun x => false) (fun x => true) ((fun b => Bool.casesOn b (inl PUnit.unit) (inr PUnit.unit)) b) = b

Next tactic:
cases b
/-- `Bool` is equivalent the sum of two `PUnit`s. -/
def boolEquivPUnitSumPUnit : Bool ≃ Sum PUnit.{u + 1} PUnit.{v + 1} :=
  ⟨fun b => b.casesOn (inl PUnit.unit) (inr PUnit.unit) , Sum.elim (fun _ => false) fun _ => true,
    fun b => by cases b <;> 

Proof state:
case false
⊢ Sum.elim (fun x => false) (fun x => true) ((fun b => Bool.casesOn b (inl PUnit.unit) (inr PUnit.unit)) false) = false

Next tactic:
rfl
/-- `Bool` is equivalent the sum of two `PUnit`s. -/
def boolEquivPUnitSumPUnit : Bool ≃ Sum PUnit.{u + 1} PUnit.{v + 1} :=
  ⟨fun b => b.casesOn (inl PUnit.unit) (inr PUnit.unit) , Sum.elim (fun _ => false) fun _ => true,
    fun b => by cases b <;> 

Proof state:
case true
⊢ Sum.elim (fun x => false) (fun x => true) ((fun b => Bool.casesOn b (inl PUnit.unit) (inr PUnit.unit)) true) = true

Next tactic:
rfl
/-- `Bool` is equivalent the sum of two `PUnit`s. -/
def boolEquivPUnitSumPUnit : Bool ≃ Sum PUnit.{u + 1} PUnit.{v + 1} :=
  ⟨fun b => b.casesOn (inl PUnit.unit) (inr PUnit.unit) , Sum.elim (fun _ => false) fun _ => true,
    fun b => by cases b <;> rfl, fun s => by 

Proof state:
s : PUnit.{u + 1} ⊕ PUnit.{v + 1}
⊢ (fun b => Bool.casesOn b (inl PUnit.unit) (inr PUnit.unit)) (Sum.elim (fun x => false) (fun x => true) s) = s

Next tactic:
rcases s with (⟨⟨⟩⟩ | ⟨⟨⟩⟩)
/-- `Bool` is equivalent the sum of two `PUnit`s. -/
def boolEquivPUnitSumPUnit : Bool ≃ Sum PUnit.{u + 1} PUnit.{v + 1} :=
  ⟨fun b => b.casesOn (inl PUnit.unit) (inr PUnit.unit) , Sum.elim (fun _ => false) fun _ => true,
    fun b => by cases b <;> rfl, fun s => by rcases s with (⟨⟨⟩⟩ | ⟨⟨⟩⟩) <;> 

Proof state:
case inl.unit
⊢ (fun b => Bool.casesOn b (inl PUnit.unit) (inr PUnit.unit))
      (Sum.elim (fun x => false) (fun x => true) (inl PUnit.unit)) =
    inl PUnit.unit

Next tactic:
rfl
/-- `Bool` is equivalent the sum of two `PUnit`s. -/
def boolEquivPUnitSumPUnit : Bool ≃ Sum PUnit.{u + 1} PUnit.{v + 1} :=
  ⟨fun b => b.casesOn (inl PUnit.unit) (inr PUnit.unit) , Sum.elim (fun _ => false) fun _ => true,
    fun b => by cases b <;> rfl, fun s => by rcases s with (⟨⟨⟩⟩ | ⟨⟨⟩⟩) <;> 

Proof state:
case inr.unit
⊢ (fun b => Bool.casesOn b (inl PUnit.unit) (inr PUnit.unit))
      (Sum.elim (fun x => false) (fun x => true) (inr PUnit.unit)) =
    inr PUnit.unit

Next tactic:
rfl
/-- Sum of types is associative up to an equivalence. -/
def sumAssoc (α β γ) : Sum (Sum α β) γ ≃ Sum α (Sum β γ) :=
  ⟨Sum.elim (Sum.elim Sum.inl (Sum.inr ∘ Sum.inl)) (Sum.inr ∘ Sum.inr),
    Sum.elim (Sum.inl ∘ Sum.inl) <| Sum.elim (Sum.inl ∘ Sum.inr) Sum.inr,
      by 

Proof state:
α : Type ?u.21945
β : Type ?u.21944
γ : Type ?u.21942
⊢ LeftInverse (Sum.elim (inl ∘ inl) (Sum.elim (inl ∘ inr) inr)) (Sum.elim (Sum.elim inl (inr ∘ inl)) (inr ∘ inr))

Next tactic:
rintro (⟨_ | _⟩ | _)
/-- Sum of types is associative up to an equivalence. -/
def sumAssoc (α β γ) : Sum (Sum α β) γ ≃ Sum α (Sum β γ) :=
  ⟨Sum.elim (Sum.elim Sum.inl (Sum.inr ∘ Sum.inl)) (Sum.inr ∘ Sum.inr),
    Sum.elim (Sum.inl ∘ Sum.inl) <| Sum.elim (Sum.inl ∘ Sum.inr) Sum.inr,
      by rintro (⟨_ | _⟩ | _) <;> 

Proof state:
case inl.inl
α : Type ?u.21945
β : Type ?u.21944
γ : Type ?u.21942
val✝ : α
⊢ Sum.elim (inl ∘ inl) (Sum.elim (inl ∘ inr) inr) (Sum.elim (Sum.elim inl (inr ∘ inl)) (inr ∘ inr) (inl (inl val✝))) =
    inl (inl val✝)

Next tactic:
rfl
/-- Sum of types is associative up to an equivalence. -/
def sumAssoc (α β γ) : Sum (Sum α β) γ ≃ Sum α (Sum β γ) :=
  ⟨Sum.elim (Sum.elim Sum.inl (Sum.inr ∘ Sum.inl)) (Sum.inr ∘ Sum.inr),
    Sum.elim (Sum.inl ∘ Sum.inl) <| Sum.elim (Sum.inl ∘ Sum.inr) Sum.inr,
      by rintro (⟨_ | _⟩ | _) <;> 

Proof state:
case inl.inr
α : Type ?u.21945
β : Type ?u.21944
γ : Type ?u.21942
val✝ : β
⊢ Sum.elim (inl ∘ inl) (Sum.elim (inl ∘ inr) inr) (Sum.elim (Sum.elim inl (inr ∘ inl)) (inr ∘ inr) (inl (inr val✝))) =
    inl (inr val✝)

Next tactic:
rfl
/-- Sum of types is associative up to an equivalence. -/
def sumAssoc (α β γ) : Sum (Sum α β) γ ≃ Sum α (Sum β γ) :=
  ⟨Sum.elim (Sum.elim Sum.inl (Sum.inr ∘ Sum.inl)) (Sum.inr ∘ Sum.inr),
    Sum.elim (Sum.inl ∘ Sum.inl) <| Sum.elim (Sum.inl ∘ Sum.inr) Sum.inr,
      by rintro (⟨_ | _⟩ | _) <;> 

Proof state:
case inr
α : Type ?u.21945
β : Type ?u.21944
γ : Type ?u.21942
val✝ : γ
⊢ Sum.elim (inl ∘ inl) (Sum.elim (inl ∘ inr) inr) (Sum.elim (Sum.elim inl (inr ∘ inl)) (inr ∘ inr) (inr val✝)) =
    inr val✝

Next tactic:
rfl
/-- Sum of types is associative up to an equivalence. -/
def sumAssoc (α β γ) : Sum (Sum α β) γ ≃ Sum α (Sum β γ) :=
  ⟨Sum.elim (Sum.elim Sum.inl (Sum.inr ∘ Sum.inl)) (Sum.inr ∘ Sum.inr),
    Sum.elim (Sum.inl ∘ Sum.inl) <| Sum.elim (Sum.inl ∘ Sum.inr) Sum.inr,
      by rintro (⟨_ | _⟩ | _) <;> rfl, by
    

Proof state:
α : Type ?u.21945
β : Type ?u.21944
γ : Type ?u.21942
⊢ Function.RightInverse (Sum.elim (inl ∘ inl) (Sum.elim (inl ∘ inr) inr))
    (Sum.elim (Sum.elim inl (inr ∘ inl)) (inr ∘ inr))

Next tactic:
rintro (_ | ⟨_ | _⟩)
/-- Sum of types is associative up to an equivalence. -/
def sumAssoc (α β γ) : Sum (Sum α β) γ ≃ Sum α (Sum β γ) :=
  ⟨Sum.elim (Sum.elim Sum.inl (Sum.inr ∘ Sum.inl)) (Sum.inr ∘ Sum.inr),
    Sum.elim (Sum.inl ∘ Sum.inl) <| Sum.elim (Sum.inl ∘ Sum.inr) Sum.inr,
      by rintro (⟨_ | _⟩ | _) <;> rfl, by
    rintro (_ | ⟨_ | _⟩) <;> 

Proof state:
case inl
α : Type ?u.21945
β : Type ?u.21944
γ : Type ?u.21942
val✝ : α
⊢ Sum.elim (Sum.elim inl (inr ∘ inl)) (inr ∘ inr) (Sum.elim (inl ∘ inl) (Sum.elim (inl ∘ inr) inr) (inl val✝)) =
    inl val✝

Next tactic:
rfl
/-- Sum of types is associative up to an equivalence. -/
def sumAssoc (α β γ) : Sum (Sum α β) γ ≃ Sum α (Sum β γ) :=
  ⟨Sum.elim (Sum.elim Sum.inl (Sum.inr ∘ Sum.inl)) (Sum.inr ∘ Sum.inr),
    Sum.elim (Sum.inl ∘ Sum.inl) <| Sum.elim (Sum.inl ∘ Sum.inr) Sum.inr,
      by rintro (⟨_ | _⟩ | _) <;> rfl, by
    rintro (_ | ⟨_ | _⟩) <;> 

Proof state:
case inr.inl
α : Type ?u.21945
β : Type ?u.21944
γ : Type ?u.21942
val✝ : β
⊢ Sum.elim (Sum.elim inl (inr ∘ inl)) (inr ∘ inr) (Sum.elim (inl ∘ inl) (Sum.elim (inl ∘ inr) inr) (inr (inl val✝))) =
    inr (inl val✝)

Next tactic:
rfl
/-- Sum of types is associative up to an equivalence. -/
def sumAssoc (α β γ) : Sum (Sum α β) γ ≃ Sum α (Sum β γ) :=
  ⟨Sum.elim (Sum.elim Sum.inl (Sum.inr ∘ Sum.inl)) (Sum.inr ∘ Sum.inr),
    Sum.elim (Sum.inl ∘ Sum.inl) <| Sum.elim (Sum.inl ∘ Sum.inr) Sum.inr,
      by rintro (⟨_ | _⟩ | _) <;> rfl, by
    rintro (_ | ⟨_ | _⟩) <;> 

Proof state:
case inr.inr
α : Type ?u.21945
β : Type ?u.21944
γ : Type ?u.21942
val✝ : γ
⊢ Sum.elim (Sum.elim inl (inr ∘ inl)) (inr ∘ inr) (Sum.elim (inl ∘ inl) (Sum.elim (inl ∘ inr) inr) (inr (inr val✝))) =
    inr (inr val✝)

Next tactic:
rfl
/-- Sum with `IsEmpty` is equivalent to the original type. -/
@[simps symm_apply]
def sumEmpty (α β) [IsEmpty β] : Sum α β ≃ α where
  toFun := Sum.elim id isEmptyElim
  invFun := inl
  left_inv s := by
    

Proof state:
α : Type ?u.23656
β : Type ?u.23655
inst✝ : IsEmpty β
s : α ⊕ β
⊢ inl (Sum.elim id (fun a => isEmptyElim a) s) = s

Next tactic:
rcases s with (_ | x)
/-- Sum with `IsEmpty` is equivalent to the original type. -/
@[simps symm_apply]
def sumEmpty (α β) [IsEmpty β] : Sum α β ≃ α where
  toFun := Sum.elim id isEmptyElim
  invFun := inl
  left_inv s := by
    rcases s with (_ | x)
    · 

Proof state:
case inl
α : Type ?u.23656
β : Type ?u.23655
inst✝ : IsEmpty β
val✝ : α
⊢ inl (Sum.elim id (fun a => isEmptyElim a) (inl val✝)) = inl val✝

Next tactic:
rfl
/-- Sum with `IsEmpty` is equivalent to the original type. -/
@[simps symm_apply]
def sumEmpty (α β) [IsEmpty β] : Sum α β ≃ α where
  toFun := Sum.elim id isEmptyElim
  invFun := inl
  left_inv s := by
    rcases s with (_ | x)
    · rfl
    · 

Proof state:
case inr
α : Type ?u.23656
β : Type ?u.23655
inst✝ : IsEmpty β
x : β
⊢ inl (Sum.elim id (fun a => isEmptyElim a) (inr x)) = inr x

Next tactic:
exact isEmptyElim x
/-- `Option α` is equivalent to `α ⊕ PUnit` -/
def optionEquivSumPUnit (α) : Option α ≃ Sum α PUnit :=
  ⟨fun o => o.elim (inr PUnit.unit) inl, fun s => s.elim some fun _ => none,
    fun o => by 

Proof state:
α : Type ?u.24390
o : Option α
⊢ (fun s => Sum.elim some (fun x => none) s) ((fun o => Option.elim o (inr PUnit.unit) inl) o) = o

Next tactic:
cases o
/-- `Option α` is equivalent to `α ⊕ PUnit` -/
def optionEquivSumPUnit (α) : Option α ≃ Sum α PUnit :=
  ⟨fun o => o.elim (inr PUnit.unit) inl, fun s => s.elim some fun _ => none,
    fun o => by cases o <;> 

Proof state:
case none
α : Type ?u.24390
⊢ (fun s => Sum.elim some (fun x => none) s) ((fun o => Option.elim o (inr PUnit.unit) inl) none) = none

Next tactic:
rfl
/-- `Option α` is equivalent to `α ⊕ PUnit` -/
def optionEquivSumPUnit (α) : Option α ≃ Sum α PUnit :=
  ⟨fun o => o.elim (inr PUnit.unit) inl, fun s => s.elim some fun _ => none,
    fun o => by cases o <;> 

Proof state:
case some
α : Type ?u.24390
val✝ : α
⊢ (fun s => Sum.elim some (fun x => none) s) ((fun o => Option.elim o (inr PUnit.unit) inl) (some val✝)) = some val✝

Next tactic:
rfl