l3lab/ntp-mathlib huggingface dataset:
/-- `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 <;> rfl,
    fun s => by 

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

Next tactic:
rcases s with (_ | ⟨⟨⟩⟩)
/-- `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 <;> rfl,
    fun s => by rcases s with (_ | ⟨⟨⟩⟩) <;> 

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

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 <;> rfl,
    fun s => by rcases s with (_ | ⟨⟨⟩⟩) <;> 

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

Next tactic:
rfl
/-- The product over `Option α` of `β a` is the binary product of the
product over `α` of `β (some α)` and `β none` -/
@[simps]
def piOptionEquivProd {β : Option α → Type*} :
    (∀ a : Option α, β a) ≃ β none × ∀ a : α, β (some a) where
  toFun f := (f none, fun a => f (some a))
  invFun x a := Option.casesOn a x.fst x.snd
  left_inv f := funext fun a => by 

Proof state:
α : Type ?u.26089
β : Option α → Type u_1
f : (a : Option α) → β a
a : Option α
⊢ (fun x a => Option.casesOn a x.1 x.2) ((fun f => (f none, fun a => f (some a))) f) a = f a

Next tactic:
cases a
/-- The product over `Option α` of `β a` is the binary product of the
product over `α` of `β (some α)` and `β none` -/
@[simps]
def piOptionEquivProd {β : Option α → Type*} :
    (∀ a : Option α, β a) ≃ β none × ∀ a : α, β (some a) where
  toFun f := (f none, fun a => f (some a))
  invFun x a := Option.casesOn a x.fst x.snd
  left_inv f := funext fun a => by cases a <;> 

Proof state:
case none
α : Type ?u.26089
β : Option α → Type u_1
f : (a : Option α) → β a
⊢ (fun x a => Option.casesOn a x.1 x.2) ((fun f => (f none, fun a => f (some a))) f) none = f none

Next tactic:
rfl
/-- The product over `Option α` of `β a` is the binary product of the
product over `α` of `β (some α)` and `β none` -/
@[simps]
def piOptionEquivProd {β : Option α → Type*} :
    (∀ a : Option α, β a) ≃ β none × ∀ a : α, β (some a) where
  toFun f := (f none, fun a => f (some a))
  invFun x a := Option.casesOn a x.fst x.snd
  left_inv f := funext fun a => by cases a <;> 

Proof state:
case some
α : Type ?u.26089
β : Option α → Type u_1
f : (a : Option α) → β a
val✝ : α
⊢ (fun x a => Option.casesOn a x.1 x.2) ((fun f => (f none, fun a => f (some a))) f) (some val✝) = f (some val✝)

Next tactic:
rfl
/-- The product over `Option α` of `β a` is the binary product of the
product over `α` of `β (some α)` and `β none` -/
@[simps]
def piOptionEquivProd {β : Option α → Type*} :
    (∀ a : Option α, β a) ≃ β none × ∀ a : α, β (some a) where
  toFun f := (f none, fun a => f (some a))
  invFun x a := Option.casesOn a x.fst x.snd
  left_inv f := funext fun a => by cases a <;> rfl
  right_inv x := by 

Proof state:
α : Type ?u.26089
β : Option α → Type u_1
x : β none × ((a : α) → β (some a))
⊢ (fun f => (f none, fun a => f (some a))) ((fun x a => Option.casesOn a x.1 x.2) x) = x

Next tactic:
simp
/-- `α ⊕ β` is equivalent to a `Sigma`-type over `Bool`. Note that this definition assumes `α` and
`β` to be types from the same universe, so it cannot be used directly to transfer theorems about
sigma types to theorems about sum types. In many cases one can use `ULift` to work around this
difficulty. -/
def sumEquivSigmaBool (α β : Type u) : Sum α β ≃ Σ b : Bool, b.casesOn α β :=
  ⟨fun s => s.elim (fun x => ⟨false, x⟩) fun x => ⟨true, x⟩, fun s =>
    match s with
    | ⟨false, a⟩ => inl a
    | ⟨true, b⟩ => inr b,
    fun s => by 

