l3lab/ntp-mathlib huggingface dataset:
/-- An equivalence that separates out the 0th fiber of `(Σ (n : ℕ), f n)`. -/
def sigmaNatSucc (f : ℕ → Type u) : (Σ n, f n) ≃ Sum (f 0) (Σ n, f (n + 1)) :=
  ⟨fun x =>
    @Sigma.casesOn ℕ f (fun _ => Sum (f 0) (Σn, f (n + 1))) x fun n =>
      @Nat.casesOn (fun i => f i → Sum (f 0) (Σn : ℕ, f (n + 1))) n (fun x : f 0 => Sum.inl x)
        fun (n : ℕ) (x : f n.succ) => Sum.inr ⟨n, x⟩,
    Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun _ => id), by 

Proof state:
f : ℕ → Type u
⊢ LeftInverse (Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun x => id)) fun x =>
    Sigma.casesOn x fun n =>
      Nat.casesOn (motive := fun i => f i → f 0 ⊕ (n : ℕ) × f (n + 1)) n (fun x => inl x) fun n x =>
        inr { fst := n, snd := x }

Next tactic:
rintro ⟨n | n, x⟩
/-- An equivalence that separates out the 0th fiber of `(Σ (n : ℕ), f n)`. -/
def sigmaNatSucc (f : ℕ → Type u) : (Σ n, f n) ≃ Sum (f 0) (Σ n, f (n + 1)) :=
  ⟨fun x =>
    @Sigma.casesOn ℕ f (fun _ => Sum (f 0) (Σn, f (n + 1))) x fun n =>
      @Nat.casesOn (fun i => f i → Sum (f 0) (Σn : ℕ, f (n + 1))) n (fun x : f 0 => Sum.inl x)
        fun (n : ℕ) (x : f n.succ) => Sum.inr ⟨n, x⟩,
    Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun _ => id), by rintro ⟨n | n, x⟩ <;> 

Proof state:
case mk.zero
f : ℕ → Type u
x : f Nat.zero
⊢ Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun x => id)
      ((fun x =>
          Sigma.casesOn x fun n =>
            Nat.casesOn (motive := fun i => f i → f 0 ⊕ (n : ℕ) × f (n + 1)) n (fun x => inl x) fun n x =>
              inr { fst := n, snd := x })
        { fst := Nat.zero, snd := x }) =
    { fst := Nat.zero, snd := x }

Next tactic:
rfl
/-- An equivalence that separates out the 0th fiber of `(Σ (n : ℕ), f n)`. -/
def sigmaNatSucc (f : ℕ → Type u) : (Σ n, f n) ≃ Sum (f 0) (Σ n, f (n + 1)) :=
  ⟨fun x =>
    @Sigma.casesOn ℕ f (fun _ => Sum (f 0) (Σn, f (n + 1))) x fun n =>
      @Nat.casesOn (fun i => f i → Sum (f 0) (Σn : ℕ, f (n + 1))) n (fun x : f 0 => Sum.inl x)
        fun (n : ℕ) (x : f n.succ) => Sum.inr ⟨n, x⟩,
    Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun _ => id), by rintro ⟨n | n, x⟩ <;> 

Proof state:
case mk.succ
f : ℕ → Type u
n : ℕ
x : f (Nat.succ n)
⊢ Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun x => id)
      ((fun x =>
          Sigma.casesOn x fun n =>
            Nat.casesOn (motive := fun i => f i → f 0 ⊕ (n : ℕ) × f (n + 1)) n (fun x => inl x) fun n x =>
              inr { fst := n, snd := x })
        { fst := Nat.succ n, snd := x }) =
    { fst := Nat.succ n, snd := x }

Next tactic:
rfl
/-- An equivalence that separates out the 0th fiber of `(Σ (n : ℕ), f n)`. -/
def sigmaNatSucc (f : ℕ → Type u) : (Σ n, f n) ≃ Sum (f 0) (Σ n, f (n + 1)) :=
  ⟨fun x =>
    @Sigma.casesOn ℕ f (fun _ => Sum (f 0) (Σn, f (n + 1))) x fun n =>
      @Nat.casesOn (fun i => f i → Sum (f 0) (Σn : ℕ, f (n + 1))) n (fun x : f 0 => Sum.inl x)
        fun (n : ℕ) (x : f n.succ) => Sum.inr ⟨n, x⟩,
    Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun _ => id), by rintro ⟨n | n, x⟩ <;> rfl, by
    

