/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Kenny Lau
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.module.pi
import Mathlib.algebra.big_operators.basic
import Mathlib.data.set.finite
import Mathlib.group_theory.submonoid.basic
import Mathlib.PostPort

universes u v l v₁ v₂ w u_1 u_2 u_3 u_4 u₁ x

namespace Mathlib

/-!
# Dependent functions with finite support

For a non-dependent version see `data/finsupp.lean`.
-/

namespace dfinsupp

structure pre (ι : Type u) (β : ι → Type v) [(i : ι) → HasZero (β i)]
where
  to_fun : (i : ι) → β i
  pre_support : multiset ι
  zero : ∀ (i : ι), i ∈ pre_support ∨ to_fun i = 0

protected instance inhabited_pre (ι : Type u) (β : ι → Type v) [(i : ι) → HasZero (β i)] : Inhabited (pre ι β) :=
  { default := pre.mk (fun (i : ι) => 0) ∅ sorry }

protected instance pre.setoid (ι : Type u) (β : ι → Type v) [(i : ι) → HasZero (β i)] : setoid (pre ι β) :=
  setoid.mk (fun (x y : pre ι β) => ∀ (i : ι), pre.to_fun x i = pre.to_fun y i) sorry

end dfinsupp

/-- A dependent function `Π i, β i` with finite support. -/
def dfinsupp {ι : Type u} (β : ι → Type v) [(i : ι) → HasZero (β i)] :=
  quotient sorry

infixl:25 " →ₚ " => Mathlib.dfinsupp

namespace dfinsupp

protected instance has_coe_to_fun {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] : has_coe_to_fun (dfinsupp fun (i : ι) => β i) :=
  has_coe_to_fun.mk (fun (_x : dfinsupp fun (i : ι) => β i) => (i : ι) → β i)
    fun (f : dfinsupp fun (i : ι) => β i) => quotient.lift_on f pre.to_fun sorry

protected instance has_zero {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] : HasZero (dfinsupp fun (i : ι) => β i) :=
  { zero := quotient.mk (pre.mk (fun (i : ι) => 0) ∅ sorry) }

protected instance inhabited {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] : Inhabited (dfinsupp fun (i : ι) => β i) :=
  { default := 0 }

@[simp] theorem zero_apply {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] (i : ι) : coe_fn 0 i = 0 :=
  rfl

theorem ext {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] {f : dfinsupp fun (i : ι) => β i} {g : dfinsupp fun (i : ι) => β i} (H : ∀ (i : ι), coe_fn f i = coe_fn g i) : f = g := sorry

/-- The composition of `f : β₁ → β₂` and `g : Π₀ i, β₁ i` is
  `map_range f hf g : Π₀ i, β₂ i`, well defined when `f 0 = 0`. -/
def map_range {ι : Type u} {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] (f : (i : ι) → β₁ i → β₂ i) (hf : ∀ (i : ι), f i 0 = 0) (g : dfinsupp fun (i : ι) => β₁ i) : dfinsupp fun (i : ι) => β₂ i :=
  quotient.lift_on g
    (fun (x : pre ι fun (i : ι) => β₁ i) =>
      quotient.mk (pre.mk (fun (i : ι) => f i (pre.to_fun x i)) (pre.pre_support x) sorry))
    sorry

@[simp] theorem map_range_apply {ι : Type u} {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] (f : (i : ι) → β₁ i → β₂ i) (hf : ∀ (i : ι), f i 0 = 0) (g : dfinsupp fun (i : ι) => β₁ i) (i : ι) : coe_fn (map_range f hf g) i = f i (coe_fn g i) :=
  quotient.induction_on g fun (x : pre ι fun (i : ι) => β₁ i) => rfl

/-- Let `f i` be a binary operation `β₁ i → β₂ i → β i` such that `f i 0 0 = 0`.
Then `zip_with f hf` is a binary operation `Π₀ i, β₁ i → Π₀ i, β₂ i → Π₀ i, β i`. -/
def zip_with {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] (f : (i : ι) → β₁ i → β₂ i → β i) (hf : ∀ (i : ι), f i 0 0 = 0) (g₁ : dfinsupp fun (i : ι) => β₁ i) (g₂ : dfinsupp fun (i : ι) => β₂ i) : dfinsupp fun (i : ι) => β i :=
  quotient.lift_on₂ g₁ g₂
    (fun (x : pre ι fun (i : ι) => β₁ i) (y : pre ι fun (i : ι) => β₂ i) =>
      quotient.mk
        (pre.mk (fun (i : ι) => f i (pre.to_fun x i) (pre.to_fun y i)) (pre.pre_support x + pre.pre_support y) sorry))
    sorry

@[simp] theorem zip_with_apply {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] (f : (i : ι) → β₁ i → β₂ i → β i) (hf : ∀ (i : ι), f i 0 0 = 0) (g₁ : dfinsupp fun (i : ι) => β₁ i) (g₂ : dfinsupp fun (i : ι) => β₂ i) (i : ι) : coe_fn (zip_with f hf g₁ g₂) i = f i (coe_fn g₁ i) (coe_fn g₂ i) :=
  quotient.induction_on₂ g₁ g₂ fun (_x : pre ι fun (i : ι) => β₁ i) (_x_1 : pre ι fun (i : ι) => β₂ i) => rfl

protected instance has_add {ι : Type u} {β : ι → Type v} [(i : ι) → add_monoid (β i)] : Add (dfinsupp fun (i : ι) => β i) :=
  { add := zip_with (fun (_x : ι) => Add.add) sorry }

@[simp] theorem add_apply {ι : Type u} {β : ι → Type v} [(i : ι) → add_monoid (β i)] (g₁ : dfinsupp fun (i : ι) => β i) (g₂ : dfinsupp fun (i : ι) => β i) (i : ι) : coe_fn (g₁ + g₂) i = coe_fn g₁ i + coe_fn g₂ i :=
  zip_with_apply (fun (_x : ι) => Add.add) has_add._proof_1 g₁ g₂ i

protected instance add_monoid {ι : Type u} {β : ι → Type v} [(i : ι) → add_monoid (β i)] : add_monoid (dfinsupp fun (i : ι) => β i) :=
  add_monoid.mk Add.add sorry 0 sorry sorry

protected instance is_add_monoid_hom {ι : Type u} {β : ι → Type v} [(i : ι) → add_monoid (β i)] {i : ι} : is_add_monoid_hom fun (g : dfinsupp fun (i : ι) => β i) => coe_fn g i :=
  is_add_monoid_hom.mk (zero_apply i)

protected instance has_neg {ι : Type u} {β : ι → Type v} [(i : ι) → add_group (β i)] : Neg (dfinsupp fun (i : ι) => β i) :=
  { neg := fun (f : dfinsupp fun (i : ι) => β i) => map_range (fun (_x : ι) => Neg.neg) sorry f }

protected instance add_comm_monoid {ι : Type u} {β : ι → Type v} [(i : ι) → add_comm_monoid (β i)] : add_comm_monoid (dfinsupp fun (i : ι) => β i) :=
  add_comm_monoid.mk add_monoid.add sorry add_monoid.zero sorry sorry sorry

@[simp] theorem neg_apply {ι : Type u} {β : ι → Type v} [(i : ι) → add_group (β i)] (g : dfinsupp fun (i : ι) => β i) (i : ι) : coe_fn (-g) i = -coe_fn g i :=
  map_range_apply (fun (_x : ι) => Neg.neg) has_neg._proof_1 g i

protected instance add_group {ι : Type u} {β : ι → Type v} [(i : ι) → add_group (β i)] : add_group (dfinsupp fun (i : ι) => β i) :=
  add_group.mk add_monoid.add sorry add_monoid.zero sorry sorry Neg.neg
    (sub_neg_monoid.sub._default add_monoid.add sorry add_monoid.zero sorry sorry Neg.neg) sorry

@[simp] theorem sub_apply {ι : Type u} {β : ι → Type v} [(i : ι) → add_group (β i)] (g₁ : dfinsupp fun (i : ι) => β i) (g₂ : dfinsupp fun (i : ι) => β i) (i : ι) : coe_fn (g₁ - g₂) i = coe_fn g₁ i - coe_fn g₂ i := sorry

protected instance add_comm_group {ι : Type u} {β : ι → Type v} [(i : ι) → add_comm_group (β i)] : add_comm_group (dfinsupp fun (i : ι) => β i) :=
  add_comm_group.mk add_group.add sorry add_group.zero sorry sorry add_group.neg add_group.sub sorry sorry

/-- Dependent functions with finite support inherit a semiring action from an action on each
coordinate. -/
protected instance has_scalar {ι : Type u} {β : ι → Type v} {γ : Type w} [semiring γ] [(i : ι) → add_comm_monoid (β i)] [(i : ι) → semimodule γ (β i)] : has_scalar γ (dfinsupp fun (i : ι) => β i) :=
  has_scalar.mk fun (c : γ) (v : dfinsupp fun (i : ι) => β i) => map_range (fun (_x : ι) => has_scalar.smul c) sorry v

@[simp] theorem smul_apply {ι : Type u} {β : ι → Type v} {γ : Type w} [semiring γ] [(i : ι) → add_comm_monoid (β i)] [(i : ι) → semimodule γ (β i)] (b : γ) (v : dfinsupp fun (i : ι) => β i) (i : ι) : coe_fn (b • v) i = b • coe_fn v i :=
  map_range_apply (fun (_x : ι) => has_scalar.smul b) (has_scalar._proof_1 b) v i

/-- Dependent functions with finite support inherit a semimodule structure from such a structure on
each coordinate. -/
protected instance semimodule {ι : Type u} {β : ι → Type v} {γ : Type w} [semiring γ] [(i : ι) → add_comm_monoid (β i)] [(i : ι) → semimodule γ (β i)] : semimodule γ (dfinsupp fun (i : ι) => β i) :=
  semimodule.mk sorry sorry

/-- `filter p f` is the function which is `f i` if `p i` is true and 0 otherwise. -/
def filter {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] (p : ι → Prop) [decidable_pred p] (f : dfinsupp fun (i : ι) => β i) : dfinsupp fun (i : ι) => β i :=
  quotient.lift_on f
    (fun (x : pre ι fun (i : ι) => β i) =>
      quotient.mk (pre.mk (fun (i : ι) => ite (p i) (pre.to_fun x i) 0) (pre.pre_support x) sorry))
    sorry

@[simp] theorem filter_apply {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] (p : ι → Prop) [decidable_pred p] (i : ι) (f : dfinsupp fun (i : ι) => β i) : coe_fn (filter p f) i = ite (p i) (coe_fn f i) 0 :=
  quotient.induction_on f fun (x : pre ι fun (i : ι) => β i) => rfl

