l3lab/ntp-mathlib huggingface dataset:
@[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
  · have : p (ep ⟨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 { val := x, property := h })
⊢ ((subtypeCongr ep en).trans (subtypeCongr ep' en')) x = (subtypeCongr (ep.trans ep') (en.trans en')) x

Next tactic:
simp [Perm.subtypeCongr.apply, h, this]
@[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
  · have : p (ep ⟨x, h⟩) := Subtype.property _
    simp [Perm.subtypeCongr.apply, h, 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).trans (subtypeCongr ep' en')) x = (subtypeCongr (ep.trans ep') (en.trans en')) x

Next tactic:
have : ¬p (en ⟨x, h⟩) := Subtype.property (en _)
@[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
  · have : p (ep ⟨x, h⟩) := Subtype.property _
    simp [Perm.subtypeCongr.apply, h, this]
  · have : ¬p (en ⟨x, h⟩) := Subtype.property (en _)
    

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 { val := x, property := h })
⊢ ((subtypeCongr ep en).trans (subtypeCongr ep' en')) x = (subtypeCongr (ep.trans ep') (en.trans en')) x

Next tactic:
simp [Perm.subtypeCongr.apply, h, symm_apply_eq, this]
/-- For a fixed function `x₀ : {a // p a} → β` defined on a subtype of `α`,
the subtype of functions `x : α → β` that agree with `x₀` on the subtype `{a // p a}`
is naturally equivalent to the type of functions `{a // ¬ p a} → β`. -/
@[simps]
def subtypePreimage : { x : α → β // x ∘ Subtype.val = x₀ } ≃ ({ a // ¬p a } → β) where
  toFun (x : { x : α → β // x ∘ Subtype.val = x₀ }) a := (x : α → β) a
  invFun x := ⟨fun a => if h : p a then x₀ ⟨a, h⟩ else x ⟨a, h⟩, funext fun ⟨a, h⟩ => dif_pos h⟩
  left_inv := fun ⟨x, hx⟩ =>
    Subtype.val_injective <|
      funext fun a => by
        

Proof state:
α : Sort ?u.38680
β : Sort ?u.38702
p : α → Prop
inst✝ : DecidablePred p
x₀ : { a // p a } → β
x✝ : { x // x ∘ Subtype.val = x₀ }
x : α → β
hx : x ∘ Subtype.val = x₀
a : α
⊢ ↑((fun x =>
            { val := fun a => if h : p a then x₀ { val := a, property := h } else x { val := a, property := h },
              property :=
                (_ :
                  (fun a => if h : p a then x₀ { val := a, property := h } else x { val := a, property := h }) ∘
                      Subtype.val =
                    x₀) })
          ((fun x a => ↑x ↑a) { val := x, property := hx }))
      a =
    ↑{ val := x, property := hx } a

Next tactic:
dsimp only
/-- For a fixed function `x₀ : {a // p a} → β` defined on a subtype of `α`,
the subtype of functions `x : α → β` that agree with `x₀` on the subtype `{a // p a}`
is naturally equivalent to the type of functions `{a // ¬ p a} → β`. -/
@[simps]
def subtypePreimage : { x : α → β // x ∘ Subtype.val = x₀ } ≃ ({ a // ¬p a } → β) where
  toFun (x : { x : α → β // x ∘ Subtype.val = x₀ }) a := (x : α → β) a
  invFun x := ⟨fun a => if h : p a then x₀ ⟨a, h⟩ else x ⟨a, h⟩, funext fun ⟨a, h⟩ => dif_pos h⟩
  left_inv := fun ⟨x, hx⟩ =>
    Subtype.val_injective <|
      funext fun a => by
        dsimp only
        

Proof state:
α : Sort ?u.38680
β : Sort ?u.38702
p : α → Prop
inst✝ : DecidablePred p
x₀ : { a // p a } → β
x✝ : { x // x ∘ Subtype.val = x₀ }
x : α → β
hx : x ∘ Subtype.val = x₀
a : α
⊢ (if h : p a then x₀ { val := a, property := h } else x a) = x a

Next tactic:
split_ifs
/-- For a fixed function `x₀ : {a // p a} → β` defined on a subtype of `α`,
the subtype of functions `x : α → β` that agree with `x₀` on the subtype `{a // p a}`
is naturally equivalent to the type of functions `{a // ¬ p a} → β`. -/
@[simps]
def subtypePreimage : { x : α → β // x ∘ Subtype.val = x₀ } ≃ ({ a // ¬p a } → β) where
  toFun (x : { x : α → β // x ∘ Subtype.val = x₀ }) a := (x : α → β) a
  invFun x := ⟨fun a => if h : p a then x₀ ⟨a, h⟩ else x ⟨a, h⟩, funext fun ⟨a, h⟩ => dif_pos h⟩
  left_inv := fun ⟨x, hx⟩ =>
    Subtype.val_injective <|
      funext fun a => by
        dsimp only
        split_ifs
        · 

Proof state:
case pos
α : Sort ?u.38680
β : Sort ?u.38702
p : α → Prop
inst✝ : DecidablePred p
x₀ : { a // p a } → β
x✝ : { x // x ∘ Subtype.val = x₀ }
x : α → β
hx : x ∘ Subtype.val = x₀
a : α
h✝ : p a
⊢ x₀ { val := a, property := h✝ } = x a

Next tactic:
rw [← hx]
/-- For a fixed function `x₀ : {a // p a} → β` defined on a subtype of `α`,
the subtype of functions `x : α → β` that agree with `x₀` on the subtype `{a // p a}`
is naturally equivalent to the type of functions `{a // ¬ p a} → β`. -/
@[simps]
def subtypePreimage : { x : α → β // x ∘ Subtype.val = x₀ } ≃ ({ a // ¬p a } → β) where
  toFun (x : { x : α → β // x ∘ Subtype.val = x₀ }) a := (x : α → β) a
  invFun x := ⟨fun a => if h : p a then x₀ ⟨a, h⟩ else x ⟨a, h⟩, funext fun ⟨a, h⟩ => dif_pos h⟩
  left_inv := fun ⟨x, hx⟩ =>
    Subtype.val_injective <|
      funext fun a => by
        dsimp only
        split_ifs
        · rw [← hx]; 

Proof state:
case pos
α : Sort ?u.38680
β : Sort ?u.38702
p : α → Prop
inst✝ : DecidablePred p
x₀ : { a // p a } → β
x✝ : { x // x ∘ Subtype.val = x₀ }
x : α → β
hx : x ∘ Subtype.val = x₀
a : α
h✝ : p a
⊢ (x ∘ Subtype.val) { val := a, property := h✝ } = x a

Next tactic:
rfl
/-- For a fixed function `x₀ : {a // p a} → β` defined on a subtype of `α`,
the subtype of functions `x : α → β` that agree with `x₀` on the subtype `{a // p a}`
is naturally equivalent to the type of functions `{a // ¬ p a} → β`. -/
@[simps]
def subtypePreimage : { x : α → β // x ∘ Subtype.val = x₀ } ≃ ({ a // ¬p a } → β) where
  toFun (x : { x : α → β // x ∘ Subtype.val = x₀ }) a := (x : α → β) a
  invFun x := ⟨fun a => if h : p a then x₀ ⟨a, h⟩ else x ⟨a, h⟩, funext fun ⟨a, h⟩ => dif_pos h⟩
  left_inv := fun ⟨x, hx⟩ =>
    Subtype.val_injective <|
      funext fun a => by
        dsimp only
        split_ifs
        · rw [← hx]; rfl
        · 

Proof state:
case neg
α : Sort ?u.38680
β : Sort ?u.38702
p : α → Prop
inst✝ : DecidablePred p
x₀ : { a // p a } → β
x✝ : { x // x ∘ Subtype.val = x₀ }
x : α → β
hx : x ∘ Subtype.val = x₀
a : α
h✝ : ¬p a
⊢ x a = x a

Next tactic:
rfl
/-- For a fixed function `x₀ : {a // p a} → β` defined on a subtype of `α`,
the subtype of functions `x : α → β` that agree with `x₀` on the subtype `{a // p a}`
is naturally equivalent to the type of functions `{a // ¬ p a} → β`. -/
@[simps]
def subtypePreimage : { x : α → β // x ∘ Subtype.val = x₀ } ≃ ({ a // ¬p a } → β) where
  toFun (x : { x : α → β // x ∘ Subtype.val = x₀ }) a := (x : α → β) a
  invFun x := ⟨fun a => if h : p a then x₀ ⟨a, h⟩ else x ⟨a, h⟩, funext fun ⟨a, h⟩ => dif_pos h⟩
  left_inv := fun ⟨x, hx⟩ =>
    Subtype.val_injective <|
      funext fun a => by
        dsimp only
        split_ifs
        · rw [← hx]; rfl
        · rfl
  right_inv x :=
    funext fun ⟨a, h⟩ =>
      show dite (p a) _ _ = _ by
        

Proof state:
α : Sort ?u.38680
β : Sort ?u.38702
p : α → Prop
inst✝ : DecidablePred p
x₀ : { a // p a } → β
x : { a // ¬p a } → β
x✝ : { a // ¬p a }
a : α
h : ¬p a
⊢ (if h_1 : p a then (fun h_2 => x₀ { val := ↑{ val := a, property := h }, property := h_2 }) h_1
    else (fun h_2 => x { val := ↑{ val := a, property := h }, property := h_2 }) h_1) =
    x { val := a, property := h }

Next tactic:
dsimp only
/-- For a fixed function `x₀ : {a // p a} → β` defined on a subtype of `α`,
the subtype of functions `x : α → β` that agree with `x₀` on the subtype `{a // p a}`
is naturally equivalent to the type of functions `{a // ¬ p a} → β`. -/
@[simps]
def subtypePreimage : { x : α → β // x ∘ Subtype.val = x₀ } ≃ ({ a // ¬p a } → β) where
  toFun (x : { x : α → β // x ∘ Subtype.val = x₀ }) a := (x : α → β) a
  invFun x := ⟨fun a => if h : p a then x₀ ⟨a, h⟩ else x ⟨a, h⟩, funext fun ⟨a, h⟩ => dif_pos h⟩
  left_inv := fun ⟨x, hx⟩ =>
    Subtype.val_injective <|
      funext fun a => by
        dsimp only
        split_ifs
        · rw [← hx]; rfl
        · rfl
  right_inv x :=
    funext fun ⟨a, h⟩ =>
      show dite (p a) _ _ = _ by
        dsimp only
        

Proof state:
α : Sort ?u.38680
β : Sort ?u.38702
p : α → Prop
inst✝ : DecidablePred p
x₀ : { a // p a } → β
x : { a // ¬p a } → β
x✝ : { a // ¬p a }
a : α
h : ¬p a
⊢ (if h : p a then x₀ { val := a, property := h } else x { val := a, property := h }) = x { val := a, property := h }

Next tactic:
rw [dif_neg h]
/-- A family of equivalences `∀ a, β₁ a ≃ β₂ a` generates an equivalence between `∀ a, β₁ a` and
`∀ a, β₂ a`. -/
def piCongrRight {β₁ β₂ : α → Sort*} (F : ∀ a, β₁ a ≃ β₂ a) : (∀ a, β₁ a) ≃ (∀ a, β₂ a) :=
  ⟨fun H a => F a (H a), fun H a => (F a).symm (H a), fun H => funext <| by 

Proof state:
α : Sort ?u.41714
β₁ : α → Sort u_1
β₂ : α → Sort u_2
F : (a : α) → β₁ a ≃ β₂ a
H : (a : α) → β₁ a
⊢ ∀ (x : α), (fun H a => (F a).symm (H a)) ((fun H a => (F a) (H a)) H) x = H x

Next tactic:
simp
/-- A family of equivalences `∀ a, β₁ a ≃ β₂ a` generates an equivalence between `∀ a, β₁ a` and
`∀ a, β₂ a`. -/
def piCongrRight {β₁ β₂ : α → Sort*} (F : ∀ a, β₁ a ≃ β₂ a) : (∀ a, β₁ a) ≃ (∀ a, β₂ a) :=
  ⟨fun H a => F a (H a), fun H a => (F a).symm (H a), fun H => funext <| by simp,
    fun H => funext <| by 

Proof state:
α : Sort ?u.41714
β₁ : α → Sort u_1
β₂ : α → Sort u_2
F : (a : α) → β₁ a ≃ β₂ a
H : (a : α) → β₂ a
⊢ ∀ (x : α), (fun H a => (F a) (H a)) ((fun H a => (F a).symm (H a)) H) x = H x

Next tactic:
simp
/-- A family of equivalences `∀ (a : α₁), β₁ ≃ β₂` generates an equivalence
between `β₁ × α₁` and `β₂ × α₁`. -/
def prodCongrLeft : β₁ × α₁ ≃ β₂ × α₁ where
  toFun ab := ⟨e ab.2 ab.1, ab.2⟩
  invFun ab := ⟨(e ab.2).symm ab.1, ab.2⟩
  left_inv := by
    

Proof state:
α₁ : Type ?u.43577
β₁ : Type ?u.43578
β₂ : Type ?u.43580
e : α₁ → β₁ ≃ β₂
⊢ LeftInverse (fun ab => ((e ab.2).symm ab.1, ab.2)) fun ab => ((e ab.2) ab.1, ab.2)

Next tactic:
rintro ⟨a, b⟩
/-- A family of equivalences `∀ (a : α₁), β₁ ≃ β₂` generates an equivalence
between `β₁ × α₁` and `β₂ × α₁`. -/
def prodCongrLeft : β₁ × α₁ ≃ β₂ × α₁ where
  toFun ab := ⟨e ab.2 ab.1, ab.2⟩
  invFun ab := ⟨(e ab.2).symm ab.1, ab.2⟩
  left_inv := by
    rintro ⟨a, b⟩
    

Proof state:
case mk
α₁ : Type ?u.43577
β₁ : Type ?u.43578
β₂ : Type ?u.43580
e : α₁ → β₁ ≃ β₂
a : β₁
b : α₁
⊢ (fun ab => ((e ab.2).symm ab.1, ab.2)) ((fun ab => ((e ab.2) ab.1, ab.2)) (a, b)) = (a, b)

Next tactic:
simp
/-- A family of equivalences `∀ (a : α₁), β₁ ≃ β₂` generates an equivalence
between `β₁ × α₁` and `β₂ × α₁`. -/
def prodCongrLeft : β₁ × α₁ ≃ β₂ × α₁ where
  toFun ab := ⟨e ab.2 ab.1, ab.2⟩
  invFun ab := ⟨(e ab.2).symm ab.1, ab.2⟩
  left_inv := by
    rintro ⟨a, b⟩
    simp
  right_inv := by
    

Proof state:
α₁ : Type ?u.43577
β₁ : Type ?u.43578
β₂ : Type ?u.43580
e : α₁ → β₁ ≃ β₂
⊢ Function.RightInverse (fun ab => ((e ab.2).symm ab.1, ab.2)) fun ab => ((e ab.2) ab.1, ab.2)

Next tactic:
rintro ⟨a, b⟩
/-- A family of equivalences `∀ (a : α₁), β₁ ≃ β₂` generates an equivalence
between `β₁ × α₁` and `β₂ × α₁`. -/
def prodCongrLeft : β₁ × α₁ ≃ β₂ × α₁ where
  toFun ab := ⟨e ab.2 ab.1, ab.2⟩
  invFun ab := ⟨(e ab.2).symm ab.1, ab.2⟩
  left_inv := by
    rintro ⟨a, b⟩
    simp
  right_inv := by
    rintro ⟨a, b⟩
    

Proof state:
case mk
α₁ : Type ?u.43577
β₁ : Type ?u.43578
β₂ : Type ?u.43580
e : α₁ → β₁ ≃ β₂
a : β₂
b : α₁
⊢ (fun ab => ((e ab.2) ab.1, ab.2)) ((fun ab => ((e ab.2).symm ab.1, ab.2)) (a, b)) = (a, b)

Next tactic:
simp
theorem prodCongr_refl_right (e : β₁ ≃ β₂) :
    prodCongr e (Equiv.refl α₁) = prodCongrLeft fun _ => e := by
  

Proof state:
α₁ : Type u_3
β₁ : Type u_1
β₂ : Type u_2
e✝ : α₁ → β₁ ≃ β₂
e : β₁ ≃ β₂
⊢ prodCongr e (Equiv.refl α₁) = prodCongrLeft fun x => e

Next tactic:
ext ⟨a, b⟩ : 1
theorem prodCongr_refl_right (e : β₁ ≃ β₂) :
    prodCongr e (Equiv.refl α₁) = prodCongrLeft fun _ => e := by
  ext ⟨a, b⟩ : 1
  

Proof state:
case H.mk
α₁ : Type u_3
β₁ : Type u_1
β₂ : Type u_2
e✝ : α₁ → β₁ ≃ β₂
e : β₁ ≃ β₂
a : β₁
b : α₁
⊢ (prodCongr e (Equiv.refl α₁)) (a, b) = (prodCongrLeft fun x => e) (a, b)

Next tactic:
simp
/-- A family of equivalences `∀ (a : α₁), β₁ ≃ β₂` generates an equivalence
between `α₁ × β₁` and `α₁ × β₂`. -/
def prodCongrRight : α₁ × β₁ ≃ α₁ × β₂ where
  toFun ab := ⟨ab.1, e ab.1 ab.2⟩
  invFun ab := ⟨ab.1, (e ab.1).symm ab.2⟩
  left_inv := by
    

Proof state:
α₁ : Type ?u.45190
β₁ : Type ?u.45189
β₂ : Type ?u.45191
e : α₁ → β₁ ≃ β₂
⊢ LeftInverse (fun ab => (ab.1, (e ab.1).symm ab.2)) fun ab => (ab.1, (e ab.1) ab.2)

Next tactic:
rintro ⟨a, b⟩
/-- A family of equivalences `∀ (a : α₁), β₁ ≃ β₂` generates an equivalence
between `α₁ × β₁` and `α₁ × β₂`. -/
def prodCongrRight : α₁ × β₁ ≃ α₁ × β₂ where
  toFun ab := ⟨ab.1, e ab.1 ab.2⟩
  invFun ab := ⟨ab.1, (e ab.1).symm ab.2⟩
  left_inv := by
    rintro ⟨a, b⟩
    

Proof state:
case mk
α₁ : Type ?u.45190
β₁ : Type ?u.45189
β₂ : Type ?u.45191
e : α₁ → β₁ ≃ β₂
a : α₁
b : β₁
⊢ (fun ab => (ab.1, (e ab.1).symm ab.2)) ((fun ab => (ab.1, (e ab.1) ab.2)) (a, b)) = (a, b)

Next tactic:
simp
/-- A family of equivalences `∀ (a : α₁), β₁ ≃ β₂` generates an equivalence
between `α₁ × β₁` and `α₁ × β₂`. -/
def prodCongrRight : α₁ × β₁ ≃ α₁ × β₂ where
  toFun ab := ⟨ab.1, e ab.1 ab.2⟩
  invFun ab := ⟨ab.1, (e ab.1).symm ab.2⟩
  left_inv := by
    rintro ⟨a, b⟩
    simp
  right_inv := by
    

Proof state:
α₁ : Type ?u.45190
β₁ : Type ?u.45189
β₂ : Type ?u.45191
e : α₁ → β₁ ≃ β₂
⊢ Function.RightInverse (fun ab => (ab.1, (e ab.1).symm ab.2)) fun ab => (ab.1, (e ab.1) ab.2)

Next tactic:
rintro ⟨a, b⟩
/-- A family of equivalences `∀ (a : α₁), β₁ ≃ β₂` generates an equivalence
between `α₁ × β₁` and `α₁ × β₂`. -/
def prodCongrRight : α₁ × β₁ ≃ α₁ × β₂ where
  toFun ab := ⟨ab.1, e ab.1 ab.2⟩
  invFun ab := ⟨ab.1, (e ab.1).symm ab.2⟩
  left_inv := by
    rintro ⟨a, b⟩
    simp
  right_inv := by
    rintro ⟨a, b⟩
    

Proof state:
case mk
α₁ : Type ?u.45190
β₁ : Type ?u.45189
β₂ : Type ?u.45191
e : α₁ → β₁ ≃ β₂
a : α₁
b : β₂
⊢ (fun ab => (ab.1, (e ab.1) ab.2)) ((fun ab => (ab.1, (e ab.1).symm ab.2)) (a, b)) = (a, b)

Next tactic:
simp
theorem prodCongr_refl_left (e : β₁ ≃ β₂) :
    prodCongr (Equiv.refl α₁) e = prodCongrRight fun _ => e := by
  

Proof state:
α₁ : Type u_3
β₁ : Type u_1
β₂ : Type u_2
e✝ : α₁ → β₁ ≃ β₂
e : β₁ ≃ β₂
⊢ prodCongr (Equiv.refl α₁) e = prodCongrRight fun x => e

Next tactic:
ext ⟨a, b⟩ : 1
theorem prodCongr_refl_left (e : β₁ ≃ β₂) :
    prodCongr (Equiv.refl α₁) e = prodCongrRight fun _ => e := by
  ext ⟨a, b⟩ : 1
  

Proof state:
case H.mk
α₁ : Type u_3
β₁ : Type u_1
β₂ : Type u_2
e✝ : α₁ → β₁ ≃ β₂
e : β₁ ≃ β₂
a : α₁
b : β₁
⊢ (prodCongr (Equiv.refl α₁) e) (a, b) = (prodCongrRight fun x => e) (a, b)

Next tactic:
simp
@[simp]
theorem prodCongrLeft_trans_prodComm :
    (prodCongrLeft e).trans (prodComm _ _) = (prodComm _ _).trans (prodCongrRight e) := by
  

Proof state:
α₁ : Type u_3
β₁ : Type u_2
β₂ : Type u_1
e : α₁ → β₁ ≃ β₂
⊢ (prodCongrLeft e).trans (prodComm β₂ α₁) = (prodComm β₁ α₁).trans (prodCongrRight e)

Next tactic:
ext ⟨a, b⟩ : 1
@[simp]
theorem prodCongrLeft_trans_prodComm :
    (prodCongrLeft e).trans (prodComm _ _) = (prodComm _ _).trans (prodCongrRight e) := by
  ext ⟨a, b⟩ : 1
  

Proof state:
case H.mk
α₁ : Type u_3
β₁ : Type u_2
β₂ : Type u_1
e : α₁ → β₁ ≃ β₂
a : β₁
b : α₁
⊢ ((prodCongrLeft e).trans (prodComm β₂ α₁)) (a, b) = ((prodComm β₁ α₁).trans (prodCongrRight e)) (a, b)

Next tactic:
simp
@[simp]
theorem prodCongrRight_trans_prodComm :
    (prodCongrRight e).trans (prodComm _ _) = (prodComm _ _).trans (prodCongrLeft e) := by
  

Proof state:
α₁ : Type u_3
β₁ : Type u_2
β₂ : Type u_1
e : α₁ → β₁ ≃ β₂
⊢ (prodCongrRight e).trans (prodComm α₁ β₂) = (prodComm α₁ β₁).trans (prodCongrLeft e)

Next tactic:
ext ⟨a, b⟩ : 1
@[simp]
theorem prodCongrRight_trans_prodComm :
    (prodCongrRight e).trans (prodComm _ _) = (prodComm _ _).trans (prodCongrLeft e) := by
  ext ⟨a, b⟩ : 1
  

Proof state:
case H.mk
α₁ : Type u_3
β₁ : Type u_2
β₂ : Type u_1
e : α₁ → β₁ ≃ β₂
a : α₁
b : β₁
⊢ ((prodCongrRight e).trans (prodComm α₁ β₂)) (a, b) = ((prodComm α₁ β₁).trans (prodCongrLeft e)) (a, b)

Next tactic:
simp
theorem sigmaCongrRight_sigmaEquivProd :
    (sigmaCongrRight e).trans (sigmaEquivProd α₁ β₂)
    = (sigmaEquivProd α₁ β₁).trans (prodCongrRight e) := by
  

Proof state:
α₁ : Type u_1
β₁ : Type u_2
β₂ : Type u_3
e : α₁ → β₁ ≃ β₂
⊢ (sigmaCongrRight e).trans (sigmaEquivProd α₁ β₂) = (sigmaEquivProd α₁ β₁).trans (prodCongrRight e)

Next tactic:
ext ⟨a, b⟩ : 1
theorem sigmaCongrRight_sigmaEquivProd :
    (sigmaCongrRight e).trans (sigmaEquivProd α₁ β₂)
    = (sigmaEquivProd α₁ β₁).trans (prodCongrRight e) := by
  ext ⟨a, b⟩ : 1
  

Proof state:
case H.mk
α₁ : Type u_1
β₁ : Type u_2
β₂ : Type u_3
e : α₁ → β₁ ≃ β₂
a : α₁
b : β₁
⊢ ((sigmaCongrRight e).trans (sigmaEquivProd α₁ β₂)) { fst := a, snd := b } =
    ((sigmaEquivProd α₁ β₁).trans (prodCongrRight e)) { fst := a, snd := b }

Next tactic:
simp
theorem sigmaEquivProd_sigmaCongrRight :
    (sigmaEquivProd α₁ β₁).symm.trans (sigmaCongrRight e)
    = (prodCongrRight e).trans (sigmaEquivProd α₁ β₂).symm := by
  

Proof state:
α₁ : Type u_1
β₁ : Type u_2
β₂ : Type u_3
e : α₁ → β₁ ≃ β₂
⊢ (sigmaEquivProd α₁ β₁).symm.trans (sigmaCongrRight e) = (prodCongrRight e).trans (sigmaEquivProd α₁ β₂).symm

Next tactic:
ext ⟨a, b⟩ : 1
theorem sigmaEquivProd_sigmaCongrRight :
    (sigmaEquivProd α₁ β₁).symm.trans (sigmaCongrRight e)
    = (prodCongrRight e).trans (sigmaEquivProd α₁ β₂).symm := by
  ext ⟨a, b⟩ : 1
  

Proof state:
case H.mk
α₁ : Type u_1
β₁ : Type u_2
β₂ : Type u_3
e : α₁ → β₁ ≃ β₂
a : α₁
b : β₁
⊢ ((sigmaEquivProd α₁ β₁).symm.trans (sigmaCongrRight e)) (a, b) =
    ((prodCongrRight e).trans (sigmaEquivProd α₁ β₂).symm) (a, b)

Next tactic:
simp only [trans_apply, sigmaCongrRight_apply, prodCongrRight_apply]
theorem sigmaEquivProd_sigmaCongrRight :
    (sigmaEquivProd α₁ β₁).symm.trans (sigmaCongrRight e)
    = (prodCongrRight e).trans (sigmaEquivProd α₁ β₂).symm := by
  ext ⟨a, b⟩ : 1
  simp only [trans_apply, sigmaCongrRight_apply, prodCongrRight_apply]
  

Proof state:
case H.mk
α₁ : Type u_1
β₁ : Type u_2
β₂ : Type u_3
e : α₁ → β₁ ≃ β₂
a : α₁
b : β₁
⊢ { fst := ((sigmaEquivProd α₁ β₁).symm (a, b)).fst,
      snd := (e ((sigmaEquivProd α₁ β₁).symm (a, b)).fst) ((sigmaEquivProd α₁ β₁).symm (a, b)).snd } =
    (sigmaEquivProd α₁ β₂).symm (a, (e a) b)

Next tactic:
rfl
/-- A variation on `Equiv.prodCongr` where the equivalence in the second component can depend
  on the first component. A typical example is a shear mapping, explaining the name of this
  declaration. -/
@[simps (config := .asFn)]
def prodShear (e₁ : α₁ ≃ α₂) (e₂ : α₁ → β₁ ≃ β₂) : α₁ × β₁ ≃ α₂ × β₂ where
  toFun := fun x : α₁ × β₁ => (e₁ x.1, e₂ x.1 x.2)
  invFun := fun y : α₂ × β₂ => (e₁.symm y.1, (e₂ <| e₁.symm y.1).symm y.2)
  left_inv := by
    

Proof state:
α₁ : Type ?u.50498
β₁ : Type ?u.50497
β₂ : Type ?u.50499
e : α₁ → β₁ ≃ β₂
α₂ : Type ?u.50500
e₁ : α₁ ≃ α₂
e₂ : α₁ → β₁ ≃ β₂
⊢ LeftInverse (fun y => (e₁.symm y.1, (e₂ (e₁.symm y.1)).symm y.2)) fun x => (e₁ x.1, (e₂ x.1) x.2)

Next tactic:
rintro ⟨x₁, y₁⟩
/-- A variation on `Equiv.prodCongr` where the equivalence in the second component can depend
  on the first component. A typical example is a shear mapping, explaining the name of this
  declaration. -/
@[simps (config := .asFn)]
def prodShear (e₁ : α₁ ≃ α₂) (e₂ : α₁ → β₁ ≃ β₂) : α₁ × β₁ ≃ α₂ × β₂ where
  toFun := fun x : α₁ × β₁ => (e₁ x.1, e₂ x.1 x.2)
  invFun := fun y : α₂ × β₂ => (e₁.symm y.1, (e₂ <| e₁.symm y.1).symm y.2)
  left_inv := by
    rintro ⟨x₁, y₁⟩
    

Proof state:
case mk
α₁ : Type ?u.50498
β₁ : Type ?u.50497
β₂ : Type ?u.50499
e : α₁ → β₁ ≃ β₂
α₂ : Type ?u.50500
e₁ : α₁ ≃ α₂
e₂ : α₁ → β₁ ≃ β₂
x₁ : α₁
y₁ : β₁
⊢ (fun y => (e₁.symm y.1, (e₂ (e₁.symm y.1)).symm y.2)) ((fun x => (e₁ x.1, (e₂ x.1) x.2)) (x₁, y₁)) = (x₁, y₁)

Next tactic:
simp only [symm_apply_apply]
/-- A variation on `Equiv.prodCongr` where the equivalence in the second component can depend
  on the first component. A typical example is a shear mapping, explaining the name of this
  declaration. -/
@[simps (config := .asFn)]
def prodShear (e₁ : α₁ ≃ α₂) (e₂ : α₁ → β₁ ≃ β₂) : α₁ × β₁ ≃ α₂ × β₂ where
  toFun := fun x : α₁ × β₁ => (e₁ x.1, e₂ x.1 x.2)
  invFun := fun y : α₂ × β₂ => (e₁.symm y.1, (e₂ <| e₁.symm y.1).symm y.2)
  left_inv := by
    rintro ⟨x₁, y₁⟩
    simp only [symm_apply_apply]
  right_inv := by
    

Proof state:
α₁ : Type ?u.50498
β₁ : Type ?u.50497
β₂ : Type ?u.50499
e : α₁ → β₁ ≃ β₂
α₂ : Type ?u.50500
e₁ : α₁ ≃ α₂
e₂ : α₁ → β₁ ≃ β₂
⊢ Function.RightInverse (fun y => (e₁.symm y.1, (e₂ (e₁.symm y.1)).symm y.2)) fun x => (e₁ x.1, (e₂ x.1) x.2)

Next tactic:
rintro ⟨x₁, y₁⟩
/-- A variation on `Equiv.prodCongr` where the equivalence in the second component can depend
  on the first component. A typical example is a shear mapping, explaining the name of this
  declaration. -/
@[simps (config := .asFn)]
def prodShear (e₁ : α₁ ≃ α₂) (e₂ : α₁ → β₁ ≃ β₂) : α₁ × β₁ ≃ α₂ × β₂ where
  toFun := fun x : α₁ × β₁ => (e₁ x.1, e₂ x.1 x.2)
  invFun := fun y : α₂ × β₂ => (e₁.symm y.1, (e₂ <| e₁.symm y.1).symm y.2)
  left_inv := by
    rintro ⟨x₁, y₁⟩
    simp only [symm_apply_apply]
  right_inv := by
    rintro ⟨x₁, y₁⟩
    

Proof state:
case mk
α₁ : Type ?u.50498
β₁ : Type ?u.50497
β₂ : Type ?u.50499
e : α₁ → β₁ ≃ β₂
α₂ : Type ?u.50500
e₁ : α₁ ≃ α₂
e₂ : α₁ → β₁ ≃ β₂
x₁ : α₂
y₁ : β₂
⊢ (fun x => (e₁ x.1, (e₂ x.1) x.2)) ((fun y => (e₁.symm y.1, (e₂ (e₁.symm y.1)).symm y.2)) (x₁, y₁)) = (x₁, y₁)

Next tactic:
simp only [apply_symm_apply]
/-- `prodExtendRight a e` extends `e : Perm β` to `Perm (α × β)` by sending `(a, b)` to
`(a, e b)` and keeping the other `(a', b)` fixed. -/
def prodExtendRight : Perm (α₁ × β₁) where
  toFun ab := if ab.fst = a then (a, e ab.snd) else ab
  invFun ab := if ab.fst = a then (a, e.symm ab.snd) else ab
  left_inv := by
    

Proof state:
α₁ : Type ?u.51937
β₁ : Type ?u.51936
inst✝ : DecidableEq α₁
a : α₁
e : Perm β₁
⊢ LeftInverse (fun ab => if ab.1 = a then (a, e.symm ab.2) else ab) fun ab => if ab.1 = a then (a, e ab.2) else ab

Next tactic:
rintro ⟨k', x⟩
/-- `prodExtendRight a e` extends `e : Perm β` to `Perm (α × β)` by sending `(a, b)` to
`(a, e b)` and keeping the other `(a', b)` fixed. -/
def prodExtendRight : Perm (α₁ × β₁) where
  toFun ab := if ab.fst = a then (a, e ab.snd) else ab
  invFun ab := if ab.fst = a then (a, e.symm ab.snd) else ab
  left_inv := by
    rintro ⟨k', x⟩
    

Proof state:
case mk
α₁ : Type ?u.51937
β₁ : Type ?u.51936
inst✝ : DecidableEq α₁
a : α₁
e : Perm β₁
k' : α₁
x : β₁
⊢ (fun ab => if ab.1 = a then (a, e.symm ab.2) else ab) ((fun ab => if ab.1 = a then (a, e ab.2) else ab) (k', x)) =
    (k', x)

Next tactic:
dsimp only
/-- `prodExtendRight a e` extends `e : Perm β` to `Perm (α × β)` by sending `(a, b)` to
`(a, e b)` and keeping the other `(a', b)` fixed. -/
def prodExtendRight : Perm (α₁ × β₁) where
  toFun ab := if ab.fst = a then (a, e ab.snd) else ab
  invFun ab := if ab.fst = a then (a, e.symm ab.snd) else ab
  left_inv := by
    rintro ⟨k', x⟩
    dsimp only
    

Proof state:
case mk
α₁ : Type ?u.51937
β₁ : Type ?u.51936
inst✝ : DecidableEq α₁
a : α₁
e : Perm β₁
k' : α₁
x : β₁
⊢ (if (if k' = a then (a, e x) else (k', x)).1 = a then (a, e.symm (if k' = a then (a, e x) else (k', x)).2)
    else if k' = a then (a, e x) else (k', x)) =
    (k', x)

Next tactic:
split_ifs with h₁ h₂
/-- `prodExtendRight a e` extends `e : Perm β` to `Perm (α × β)` by sending `(a, b)` to
`(a, e b)` and keeping the other `(a', b)` fixed. -/
def prodExtendRight : Perm (α₁ × β₁) where
  toFun ab := if ab.fst = a then (a, e ab.snd) else ab
  invFun ab := if ab.fst = a then (a, e.symm ab.snd) else ab
  left_inv := by
    rintro ⟨k', x⟩
    dsimp only
    split_ifs with h₁ h₂
    · 

Proof state:
case pos
α₁ : Type ?u.51937
β₁ : Type ?u.51936
inst✝ : DecidableEq α₁
a : α₁
e : Perm β₁
k' : α₁
x : β₁
h₁ : k' = a
h₂ : (a, e x).1 = a
⊢ (a, e.symm (a, e x).2) = (k', x)

Next tactic:
simp [h₁]