Proof state:
α β : Type u
s : α ⊕ β
⊢ (fun s =>
        match s with
        | { fst := false, snd := a } => inl a
        | { fst := true, snd := b } => inr b)
      ((fun s => Sum.elim (fun x => { fst := false, snd := x }) (fun x => { fst := true, snd := x }) s) s) =
    s

Next tactic:
cases s
/-- `α ⊕ β` is equivalent to a `Sigma`-type over `Bool`. Note that this definition assumes `α` and
`β` to be types from the same universe, so it cannot be used directly to transfer theorems about
sigma types to theorems about sum types. In many cases one can use `ULift` to work around this
difficulty. -/
def sumEquivSigmaBool (α β : Type u) : Sum α β ≃ Σ b : Bool, b.casesOn α β :=
  ⟨fun s => s.elim (fun x => ⟨false, x⟩) fun x => ⟨true, x⟩, fun s =>
    match s with
    | ⟨false, a⟩ => inl a
    | ⟨true, b⟩ => inr b,
    fun s => by cases s <;> 

Proof state:
case inl
α β : Type u
val✝ : α
⊢ (fun s =>
        match s with
        | { fst := false, snd := a } => inl a
        | { fst := true, snd := b } => inr b)
      ((fun s => Sum.elim (fun x => { fst := false, snd := x }) (fun x => { fst := true, snd := x }) s) (inl val✝)) =
    inl val✝

Next tactic:
rfl
/-- `α ⊕ β` is equivalent to a `Sigma`-type over `Bool`. Note that this definition assumes `α` and
`β` to be types from the same universe, so it cannot be used directly to transfer theorems about
sigma types to theorems about sum types. In many cases one can use `ULift` to work around this
difficulty. -/
def sumEquivSigmaBool (α β : Type u) : Sum α β ≃ Σ b : Bool, b.casesOn α β :=
  ⟨fun s => s.elim (fun x => ⟨false, x⟩) fun x => ⟨true, x⟩, fun s =>
    match s with
    | ⟨false, a⟩ => inl a
    | ⟨true, b⟩ => inr b,
    fun s => by cases s <;> 

Proof state:
case inr
α β : Type u
val✝ : β
⊢ (fun s =>
        match s with
        | { fst := false, snd := a } => inl a
        | { fst := true, snd := b } => inr b)
      ((fun s => Sum.elim (fun x => { fst := false, snd := x }) (fun x => { fst := true, snd := x }) s) (inr val✝)) =
    inr val✝

Next tactic:
rfl
/-- `α ⊕ β` is equivalent to a `Sigma`-type over `Bool`. Note that this definition assumes `α` and
`β` to be types from the same universe, so it cannot be used directly to transfer theorems about
sigma types to theorems about sum types. In many cases one can use `ULift` to work around this
difficulty. -/
def sumEquivSigmaBool (α β : Type u) : Sum α β ≃ Σ b : Bool, b.casesOn α β :=
  ⟨fun s => s.elim (fun x => ⟨false, x⟩) fun x => ⟨true, x⟩, fun s =>
    match s with
    | ⟨false, a⟩ => inl a
    | ⟨true, b⟩ => inr b,
    fun s => by cases s <;> rfl, fun s => by 

Proof state:
α β : Type u
s : (b : Bool) × Bool.casesOn b α β
⊢ (fun s => Sum.elim (fun x => { fst := false, snd := x }) (fun x => { fst := true, snd := x }) s)
      ((fun s =>
          match s with
          | { fst := false, snd := a } => inl a
          | { fst := true, snd := b } => inr b)
        s) =
    s

Next tactic:
rcases s with ⟨_ | _, _⟩
/-- `α ⊕ β` is equivalent to a `Sigma`-type over `Bool`. Note that this definition assumes `α` and
`β` to be types from the same universe, so it cannot be used directly to transfer theorems about
sigma types to theorems about sum types. In many cases one can use `ULift` to work around this
difficulty. -/
def sumEquivSigmaBool (α β : Type u) : Sum α β ≃ Σ b : Bool, b.casesOn α β :=
  ⟨fun s => s.elim (fun x => ⟨false, x⟩) fun x => ⟨true, x⟩, fun s =>
    match s with
    | ⟨false, a⟩ => inl a
    | ⟨true, b⟩ => inr b,
    fun s => by cases s <;> rfl, fun s => by rcases s with ⟨_ | _, _⟩ <;> 