theorem filter_apply_pos {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] {p : ι → Prop} [decidable_pred p] (f : dfinsupp fun (i : ι) => β i) {i : ι} (h : p i) : coe_fn (filter p f) i = coe_fn f i := sorry

theorem filter_apply_neg {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] {p : ι → Prop} [decidable_pred p] (f : dfinsupp fun (i : ι) => β i) {i : ι} (h : ¬p i) : coe_fn (filter p f) i = 0 := sorry

theorem filter_pos_add_filter_neg {ι : Type u} {β : ι → Type v} [(i : ι) → add_monoid (β i)] (f : dfinsupp fun (i : ι) => β i) (p : ι → Prop) [decidable_pred p] : filter p f + filter (fun (i : ι) => ¬p i) f = f := sorry

/-- `subtype_domain p f` is the restriction of the finitely supported function
  `f` to the subtype `p`. -/
def subtype_domain {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] (p : ι → Prop) [decidable_pred p] (f : dfinsupp fun (i : ι) => β i) : dfinsupp fun (i : Subtype p) => β ↑i :=
  quotient.lift_on f
    (fun (x : pre ι fun (i : ι) => β i) =>
      quotient.mk
        (pre.mk (fun (i : Subtype p) => pre.to_fun x ↑i)
          (multiset.map
            (fun (j : Subtype fun (x_1 : ι) => x_1 ∈ multiset.filter p (pre.pre_support x)) =>
              { val := ↑j, property := sorry })
            (multiset.attach (multiset.filter p (pre.pre_support x))))
          sorry))
    sorry

@[simp] theorem subtype_domain_zero {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] {p : ι → Prop} [decidable_pred p] : subtype_domain p 0 = 0 :=
  rfl

@[simp] theorem subtype_domain_apply {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] {p : ι → Prop} [decidable_pred p] {i : Subtype p} {v : dfinsupp fun (i : ι) => β i} : coe_fn (subtype_domain p v) i = coe_fn v ↑i :=
  quotient.induction_on v fun (x : pre ι fun (i : ι) => β i) => rfl

@[simp] theorem subtype_domain_add {ι : Type u} {β : ι → Type v} [(i : ι) → add_monoid (β i)] {p : ι → Prop} [decidable_pred p] {v : dfinsupp fun (i : ι) => β i} {v' : dfinsupp fun (i : ι) => β i} : subtype_domain p (v + v') = subtype_domain p v + subtype_domain p v' := sorry

protected instance subtype_domain.is_add_monoid_hom {ι : Type u} {β : ι → Type v} [(i : ι) → add_monoid (β i)] {p : ι → Prop} [decidable_pred p] : is_add_monoid_hom (subtype_domain p) :=
  is_add_monoid_hom.mk subtype_domain_zero

@[simp] theorem subtype_domain_neg {ι : Type u} {β : ι → Type v} [(i : ι) → add_group (β i)] {p : ι → Prop} [decidable_pred p] {v : dfinsupp fun (i : ι) => β i} : subtype_domain p (-v) = -subtype_domain p v := sorry

@[simp] theorem subtype_domain_sub {ι : Type u} {β : ι → Type v} [(i : ι) → add_group (β i)] {p : ι → Prop} [decidable_pred p] {v : dfinsupp fun (i : ι) => β i} {v' : dfinsupp fun (i : ι) => β i} : subtype_domain p (v - v') = subtype_domain p v - subtype_domain p v' := sorry

theorem finite_supp {ι : Type u} {β : ι → Type v} [(i : ι) → HasZero (β i)] (f : dfinsupp fun (i : ι) => β i) : set.finite (set_of fun (i : ι) => coe_fn f i ≠ 0) := sorry

/-- Create an element of `Π₀ i, β i` from a finset `s` and a function `x`
defined on this `finset`. -/
def mk {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] (s : finset ι) (x : (i : ↥↑s) → β ↑i) : dfinsupp fun (i : ι) => β i :=
  quotient.mk
    (pre.mk (fun (i : ι) => dite (i ∈ s) (fun (H : i ∈ s) => x { val := i, property := H }) fun (H : ¬i ∈ s) => 0)
      (finset.val s) sorry)

