/-
Copyright (c) 2019 Amelia Livingston. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Amelia Livingston, Bryan Gin-ge Chen, Patrick Massot

! This file was ported from Lean 3 source module data.setoid.partition
! leanprover-community/mathlib commit 50832daea47b195a48b5b33b1c8b2162c48c3afc
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Fintype.Basic
import Mathbin.Data.Set.Finite
import Mathbin.Data.Setoid.Basic
import Mathbin.Order.Partition.Finpartition

/-!
# Equivalence relations: partitions

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

This file comprises properties of equivalence relations viewed as partitions.
There are two implementations of partitions here:
* A collection `c : set (set α)` of sets is a partition of `α` if `∅ ∉ c` and each element `a : α`
  belongs to a unique set `b ∈ c`. This is expressed as `is_partition c`
* An indexed partition is a map `s : ι → α` whose image is a partition. This is
  expressed as `indexed_partition s`.

Of course both implementations are related to `quotient` and `setoid`.

`setoid.is_partition.partition` and `finpartition.is_partition_parts` furnish
a link between `setoid.is_partition` and `finpartition`.

## TODO

Could the design of `finpartition` inform the one of `setoid.is_partition`? Maybe bundling it and
changing it from `set (set α)` to `set α` where `[lattice α] [order_bot α]` would make it more
usable.

## Tags

setoid, equivalence, iseqv, relation, equivalence relation, partition, equivalence class
-/

namespace Setoid

variable {α : Type _}

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (b «expr ∈ » c) -/
#print Setoid.eq_of_mem_eqv_class /-
/-- If x ∈ α is in 2 elements of a set of sets partitioning α, those 2 sets are equal. -/
theorem eq_of_mem_eqv_class {c : Set (Set α)} (H : ∀ a, ∃! (b : _)(_ : b ∈ c), a ∈ b) {x b b'}
    (hc : b ∈ c) (hb : x ∈ b) (hc' : b' ∈ c) (hb' : x ∈ b') : b = b' :=
  (H x).unique₂ hc hb hc' hb'
#align setoid.eq_of_mem_eqv_class Setoid.eq_of_mem_eqv_class
-/

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (b «expr ∈ » c) -/
#print Setoid.mkClasses /-
/-- Makes an equivalence relation from a set of sets partitioning α. -/
def mkClasses (c : Set (Set α)) (H : ∀ a, ∃! (b : _)(_ : b ∈ c), a ∈ b) : Setoid α :=
  ⟨fun x y => ∀ s ∈ c, x ∈ s → y ∈ s,
    ⟨fun _ _ _ hx => hx, fun x y h s hs hy =>
      (H x).elim₂ fun t ht hx _ =>
        have : s = t := eq_of_mem_eqv_class H hs hy ht (h t ht hx)
        this.symm ▸ hx,
      fun x y z h1 h2 s hs hx =>
      (H y).elim₂ fun t ht hy _ =>
        (H z).elim₂ fun t' ht' hz _ =>
          have hst : s = t := eq_of_mem_eqv_class H hs (h1 _ hs hx) ht hy
          have htt' : t = t' := eq_of_mem_eqv_class H ht (h2 _ ht hy) ht' hz
          (hst.trans htt').symm ▸ hz⟩⟩
#align setoid.mk_classes Setoid.mkClasses
-/

#print Setoid.classes /-
/-- Makes the equivalence classes of an equivalence relation. -/
def classes (r : Setoid α) : Set (Set α) :=
  { s | ∃ y, s = { x | r.Rel x y } }
#align setoid.classes Setoid.classes
-/

#print Setoid.mem_classes /-
theorem mem_classes (r : Setoid α) (y) : { x | r.Rel x y } ∈ r.classes :=
  ⟨y, rfl⟩
#align setoid.mem_classes Setoid.mem_classes
-/

#print Setoid.classes_ker_subset_fiber_set /-
theorem classes_ker_subset_fiber_set {β : Type _} (f : α → β) :
    (Setoid.ker f).classes ⊆ Set.range fun y => { x | f x = y } :=
  by
  rintro s ⟨x, rfl⟩
  rw [Set.mem_range]
  exact ⟨f x, rfl⟩
#align setoid.classes_ker_subset_fiber_set Setoid.classes_ker_subset_fiber_set
-/

/- warning: setoid.finite_classes_ker -> Setoid.finite_classes_ker is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Finite.{succ u2} β] (f : α -> β), Set.Finite.{u1} (Set.{u1} α) (Setoid.classes.{u1} α (Setoid.ker.{u1, u2} α β f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Finite.{succ u1} β] (f : α -> β), Set.Finite.{u2} (Set.{u2} α) (Setoid.classes.{u2} α (Setoid.ker.{u2, u1} α β f))
Case conversion may be inaccurate. Consider using '#align setoid.finite_classes_ker Setoid.finite_classes_kerₓ'. -/
theorem finite_classes_ker {α β : Type _} [Finite β] (f : α → β) : (Setoid.ker f).classes.Finite :=
  (Set.finite_range _).Subset <| classes_ker_subset_fiber_set f
#align setoid.finite_classes_ker Setoid.finite_classes_ker

/- warning: setoid.card_classes_ker_le -> Setoid.card_classes_ker_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Fintype.{u2} β] (f : α -> β) [_inst_2 : Fintype.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} (Set.{u1} α)) Type.{u1} (Set.hasCoeToSort.{u1} (Set.{u1} α)) (Setoid.classes.{u1} α (Setoid.ker.{u1, u2} α β f)))], LE.le.{0} Nat Nat.hasLe (Fintype.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} (Set.{u1} α)) Type.{u1} (Set.hasCoeToSort.{u1} (Set.{u1} α)) (Setoid.classes.{u1} α (Setoid.ker.{u1, u2} α β f))) _inst_2) (Fintype.card.{u2} β _inst_1)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Fintype.{u1} β] (f : α -> β) [_inst_2 : Fintype.{u2} (Set.Elem.{u2} (Set.{u2} α) (Setoid.classes.{u2} α (Setoid.ker.{u2, u1} α β f)))], LE.le.{0} Nat instLENat (Fintype.card.{u2} (Set.Elem.{u2} (Set.{u2} α) (Setoid.classes.{u2} α (Setoid.ker.{u2, u1} α β f))) _inst_2) (Fintype.card.{u1} β _inst_1)
Case conversion may be inaccurate. Consider using '#align setoid.card_classes_ker_le Setoid.card_classes_ker_leₓ'. -/
theorem card_classes_ker_le {α β : Type _} [Fintype β] (f : α → β)
    [Fintype (Setoid.ker f).classes] : Fintype.card (Setoid.ker f).classes ≤ Fintype.card β := by
  classical exact
      le_trans (Set.card_le_of_subset (classes_ker_subset_fiber_set f)) (Fintype.card_range_le _)
#align setoid.card_classes_ker_le Setoid.card_classes_ker_le

#print Setoid.eq_iff_classes_eq /-
/-- Two equivalence relations are equal iff all their equivalence classes are equal. -/
theorem eq_iff_classes_eq {r₁ r₂ : Setoid α} :
    r₁ = r₂ ↔ ∀ x, { y | r₁.Rel x y } = { y | r₂.Rel x y } :=
  ⟨fun h x => h ▸ rfl, fun h => ext' fun x => Set.ext_iff.1 <| h x⟩
#align setoid.eq_iff_classes_eq Setoid.eq_iff_classes_eq
-/

/- warning: setoid.rel_iff_exists_classes -> Setoid.rel_iff_exists_classes is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (r : Setoid.{succ u1} α) {x : α} {y : α}, Iff (Setoid.Rel.{u1} α r x y) (Exists.{succ u1} (Set.{u1} α) (fun (c : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) c (Setoid.classes.{u1} α r)) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) c (Setoid.classes.{u1} α r)) => And (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x c) (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) y c))))
but is expected to have type
  forall {α : Type.{u1}} (r : Setoid.{succ u1} α) {x : α} {y : α}, Iff (Setoid.Rel.{u1} α r x y) (Exists.{succ u1} (Set.{u1} α) (fun (c : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.instMembershipSet.{u1} (Set.{u1} α)) c (Setoid.classes.{u1} α r)) (And (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x c) (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) y c))))
Case conversion may be inaccurate. Consider using '#align setoid.rel_iff_exists_classes Setoid.rel_iff_exists_classesₓ'. -/
theorem rel_iff_exists_classes (r : Setoid α) {x y} : r.Rel x y ↔ ∃ c ∈ r.classes, x ∈ c ∧ y ∈ c :=
  ⟨fun h => ⟨_, r.mem_classes y, h, r.refl' y⟩, fun ⟨c, ⟨z, hz⟩, hx, hy⟩ =>
    by
    subst c
    exact r.trans' hx (r.symm' hy)⟩
#align setoid.rel_iff_exists_classes Setoid.rel_iff_exists_classes

#print Setoid.classes_inj /-
/-- Two equivalence relations are equal iff their equivalence classes are equal. -/
theorem classes_inj {r₁ r₂ : Setoid α} : r₁ = r₂ ↔ r₁.classes = r₂.classes :=
  ⟨fun h => h ▸ rfl, fun h => ext' fun a b => by simp only [rel_iff_exists_classes, exists_prop, h]⟩
#align setoid.classes_inj Setoid.classes_inj
-/

#print Setoid.empty_not_mem_classes /-
/-- The empty set is not an equivalence class. -/
theorem empty_not_mem_classes {r : Setoid α} : ∅ ∉ r.classes := fun ⟨y, hy⟩ =>
  Set.not_mem_empty y <| hy.symm ▸ r.refl' y