Proof state:
f : ℕ → Type u
⊢ Function.RightInverse (Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun x => id)) fun x =>
    Sigma.casesOn x fun n =>
      Nat.casesOn (motive := fun i => f i → f 0 ⊕ (n : ℕ) × f (n + 1)) n (fun x => inl x) fun n x =>
        inr { fst := n, snd := x }

Next tactic:
rintro (x | ⟨n, x⟩)
/-- An equivalence that separates out the 0th fiber of `(Σ (n : ℕ), f n)`. -/
def sigmaNatSucc (f : ℕ → Type u) : (Σ n, f n) ≃ Sum (f 0) (Σ n, f (n + 1)) :=
  ⟨fun x =>
    @Sigma.casesOn ℕ f (fun _ => Sum (f 0) (Σn, f (n + 1))) x fun n =>
      @Nat.casesOn (fun i => f i → Sum (f 0) (Σn : ℕ, f (n + 1))) n (fun x : f 0 => Sum.inl x)
        fun (n : ℕ) (x : f n.succ) => Sum.inr ⟨n, x⟩,
    Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun _ => id), by rintro ⟨n | n, x⟩ <;> rfl, by
    rintro (x | ⟨n, x⟩) <;> 

Proof state:
case inl
f : ℕ → Type u
x : f 0
⊢ (fun x =>
        Sigma.casesOn x fun n =>
          Nat.casesOn (motive := fun i => f i → f 0 ⊕ (n : ℕ) × f (n + 1)) n (fun x => inl x) fun n x =>
            inr { fst := n, snd := x })
      (Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun x => id) (inl x)) =
    inl x

Next tactic:
rfl
/-- An equivalence that separates out the 0th fiber of `(Σ (n : ℕ), f n)`. -/
def sigmaNatSucc (f : ℕ → Type u) : (Σ n, f n) ≃ Sum (f 0) (Σ n, f (n + 1)) :=
  ⟨fun x =>
    @Sigma.casesOn ℕ f (fun _ => Sum (f 0) (Σn, f (n + 1))) x fun n =>
      @Nat.casesOn (fun i => f i → Sum (f 0) (Σn : ℕ, f (n + 1))) n (fun x : f 0 => Sum.inl x)
        fun (n : ℕ) (x : f n.succ) => Sum.inr ⟨n, x⟩,
    Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun _ => id), by rintro ⟨n | n, x⟩ <;> rfl, by
    rintro (x | ⟨n, x⟩) <;> 

Proof state:
case inr.mk
f : ℕ → Type u
n : ℕ
x : f (n + 1)
⊢ (fun x =>
        Sigma.casesOn x fun n =>
          Nat.casesOn (motive := fun i => f i → f 0 ⊕ (n : ℕ) × f (n + 1)) n (fun x => inl x) fun n x =>
            inr { fst := n, snd := x })
      (Sum.elim (Sigma.mk 0) (Sigma.map Nat.succ fun x => id) (inr { fst := n, snd := x })) =
    inr { fst := n, snd := x }

Next tactic:
rfl
/-- The product `Bool × α` is equivalent to `α ⊕ α`. -/
@[simps]
def boolProdEquivSum (α) : Bool × α ≃ Sum α α where
  toFun p := p.1.casesOn (inl p.2) (inr p.2)
  invFun := Sum.elim (Prod.mk false) (Prod.mk true)
  left_inv := by 

Proof state:
α : Type ?u.64356
⊢ LeftInverse (Sum.elim (Prod.mk false) (Prod.mk true)) fun p => Bool.casesOn p.1 (inl p.2) (inr p.2)

Next tactic:
rintro ⟨_ | _, _⟩
/-- The product `Bool × α` is equivalent to `α ⊕ α`. -/
@[simps]
def boolProdEquivSum (α) : Bool × α ≃ Sum α α where
  toFun p := p.1.casesOn (inl p.2) (inr p.2)
  invFun := Sum.elim (Prod.mk false) (Prod.mk true)
  left_inv := by rintro ⟨_ | _, _⟩ <;> 

Proof state:
case mk.false
α : Type ?u.64356
snd✝ : α
⊢ Sum.elim (Prod.mk false) (Prod.mk true) ((fun p => Bool.casesOn p.1 (inl p.2) (inr p.2)) (false, snd✝)) =
    (false, snd✝)

Next tactic:
rfl
/-- The product `Bool × α` is equivalent to `α ⊕ α`. -/
@[simps]
def boolProdEquivSum (α) : Bool × α ≃ Sum α α where
  toFun p := p.1.casesOn (inl p.2) (inr p.2)
  invFun := Sum.elim (Prod.mk false) (Prod.mk true)
  left_inv := by rintro ⟨_ | _, _⟩ <;> 