@[simp] theorem mk_apply {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {s : finset ι} {x : (i : ↥↑s) → β ↑i} {i : ι} : coe_fn (mk s x) i = dite (i ∈ s) (fun (H : i ∈ s) => x { val := i, property := H }) fun (H : ¬i ∈ s) => 0 :=
  rfl

theorem mk_injective {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] (s : finset ι) : function.injective (mk s) := sorry

/-- The function `single i b : Π₀ i, β i` sends `i` to `b`
and all other points to `0`. -/
def single {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] (i : ι) (b : β i) : dfinsupp fun (i : ι) => β i :=
  mk (singleton i) fun (j : ↥↑(singleton i)) => eq.rec_on sorry b

@[simp] theorem single_apply {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {i : ι} {i' : ι} {b : β i} : coe_fn (single i b) i' = dite (i = i') (fun (h : i = i') => eq.rec_on h b) fun (h : ¬i = i') => 0 := sorry

@[simp] theorem single_zero {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {i : ι} : single i 0 = 0 := sorry

@[simp] theorem single_eq_same {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {i : ι} {b : β i} : coe_fn (single i b) i = b := sorry

theorem single_eq_of_ne {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {i : ι} {i' : ι} {b : β i} (h : i ≠ i') : coe_fn (single i b) i' = 0 := sorry

theorem single_injective {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {i : ι} : function.injective (single i) := sorry

/-- Like `finsupp.single_eq_single_iff`, but with a `heq` due to dependent types -/
theorem single_eq_single_iff {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] (i : ι) (j : ι) (xi : β i) (xj : β j) : single i xi = single j xj ↔ i = j ∧ xi == xj ∨ xi = 0 ∧ xj = 0 := sorry

/-- Redefine `f i` to be `0`. -/
def erase {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] (i : ι) (f : dfinsupp fun (i : ι) => β i) : dfinsupp fun (i : ι) => β i :=
  quotient.lift_on f
    (fun (x : pre ι fun (i : ι) => β i) =>
      quotient.mk (pre.mk (fun (j : ι) => ite (j = i) 0 (pre.to_fun x j)) (pre.pre_support x) sorry))
    sorry

@[simp] theorem erase_apply {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {i : ι} {j : ι} {f : dfinsupp fun (i : ι) => β i} : coe_fn (erase i f) j = ite (j = i) 0 (coe_fn f j) :=
  quotient.induction_on f fun (x : pre ι fun (i : ι) => β i) => rfl

@[simp] theorem erase_same {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {i : ι} {f : dfinsupp fun (i : ι) => β i} : coe_fn (erase i f) i = 0 := sorry

theorem erase_ne {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {i : ι} {i' : ι} {f : dfinsupp fun (i : ι) => β i} (h : i' ≠ i) : coe_fn (erase i f) i' = coe_fn f i' := sorry

@[simp] theorem single_add {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] {i : ι} {b₁ : β i} {b₂ : β i} : single i (b₁ + b₂) = single i b₁ + single i b₂ := sorry

/-- `dfinsupp.single` as an `add_monoid_hom`. -/
@[simp] theorem single_add_hom_apply {ι : Type u} (β : ι → Type v) [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] (i : ι) (b : β i) : coe_fn (single_add_hom β i) b = single i b :=
  Eq.refl (coe_fn (single_add_hom β i) b)

theorem single_add_erase {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] {i : ι} {f : dfinsupp fun (i : ι) => β i} : single i (coe_fn f i) + erase i f = f := sorry

theorem erase_add_single {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] {i : ι} {f : dfinsupp fun (i : ι) => β i} : erase i f + single i (coe_fn f i) = f := sorry

protected theorem induction {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] {p : (dfinsupp fun (i : ι) => β i) → Prop} (f : dfinsupp fun (i : ι) => β i) (h0 : p 0) (ha : ∀ (i : ι) (b : β i) (f : dfinsupp fun (i : ι) => β i), coe_fn f i = 0 → b ≠ 0 → p f → p (single i b + f)) : p f := sorry

theorem induction₂ {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] {p : (dfinsupp fun (i : ι) => β i) → Prop} (f : dfinsupp fun (i : ι) => β i) (h0 : p 0) (ha : ∀ (i : ι) (b : β i) (f : dfinsupp fun (i : ι) => β i), coe_fn f i = 0 → b ≠ 0 → p f → p (f + single i b)) : p f := sorry

@[simp] theorem add_closure_Union_range_single {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] : add_submonoid.closure (set.Union fun (i : ι) => set.range (single i)) = ⊤ := sorry

/-- If two additive homomorphisms from `Π₀ i, β i` are equal on each `single a b`, then
they are equal. -/
theorem add_hom_ext {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] {γ : Type w} [add_monoid γ] {f : (dfinsupp fun (i : ι) => β i) →+ γ} {g : (dfinsupp fun (i : ι) => β i) →+ γ} (H : ∀ (i : ι) (y : β i), coe_fn f (single i y) = coe_fn g (single i y)) : f = g := sorry

/-- If two additive homomorphisms from `Π₀ i, β i` are equal on each `single a b`, then
they are equal.

See note [partially-applied ext lemmas]. -/
theorem add_hom_ext' {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] {γ : Type w} [add_monoid γ] {f : (dfinsupp fun (i : ι) => β i) →+ γ} {g : (dfinsupp fun (i : ι) => β i) →+ γ} (H : ∀ (x : ι), add_monoid_hom.comp f (single_add_hom β x) = add_monoid_hom.comp g (single_add_hom β x)) : f = g :=
  add_hom_ext fun (x : ι) => add_monoid_hom.congr_fun (H x)

@[simp] theorem mk_add {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] {s : finset ι} {x : (i : ↥↑s) → β ↑i} {y : (i : ↥↑s) → β ↑i} : mk s (x + y) = mk s x + mk s y := sorry

@[simp] theorem mk_zero {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] {s : finset ι} : mk s 0 = 0 := sorry

@[simp] theorem mk_neg {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_group (β i)] {s : finset ι} {x : (i : ↥↑s) → β (subtype.val i)} : mk s (-x) = -mk s x := sorry

@[simp] theorem mk_sub {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_group (β i)] {s : finset ι} {x : (i : ↥↑s) → β (subtype.val i)} {y : (i : ↥↑s) → β (subtype.val i)} : mk s (x - y) = mk s x - mk s y := sorry

protected instance mk.is_add_group_hom {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_group (β i)] {s : finset ι} : is_add_group_hom (mk s) :=
  is_add_group_hom.mk

@[simp] theorem mk_smul {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] (γ : Type w) [semiring γ] [(i : ι) → add_comm_monoid (β i)] [(i : ι) → semimodule γ (β i)] {s : finset ι} {c : γ} (x : (i : ↥↑s) → β (subtype.val i)) : mk s (c • x) = c • mk s x := sorry

@[simp] theorem single_smul {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] (γ : Type w) [semiring γ] [(i : ι) → add_comm_monoid (β i)] [(i : ι) → semimodule γ (β i)] {i : ι} {c : γ} {x : β i} : single i (c • x) = c • single i x := sorry

/-- Set `{i | f x ≠ 0}` as a `finset`. -/
def support {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] (f : dfinsupp fun (i : ι) => β i) : finset ι :=
  quotient.lift_on f
    (fun (x : pre ι fun (i : ι) => β i) =>
      finset.filter (fun (i : ι) => pre.to_fun x i ≠ 0) (multiset.to_finset (pre.pre_support x)))
    sorry

@[simp] theorem support_mk_subset {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {s : finset ι} {x : (i : ↥↑s) → β (subtype.val i)} : support (mk s x) ⊆ s :=
  fun (i : ι) (H : i ∈ support (mk s x)) => iff.mp multiset.mem_to_finset (and.left (iff.mp finset.mem_filter H))

@[simp] theorem mem_support_to_fun {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] (f : dfinsupp fun (i : ι) => β i) (i : ι) : i ∈ support f ↔ coe_fn f i ≠ 0 := sorry

theorem eq_mk_support {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] (f : dfinsupp fun (i : ι) => β i) : f = mk (support f) fun (i : ↥↑(support f)) => coe_fn f ↑i := sorry

@[simp] theorem support_zero {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] : support 0 = ∅ :=
  rfl

theorem mem_support_iff {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] (f : dfinsupp fun (i : ι) => β i) (i : ι) : i ∈ support f ↔ coe_fn f i ≠ 0 :=
  mem_support_to_fun f

@[simp] theorem support_eq_empty {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {f : dfinsupp fun (i : ι) => β i} : support f = ∅ ↔ f = 0 := sorry

protected instance decidable_zero {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] : decidable_pred (Eq 0) :=
  fun (f : dfinsupp fun (i : ι) => β i) => decidable_of_iff (support f = ∅) sorry

theorem support_subset_iff {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {s : set ι} {f : dfinsupp fun (i : ι) => β i} : ↑(support f) ⊆ s ↔ ∀ (i : ι), ¬i ∈ s → coe_fn f i = 0 := sorry

theorem support_single_ne_zero {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {i : ι} {b : β i} (hb : b ≠ 0) : support (single i b) = singleton i := sorry

theorem support_single_subset {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {i : ι} {b : β i} : support (single i b) ⊆ singleton i :=
  support_mk_subset

theorem map_range_def {ι : Type u} [dec : DecidableEq ι] {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] [(i : ι) → (x : β₁ i) → Decidable (x ≠ 0)] {f : (i : ι) → β₁ i → β₂ i} {hf : ∀ (i : ι), f i 0 = 0} {g : dfinsupp fun (i : ι) => β₁ i} : map_range f hf g = mk (support g) fun (i : ↥↑(support g)) => f (subtype.val i) (coe_fn g (subtype.val i)) := sorry

@[simp] theorem map_range_single {ι : Type u} [dec : DecidableEq ι] {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] {f : (i : ι) → β₁ i → β₂ i} {hf : ∀ (i : ι), f i 0 = 0} {i : ι} {b : β₁ i} : map_range f hf (single i b) = single i (f i b) := sorry

theorem support_map_range {ι : Type u} [dec : DecidableEq ι] {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] [(i : ι) → (x : β₁ i) → Decidable (x ≠ 0)] [(i : ι) → (x : β₂ i) → Decidable (x ≠ 0)] {f : (i : ι) → β₁ i → β₂ i} {hf : ∀ (i : ι), f i 0 = 0} {g : dfinsupp fun (i : ι) => β₁ i} : support (map_range f hf g) ⊆ support g := sorry

theorem zip_with_def {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] [(i : ι) → (x : β₁ i) → Decidable (x ≠ 0)] [(i : ι) → (x : β₂ i) → Decidable (x ≠ 0)] {f : (i : ι) → β₁ i → β₂ i → β i} {hf : ∀ (i : ι), f i 0 0 = 0} {g₁ : dfinsupp fun (i : ι) => β₁ i} {g₂ : dfinsupp fun (i : ι) => β₂ i} : zip_with f hf g₁ g₂ =
  mk (support g₁ ∪ support g₂)
    fun (i : ↥↑(support g₁ ∪ support g₂)) => f (subtype.val i) (coe_fn g₁ (subtype.val i)) (coe_fn g₂ (subtype.val i)) := sorry

theorem support_zip_with {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] [(i : ι) → (x : β₁ i) → Decidable (x ≠ 0)] [(i : ι) → (x : β₂ i) → Decidable (x ≠ 0)] {f : (i : ι) → β₁ i → β₂ i → β i} {hf : ∀ (i : ι), f i 0 0 = 0} {g₁ : dfinsupp fun (i : ι) => β₁ i} {g₂ : dfinsupp fun (i : ι) => β₂ i} : support (zip_with f hf g₁ g₂) ⊆ support g₁ ∪ support g₂ := sorry

theorem erase_def {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] (i : ι) (f : dfinsupp fun (i : ι) => β i) : erase i f = mk (finset.erase (support f) i) fun (j : ↥↑(finset.erase (support f) i)) => coe_fn f (subtype.val j) := sorry

@[simp] theorem support_erase {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] (i : ι) (f : dfinsupp fun (i : ι) => β i) : support (erase i f) = finset.erase (support f) i := sorry

theorem filter_def {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {p : ι → Prop} [decidable_pred p] (f : dfinsupp fun (i : ι) => β i) : filter p f = mk (finset.filter p (support f)) fun (i : ↥↑(finset.filter p (support f))) => coe_fn f (subtype.val i) := sorry

@[simp] theorem support_filter {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {p : ι → Prop} [decidable_pred p] (f : dfinsupp fun (i : ι) => β i) : support (filter p f) = finset.filter p (support f) := sorry

theorem subtype_domain_def {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {p : ι → Prop} [decidable_pred p] (f : dfinsupp fun (i : ι) => β i) : subtype_domain p f = mk (finset.subtype p (support f)) fun (i : ↥↑(finset.subtype p (support f))) => coe_fn f ↑i := sorry

@[simp] theorem support_subtype_domain {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {p : ι → Prop} [decidable_pred p] {f : dfinsupp fun (i : ι) => β i} : support (subtype_domain p f) = finset.subtype p (support f) := sorry

theorem support_add {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {g₁ : dfinsupp fun (i : ι) => β i} {g₂ : dfinsupp fun (i : ι) => β i} : support (g₁ + g₂) ⊆ support g₁ ∪ support g₂ :=
  support_zip_with

@[simp] theorem support_neg {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_group (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {f : dfinsupp fun (i : ι) => β i} : support (-f) = support f := sorry

theorem support_smul {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [semiring γ] [(i : ι) → add_comm_monoid (β i)] [(i : ι) → semimodule γ (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] (b : γ) (v : dfinsupp fun (i : ι) => β i) : support (b • v) ⊆ support v :=
  support_map_range

protected instance decidable_eq {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → HasZero (β i)] [(i : ι) → DecidableEq (β i)] : DecidableEq (dfinsupp fun (i : ι) => β i) :=
  fun (f g : dfinsupp fun (i : ι) => β i) =>
    decidable_of_iff (support f = support g ∧ ∀ (i : ι), i ∈ support f → coe_fn f i = coe_fn g i) sorry

-- [to_additive sum] for dfinsupp.prod doesn't work, the equation lemmas are not generated

/-- `sum f g` is the sum of `g i (f i)` over the support of `f`. -/
def sum {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_monoid γ] (f : dfinsupp fun (i : ι) => β i) (g : (i : ι) → β i → γ) : γ :=
  finset.sum (support f) fun (i : ι) => g i (coe_fn f i)

/-- `prod f g` is the product of `g i (f i)` over the support of `f`. -/
def prod {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [comm_monoid γ] (f : dfinsupp fun (i : ι) => β i) (g : (i : ι) → β i → γ) : γ :=
  finset.prod (support f) fun (i : ι) => g i (coe_fn f i)

theorem prod_map_range_index {ι : Type u} [dec : DecidableEq ι] {γ : Type w} {β₁ : ι → Type v₁} {β₂ : ι → Type v₂} [(i : ι) → HasZero (β₁ i)] [(i : ι) → HasZero (β₂ i)] [(i : ι) → (x : β₁ i) → Decidable (x ≠ 0)] [(i : ι) → (x : β₂ i) → Decidable (x ≠ 0)] [comm_monoid γ] {f : (i : ι) → β₁ i → β₂ i} {hf : ∀ (i : ι), f i 0 = 0} {g : dfinsupp fun (i : ι) => β₁ i} {h : (i : ι) → β₂ i → γ} (h0 : ∀ (i : ι), h i 0 = 1) : prod (map_range f hf g) h = prod g fun (i : ι) (b : β₁ i) => h i (f i b) := sorry

theorem sum_zero_index {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_comm_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_monoid γ] {h : (i : ι) → β i → γ} : sum 0 h = 0 :=
  rfl

theorem sum_single_index {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_monoid γ] {i : ι} {b : β i} {h : (i : ι) → β i → γ} (h_zero : h i 0 = 0) : sum (single i b) h = h i b := sorry

theorem sum_neg_index {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_group (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_monoid γ] {g : dfinsupp fun (i : ι) => β i} {h : (i : ι) → β i → γ} (h0 : ∀ (i : ι), h i 0 = 0) : sum (-g) h = sum g fun (i : ι) (b : β i) => h i (-b) :=
  sum_map_range_index h0

theorem sum_comm {γ : Type w} {ι₁ : Type u_1} {ι₂ : Type u_2} {β₁ : ι₁ → Type u_3} {β₂ : ι₂ → Type u_4} [DecidableEq ι₁] [DecidableEq ι₂] [(i : ι₁) → HasZero (β₁ i)] [(i : ι₂) → HasZero (β₂ i)] [(i : ι₁) → (x : β₁ i) → Decidable (x ≠ 0)] [(i : ι₂) → (x : β₂ i) → Decidable (x ≠ 0)] [add_comm_monoid γ] (f₁ : dfinsupp fun (i : ι₁) => β₁ i) (f₂ : dfinsupp fun (i : ι₂) => β₂ i) (h : (i : ι₁) → β₁ i → (i : ι₂) → β₂ i → γ) : (sum f₁ fun (i₁ : ι₁) (x₁ : β₁ i₁) => sum f₂ fun (i₂ : ι₂) (x₂ : β₂ i₂) => h i₁ x₁ i₂ x₂) =
  sum f₂ fun (i₂ : ι₂) (x₂ : β₂ i₂) => sum f₁ fun (i₁ : ι₁) (x₁ : β₁ i₁) => h i₁ x₁ i₂ x₂ :=
  finset.sum_comm

@[simp] theorem sum_apply {ι : Type u} {β : ι → Type v} {ι₁ : Type u₁} [DecidableEq ι₁] {β₁ : ι₁ → Type v₁} [(i₁ : ι₁) → HasZero (β₁ i₁)] [(i : ι₁) → (x : β₁ i) → Decidable (x ≠ 0)] [(i : ι) → add_comm_monoid (β i)] {f : dfinsupp fun (i₁ : ι₁) => β₁ i₁} {g : (i₁ : ι₁) → β₁ i₁ → dfinsupp fun (i : ι) => β i} {i₂ : ι} : coe_fn (sum f g) i₂ = sum f fun (i₁ : ι₁) (b : β₁ i₁) => coe_fn (g i₁ b) i₂ :=
  Eq.symm (finset.sum_hom (support f) fun (f : dfinsupp fun (i : ι) => β i) => coe_fn f i₂)

theorem support_sum {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {ι₁ : Type u₁} [DecidableEq ι₁] {β₁ : ι₁ → Type v₁} [(i₁ : ι₁) → HasZero (β₁ i₁)] [(i : ι₁) → (x : β₁ i) → Decidable (x ≠ 0)] [(i : ι) → add_comm_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {f : dfinsupp fun (i₁ : ι₁) => β₁ i₁} {g : (i₁ : ι₁) → β₁ i₁ → dfinsupp fun (i : ι) => β i} : support (sum f g) ⊆ finset.bUnion (support f) fun (i : ι₁) => support (g i (coe_fn f i)) := sorry

@[simp] theorem prod_one {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_comm_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [comm_monoid γ] {f : dfinsupp fun (i : ι) => β i} : (prod f fun (i : ι) (b : β i) => 1) = 1 :=
  finset.prod_const_one

@[simp] theorem sum_add {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_comm_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_monoid γ] {f : dfinsupp fun (i : ι) => β i} {h₁ : (i : ι) → β i → γ} {h₂ : (i : ι) → β i → γ} : (sum f fun (i : ι) (b : β i) => h₁ i b + h₂ i b) = sum f h₁ + sum f h₂ :=
  finset.sum_add_distrib

@[simp] theorem sum_neg {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_comm_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_group γ] {f : dfinsupp fun (i : ι) => β i} {h : (i : ι) → β i → γ} : (sum f fun (i : ι) (b : β i) => -h i b) = -sum f h :=
  finset.sum_hom (support f) Neg.neg

theorem prod_add_index {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_comm_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [comm_monoid γ] {f : dfinsupp fun (i : ι) => β i} {g : dfinsupp fun (i : ι) => β i} {h : (i : ι) → β i → γ} (h_zero : ∀ (i : ι), h i 0 = 1) (h_add : ∀ (i : ι) (b₁ b₂ : β i), h i (b₁ + b₂) = h i b₁ * h i b₂) : prod (f + g) h = prod f h * prod g h := sorry

/--
When summing over an `add_monoid_hom`, the decidability assumption is not needed, and the result is
also an `add_monoid_hom`.
-/
def sum_add_hom {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_monoid (β i)] [add_comm_monoid γ] (φ : (i : ι) → β i →+ γ) : (dfinsupp fun (i : ι) => β i) →+ γ :=
  add_monoid_hom.mk
    (fun (f : dfinsupp fun (i : ι) => β i) =>
      quotient.lift_on f
        (fun (x : pre ι fun (i : ι) => β i) =>
          finset.sum (multiset.to_finset (pre.pre_support x)) fun (i : ι) => coe_fn (φ i) (pre.to_fun x i))
        sorry)
    sorry sorry

@[simp] theorem sum_add_hom_single {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_monoid (β i)] [add_comm_monoid γ] (φ : (i : ι) → β i →+ γ) (i : ι) (x : β i) : coe_fn (sum_add_hom φ) (single i x) = coe_fn (φ i) x := sorry

@[simp] theorem sum_add_hom_comp_single {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_comm_monoid (β i)] [add_comm_monoid γ] (f : (i : ι) → β i →+ γ) (i : ι) : add_monoid_hom.comp (sum_add_hom f) (single_add_hom β i) = f i :=
  add_monoid_hom.ext fun (x : β i) => sum_add_hom_single f i x

/-- While we didn't need decidable instances to define it, we do to reduce it to a sum -/
theorem sum_add_hom_apply {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_monoid γ] (φ : (i : ι) → β i →+ γ) (f : dfinsupp fun (i : ι) => β i) : coe_fn (sum_add_hom φ) f = sum f fun (x : ι) => ⇑(φ x) := sorry

/-- The `dfinsupp` version of `finsupp.lift_add_hom`,-/
@[simp] theorem lift_add_hom_symm_apply {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_monoid (β i)] [add_comm_monoid γ] (F : (dfinsupp fun (i : ι) => β i) →+ γ) (i : ι) : coe_fn (add_equiv.symm lift_add_hom) F i = add_monoid_hom.comp F (single_add_hom β i) :=
  Eq.refl (coe_fn (add_equiv.symm lift_add_hom) F i)

/-- The `dfinsupp` version of `finsupp.lift_add_hom_single_add_hom`,-/
@[simp] theorem lift_add_hom_single_add_hom {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_comm_monoid (β i)] : coe_fn lift_add_hom (single_add_hom β) = add_monoid_hom.id (dfinsupp fun (i : ι) => β i) :=
  iff.mpr (equiv.apply_eq_iff_eq_symm_apply (add_equiv.to_equiv lift_add_hom)) rfl

/-- The `dfinsupp` version of `finsupp.lift_add_hom_apply_single`,-/
theorem lift_add_hom_apply_single {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_comm_monoid (β i)] [add_comm_monoid γ] (f : (i : ι) → β i →+ γ) (i : ι) (x : β i) : coe_fn (coe_fn lift_add_hom f) (single i x) = coe_fn (f i) x := sorry

/-- The `dfinsupp` version of `finsupp.lift_add_hom_comp_single`,-/
theorem lift_add_hom_comp_single {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_comm_monoid (β i)] [add_comm_monoid γ] (f : (i : ι) → β i →+ γ) (i : ι) : add_monoid_hom.comp (coe_fn lift_add_hom f) (single_add_hom β i) = f i := sorry

/-- The `dfinsupp` version of `finsupp.comp_lift_add_hom`,-/
theorem comp_lift_add_hom {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} {δ : Type u_1} [(i : ι) → add_comm_monoid (β i)] [add_comm_monoid γ] [add_comm_monoid δ] (g : γ →+ δ) (f : (i : ι) → β i →+ γ) : add_monoid_hom.comp g (coe_fn lift_add_hom f) = coe_fn lift_add_hom fun (a : ι) => add_monoid_hom.comp g (f a) := sorry

theorem sum_sub_index {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → add_comm_group (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_group γ] {f : dfinsupp fun (i : ι) => β i} {g : dfinsupp fun (i : ι) => β i} {h : (i : ι) → β i → γ} (h_sub : ∀ (i : ι) (b₁ b₂ : β i), h i (b₁ - b₂) = h i b₁ - h i b₂) : sum (f - g) h = sum f h - sum g h := sorry

theorem sum_finset_sum_index {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} {α : Type x} [(i : ι) → add_comm_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_monoid γ] {s : finset α} {g : α → dfinsupp fun (i : ι) => β i} {h : (i : ι) → β i → γ} (h_zero : ∀ (i : ι), h i 0 = 0) (h_add : ∀ (i : ι) (b₁ b₂ : β i), h i (b₁ + b₂) = h i b₁ + h i b₂) : (finset.sum s fun (i : α) => sum (g i) h) = sum (finset.sum s fun (i : α) => g i) h := sorry

theorem sum_sum_index {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} {ι₁ : Type u₁} [DecidableEq ι₁] {β₁ : ι₁ → Type v₁} [(i₁ : ι₁) → HasZero (β₁ i₁)] [(i : ι₁) → (x : β₁ i) → Decidable (x ≠ 0)] [(i : ι) → add_comm_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_monoid γ] {f : dfinsupp fun (i₁ : ι₁) => β₁ i₁} {g : (i₁ : ι₁) → β₁ i₁ → dfinsupp fun (i : ι) => β i} {h : (i : ι) → β i → γ} (h_zero : ∀ (i : ι), h i 0 = 0) (h_add : ∀ (i : ι) (b₁ b₂ : β i), h i (b₁ + b₂) = h i b₁ + h i b₂) : sum (sum f g) h = sum f fun (i : ι₁) (b : β₁ i) => sum (g i b) h :=
  Eq.symm (sum_finset_sum_index h_zero h_add)

@[simp] theorem sum_single {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] [(i : ι) → add_comm_monoid (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] {f : dfinsupp fun (i : ι) => β i} : sum f single = f := sorry

theorem sum_subtype_domain_index {ι : Type u} {β : ι → Type v} [dec : DecidableEq ι] {γ : Type w} [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [add_comm_monoid γ] {v : dfinsupp fun (i : ι) => β i} {p : ι → Prop} [decidable_pred p] {h : (i : ι) → β i → γ} (hp : ∀ (x : ι), x ∈ support v → p x) : (sum (subtype_domain p v) fun (i : Subtype p) (b : β ↑i) => h (↑i) b) = sum v h := sorry

theorem subtype_domain_sum {ι : Type u} {β : ι → Type v} {γ : Type w} [(i : ι) → add_comm_monoid (β i)] {s : finset γ} {h : γ → dfinsupp fun (i : ι) => β i} {p : ι → Prop} [decidable_pred p] : subtype_domain p (finset.sum s fun (c : γ) => h c) = finset.sum s fun (c : γ) => subtype_domain p (h c) :=
  Eq.symm (finset.sum_hom s (subtype_domain p))

theorem subtype_domain_finsupp_sum {ι : Type u} {β : ι → Type v} {γ : Type w} {δ : γ → Type x} [DecidableEq γ] [(c : γ) → HasZero (δ c)] [(c : γ) → (x : δ c) → Decidable (x ≠ 0)] [(i : ι) → add_comm_monoid (β i)] {p : ι → Prop} [decidable_pred p] {s : dfinsupp fun (c : γ) => δ c} {h : (c : γ) → δ c → dfinsupp fun (i : ι) => β i} : subtype_domain p (sum s h) = sum s fun (c : γ) (d : δ c) => subtype_domain p (h c d) :=
  subtype_domain_sum

end dfinsupp

/-! ### Product and sum lemmas for bundled morphisms -/

namespace monoid_hom

@[simp] theorem map_dfinsupp_prod {ι : Type u} {β : ι → Type v} [DecidableEq ι] {R : Type u_1} {S : Type u_2} [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [comm_monoid R] [comm_monoid S] (h : R →* S) (f : dfinsupp fun (i : ι) => β i) (g : (i : ι) → β i → R) : coe_fn h (dfinsupp.prod f g) = dfinsupp.prod f fun (a : ι) (b : β a) => coe_fn h (g a b) :=
  map_prod h (fun (i : ι) => g i (coe_fn f i)) (dfinsupp.support f)

theorem coe_dfinsupp_prod {ι : Type u} {β : ι → Type v} [DecidableEq ι] {R : Type u_1} {S : Type u_2} [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [monoid R] [comm_monoid S] (f : dfinsupp fun (i : ι) => β i) (g : (i : ι) → β i → R →* S) : ⇑(dfinsupp.prod f g) = dfinsupp.prod f fun (a : ι) (b : β a) => ⇑(g a b) :=
  coe_prod (fun (i : ι) => g i (coe_fn f i)) (dfinsupp.support f)

@[simp] theorem dfinsupp_prod_apply {ι : Type u} {β : ι → Type v} [DecidableEq ι] {R : Type u_1} {S : Type u_2} [(i : ι) → HasZero (β i)] [(i : ι) → (x : β i) → Decidable (x ≠ 0)] [monoid R] [comm_monoid S] (f : dfinsupp fun (i : ι) => β i) (g : (i : ι) → β i → R →* S) (r : R) : coe_fn (dfinsupp.prod f g) r = dfinsupp.prod f fun (a : ι) (b : β a) => coe_fn (g a b) r :=
  finset_prod_apply (fun (i : ι) => g i (coe_fn f i)) (dfinsupp.support f) r


/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import tactic.lint
import tactic.ext

/-!
# Congruence and related tactics

This file contains the tactic `congr'`, which is an extension of `congr`, and various tactics
using `congr'` internally.

`congr'` has some advantages over `congr`:
* It turns `↔` to equalities, before trying another congr lemma
* You can write `congr' n` to give the maximal depth of recursive applications. This is useful if
  `congr` breaks down the goal to aggressively, and the resulting goals are false.
* You can write `congr' with ...` to do `congr', ext ...` in a single tactic.

Other tactics in this file:
* `rcongr`: repeatedly apply `congr'` and `ext.`
* `convert`: like `exact`, but produces an equality goal if the type doesn't match.
* `convert_to`: changes the goal, if you prove an equality between the old goal and the new goal.
* `ac_change`: like `convert_to`, but uses `ac_refl` to discharge the goals.
-/

open tactic
setup_tactic_parser

namespace tactic

/-- Apply the constant `iff_of_eq` to the goal. -/
meta def apply_iff_congr_core : tactic unit :=
applyc ``iff_of_eq

/-- The main part of the body for the loop in `congr'`. This will try to replace a goal `f x = f y`
 with `x = y`. Also has support for `==` and `↔`. -/
meta def congr_core' : tactic unit :=
do tgt ← target,
   apply_eq_congr_core tgt
   <|> apply_heq_congr_core
   <|> apply_iff_congr_core
   <|> fail "congr tactic failed"

/-- The main function in `convert_to`. Changes the goal to `r` and a proof obligation that the goal
  is equal to `r`. -/
meta def convert_to_core (r : pexpr) : tactic unit :=
do tgt ← target,
   h   ← to_expr ``(_ : %%tgt = %%r),
   rewrite_target h,
   swap

/-- Attempts to prove the goal by proof irrelevance, but avoids unifying universe metavariables
to do so. -/
meta def by_proof_irrel : tactic unit :=
do tgt ← target,
  @expr.const tt n [level.zero] ← pure tgt.get_app_fn,
  if n = ``eq then `[apply proof_irrel] else
  if n = ``heq then `[apply proof_irrel_heq] else
  failed

/--
Same as the `congr` tactic, but takes an optional argument which gives
the depth of recursive applications.
* This is useful when `congr` is too aggressive in breaking down the goal.
* For example, given `⊢ f (g (x + y)) = f (g (y + x))`, `congr'` produces the goals `⊢ x = y`
  and `⊢ y = x`, while `congr' 2` produces the intended `⊢ x + y = y + x`.
* If, at any point, a subgoal matches a hypothesis then the subgoal will be closed.
-/
meta def congr' : option ℕ → tactic unit
| o := focus1 $
  assumption <|> reflexivity transparency.none <|> by_proof_irrel <|>
  (guard (o ≠ some 0) >> congr_core' >>
    all_goals' (try (congr' (nat.pred <$> o)))) <|>
  reflexivity

namespace interactive

/--
Same as the `congr` tactic, but takes an optional argument which gives
the depth of recursive applications.
* This is useful when `congr` is too aggressive in breaking down the goal.
* For example, given `⊢ f (g (x + y)) = f (g (y + x))`, `congr'` produces the goals `⊢ x = y`
  and `⊢ y = x`, while `congr' 2` produces the intended `⊢ x + y = y + x`.
* If, at any point, a subgoal matches a hypothesis then the subgoal will be closed.
* You can use `congr' with p (: n)?` to call `ext p (: n)?` to all subgoals generated by `congr'`.
  For example, if the goal is `⊢ f '' s = g '' s` then `congr' with x` generates the goal
  `x : α ⊢ f x = g x`.
-/
meta def congr' (n : parse (with_desc "n" small_nat)?) :
  parse (tk "with" *> prod.mk <$> rintro_patt_parse_hi* <*> (tk ":" *> small_nat)?)? →
  tactic unit
| none         := tactic.congr' n
| (some ⟨p, m⟩) := focus1 (tactic.congr' n >> all_goals' (tactic.ext p.join m $> ()))

/--
Repeatedly and apply `congr'` and `ext`, using the given patterns as arguments for `ext`.

There are two ways this tactic stops:
* `congr'` fails (makes no progress), after having already applied `ext`.
* `congr'` canceled out the last usage of `ext`. In this case, the state is reverted to before
  the `congr'` was applied.

For example, when the goal is
```lean
⊢ (λ x, f x + 3) '' s = (λ x, g x + 3) '' s
```
then `rcongr x` produces the goal
```lean
x : α ⊢ f x = g x
```
This gives the same result as `congr', ext x, congr'`.

In contrast, `congr'` would produce
```lean
⊢ (λ x, f x + 3) = (λ x, g x + 3)
```
and `congr' with x` (or `congr', ext x`) would produce
```lean
x : α ⊢ f x + 3 = g x + 3
```
-/
meta def rcongr : parse (list.join <$> rintro_patt_parse_hi*) → tactic unit
| ps := do
  t ← target,
  qs ← try_core (tactic.ext ps none),
  some () ← try_core (tactic.congr' none >>
    (done <|> do s ← target, guard $ ¬ s =ₐ t)) | skip,
  done <|> rcongr (qs.get_or_else ps)

add_tactic_doc
{ name       := "congr'",
  category   := doc_category.tactic,
  decl_names := [`tactic.interactive.congr', `tactic.interactive.congr, `tactic.interactive.rcongr],
  tags       := ["congruence"],
  inherit_description_from := `tactic.interactive.congr' }

/--
The `exact e` and `refine e` tactics require a term `e` whose type is
definitionally equal to the goal. `convert e` is similar to `refine e`,
but the type of `e` is not required to exactly match the
goal. Instead, new goals are created for differences between the type
of `e` and the goal. For example, in the proof state

```lean
n : ℕ,
e : prime (2 * n + 1)
⊢ prime (n + n + 1)
```

the tactic `convert e` will change the goal to

```lean
⊢ n + n = 2 * n
```

In this example, the new goal can be solved using `ring`.

The `convert` tactic applies congruence lemmas eagerly before reducing,
therefore it can fail in cases where `exact` succeeds:
```lean
def p (n : ℕ) := true
example (h : p 0) : p 1 := by exact h -- succeeds
example (h : p 0) : p 1 := by convert h -- fails, with leftover goal `1 = 0`
```

If `x y : t`, and an instance `subsingleton t` is in scope, then any goals of the form
`x = y` are solved automatically.

The syntax `convert ← e` will reverse the direction of the new goals
(producing `⊢ 2 * n = n + n` in this example).

Internally, `convert e` works by creating a new goal asserting that
the goal equals the type of `e`, then simplifying it using
`congr'`. The syntax `convert e using n` can be used to control the
depth of matching (like `congr' n`). In the example, `convert e using
1` would produce a new goal `⊢ n + n + 1 = 2 * n + 1`.
-/
meta def convert (sym : parse (with_desc "←" (tk "<-")?)) (r : parse texpr)
  (n : parse (tk "using" *> small_nat)?) : tactic unit :=
do tgt ← target,
  u ← infer_type tgt,
  r ← i_to_expr ``(%%r : (_ : %%u)),
  src ← infer_type r,
  src ← simp_lemmas.mk.dsimplify [] src {fail_if_unchanged := ff},
  v ← to_expr (if sym.is_some then ``(%%src = %%tgt) else ``(%%tgt = %%src)) tt ff >>= mk_meta_var,
  (if sym.is_some then mk_eq_mp v r else mk_eq_mpr v r) >>= tactic.exact,
  gs ← get_goals,
  set_goals [v],
  try (tactic.congr' n),
  gs' ← get_goals,
  set_goals $ gs' ++ gs

add_tactic_doc
{ name       := "convert",
  category   := doc_category.tactic,
  decl_names := [`tactic.interactive.convert],
  tags       := ["congruence"] }

/--
`convert_to g using n` attempts to change the current goal to `g`, but unlike `change`,
it will generate equality proof obligations using `congr' n` to resolve discrepancies.
`convert_to g` defaults to using `congr' 1`.

`convert_to` is similar to `convert`, but `convert_to` takes a type (the desired subgoal) while
`convert` takes a proof term.
That is, `convert_to g using n` is equivalent to `convert (_ : g) using n`.
-/
meta def convert_to (r : parse texpr) (n : parse (tk "using" *> small_nat)?) : tactic unit :=
match n with
  | none     := convert_to_core r >> `[congr' 1]
  | (some 0) := convert_to_core r
  | (some o) := convert_to_core r >> tactic.congr' o
end

/--
`ac_change g using n` is `convert_to g using n` followed by `ac_refl`. It is useful for
rearranging/reassociating e.g. sums:
```lean
example (a b c d e f g N : ℕ) : (a + b) + (c + d) + (e + f) + g ≤ N :=
begin
  ac_change a + d + e + f + c + g + b ≤ _,
-- ⊢ a + d + e + f + c + g + b ≤ N
end
```

##  Related tactic: `move_add`
In the case in which the expression to be changed is a sum of terms, tactic
`tactive.interactive.move_add` can also be useful. -/
meta def ac_change (r : parse texpr) (n : parse (tk "using" *> small_nat)?) : tactic unit :=
convert_to r n; try ac_refl

add_tactic_doc
{ name       := "convert_to",
  category   := doc_category.tactic,
  decl_names := [`tactic.interactive.convert_to, `tactic.interactive.ac_change],
  tags       := ["congruence"],
  inherit_description_from := `tactic.interactive.convert_to }

end interactive

end tactic


import Smt

theorem cong (p q : Bool) (f : Bool → Bool) : p == q → f p == f q := by
  smt
  cases p <;> cases q <;> cases f true <;> cases f false <;> simp_all


import mynat.definition
import mynat.add
import mynat.mul

lemma example1 (x y z : mynat) : x * y + z = x * y + z :=
begin
refl,
end


import for_mathlib.category_theory.localization.predicate

noncomputable theory

namespace category_theory

open localization category

namespace adjunction

variables {C₁ C₂ D₁ D₂ : Type*} [category C₁] [category C₂] [category D₁] [category D₂]
  {G : C₁ ⥤ C₂} {F : C₂ ⥤ C₁} (adj : G ⊣ F)
  (L₁ : C₁ ⥤ D₁) (W₁ : morphism_property C₁) [L₁.is_localization W₁]
  (L₂ : C₂ ⥤ D₂) (W₂ : morphism_property C₂) [L₂.is_localization W₂]
  (G' : D₁ ⥤ D₂) (F' : D₂ ⥤ D₁)

include adj
/-- Should be redone using Comm_sq instead of `lifting`. -/
def localization [lifting L₁ W₁ (G ⋙ L₂) G'] [lifting L₂ W₂ (F ⋙ L₁) F'] :
  G' ⊣ F' := adjunction.mk_of_unit_counit
begin
  let α₁ := lifting.iso L₂ W₂ (F ⋙ L₁) F',
  let α₂ := lifting.iso L₁ W₁ (G ⋙ L₂) G',
  let e₁ : (G ⋙ L₂) ⋙ F' ≅ (G ⋙ F) ⋙ L₁ := iso_whisker_left G α₁,
  let e₂ : (F ⋙ L₁) ⋙ G' ≅ (F ⋙ G) ⋙ L₂ := iso_whisker_left F α₂,
  letI : lifting L₁ W₁ ((G ⋙ F) ⋙ L₁) (G' ⋙ F') :=
    lifting.of_isos L₁ W₁ e₁ (iso.refl (G' ⋙ F')),
  letI : lifting L₂ W₂ ((F ⋙ G) ⋙ L₂) (F' ⋙ G') :=
    lifting.of_isos L₂ W₂ e₂ (iso.refl (F' ⋙ G')),
  let ε := lift_nat_trans L₁ W₁ L₁ ((G ⋙ F) ⋙ L₁) (𝟭 D₁) (G' ⋙ F')
    ((functor.left_unitor L₁).inv ≫ nat_trans.hcomp adj.unit (𝟙 L₁)),
  let η := lift_nat_trans L₂ W₂ ((F ⋙ G) ⋙ L₂) L₂ (F' ⋙ G') (𝟭 D₂)
      (nat_trans.hcomp adj.counit (𝟙 L₂) ≫ (functor.left_unitor L₂).hom),
  have hε : ∀ (X₁ : C₁), ε.app (L₁.obj X₁) = L₁.map (adj.unit.app X₁) ≫
    α₁.inv.app (G.obj X₁) ≫ F'.map (α₂.inv.app X₁),
  { intro X₁,
    rw lift_nat_trans_app,
    dsimp,
    simp only [id_comp, comp_id], },
  have hη : ∀ (X₂ : C₂), η.app (L₂.obj X₂) = G'.map (α₁.hom.app X₂) ≫
    α₂.hom.app (F.obj X₂) ≫ L₂.map (adj.counit.app X₂),
  { intro X₂,
    rw lift_nat_trans_app,
    dsimp,
    simp only [id_comp, comp_id, assoc], },
  exact
  { unit := ε,
    counit := η,
    left_triangle' := nat_trans_ext L₁ W₁ _ _ (λ X₁, begin
      have eq := congr_app adj.left_triangle X₁,
      dsimp at ⊢ eq,
      erw [id_comp, hε, G'.map_comp, G'.map_comp, assoc, assoc, η.naturality, hη, assoc, assoc],
      slice_lhs 2 3 { erw [← G'.map_comp, α₁.inv_hom_id_app, G'.map_id], },
      erw [assoc, assoc, id_comp, α₂.hom.naturality_assoc, ← L₂.map_comp_assoc, eq,
        L₂.map_id, id_comp, α₂.hom_inv_id_app],
      refl,
    end),
    right_triangle' := nat_trans_ext L₂ W₂ _ _ (λ X₂, begin
      have eq := congr_app adj.right_triangle X₂,
      dsimp at ⊢ eq,
      erw [id_comp, hη, F'.map_comp, F'.map_comp, ← ε.naturality_assoc, hε, assoc, assoc],
      slice_lhs 4 5 { erw [← F'.map_comp, α₂.inv_hom_id_app, F'.map_id], },
      erw [id_comp, ← α₁.inv.naturality, ← L₁.map_comp_assoc, eq,
        L₁.map_id, id_comp, α₁.hom_inv_id_app],
      refl,
    end) },
end

@[simp]
lemma localization_unit_app [lifting L₁ W₁ (G ⋙ L₂) G'] [lifting L₂ W₂ (F ⋙ L₁) F'] (X₁ : C₁) :
  (adj.localization L₁ W₁ L₂ W₂ G' F').unit.app (L₁.obj X₁) =
    L₁.map (adj.unit.app X₁) ≫ (lifting.iso L₂ W₂ (F ⋙ L₁) F').inv.app (G.obj X₁) ≫
    F'.map ((lifting.iso L₁ W₁ (G ⋙ L₂) G').inv.app X₁) :=
begin
  dsimp only [localization, adjunction.mk_of_unit_counit],
  rw lift_nat_trans_app,
  dsimp,
  simp only [id_comp, comp_id],
end

@[simp]
lemma localization_counit_app [lifting L₁ W₁ (G ⋙ L₂) G'] [lifting L₂ W₂ (F ⋙ L₁) F'] (X₂ : C₂) :
  (adj.localization L₁ W₁ L₂ W₂ G' F').counit.app (L₂.obj X₂) =
    G'.map (((lifting.iso L₂ W₂ (F ⋙ L₁) F')).hom.app X₂) ≫
      (lifting.iso L₁ W₁ (G ⋙ L₂) G').hom.app (F.obj X₂) ≫ L₂.map (adj.counit.app X₂) :=
begin
  dsimp only [localization, adjunction.mk_of_unit_counit],
  rw lift_nat_trans_app,
  dsimp,
  simp only [id_comp, comp_id, assoc],
end

end adjunction

end category_theory


/-
Copyright (c) 2021 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser

! This file was ported from Lean 3 source module group_theory.perm.option
! leanprover-community/mathlib commit ee05e9ce1322178f0c12004eb93c00d2c8c00ed2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Fintype.Perm
import Mathbin.GroupTheory.Perm.Sign
import Mathbin.Logic.Equiv.Option

/-!
# Permutations of `option α`

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.
-/

open Equiv

/- warning: equiv.option_congr_one -> Equiv.optionCongr_one is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Eq.{succ u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.optionCongr.{u1, u1} α α (OfNat.ofNat.{u1} (Equiv.Perm.{succ u1} α) 1 (OfNat.mk.{u1} (Equiv.Perm.{succ u1} α) 1 (One.one.{u1} (Equiv.Perm.{succ u1} α) (MulOneClass.toHasOne.{u1} (Equiv.Perm.{succ u1} α) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α))))))))) (OfNat.ofNat.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) 1 (OfNat.mk.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) 1 (One.one.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (MulOneClass.toHasOne.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Monoid.toMulOneClass.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α)))))))))
but is expected to have type
  forall {α : Type.{u1}}, Eq.{succ u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.optionCongr.{u1, u1} α α (OfNat.ofNat.{u1} (Equiv.Perm.{succ u1} α) 1 (One.toOfNat1.{u1} (Equiv.Perm.{succ u1} α) (InvOneClass.toOne.{u1} (Equiv.Perm.{succ u1} α) (DivInvOneMonoid.toInvOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivisionMonoid.toDivInvOneMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivisionMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))))))) (OfNat.ofNat.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) 1 (One.toOfNat1.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (InvOneClass.toOne.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (DivInvOneMonoid.toInvOneClass.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (DivisionMonoid.toDivInvOneMonoid.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Group.toDivisionMonoid.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))))))
Case conversion may be inaccurate. Consider using '#align equiv.option_congr_one Equiv.optionCongr_oneₓ'. -/
@[simp]
theorem Equiv.optionCongr_one {α : Type _} : (1 : Perm α).optionCongr = 1 :=
  Equiv.optionCongr_refl
#align equiv.option_congr_one Equiv.optionCongr_one

/- warning: equiv.option_congr_swap -> Equiv.optionCongr_swap is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (x : α) (y : α), Eq.{succ u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.optionCongr.{u1, u1} α α (Equiv.swap.{succ u1} α (fun (a : α) (b : α) => _inst_1 a b) x y)) (Equiv.swap.{succ u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => Option.decidableEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (Option.some.{u1} α x) (Option.some.{u1} α y))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (x : α) (y : α), Eq.{succ u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.optionCongr.{u1, u1} α α (Equiv.swap.{succ u1} α (fun (a : α) (b : α) => _inst_1 a b) x y)) (Equiv.swap.{succ u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => instDecidableEqOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (Option.some.{u1} α x) (Option.some.{u1} α y))
Case conversion may be inaccurate. Consider using '#align equiv.option_congr_swap Equiv.optionCongr_swapₓ'. -/
@[simp]
theorem Equiv.optionCongr_swap {α : Type _} [DecidableEq α] (x y : α) :
    optionCongr (swap x y) = swap (some x) (some y) :=
  by
  ext (_ | i)
  · simp [swap_apply_of_ne_of_ne]
  · by_cases hx : i = x
    simp [hx, swap_apply_of_ne_of_ne]
    by_cases hy : i = y <;> simp [hx, hy, swap_apply_of_ne_of_ne]
#align equiv.option_congr_swap Equiv.optionCongr_swap

/- warning: equiv.option_congr_sign -> Equiv.optionCongr_sign is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] [_inst_2 : Fintype.{u1} α] (e : Equiv.Perm.{succ u1} α), Eq.{1} (Units.{0} Int Int.monoid) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.mulOneClass.{0} Int Int.monoid)) (fun (_x : MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.mulOneClass.{0} Int Int.monoid)) => (Equiv.Perm.{succ u1} (Option.{u1} α)) -> (Units.{0} Int Int.monoid)) (MonoidHom.hasCoeToFun.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.mulOneClass.{0} Int Int.monoid)) (Equiv.Perm.sign.{u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => Option.decidableEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (Option.fintype.{u1} α _inst_2)) (Equiv.optionCongr.{u1, u1} α α e)) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.mulOneClass.{0} Int Int.monoid)) (fun (_x : MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.mulOneClass.{0} Int Int.monoid)) => (Equiv.Perm.{succ u1} α) -> (Units.{0} Int Int.monoid)) (MonoidHom.hasCoeToFun.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.mulOneClass.{0} Int Int.monoid)) (Equiv.Perm.sign.{u1} α (fun (a : α) (b : α) => _inst_1 a b) _inst_2) e)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] [_inst_2 : Fintype.{u1} α] (e : Equiv.Perm.{succ u1} α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Equiv.Perm.{succ u1} (Option.{u1} α)) => Units.{0} Int Int.instMonoidInt) (Equiv.optionCongr.{u1, u1} α α e)) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (fun (_x : Equiv.Perm.{succ u1} (Option.{u1} α)) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Equiv.Perm.{succ u1} (Option.{u1} α)) => Units.{0} Int Int.instMonoidInt) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (MulOneClass.toMul.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α)))))) (MulOneClass.toMul.{0} (Units.{0} Int Int.instMonoidInt) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt) (MonoidHom.monoidHomClass.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)))) (Equiv.Perm.sign.{u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => instDecidableEqOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (instFintypeOption.{u1} α _inst_2)) (Equiv.optionCongr.{u1, u1} α α e)) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} α) (fun (_x : Equiv.Perm.{succ u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Equiv.Perm.{succ u1} α) => Units.{0} Int Int.instMonoidInt) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (MulOneClass.toMul.{u1} (Equiv.Perm.{succ u1} α) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α))))) (MulOneClass.toMul.{0} (Units.{0} Int Int.instMonoidInt) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt) (MonoidHom.monoidHomClass.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)))) (Equiv.Perm.sign.{u1} α (fun (a : α) (b : α) => _inst_1 a b) _inst_2) e)
Case conversion may be inaccurate. Consider using '#align equiv.option_congr_sign Equiv.optionCongr_signₓ'. -/
@[simp]
theorem Equiv.optionCongr_sign {α : Type _} [DecidableEq α] [Fintype α] (e : Perm α) :
    Perm.sign e.optionCongr = Perm.sign e :=
  by
  apply perm.swap_induction_on e
  · simp [perm.one_def]
  · intro f x y hne h
    simp [h, hne, perm.mul_def, ← Equiv.optionCongr_trans]