#align setoid.empty_not_mem_classes Setoid.empty_not_mem_classes
-/

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (b «expr ∈ » r.classes) -/
#print Setoid.classes_eqv_classes /-
/-- Equivalence classes partition the type. -/
theorem classes_eqv_classes {r : Setoid α} (a) : ∃! (b : _)(_ : b ∈ r.classes), a ∈ b :=
  ExistsUnique.intro₂ { x | r.Rel x a } (r.mem_classes a) (r.refl' _) <|
    by
    rintro _ ⟨y, rfl⟩ ha
    ext x
    exact ⟨fun hx => r.trans' hx (r.symm' ha), fun hx => r.trans' hx ha⟩
#align setoid.classes_eqv_classes Setoid.classes_eqv_classes
-/

#print Setoid.eq_of_mem_classes /-
/-- If x ∈ α is in 2 equivalence classes, the equivalence classes are equal. -/
theorem eq_of_mem_classes {r : Setoid α} {x b} (hc : b ∈ r.classes) (hb : x ∈ b) {b'}
    (hc' : b' ∈ r.classes) (hb' : x ∈ b') : b = b' :=
  eq_of_mem_eqv_class classes_eqv_classes hc hb hc' hb'
#align setoid.eq_of_mem_classes Setoid.eq_of_mem_classes
-/

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (b «expr ∈ » c) -/
#print Setoid.eq_eqv_class_of_mem /-
/-- The elements of a set of sets partitioning α are the equivalence classes of the
    equivalence relation defined by the set of sets. -/
theorem eq_eqv_class_of_mem {c : Set (Set α)} (H : ∀ a, ∃! (b : _)(_ : b ∈ c), a ∈ b) {s y}
    (hs : s ∈ c) (hy : y ∈ s) : s = { x | (mkClasses c H).Rel x y } :=
  Set.ext fun x =>
    ⟨fun hs' => symm' (mkClasses c H) fun b' hb' h' => eq_of_mem_eqv_class H hs hy hb' h' ▸ hs',
      fun hx =>
      (H x).elim₂ fun b' hc' hb' h' =>
        (eq_of_mem_eqv_class H hs hy hc' <| hx b' hc' hb').symm ▸ hb'⟩
#align setoid.eq_eqv_class_of_mem Setoid.eq_eqv_class_of_mem
-/

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (b «expr ∈ » c) -/
#print Setoid.eqv_class_mem /-
/-- The equivalence classes of the equivalence relation defined by a set of sets
    partitioning α are elements of the set of sets. -/
theorem eqv_class_mem {c : Set (Set α)} (H : ∀ a, ∃! (b : _)(_ : b ∈ c), a ∈ b) {y} :
    { x | (mkClasses c H).Rel x y } ∈ c :=
  (H y).elim₂ fun b hc hy hb => eq_eqv_class_of_mem H hc hy ▸ hc
#align setoid.eqv_class_mem Setoid.eqv_class_mem
-/

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (b «expr ∈ » c) -/
#print Setoid.eqv_class_mem' /-
theorem eqv_class_mem' {c : Set (Set α)} (H : ∀ a, ∃! (b : _)(_ : b ∈ c), a ∈ b) {x} :
    { y : α | (mkClasses c H).Rel x y } ∈ c :=
  by
  convert Setoid.eqv_class_mem H
  ext
  rw [Setoid.comm']
#align setoid.eqv_class_mem' Setoid.eqv_class_mem'
-/

/- warning: setoid.eqv_classes_disjoint -> Setoid.eqv_classes_disjoint is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {c : Set.{u1} (Set.{u1} α)}, (forall (a : α), ExistsUnique.{succ u1} (Set.{u1} α) (fun (b : Set.{u1} α) => ExistsUnique.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) b c) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) b c) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a b))) -> (Set.PairwiseDisjoint.{u1, u1} (Set.{u1} α) (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} α) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α))) c (id.{succ u1} (Set.{u1} α)))
but is expected to have type
  forall {α : Type.{u1}} {c : Set.{u1} (Set.{u1} α)}, (forall (a : α), ExistsUnique.{succ u1} (Set.{u1} α) (fun (b : Set.{u1} α) => ExistsUnique.{0} (Membership.mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.instMembershipSet.{u1} (Set.{u1} α)) b c) (fun (H : Membership.mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.instMembershipSet.{u1} (Set.{u1} α)) b c) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a b))) -> (Set.PairwiseDisjoint.{u1, u1} (Set.{u1} α) (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} α) (Preorder.toLE.{u1} (Set.{u1} α) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) c (id.{succ u1} (Set.{u1} α)))
Case conversion may be inaccurate. Consider using '#align setoid.eqv_classes_disjoint Setoid.eqv_classes_disjointₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (b «expr ∈ » c) -/
/-- Distinct elements of a set of sets partitioning α are disjoint. -/
theorem eqv_classes_disjoint {c : Set (Set α)} (H : ∀ a, ∃! (b : _)(_ : b ∈ c), a ∈ b) :
    c.PairwiseDisjoint id := fun b₁ h₁ b₂ h₂ h =>
  Set.disjoint_left.2 fun x hx1 hx2 =>
    (H x).elim₂ fun b hc hx hb => h <| eq_of_mem_eqv_class H h₁ hx1 h₂ hx2
#align setoid.eqv_classes_disjoint Setoid.eqv_classes_disjoint

/- warning: setoid.eqv_classes_of_disjoint_union -> Setoid.eqv_classes_of_disjoint_union is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {c : Set.{u1} (Set.{u1} α)}, (Eq.{succ u1} (Set.{u1} α) (Set.unionₛ.{u1} α c) (Set.univ.{u1} α)) -> (Set.PairwiseDisjoint.{u1, u1} (Set.{u1} α) (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} α) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α))) c (id.{succ u1} (Set.{u1} α))) -> (forall (a : α), ExistsUnique.{succ u1} (Set.{u1} α) (fun (b : Set.{u1} α) => ExistsUnique.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) b c) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) b c) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a b)))
but is expected to have type
  forall {α : Type.{u1}} {c : Set.{u1} (Set.{u1} α)}, (Eq.{succ u1} (Set.{u1} α) (Set.unionₛ.{u1} α c) (Set.univ.{u1} α)) -> (Set.PairwiseDisjoint.{u1, u1} (Set.{u1} α) (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} α) (Preorder.toLE.{u1} (Set.{u1} α) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) c (id.{succ u1} (Set.{u1} α))) -> (forall (a : α), ExistsUnique.{succ u1} (Set.{u1} α) (fun (b : Set.{u1} α) => ExistsUnique.{0} (Membership.mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.instMembershipSet.{u1} (Set.{u1} α)) b c) (fun (H : Membership.mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.instMembershipSet.{u1} (Set.{u1} α)) b c) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a b)))
Case conversion may be inaccurate. Consider using '#align setoid.eqv_classes_of_disjoint_union Setoid.eqv_classes_of_disjoint_unionₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (b «expr ∈ » c) -/
/-- A set of disjoint sets covering α partition α (classical). -/
theorem eqv_classes_of_disjoint_union {c : Set (Set α)} (hu : Set.unionₛ c = @Set.univ α)
    (H : c.PairwiseDisjoint id) (a) : ∃! (b : _)(_ : b ∈ c), a ∈ b :=
  let ⟨b, hc, ha⟩ := Set.mem_unionₛ.1 <| show a ∈ _ by rw [hu] <;> exact Set.mem_univ a
  ExistsUnique.intro₂ b hc ha fun b' hc' ha' => H.elim_set hc' hc a ha' ha
#align setoid.eqv_classes_of_disjoint_union Setoid.eqv_classes_of_disjoint_union

/- warning: setoid.setoid_of_disjoint_union -> Setoid.setoidOfDisjointUnion is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {c : Set.{u1} (Set.{u1} α)}, (Eq.{succ u1} (Set.{u1} α) (Set.unionₛ.{u1} α c) (Set.univ.{u1} α)) -> (Set.PairwiseDisjoint.{u1, u1} (Set.{u1} α) (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} α) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α))) c (id.{succ u1} (Set.{u1} α))) -> (Setoid.{succ u1} α)
but is expected to have type
  forall {α : Type.{u1}} {c : Set.{u1} (Set.{u1} α)}, (Eq.{succ u1} (Set.{u1} α) (Set.unionₛ.{u1} α c) (Set.univ.{u1} α)) -> (Set.PairwiseDisjoint.{u1, u1} (Set.{u1} α) (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} α) (Preorder.toLE.{u1} (Set.{u1} α) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) c (id.{succ u1} (Set.{u1} α))) -> (Setoid.{succ u1} α)
Case conversion may be inaccurate. Consider using '#align setoid.setoid_of_disjoint_union Setoid.setoidOfDisjointUnionₓ'. -/
/-- Makes an equivalence relation from a set of disjoints sets covering α. -/
def setoidOfDisjointUnion {c : Set (Set α)} (hu : Set.unionₛ c = @Set.univ α)
    (H : c.PairwiseDisjoint id) : Setoid α :=
  Setoid.mkClasses c <| eqv_classes_of_disjoint_union hu H
#align setoid.setoid_of_disjoint_union Setoid.setoidOfDisjointUnion

#print Setoid.mkClasses_classes /-
/-- The equivalence relation made from the equivalence classes of an equivalence
    relation r equals r. -/