Proof state:
case mk.false
α β : Type u
snd✝ : Bool.casesOn false α β
⊢ (fun s => Sum.elim (fun x => { fst := false, snd := x }) (fun x => { fst := true, snd := x }) s)
      ((fun s =>
          match s with
          | { fst := false, snd := a } => inl a
          | { fst := true, snd := b } => inr b)
        { fst := false, snd := snd✝ }) =
    { fst := false, snd := snd✝ }

Next tactic:
rfl
/-- `α ⊕ β` is equivalent to a `Sigma`-type over `Bool`. Note that this definition assumes `α` and
`β` to be types from the same universe, so it cannot be used directly to transfer theorems about
sigma types to theorems about sum types. In many cases one can use `ULift` to work around this
difficulty. -/
def sumEquivSigmaBool (α β : Type u) : Sum α β ≃ Σ b : Bool, b.casesOn α β :=
  ⟨fun s => s.elim (fun x => ⟨false, x⟩) fun x => ⟨true, x⟩, fun s =>
    match s with
    | ⟨false, a⟩ => inl a
    | ⟨true, b⟩ => inr b,
    fun s => by cases s <;> rfl, fun s => by rcases s with ⟨_ | _, _⟩ <;> 

Proof state:
case mk.true
α β : Type u
snd✝ : Bool.casesOn true α β
⊢ (fun s => Sum.elim (fun x => { fst := false, snd := x }) (fun x => { fst := true, snd := x }) s)
      ((fun s =>
          match s with
          | { fst := false, snd := a } => inl a
          | { fst := true, snd := b } => inr b)
        { fst := true, snd := snd✝ }) =
    { fst := true, snd := snd✝ }

Next tactic:
rfl
/-- For any predicate `p` on `α`,
the sum of the two subtypes `{a // p a}` and its complement `{a // ¬ p a}`
is naturally equivalent to `α`.