Proof state:
case mk.true
α : Type ?u.64356
snd✝ : α
⊢ Sum.elim (Prod.mk false) (Prod.mk true) ((fun p => Bool.casesOn p.1 (inl p.2) (inr p.2)) (true, snd✝)) = (true, snd✝)

Next tactic:
rfl
/-- The product `Bool × α` is equivalent to `α ⊕ α`. -/
@[simps]
def boolProdEquivSum (α) : Bool × α ≃ Sum α α where
  toFun p := p.1.casesOn (inl p.2) (inr p.2)
  invFun := Sum.elim (Prod.mk false) (Prod.mk true)
  left_inv := by rintro ⟨_ | _, _⟩ <;> rfl
  right_inv := by 

Proof state:
α : Type ?u.64356
⊢ Function.RightInverse (Sum.elim (Prod.mk false) (Prod.mk true)) fun p => Bool.casesOn p.1 (inl p.2) (inr p.2)

Next tactic:
rintro (_ | _)
/-- The product `Bool × α` is equivalent to `α ⊕ α`. -/
@[simps]
def boolProdEquivSum (α) : Bool × α ≃ Sum α α where
  toFun p := p.1.casesOn (inl p.2) (inr p.2)
  invFun := Sum.elim (Prod.mk false) (Prod.mk true)
  left_inv := by rintro ⟨_ | _, _⟩ <;> rfl
  right_inv := by rintro (_ | _) <;> 

Proof state:
case inl
α : Type ?u.64356
val✝ : α
⊢ (fun p => Bool.casesOn p.1 (inl p.2) (inr p.2)) (Sum.elim (Prod.mk false) (Prod.mk true) (inl val✝)) = inl val✝

Next tactic:
rfl
/-- The product `Bool × α` is equivalent to `α ⊕ α`. -/
@[simps]
def boolProdEquivSum (α) : Bool × α ≃ Sum α α where
  toFun p := p.1.casesOn (inl p.2) (inr p.2)
  invFun := Sum.elim (Prod.mk false) (Prod.mk true)
  left_inv := by rintro ⟨_ | _, _⟩ <;> rfl
  right_inv := by rintro (_ | _) <;> 

Proof state:
case inr
α : Type ?u.64356
val✝ : α
⊢ (fun p => Bool.casesOn p.1 (inl p.2) (inr p.2)) (Sum.elim (Prod.mk false) (Prod.mk true) (inr val✝)) = inr val✝

Next tactic:
rfl
/-- The set of natural numbers is equivalent to `ℕ ⊕ PUnit`. -/
def natEquivNatSumPUnit : ℕ ≃ Sum ℕ PUnit where
  toFun n := Nat.casesOn n (inr PUnit.unit) inl
  invFun := Sum.elim Nat.succ fun _ => 0
  left_inv n := by 

Proof state:
n : ℕ
⊢ Sum.elim succ (fun x => 0) ((fun n => Nat.casesOn n (inr PUnit.unit) inl) n) = n

Next tactic:
cases n
/-- The set of natural numbers is equivalent to `ℕ ⊕ PUnit`. -/
def natEquivNatSumPUnit : ℕ ≃ Sum ℕ PUnit where
  toFun n := Nat.casesOn n (inr PUnit.unit) inl
  invFun := Sum.elim Nat.succ fun _ => 0
  left_inv n := by cases n <;> 

Proof state:
case zero
⊢ Sum.elim succ (fun x => 0) ((fun n => Nat.casesOn n (inr PUnit.unit) inl) zero) = zero

Next tactic:
rfl
/-- The set of natural numbers is equivalent to `ℕ ⊕ PUnit`. -/
def natEquivNatSumPUnit : ℕ ≃ Sum ℕ PUnit where
  toFun n := Nat.casesOn n (inr PUnit.unit) inl
  invFun := Sum.elim Nat.succ fun _ => 0
  left_inv n := by cases n <;> 

Proof state:
case succ
n✝ : ℕ
⊢ Sum.elim succ (fun x => 0) ((fun n => Nat.casesOn n (inr PUnit.unit) inl) (succ n✝)) = succ n✝

Next tactic:
rfl
/-- The set of natural numbers is equivalent to `ℕ ⊕ PUnit`. -/
def natEquivNatSumPUnit : ℕ ≃ Sum ℕ PUnit where
  toFun n := Nat.casesOn n (inr PUnit.unit) inl
  invFun := Sum.elim Nat.succ fun _ => 0
  left_inv n := by cases n <;> rfl
  right_inv := by 