theorem mkClasses_classes (r : Setoid α) : mkClasses r.classes classes_eqv_classes = r :=
  ext' fun x y =>
    ⟨fun h => r.symm' (h { z | r.Rel z x } (r.mem_classes x) <| r.refl' x), fun h b hb hx =>
      eq_of_mem_classes (r.mem_classes x) (r.refl' x) hb hx ▸ r.symm' h⟩
#align setoid.mk_classes_classes Setoid.mkClasses_classes
-/

#print Setoid.unionₛ_classes /-
@[simp]
theorem unionₛ_classes (r : Setoid α) : ⋃₀ r.classes = Set.univ :=
  Set.eq_univ_of_forall fun x => Set.mem_unionₛ.2 ⟨{ y | r.Rel y x }, ⟨x, rfl⟩, Setoid.refl _⟩
#align setoid.sUnion_classes Setoid.unionₛ_classes
-/

section Partition

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (b «expr ∈ » c) -/
#print Setoid.IsPartition /-
/-- A collection `c : set (set α)` of sets is a partition of `α` into pairwise
disjoint sets if `∅ ∉ c` and each element `a : α` belongs to a unique set `b ∈ c`. -/
def IsPartition (c : Set (Set α)) :=
  ∅ ∉ c ∧ ∀ a, ∃! (b : _)(_ : b ∈ c), a ∈ b
#align setoid.is_partition Setoid.IsPartition
-/

#print Setoid.nonempty_of_mem_partition /-
/-- A partition of `α` does not contain the empty set. -/
theorem nonempty_of_mem_partition {c : Set (Set α)} (hc : IsPartition c) {s} (h : s ∈ c) :
    s.Nonempty :=
  Set.nonempty_iff_ne_empty.2 fun hs0 => hc.1 <| hs0 ▸ h
#align setoid.nonempty_of_mem_partition Setoid.nonempty_of_mem_partition
-/

#print Setoid.isPartition_classes /-
theorem isPartition_classes (r : Setoid α) : IsPartition r.classes :=
  ⟨empty_not_mem_classes, classes_eqv_classes⟩
#align setoid.is_partition_classes Setoid.isPartition_classes
-/

/- warning: setoid.is_partition.pairwise_disjoint -> Setoid.IsPartition.pairwiseDisjoint is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {c : Set.{u1} (Set.{u1} α)}, (Setoid.IsPartition.{u1} α c) -> (Set.PairwiseDisjoint.{u1, u1} (Set.{u1} α) (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} α) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α))) c (id.{succ u1} (Set.{u1} α)))
but is expected to have type
  forall {α : Type.{u1}} {c : Set.{u1} (Set.{u1} α)}, (Setoid.IsPartition.{u1} α c) -> (Set.PairwiseDisjoint.{u1, u1} (Set.{u1} α) (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} α) (Preorder.toLE.{u1} (Set.{u1} α) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) c (id.{succ u1} (Set.{u1} α)))
Case conversion may be inaccurate. Consider using '#align setoid.is_partition.pairwise_disjoint Setoid.IsPartition.pairwiseDisjointₓ'. -/
theorem IsPartition.pairwiseDisjoint {c : Set (Set α)} (hc : IsPartition c) :
    c.PairwiseDisjoint id :=
  eqv_classes_disjoint hc.2
#align setoid.is_partition.pairwise_disjoint Setoid.IsPartition.pairwiseDisjoint

#print Setoid.IsPartition.unionₛ_eq_univ /-
theorem IsPartition.unionₛ_eq_univ {c : Set (Set α)} (hc : IsPartition c) : ⋃₀ c = Set.univ :=
  Set.eq_univ_of_forall fun x =>
    Set.mem_unionₛ.2 <|
      let ⟨t, ht⟩ := hc.2 x
      ⟨t, by
        simp only [exists_unique_iff_exists] at ht
        tauto⟩
#align setoid.is_partition.sUnion_eq_univ Setoid.IsPartition.unionₛ_eq_univ
-/

#print Setoid.exists_of_mem_partition /-
/-- All elements of a partition of α are the equivalence class of some y ∈ α. -/
theorem exists_of_mem_partition {c : Set (Set α)} (hc : IsPartition c) {s} (hs : s ∈ c) :
    ∃ y, s = { x | (mkClasses c hc.2).Rel x y } :=
  let ⟨y, hy⟩ := nonempty_of_mem_partition hc hs
  ⟨y, eq_eqv_class_of_mem hc.2 hs hy⟩
#align setoid.exists_of_mem_partition Setoid.exists_of_mem_partition
-/

#print Setoid.classes_mkClasses /-
/-- The equivalence classes of the equivalence relation defined by a partition of α equal
    the original partition. -/
theorem classes_mkClasses (c : Set (Set α)) (hc : IsPartition c) : (mkClasses c hc.2).classes = c :=
  Set.ext fun s =>
    ⟨fun ⟨y, hs⟩ =>
      (hc.2 y).elim₂ fun b hm hb hy => by
        rwa [show s = b from
            hs.symm ▸
              Set.ext fun x =>
                ⟨fun hx => symm' (mk_classes c hc.2) hx b hm hb, fun hx b' hc' hx' =>
                  eq_of_mem_eqv_class hc.2 hm hx hc' hx' ▸ hb⟩],
      exists_of_mem_partition hc⟩
#align setoid.classes_mk_classes Setoid.classes_mkClasses
-/

#print Setoid.Partition.le /-
/-- Defining `≤` on partitions as the `≤` defined on their induced equivalence relations. -/
instance Partition.le : LE (Subtype (@IsPartition α)) :=
  ⟨fun x y => mkClasses x.1 x.2.2 ≤ mkClasses y.1 y.2.2⟩
#align setoid.partition.le Setoid.Partition.le
-/

#print Setoid.Partition.partialOrder /-
/-- Defining a partial order on partitions as the partial order on their induced
    equivalence relations. -/
instance Partition.partialOrder : PartialOrder (Subtype (@IsPartition α))
    where
  le := (· ≤ ·)
  lt x y := x ≤ y ∧ ¬y ≤ x
  le_refl _ := @le_refl (Setoid α) _ _
  le_trans _ _ _ := @le_trans (Setoid α) _ _ _ _
  lt_iff_le_not_le _ _ := Iff.rfl
  le_antisymm x y hx hy := by
    let h := @le_antisymm (Setoid α) _ _ _ hx hy
    rw [Subtype.ext_iff_val, ← classes_mk_classes x.1 x.2, ← classes_mk_classes y.1 y.2, h]
#align setoid.partition.partial_order Setoid.Partition.partialOrder
-/

variable (α)

#print Setoid.Partition.orderIso /-
/-- The order-preserving bijection between equivalence relations on a type `α`, and
  partitions of `α` into subsets. -/
protected def Partition.orderIso : Setoid α ≃o { C : Set (Set α) // IsPartition C }
    where
  toFun r := ⟨r.classes, empty_not_mem_classes, classes_eqv_classes⟩
  invFun C := mkClasses C.1 C.2.2
  left_inv := mkClasses_classes
  right_inv C := by rw [Subtype.ext_iff_val, ← classes_mk_classes C.1 C.2]
  map_rel_iff' r s :=
    by
    conv_rhs => rw [← mk_classes_classes r, ← mk_classes_classes s]
    rfl
#align setoid.partition.order_iso Setoid.Partition.orderIso
-/

variable {α}

#print Setoid.Partition.completeLattice /-
/-- A complete lattice instance for partitions; there is more infrastructure for the
    equivalent complete lattice on equivalence relations. -/
instance Partition.completeLattice : CompleteLattice (Subtype (@IsPartition α)) :=
  GaloisInsertion.liftCompleteLattice <|
    @OrderIso.toGaloisInsertion _ (Subtype (@IsPartition α)) _ (PartialOrder.toPreorder _) <|
      Partition.orderIso α
#align setoid.partition.complete_lattice Setoid.Partition.completeLattice
-/

end Partition

/- warning: setoid.is_partition.finpartition -> Setoid.IsPartition.finpartition is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {c : Finset.{u1} (Set.{u1} α)}, (Setoid.IsPartition.{u1} α ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Finset.{u1} (Set.{u1} α)) (Set.{u1} (Set.{u1} α)) (HasLiftT.mk.{succ u1, succ u1} (Finset.{u1} (Set.{u1} α)) (Set.{u1} (Set.{u1} α)) (CoeTCₓ.coe.{succ u1, succ u1} (Finset.{u1} (Set.{u1} α)) (Set.{u1} (Set.{u1} α)) (Finset.Set.hasCoeT.{u1} (Set.{u1} α)))) c)) -> (Finpartition.{u1} (Set.{u1} α) (CompleteLattice.toLattice.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} α) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α))) (Set.univ.{u1} α))
but is expected to have type
  forall {α : Type.{u1}} {c : Finset.{u1} (Set.{u1} α)}, (Setoid.IsPartition.{u1} α (Finset.toSet.{u1} (Set.{u1} α) c)) -> (Finpartition.{u1} (Set.{u1} α) (CompleteLattice.toLattice.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} α) (Preorder.toLE.{u1} (Set.{u1} α) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (SemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (Lattice.toSemilatticeInf.{u1} (Set.{u1} α) (CompleteLattice.toLattice.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α))))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) (Set.univ.{u1} α))
Case conversion may be inaccurate. Consider using '#align setoid.is_partition.finpartition Setoid.IsPartition.finpartitionₓ'. -/
/-- A finite setoid partition furnishes a finpartition -/
@[simps]
def IsPartition.finpartition {c : Finset (Set α)} (hc : Setoid.IsPartition (c : Set (Set α))) :
    Finpartition (Set.univ : Set α) where
  parts := c
  SupIndep := Finset.supIndep_iff_pairwiseDisjoint.mpr <| eqv_classes_disjoint hc.2
  supParts := c.sup_id_set_eq_unionₛ.trans hc.unionₛ_eq_univ
  not_bot_mem := hc.left
#align setoid.is_partition.finpartition Setoid.IsPartition.finpartition

end Setoid