#align equiv.option_congr_sign Equiv.optionCongr_sign

/- warning: map_equiv_remove_none -> map_equiv_removeNone is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (σ : Equiv.Perm.{succ u1} (Option.{u1} α)), Eq.{succ u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.optionCongr.{u1, u1} α α (Equiv.removeNone.{u1, u1} α α σ)) (HMul.hMul.{u1, u1, u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (instHMul.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (MulOneClass.toHasMul.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Monoid.toMulOneClass.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))))) (Equiv.swap.{succ u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => Option.decidableEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (Option.none.{u1} α) (coeFn.{succ u1, succ u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (fun (_x : Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) => (Option.{u1} α) -> (Option.{u1} α)) (Equiv.hasCoeToFun.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) σ (Option.none.{u1} α))) σ)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] (σ : Equiv.Perm.{succ u1} (Option.{u1} α)), Eq.{succ u1} (Equiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) (Equiv.optionCongr.{u1, u1} α α (Equiv.removeNone.{u1, u1} α α σ)) (HMul.hMul.{u1, u1, u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (instHMul.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (MulOneClass.toMul.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))))) (Equiv.swap.{succ u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => instDecidableEqOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (Option.none.{u1} α) (FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Option.{u1} α) (fun (_x : Option.{u1} α) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Option.{u1} α) => Option.{u1} α) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u1} (Option.{u1} α) (Option.{u1} α)) σ (Option.none.{u1} α))) σ)
Case conversion may be inaccurate. Consider using '#align map_equiv_remove_none map_equiv_removeNoneₓ'. -/
@[simp]
theorem map_equiv_removeNone {α : Type _} [DecidableEq α] (σ : Perm (Option α)) :
    (removeNone σ).optionCongr = swap none (σ none) * σ :=
  by
  ext1 x
  have : Option.map (⇑(remove_none σ)) x = (swap none (σ none)) (σ x) :=
    by
    cases x
    · simp
    · cases h : σ (some x)
      · simp [remove_none_none _ h]
      · have hn : σ (some x) ≠ none := by simp [h]
        have hσn : σ (some x) ≠ σ none := σ.injective.ne (by simp)
        simp [remove_none_some _ ⟨_, h⟩, ← h, swap_apply_of_ne_of_ne hn hσn]
  simpa using this