Proof state:
⊢ Function.RightInverse (Sum.elim succ fun x => 0) fun n => Nat.casesOn n (inr PUnit.unit) inl

Next tactic:
rintro (_ | _)
/-- The set of natural numbers is equivalent to `ℕ ⊕ PUnit`. -/
def natEquivNatSumPUnit : ℕ ≃ Sum ℕ PUnit where
  toFun n := Nat.casesOn n (inr PUnit.unit) inl
  invFun := Sum.elim Nat.succ fun _ => 0
  left_inv n := by cases n <;> rfl
  right_inv := by rintro (_ | _) <;> 

Proof state:
case inl
val✝ : ℕ
⊢ (fun n => Nat.casesOn n (inr PUnit.unit) inl) (Sum.elim succ (fun x => 0) (inl val✝)) = inl val✝

Next tactic:
rfl
/-- The set of natural numbers is equivalent to `ℕ ⊕ PUnit`. -/
def natEquivNatSumPUnit : ℕ ≃ Sum ℕ PUnit where
  toFun n := Nat.casesOn n (inr PUnit.unit) inl
  invFun := Sum.elim Nat.succ fun _ => 0
  left_inv n := by cases n <;> rfl
  right_inv := by rintro (_ | _) <;> 

Proof state:
case inr
val✝ : PUnit.{?u.65141 + 1}
⊢ (fun n => Nat.casesOn n (inr PUnit.unit) inl) (Sum.elim succ (fun x => 0) (inr val✝)) = inr val✝

Next tactic:
rfl
/-- The type of integer numbers is equivalent to `ℕ ⊕ ℕ`. -/
def intEquivNatSumNat : ℤ ≃ Sum ℕ ℕ where
  toFun z := Int.casesOn z inl inr
  invFun := Sum.elim Int.ofNat Int.negSucc
  left_inv := by 

Proof state:
⊢ LeftInverse (Sum.elim Int.ofNat Int.negSucc) fun z => Int.casesOn z inl inr

Next tactic:
rintro (m | n)
/-- The type of integer numbers is equivalent to `ℕ ⊕ ℕ`. -/
def intEquivNatSumNat : ℤ ≃ Sum ℕ ℕ where
  toFun z := Int.casesOn z inl inr
  invFun := Sum.elim Int.ofNat Int.negSucc
  left_inv := by rintro (m | n) <;> 

Proof state:
case ofNat
m : ℕ
⊢ Sum.elim Int.ofNat Int.negSucc ((fun z => Int.casesOn z inl inr) (Int.ofNat m)) = Int.ofNat m

Next tactic:
rfl
/-- The type of integer numbers is equivalent to `ℕ ⊕ ℕ`. -/
def intEquivNatSumNat : ℤ ≃ Sum ℕ ℕ where
  toFun z := Int.casesOn z inl inr
  invFun := Sum.elim Int.ofNat Int.negSucc
  left_inv := by rintro (m | n) <;> 

Proof state:
case negSucc
n : ℕ
⊢ Sum.elim Int.ofNat Int.negSucc ((fun z => Int.casesOn z inl inr) (Int.negSucc n)) = Int.negSucc n

Next tactic:
rfl
/-- The type of integer numbers is equivalent to `ℕ ⊕ ℕ`. -/
def intEquivNatSumNat : ℤ ≃ Sum ℕ ℕ where
  toFun z := Int.casesOn z inl inr
  invFun := Sum.elim Int.ofNat Int.negSucc
  left_inv := by rintro (m | n) <;> rfl
  right_inv := by 

Proof state:
⊢ Function.RightInverse (Sum.elim Int.ofNat Int.negSucc) fun z => Int.casesOn z inl inr

Next tactic:
rintro (m | n)
/-- The type of integer numbers is equivalent to `ℕ ⊕ ℕ`. -/
def intEquivNatSumNat : ℤ ≃ Sum ℕ ℕ where
  toFun z := Int.casesOn z inl inr
  invFun := Sum.elim Int.ofNat Int.negSucc
  left_inv := by rintro (m | n) <;> rfl
  right_inv := by rintro (m | n) <;> 

Proof state:
case inl
m : ℕ
⊢ (fun z => Int.casesOn z inl inr) (Sum.elim Int.ofNat Int.negSucc (inl m)) = inl m