/- warning: finpartition.is_partition_parts -> Finpartition.isPartition_parts is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (f : Finpartition.{u1} (Set.{u1} α) (CompleteLattice.toLattice.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} α) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α))) (Set.univ.{u1} α)), Setoid.IsPartition.{u1} α ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Finset.{u1} (Set.{u1} α)) (Set.{u1} (Set.{u1} α)) (HasLiftT.mk.{succ u1, succ u1} (Finset.{u1} (Set.{u1} α)) (Set.{u1} (Set.{u1} α)) (CoeTCₓ.coe.{succ u1, succ u1} (Finset.{u1} (Set.{u1} α)) (Set.{u1} (Set.{u1} α)) (Finset.Set.hasCoeT.{u1} (Set.{u1} α)))) (Finpartition.parts.{u1} (Set.{u1} α) (CompleteLattice.toLattice.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.completeBooleanAlgebra.{u1} α))))) (GeneralizedBooleanAlgebra.toOrderBot.{u1} (Set.{u1} α) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α))) (Set.univ.{u1} α) f))
but is expected to have type
  forall {α : Type.{u1}} (f : Finpartition.{u1} (Set.{u1} α) (CompleteLattice.toLattice.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} α) (Preorder.toLE.{u1} (Set.{u1} α) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (SemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (Lattice.toSemilatticeInf.{u1} (Set.{u1} α) (CompleteLattice.toLattice.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α))))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) (Set.univ.{u1} α)), Setoid.IsPartition.{u1} α (Finset.toSet.{u1} (Set.{u1} α) (Finpartition.parts.{u1} (Set.{u1} α) (CompleteLattice.toLattice.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} α) (Preorder.toLE.{u1} (Set.{u1} α) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (SemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (Lattice.toSemilatticeInf.{u1} (Set.{u1} α) (CompleteLattice.toLattice.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α))))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) (Set.univ.{u1} α) f))
Case conversion may be inaccurate. Consider using '#align finpartition.is_partition_parts Finpartition.isPartition_partsₓ'. -/
/-- A finpartition gives rise to a setoid partition -/
theorem Finpartition.isPartition_parts {α} (f : Finpartition (Set.univ : Set α)) :
    Setoid.IsPartition (f.parts : Set (Set α)) :=
  ⟨f.not_bot_mem,
    Setoid.eqv_classes_of_disjoint_union (f.parts.sup_id_set_eq_unionₛ.symm.trans f.supParts)
      f.SupIndep.PairwiseDisjoint⟩
#align finpartition.is_partition_parts Finpartition.isPartition_parts

#print IndexedPartition /-
/-- Constructive information associated with a partition of a type `α` indexed by another type `ι`,
`s : ι → set α`.

`indexed_partition.index` sends an element to its index, while `indexed_partition.some` sends
an index to an element of the corresponding set.

This type is primarily useful for definitional control of `s` - if this is not needed, then
`setoid.ker index` by itself may be sufficient. -/
structure IndexedPartition {ι α : Type _} (s : ι → Set α) where
  eq_of_mem : ∀ {x i j}, x ∈ s i → x ∈ s j → i = j
  some : ι → α
  some_mem : ∀ i, some i ∈ s i
  index : α → ι
  mem_index : ∀ x, x ∈ s (index x)
#align indexed_partition IndexedPartition
-/

/- warning: indexed_partition.mk' -> IndexedPartition.mk' is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} (s : ι -> (Set.{u2} α)), (forall (i : ι) (j : ι), (Ne.{succ u1} ι i j) -> (Disjoint.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.completeBooleanAlgebra.{u2} α)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u2} (Set.{u2} α) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u2} (Set.{u2} α) (Set.booleanAlgebra.{u2} α))) (s i) (s j))) -> (forall (i : ι), Set.Nonempty.{u2} α (s i)) -> (forall (x : α), Exists.{succ u1} ι (fun (i : ι) => Membership.Mem.{u2, u2} α (Set.{u2} α) (Set.hasMem.{u2} α) x (s i))) -> (IndexedPartition.{u1, u2} ι α s)
but is expected to have type
  forall {ι : Type.{u1}} {α : Type.{u2}} (s : ι -> (Set.{u2} α)), (forall (i : ι) (j : ι), (Ne.{succ u1} ι i j) -> (Disjoint.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.instCompleteBooleanAlgebraSet.{u2} α)))))) (BoundedOrder.toOrderBot.{u2} (Set.{u2} α) (Preorder.toLE.{u2} (Set.{u2} α) (PartialOrder.toPreorder.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.instCompleteBooleanAlgebraSet.{u2} α)))))))) (CompleteLattice.toBoundedOrder.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.instCompleteBooleanAlgebraSet.{u2} α)))))) (s i) (s j))) -> (forall (i : ι), Set.Nonempty.{u2} α (s i)) -> (forall (x : α), Exists.{succ u1} ι (fun (i : ι) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (s i))) -> (IndexedPartition.{u1, u2} ι α s)
Case conversion may be inaccurate. Consider using '#align indexed_partition.mk' IndexedPartition.mk'ₓ'. -/
/-- The non-constructive constructor for `indexed_partition`. -/
noncomputable def IndexedPartition.mk' {ι α : Type _} (s : ι → Set α)
    (dis : ∀ i j, i ≠ j → Disjoint (s i) (s j)) (nonempty : ∀ i, (s i).Nonempty)
    (ex : ∀ x, ∃ i, x ∈ s i) : IndexedPartition s
    where
  eq_of_mem x i j hxi hxj := by_contradiction fun h => (dis _ _ h).le_bot ⟨hxi, hxj⟩
  some i := (Nonempty i).some
  some_mem i := (Nonempty i).choose_spec
  index x := (ex x).some
  mem_index x := (ex x).choose_spec
#align indexed_partition.mk' IndexedPartition.mk'

namespace IndexedPartition

open Set

variable {ι α : Type _} {s : ι → Set α} (hs : IndexedPartition s)

/-- On a unique index set there is the obvious trivial partition -/
instance [Unique ι] [Inhabited α] : Inhabited (IndexedPartition fun i : ι => (Set.univ : Set α)) :=
  ⟨{  eq_of_mem := fun x i j hi hj => Subsingleton.elim _ _
      some := default
      some_mem := Set.mem_univ
      index := default
      mem_index := Set.mem_univ }⟩

attribute [simp] some_mem mem_index

include hs

/- warning: indexed_partition.exists_mem -> IndexedPartition.exists_mem is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} {s : ι -> (Set.{u2} α)}, (IndexedPartition.{u1, u2} ι α s) -> (forall (x : α), Exists.{succ u1} ι (fun (i : ι) => Membership.Mem.{u2, u2} α (Set.{u2} α) (Set.hasMem.{u2} α) x (s i)))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} {s : ι -> (Set.{u1} α)}, (IndexedPartition.{u2, u1} ι α s) -> (forall (x : α), Exists.{succ u2} ι (fun (i : ι) => Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) x (s i)))
Case conversion may be inaccurate. Consider using '#align indexed_partition.exists_mem IndexedPartition.exists_memₓ'. -/
theorem exists_mem (x : α) : ∃ i, x ∈ s i :=
  ⟨hs.index x, hs.mem_index x⟩
#align indexed_partition.exists_mem IndexedPartition.exists_mem

#print IndexedPartition.unionᵢ /-
theorem unionᵢ : (⋃ i, s i) = univ := by
  ext x
  simp [hs.exists_mem x]
#align indexed_partition.Union IndexedPartition.unionᵢ
-/

/- warning: indexed_partition.disjoint -> IndexedPartition.disjoint is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} {s : ι -> (Set.{u2} α)}, (IndexedPartition.{u1, u2} ι α s) -> (forall {i : ι} {j : ι}, (Ne.{succ u1} ι i j) -> (Disjoint.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.completeBooleanAlgebra.{u2} α)))))) (GeneralizedBooleanAlgebra.toOrderBot.{u2} (Set.{u2} α) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u2} (Set.{u2} α) (Set.booleanAlgebra.{u2} α))) (s i) (s j)))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} {s : ι -> (Set.{u1} α)}, (IndexedPartition.{u2, u1} ι α s) -> (forall {i : ι} {j : ι}, (Ne.{succ u2} ι i j) -> (Disjoint.{u1} (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) (BoundedOrder.toOrderBot.{u1} (Set.{u1} α) (Preorder.toLE.{u1} (Set.{u1} α) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))))) (CompleteLattice.toBoundedOrder.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α)))))) (s i) (s j)))
Case conversion may be inaccurate. Consider using '#align indexed_partition.disjoint IndexedPartition.disjointₓ'. -/
theorem disjoint : ∀ {i j}, i ≠ j → Disjoint (s i) (s j) := fun i j h =>
  disjoint_left.mpr fun x hxi hxj => h (hs.eq_of_mem hxi hxj)
#align indexed_partition.disjoint IndexedPartition.disjoint

#print IndexedPartition.mem_iff_index_eq /-
theorem mem_iff_index_eq {x i} : x ∈ s i ↔ hs.index x = i :=
  ⟨fun hxi => (hs.eq_of_mem hxi (hs.mem_index x)).symm, fun h => h ▸ hs.mem_index _⟩
#align indexed_partition.mem_iff_index_eq IndexedPartition.mem_iff_index_eq
-/

#print IndexedPartition.eq /-
theorem eq (i) : s i = { x | hs.index x = i } :=
  Set.ext fun _ => hs.mem_iff_index_eq
#align indexed_partition.eq IndexedPartition.eq
-/

#print IndexedPartition.setoid /-
/-- The equivalence relation associated to an indexed partition. Two
elements are equivalent if they belong to the same set of the partition. -/
protected abbrev setoid (hs : IndexedPartition s) : Setoid α :=
  Setoid.ker hs.index
#align indexed_partition.setoid IndexedPartition.setoid
-/

/- warning: indexed_partition.index_some -> IndexedPartition.index_some is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} {s : ι -> (Set.{u2} α)} (hs : IndexedPartition.{u1, u2} ι α s) (i : ι), Eq.{succ u1} ι (IndexedPartition.index.{u1, u2} ι α s hs (IndexedPartition.some.{u1, u2} ι α s hs i)) i
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} {s : ι -> (Set.{u1} α)} (hs : IndexedPartition.{u2, u1} ι α s) (i : ι), Eq.{succ u2} ι (IndexedPartition.index.{u2, u1} ι α s hs (IndexedPartition.some.{u2, u1} ι α s hs i)) i
Case conversion may be inaccurate. Consider using '#align indexed_partition.index_some IndexedPartition.index_someₓ'. -/
@[simp]
theorem index_some (i : ι) : hs.index (hs.some i) = i :=
  (mem_iff_index_eq _).1 <| hs.some_mem i