#align map_equiv_remove_none map_equiv_removeNone

#print Equiv.Perm.decomposeOption /-
/-- Permutations of `option α` are equivalent to fixing an
`option α` and permuting the remaining with a `perm α`.
The fixed `option α` is swapped with `none`. -/
@[simps]
def Equiv.Perm.decomposeOption {α : Type _} [DecidableEq α] : Perm (Option α) ≃ Option α × Perm α
    where
  toFun σ := (σ none, removeNone σ)
  invFun i := swap none i.1 * i.2.optionCongr
  left_inv σ := by simp
  right_inv := fun ⟨x, σ⟩ =>
    by
    have : remove_none (swap none x * σ.option_congr) = σ :=
      Equiv.optionCongr_injective (by simp [← mul_assoc])
    simp [← perm.eq_inv_iff_eq, this]
#align equiv.perm.decompose_option Equiv.Perm.decomposeOption
-/

#print Equiv.Perm.decomposeOption_symm_of_none_apply /-
theorem Equiv.Perm.decomposeOption_symm_of_none_apply {α : Type _} [DecidableEq α] (e : Perm α)
    (i : Option α) : Equiv.Perm.decomposeOption.symm (none, e) i = i.map e := by simp
#align equiv.perm.decompose_option_symm_of_none_apply Equiv.Perm.decomposeOption_symm_of_none_apply
-/