Next tactic:
rfl
/-- The type of integer numbers is equivalent to `ℕ ⊕ ℕ`. -/
def intEquivNatSumNat : ℤ ≃ Sum ℕ ℕ where
  toFun z := Int.casesOn z inl inr
  invFun := Sum.elim Int.ofNat Int.negSucc
  left_inv := by rintro (m | n) <;> rfl
  right_inv := by rintro (m | n) <;> 

Proof state:
case inr
n : ℕ
⊢ (fun z => Int.casesOn z inl inr) (Sum.elim Int.ofNat Int.negSucc (inr n)) = inr n

Next tactic:
rfl
/-- An equivalence between `α` and `β` generates an equivalence between `List α` and `List β`. -/
def listEquivOfEquiv (e : α ≃ β) : List α ≃ List β where
  toFun := List.map e
  invFun := List.map e.symm
  left_inv l := by 

Proof state:
α : Type ?u.65679
β : Type ?u.65680
e : α ≃ β
l : List α
⊢ List.map (⇑e.symm) (List.map (⇑e) l) = l

Next tactic:
rw [List.map_map, e.symm_comp_self, List.map_id]
/-- An equivalence between `α` and `β` generates an equivalence between `List α` and `List β`. -/
def listEquivOfEquiv (e : α ≃ β) : List α ≃ List β where
  toFun := List.map e
  invFun := List.map e.symm
  left_inv l := by rw [List.map_map, e.symm_comp_self, List.map_id]
  right_inv l := by 

Proof state:
α : Type ?u.65679
β : Type ?u.65680
e : α ≃ β
l : List β
⊢ List.map (⇑e) (List.map (⇑e.symm) l) = l