#align indexed_partition.index_some IndexedPartition.index_some

#print IndexedPartition.some_index /-
theorem some_index (x : α) : hs.Setoid.Rel (hs.some (hs.index x)) x :=
  hs.index_some (hs.index x)
#align indexed_partition.some_index IndexedPartition.some_index
-/

#print IndexedPartition.Quotient /-
/-- The quotient associated to an indexed partition. -/
protected def Quotient :=
  Quotient hs.Setoid
#align indexed_partition.quotient IndexedPartition.Quotient
-/

#print IndexedPartition.proj /-
/-- The projection onto the quotient associated to an indexed partition. -/
def proj : α → hs.Quotient :=
  Quotient.mk''
#align indexed_partition.proj IndexedPartition.proj
-/

instance [Inhabited α] : Inhabited hs.Quotient :=
  ⟨hs.proj default⟩

#print IndexedPartition.proj_eq_iff /-
theorem proj_eq_iff {x y : α} : hs.proj x = hs.proj y ↔ hs.index x = hs.index y :=
  Quotient.eq_rel
#align indexed_partition.proj_eq_iff IndexedPartition.proj_eq_iff
-/

#print IndexedPartition.proj_some_index /-
@[simp]
theorem proj_some_index (x : α) : hs.proj (hs.some (hs.index x)) = hs.proj x :=
  Quotient.eq''.2 (hs.some_index x)
#align indexed_partition.proj_some_index IndexedPartition.proj_some_index
-/

#print IndexedPartition.equivQuotient /-
/-- The obvious equivalence between the quotient associated to an indexed partition and
the indexing type. -/
def equivQuotient : ι ≃ hs.Quotient :=
  (Setoid.quotientKerEquivOfRightInverse hs.index hs.some <| hs.index_some).symm
#align indexed_partition.equiv_quotient IndexedPartition.equivQuotient
-/

#print IndexedPartition.equivQuotient_index_apply /-
@[simp]
theorem equivQuotient_index_apply (x : α) : hs.equivQuotient (hs.index x) = hs.proj x :=
  hs.proj_eq_iff.mpr (some_index hs x)
#align indexed_partition.equiv_quotient_index_apply IndexedPartition.equivQuotient_index_apply
-/

/- warning: indexed_partition.equiv_quotient_symm_proj_apply -> IndexedPartition.equivQuotient_symm_proj_apply is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} {s : ι -> (Set.{u2} α)} (hs : IndexedPartition.{u1, u2} ι α s) (x : α), Eq.{succ u1} ι (coeFn.{max 1 (max (succ u2) (succ u1)) (succ u1) (succ u2), max (succ u2) (succ u1)} (Equiv.{succ u2, succ u1} (IndexedPartition.Quotient.{u1, u2} ι α s hs) ι) (fun (_x : Equiv.{succ u2, succ u1} (IndexedPartition.Quotient.{u1, u2} ι α s hs) ι) => (IndexedPartition.Quotient.{u1, u2} ι α s hs) -> ι) (Equiv.hasCoeToFun.{succ u2, succ u1} (IndexedPartition.Quotient.{u1, u2} ι α s hs) ι) (Equiv.symm.{succ u1, succ u2} ι (IndexedPartition.Quotient.{u1, u2} ι α s hs) (IndexedPartition.equivQuotient.{u1, u2} ι α s hs)) (IndexedPartition.proj.{u1, u2} ι α s hs x)) (IndexedPartition.index.{u1, u2} ι α s hs x)
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} {s : ι -> (Set.{u1} α)} (hs : IndexedPartition.{u2, u1} ι α s) (x : α), Eq.{succ u2} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : IndexedPartition.Quotient.{u2, u1} ι α s hs) => ι) (IndexedPartition.proj.{u2, u1} ι α s hs x)) (FunLike.coe.{max (succ u2) (succ u1), succ u1, succ u2} (Equiv.{succ u1, succ u2} (IndexedPartition.Quotient.{u2, u1} ι α s hs) ι) (IndexedPartition.Quotient.{u2, u1} ι α s hs) (fun (_x : IndexedPartition.Quotient.{u2, u1} ι α s hs) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : IndexedPartition.Quotient.{u2, u1} ι α s hs) => ι) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u2} (IndexedPartition.Quotient.{u2, u1} ι α s hs) ι) (Equiv.symm.{succ u2, succ u1} ι (IndexedPartition.Quotient.{u2, u1} ι α s hs) (IndexedPartition.equivQuotient.{u2, u1} ι α s hs)) (IndexedPartition.proj.{u2, u1} ι α s hs x)) (IndexedPartition.index.{u2, u1} ι α s hs x)
Case conversion may be inaccurate. Consider using '#align indexed_partition.equiv_quotient_symm_proj_apply IndexedPartition.equivQuotient_symm_proj_applyₓ'. -/
@[simp]
theorem equivQuotient_symm_proj_apply (x : α) : hs.equivQuotient.symm (hs.proj x) = hs.index x :=
  rfl
#align indexed_partition.equiv_quotient_symm_proj_apply IndexedPartition.equivQuotient_symm_proj_apply

#print IndexedPartition.equivQuotient_index /-
theorem equivQuotient_index : hs.equivQuotient ∘ hs.index = hs.proj :=
  funext hs.equivQuotient_index_apply
#align indexed_partition.equiv_quotient_index IndexedPartition.equivQuotient_index
-/

#print IndexedPartition.out /-
/-- A map choosing a representative for each element of the quotient associated to an indexed
partition. This is a computable version of `quotient.out'` using `indexed_partition.some`. -/
def out : hs.Quotient ↪ α :=
  hs.equivQuotient.symm.toEmbedding.trans ⟨hs.some, Function.LeftInverse.injective hs.index_some⟩
#align indexed_partition.out IndexedPartition.out
-/

#print IndexedPartition.out_proj /-
/-- This lemma is analogous to `quotient.mk_out'`. -/
@[simp]
theorem out_proj (x : α) : hs.out (hs.proj x) = hs.some (hs.index x) :=
  rfl
#align indexed_partition.out_proj IndexedPartition.out_proj
-/

/- warning: indexed_partition.index_out' -> IndexedPartition.index_out' is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} {s : ι -> (Set.{u2} α)} (hs : IndexedPartition.{u1, u2} ι α s) (x : IndexedPartition.Quotient.{u1, u2} ι α s hs), Eq.{succ u1} ι (IndexedPartition.index.{u1, u2} ι α s hs (Quotient.out'.{succ u2} α (IndexedPartition.setoid.{u1, u2} ι α s hs) x)) (IndexedPartition.index.{u1, u2} ι α s hs (coeFn.{succ u2, succ u2} (Function.Embedding.{succ u2, succ u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs) α) (fun (_x : Function.Embedding.{succ u2, succ u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs) α) => (IndexedPartition.Quotient.{u1, u2} ι α s hs) -> α) (Function.Embedding.hasCoeToFun.{succ u2, succ u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs) α) (IndexedPartition.out.{u1, u2} ι α s hs) x))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} {s : ι -> (Set.{u1} α)} (hs : IndexedPartition.{u2, u1} ι α s) (x : IndexedPartition.Quotient.{u2, u1} ι α s hs), Eq.{succ u2} ι (IndexedPartition.index.{u2, u1} ι α s hs (Quotient.out'.{succ u1} α (IndexedPartition.setoid.{u2, u1} ι α s hs) x)) (IndexedPartition.index.{u2, u1} ι α s hs (FunLike.coe.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs) α) (IndexedPartition.Quotient.{u2, u1} ι α s hs) (fun (_x : IndexedPartition.Quotient.{u2, u1} ι α s hs) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : IndexedPartition.Quotient.{u2, u1} ι α s hs) => α) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs) α) (IndexedPartition.Quotient.{u2, u1} ι α s hs) α (Function.instEmbeddingLikeEmbedding.{succ u1, succ u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs) α)) (IndexedPartition.out.{u2, u1} ι α s hs) x))
Case conversion may be inaccurate. Consider using '#align indexed_partition.index_out' IndexedPartition.index_out'ₓ'. -/
/-- The indices of `quotient.out'` and `indexed_partition.out` are equal. -/
theorem index_out' (x : hs.Quotient) : hs.index x.out' = hs.index (hs.out x) :=
  Quotient.inductionOn' x fun x => (Setoid.ker_apply_mk_out' x).trans (hs.index_some _).symm
#align indexed_partition.index_out' IndexedPartition.index_out'

/- warning: indexed_partition.proj_out -> IndexedPartition.proj_out is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} {s : ι -> (Set.{u2} α)} (hs : IndexedPartition.{u1, u2} ι α s) (x : IndexedPartition.Quotient.{u1, u2} ι α s hs), Eq.{succ u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs) (IndexedPartition.proj.{u1, u2} ι α s hs (coeFn.{succ u2, succ u2} (Function.Embedding.{succ u2, succ u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs) α) (fun (_x : Function.Embedding.{succ u2, succ u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs) α) => (IndexedPartition.Quotient.{u1, u2} ι α s hs) -> α) (Function.Embedding.hasCoeToFun.{succ u2, succ u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs) α) (IndexedPartition.out.{u1, u2} ι α s hs) x)) x
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} {s : ι -> (Set.{u1} α)} (hs : IndexedPartition.{u2, u1} ι α s) (x : IndexedPartition.Quotient.{u2, u1} ι α s hs), Eq.{succ u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs) (IndexedPartition.proj.{u2, u1} ι α s hs (FunLike.coe.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs) α) (IndexedPartition.Quotient.{u2, u1} ι α s hs) (fun (_x : IndexedPartition.Quotient.{u2, u1} ι α s hs) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : IndexedPartition.Quotient.{u2, u1} ι α s hs) => α) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs) α) (IndexedPartition.Quotient.{u2, u1} ι α s hs) α (Function.instEmbeddingLikeEmbedding.{succ u1, succ u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs) α)) (IndexedPartition.out.{u2, u1} ι α s hs) x)) x
Case conversion may be inaccurate. Consider using '#align indexed_partition.proj_out IndexedPartition.proj_outₓ'. -/
/-- This lemma is analogous to `quotient.out_eq'`. -/
@[simp]
theorem proj_out (x : hs.Quotient) : hs.proj (hs.out x) = x :=
  Quotient.inductionOn' x fun x => Quotient.sound' <| hs.some_index x