/- warning: equiv.perm.decompose_option_symm_sign -> Equiv.Perm.decomposeOption_symm_sign is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] [_inst_2 : Fintype.{u1} α] (e : Equiv.Perm.{succ u1} α), Eq.{1} (Units.{0} Int Int.monoid) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.mulOneClass.{0} Int Int.monoid)) (fun (_x : MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.mulOneClass.{0} Int Int.monoid)) => (Equiv.Perm.{succ u1} (Option.{u1} α)) -> (Units.{0} Int Int.monoid)) (MonoidHom.hasCoeToFun.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.mulOneClass.{0} Int Int.monoid)) (Equiv.Perm.sign.{u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => Option.decidableEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (Option.fintype.{u1} α _inst_2)) (coeFn.{succ u1, succ u1} (Equiv.{succ u1, succ u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α))) (fun (_x : Equiv.{succ u1, succ u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α))) => (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) -> (Equiv.Perm.{succ u1} (Option.{u1} α))) (Equiv.hasCoeToFun.{succ u1, succ u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α))) (Equiv.symm.{succ u1, succ u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.decomposeOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b))) (Prod.mk.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α) (Option.none.{u1} α) e))) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.mulOneClass.{0} Int Int.monoid)) (fun (_x : MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.mulOneClass.{0} Int Int.monoid)) => (Equiv.Perm.{succ u1} α) -> (Units.{0} Int Int.monoid)) (MonoidHom.hasCoeToFun.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.monoid) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.mulOneClass.{0} Int Int.monoid)) (Equiv.Perm.sign.{u1} α (fun (a : α) (b : α) => _inst_1 a b) _inst_2) e)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] [_inst_2 : Fintype.{u1} α] (e : Equiv.Perm.{succ u1} α), Eq.{1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Equiv.Perm.{succ u1} (Option.{u1} α)) => Units.{0} Int Int.instMonoidInt) (FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.{succ u1, succ u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α))) (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (fun (a : Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) => Equiv.Perm.{succ u1} (Option.{u1} α)) a) (Equiv.instFunLikeEquiv.{succ u1, succ u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α))) (Equiv.symm.{succ u1, succ u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.decomposeOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b))) (Prod.mk.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α) (Option.none.{u1} α) e))) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (fun (_x : Equiv.Perm.{succ u1} (Option.{u1} α)) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Equiv.Perm.{succ u1} (Option.{u1} α)) => Units.{0} Int Int.instMonoidInt) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (MulOneClass.toMul.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α)))))) (MulOneClass.toMul.{0} (Units.{0} Int Int.instMonoidInt) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt) (MonoidHom.monoidHomClass.{u1, 0} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.Perm.permGroup.{u1} (Option.{u1} α))))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)))) (Equiv.Perm.sign.{u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => instDecidableEqOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (instFintypeOption.{u1} α _inst_2)) (FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.{succ u1, succ u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α))) (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (fun (_x : Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) => Equiv.Perm.{succ u1} (Option.{u1} α)) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α))) (Equiv.symm.{succ u1, succ u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.decomposeOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b))) (Prod.mk.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α) (Option.none.{u1} α) e))) (FunLike.coe.{succ u1, succ u1, 1} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} α) (fun (_x : Equiv.Perm.{succ u1} α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : Equiv.Perm.{succ u1} α) => Units.{0} Int Int.instMonoidInt) _x) (MulHomClass.toFunLike.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (MulOneClass.toMul.{u1} (Equiv.Perm.{succ u1} α) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α))))) (MulOneClass.toMul.{0} (Units.{0} Int Int.instMonoidInt) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (MonoidHomClass.toMulHomClass.{u1, u1, 0} (MonoidHom.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)) (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt) (MonoidHom.monoidHomClass.{u1, 0} (Equiv.Perm.{succ u1} α) (Units.{0} Int Int.instMonoidInt) (Monoid.toMulOneClass.{u1} (Equiv.Perm.{succ u1} α) (DivInvMonoid.toMonoid.{u1} (Equiv.Perm.{succ u1} α) (Group.toDivInvMonoid.{u1} (Equiv.Perm.{succ u1} α) (Equiv.Perm.permGroup.{u1} α)))) (Units.instMulOneClassUnits.{0} Int Int.instMonoidInt)))) (Equiv.Perm.sign.{u1} α (fun (a : α) (b : α) => _inst_1 a b) _inst_2) e)
Case conversion may be inaccurate. Consider using '#align equiv.perm.decompose_option_symm_sign Equiv.Perm.decomposeOption_symm_signₓ'. -/
theorem Equiv.Perm.decomposeOption_symm_sign {α : Type _} [DecidableEq α] [Fintype α] (e : Perm α) :
    Perm.sign (Equiv.Perm.decomposeOption.symm (none, e)) = Perm.sign e := by simp