Next tactic:
rw [List.map_map, e.self_comp_symm, List.map_id]
/-- If `α` is equivalent to `β` and the predicates `p : α → Prop` and `q : β → Prop` are equivalent
at corresponding points, then `{a // p a}` is equivalent to `{b // q b}`.
For the statement where `α = β`, that is, `e : perm α`, see `Perm.subtypePerm`. -/
def subtypeEquiv {p : α → Prop} {q : β → Prop} (e : α ≃ β) (h : ∀ a, p a ↔ q (e a)) :
    { a : α // p a } ≃ { b : β // q b } where
  toFun a := ⟨e a, (h _).mp a.property⟩
  invFun b := ⟨e.symm b, (h _).mpr ((e.apply_symm_apply b).symm ▸ b.property)⟩
  left_inv a := Subtype.ext <| by 

Proof state:
α : Sort ?u.66872
β : Sort ?u.66876
p : α → Prop
q : β → Prop
e : α ≃ β
h : ∀ (a : α), p a ↔ q (e a)
a : { a // p a }
⊢ ↑((fun b => { val := e.symm ↑b, property := (_ : p (e.symm ↑b)) })
        ((fun a => { val := e ↑a, property := (_ : q (e ↑a)) }) a)) =
    ↑a

Next tactic:
simp
/-- If `α` is equivalent to `β` and the predicates `p : α → Prop` and `q : β → Prop` are equivalent
at corresponding points, then `{a // p a}` is equivalent to `{b // q b}`.
For the statement where `α = β`, that is, `e : perm α`, see `Perm.subtypePerm`. -/
def subtypeEquiv {p : α → Prop} {q : β → Prop} (e : α ≃ β) (h : ∀ a, p a ↔ q (e a)) :
    { a : α // p a } ≃ { b : β // q b } where
  toFun a := ⟨e a, (h _).mp a.property⟩
  invFun b := ⟨e.symm b, (h _).mpr ((e.apply_symm_apply b).symm ▸ b.property)⟩
  left_inv a := Subtype.ext <| by simp
  right_inv b := Subtype.ext <| by 

Proof state:
α : Sort ?u.66872
β : Sort ?u.66876
p : α → Prop
q : β → Prop
e : α ≃ β
h : ∀ (a : α), p a ↔ q (e a)
b : { b // q b }
⊢ ↑((fun a => { val := e ↑a, property := (_ : q (e ↑a)) })
        ((fun b => { val := e.symm ↑b, property := (_ : p (e.symm ↑b)) }) b)) =
    ↑b

Next tactic:
simp
@[simp]
theorem subtypeEquiv_refl {p : α → Prop} (h : ∀ a, p a ↔ p (Equiv.refl _ a) := fun a => Iff.rfl) :
    (Equiv.refl α).subtypeEquiv h = Equiv.refl { a : α // p a } := by
  

Proof state:
α : Sort u_1
p : α → Prop
h : optParam (∀ (a : α), p a ↔ p ((Equiv.refl α) a)) (_ : ∀ (a : α), p a ↔ p a)
⊢ subtypeEquiv (Equiv.refl α) h = Equiv.refl { a // p a }

Next tactic:
ext
@[simp]
theorem subtypeEquiv_refl {p : α → Prop} (h : ∀ a, p a ↔ p (Equiv.refl _ a) := fun a => Iff.rfl) :
    (Equiv.refl α).subtypeEquiv h = Equiv.refl { a : α // p a } := by
  ext
  

Proof state:
case H.a
α : Sort u_1
p : α → Prop
h : optParam (∀ (a : α), p a ↔ p ((Equiv.refl α) a)) (_ : ∀ (a : α), p a ↔ p a)
x✝ : { a // p a }
⊢ ↑((subtypeEquiv (Equiv.refl α) h) x✝) = ↑((Equiv.refl { a // p a }) x✝)

Next tactic:
rfl
@[simp]
theorem subtypeEquiv_symm {p : α → Prop} {q : β → Prop} (e : α ≃ β) (h : ∀ a : α, p a ↔ q (e a)) :
    (e.subtypeEquiv h).symm =
      e.symm.subtypeEquiv fun a => by
        

Proof state:
α : Sort ?u.68205
β : Sort ?u.68209
p : α → Prop
q : β → Prop
e : α ≃ β
h : ∀ (a : α), p a ↔ q (e a)
a : β
⊢ q a ↔ p (e.symm a)

Next tactic:
convert (h <| e.symm a).symm
@[simp]
theorem subtypeEquiv_symm {p : α → Prop} {q : β → Prop} (e : α ≃ β) (h : ∀ a : α, p a ↔ q (e a)) :
    (e.subtypeEquiv h).symm =
      e.symm.subtypeEquiv fun a => by
        convert (h <| e.symm a).symm
        

Proof state:
case h.e'_1.h.e'_1
α : Sort ?u.68205
β : Sort ?u.68209
p : α → Prop
q : β → Prop
e : α ≃ β
h : ∀ (a : α), p a ↔ q (e a)
a : β
⊢ a = e (e.symm a)

Next tactic:
exact (e.apply_symm_apply a).symm
/-- If `α ≃ β`, then for any predicate `p : β → Prop` the subtype `{a // p (e a)}` is equivalent
to the subtype `{b // p b}`. -/
def subtypeEquivOfSubtype {p : β → Prop} (e : α ≃ β) : { a : α // p (e a) } ≃ { b : β // p b } :=
  subtypeEquiv e <| by 

Proof state:
β : Sort ?u.71562
α : Sort ?u.71567
p : β → Prop
e : α ≃ β
⊢ ∀ (a : α), p (e a) ↔ p (e a)

Next tactic:
simp
/-- A subtype of a subtype is equivalent to the subtype of elements satisfying both predicates. This
version allows the “inner” predicate to depend on `h : p a`. -/
@[simps]
def subtypeSubtypeEquivSubtypeExists (p : α → Prop) (q : Subtype p → Prop) :
    Subtype q ≃ { a : α // ∃ h : p a, q ⟨a, h⟩ } :=
  ⟨fun a =>
    ⟨a.1, a.1.2, by
      

Proof state:
α : Sort ?u.72480
p : α → Prop
q : Subtype p → Prop
a : Subtype q
⊢ q { val := ↑↑a, property := (_ : p ↑↑a) }

Next tactic:
rcases a with ⟨⟨a, hap⟩, haq⟩
/-- A subtype of a subtype is equivalent to the subtype of elements satisfying both predicates. This
version allows the “inner” predicate to depend on `h : p a`. -/
@[simps]
def subtypeSubtypeEquivSubtypeExists (p : α → Prop) (q : Subtype p → Prop) :
    Subtype q ≃ { a : α // ∃ h : p a, q ⟨a, h⟩ } :=
  ⟨fun a =>
    ⟨a.1, a.1.2, by
      rcases a with ⟨⟨a, hap⟩, haq⟩
      

Proof state:
case mk.mk
α : Sort ?u.72480
p : α → Prop
q : Subtype p → Prop
a : α
hap : p a
haq : q { val := a, property := hap }
⊢ q
    { val := ↑↑{ val := { val := a, property := hap }, property := haq },
      property := (_ : p ↑↑{ val := { val := a, property := hap }, property := haq }) }

Next tactic:
exact haq
/-- If for each `x` we have `p x ↔ q (f x)`, then `Σ y : {y // q y}, f ⁻¹' {y}` is equivalent
to `{x // p x}`. -/
def sigmaSubtypeFiberEquivSubtype {α β : Type*} (f : α → β) {p : α → Prop} {q : β → Prop}
    (h : ∀ x, p x ↔ q (f x)) : (Σ y : Subtype q, { x : α // f x = y }) ≃ Subtype p :=
  calc
    (Σy : Subtype q, { x : α // f x = y }) ≃ Σy :
        Subtype q, { x : Subtype p // Subtype.mk (f x) ((h x).1 x.2) = y } := by 

Proof state:
α : Type u_1
β : Type u_2
f : α → β
p : α → Prop
q : β → Prop
h : ∀ (x : α), p x ↔ q (f x)
⊢ (y : Subtype q) × { x // f x = ↑y } ≃ (y : Subtype q) × { x // { val := f ↑x, property := (_ : q (f ↑x)) } = y }

Next tactic:
{
          apply sigmaCongrRight
          intro y
          apply Equiv.symm
          refine' (subtypeSubtypeEquivSubtypeExists _ _).trans (subtypeEquivRight _)
          intro x
          exact ⟨fun ⟨hp, h'⟩ => congr_arg Subtype.val h', fun h' => ⟨(h x).2 (h'.symm ▸ y.2),
            Subtype.eq h'⟩⟩ }
/-- If for each `x` we have `p x ↔ q (f x)`, then `Σ y : {y // q y}, f ⁻¹' {y}` is equivalent
to `{x // p x}`. -/
def sigmaSubtypeFiberEquivSubtype {α β : Type*} (f : α → β) {p : α → Prop} {q : β → Prop}
    (h : ∀ x, p x ↔ q (f x)) : (Σ y : Subtype q, { x : α // f x = y }) ≃ Subtype p :=
  calc
    (Σy : Subtype q, { x : α // f x = y }) ≃ Σy :
        Subtype q, { x : Subtype p // Subtype.mk (f x) ((h x).1 x.2) = y } := by {
          

Proof state:
α : Type u_1
β : Type u_2
f : α → β
p : α → Prop
q : β → Prop
h : ∀ (x : α), p x ↔ q (f x)
⊢ (y : Subtype q) × { x // f x = ↑y } ≃ (y : Subtype q) × { x // { val := f ↑x, property := (_ : q (f ↑x)) } = y }

Next tactic:
apply sigmaCongrRight
/-- If for each `x` we have `p x ↔ q (f x)`, then `Σ y : {y // q y}, f ⁻¹' {y}` is equivalent
to `{x // p x}`. -/
def sigmaSubtypeFiberEquivSubtype {α β : Type*} (f : α → β) {p : α → Prop} {q : β → Prop}
    (h : ∀ x, p x ↔ q (f x)) : (Σ y : Subtype q, { x : α // f x = y }) ≃ Subtype p :=
  calc
    (Σy : Subtype q, { x : α // f x = y }) ≃ Σy :
        Subtype q, { x : Subtype p // Subtype.mk (f x) ((h x).1 x.2) = y } := by {
          apply sigmaCongrRight
          

Proof state:
case F
α : Type u_1
β : Type u_2
f : α → β
p : α → Prop
q : β → Prop
h : ∀ (x : α), p x ↔ q (f x)
⊢ (a : Subtype q) → { x // f x = ↑a } ≃ { x // { val := f ↑x, property := (_ : q (f ↑x)) } = a }

Next tactic:
intro y
/-- If for each `x` we have `p x ↔ q (f x)`, then `Σ y : {y // q y}, f ⁻¹' {y}` is equivalent
to `{x // p x}`. -/
def sigmaSubtypeFiberEquivSubtype {α β : Type*} (f : α → β) {p : α → Prop} {q : β → Prop}
    (h : ∀ x, p x ↔ q (f x)) : (Σ y : Subtype q, { x : α // f x = y }) ≃ Subtype p :=
  calc
    (Σy : Subtype q, { x : α // f x = y }) ≃ Σy :
        Subtype q, { x : Subtype p // Subtype.mk (f x) ((h x).1 x.2) = y } := by {
          apply sigmaCongrRight
          intro y
          

Proof state:
case F
α : Type u_1
β : Type u_2
f : α → β
p : α → Prop
q : β → Prop
h : ∀ (x : α), p x ↔ q (f x)
y : Subtype q
⊢ { x // f x = ↑y } ≃ { x // { val := f ↑x, property := (_ : q (f ↑x)) } = y }

Next tactic:
apply Equiv.symm
/-- If for each `x` we have `p x ↔ q (f x)`, then `Σ y : {y // q y}, f ⁻¹' {y}` is equivalent
to `{x // p x}`. -/
def sigmaSubtypeFiberEquivSubtype {α β : Type*} (f : α → β) {p : α → Prop} {q : β → Prop}
    (h : ∀ x, p x ↔ q (f x)) : (Σ y : Subtype q, { x : α // f x = y }) ≃ Subtype p :=
  calc
    (Σy : Subtype q, { x : α // f x = y }) ≃ Σy :
        Subtype q, { x : Subtype p // Subtype.mk (f x) ((h x).1 x.2) = y } := by {
          apply sigmaCongrRight
          intro y
          apply Equiv.symm
          

Proof state:
case F.e
α : Type u_1
β : Type u_2
f : α → β
p : α → Prop
q : β → Prop
h : ∀ (x : α), p x ↔ q (f x)
y : Subtype q
⊢ { x // { val := f ↑x, property := (_ : q (f ↑x)) } = y } ≃ { x // f x = ↑y }

Next tactic:
refine' (subtypeSubtypeEquivSubtypeExists _ _).trans (subtypeEquivRight _)
/-- If for each `x` we have `p x ↔ q (f x)`, then `Σ y : {y // q y}, f ⁻¹' {y}` is equivalent
to `{x // p x}`. -/
def sigmaSubtypeFiberEquivSubtype {α β : Type*} (f : α → β) {p : α → Prop} {q : β → Prop}
    (h : ∀ x, p x ↔ q (f x)) : (Σ y : Subtype q, { x : α // f x = y }) ≃ Subtype p :=
  calc
    (Σy : Subtype q, { x : α // f x = y }) ≃ Σy :
        Subtype q, { x : Subtype p // Subtype.mk (f x) ((h x).1 x.2) = y } := by {
          apply sigmaCongrRight
          intro y
          apply Equiv.symm
          refine' (subtypeSubtypeEquivSubtypeExists _ _).trans (subtypeEquivRight _)
          

Proof state:
case F.e
α : Type u_1
β : Type u_2
f : α → β
p : α → Prop
q : β → Prop
h : ∀ (x : α), p x ↔ q (f x)
y : Subtype q
⊢ ∀ (x : α),
    (∃ h_1, { val := f ↑{ val := x, property := h_1 }, property := (_ : q (f ↑{ val := x, property := h_1 })) } = y) ↔
      f x = ↑y

Next tactic:
intro x
/-- If for each `x` we have `p x ↔ q (f x)`, then `Σ y : {y // q y}, f ⁻¹' {y}` is equivalent
to `{x // p x}`. -/
def sigmaSubtypeFiberEquivSubtype {α β : Type*} (f : α → β) {p : α → Prop} {q : β → Prop}
    (h : ∀ x, p x ↔ q (f x)) : (Σ y : Subtype q, { x : α // f x = y }) ≃ Subtype p :=
  calc
    (Σy : Subtype q, { x : α // f x = y }) ≃ Σy :
        Subtype q, { x : Subtype p // Subtype.mk (f x) ((h x).1 x.2) = y } := by {
          apply sigmaCongrRight
          intro y
          apply Equiv.symm
          refine' (subtypeSubtypeEquivSubtypeExists _ _).trans (subtypeEquivRight _)
          intro x
          

Proof state:
case F.e
α : Type u_1
β : Type u_2
f : α → β
p : α → Prop
q : β → Prop
h : ∀ (x : α), p x ↔ q (f x)
y : Subtype q
x : α
⊢ (∃ h_1, { val := f ↑{ val := x, property := h_1 }, property := (_ : q (f ↑{ val := x, property := h_1 })) } = y) ↔
    f x = ↑y

Next tactic:
exact ⟨fun ⟨hp, h'⟩ => congr_arg Subtype.val h', fun h' => ⟨(h x).2 (h'.symm ▸ y.2),
            Subtype.eq h'⟩⟩
/-- A sigma type over an `Option` is equivalent to the sigma set over the original type,
if the fiber is empty at none. -/
def sigmaOptionEquivOfSome (p : Option α → Type v) (h : p none → False) :
    (Σ x : Option α, p x) ≃ Σ x : α, p (some x) :=
  haveI h' : ∀ x, p x → x.isSome := by
    

Proof state:
α : Type ?u.77889
p : Option α → Type v
h : p none → False
⊢ ∀ (x : Option α), p x → Option.isSome x = true

Next tactic:
intro x