#align indexed_partition.proj_out IndexedPartition.proj_out

#print IndexedPartition.class_of /-
theorem class_of {x : α} : setOf (hs.Setoid.Rel x) = s (hs.index x) :=
  Set.ext fun y => eq_comm.trans hs.mem_iff_index_eq.symm
#align indexed_partition.class_of IndexedPartition.class_of
-/

/- warning: indexed_partition.proj_fiber -> IndexedPartition.proj_fiber is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : Type.{u2}} {s : ι -> (Set.{u2} α)} (hs : IndexedPartition.{u1, u2} ι α s) (x : IndexedPartition.Quotient.{u1, u2} ι α s hs), Eq.{succ u2} (Set.{u2} α) (Set.preimage.{u2, u2} α (IndexedPartition.Quotient.{u1, u2} ι α s hs) (IndexedPartition.proj.{u1, u2} ι α s hs) (Singleton.singleton.{u2, u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs) (Set.{u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs)) (Set.hasSingleton.{u2} (IndexedPartition.Quotient.{u1, u2} ι α s hs)) x)) (s (coeFn.{max 1 (max (succ u2) (succ u1)) (succ u1) (succ u2), max (succ u2) (succ u1)} (Equiv.{succ u2, succ u1} (IndexedPartition.Quotient.{u1, u2} ι α s hs) ι) (fun (_x : Equiv.{succ u2, succ u1} (IndexedPartition.Quotient.{u1, u2} ι α s hs) ι) => (IndexedPartition.Quotient.{u1, u2} ι α s hs) -> ι) (Equiv.hasCoeToFun.{succ u2, succ u1} (IndexedPartition.Quotient.{u1, u2} ι α s hs) ι) (Equiv.symm.{succ u1, succ u2} ι (IndexedPartition.Quotient.{u1, u2} ι α s hs) (IndexedPartition.equivQuotient.{u1, u2} ι α s hs)) x))
but is expected to have type
  forall {ι : Type.{u2}} {α : Type.{u1}} {s : ι -> (Set.{u1} α)} (hs : IndexedPartition.{u2, u1} ι α s) (x : IndexedPartition.Quotient.{u2, u1} ι α s hs), Eq.{succ u1} (Set.{u1} α) (Set.preimage.{u1, u1} α (IndexedPartition.Quotient.{u2, u1} ι α s hs) (IndexedPartition.proj.{u2, u1} ι α s hs) (Singleton.singleton.{u1, u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs) (Set.{u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs)) (Set.instSingletonSet.{u1} (IndexedPartition.Quotient.{u2, u1} ι α s hs)) x)) (s (FunLike.coe.{max (succ u2) (succ u1), succ u1, succ u2} (Equiv.{succ u1, succ u2} (IndexedPartition.Quotient.{u2, u1} ι α s hs) ι) (IndexedPartition.Quotient.{u2, u1} ι α s hs) (fun (_x : IndexedPartition.Quotient.{u2, u1} ι α s hs) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : IndexedPartition.Quotient.{u2, u1} ι α s hs) => ι) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u2} (IndexedPartition.Quotient.{u2, u1} ι α s hs) ι) (Equiv.symm.{succ u2, succ u1} ι (IndexedPartition.Quotient.{u2, u1} ι α s hs) (IndexedPartition.equivQuotient.{u2, u1} ι α s hs)) x))
Case conversion may be inaccurate. Consider using '#align indexed_partition.proj_fiber IndexedPartition.proj_fiberₓ'. -/
theorem proj_fiber (x : hs.Quotient) : hs.proj ⁻¹' {x} = s (hs.equivQuotient.symm x) :=
  Quotient.inductionOn' x fun x => by
    ext y
    simp only [Set.mem_preimage, Set.mem_singleton_iff, hs.mem_iff_index_eq]
    exact Quotient.eq''
#align indexed_partition.proj_fiber IndexedPartition.proj_fiber

end IndexedPartition


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

import Aesop

def Involutive (f : α → α) : Prop :=
  ∀ x, f (f x) = x

example : Involutive not := by
  aesop (add norm simp Involutive)

example : Involutive not := by
  aesop (add norm unfold Involutive)


/-
Copyright (c) 2021 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot
-/
import algebra.order.with_zero
import topology.algebra.order.basic

/-!
# The topology on linearly ordered commutative groups with zero

Let `Γ₀` be a linearly ordered commutative group to which we have adjoined a zero element.
Then `Γ₀` may naturally be endowed with a topology that turns `Γ₀` into a topological monoid.
Neighborhoods of zero are sets containing `{γ | γ < γ₀}` for some invertible element `γ₀`
and every invertible element is open.
In particular the topology is the following:
"a subset `U ⊆ Γ₀` is open if `0 ∉ U` or if there is an invertible
`γ₀ ∈ Γ₀ such that {γ | γ < γ₀} ⊆ U`", but this fact is not proven here since the neighborhoods
description is what is actually useful.

We prove this topology is ordered and regular (in addition to be compatible with the monoid
structure).

All this is useful to extend a valuation to a completion. This is an abstract version of how the
absolute value (resp. `p`-adic absolute value) on `ℚ` is extended to `ℝ` (resp. `ℚₚ`).

## Implementation notes

This topology is not defined as an instance since it may not be the desired topology on
a linearly ordered commutative group with zero. You can locally activate this topology using
`local attribute [instance] linear_ordered_comm_group_with_zero.topological_space`
All other instances will (`ordered_topology`, `regular_space`, `has_continuous_mul`) then follow.

-/

open_locale topological_space
open topological_space filter set

namespace linear_ordered_comm_group_with_zero

variables (Γ₀ : Type*) [linear_ordered_comm_group_with_zero Γ₀]

/-- The neighbourhoods around γ ∈ Γ₀, used in the definition of the topology on Γ₀.
These neighbourhoods are defined as follows:
A set s is a neighbourhood of 0 if there is an invertible γ₀ ∈ Γ₀ such that {γ | γ < γ₀} ⊆ s.
If γ ≠ 0, then every set that contains γ is a neighbourhood of γ. -/
def nhds_fun (x : Γ₀) : filter Γ₀ :=
if x = 0 then ⨅ (γ₀ : Γ₀ˣ), principal {γ | γ < γ₀} else pure x

/-- The topology on a linearly ordered commutative group with a zero element adjoined.
A subset U is open if 0 ∉ U or if there is an invertible element γ₀ such that {γ | γ < γ₀} ⊆ U. -/
protected def topological_space : topological_space Γ₀ :=
topological_space.mk_of_nhds (nhds_fun Γ₀)

local attribute [instance] linear_ordered_comm_group_with_zero.topological_space

/-- The neighbourhoods {γ | γ < γ₀} of 0 form a directed set indexed by the invertible
elements γ₀. -/
lemma directed_lt : directed (≥) (λ γ₀ : Γ₀ˣ, principal {γ : Γ₀ | γ < γ₀}) :=
begin
  intros γ₁ γ₂,
  use linear_order.min γ₁ γ₂ ; dsimp only,
  split ; rw [ge_iff_le, principal_mono] ; intros x x_in,
  { calc x < ↑(linear_order.min γ₁ γ₂) : x_in
        ... ≤ γ₁ : min_le_left γ₁ γ₂ },
  { calc x < ↑(linear_order.min γ₁ γ₂) : x_in
        ... ≤ γ₂ : min_le_right γ₁ γ₂ }
end

-- We need two auxilliary lemmas to show that nhds_fun accurately describes the neighbourhoods
-- coming from the topology (that is defined in terms of nhds_fun).

/-- At all points of a linearly ordered commutative group with a zero element adjoined,
the pure filter is smaller than the filter given by nhds_fun. -/
lemma pure_le_nhds_fun : pure ≤ nhds_fun Γ₀ :=
λ x, by { by_cases hx : x = 0; simp [hx, nhds_fun] }

/-- For every point Γ₀, and every “neighbourhood” s of it (described by nhds_fun), there is a
smaller “neighbourhood” t ⊆ s, such that s is a “neighbourhood“ of all the points in t. -/
lemma nhds_fun_ok (x : Γ₀) {s} (s_in : s ∈ nhds_fun Γ₀ x) :
  (∃ t ∈ nhds_fun Γ₀ x, t ⊆ s ∧ ∀ y ∈ t, s ∈ nhds_fun Γ₀ y) :=
begin
  by_cases hx : x = 0,
  { simp only [hx, nhds_fun, exists_prop, if_true, eq_self_iff_true] at s_in ⊢,
    cases (mem_infi_of_directed (directed_lt Γ₀) _).mp s_in with γ₀ h,
    use {γ : Γ₀ | γ < γ₀},
    rw mem_principal at h,
    split,
    { apply mem_infi_of_mem γ₀,
      rw mem_principal },
    { refine ⟨h, λ y y_in, _⟩,
      by_cases hy : y = 0,
      { simp only [hy, if_true, eq_self_iff_true],
        apply mem_infi_of_mem γ₀,
        rwa mem_principal },
      { simp [hy, h y_in] } } },
  { simp only [hx, nhds_fun, exists_prop, if_false, mem_pure] at s_in ⊢,
    refine ⟨{x}, mem_singleton _, singleton_subset_iff.2 s_in, λ y y_in, _⟩,
    simpa [mem_singleton_iff.mp y_in, hx] }
end

variables  {Γ₀}