#align equiv.perm.decompose_option_symm_sign Equiv.Perm.decomposeOption_symm_sign

/- warning: finset.univ_perm_option -> Finset.univ_perm_option is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] [_inst_2 : Fintype.{u1} α], Eq.{succ u1} (Finset.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α))) (Finset.univ.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.fintype.{u1, u1} (Option.{u1} α) (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => Option.decidableEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (fun (a : Option.{u1} α) (b : Option.{u1} α) => Option.decidableEq.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (Option.fintype.{u1} α _inst_2) (Option.fintype.{u1} α _inst_2))) (Finset.map.{u1, u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.toEmbedding.{succ u1, succ u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.symm.{succ u1, succ u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.decomposeOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b)))) (Finset.univ.{u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Prod.fintype.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α) (Option.fintype.{u1} α _inst_2) (Equiv.fintype.{u1, u1} α α (fun (a : α) (b : α) => _inst_1 a b) (fun (a : α) (b : α) => _inst_1 a b) _inst_2 _inst_2))))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} α] [_inst_2 : Fintype.{u1} α], Eq.{succ u1} (Finset.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α))) (Finset.univ.{u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (equivFintype.{u1, u1} (Option.{u1} α) (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => instDecidableEqOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (fun (a : Option.{u1} α) (b : Option.{u1} α) => instDecidableEqOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b) a b) (instFintypeOption.{u1} α _inst_2) (instFintypeOption.{u1} α _inst_2))) (Finset.map.{u1, u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.toEmbedding.{succ u1, succ u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.{succ u1} (Option.{u1} α)) (Equiv.symm.{succ u1, succ u1} (Equiv.Perm.{succ u1} (Option.{u1} α)) (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (Equiv.Perm.decomposeOption.{u1} α (fun (a : α) (b : α) => _inst_1 a b)))) (Finset.univ.{u1} (Prod.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α)) (instFintypeProd.{u1, u1} (Option.{u1} α) (Equiv.Perm.{succ u1} α) (instFintypeOption.{u1} α _inst_2) (equivFintype.{u1, u1} α α (fun (a : α) (b : α) => _inst_1 a b) (fun (a : α) (b : α) => _inst_1 a b) _inst_2 _inst_2))))
Case conversion may be inaccurate. Consider using '#align finset.univ_perm_option Finset.univ_perm_optionₓ'. -/
/-- The set of all permutations of `option α` can be constructed by augmenting the set of
permutations of `α` by each element of `option α` in turn. -/
theorem Finset.univ_perm_option {α : Type _} [DecidableEq α] [Fintype α] :
    @Finset.univ (Perm <| Option α) _ =
      (Finset.univ : Finset <| Option α × Perm α).map Equiv.Perm.decomposeOption.symm.toEmbedding :=
  (Finset.univ_map_equiv_to_embedding _).symm
#align finset.univ_perm_option Finset.univ_perm_option


/-
Copyright (c) 2022 Joël Riou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joël Riou
-/

import category_theory.idempotents.functor_extension
import for_mathlib.functor_misc

/-!
# Extension of functors to the idempotent completion

In this file, we obtain an equivalence of categories
`karoubi_universal₁ C D : (C ⥤ karoubi D) ≌ (karoubi C ⥤ karoubi D)` for
all categories `C` and `D`. The key construction is `functor_extension₁`
which extends a functor `C ⥤ karoubi D` to a functor `karoubi C ⥤ karoubi D`.

TODO : If `D` is idempotent complete, we also have
`karoubi_universal C D : C ⥤ D ≌ karoubi C ⥤ D`.

-/

open category_theory.category
open category_theory.idempotents.karoubi

namespace category_theory

namespace idempotents

variables (C D E : Type*) [category C] [category D] [category E]

namespace karoubi_universal₁

/-
@[simps]
def counit_iso :
  (whiskering_left C (karoubi C) (karoubi D)).obj (to_karoubi C) ⋙
    functor_extension₁ C D ≅ 𝟭 _ :=
nat_iso.of_components (λ G,
{ hom :=
  { app := λ P,
    { f := (G.map (decomp_id_p P)).f,
      comm := begin
        have eq : P.decomp_id_p = (to_karoubi C).map P.p ≫ P.decomp_id_p ≫ 𝟙 _,
        { simp only [P.idem, decomp_id_p_f, to_karoubi_map_f, id_eq, comp_f, hom_ext], },
        simpa only [hom_ext, G.map_comp, G.map_id] using G.congr_map eq,
      end },
      naturality' := λ P Q f, begin
        ext,
        simpa only [hom_ext, G.map_comp] using (G.congr_map (decomp_id_p_naturality f)).symm,
      end },
  inv :=
  { app := λ P,
    { f := (G.map (decomp_id_i P)).f,
      comm := begin
        have eq : P.decomp_id_i = 𝟙 _ ≫ P.decomp_id_i ≫ (to_karoubi C).map P.p,
        { simp only [idem, id_eq, hom_ext, decomp_id_i_f, comp_f, to_karoubi_map_f], },
        simpa only [hom_ext, G.map_comp, G.map_id] using (G.congr_map eq),
      end, },
    naturality' := λ P Q f, begin
      ext,
      simpa only [hom_ext, G.map_comp] using G.congr_map (decomp_id_i_naturality f),
    end },
  hom_inv_id' := begin
    ext P,
    simpa only [hom_ext, G.map_comp, G.map_id] using G.congr_map P.decomp_p.symm,
  end,
  inv_hom_id' := begin
    ext P,
    simpa only [hom_ext, G.map_comp, G.map_id] using G.congr_map P.decomp_id.symm,
  end, })
begin
  intros G G' φ,
  ext P,
  dsimp,
  simp only [nat_trans_eq φ P, comp_f, functor_extension₁.map_app_f,
    functor.comp_map, whisker_left_app],
  rw [P.decomp_p, G.map_comp, comp_f, assoc, assoc],
  refl,
end-/

end karoubi_universal₁

/-@[simps]
def karoubi_universal₁ : (C ⥤ karoubi D) ≌ (karoubi C ⥤ karoubi D) :=
{ functor := functor_extension₁ C D,
  inverse := (whiskering_left C (karoubi C) (karoubi D)).obj (to_karoubi C),
  unit_iso := eq_to_iso (functor_extension₁_comp_whiskering_left_to_karoubi C D).symm,
  counit_iso := karoubi_universal₁.counit_iso C D,
  functor_unit_iso_comp' := λ F, begin
    ext P,
    simpa only [eq_to_iso.hom, eq_to_hom_app, eq_to_hom_map, eq_to_hom_refl, id_comp]
      using F.congr_map P.idem,
  end, }

lemma functor_extension₁_comp
  (F : C ⥤ karoubi D) (G : D ⥤ karoubi E) :
  (functor_extension₁ C E).obj (F ⋙ (functor_extension₁ D E).obj G) =
  (functor_extension₁ C D).obj F ⋙ (functor_extension₁ D E).obj G :=
begin
  apply functor.ext,
  { intros X Y f,
    erw [id_comp, comp_id],
    refl, },
  { intro P,
    ext,
    { dsimp,
      erw [comp_id, id_comp], },
    { refl, }, }
end

@[simps]
def functor_extension₂ : (C ⥤ D) ⥤ (karoubi C ⥤ karoubi D) :=
(whiskering_right C D (karoubi D)).obj (to_karoubi D) ⋙ functor_extension₁ C D

lemma functor_extension₂_comp_whiskering_left_to_karoubi :
  functor_extension₂ C D ⋙ (whiskering_left C (karoubi C) (karoubi D)).obj (to_karoubi C) =
  (whiskering_right C D (karoubi D)).obj (to_karoubi D) :=
by simp only [functor_extension₂, functor.assoc_eq,
  functor_extension₁_comp_whiskering_left_to_karoubi, functor.comp_id]

section is_idempotent_complete

variable [is_idempotent_complete D]

noncomputable instance : is_equivalence (to_karoubi D) := to_karoubi_is_equivalence D

@[simps]
noncomputable def karoubi_universal₂ : (C ⥤ D) ≌
  (karoubi C ⥤ karoubi D) :=
(equivalence.congr_right (to_karoubi D).as_equivalence).trans
    (karoubi_universal₁ C D)

lemma karoubi_universal₂_functor_eq :
  (karoubi_universal₂ C D).functor = functor_extension₂ C D := rfl

noncomputable instance : is_equivalence (functor_extension₂ C D) :=
by { rw ← karoubi_universal₂_functor_eq, apply_instance, }

@[simps]
noncomputable def functor_extension :
  (C ⥤ D) ⥤ (karoubi C ⥤ D) :=
functor_extension₂ C D ⋙ (whiskering_right (karoubi C) (karoubi D) D).obj
    (to_karoubi_is_equivalence D).inverse

@[simps]
noncomputable def karoubi_universal : (C ⥤ D) ≌ (karoubi C ⥤ D) :=
(karoubi_universal₂ C D).trans (equivalence.congr_right (to_karoubi D).as_equivalence.symm)

lemma karoubi_universal_functor_eq :
  (karoubi_universal C D).functor = functor_extension C D := rfl

noncomputable instance : is_equivalence (functor_extension C D) :=
by { rw ← karoubi_universal_functor_eq, apply_instance, }

noncomputable instance : is_equivalence ((whiskering_left C (karoubi C) D).obj (to_karoubi C)) :=
begin
  let F₁ := ((whiskering_left C (karoubi C) D).obj (to_karoubi C)),
  let F₂ := ((whiskering_right C _ _).obj (to_karoubi D) ⋙
    (whiskering_right C _ _).obj (to_karoubi D).inv),
  apply is_equivalence.cancel_comp_right F₁ F₂,
  { exact is_equivalence.of_equivalence
      (@equivalence.congr_right _ _ _ _ C _
      ((to_karoubi D).as_equivalence.trans (to_karoubi D).as_equivalence.symm)), },
  { rw [show F₁ ⋙ F₂ = (karoubi_universal C D).inverse, by refl],
    apply_instance, }
end

end is_idempotent_complete-/

end idempotents

end category_theory