See `subtypeOrEquiv` for sum types over subtypes `{x // p x}` and `{x // q x}`
that are not necessarily `IsCompl p q`.  -/
def sumCompl {α : Type*} (p : α → Prop) [DecidablePred p] :
    Sum { a // p a } { a // ¬p a } ≃ α where
  toFun := Sum.elim Subtype.val Subtype.val
  invFun a := if h : p a then Sum.inl ⟨a, h⟩ else Sum.inr ⟨a, h⟩
  left_inv := by
    

Proof state:
α : Type u_1
p : α → Prop
inst✝ : DecidablePred p
⊢ LeftInverse (fun a => if h : p a then Sum.inl { val := a, property := h } else Sum.inr { val := a, property := h })
    (Sum.elim Subtype.val Subtype.val)

Next tactic:
rintro (⟨x, hx⟩ | ⟨x, hx⟩)
/-- For any predicate `p` on `α`,
the sum of the two subtypes `{a // p a}` and its complement `{a // ¬ p a}`
is naturally equivalent to `α`.

See `subtypeOrEquiv` for sum types over subtypes `{x // p x}` and `{x // q x}`
that are not necessarily `IsCompl p q`.  -/
def sumCompl {α : Type*} (p : α → Prop) [DecidablePred p] :
    Sum { a // p a } { a // ¬p a } ≃ α where
  toFun := Sum.elim Subtype.val Subtype.val
  invFun a := if h : p a then Sum.inl ⟨a, h⟩ else Sum.inr ⟨a, h⟩
  left_inv := by
    rintro (⟨x, hx⟩ | ⟨x, hx⟩) <;> 

Proof state:
case inl.mk
α : Type u_1
p : α → Prop
inst✝ : DecidablePred p
x : α
hx : p x
⊢ (fun a => if h : p a then Sum.inl { val := a, property := h } else Sum.inr { val := a, property := h })
      (Sum.elim Subtype.val Subtype.val (Sum.inl { val := x, property := hx })) =
    Sum.inl { val := x, property := hx }

Next tactic:
dsimp
/-- For any predicate `p` on `α`,
the sum of the two subtypes `{a // p a}` and its complement `{a // ¬ p a}`
is naturally equivalent to `α`.

See `subtypeOrEquiv` for sum types over subtypes `{x // p x}` and `{x // q x}`
that are not necessarily `IsCompl p q`.  -/
def sumCompl {α : Type*} (p : α → Prop) [DecidablePred p] :
    Sum { a // p a } { a // ¬p a } ≃ α where
  toFun := Sum.elim Subtype.val Subtype.val
  invFun a := if h : p a then Sum.inl ⟨a, h⟩ else Sum.inr ⟨a, h⟩
  left_inv := by
    rintro (⟨x, hx⟩ | ⟨x, hx⟩) <;> 

Proof state:
case inr.mk
α : Type u_1
p : α → Prop
inst✝ : DecidablePred p
x : α
hx : ¬p x
⊢ (fun a => if h : p a then Sum.inl { val := a, property := h } else Sum.inr { val := a, property := h })
      (Sum.elim Subtype.val Subtype.val (Sum.inr { val := x, property := hx })) =
    Sum.inr { val := x, property := hx }

Next tactic:
dsimp
/-- For any predicate `p` on `α`,
the sum of the two subtypes `{a // p a}` and its complement `{a // ¬ p a}`
is naturally equivalent to `α`.

See `subtypeOrEquiv` for sum types over subtypes `{x // p x}` and `{x // q x}`
that are not necessarily `IsCompl p q`.  -/
def sumCompl {α : Type*} (p : α → Prop) [DecidablePred p] :
    Sum { a // p a } { a // ¬p a } ≃ α where
  toFun := Sum.elim Subtype.val Subtype.val
  invFun a := if h : p a then Sum.inl ⟨a, h⟩ else Sum.inr ⟨a, h⟩
  left_inv := by
    rintro (⟨x, hx⟩ | ⟨x, hx⟩) <;> dsimp
    · 

Proof state:
case inl.mk
α : Type u_1
p : α → Prop
inst✝ : DecidablePred p
x : α
hx : p x
⊢ (if h : p x then Sum.inl { val := x, property := h } else Sum.inr { val := x, property := h }) =
    Sum.inl { val := x, property := hx }

Next tactic:
rw [dif_pos]
/-- For any predicate `p` on `α`,
the sum of the two subtypes `{a // p a}` and its complement `{a // ¬ p a}`
is naturally equivalent to `α`.

See `subtypeOrEquiv` for sum types over subtypes `{x // p x}` and `{x // q x}`
that are not necessarily `IsCompl p q`.  -/
def sumCompl {α : Type*} (p : α → Prop) [DecidablePred p] :
    Sum { a // p a } { a // ¬p a } ≃ α where
  toFun := Sum.elim Subtype.val Subtype.val
  invFun a := if h : p a then Sum.inl ⟨a, h⟩ else Sum.inr ⟨a, h⟩
  left_inv := by
    rintro (⟨x, hx⟩ | ⟨x, hx⟩) <;> dsimp
    · rw [dif_pos]
    · 

Proof state:
case inr.mk
α : Type u_1
p : α → Prop
inst✝ : DecidablePred p
x : α
hx : ¬p x
⊢ (if h : p x then Sum.inl { val := x, property := h } else Sum.inr { val := x, property := h }) =
    Sum.inr { val := x, property := hx }

Next tactic:
rw [dif_neg]
/-- For any predicate `p` on `α`,
the sum of the two subtypes `{a // p a}` and its complement `{a // ¬ p a}`
is naturally equivalent to `α`.

See `subtypeOrEquiv` for sum types over subtypes `{x // p x}` and `{x // q x}`
that are not necessarily `IsCompl p q`.  -/
def sumCompl {α : Type*} (p : α → Prop) [DecidablePred p] :
    Sum { a // p a } { a // ¬p a } ≃ α where
  toFun := Sum.elim Subtype.val Subtype.val
  invFun a := if h : p a then Sum.inl ⟨a, h⟩ else Sum.inr ⟨a, h⟩
  left_inv := by
    rintro (⟨x, hx⟩ | ⟨x, hx⟩) <;> dsimp
    · rw [dif_pos]
    · rw [dif_neg]
  right_inv a := by
    

Proof state:
α : Type u_1
p : α → Prop
inst✝ : DecidablePred p
a : α
⊢ Sum.elim Subtype.val Subtype.val
      ((fun a => if h : p a then Sum.inl { val := a, property := h } else Sum.inr { val := a, property := h }) a) =
    a

Next tactic:
dsimp
/-- For any predicate `p` on `α`,
the sum of the two subtypes `{a // p a}` and its complement `{a // ¬ p a}`
is naturally equivalent to `α`.

See `subtypeOrEquiv` for sum types over subtypes `{x // p x}` and `{x // q x}`
that are not necessarily `IsCompl p q`.  -/
def sumCompl {α : Type*} (p : α → Prop) [DecidablePred p] :
    Sum { a // p a } { a // ¬p a } ≃ α where
  toFun := Sum.elim Subtype.val Subtype.val
  invFun a := if h : p a then Sum.inl ⟨a, h⟩ else Sum.inr ⟨a, h⟩
  left_inv := by
    rintro (⟨x, hx⟩ | ⟨x, hx⟩) <;> dsimp
    · rw [dif_pos]
    · rw [dif_neg]
  right_inv a := by
    dsimp
    

Proof state:
α : Type u_1
p : α → Prop
inst✝ : DecidablePred p
a : α
⊢ Sum.elim Subtype.val Subtype.val
      (if h : p a then Sum.inl { val := a, property := h } else Sum.inr { val := a, property := h }) =
    a

Next tactic:
split_ifs
/-- For any predicate `p` on `α`,
the sum of the two subtypes `{a // p a}` and its complement `{a // ¬ p a}`
is naturally equivalent to `α`.

See `subtypeOrEquiv` for sum types over subtypes `{x // p x}` and `{x // q x}`
that are not necessarily `IsCompl p q`.  -/
def sumCompl {α : Type*} (p : α → Prop) [DecidablePred p] :
    Sum { a // p a } { a // ¬p a } ≃ α where
  toFun := Sum.elim Subtype.val Subtype.val
  invFun a := if h : p a then Sum.inl ⟨a, h⟩ else Sum.inr ⟨a, h⟩
  left_inv := by
    rintro (⟨x, hx⟩ | ⟨x, hx⟩) <;> dsimp
    · rw [dif_pos]
    · rw [dif_neg]
  right_inv a := by
    dsimp
    split_ifs <;> 

Proof state:
case pos
α : Type u_1
p : α → Prop
inst✝ : DecidablePred p
a : α
h✝ : p a
⊢ Sum.elim Subtype.val Subtype.val (Sum.inl { val := a, property := h✝ }) = a

Next tactic:
rfl
/-- For any predicate `p` on `α`,
the sum of the two subtypes `{a // p a}` and its complement `{a // ¬ p a}`
is naturally equivalent to `α`.

See `subtypeOrEquiv` for sum types over subtypes `{x // p x}` and `{x // q x}`
that are not necessarily `IsCompl p q`.  -/
def sumCompl {α : Type*} (p : α → Prop) [DecidablePred p] :
    Sum { a // p a } { a // ¬p a } ≃ α where
  toFun := Sum.elim Subtype.val Subtype.val
  invFun a := if h : p a then Sum.inl ⟨a, h⟩ else Sum.inr ⟨a, h⟩
  left_inv := by
    rintro (⟨x, hx⟩ | ⟨x, hx⟩) <;> dsimp
    · rw [dif_pos]
    · rw [dif_neg]
  right_inv a := by
    dsimp
    split_ifs <;> 

Proof state:
case neg
α : Type u_1
p : α → Prop
inst✝ : DecidablePred p
a : α
h✝ : ¬p a
⊢ Sum.elim Subtype.val Subtype.val (Sum.inr { val := a, property := h✝ }) = a

Next tactic:
rfl
theorem Perm.subtypeCongr.apply (a : ε) : ep.subtypeCongr en a =
    if h : p a then (ep ⟨a, h⟩ : ε) else en ⟨a, h⟩ := by
  

Proof state:
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
a : ε
⊢ (subtypeCongr ep en) a = if h : p a then ↑(ep { val := a, property := h }) else ↑(en { val := a, property := h })

Next tactic:
by_cases h : p a
theorem Perm.subtypeCongr.apply (a : ε) : ep.subtypeCongr en a =
    if h : p a then (ep ⟨a, h⟩ : ε) else en ⟨a, h⟩ := by
  by_cases h : p a <;> 

Proof state:
case pos
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
a : ε
h : p a
⊢ (subtypeCongr ep en) a = if h : p a then ↑(ep { val := a, property := h }) else ↑(en { val := a, property := h })

Next tactic:
simp [Perm.subtypeCongr, h]
theorem Perm.subtypeCongr.apply (a : ε) : ep.subtypeCongr en a =
    if h : p a then (ep ⟨a, h⟩ : ε) else en ⟨a, h⟩ := by
  by_cases h : p a <;> 

Proof state:
case neg
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
a : ε
h : ¬p a
⊢ (subtypeCongr ep en) a = if h : p a then ↑(ep { val := a, property := h }) else ↑(en { val := a, property := h })

Next tactic:
simp [Perm.subtypeCongr, h]
@[simp]
theorem Perm.subtypeCongr.left_apply {a : ε} (h : p a) : ep.subtypeCongr en a = ep ⟨a, h⟩ := by
  

Proof state:
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
a : ε
h : p a
⊢ (subtypeCongr ep en) a = ↑(ep { val := a, property := h })

Next tactic:
simp [Perm.subtypeCongr.apply, h]
@[simp]
theorem Perm.subtypeCongr.right_apply {a : ε} (h : ¬p a) : ep.subtypeCongr en a = en ⟨a, h⟩ := by
  

Proof state:
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
a : ε
h : ¬p a
⊢ (subtypeCongr ep en) a = ↑(en { val := a, property := h })

Next tactic:
simp [Perm.subtypeCongr.apply, h]
@[simp]
theorem Perm.subtypeCongr.refl :
    Perm.subtypeCongr (Equiv.refl { a // p a }) (Equiv.refl { a // ¬p a }) = Equiv.refl ε := by
  

Proof state:
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
⊢ subtypeCongr (Equiv.refl { a // p a }) (Equiv.refl { a // ¬p a }) = Equiv.refl ε

Next tactic:
ext x
@[simp]
theorem Perm.subtypeCongr.refl :
    Perm.subtypeCongr (Equiv.refl { a // p a }) (Equiv.refl { a // ¬p a }) = Equiv.refl ε := by
  ext x
  

Proof state:
case H
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
⊢ (subtypeCongr (Equiv.refl { a // p a }) (Equiv.refl { a // ¬p a })) x = (Equiv.refl ε) x

Next tactic:
by_cases h:p x
@[simp]
theorem Perm.subtypeCongr.refl :
    Perm.subtypeCongr (Equiv.refl { a // p a }) (Equiv.refl { a // ¬p a }) = Equiv.refl ε := by
  ext x
  by_cases h:p x <;> 

Proof state:
case pos
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
h : p x
⊢ (subtypeCongr (Equiv.refl { a // p a }) (Equiv.refl { a // ¬p a })) x = (Equiv.refl ε) x

Next tactic:
simp [h]
@[simp]
theorem Perm.subtypeCongr.refl :
    Perm.subtypeCongr (Equiv.refl { a // p a }) (Equiv.refl { a // ¬p a }) = Equiv.refl ε := by
  ext x
  by_cases h:p x <;> 

Proof state:
case neg
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
h : ¬p x
⊢ (subtypeCongr (Equiv.refl { a // p a }) (Equiv.refl { a // ¬p a })) x = (Equiv.refl ε) x

Next tactic:
simp [h]
@[simp]
theorem Perm.subtypeCongr.symm : (ep.subtypeCongr en).symm = Perm.subtypeCongr ep.symm en.symm := by
  

Proof state:
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
⊢ (subtypeCongr ep en).symm = subtypeCongr ep.symm en.symm

Next tactic:
ext x
@[simp]
theorem Perm.subtypeCongr.symm : (ep.subtypeCongr en).symm = Perm.subtypeCongr ep.symm en.symm := by
  ext x
  

Proof state:
case H
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
⊢ (subtypeCongr ep en).symm x = (subtypeCongr ep.symm en.symm) x

Next tactic:
by_cases h:p x
@[simp]
theorem Perm.subtypeCongr.symm : (ep.subtypeCongr en).symm = Perm.subtypeCongr ep.symm en.symm := by
  ext x
  by_cases h:p x
  · 

Proof state:
case pos
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
h : p x
⊢ (subtypeCongr ep en).symm x = (subtypeCongr ep.symm en.symm) x

Next tactic:
have : p (ep.symm ⟨x, h⟩) := Subtype.property _
@[simp]
theorem Perm.subtypeCongr.symm : (ep.subtypeCongr en).symm = Perm.subtypeCongr ep.symm en.symm := by
  ext x
  by_cases h:p x
  · have : p (ep.symm ⟨x, h⟩) := Subtype.property _
    

Proof state:
case pos
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
h : p x
this : p ↑(ep.symm { val := x, property := h })
⊢ (subtypeCongr ep en).symm x = (subtypeCongr ep.symm en.symm) x

Next tactic:
simp [Perm.subtypeCongr.apply, h, symm_apply_eq, this]
@[simp]
theorem Perm.subtypeCongr.symm : (ep.subtypeCongr en).symm = Perm.subtypeCongr ep.symm en.symm := by
  ext x
  by_cases h:p x
  · have : p (ep.symm ⟨x, h⟩) := Subtype.property _
    simp [Perm.subtypeCongr.apply, h, symm_apply_eq, this]
  · 

Proof state:
case neg
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
h : ¬p x
⊢ (subtypeCongr ep en).symm x = (subtypeCongr ep.symm en.symm) x

Next tactic:
have : ¬p (en.symm ⟨x, h⟩) := Subtype.property (en.symm _)
@[simp]
theorem Perm.subtypeCongr.symm : (ep.subtypeCongr en).symm = Perm.subtypeCongr ep.symm en.symm := by
  ext x
  by_cases h:p x
  · have : p (ep.symm ⟨x, h⟩) := Subtype.property _
    simp [Perm.subtypeCongr.apply, h, symm_apply_eq, this]
  · have : ¬p (en.symm ⟨x, h⟩) := Subtype.property (en.symm _)
    

Proof state:
case neg
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
h : ¬p x
this : ¬p ↑(en.symm { val := x, property := h })
⊢ (subtypeCongr ep en).symm x = (subtypeCongr ep.symm en.symm) x

Next tactic:
simp [Perm.subtypeCongr.apply, h, symm_apply_eq, this]
@[simp]
theorem Perm.subtypeCongr.trans :
    (ep.subtypeCongr en).trans (ep'.subtypeCongr en')
    = Perm.subtypeCongr (ep.trans ep') (en.trans en') := by
  

Proof state:
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
⊢ (subtypeCongr ep en).trans (subtypeCongr ep' en') = subtypeCongr (ep.trans ep') (en.trans en')

Next tactic:
ext x
@[simp]
theorem Perm.subtypeCongr.trans :
    (ep.subtypeCongr en).trans (ep'.subtypeCongr en')
    = Perm.subtypeCongr (ep.trans ep') (en.trans en') := by
  ext x
  

Proof state:
case H
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
⊢ ((subtypeCongr ep en).trans (subtypeCongr ep' en')) x = (subtypeCongr (ep.trans ep') (en.trans en')) x

Next tactic:
by_cases h:p x
@[simp]
theorem Perm.subtypeCongr.trans :
    (ep.subtypeCongr en).trans (ep'.subtypeCongr en')
    = Perm.subtypeCongr (ep.trans ep') (en.trans en') := by
  ext x
  by_cases h:p x
  · 

Proof state:
case pos
ε : Type u_1
p : ε → Prop
inst✝ : DecidablePred p
ep ep' : Perm { a // p a }
en en' : Perm { a // ¬p a }
x : ε
h : p x
⊢ ((subtypeCongr ep en).trans (subtypeCongr ep' en')) x = (subtypeCongr (ep.trans ep') (en.trans en')) x

Next tactic:
have : p (ep ⟨x, h⟩) := Subtype.property _