/-- The neighbourhood filter of an invertible element consists of all sets containing that
element. -/
lemma nhds_coe_units (γ : Γ₀ˣ) : 𝓝 (γ : Γ₀) = pure (γ : Γ₀) :=
calc 𝓝 (γ : Γ₀) = nhds_fun Γ₀ γ : nhds_mk_of_nhds (nhds_fun Γ₀) γ (pure_le_nhds_fun Γ₀)
                                                   (nhds_fun_ok Γ₀)
              ... = pure (γ : Γ₀) : if_neg γ.ne_zero

/-- The neighbourhood filter of a nonzero element consists of all sets containing that
element. -/
@[simp] lemma nhds_of_ne_zero (γ : Γ₀) (h : γ ≠ 0) :
  𝓝 γ = pure γ :=
nhds_coe_units (units.mk0 _ h)

/-- If γ is an invertible element of a linearly ordered group with zero element adjoined,
then {γ} is a neighbourhood of γ. -/
lemma singleton_nhds_of_units (γ : Γ₀ˣ) : ({γ} : set Γ₀) ∈ 𝓝 (γ : Γ₀) :=
by simp

/-- If γ is a nonzero element of a linearly ordered group with zero element adjoined,
then {γ} is a neighbourhood of γ. -/
lemma singleton_nhds_of_ne_zero (γ : Γ₀) (h : γ ≠ 0) : ({γ} : set Γ₀) ∈ 𝓝 (γ : Γ₀) :=
by simp [h]

/-- If U is a neighbourhood of 0 in a linearly ordered group with zero element adjoined,
then there exists an invertible element γ₀ such that {γ | γ < γ₀} ⊆ U. -/
lemma has_basis_nhds_zero :
  has_basis (𝓝 (0 : Γ₀)) (λ _, true) (λ γ₀ : Γ₀ˣ, {γ : Γ₀ | γ < γ₀}) :=
⟨begin
  intro U,
  rw nhds_mk_of_nhds (nhds_fun Γ₀) 0 (pure_le_nhds_fun Γ₀) (nhds_fun_ok Γ₀),
  simp only [nhds_fun, if_true, eq_self_iff_true, exists_true_left],
  simp_rw [mem_infi_of_directed (directed_lt Γ₀), mem_principal]
end⟩

/-- If γ is an invertible element of a linearly ordered group with zero element adjoined,
then {x | x < γ} is a neighbourhood of 0. -/
lemma nhds_zero_of_units (γ : Γ₀ˣ) : {x : Γ₀ | x < γ} ∈ 𝓝 (0 : Γ₀) :=
by { rw has_basis_nhds_zero.mem_iff, use γ, simp }

lemma tendsto_zero {α : Type*} {F : filter α} {f : α → Γ₀} :
  tendsto f F (𝓝 (0 : Γ₀)) ↔ ∀ γ₀ : Γ₀ˣ, { x : α | f x < γ₀ } ∈ F :=
by simpa using has_basis_nhds_zero.tendsto_right_iff

/-- If γ is a nonzero element of a linearly ordered group with zero element adjoined,
then {x | x < γ} is a neighbourhood of 0. -/
lemma nhds_zero_of_ne_zero (γ : Γ₀) (h : γ ≠ 0) : {x : Γ₀ | x < γ} ∈ 𝓝 (0 : Γ₀) :=
nhds_zero_of_units (units.mk0 _ h)

lemma has_basis_nhds_units (γ : Γ₀ˣ) :
  has_basis (𝓝 (γ : Γ₀)) (λ i : unit, true) (λ i, {γ}) :=
begin
  rw nhds_of_ne_zero _ γ.ne_zero,
  exact has_basis_pure γ
end

lemma has_basis_nhds_of_ne_zero {x : Γ₀} (h : x ≠ 0) :
  has_basis (𝓝 x) (λ i : unit, true) (λ i, {x}) :=
has_basis_nhds_units (units.mk0 x h)

lemma tendsto_units {α : Type*} {F : filter α} {f : α → Γ₀} {γ₀ : Γ₀ˣ} :
  tendsto f F (𝓝 (γ₀ : Γ₀)) ↔ { x : α | f x = γ₀ } ∈ F :=
begin
  rw (has_basis_nhds_units γ₀).tendsto_right_iff,
  simpa
end

lemma tendsto_of_ne_zero {α : Type*} {F : filter α} {f : α → Γ₀} {γ : Γ₀} (h : γ ≠ 0) :
  tendsto f F (𝓝 γ) ↔ { x : α | f x = γ } ∈ F :=
@tendsto_units _ _ _ F f (units.mk0 γ h)

variable (Γ₀)

/-- The topology on a linearly ordered group with zero element adjoined
is compatible with the order structure. -/
@[priority 100]
instance ordered_topology : order_closed_topology Γ₀ :=
{ is_closed_le' :=
  begin
    rw ← is_open_compl_iff,
    show is_open {p : Γ₀ × Γ₀ | ¬p.fst ≤ p.snd},
    simp only [not_le],
    rw is_open_iff_mem_nhds,
    rintros ⟨a,b⟩ hab,
    change b < a at hab,
    have ha : a ≠ 0 := ne_zero_of_lt hab,
    rw [nhds_prod_eq, mem_prod_iff],
    by_cases hb : b = 0,
    { subst b,
      use [{a}, singleton_nhds_of_ne_zero _ ha, {x : Γ₀ | x < a}, nhds_zero_of_ne_zero _ ha],
      intros p p_in,
      cases mem_prod.1 p_in with h1 h2,
      rw mem_singleton_iff at h1,
      change p.2 < p.1,
      rwa h1 },
    { use [{a}, singleton_nhds_of_ne_zero _ ha, {b}, singleton_nhds_of_ne_zero _ hb],
      intros p p_in,
      cases mem_prod.1 p_in with h1 h2,
      rw mem_singleton_iff at h1 h2,
      change p.2 < p.1,
      rwa [h1, h2] }
  end }

/-- The topology on a linearly ordered group with zero element adjoined is T₃ (aka regular). -/
@[priority 100]
instance regular_space : regular_space Γ₀ :=
begin
  haveI : t1_space Γ₀ := t2_space.t1_space,
  split,
  intros s x s_closed x_not_in_s,
  by_cases hx : x = 0,
  { refine ⟨s, _, subset.rfl, _⟩,
    { subst x,
      rw is_open_iff_mem_nhds,
      intros y hy,
      by_cases hy' : y = 0, { subst y, contradiction },
      simpa [hy'] },
    { erw inf_eq_bot_iff,
      use sᶜ,
      simp only [exists_prop, mem_principal],
      exact ⟨s_closed.compl_mem_nhds x_not_in_s, ⟨s, subset.refl s, by simp⟩⟩ } },
  { simp only [nhds_within, inf_eq_bot_iff, exists_prop, mem_principal],
    exact ⟨{x}ᶜ, is_open_compl_iff.mpr is_closed_singleton, by rwa subset_compl_singleton_iff,
           {x}, singleton_nhds_of_ne_zero x hx, {x}ᶜ, by simp [subset.refl]⟩ }
end

/-- The topology on a linearly ordered group with zero element adjoined makes it a topological
monoid. -/
@[priority 100]
instance : has_continuous_mul Γ₀ :=
⟨begin
  have common : ∀ y ≠ (0 : Γ₀), continuous_at (λ (p : Γ₀ × Γ₀), p.fst * p.snd) (0, y),
  { intros y hy,
    set γ := units.mk0 y hy,
    suffices : tendsto (λ (p : Γ₀ × Γ₀), p.fst * p.snd) ((𝓝 0).prod (𝓝 γ)) (𝓝 0),
    by simpa [continuous_at, nhds_prod_eq],
    suffices : ∀ (γ' : Γ₀ˣ), ∃ (γ''  : Γ₀ˣ), ∀ (a b : Γ₀), a < γ'' → b = y → a * b < γ',
    { rw (has_basis_nhds_zero.prod $ has_basis_nhds_units γ).tendsto_iff has_basis_nhds_zero,
      simpa },
    intros γ',
    use γ⁻¹*γ',
    rintros a b ha hb,
    rw [hb, mul_comm],
    rw [units.coe_mul] at ha,
    simpa using inv_mul_lt_of_lt_mul₀ ha },
  rw continuous_iff_continuous_at,
  rintros ⟨x, y⟩,
  by_cases hx : x = 0; by_cases hy : y = 0,
  { suffices : tendsto (λ (p : Γ₀ × Γ₀), p.fst * p.snd) (𝓝 (0, 0)) (𝓝 0),
    by simpa [hx, hy, continuous_at],
    suffices : ∀ (γ : Γ₀ˣ), ∃ (γ' : Γ₀ˣ), ∀ (a b : Γ₀), a < γ' → b < γ' → a * b < γ,
    by simpa [nhds_prod_eq, has_basis_nhds_zero.prod_self.tendsto_iff has_basis_nhds_zero],
    intros γ,
    rcases exists_square_le γ with ⟨γ', h⟩,
    use γ',
    intros a b ha hb,
    calc a*b < γ'*γ' : mul_lt_mul₀ ha hb
    ... ≤ γ : by exact_mod_cast h },
  { rw hx,
    exact common y hy },
  { rw hy,
    have : (λ (p : Γ₀ × Γ₀), p.fst * p.snd) =
           (λ (p : Γ₀ × Γ₀), p.fst * p.snd) ∘ (λ p : Γ₀ × Γ₀, (p.2, p.1)),
    by { ext, rw [mul_comm] },
    rw this,
    apply continuous_at.comp _ continuous_swap.continuous_at,
    exact common x hx },
  { change tendsto _ _ _,
    rw [nhds_prod_eq],
    rw ((has_basis_nhds_of_ne_zero hx).prod (has_basis_nhds_of_ne_zero hy)).tendsto_iff
       (has_basis_nhds_of_ne_zero $ mul_ne_zero hx hy),
    suffices : ∀ (a b : Γ₀), a = x → b = y → a * b = x * y, by simpa,
    rintros a b rfl rfl,
    refl },
end⟩

end linear_ordered_comm_group_with_zero


/-
Copyright (c) 2022 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel

! This file was ported from Lean 3 source module analysis.special_functions.polar_coord
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.MeasureTheory.Function.Jacobian

/-!
# Polar coordinates

We define polar coordinates, as a local homeomorphism in `ℝ^2` between `ℝ^2 - (-∞, 0]` and
`(0, +∞) × (-π, π)`. Its inverse is given by `(r, θ) ↦ (r cos θ, r sin θ)`.

It satisfies the following change of variables formula (see `integral_comp_polar_coord_symm`):
`∫ p in polar_coord.target, p.1 • f (polar_coord.symm p) = ∫ p, f p`

-/

noncomputable section

open Real Set MeasureTheory

open Real Topology

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- The polar coordinates local homeomorphism in `ℝ^2`, mapping `(r cos θ, r sin θ)` to `(r, θ)`.
It is a homeomorphism between `ℝ^2 - (-∞, 0]` and `(0, +∞) × (-π, π)`. -/
@[simps]
def polarCoord : LocalHomeomorph (ℝ × ℝ) (ℝ × ℝ)
    where
  toFun q := (Real.sqrt (q.1 ^ 2 + q.2 ^ 2), Complex.arg (Complex.equivRealProd.symm q))
  invFun p := (p.1 * cos p.2, p.1 * sin p.2)
  source := { q | 0 < q.1 } ∪ { q | q.2 ≠ 0 }
  target := Ioi (0 : ℝ) ×ˢ Ioo (-π) π
  map_target' := by
    rintro ⟨r, θ⟩ ⟨hr, hθ⟩
    dsimp at hr hθ
    rcases eq_or_ne θ 0 with (rfl | h'θ)
    · simpa using hr
    · right
      simpa only [ne_of_gt hr, Ne.def, mem_set_of_eq, mul_eq_zero, false_or_iff,
        sin_eq_zero_iff_of_lt_of_lt hθ.1 hθ.2] using h'θ
  map_source' := by
    rintro ⟨x, y⟩ hxy
    simp only [prod_mk_mem_set_prod_eq, mem_Ioi, sqrt_pos, mem_Ioo, Complex.neg_pi_lt_arg,
      true_and_iff, Complex.arg_lt_pi_iff]
    constructor
    · cases hxy
      · dsimp at hxy
        linarith [sq_pos_of_ne_zero _ hxy.ne', sq_nonneg y]
      · linarith [sq_nonneg x, sq_pos_of_ne_zero _ hxy]
    · cases hxy
      · exact Or.inl (le_of_lt hxy)
      · exact Or.inr hxy
  right_inv' := by
    rintro ⟨r, θ⟩ ⟨hr, hθ⟩
    dsimp at hr hθ
    simp only [Prod.mk.inj_iff]
    constructor
    · conv_rhs => rw [← sqrt_sq (le_of_lt hr), ← one_mul (r ^ 2), ← sin_sq_add_cos_sq θ]
      congr 1
      ring
    · convert Complex.arg_mul_cos_add_sin_mul_i hr ⟨hθ.1, hθ.2.le⟩
      simp only [Complex.equivRealProd_symm_apply, Complex.ofReal_mul, Complex.ofReal_cos,
        Complex.ofReal_sin]
      ring
  left_inv' := by
    rintro ⟨x, y⟩ hxy
    have A : sqrt (x ^ 2 + y ^ 2) = Complex.abs (x + y * Complex.I) := by
      simp only [Complex.abs_def, Complex.normSq, pow_two, MonoidWithZeroHom.coe_mk, Complex.add_re,
        Complex.ofReal_re, Complex.mul_re, Complex.I_re, MulZeroClass.mul_zero, Complex.ofReal_im,
        Complex.I_im, sub_self, add_zero, Complex.add_im, Complex.mul_im, mul_one, zero_add]
    have Z := Complex.abs_mul_cos_add_sin_mul_i (x + y * Complex.I)
    simp only [← Complex.ofReal_cos, ← Complex.ofReal_sin, mul_add, ← Complex.ofReal_mul, ←
      mul_assoc] at Z
    simpa [A, -Complex.ofReal_cos, -Complex.ofReal_sin] using Complex.ext_iff.1 Z
  open_target := isOpen_Ioi.Prod isOpen_Ioo
  open_source :=
    (isOpen_lt continuous_const continuous_fst).union
      (isOpen_ne_fun continuous_snd continuous_const)
  continuous_invFun :=
    ((continuous_fst.mul (continuous_cos.comp continuous_snd)).prod_mk
        (continuous_fst.mul (continuous_sin.comp continuous_snd))).ContinuousOn
  continuous_toFun :=
    by
    apply ((continuous_fst.pow 2).add (continuous_snd.pow 2)).sqrt.ContinuousOn.Prod
    have A :
      maps_to complex.equiv_real_prod.symm ({ q : ℝ × ℝ | 0 < q.1 } ∪ { q : ℝ × ℝ | q.2 ≠ 0 })
        { z | 0 < z.re ∨ z.im ≠ 0 } :=
      by
      rintro ⟨x, y⟩ hxy
      simpa only using hxy
    apply ContinuousOn.comp (fun z hz => _) _ A
    · exact (Complex.continuousAt_arg hz).ContinuousWithinAt
    · exact complex.equiv_real_prod_clm.symm.continuous.continuous_on
#align polar_coord polarCoord

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `«expr!![ » -/
/- ./././Mathport/Syntax/Translate/Expr.lean:387:14: unsupported user notation matrix.notation -/
theorem hasFderivAt_polarCoord_symm (p : ℝ × ℝ) :
    HasFderivAt polarCoord.symm
      (Matrix.toLin (Basis.finTwoProd ℝ) (Basis.finTwoProd ℝ)
          («expr!![ »
            "./././Mathport/Syntax/Translate/Expr.lean:387:14: unsupported user notation matrix.notation")).toContinuousLinearMap
      p :=
  by
  rw [Matrix.toLin_finTwoProd_toContinuousLinearMap]
  convert HasFderivAt.prod
        (has_fderiv_at_fst.mul ((has_deriv_at_cos p.2).comp_hasFderivAt p hasFderivAt_snd))
        (has_fderiv_at_fst.mul ((has_deriv_at_sin p.2).comp_hasFderivAt p hasFderivAt_snd)) using
      2 <;>
    simp only [smul_smul, add_comm, neg_mul, neg_smul, smul_neg]
#align has_fderiv_at_polar_coord_symm hasFderivAt_polarCoord_symm

theorem polarCoord_source_ae_eq_univ : polarCoord.source =ᵐ[volume] univ :=
  by
  have A : polar_coord.sourceᶜ ⊆ (LinearMap.snd ℝ ℝ ℝ).ker :=
    by
    intro x hx
    simp only [polarCoord_source, compl_union, mem_inter_iff, mem_compl_iff, mem_set_of_eq, not_lt,
      Classical.not_not] at hx
    exact hx.2
  have B : volume ((LinearMap.snd ℝ ℝ ℝ).ker : Set (ℝ × ℝ)) = 0 :=
    by
    apply measure.add_haar_submodule
    rw [Ne.def, LinearMap.ker_eq_top]
    intro h
    have : (LinearMap.snd ℝ ℝ ℝ) (0, 1) = (0 : ℝ × ℝ →ₗ[ℝ] ℝ) (0, 1) := by rw [h]
    simpa using this
  simp only [ae_eq_univ]
  exact le_antisymm ((measure_mono A).trans (le_of_eq B)) bot_le
#align polar_coord_source_ae_eq_univ polarCoord_source_ae_eq_univ

/- ./././Mathport/Syntax/Translate/Expr.lean:207:4: warning: unsupported notation `«expr!![ » -/
/- ./././Mathport/Syntax/Translate/Expr.lean:387:14: unsupported user notation matrix.notation -/
theorem integral_comp_polarCoord_symm {E : Type _} [NormedAddCommGroup E] [NormedSpace ℝ E]
    [CompleteSpace E] (f : ℝ × ℝ → E) :
    (∫ p in polarCoord.target, p.1 • f (polarCoord.symm p)) = ∫ p, f p :=
  by
  set B : ℝ × ℝ → ℝ × ℝ →L[ℝ] ℝ × ℝ := fun p =>
    (Matrix.toLin (Basis.finTwoProd ℝ) (Basis.finTwoProd ℝ)
        («expr!![ »
          "./././Mathport/Syntax/Translate/Expr.lean:387:14: unsupported user notation matrix.notation")).toContinuousLinearMap with
    hB
  have A : ∀ p ∈ polar_coord.symm.source, HasFderivAt polar_coord.symm (B p) p := fun p hp =>
    hasFderivAt_polarCoord_symm p
  have B_det : ∀ p, (B p).det = p.1 := by
    intro p
    conv_rhs => rw [← one_mul p.1, ← cos_sq_add_sin_sq p.2]
    simp only [neg_mul, LinearMap.det_toContinuousLinearMap, LinearMap.det_toLin,
      Matrix.det_fin_two_of, sub_neg_eq_add]
    ring
  symm
  calc
    (∫ p, f p) = ∫ p in polar_coord.source, f p :=
      by
      rw [← integral_univ]
      apply set_integral_congr_set_ae
      exact polar_coord_source_ae_eq_univ.symm
    _ = ∫ p in polar_coord.target, abs (B p).det • f (polar_coord.symm p) := by
      apply integral_target_eq_integral_abs_det_fderiv_smul volume A
    _ = ∫ p in polar_coord.target, p.1 • f (polar_coord.symm p) :=
      by
      apply set_integral_congr polar_coord.open_target.measurable_set fun x hx => _
      rw [B_det, abs_of_pos]
      exact hx.1

#align integral_comp_polar_coord_symm integral_comp_polarCoord_symm