/-
Copyright (c) 2014 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura, Johannes Hölzl, Mario Carneiro

-- QUESTION: can make the first argument in ∀ x ∈ a, ... implicit?
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.order.complete_boolean_algebra
import Mathlib.data.sigma.basic
import Mathlib.order.galois_connection
import Mathlib.order.directed
import Mathlib.PostPort

universes u v x y u_1 u_2 u_3 w

namespace Mathlib

namespace set

protected instance lattice_set {α : Type u} : complete_lattice (set α) :=
  complete_lattice.mk boolean_algebra.sup boolean_algebra.le boolean_algebra.lt sorry sorry sorry
    sorry sorry sorry boolean_algebra.inf sorry sorry sorry boolean_algebra.top sorry
    boolean_algebra.bot sorry
    (fun (s : set (set α)) => set_of fun (a : α) => ∃ (t : set α), ∃ (H : t ∈ s), a ∈ t)
    (fun (s : set (set α)) => set_of fun (a : α) => ∀ (t : set α), t ∈ s → a ∈ t) sorry sorry sorry
    sorry

/-- Image is monotone. See `set.image_image` for the statement in terms of `⊆`. -/
theorem monotone_image {α : Type u} {β : Type v} {f : α → β} : monotone (image f) :=
  fun (s t : set α) (h : s ⊆ t) => image_subset f h

theorem monotone_inter {α : Type u} {β : Type v} [preorder β] {f : β → set α} {g : β → set α}
    (hf : monotone f) (hg : monotone g) : monotone fun (x : β) => f x ∩ g x :=
  fun (b₁ b₂ : β) (h : b₁ ≤ b₂) => inter_subset_inter (hf h) (hg h)

theorem monotone_union {α : Type u} {β : Type v} [preorder β] {f : β → set α} {g : β → set α}
    (hf : monotone f) (hg : monotone g) : monotone fun (x : β) => f x ∪ g x :=
  fun (b₁ b₂ : β) (h : b₁ ≤ b₂) => union_subset_union (hf h) (hg h)

theorem monotone_set_of {α : Type u} {β : Type v} [preorder α] {p : α → β → Prop}
    (hp : ∀ (b : β), monotone fun (a : α) => p a b) :
    monotone fun (a : α) => set_of fun (b : β) => p a b :=
  fun (a a' : α) (h : a ≤ a') (b : β) => hp b h

protected theorem image_preimage {α : Type u} {β : Type v} {f : α → β} :
    galois_connection (image f) (preimage f) :=
  fun (a : set α) (b : set β) => image_subset_iff

/-- `kern_image f s` is the set of `y` such that `f ⁻¹ y ⊆ s` -/
def kern_image {α : Type u} {β : Type v} (f : α → β) (s : set α) : set β :=
  set_of fun (y : β) => ∀ {x : α}, f x = y → x ∈ s

protected theorem preimage_kern_image {α : Type u} {β : Type v} {f : α → β} :
    galois_connection (preimage f) (kern_image f) :=
  sorry

/- union and intersection over a family of sets indexed by a type -/

/-- Indexed union of a family of sets -/
def Union {β : Type v} {ι : Sort x} (s : ι → set β) : set β := supr s

/-- Indexed intersection of a family of sets -/
def Inter {β : Type v} {ι : Sort x} (s : ι → set β) : set β := infi s

@[simp] theorem mem_Union {β : Type v} {ι : Sort x} {x : β} {s : ι → set β} :
    x ∈ Union s ↔ ∃ (i : ι), x ∈ s i :=
  sorry

/- alternative proof: dsimp [Union, supr, Sup]; simp -/

theorem set_of_exists {β : Type v} {ι : Sort x} (p : ι → β → Prop) :
    (set_of fun (x : β) => ∃ (i : ι), p i x) = Union fun (i : ι) => set_of fun (x : β) => p i x :=
  ext fun (i : β) => iff.symm mem_Union

@[simp] theorem mem_Inter {β : Type v} {ι : Sort x} {x : β} {s : ι → set β} :
    x ∈ Inter s ↔ ∀ (i : ι), x ∈ s i :=
  sorry

theorem set_of_forall {β : Type v} {ι : Sort x} (p : ι → β → Prop) :
    (set_of fun (x : β) => ∀ (i : ι), p i x) = Inter fun (i : ι) => set_of fun (x : β) => p i x :=
  ext fun (i : β) => iff.symm mem_Inter

-- TODO: should be simpler when sets' order is based on lattices

theorem Union_subset {β : Type v} {ι : Sort x} {s : ι → set β} {t : set β}
    (h : ∀ (i : ι), s i ⊆ t) : (Union fun (i : ι) => s i) ⊆ t :=
  supr_le h

theorem Union_subset_iff {β : Type v} {ι : Sort x} {s : ι → set β} {t : set β} :
    (Union fun (i : ι) => s i) ⊆ t ↔ ∀ (i : ι), s i ⊆ t :=
  { mp := fun (h : (Union fun (i : ι) => s i) ⊆ t) (i : ι) => subset.trans (le_supr s i) h,
    mpr := Union_subset }

theorem mem_Inter_of_mem {β : Type v} {ι : Sort x} {x : β} {s : ι → set β} :
    (∀ (i : ι), x ∈ s i) → x ∈ Inter fun (i : ι) => s i :=
  iff.mpr mem_Inter

-- TODO: should be simpler when sets' order is based on lattices

theorem subset_Inter {β : Type v} {ι : Sort x} {t : set β} {s : ι → set β}
    (h : ∀ (i : ι), t ⊆ s i) : t ⊆ Inter fun (i : ι) => s i :=
  le_infi h

theorem subset_Inter_iff {β : Type v} {ι : Sort x} {t : set β} {s : ι → set β} :
    (t ⊆ Inter fun (i : ι) => s i) ↔ ∀ (i : ι), t ⊆ s i :=
  le_infi_iff

theorem subset_Union {β : Type v} {ι : Sort x} (s : ι → set β) (i : ι) :
    s i ⊆ Union fun (i : ι) => s i :=
  le_supr

-- This rather trivial consequence is convenient with `apply`,

-- and has `i` explicit for this use case.

theorem subset_subset_Union {β : Type v} {ι : Sort x} {A : set β} {s : ι → set β} (i : ι)
    (h : A ⊆ s i) : A ⊆ Union fun (i : ι) => s i :=
  subset.trans h (subset_Union s i)

theorem Inter_subset {β : Type v} {ι : Sort x} (s : ι → set β) (i : ι) :
    (Inter fun (i : ι) => s i) ⊆ s i :=
  infi_le

theorem Inter_subset_of_subset {α : Type u} {ι : Sort x} {s : ι → set α} {t : set α} (i : ι)
    (h : s i ⊆ t) : (Inter fun (i : ι) => s i) ⊆ t :=
  subset.trans (Inter_subset s i) h

theorem Inter_subset_Inter {α : Type u} {ι : Sort x} {s : ι → set α} {t : ι → set α}
    (h : ∀ (i : ι), s i ⊆ t i) : (Inter fun (i : ι) => s i) ⊆ Inter fun (i : ι) => t i :=
  subset_Inter fun (i : ι) => Inter_subset_of_subset i (h i)

theorem Inter_subset_Inter2 {α : Type u} {ι : Sort x} {ι' : Sort y} {s : ι → set α} {t : ι' → set α}
    (h : ∀ (j : ι'), ∃ (i : ι), s i ⊆ t j) :
    (Inter fun (i : ι) => s i) ⊆ Inter fun (j : ι') => t j :=
  sorry

theorem Inter_set_of {α : Type u} {ι : Sort x} (P : ι → α → Prop) :
    (Inter fun (i : ι) => set_of fun (x : α) => P i x) = set_of fun (x : α) => ∀ (i : ι), P i x :=
  sorry

theorem Union_const {β : Type v} {ι : Sort x} [Nonempty ι] (s : set β) :
    (Union fun (i : ι) => s) = s :=
  sorry

theorem Inter_const {β : Type v} {ι : Sort x} [Nonempty ι] (s : set β) :
    (Inter fun (i : ι) => s) = s :=
  sorry

@[simp] theorem compl_Union {β : Type v} {ι : Sort x} (s : ι → set β) :
    (Union fun (i : ι) => s i)ᶜ = Inter fun (i : ι) => s iᶜ :=
  sorry

-- classical -- complete_boolean_algebra

theorem compl_Inter {β : Type v} {ι : Sort x} (s : ι → set β) :
    (Inter fun (i : ι) => s i)ᶜ = Union fun (i : ι) => s iᶜ :=
  sorry

-- classical -- complete_boolean_algebra

theorem Union_eq_comp_Inter_comp {β : Type v} {ι : Sort x} (s : ι → set β) :
    (Union fun (i : ι) => s i) = ((Inter fun (i : ι) => s iᶜ)ᶜ) :=
  sorry

-- classical -- complete_boolean_algebra

theorem Inter_eq_comp_Union_comp {β : Type v} {ι : Sort x} (s : ι → set β) :
    (Inter fun (i : ι) => s i) = ((Union fun (i : ι) => s iᶜ)ᶜ) :=
  sorry

theorem inter_Union {β : Type v} {ι : Sort x} (s : set β) (t : ι → set β) :
    (s ∩ Union fun (i : ι) => t i) = Union fun (i : ι) => s ∩ t i :=
  sorry

theorem Union_inter {β : Type v} {ι : Sort x} (s : set β) (t : ι → set β) :
    (Union fun (i : ι) => t i) ∩ s = Union fun (i : ι) => t i ∩ s :=
  sorry

theorem Union_union_distrib {β : Type v} {ι : Sort x} (s : ι → set β) (t : ι → set β) :
    (Union fun (i : ι) => s i ∪ t i) = (Union fun (i : ι) => s i) ∪ Union fun (i : ι) => t i :=
  sorry

theorem Inter_inter_distrib {β : Type v} {ι : Sort x} (s : ι → set β) (t : ι → set β) :
    (Inter fun (i : ι) => s i ∩ t i) = (Inter fun (i : ι) => s i) ∩ Inter fun (i : ι) => t i :=
  sorry

theorem union_Union {β : Type v} {ι : Sort x} [Nonempty ι] (s : set β) (t : ι → set β) :
    (s ∪ Union fun (i : ι) => t i) = Union fun (i : ι) => s ∪ t i :=
  sorry

theorem Union_union {β : Type v} {ι : Sort x} [Nonempty ι] (s : set β) (t : ι → set β) :
    (Union fun (i : ι) => t i) ∪ s = Union fun (i : ι) => t i ∪ s :=
  sorry

theorem inter_Inter {β : Type v} {ι : Sort x} [Nonempty ι] (s : set β) (t : ι → set β) :
    (s ∩ Inter fun (i : ι) => t i) = Inter fun (i : ι) => s ∩ t i :=
  sorry

theorem Inter_inter {β : Type v} {ι : Sort x} [Nonempty ι] (s : set β) (t : ι → set β) :
    (Inter fun (i : ι) => t i) ∩ s = Inter fun (i : ι) => t i ∩ s :=
  sorry

-- classical

theorem union_Inter {β : Type v} {ι : Sort x} (s : set β) (t : ι → set β) :
    (s ∪ Inter fun (i : ι) => t i) = Inter fun (i : ι) => s ∪ t i :=
  sorry

theorem Union_diff {β : Type v} {ι : Sort x} (s : set β) (t : ι → set β) :
    (Union fun (i : ι) => t i) \ s = Union fun (i : ι) => t i \ s :=
  Union_inter (fun (a : β) => a ∈ s → False) fun (i : ι) => t i

theorem diff_Union {β : Type v} {ι : Sort x} [Nonempty ι] (s : set β) (t : ι → set β) :
    (s \ Union fun (i : ι) => t i) = Inter fun (i : ι) => s \ t i :=
  sorry

theorem diff_Inter {β : Type v} {ι : Sort x} (s : set β) (t : ι → set β) :
    (s \ Inter fun (i : ι) => t i) = Union fun (i : ι) => s \ t i :=
  sorry

theorem directed_on_Union {α : Type u} {r : α → α → Prop} {ι : Sort v} {f : ι → set α}
    (hd : directed has_subset.subset f) (h : ∀ (x : ι), directed_on r (f x)) :
    directed_on r (Union fun (x : ι) => f x) :=
  sorry

theorem Union_inter_subset {ι : Sort u_1} {α : Type u_2} {s : ι → set α} {t : ι → set α} :
    (Union fun (i : ι) => s i ∩ t i) ⊆ (Union fun (i : ι) => s i) ∩ Union fun (i : ι) => t i :=
  sorry

theorem Union_inter_of_monotone {ι : Type u_1} {α : Type u_2} [semilattice_sup ι] {s : ι → set α}
    {t : ι → set α} (hs : monotone s) (ht : monotone t) :
    (Union fun (i : ι) => s i ∩ t i) = (Union fun (i : ι) => s i) ∩ Union fun (i : ι) => t i :=
  sorry

/-- An equality version of this lemma is `Union_Inter_of_monotone` in `data.set.finite`. -/
theorem Union_Inter_subset {ι : Sort u_1} {ι' : Sort u_2} {α : Type u_3} {s : ι → ι' → set α} :
    (Union fun (j : ι') => Inter fun (i : ι) => s i j) ⊆
        Inter fun (i : ι) => Union fun (j : ι') => s i j :=
  sorry

/- bounded unions and intersections -/

theorem mem_bUnion_iff {α : Type u} {β : Type v} {s : set α} {t : α → set β} {y : β} :
    (y ∈ Union fun (x : α) => Union fun (H : x ∈ s) => t x) ↔ ∃ (x : α), ∃ (H : x ∈ s), y ∈ t x :=
  sorry

theorem mem_bInter_iff {α : Type u} {β : Type v} {s : set α} {t : α → set β} {y : β} :
    (y ∈ Inter fun (x : α) => Inter fun (H : x ∈ s) => t x) ↔ ∀ (x : α), x ∈ s → y ∈ t x :=
  sorry

theorem mem_bUnion {α : Type u} {β : Type v} {s : set α} {t : α → set β} {x : α} {y : β}
    (xs : x ∈ s) (ytx : y ∈ t x) : y ∈ Union fun (x : α) => Union fun (H : x ∈ s) => t x :=
  sorry

theorem mem_bInter {α : Type u} {β : Type v} {s : set α} {t : α → set β} {y : β}
    (h : ∀ (x : α), x ∈ s → y ∈ t x) : y ∈ Inter fun (x : α) => Inter fun (H : x ∈ s) => t x :=
  eq.mpr (id (Eq.trans (propext mem_Inter) (forall_congr_eq fun (i : α) => propext mem_Inter))) h

theorem bUnion_subset {α : Type u} {β : Type v} {s : set α} {t : set β} {u : α → set β}
    (h : ∀ (x : α), x ∈ s → u x ⊆ t) : (Union fun (x : α) => Union fun (H : x ∈ s) => u x) ⊆ t :=
  (fun (this : (supr fun (x : α) => supr fun (H : x ∈ s) => u x) ≤ t) => this)
    (supr_le fun (x : α) => supr_le (h x))

theorem subset_bInter {α : Type u} {β : Type v} {s : set α} {t : set β} {u : α → set β}
    (h : ∀ (x : α), x ∈ s → t ⊆ u x) : t ⊆ Inter fun (x : α) => Inter fun (H : x ∈ s) => u x :=
  subset_Inter fun (x : α) => subset_Inter (h x)

theorem subset_bUnion_of_mem {α : Type u} {β : Type v} {s : set α} {u : α → set β} {x : α}
    (xs : x ∈ s) : u x ⊆ Union fun (x : α) => Union fun (H : x ∈ s) => u x :=
  (fun (this : u x ≤ supr fun (x : α) => supr fun (H : x ∈ s) => u x) => this)
    (le_supr_of_le x (le_supr (fun (xs : x ∈ s) => u x) xs))

theorem bInter_subset_of_mem {α : Type u} {β : Type v} {s : set α} {t : α → set β} {x : α}
    (xs : x ∈ s) : (Inter fun (x : α) => Inter fun (H : x ∈ s) => t x) ⊆ t x :=
  (fun (this : (infi fun (x : α) => infi fun (H : x ∈ s) => t x) ≤ t x) => this)
    (infi_le_of_le x (infi_le (fun (H : x ∈ s) => t x) xs))

theorem bUnion_subset_bUnion_left {α : Type u} {β : Type v} {s : set α} {s' : set α} {t : α → set β}
    (h : s ⊆ s') :
    (Union fun (x : α) => Union fun (H : x ∈ s) => t x) ⊆
        Union fun (x : α) => Union fun (H : x ∈ s') => t x :=
  bUnion_subset fun (x : α) (xs : x ∈ s) => subset_bUnion_of_mem (h xs)

theorem bInter_subset_bInter_left {α : Type u} {β : Type v} {s : set α} {s' : set α} {t : α → set β}
    (h : s' ⊆ s) :
    (Inter fun (x : α) => Inter fun (H : x ∈ s) => t x) ⊆
        Inter fun (x : α) => Inter fun (H : x ∈ s') => t x :=
  subset_bInter fun (x : α) (xs : x ∈ s') => bInter_subset_of_mem (h xs)

theorem bUnion_subset_bUnion_right {α : Type u} {β : Type v} {s : set α} {t1 : α → set β}
    {t2 : α → set β} (h : ∀ (x : α), x ∈ s → t1 x ⊆ t2 x) :
    (Union fun (x : α) => Union fun (H : x ∈ s) => t1 x) ⊆
        Union fun (x : α) => Union fun (H : x ∈ s) => t2 x :=
  bUnion_subset fun (x : α) (xs : x ∈ s) => subset.trans (h x xs) (subset_bUnion_of_mem xs)

theorem bInter_subset_bInter_right {α : Type u} {β : Type v} {s : set α} {t1 : α → set β}
    {t2 : α → set β} (h : ∀ (x : α), x ∈ s → t1 x ⊆ t2 x) :
    (Inter fun (x : α) => Inter fun (H : x ∈ s) => t1 x) ⊆
        Inter fun (x : α) => Inter fun (H : x ∈ s) => t2 x :=
  subset_bInter fun (x : α) (xs : x ∈ s) => subset.trans (bInter_subset_of_mem xs) (h x xs)

theorem bUnion_subset_bUnion {α : Type u} {β : Type v} {γ : Type u_1} {s : set α} {t : α → set β}
    {s' : set γ} {t' : γ → set β}
    (h : ∀ (x : α) (H : x ∈ s), ∃ (y : γ), ∃ (H : y ∈ s'), t x ⊆ t' y) :
    (Union fun (x : α) => Union fun (H : x ∈ s) => t x) ⊆
        Union fun (y : γ) => Union fun (H : y ∈ s') => t' y :=
  sorry

theorem bInter_mono' {α : Type u} {β : Type v} {s : set α} {s' : set α} {t : α → set β}
    {t' : α → set β} (hs : s ⊆ s') (h : ∀ (x : α), x ∈ s → t x ⊆ t' x) :
    (Inter fun (x : α) => Inter fun (H : x ∈ s') => t x) ⊆
        Inter fun (x : α) => Inter fun (H : x ∈ s) => t' x :=
  sorry

theorem bInter_mono {α : Type u} {β : Type v} {s : set α} {t : α → set β} {t' : α → set β}
    (h : ∀ (x : α), x ∈ s → t x ⊆ t' x) :
    (Inter fun (x : α) => Inter fun (H : x ∈ s) => t x) ⊆
        Inter fun (x : α) => Inter fun (H : x ∈ s) => t' x :=
  bInter_mono' (subset.refl s) h

theorem bUnion_mono {α : Type u} {β : Type v} {s : set α} {t : α → set β} {t' : α → set β}
    (h : ∀ (x : α), x ∈ s → t x ⊆ t' x) :
    (Union fun (x : α) => Union fun (H : x ∈ s) => t x) ⊆
        Union fun (x : α) => Union fun (H : x ∈ s) => t' x :=
  bUnion_subset_bUnion fun (x : α) (x_in : x ∈ s) => Exists.intro x (Exists.intro x_in (h x x_in))

theorem bUnion_eq_Union {α : Type u} {β : Type v} (s : set α) (t : (x : α) → x ∈ s → set β) :
    (Union fun (x : α) => Union fun (H : x ∈ s) => t x H) =
        Union fun (x : ↥s) => t (↑x) (subtype.property x) :=
  supr_subtype'

theorem bInter_eq_Inter {α : Type u} {β : Type v} (s : set α) (t : (x : α) → x ∈ s → set β) :
    (Inter fun (x : α) => Inter fun (H : x ∈ s) => t x H) =
        Inter fun (x : ↥s) => t (↑x) (subtype.property x) :=
  infi_subtype'

theorem bInter_empty {α : Type u} {β : Type v} (u : α → set β) :
    (Inter fun (x : α) => Inter fun (H : x ∈ ∅) => u x) = univ :=
  (fun (this : (infi fun (x : α) => infi fun (H : x ∈ ∅) => u x) = ⊤) => this) infi_emptyset

theorem bInter_univ {α : Type u} {β : Type v} (u : α → set β) :
    (Inter fun (x : α) => Inter fun (H : x ∈ univ) => u x) = Inter fun (x : α) => u x :=
  infi_univ

-- TODO(Jeremy): here is an artifact of the the encoding of bounded intersection:

-- without dsimp, the next theorem fails to type check, because there is a lambda

-- in a type that needs to be contracted. Using simp [eq_of_mem_singleton xa] also works.

@[simp] theorem bInter_singleton {α : Type u} {β : Type v} (a : α) (s : α → set β) :
    (Inter fun (x : α) => Inter fun (H : x ∈ singleton a) => s x) = s a :=
  sorry

theorem bInter_union {α : Type u} {β : Type v} (s : set α) (t : set α) (u : α → set β) :
    (Inter fun (x : α) => Inter fun (H : x ∈ s ∪ t) => u x) =
        (Inter fun (x : α) => Inter fun (H : x ∈ s) => u x) ∩
          Inter fun (x : α) => Inter fun (H : x ∈ t) => u x :=
  sorry

-- TODO(Jeremy): simp [insert_eq, bInter_union] doesn't work

@[simp] theorem bInter_insert {α : Type u} {β : Type v} (a : α) (s : set α) (t : α → set β) :
    (Inter fun (x : α) => Inter fun (H : x ∈ insert a s) => t x) =
        t a ∩ Inter fun (x : α) => Inter fun (H : x ∈ s) => t x :=
  sorry

-- TODO(Jeremy): another example of where an annotation is needed

theorem bInter_pair {α : Type u} {β : Type v} (a : α) (b : α) (s : α → set β) :
    (Inter fun (x : α) => Inter fun (H : x ∈ insert a (singleton b)) => s x) = s a ∩ s b :=
  sorry

theorem bUnion_empty {α : Type u} {β : Type v} (s : α → set β) :
    (Union fun (x : α) => Union fun (H : x ∈ ∅) => s x) = ∅ :=
  supr_emptyset

theorem bUnion_univ {α : Type u} {β : Type v} (s : α → set β) :
    (Union fun (x : α) => Union fun (H : x ∈ univ) => s x) = Union fun (x : α) => s x :=
  supr_univ

@[simp] theorem bUnion_singleton {α : Type u} {β : Type v} (a : α) (s : α → set β) :
    (Union fun (x : α) => Union fun (H : x ∈ singleton a) => s x) = s a :=
  supr_singleton

@[simp] theorem bUnion_of_singleton {α : Type u} (s : set α) :
    (Union fun (x : α) => Union fun (H : x ∈ s) => singleton x) = s :=
  sorry

theorem bUnion_union {α : Type u} {β : Type v} (s : set α) (t : set α) (u : α → set β) :
    (Union fun (x : α) => Union fun (H : x ∈ s ∪ t) => u x) =
        (Union fun (x : α) => Union fun (H : x ∈ s) => u x) ∪
          Union fun (x : α) => Union fun (H : x ∈ t) => u x :=
  supr_union

@[simp] theorem Union_subtype {α : Type u_1} {β : Type u_2} (s : set α) (f : α → set β) :
    (Union fun (i : ↥s) => f ↑i) = Union fun (i : α) => Union fun (H : i ∈ s) => f i :=
  Eq.symm (bUnion_eq_Union s fun (x : α) (_x : x ∈ s) => f x)

-- TODO(Jeremy): once again, simp doesn't do it alone.

@[simp] theorem bUnion_insert {α : Type u} {β : Type v} (a : α) (s : set α) (t : α → set β) :
    (Union fun (x : α) => Union fun (H : x ∈ insert a s) => t x) =
        t a ∪ Union fun (x : α) => Union fun (H : x ∈ s) => t x :=
  sorry

theorem bUnion_pair {α : Type u} {β : Type v} (a : α) (b : α) (s : α → set β) :
    (Union fun (x : α) => Union fun (H : x ∈ insert a (singleton b)) => s x) = s a ∪ s b :=
  sorry

@[simp] theorem compl_bUnion {α : Type u} {β : Type v} (s : set α) (t : α → set β) :
    (Union fun (i : α) => Union fun (H : i ∈ s) => t i)ᶜ =
        Inter fun (i : α) => Inter fun (H : i ∈ s) => t iᶜ :=
  sorry

-- classical -- complete_boolean_algebra

theorem compl_bInter {α : Type u} {β : Type v} (s : set α) (t : α → set β) :
    (Inter fun (i : α) => Inter fun (H : i ∈ s) => t i)ᶜ =
        Union fun (i : α) => Union fun (H : i ∈ s) => t iᶜ :=
  sorry

theorem inter_bUnion {α : Type u} {β : Type v} (s : set α) (t : α → set β) (u : set β) :
    (u ∩ Union fun (i : α) => Union fun (H : i ∈ s) => t i) =
        Union fun (i : α) => Union fun (H : i ∈ s) => u ∩ t i :=
  sorry

theorem bUnion_inter {α : Type u} {β : Type v} (s : set α) (t : α → set β) (u : set β) :
    (Union fun (i : α) => Union fun (H : i ∈ s) => t i) ∩ u =
        Union fun (i : α) => Union fun (H : i ∈ s) => t i ∩ u :=
  sorry

/-- Intersection of a set of sets. -/
def sInter {α : Type u} (S : set (set α)) : set α := Inf S

prefix:110 "⋂₀" => Mathlib.set.sInter

theorem mem_sUnion_of_mem {α : Type u} {x : α} {t : set α} {S : set (set α)} (hx : x ∈ t)
    (ht : t ∈ S) : x ∈ ⋃₀S :=
  Exists.intro t (Exists.intro ht hx)

theorem mem_sUnion {α : Type u} {x : α} {S : set (set α)} :
    x ∈ ⋃₀S ↔ ∃ (t : set α), ∃ (H : t ∈ S), x ∈ t :=
  iff.rfl

-- is this theorem really necessary?

theorem not_mem_of_not_mem_sUnion {α : Type u} {x : α} {t : set α} {S : set (set α)} (hx : ¬x ∈ ⋃₀S)
    (ht : t ∈ S) : ¬x ∈ t :=
  fun (h : x ∈ t) => hx (Exists.intro t (Exists.intro ht h))

@[simp] theorem mem_sInter {α : Type u} {x : α} {S : set (set α)} :
    x ∈ ⋂₀S ↔ ∀ (t : set α), t ∈ S → x ∈ t :=
  iff.rfl

theorem sInter_subset_of_mem {α : Type u} {S : set (set α)} {t : set α} (tS : t ∈ S) : ⋂₀S ⊆ t :=
  Inf_le tS

theorem subset_sUnion_of_mem {α : Type u} {S : set (set α)} {t : set α} (tS : t ∈ S) : t ⊆ ⋃₀S :=
  le_Sup tS

theorem subset_sUnion_of_subset {α : Type u} {s : set α} (t : set (set α)) (u : set α) (h₁ : s ⊆ u)
    (h₂ : u ∈ t) : s ⊆ ⋃₀t :=
  subset.trans h₁ (subset_sUnion_of_mem h₂)

theorem sUnion_subset {α : Type u} {S : set (set α)} {t : set α}
    (h : ∀ (t' : set α), t' ∈ S → t' ⊆ t) : ⋃₀S ⊆ t :=
  Sup_le h

theorem sUnion_subset_iff {α : Type u} {s : set (set α)} {t : set α} :
    ⋃₀s ⊆ t ↔ ∀ (t' : set α), t' ∈ s → t' ⊆ t :=
  { mp :=
      fun (h : ⋃₀s ⊆ t) (t' : set α) (ht' : t' ∈ s) => subset.trans (subset_sUnion_of_mem ht') h,
    mpr := sUnion_subset }

theorem subset_sInter {α : Type u} {S : set (set α)} {t : set α}
    (h : ∀ (t' : set α), t' ∈ S → t ⊆ t') : t ⊆ ⋂₀S :=
  le_Inf h

theorem sUnion_subset_sUnion {α : Type u} {S : set (set α)} {T : set (set α)} (h : S ⊆ T) :
    ⋃₀S ⊆ ⋃₀T :=
  sUnion_subset fun (s : set α) (hs : s ∈ S) => subset_sUnion_of_mem (h hs)

theorem sInter_subset_sInter {α : Type u} {S : set (set α)} {T : set (set α)} (h : S ⊆ T) :
    ⋂₀T ⊆ ⋂₀S :=
  subset_sInter fun (s : set α) (hs : s ∈ S) => sInter_subset_of_mem (h hs)

@[simp] theorem sUnion_empty {α : Type u} : ⋃₀∅ = ∅ := Sup_empty

@[simp] theorem sInter_empty {α : Type u} : ⋂₀∅ = univ := Inf_empty

@[simp] theorem sUnion_singleton {α : Type u} (s : set α) : ⋃₀singleton s = s := Sup_singleton

@[simp] theorem sInter_singleton {α : Type u} (s : set α) : ⋂₀singleton s = s := Inf_singleton

@[simp] theorem sUnion_eq_empty {α : Type u} {S : set (set α)} :
    ⋃₀S = ∅ ↔ ∀ (s : set α), s ∈ S → s = ∅ :=
  Sup_eq_bot

@[simp] theorem sInter_eq_univ {α : Type u} {S : set (set α)} :
    ⋂₀S = univ ↔ ∀ (s : set α), s ∈ S → s = univ :=
  Inf_eq_top

@[simp] theorem nonempty_sUnion {α : Type u} {S : set (set α)} :
    set.nonempty (⋃₀S) ↔ ∃ (s : set α), ∃ (H : s ∈ S), set.nonempty s :=
  sorry

theorem nonempty.of_sUnion {α : Type u} {s : set (set α)} (h : set.nonempty (⋃₀s)) :
    set.nonempty s :=
  sorry

theorem nonempty.of_sUnion_eq_univ {α : Type u} [Nonempty α] {s : set (set α)} (h : ⋃₀s = univ) :
    set.nonempty s :=
  nonempty.of_sUnion (Eq.symm h ▸ univ_nonempty)

theorem sUnion_union {α : Type u} (S : set (set α)) (T : set (set α)) : ⋃₀(S ∪ T) = ⋃₀S ∪ ⋃₀T :=
  Sup_union

theorem sInter_union {α : Type u} (S : set (set α)) (T : set (set α)) : ⋂₀(S ∪ T) = ⋂₀S ∩ ⋂₀T :=
  Inf_union

theorem sInter_Union {α : Type u} {ι : Sort x} (s : ι → set (set α)) :
    (⋂₀Union fun (i : ι) => s i) = Inter fun (i : ι) => ⋂₀s i :=
  sorry

@[simp] theorem sUnion_insert {α : Type u} (s : set α) (T : set (set α)) : ⋃₀insert s T = s ∪ ⋃₀T :=
  Sup_insert

@[simp] theorem sInter_insert {α : Type u} (s : set α) (T : set (set α)) : ⋂₀insert s T = s ∩ ⋂₀T :=
  Inf_insert

theorem sUnion_pair {α : Type u} (s : set α) (t : set α) : ⋃₀insert s (singleton t) = s ∪ t :=
  Sup_pair

theorem sInter_pair {α : Type u} (s : set α) (t : set α) : ⋂₀insert s (singleton t) = s ∩ t :=
  Inf_pair

@[simp] theorem sUnion_image {α : Type u} {β : Type v} (f : α → set β) (s : set α) :
    ⋃₀(f '' s) = Union fun (x : α) => Union fun (H : x ∈ s) => f x :=
  Sup_image

@[simp] theorem sInter_image {α : Type u} {β : Type v} (f : α → set β) (s : set α) :
    ⋂₀(f '' s) = Inter fun (x : α) => Inter fun (H : x ∈ s) => f x :=
  Inf_image

@[simp] theorem sUnion_range {β : Type v} {ι : Sort x} (f : ι → set β) :
    ⋃₀range f = Union fun (x : ι) => f x :=
  rfl

@[simp] theorem sInter_range {β : Type v} {ι : Sort x} (f : ι → set β) :
    ⋂₀range f = Inter fun (x : ι) => f x :=
  rfl

theorem Union_eq_univ_iff {α : Type u} {ι : Sort x} {f : ι → set α} :
    (Union fun (i : ι) => f i) = univ ↔ ∀ (x : α), ∃ (i : ι), x ∈ f i :=
  sorry

theorem bUnion_eq_univ_iff {α : Type u} {β : Type v} {f : α → set β} {s : set α} :
    (Union fun (x : α) => Union fun (H : x ∈ s) => f x) = univ ↔
        ∀ (y : β), ∃ (x : α), ∃ (H : x ∈ s), y ∈ f x :=
  sorry

theorem sUnion_eq_univ_iff {α : Type u} {c : set (set α)} :
    ⋃₀c = univ ↔ ∀ (a : α), ∃ (b : set α), ∃ (H : b ∈ c), a ∈ b :=
  sorry

theorem compl_sUnion {α : Type u} (S : set (set α)) : ⋃₀Sᶜ = ⋂₀(compl '' S) := sorry

-- classical

theorem sUnion_eq_compl_sInter_compl {α : Type u} (S : set (set α)) : ⋃₀S = (⋂₀(compl '' S)ᶜ) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (⋃₀S = (⋂₀(compl '' S)ᶜ))) (Eq.symm (compl_compl (⋃₀S)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (⋃₀Sᶜᶜ = (⋂₀(compl '' S)ᶜ))) (compl_sUnion S)))
      (Eq.refl (⋂₀(compl '' S)ᶜ)))

-- classical

theorem compl_sInter {α : Type u} (S : set (set α)) : ⋂₀Sᶜ = ⋃₀(compl '' S) :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (⋂₀Sᶜ = ⋃₀(compl '' S))) (sUnion_eq_compl_sInter_compl (compl '' S))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (⋂₀Sᶜ = (⋂₀(compl '' (compl '' S))ᶜ))) (compl_compl_image S)))
      (Eq.refl (⋂₀Sᶜ)))

-- classical

theorem sInter_eq_comp_sUnion_compl {α : Type u} (S : set (set α)) : ⋂₀S = (⋃₀(compl '' S)ᶜ) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (⋂₀S = (⋃₀(compl '' S)ᶜ))) (Eq.symm (compl_compl (⋂₀S)))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (⋂₀Sᶜᶜ = (⋃₀(compl '' S)ᶜ))) (compl_sInter S)))
      (Eq.refl (⋃₀(compl '' S)ᶜ)))

theorem inter_empty_of_inter_sUnion_empty {α : Type u} {s : set α} {t : set α} {S : set (set α)}
    (hs : t ∈ S) (h : s ∩ ⋃₀S = ∅) : s ∩ t = ∅ :=
  eq_empty_of_subset_empty
    (eq.mpr (id (Eq._oldrec (Eq.refl (s ∩ t ⊆ ∅)) (Eq.symm h)))
      (inter_subset_inter_right s (subset_sUnion_of_mem hs)))

theorem range_sigma_eq_Union_range {α : Type u} {β : Type v} {γ : α → Type u_1} (f : sigma γ → β) :
    range f = Union fun (a : α) => range fun (b : γ a) => f (sigma.mk a b) :=
  sorry

theorem Union_eq_range_sigma {α : Type u} {β : Type v} (s : α → set β) :
    (Union fun (i : α) => s i) = range fun (a : sigma fun (i : α) => ↥(s i)) => ↑(sigma.snd a) :=
  sorry

theorem Union_image_preimage_sigma_mk_eq_self {ι : Type u_1} {σ : ι → Type u_2}
    (s : set (sigma σ)) : (Union fun (i : ι) => sigma.mk i '' (sigma.mk i ⁻¹' s)) = s :=
  sorry

theorem sUnion_mono {α : Type u} {s : set (set α)} {t : set (set α)} (h : s ⊆ t) : ⋃₀s ⊆ ⋃₀t :=
  sUnion_subset fun (t' : set α) (ht' : t' ∈ s) => subset_sUnion_of_mem (h ht')

theorem Union_subset_Union {α : Type u} {ι : Sort x} {s : ι → set α} {t : ι → set α}
    (h : ∀ (i : ι), s i ⊆ t i) : (Union fun (i : ι) => s i) ⊆ Union fun (i : ι) => t i :=
  supr_le_supr h

theorem Union_subset_Union2 {α : Type u} {ι : Sort x} {ι₂ : Sort u_1} {s : ι → set α}
    {t : ι₂ → set α} (h : ∀ (i : ι), ∃ (j : ι₂), s i ⊆ t j) :
    (Union fun (i : ι) => s i) ⊆ Union fun (i : ι₂) => t i :=
  supr_le_supr2 h

theorem Union_subset_Union_const {α : Type u} {ι : Sort x} {ι₂ : Sort x} {s : set α} (h : ι → ι₂) :
    (Union fun (i : ι) => s) ⊆ Union fun (j : ι₂) => s :=
  supr_le_supr_const h

@[simp] theorem Union_of_singleton (α : Type u) : (Union fun (x : α) => singleton x) = univ := sorry

@[simp] theorem Union_of_singleton_coe {α : Type u} (s : set α) :
    (Union fun (i : ↥s) => singleton ↑i) = s :=
  sorry

theorem bUnion_subset_Union {α : Type u} {β : Type v} (s : set α) (t : α → set β) :
    (Union fun (x : α) => Union fun (H : x ∈ s) => t x) ⊆ Union fun (x : α) => t x :=
  Union_subset_Union fun (i : α) => Union_subset fun (h : i ∈ s) => subset.refl (t i)

theorem sUnion_eq_bUnion {α : Type u} {s : set (set α)} :
    ⋃₀s = Union fun (i : set α) => Union fun (h : i ∈ s) => i :=
  sorry

theorem sInter_eq_bInter {α : Type u} {s : set (set α)} :
    ⋂₀s = Inter fun (i : set α) => Inter fun (h : i ∈ s) => i :=
  sorry

theorem sUnion_eq_Union {α : Type u} {s : set (set α)} : ⋃₀s = Union fun (i : ↥s) => ↑i := sorry

theorem sInter_eq_Inter {α : Type u} {s : set (set α)} : ⋂₀s = Inter fun (i : ↥s) => ↑i := sorry

theorem union_eq_Union {α : Type u} {s₁ : set α} {s₂ : set α} :
    s₁ ∪ s₂ = Union fun (b : Bool) => cond b s₁ s₂ :=
  sorry

theorem inter_eq_Inter {α : Type u} {s₁ : set α} {s₂ : set α} :
    s₁ ∩ s₂ = Inter fun (b : Bool) => cond b s₁ s₂ :=
  sorry

protected instance complete_boolean_algebra {α : Type u} : complete_boolean_algebra (set α) :=
  complete_boolean_algebra.mk boolean_algebra.sup boolean_algebra.le boolean_algebra.lt sorry sorry
    sorry sorry sorry sorry boolean_algebra.inf sorry sorry sorry sorry boolean_algebra.top sorry
    boolean_algebra.bot sorry compl has_sdiff.sdiff sorry sorry sorry complete_lattice.Sup
    complete_lattice.Inf sorry sorry sorry sorry sorry sorry

theorem sInter_union_sInter {α : Type u} {S : set (set α)} {T : set (set α)} :
    ⋂₀S ∪ ⋂₀T =
        Inter
          fun (p : set α × set α) => Inter fun (H : p ∈ set.prod S T) => prod.fst p ∪ prod.snd p :=
  Inf_sup_Inf

theorem sUnion_inter_sUnion {α : Type u} {s : set (set α)} {t : set (set α)} :
    ⋃₀s ∩ ⋃₀t =
        Union
          fun (p : set α × set α) => Union fun (H : p ∈ set.prod s t) => prod.fst p ∩ prod.snd p :=
  Sup_inf_Sup

/-- If `S` is a set of sets, and each `s ∈ S` can be represented as an intersection
of sets `T s hs`, then `⋂₀ S` is the intersection of the union of all `T s hs`. -/
theorem sInter_bUnion {α : Type u} {S : set (set α)} {T : (s : set α) → s ∈ S → set (set α)}
    (hT : ∀ (s : set α) (H : s ∈ S), s = ⋂₀T s H) :
    (⋂₀Union fun (s : set α) => Union fun (H : s ∈ S) => T s H) = ⋂₀S :=
  sorry

/-- If `S` is a set of sets, and each `s ∈ S` can be represented as an union
of sets `T s hs`, then `⋃₀ S` is the union of the union of all `T s hs`. -/
theorem sUnion_bUnion {α : Type u} {S : set (set α)} {T : (s : set α) → s ∈ S → set (set α)}
    (hT : ∀ (s : set α) (H : s ∈ S), s = ⋃₀T s H) :
    (⋃₀Union fun (s : set α) => Union fun (H : s ∈ S) => T s H) = ⋃₀S :=
  sorry

theorem Union_range_eq_sUnion {α : Type u_1} {β : Type u_2} (C : set (set α))
    {f : (s : ↥C) → β → ↥s} (hf : ∀ (s : ↥C), function.surjective (f s)) :
    (Union fun (y : β) => range fun (s : ↥C) => subtype.val (f s y)) = ⋃₀C :=
  sorry

theorem Union_range_eq_Union {ι : Type u_1} {α : Type u_2} {β : Type u_3} (C : ι → set α)
    {f : (x : ι) → β → ↥(C x)} (hf : ∀ (x : ι), function.surjective (f x)) :
    (Union fun (y : β) => range fun (x : ι) => subtype.val (f x y)) = Union fun (x : ι) => C x :=
  sorry

theorem union_distrib_Inter_right {α : Type u} {ι : Type u_1} (s : ι → set α) (t : set α) :
    (Inter fun (i : ι) => s i) ∪ t = Inter fun (i : ι) => s i ∪ t :=
  sorry

theorem union_distrib_Inter_left {α : Type u} {ι : Type u_1} (s : ι → set α) (t : set α) :
    (t ∪ Inter fun (i : ι) => s i) = Inter fun (i : ι) => t ∪ s i :=
  sorry

/-!
### `maps_to`
-/

theorem maps_to_sUnion {α : Type u} {β : Type v} {S : set (set α)} {t : set β} {f : α → β}
    (H : ∀ (s : set α), s ∈ S → maps_to f s t) : maps_to f (⋃₀S) t :=
  sorry

theorem maps_to_Union {α : Type u} {β : Type v} {ι : Sort x} {s : ι → set α} {t : set β} {f : α → β}
    (H : ∀ (i : ι), maps_to f (s i) t) : maps_to f (Union fun (i : ι) => s i) t :=
  maps_to_sUnion (iff.mpr forall_range_iff H)

theorem maps_to_bUnion {α : Type u} {β : Type v} {ι : Sort x} {p : ι → Prop}
    {s : (i : ι) → p i → set α} {t : set β} {f : α → β}
    (H : ∀ (i : ι) (hi : p i), maps_to f (s i hi) t) :
    maps_to f (Union fun (i : ι) => Union fun (hi : p i) => s i hi) t :=
  maps_to_Union fun (i : ι) => maps_to_Union (H i)

theorem maps_to_Union_Union {α : Type u} {β : Type v} {ι : Sort x} {s : ι → set α} {t : ι → set β}
    {f : α → β} (H : ∀ (i : ι), maps_to f (s i) (t i)) :
    maps_to f (Union fun (i : ι) => s i) (Union fun (i : ι) => t i) :=
  maps_to_Union fun (i : ι) => maps_to.mono (subset.refl (s i)) (subset_Union t i) (H i)

theorem maps_to_bUnion_bUnion {α : Type u} {β : Type v} {ι : Sort x} {p : ι → Prop}
    {s : (i : ι) → p i → set α} {t : (i : ι) → p i → set β} {f : α → β}
    (H : ∀ (i : ι) (hi : p i), maps_to f (s i hi) (t i hi)) :
    maps_to f (Union fun (i : ι) => Union fun (hi : p i) => s i hi)
        (Union fun (i : ι) => Union fun (hi : p i) => t i hi) :=
  maps_to_Union_Union fun (i : ι) => maps_to_Union_Union (H i)

theorem maps_to_sInter {α : Type u} {β : Type v} {s : set α} {T : set (set β)} {f : α → β}
    (H : ∀ (t : set β), t ∈ T → maps_to f s t) : maps_to f s (⋂₀T) :=
  fun (x : α) (hx : x ∈ s) (t : set β) (ht : t ∈ T) => H t ht hx

theorem maps_to_Inter {α : Type u} {β : Type v} {ι : Sort x} {s : set α} {t : ι → set β} {f : α → β}
    (H : ∀ (i : ι), maps_to f s (t i)) : maps_to f s (Inter fun (i : ι) => t i) :=
  fun (x : α) (hx : x ∈ s) => iff.mpr mem_Inter fun (i : ι) => H i hx

theorem maps_to_bInter {α : Type u} {β : Type v} {ι : Sort x} {p : ι → Prop} {s : set α}
    {t : (i : ι) → p i → set β} {f : α → β} (H : ∀ (i : ι) (hi : p i), maps_to f s (t i hi)) :
    maps_to f s (Inter fun (i : ι) => Inter fun (hi : p i) => t i hi) :=
  maps_to_Inter fun (i : ι) => maps_to_Inter (H i)

theorem maps_to_Inter_Inter {α : Type u} {β : Type v} {ι : Sort x} {s : ι → set α} {t : ι → set β}
    {f : α → β} (H : ∀ (i : ι), maps_to f (s i) (t i)) :
    maps_to f (Inter fun (i : ι) => s i) (Inter fun (i : ι) => t i) :=
  maps_to_Inter fun (i : ι) => maps_to.mono (Inter_subset s i) (subset.refl (t i)) (H i)

theorem maps_to_bInter_bInter {α : Type u} {β : Type v} {ι : Sort x} {p : ι → Prop}
    {s : (i : ι) → p i → set α} {t : (i : ι) → p i → set β} {f : α → β}
    (H : ∀ (i : ι) (hi : p i), maps_to f (s i hi) (t i hi)) :
    maps_to f (Inter fun (i : ι) => Inter fun (hi : p i) => s i hi)
        (Inter fun (i : ι) => Inter fun (hi : p i) => t i hi) :=
  maps_to_Inter_Inter fun (i : ι) => maps_to_Inter_Inter (H i)

theorem image_Inter_subset {α : Type u} {β : Type v} {ι : Sort x} (s : ι → set α) (f : α → β) :
    (f '' Inter fun (i : ι) => s i) ⊆ Inter fun (i : ι) => f '' s i :=
  maps_to.image_subset (maps_to_Inter_Inter fun (i : ι) => maps_to_image f (s i))

theorem image_bInter_subset {α : Type u} {β : Type v} {ι : Sort x} {p : ι → Prop}
    (s : (i : ι) → p i → set α) (f : α → β) :
    (f '' Inter fun (i : ι) => Inter fun (hi : p i) => s i hi) ⊆
        Inter fun (i : ι) => Inter fun (hi : p i) => f '' s i hi :=
  maps_to.image_subset (maps_to_bInter_bInter fun (i : ι) (hi : p i) => maps_to_image f (s i hi))

theorem image_sInter_subset {α : Type u} {β : Type v} (S : set (set α)) (f : α → β) :
    f '' ⋂₀S ⊆ Inter fun (s : set α) => Inter fun (H : s ∈ S) => f '' s :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (f '' ⋂₀S ⊆ Inter fun (s : set α) => Inter fun (H : s ∈ S) => f '' s))
        sInter_eq_bInter))
    (image_bInter_subset (fun (i : set α) (hi : i ∈ S) => i) f)

/-!
### `inj_on`
-/

theorem inj_on.image_Inter_eq {α : Type u} {β : Type v} {ι : Sort x} [Nonempty ι] {s : ι → set α}
    {f : α → β} (h : inj_on f (Union fun (i : ι) => s i)) :
    (f '' Inter fun (i : ι) => s i) = Inter fun (i : ι) => f '' s i :=
  sorry

theorem inj_on.image_bInter_eq {α : Type u} {β : Type v} {ι : Sort x} {p : ι → Prop}
    {s : (i : ι) → p i → set α} (hp : ∃ (i : ι), p i) {f : α → β}
    (h : inj_on f (Union fun (i : ι) => Union fun (hi : p i) => s i hi)) :
    (f '' Inter fun (i : ι) => Inter fun (hi : p i) => s i hi) =
        Inter fun (i : ι) => Inter fun (hi : p i) => f '' s i hi :=
  sorry

theorem inj_on_Union_of_directed {α : Type u} {β : Type v} {ι : Sort x} {s : ι → set α}
    (hs : directed has_subset.subset s) {f : α → β} (hf : ∀ (i : ι), inj_on f (s i)) :
    inj_on f (Union fun (i : ι) => s i) :=
  sorry

/-!
### `surj_on`
-/

theorem surj_on_sUnion {α : Type u} {β : Type v} {s : set α} {T : set (set β)} {f : α → β}
    (H : ∀ (t : set β), t ∈ T → surj_on f s t) : surj_on f s (⋃₀T) :=
  sorry

theorem surj_on_Union {α : Type u} {β : Type v} {ι : Sort x} {s : set α} {t : ι → set β} {f : α → β}
    (H : ∀ (i : ι), surj_on f s (t i)) : surj_on f s (Union fun (i : ι) => t i) :=
  surj_on_sUnion (iff.mpr forall_range_iff H)

theorem surj_on_Union_Union {α : Type u} {β : Type v} {ι : Sort x} {s : ι → set α} {t : ι → set β}
    {f : α → β} (H : ∀ (i : ι), surj_on f (s i) (t i)) :
    surj_on f (Union fun (i : ι) => s i) (Union fun (i : ι) => t i) :=
  surj_on_Union fun (i : ι) => surj_on.mono (subset_Union s i) (subset.refl (t i)) (H i)

theorem surj_on_bUnion {α : Type u} {β : Type v} {ι : Sort x} {p : ι → Prop} {s : set α}
    {t : (i : ι) → p i → set β} {f : α → β} (H : ∀ (i : ι) (hi : p i), surj_on f s (t i hi)) :
    surj_on f s (Union fun (i : ι) => Union fun (hi : p i) => t i hi) :=
  surj_on_Union fun (i : ι) => surj_on_Union (H i)

theorem surj_on_bUnion_bUnion {α : Type u} {β : Type v} {ι : Sort x} {p : ι → Prop}
    {s : (i : ι) → p i → set α} {t : (i : ι) → p i → set β} {f : α → β}
    (H : ∀ (i : ι) (hi : p i), surj_on f (s i hi) (t i hi)) :
    surj_on f (Union fun (i : ι) => Union fun (hi : p i) => s i hi)
        (Union fun (i : ι) => Union fun (hi : p i) => t i hi) :=
  surj_on_Union_Union fun (i : ι) => surj_on_Union_Union (H i)

theorem surj_on_Inter {α : Type u} {β : Type v} {ι : Sort x} [hi : Nonempty ι] {s : ι → set α}
    {t : set β} {f : α → β} (H : ∀ (i : ι), surj_on f (s i) t)
    (Hinj : inj_on f (Union fun (i : ι) => s i)) : surj_on f (Inter fun (i : ι) => s i) t :=
  sorry

theorem surj_on_Inter_Inter {α : Type u} {β : Type v} {ι : Sort x} [hi : Nonempty ι] {s : ι → set α}
    {t : ι → set β} {f : α → β} (H : ∀ (i : ι), surj_on f (s i) (t i))
    (Hinj : inj_on f (Union fun (i : ι) => s i)) :
    surj_on f (Inter fun (i : ι) => s i) (Inter fun (i : ι) => t i) :=
  surj_on_Inter
    (fun (i : ι) => surj_on.mono (subset.refl (s i)) (Inter_subset (fun (i : ι) => t i) i) (H i))
    Hinj

/-!
### `bij_on`
-/

theorem bij_on_Union {α : Type u} {β : Type v} {ι : Sort x} {s : ι → set α} {t : ι → set β}
    {f : α → β} (H : ∀ (i : ι), bij_on f (s i) (t i)) (Hinj : inj_on f (Union fun (i : ι) => s i)) :
    bij_on f (Union fun (i : ι) => s i) (Union fun (i : ι) => t i) :=
  { left := maps_to_Union_Union fun (i : ι) => bij_on.maps_to (H i),
    right := { left := Hinj, right := surj_on_Union_Union fun (i : ι) => bij_on.surj_on (H i) } }

theorem bij_on_Inter {α : Type u} {β : Type v} {ι : Sort x} [hi : Nonempty ι] {s : ι → set α}
    {t : ι → set β} {f : α → β} (H : ∀ (i : ι), bij_on f (s i) (t i))
    (Hinj : inj_on f (Union fun (i : ι) => s i)) :
    bij_on f (Inter fun (i : ι) => s i) (Inter fun (i : ι) => t i) :=
  sorry

theorem bij_on_Union_of_directed {α : Type u} {β : Type v} {ι : Sort x} {s : ι → set α}
    (hs : directed has_subset.subset s) {t : ι → set β} {f : α → β}
    (H : ∀ (i : ι), bij_on f (s i) (t i)) :
    bij_on f (Union fun (i : ι) => s i) (Union fun (i : ι) => t i) :=
  bij_on_Union H (inj_on_Union_of_directed hs fun (i : ι) => bij_on.inj_on (H i))

theorem bij_on_Inter_of_directed {α : Type u} {β : Type v} {ι : Sort x} [Nonempty ι] {s : ι → set α}
    (hs : directed has_subset.subset s) {t : ι → set β} {f : α → β}
    (H : ∀ (i : ι), bij_on f (s i) (t i)) :
    bij_on f (Inter fun (i : ι) => s i) (Inter fun (i : ι) => t i) :=
  bij_on_Inter H (inj_on_Union_of_directed hs fun (i : ι) => bij_on.inj_on (H i))

@[simp] theorem Inter_pos {α : Type u} {p : Prop} {μ : p → set α} (hp : p) :
    (Inter fun (h : p) => μ h) = μ hp :=
  infi_pos hp

@[simp] theorem Inter_neg {α : Type u} {p : Prop} {μ : p → set α} (hp : ¬p) :
    (Inter fun (h : p) => μ h) = univ :=
  infi_neg hp

@[simp] theorem Union_pos {α : Type u} {p : Prop} {μ : p → set α} (hp : p) :
    (Union fun (h : p) => μ h) = μ hp :=
  supr_pos hp

@[simp] theorem Union_neg {α : Type u} {p : Prop} {μ : p → set α} (hp : ¬p) :
    (Union fun (h : p) => μ h) = ∅ :=
  supr_neg hp

@[simp] theorem Union_empty {α : Type u} {ι : Sort x} : (Union fun (i : ι) => ∅) = ∅ := supr_bot

@[simp] theorem Inter_univ {α : Type u} {ι : Sort x} : (Inter fun (i : ι) => univ) = univ :=
  infi_top

@[simp] theorem Union_eq_empty {α : Type u} {ι : Sort x} {s : ι → set α} :
    (Union fun (i : ι) => s i) = ∅ ↔ ∀ (i : ι), s i = ∅ :=
  supr_eq_bot

@[simp] theorem Inter_eq_univ {α : Type u} {ι : Sort x} {s : ι → set α} :
    (Inter fun (i : ι) => s i) = univ ↔ ∀ (i : ι), s i = univ :=
  infi_eq_top

@[simp] theorem nonempty_Union {α : Type u} {ι : Sort x} {s : ι → set α} :
    set.nonempty (Union fun (i : ι) => s i) ↔ ∃ (i : ι), set.nonempty (s i) :=
  sorry

theorem image_Union {α : Type u} {β : Type v} {ι : Sort x} {f : α → β} {s : ι → set α} :
    (f '' Union fun (i : ι) => s i) = Union fun (i : ι) => f '' s i :=
  sorry

theorem univ_subtype {α : Type u} {p : α → Prop} :
    univ = Union fun (x : α) => Union fun (h : p x) => singleton { val := x, property := h } :=
  sorry

theorem range_eq_Union {α : Type u} {ι : Sort u_1} (f : ι → α) :
    range f = Union fun (i : ι) => singleton (f i) :=
  sorry

theorem image_eq_Union {α : Type u} {β : Type v} (f : α → β) (s : set α) :
    f '' s = Union fun (i : α) => Union fun (H : i ∈ s) => singleton (f i) :=
  sorry

@[simp] theorem bUnion_range {α : Type u} {β : Type v} {ι : Sort x} {f : ι → α} {g : α → set β} :
    (Union fun (x : α) => Union fun (H : x ∈ range f) => g x) = Union fun (y : ι) => g (f y) :=
  supr_range

@[simp] theorem bInter_range {α : Type u} {β : Type v} {ι : Sort x} {f : ι → α} {g : α → set β} :
    (Inter fun (x : α) => Inter fun (H : x ∈ range f) => g x) = Inter fun (y : ι) => g (f y) :=
  infi_range

@[simp] theorem bUnion_image {α : Type u} {β : Type v} {γ : Type w} {s : set γ} {f : γ → α}
    {g : α → set β} :
    (Union fun (x : α) => Union fun (H : x ∈ f '' s) => g x) =
        Union fun (y : γ) => Union fun (H : y ∈ s) => g (f y) :=
  supr_image

@[simp] theorem bInter_image {α : Type u} {β : Type v} {γ : Type w} {s : set γ} {f : γ → α}
    {g : α → set β} :
    (Inter fun (x : α) => Inter fun (H : x ∈ f '' s) => g x) =
        Inter fun (y : γ) => Inter fun (H : y ∈ s) => g (f y) :=
  infi_image

theorem Union_image_left {α : Type u} {β : Type v} {γ : Type w} (f : α → β → γ) {s : set α}
    {t : set β} : (Union fun (a : α) => Union fun (H : a ∈ s) => f a '' t) = image2 f s t :=
  sorry

theorem Union_image_right {α : Type u} {β : Type v} {γ : Type w} (f : α → β → γ) {s : set α}
    {t : set β} :
    (Union fun (b : β) => Union fun (H : b ∈ t) => (fun (a : α) => f a b) '' s) = image2 f s t :=
  sorry

theorem monotone_preimage {α : Type u} {β : Type v} {f : α → β} : monotone (preimage f) :=
  fun (a b : set β) (h : a ≤ b) => preimage_mono h

@[simp] theorem preimage_Union {α : Type u} {β : Type v} {ι : Sort w} {f : α → β} {s : ι → set β} :
    (f ⁻¹' Union fun (i : ι) => s i) = Union fun (i : ι) => f ⁻¹' s i :=
  sorry

theorem preimage_bUnion {α : Type u} {β : Type v} {ι : Type u_1} {f : α → β} {s : set ι}
    {t : ι → set β} :
    (f ⁻¹' Union fun (i : ι) => Union fun (H : i ∈ s) => t i) =
        Union fun (i : ι) => Union fun (H : i ∈ s) => f ⁻¹' t i :=
  sorry

@[simp] theorem preimage_sUnion {α : Type u} {β : Type v} {f : α → β} {s : set (set β)} :
    f ⁻¹' ⋃₀s = Union fun (t : set β) => Union fun (H : t ∈ s) => f ⁻¹' t :=
  sorry

theorem preimage_Inter {α : Type u} {β : Type v} {ι : Sort u_1} {s : ι → set β} {f : α → β} :
    (f ⁻¹' Inter fun (i : ι) => s i) = Inter fun (i : ι) => f ⁻¹' s i :=
  sorry

theorem preimage_bInter {α : Type u} {β : Type v} {γ : Type w} {s : γ → set β} {t : set γ}
    {f : α → β} :
    (f ⁻¹' Inter fun (i : γ) => Inter fun (H : i ∈ t) => s i) =
        Inter fun (i : γ) => Inter fun (H : i ∈ t) => f ⁻¹' s i :=
  sorry

@[simp] theorem bUnion_preimage_singleton {α : Type u} {β : Type v} (f : α → β) (s : set β) :
    (Union fun (y : β) => Union fun (H : y ∈ s) => f ⁻¹' singleton y) = f ⁻¹' s :=
  sorry

theorem bUnion_range_preimage_singleton {α : Type u} {β : Type v} (f : α → β) :
    (Union fun (y : β) => Union fun (H : y ∈ range f) => f ⁻¹' singleton y) = univ :=
  sorry

theorem monotone_prod {α : Type u} {β : Type v} {γ : Type w} [preorder α] {f : α → set β}
    {g : α → set γ} (hf : monotone f) (hg : monotone g) :
    monotone fun (x : α) => set.prod (f x) (g x) :=
  fun (a b : α) (h : a ≤ b) => prod_mono (hf h) (hg h)

theorem Mathlib.monotone.set_prod {α : Type u} {β : Type v} {γ : Type w} [preorder α]
    {f : α → set β} {g : α → set γ} (hf : monotone f) (hg : monotone g) :
    monotone fun (x : α) => set.prod (f x) (g x) :=
  monotone_prod

theorem prod_Union {α : Type u} {β : Type v} {ι : Sort u_1} {s : set α} {t : ι → set β} :
    set.prod s (Union fun (i : ι) => t i) = Union fun (i : ι) => set.prod s (t i) :=
  sorry

theorem prod_bUnion {α : Type u} {β : Type v} {ι : Type u_1} {u : set ι} {s : set α}
    {t : ι → set β} :
    set.prod s (Union fun (i : ι) => Union fun (H : i ∈ u) => t i) =
        Union fun (i : ι) => Union fun (H : i ∈ u) => set.prod s (t i) :=
  sorry

theorem prod_sUnion {α : Type u} {β : Type v} {s : set α} {C : set (set β)} :
    set.prod s (⋃₀C) = ⋃₀((fun (t : set β) => set.prod s t) '' C) :=
  sorry

theorem Union_prod {α : Type u} {β : Type v} {ι : Sort u_1} {s : ι → set α} {t : set β} :
    set.prod (Union fun (i : ι) => s i) t = Union fun (i : ι) => set.prod (s i) t :=
  sorry

theorem bUnion_prod {α : Type u} {β : Type v} {ι : Type u_1} {u : set ι} {s : ι → set α}
    {t : set β} :
    set.prod (Union fun (i : ι) => Union fun (H : i ∈ u) => s i) t =
        Union fun (i : ι) => Union fun (H : i ∈ u) => set.prod (s i) t :=
  sorry

theorem sUnion_prod {α : Type u} {β : Type v} {C : set (set α)} {t : set β} :
    set.prod (⋃₀C) t = ⋃₀((fun (s : set α) => set.prod s t) '' C) :=
  sorry

theorem Union_prod_of_monotone {α : Type u} {β : Type v} {γ : Type w} [semilattice_sup α]
    {s : α → set β} {t : α → set γ} (hs : monotone s) (ht : monotone t) :
    (Union fun (x : α) => set.prod (s x) (t x)) =
        set.prod (Union fun (x : α) => s x) (Union fun (x : α) => t x) :=
  sorry

/-- Given a set `s` of functions `α → β` and `t : set α`, `seq s t` is the union of `f '' t` over
all `f ∈ s`. -/
def seq {α : Type u} {β : Type v} (s : set (α → β)) (t : set α) : set β :=
  set_of fun (b : β) => ∃ (f : α → β), ∃ (H : f ∈ s), ∃ (a : α), ∃ (H : a ∈ t), f a = b

theorem seq_def {α : Type u} {β : Type v} {s : set (α → β)} {t : set α} :
    seq s t = Union fun (f : α → β) => Union fun (H : f ∈ s) => f '' t :=
  sorry

@[simp] theorem mem_seq_iff {α : Type u} {β : Type v} {s : set (α → β)} {t : set α} {b : β} :
    b ∈ seq s t ↔ ∃ (f : α → β), ∃ (H : f ∈ s), ∃ (a : α), ∃ (H : a ∈ t), f a = b :=
  iff.rfl

theorem seq_subset {α : Type u} {β : Type v} {s : set (α → β)} {t : set α} {u : set β} :
    seq s t ⊆ u ↔ ∀ (f : α → β), f ∈ s → ∀ (a : α), a ∈ t → f a ∈ u :=
  sorry

theorem seq_mono {α : Type u} {β : Type v} {s₀ : set (α → β)} {s₁ : set (α → β)} {t₀ : set α}
    {t₁ : set α} (hs : s₀ ⊆ s₁) (ht : t₀ ⊆ t₁) : seq s₀ t₀ ⊆ seq s₁ t₁ :=
  sorry

theorem singleton_seq {α : Type u} {β : Type v} {f : α → β} {t : set α} :
    seq (singleton f) t = f '' t :=
  sorry

theorem seq_singleton {α : Type u} {β : Type v} {s : set (α → β)} {a : α} :
    seq s (singleton a) = (fun (f : α → β) => f a) '' s :=
  sorry

theorem seq_seq {α : Type u} {β : Type v} {γ : Type w} {s : set (β → γ)} {t : set (α → β)}
    {u : set α} : seq s (seq t u) = seq (seq (function.comp '' s) t) u :=
  sorry

theorem image_seq {α : Type u} {β : Type v} {γ : Type w} {f : β → γ} {s : set (α → β)} {t : set α} :
    f '' seq s t = seq (function.comp f '' s) t :=
  sorry

theorem prod_eq_seq {α : Type u} {β : Type v} {s : set α} {t : set β} :
    set.prod s t = seq (Prod.mk '' s) t :=
  sorry

theorem prod_image_seq_comm {α : Type u} {β : Type v} (s : set α) (t : set β) :
    seq (Prod.mk '' s) t = seq ((fun (b : β) (a : α) => (a, b)) '' t) s :=
  sorry

theorem image2_eq_seq {α : Type u} {β : Type v} {γ : Type w} (f : α → β → γ) (s : set α)
    (t : set β) : image2 f s t = seq (f '' s) t :=
  sorry

protected instance monad : Monad set :=
  { toApplicative :=
      { toFunctor :=
          { map := fun (α β : Type u) => image,
            mapConst := fun (α β : Type u) => image ∘ function.const β },
        toPure := { pure := fun (α : Type u) (a : α) => singleton a },
        toSeq := { seq := fun (α β : Type u) => seq },
        toSeqLeft :=
          { seqLeft :=
              fun (α β : Type u) (a : set α) (b : set β) =>
                (fun (α β : Type u) => seq) β α
                  ((fun (α β : Type u) => image) α (β → α) (function.const β) a) b },
        toSeqRight :=
          { seqRight :=
              fun (α β : Type u) (a : set α) (b : set β) =>
                (fun (α β : Type u) => seq) β β
                  ((fun (α β : Type u) => image) α (β → β) (function.const α id) a) b } },
    toBind :=
      { bind :=
          fun (α β : Type u) (s : set α) (f : α → set β) =>
            Union fun (i : α) => Union fun (H : i ∈ s) => f i } }

@[simp] theorem bind_def {α' : Type u} {β' : Type u} {s : set α'} {f : α' → set β'} :
    s >>= f = Union fun (i : α') => Union fun (H : i ∈ s) => f i :=
  rfl

@[simp] theorem fmap_eq_image {α' : Type u} {β' : Type u} {s : set α'} (f : α' → β') :
    f <$> s = f '' s :=
  rfl

@[simp] theorem seq_eq_set_seq {α : Type u_1} {β : Type u_1} (s : set (α → β)) (t : set α) :
    s <*> t = seq s t :=
  rfl

@[simp] theorem pure_def {α : Type u} (a : α) : pure a = singleton a := rfl

protected instance is_lawful_monad : is_lawful_monad set := sorry

protected instance is_comm_applicative : is_comm_applicative set :=
  is_comm_applicative.mk fun (α β : Type u) (s : set α) (t : set β) => prod_image_seq_comm s t

theorem pi_def {α : Type u} {π : α → Type u_1} (i : set α) (s : (a : α) → set (π a)) :
    pi i s = Inter fun (a : α) => Inter fun (H : a ∈ i) => function.eval a ⁻¹' s a :=
  sorry

theorem pi_diff_pi_subset {α : Type u} {π : α → Type u_1} (i : set α) (s : (a : α) → set (π a))
    (t : (a : α) → set (π a)) :
    pi i s \ pi i t ⊆
        Union fun (a : α) => Union fun (H : a ∈ i) => function.eval a ⁻¹' (s a \ t a) :=
  sorry

end set

/-! ### Disjoint sets -/

namespace disjoint

/-! We define some lemmas in the `disjoint` namespace to be able to use projection notation. -/

theorem union_left {α : Type u} {s : set α} {t : set α} {u : set α} (hs : disjoint s u)
    (ht : disjoint t u) : disjoint (s ∪ t) u :=
  sup_left hs ht

theorem union_right {α : Type u} {s : set α} {t : set α} {u : set α} (ht : disjoint s t)
    (hu : disjoint s u) : disjoint s (t ∪ u) :=
  sup_right ht hu

theorem preimage {α : Type u_1} {β : Type u_2} (f : α → β) {s : set β} {t : set β}
    (h : disjoint s t) : disjoint (f ⁻¹' s) (f ⁻¹' t) :=
  fun (x : α) (hx : x ∈ f ⁻¹' s ⊓ f ⁻¹' t) => h hx

end disjoint

namespace set

protected theorem disjoint_iff {α : Type u} {s : set α} {t : set α} : disjoint s t ↔ s ∩ t ⊆ ∅ :=
  iff.rfl

theorem disjoint_iff_inter_eq_empty {α : Type u} {s : set α} {t : set α} :
    disjoint s t ↔ s ∩ t = ∅ :=
  disjoint_iff

theorem not_disjoint_iff {α : Type u} {s : set α} {t : set α} :
    ¬disjoint s t ↔ ∃ (x : α), x ∈ s ∧ x ∈ t :=
  iff.trans not_forall (exists_congr fun (x : α) => not_not)

theorem disjoint_left {α : Type u} {s : set α} {t : set α} :
    disjoint s t ↔ ∀ {a : α}, a ∈ s → ¬a ∈ t :=
  (fun (this : (∀ (x : α), ¬x ∈ s ∩ t) ↔ ∀ (a : α), a ∈ s → ¬a ∈ t) => this)
    { mp := fun (h : ∀ (x : α), ¬x ∈ s ∩ t) (a : α) => iff.mp not_and (h a),
      mpr := fun (h : ∀ (a : α), a ∈ s → ¬a ∈ t) (a : α) => iff.mpr not_and (h a) }

theorem disjoint_right {α : Type u} {s : set α} {t : set α} :
    disjoint s t ↔ ∀ {a : α}, a ∈ t → ¬a ∈ s :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (disjoint s t ↔ ∀ {a : α}, a ∈ t → ¬a ∈ s)) (propext disjoint.comm)))
    (eq.mpr
      (id (Eq._oldrec (Eq.refl (disjoint t s ↔ ∀ {a : α}, a ∈ t → ¬a ∈ s)) (propext disjoint_left)))
      (iff.refl (∀ {a : α}, a ∈ t → ¬a ∈ s)))

theorem disjoint_of_subset_left {α : Type u} {s : set α} {t : set α} {u : set α} (h : s ⊆ u)
    (d : disjoint u t) : disjoint s t :=
  disjoint.mono_left h d

theorem disjoint_of_subset_right {α : Type u} {s : set α} {t : set α} {u : set α} (h : t ⊆ u)
    (d : disjoint s u) : disjoint s t :=
  disjoint.mono_right h d

theorem disjoint_of_subset {α : Type u} {s : set α} {t : set α} {u : set α} {v : set α} (h1 : s ⊆ u)
    (h2 : t ⊆ v) (d : disjoint u v) : disjoint s t :=
  disjoint.mono h1 h2 d

@[simp] theorem disjoint_union_left {α : Type u} {s : set α} {t : set α} {u : set α} :
    disjoint (s ∪ t) u ↔ disjoint s u ∧ disjoint t u :=
  disjoint_sup_left

@[simp] theorem disjoint_union_right {α : Type u} {s : set α} {t : set α} {u : set α} :
    disjoint s (t ∪ u) ↔ disjoint s t ∧ disjoint s u :=
  disjoint_sup_right

theorem disjoint_diff {α : Type u} {a : set α} {b : set α} : disjoint a (b \ a) :=
  iff.mpr disjoint_iff (inter_diff_self a b)

@[simp] theorem disjoint_empty {α : Type u} (s : set α) : disjoint s ∅ := disjoint_bot_right

@[simp] theorem empty_disjoint {α : Type u} (s : set α) : disjoint ∅ s := disjoint_bot_left

@[simp] theorem univ_disjoint {α : Type u} {s : set α} : disjoint univ s ↔ s = ∅ := top_disjoint

@[simp] theorem disjoint_univ {α : Type u} {s : set α} : disjoint s univ ↔ s = ∅ := disjoint_top

@[simp] theorem disjoint_singleton_left {α : Type u} {a : α} {s : set α} :
    disjoint (singleton a) s ↔ ¬a ∈ s :=
  sorry

@[simp] theorem disjoint_singleton_right {α : Type u} {a : α} {s : set α} :
    disjoint s (singleton a) ↔ ¬a ∈ s :=
  eq.mpr (id (Eq._oldrec (Eq.refl (disjoint s (singleton a) ↔ ¬a ∈ s)) (propext disjoint.comm)))
    disjoint_singleton_left

theorem disjoint_image_image {α : Type u} {β : Type v} {γ : Type w} {f : β → α} {g : γ → α}
    {s : set β} {t : set γ} (h : ∀ (b : β), b ∈ s → ∀ (c : γ), c ∈ t → f b ≠ g c) :
    disjoint (f '' s) (g '' t) :=
  sorry

theorem pairwise_on_disjoint_fiber {α : Type u} {β : Type v} (f : α → β) (s : set β) :
    pairwise_on s (disjoint on fun (y : β) => f ⁻¹' singleton y) :=
  sorry

theorem preimage_eq_empty {α : Type u} {β : Type v} {f : α → β} {s : set β}
    (h : disjoint s (range f)) : f ⁻¹' s = ∅ :=
  sorry

theorem preimage_eq_empty_iff {α : Type u} {β : Type v} {f : α → β} {s : set β} :
    disjoint s (range f) ↔ f ⁻¹' s = ∅ :=
  sorry

end set

namespace set

/-- A collection of sets is `pairwise_disjoint`, if any two different sets in this collection
are disjoint.  -/
def pairwise_disjoint {α : Type u} (s : set (set α)) := pairwise_on s disjoint

theorem pairwise_disjoint.subset {α : Type u} {s : set (set α)} {t : set (set α)} (h : s ⊆ t)
    (ht : pairwise_disjoint t) : pairwise_disjoint s :=
  pairwise_on.mono h ht

theorem pairwise_disjoint.range {α : Type u} {s : set (set α)} (f : ↥s → set α)
    (hf : ∀ (x : ↥s), f x ⊆ subtype.val x) (ht : pairwise_disjoint s) :
    pairwise_disjoint (range f) :=
  sorry

/- classical -/

theorem pairwise_disjoint.elim {α : Type u} {s : set (set α)} (h : pairwise_disjoint s) {x : set α}
    {y : set α} (hx : x ∈ s) (hy : y ∈ s) (z : α) (hzx : z ∈ x) (hzy : z ∈ y) : x = y :=
  iff.mp not_not fun (h' : ¬x = y) => h x hx y hy h' { left := hzx, right := hzy }

end set

namespace set

theorem subset_diff {α : Type u} {s : set α} {t : set α} {u : set α} :
    s ⊆ t \ u ↔ s ⊆ t ∧ disjoint s u :=
  sorry

/-- If `t` is an indexed family of sets, then there is a natural map from `Σ i, t i` to `⋃ i, t i`
sending `⟨i, x⟩` to `x`. -/
def sigma_to_Union {α : Type u} {β : Type v} (t : α → set β) (x : sigma fun (i : α) => ↥(t i)) :
    ↥(Union fun (i : α) => t i) :=
  { val := ↑(sigma.snd x), property := sorry }

theorem sigma_to_Union_surjective {α : Type u} {β : Type v} (t : α → set β) :
    function.surjective (sigma_to_Union t) :=
  sorry

theorem sigma_to_Union_injective {α : Type u} {β : Type v} (t : α → set β)
    (h : ∀ (i j : α), i ≠ j → disjoint (t i) (t j)) : function.injective (sigma_to_Union t) :=
  sorry

theorem sigma_to_Union_bijective {α : Type u} {β : Type v} (t : α → set β)
    (h : ∀ (i j : α), i ≠ j → disjoint (t i) (t j)) : function.bijective (sigma_to_Union t) :=
  { left := sigma_to_Union_injective t h, right := sigma_to_Union_surjective t }

/-- Equivalence between a disjoint union and a dependent sum. -/
def Union_eq_sigma_of_disjoint {α : Type u} {β : Type v} {t : α → set β}
    (h : ∀ (i j : α), i ≠ j → disjoint (t i) (t j)) :
    ↥(Union fun (i : α) => t i) ≃ sigma fun (i : α) => ↥(t i) :=
  equiv.symm (equiv.of_bijective (sigma_to_Union t) (sigma_to_Union_bijective t h))

/-- Equivalence between a disjoint bounded union and a dependent sum. -/
def bUnion_eq_sigma_of_disjoint {α : Type u} {β : Type v} {s : set α} {t : α → set β}
    (h : pairwise_on s (disjoint on t)) :
    ↥(Union fun (i : α) => Union fun (H : i ∈ s) => t i) ≃
        sigma fun (i : ↥s) => ↥(t (subtype.val i)) :=
  equiv.trans (equiv.set_congr sorry) (Union_eq_sigma_of_disjoint sorry)

end Mathlib


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Yury G. Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.logic.function.basic
import Mathlib.PostPort

universes u_1 u_2 u v w x u_3 u_4

namespace Mathlib

/-!
# More theorems about the sum type
-/

/-- Check if a sum is `inl` and if so, retrieve its contents. -/
@[simp] def sum.get_left {α : Type u_1} {β : Type u_2} : α ⊕ β → Option α := sorry

/-- Check if a sum is `inr` and if so, retrieve its contents. -/
@[simp] def sum.get_right {α : Type u_1} {β : Type u_2} : α ⊕ β → Option β := sorry

/-- Check if a sum is `inl`. -/
@[simp] def sum.is_left {α : Type u_1} {β : Type u_2} : α ⊕ β → Bool := sorry

/-- Check if a sum is `inr`. -/
@[simp] def sum.is_right {α : Type u_1} {β : Type u_2} : α ⊕ β → Bool := sorry

protected instance sum.decidable_eq (α : Type u) [a : DecidableEq α] (β : Type v) :
    [a : DecidableEq β] → DecidableEq (α ⊕ β) :=
  sorry

@[simp] theorem sum.forall {α : Type u} {β : Type v} {p : α ⊕ β → Prop} :
    (∀ (x : α ⊕ β), p x) ↔ (∀ (a : α), p (sum.inl a)) ∧ ∀ (b : β), p (sum.inr b) :=
  sorry

@[simp] theorem sum.exists {α : Type u} {β : Type v} {p : α ⊕ β → Prop} :
    (∃ (x : α ⊕ β), p x) ↔ (∃ (a : α), p (sum.inl a)) ∨ ∃ (b : β), p (sum.inr b) :=
  sorry

namespace sum

theorem injective_inl {α : Type u} {β : Type v} : function.injective inl := fun (x y : α) => inl.inj

theorem injective_inr {α : Type u} {β : Type v} : function.injective inr := fun (x y : β) => inr.inj

/-- Map `α ⊕ β` to `α' ⊕ β'` sending `α` to `α'` and `β` to `β'`. -/
protected def map {α : Type u} {α' : Type w} {β : Type v} {β' : Type x} (f : α → α') (g : β → β') :
    α ⊕ β → α' ⊕ β' :=
  sorry

@[simp] theorem map_inl {α : Type u} {α' : Type w} {β : Type v} {β' : Type x} (f : α → α')
    (g : β → β') (x : α) : sum.map f g (inl x) = inl (f x) :=
  rfl

@[simp] theorem map_inr {α : Type u} {α' : Type w} {β : Type v} {β' : Type x} (f : α → α')
    (g : β → β') (x : β) : sum.map f g (inr x) = inr (g x) :=
  rfl

@[simp] theorem map_map {α : Type u} {α' : Type w} {β : Type v} {β' : Type x} {α'' : Type u_1}
    {β'' : Type u_2} (f' : α' → α'') (g' : β' → β'') (f : α → α') (g : β → β') (x : α ⊕ β) :
    sum.map f' g' (sum.map f g x) = sum.map (f' ∘ f) (g' ∘ g) x :=
  sum.cases_on x
    (fun (x : α) =>
      idRhs (sum.map f' g' (sum.map f g (inl x)) = sum.map f' g' (sum.map f g (inl x))) rfl)
    fun (x : β) =>
      idRhs (sum.map f' g' (sum.map f g (inr x)) = sum.map f' g' (sum.map f g (inr x))) rfl

@[simp] theorem map_comp_map {α : Type u} {α' : Type w} {β : Type v} {β' : Type x} {α'' : Type u_1}
    {β'' : Type u_2} (f' : α' → α'') (g' : β' → β'') (f : α → α') (g : β → β') :
    sum.map f' g' ∘ sum.map f g = sum.map (f' ∘ f) (g' ∘ g) :=
  funext (map_map f' g' f g)

@[simp] theorem map_id_id (α : Type u_1) (β : Type u_2) : sum.map id id = id :=
  funext fun (x : α ⊕ β) => sum.rec_on x (fun (_x : α) => rfl) fun (_x : β) => rfl

theorem inl.inj_iff {α : Type u} {β : Type v} {a : α} {b : α} : inl a = inl b ↔ a = b :=
  { mp := inl.inj, mpr := congr_arg fun {a : α} => inl a }

theorem inr.inj_iff {α : Type u} {β : Type v} {a : β} {b : β} : inr a = inr b ↔ a = b :=
  { mp := inr.inj, mpr := congr_arg fun {a : β} => inr a }

theorem inl_ne_inr {α : Type u} {β : Type v} {a : α} {b : β} : inl a ≠ inr b :=
  fun (ᾰ : inl a = inr b) =>
    eq.dcases_on ᾰ (fun (H_1 : inr b = inl a) => sum.no_confusion H_1) (Eq.refl (inr b))
      (HEq.refl ᾰ)

theorem inr_ne_inl {α : Type u} {β : Type v} {a : α} {b : β} : inr b ≠ inl a :=
  fun (ᾰ : inr b = inl a) =>
    eq.dcases_on ᾰ (fun (H_1 : inl a = inr b) => sum.no_confusion H_1) (Eq.refl (inl a))
      (HEq.refl ᾰ)

/-- Define a function on `α ⊕ β` by giving separate definitions on `α` and `β`. -/
protected def elim {α : Type u_1} {β : Type u_2} {γ : Sort u_3} (f : α → γ) (g : β → γ) :
    α ⊕ β → γ :=
  fun (x : α ⊕ β) => sum.rec_on x f g

@[simp] theorem elim_inl {α : Type u_1} {β : Type u_2} {γ : Sort u_3} (f : α → γ) (g : β → γ)
    (x : α) : sum.elim f g (inl x) = f x :=
  rfl

@[simp] theorem elim_inr {α : Type u_1} {β : Type u_2} {γ : Sort u_3} (f : α → γ) (g : β → γ)
    (x : β) : sum.elim f g (inr x) = g x :=
  rfl

@[simp] theorem elim_comp_inl {α : Type u_1} {β : Type u_2} {γ : Sort u_3} (f : α → γ) (g : β → γ) :
    sum.elim f g ∘ inl = f :=
  rfl

@[simp] theorem elim_comp_inr {α : Type u_1} {β : Type u_2} {γ : Sort u_3} (f : α → γ) (g : β → γ) :
    sum.elim f g ∘ inr = g :=
  rfl

@[simp] theorem elim_inl_inr {α : Type u_1} {β : Type u_2} : sum.elim inl inr = id :=
  funext fun (x : α ⊕ β) => sum.cases_on x (fun (_x : α) => rfl) fun (_x : β) => rfl

theorem comp_elim {α : Type u_1} {β : Type u_2} {γ : Sort u_3} {δ : Sort u_4} (f : γ → δ)
    (g : α → γ) (h : β → γ) : f ∘ sum.elim g h = sum.elim (f ∘ g) (f ∘ h) :=
  funext fun (x : α ⊕ β) => sum.cases_on x (fun (_x : α) => rfl) fun (_x : β) => rfl

@[simp] theorem elim_comp_inl_inr {α : Type u_1} {β : Type u_2} {γ : Sort u_3} (f : α ⊕ β → γ) :
    sum.elim (f ∘ inl) (f ∘ inr) = f :=
  funext fun (x : α ⊕ β) => sum.cases_on x (fun (_x : α) => rfl) fun (_x : β) => rfl

@[simp] theorem update_elim_inl {α : Type u_1} {β : Type u_2} {γ : Sort u_3} [DecidableEq α]
    [DecidableEq (α ⊕ β)] {f : α → γ} {g : β → γ} {i : α} {x : γ} :
    function.update (sum.elim f g) (inl i) x = sum.elim (function.update f i x) g :=
  sorry

@[simp] theorem update_elim_inr {α : Type u_1} {β : Type u_2} {γ : Sort u_3} [DecidableEq β]
    [DecidableEq (α ⊕ β)] {f : α → γ} {g : β → γ} {i : β} {x : γ} :
    function.update (sum.elim f g) (inr i) x = sum.elim f (function.update g i x) :=
  sorry

@[simp] theorem update_inl_comp_inl {α : Type u_1} {β : Type u_2} {γ : Sort u_3} [DecidableEq α]
    [DecidableEq (α ⊕ β)] {f : α ⊕ β → γ} {i : α} {x : γ} :
    function.update f (inl i) x ∘ inl = function.update (f ∘ inl) i x :=
  function.update_comp_eq_of_injective f injective_inl i x

@[simp] theorem update_inl_apply_inl {α : Type u_1} {β : Type u_2} {γ : Sort u_3} [DecidableEq α]
    [DecidableEq (α ⊕ β)] {f : α ⊕ β → γ} {i : α} {j : α} {x : γ} :
    function.update f (inl i) x (inl j) = function.update (f ∘ inl) i x j :=
  sorry

@[simp] theorem update_inl_comp_inr {α : Type u_1} {β : Type u_2} {γ : Sort u_3}
    [DecidableEq (α ⊕ β)] {f : α ⊕ β → γ} {i : α} {x : γ} :
    function.update f (inl i) x ∘ inr = f ∘ inr :=
  function.update_comp_eq_of_forall_ne f x fun (_x : β) => inr_ne_inl

@[simp] theorem update_inl_apply_inr {α : Type u_1} {β : Type u_2} {γ : Sort u_3}
    [DecidableEq (α ⊕ β)] {f : α ⊕ β → γ} {i : α} {j : β} {x : γ} :
    function.update f (inl i) x (inr j) = f (inr j) :=
  function.update_noteq inr_ne_inl x f

@[simp] theorem update_inr_comp_inl {α : Type u_1} {β : Type u_2} {γ : Sort u_3}
    [DecidableEq (α ⊕ β)] {f : α ⊕ β → γ} {i : β} {x : γ} :
    function.update f (inr i) x ∘ inl = f ∘ inl :=
  function.update_comp_eq_of_forall_ne f x fun (_x : α) => inl_ne_inr

@[simp] theorem update_inr_apply_inl {α : Type u_1} {β : Type u_2} {γ : Sort u_3}
    [DecidableEq (α ⊕ β)] {f : α ⊕ β → γ} {i : α} {j : β} {x : γ} :
    function.update f (inr j) x (inl i) = f (inl i) :=
  function.update_noteq inl_ne_inr x f

@[simp] theorem update_inr_comp_inr {α : Type u_1} {β : Type u_2} {γ : Sort u_3} [DecidableEq β]
    [DecidableEq (α ⊕ β)] {f : α ⊕ β → γ} {i : β} {x : γ} :
    function.update f (inr i) x ∘ inr = function.update (f ∘ inr) i x :=
  function.update_comp_eq_of_injective f injective_inr i x

@[simp] theorem update_inr_apply_inr {α : Type u_1} {β : Type u_2} {γ : Sort u_3} [DecidableEq β]
    [DecidableEq (α ⊕ β)] {f : α ⊕ β → γ} {i : β} {j : β} {x : γ} :
    function.update f (inr i) x (inr j) = function.update (f ∘ inr) i x j :=
  sorry

inductive lex {α : Type u} {β : Type v} (ra : α → α → Prop) (rb : β → β → Prop) :
    α ⊕ β → α ⊕ β → Prop
    where
| inl : ∀ {a₁ a₂ : α}, ra a₁ a₂ → lex ra rb (inl a₁) (inl a₂)
| inr : ∀ {b₁ b₂ : β}, rb b₁ b₂ → lex ra rb (inr b₁) (inr b₂)
| sep : ∀ (a : α) (b : β), lex ra rb (inl a) (inr b)

@[simp] theorem lex_inl_inl {α : Type u} {β : Type v} {ra : α → α → Prop} {rb : β → β → Prop}
    {a₁ : α} {a₂ : α} : lex ra rb (inl a₁) (inl a₂) ↔ ra a₁ a₂ :=
  sorry

@[simp] theorem lex_inr_inr {α : Type u} {β : Type v} {ra : α → α → Prop} {rb : β → β → Prop}
    {b₁ : β} {b₂ : β} : lex ra rb (inr b₁) (inr b₂) ↔ rb b₁ b₂ :=
  sorry

@[simp] theorem lex_inr_inl {α : Type u} {β : Type v} {ra : α → α → Prop} {rb : β → β → Prop}
    {b : β} {a : α} : ¬lex ra rb (inr b) (inl a) :=
  sorry

theorem lex_acc_inl {α : Type u} {β : Type v} {ra : α → α → Prop} {rb : β → β → Prop} {a : α}
    (aca : acc ra a) : acc (lex ra rb) (inl a) :=
  sorry

theorem lex_acc_inr {α : Type u} {β : Type v} {ra : α → α → Prop} {rb : β → β → Prop}
    (aca : ∀ (a : α), acc (lex ra rb) (inl a)) {b : β} (acb : acc rb b) : acc (lex ra rb) (inr b) :=
  sorry

theorem lex_wf {α : Type u} {β : Type v} {ra : α → α → Prop} {rb : β → β → Prop}
    (ha : well_founded ra) (hb : well_founded rb) : well_founded (lex ra rb) :=
  (fun (aca : ∀ (a : α), acc (lex ra rb) (inl a)) =>
      well_founded.intro
        fun (x : α ⊕ β) =>
          sum.rec_on x aca fun (b : β) => lex_acc_inr aca (well_founded.apply hb b))
    fun (a : α) => lex_acc_inl (well_founded.apply ha a)

/-- Swap the factors of a sum type -/
@[simp] def swap {α : Type u} {β : Type v} : α ⊕ β → β ⊕ α := sorry

@[simp] theorem swap_swap {α : Type u} {β : Type v} (x : α ⊕ β) : swap (swap x) = x :=
  sum.cases_on x (fun (x : α) => Eq.refl (swap (swap (inl x))))
    fun (x : β) => Eq.refl (swap (swap (inr x)))

@[simp] theorem swap_swap_eq {α : Type u} {β : Type v} : swap ∘ swap = id := funext swap_swap

@[simp] theorem swap_left_inverse {α : Type u} {β : Type v} : function.left_inverse swap swap :=
  swap_swap

@[simp] theorem swap_right_inverse {α : Type u} {β : Type v} : function.right_inverse swap swap :=
  swap_swap

end sum

namespace function

theorem injective.sum_elim {α : Type u} {β : Type v} {γ : Sort u_1} {f : α → γ} {g : β → γ}
    (hf : injective f) (hg : injective g) (hfg : ∀ (a : α) (b : β), f a ≠ g b) :
    injective (sum.elim f g) :=
  sorry

theorem injective.sum_map {α : Type u} {α' : Type w} {β : Type v} {β' : Type x} {f : α → β}
    {g : α' → β'} (hf : injective f) (hg : injective g) : injective (sum.map f g) :=
  sorry

theorem surjective.sum_map {α : Type u} {α' : Type w} {β : Type v} {β' : Type x} {f : α → β}
    {g : α' → β'} (hf : surjective f) (hg : surjective g) : surjective (sum.map f g) :=
  sorry

end Mathlib


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

import tactic.rcases

/-!
# lift tactic

This file defines the `lift` tactic, allowing the user to lift elements from one type to another
under a specified condition.

## Tags

lift, tactic
-/

/-- A class specifying that you can lift elements from `α` to `β` assuming `cond` is true.
  Used by the tactic `lift`. -/
class can_lift (α β : Sort*) (coe : out_param $ β → α) (cond : out_param $ α → Prop) :=
(prf : ∀(x : α), cond x → ∃(y : β), coe y = x)

instance : can_lift ℤ ℕ coe ((≤) 0) :=
⟨λ n hn, ⟨n.nat_abs, int.nat_abs_of_nonneg hn⟩⟩

/-- Enable automatic handling of pi types in `can_lift`. -/
instance pi.can_lift (ι : Sort*) (α β : ι → Sort*)
  (coe : Π i, β i → α i) (P : Π i, α i → Prop)
  [Π i : ι, can_lift (α i) (β i) (coe i) (P i)] :
  can_lift (Π i : ι, α i) (Π i : ι, β i) (λ f i, coe i (f i)) (λ f, ∀ i, P i (f i)) :=
{ prf := λ f hf, ⟨λ i, classical.some (can_lift.prf (f i) (hf i)), funext $ λ i,
    classical.some_spec (can_lift.prf (f i) (hf i))⟩ }

lemma subtype.exists_pi_extension {ι : Sort*} {α : ι → Sort*} [ne : Π i, nonempty (α i)]
  {p : ι → Prop} (f : Π i : subtype p, α i) :
  ∃ g : Π i : ι, α i, (λ i : subtype p, g i) = f :=
begin
  tactic.classical,
  refine ⟨λ i, if hi : p i then f ⟨i, hi⟩ else classical.choice (ne i), funext _⟩,
  rintro ⟨i, hi⟩,
  exact dif_pos hi
end

instance pi_subtype.can_lift (ι : Sort*) (α : ι → Sort*) [ne : Π i, nonempty (α i)]
  (p : ι → Prop) :
  can_lift (Π i : subtype p, α i) (Π i, α i) (λ f i, f i) (λ _, true) :=
{ prf := λ f _, subtype.exists_pi_extension f }

instance pi_subtype.can_lift' (ι : Sort*) (α : Sort*) [ne : nonempty α] (p : ι → Prop) :
  can_lift (subtype p → α) (ι → α) (λ f i, f i) (λ _, true) :=
pi_subtype.can_lift ι (λ _, α) p

instance subtype.can_lift {α : Sort*} (p : α → Prop) : can_lift α {x // p x} coe p :=
{ prf := λ a ha, ⟨⟨a, ha⟩, rfl⟩ }

open tactic

namespace tactic

/--
Construct the proof of `cond x` in the lift tactic.
*  `e` is the expression being lifted and `h` is the specified proof of `can_lift.cond e`.
*  `old_tp` and `new_tp` are the arguments to `can_lift` and `inst` is the `can_lift`-instance.
*  `s` and `to_unfold` contain the information of the simp set used to simplify.

If the proof was specified, we check whether it has the correct type.
If it doesn't have the correct type, we display an error message.

If the proof was not specified, we create assert it as a local constant.
(The name of this local constant doesn't matter, since `lift` will remove it from the context.)
-/
meta def get_lift_prf (h : option pexpr) (e P : expr) : tactic (expr × bool) := do
  let expected_prf_ty := P.app e,
  expected_prf_ty ← simp_lemmas.mk.dsimplify [] expected_prf_ty {fail_if_unchanged := ff},
  match h with
  | some h := do
      e ← decorate_error "lift tactic failed." (i_to_expr ``((%%h : %%expected_prf_ty))),
      return (e, tt)
  | none   := do
      prf_nm ← get_unused_name,
      prf ← assert prf_nm expected_prf_ty,
      swap,
      return (prf, ff)
  end

/-- Lift the expression `p` to the type `t`, with proof obligation given by `h`.
  The list `n` is used for the two newly generated names, and to specify whether `h` should
  remain in the local context. See the doc string of `tactic.interactive.lift` for more information.
  -/
meta def lift (p : pexpr) (t : pexpr) (h : option pexpr) (n : list name) : tactic unit :=
do
  propositional_goal <|>
    fail "lift tactic failed. Tactic is only applicable when the target is a proposition.",
  e ← i_to_expr p,
  old_tp ← infer_type e,
  new_tp ← i_to_expr ``(%%t : Sort*),
  coe ← i_to_expr (``(%%new_tp → %%old_tp)) >>= mk_meta_var,
  P ← i_to_expr (``(%%old_tp → Prop)) >>= mk_meta_var,
  inst_type ← mk_app ``can_lift [old_tp, new_tp, coe, P],
  inst ← mk_instance inst_type <|>
    pformat!"Failed to find a lift from {old_tp} to {new_tp}. Provide an instance of\n  {inst_type}"
    >>= fail,
  inst ← instantiate_mvars inst,
  coe ← instantiate_mvars coe,
  P ← instantiate_mvars P,
  (prf_cond, b) ← get_lift_prf h e P,
  let prf_nm := if prf_cond.is_local_constant then some prf_cond.local_pp_name else none,
  /- We use mk_mapp to apply `can_lift.prf` to all but one argument, and then just use expr.app
  for the last argument. For some reason we get an error when applying mk_mapp it to all
  arguments. -/
  prf_ex0 ← mk_mapp `can_lift.prf [old_tp, new_tp, coe, P, inst, e],
  let prf_ex := prf_ex0 prf_cond,
  /- Find the name of the new variable -/
  new_nm ← if n ≠ [] then return n.head
    else if e.is_local_constant then return e.local_pp_name
    else get_unused_name,
  /- Find the name of the proof of the equation -/
  eq_nm ← if hn : 1 < n.length then return (n.nth_le 1 hn)
    else if e.is_local_constant then return `rfl
    else get_unused_name `h,
  /- We add the proof of the existential statement to the context -/
  temp_nm ← get_unused_name,
  temp_e ← note temp_nm none prf_ex,
  dsimp_hyp temp_e none [] { fail_if_unchanged := ff },
  /- We case on the existential. We use `rcases` because `eq_nm` could be `rfl`. -/
  rcases none (pexpr.of_expr temp_e) $ rcases_patt.tuple ([new_nm, eq_nm].map rcases_patt.one),
  /- If the lifted variable is not a local constant,
    try to rewrite it away using the new equality. -/
  when (¬ e.is_local_constant) (get_local eq_nm >>=
    λ e, interactive.rw ⟨[⟨⟨0, 0⟩, tt, (pexpr.of_expr e)⟩], none⟩ interactive.loc.wildcard),
  /- If the proof `prf_cond` is a local constant, remove it from the context,
    unless `n` specifies to keep it. -/
  if h_prf_nm : prf_nm.is_some ∧ n.nth 2 ≠ prf_nm then
    get_local (option.get h_prf_nm.1) >>= clear else skip,
  if b then skip else swap

setup_tactic_parser

/-- Parses an optional token "using" followed by a trailing `pexpr`. -/
meta def using_texpr := (tk "using" *> texpr)?

/-- Parses a token "to" followed by a trailing `pexpr`. -/
meta def to_texpr := (tk "to" *> texpr)

namespace interactive

/--
Lift an expression to another type.
* Usage: `'lift' expr 'to' expr ('using' expr)? ('with' id (id id?)?)?`.
* If `n : ℤ` and `hn : n ≥ 0` then the tactic `lift n to ℕ using hn` creates a new
  constant of type `ℕ`, also named `n` and replaces all occurrences of the old variable `(n : ℤ)`
  with `↑n` (where `n` in the new variable). It will remove `n` and `hn` from the context.
  + So for example the tactic `lift n to ℕ using hn` transforms the goal
    `n : ℤ, hn : n ≥ 0, h : P n ⊢ n = 3` to `n : ℕ, h : P ↑n ⊢ ↑n = 3`
    (here `P` is some term of type `ℤ → Prop`).
* The argument `using hn` is optional, the tactic `lift n to ℕ` does the same, but also creates a
  new subgoal that `n ≥ 0` (where `n` is the old variable).
  This subgoal will be placed at the top of the goal list.
  + So for example the tactic `lift n to ℕ` transforms the goal
    `n : ℤ, h : P n ⊢ n = 3` to two goals
    `n : ℤ, h : P n ⊢ n ≥ 0` and `n : ℕ, h : P ↑n ⊢ ↑n = 3`.
* You can also use `lift n to ℕ using e` where `e` is any expression of type `n ≥ 0`.
* Use `lift n to ℕ with k` to specify the name of the new variable.
* Use `lift n to ℕ with k hk` to also specify the name of the equality `↑k = n`. In this case, `n`
  will remain in the context. You can use `rfl` for the name of `hk` to substitute `n` away
  (i.e. the default behavior).
* You can also use `lift e to ℕ with k hk` where `e` is any expression of type `ℤ`.
  In this case, the `hk` will always stay in the context, but it will be used to rewrite `e` in
  all hypotheses and the target.
  + So for example the tactic `lift n + 3 to ℕ using hn with k hk` transforms the goal
    `n : ℤ, hn : n + 3 ≥ 0, h : P (n + 3) ⊢ n + 3 = 2 * n` to the goal
    `n : ℤ, k : ℕ, hk : ↑k = n + 3, h : P ↑k ⊢ ↑k = 2 * n`.
* The tactic `lift n to ℕ using h` will remove `h` from the context. If you want to keep it,
  specify it again as the third argument to `with`, like this: `lift n to ℕ using h with n rfl h`.
* More generally, this can lift an expression from `α` to `β` assuming that there is an instance
  of `can_lift α β`. In this case the proof obligation is specified by `can_lift.cond`.
* Given an instance `can_lift β γ`, it can also lift `α → β` to `α → γ`; more generally, given
  `β : Π a : α, Type*`, `γ : Π a : α, Type*`, and `[Π a : α, can_lift (β a) (γ a)]`, it
  automatically generates an instance `can_lift (Π a, β a) (Π a, γ a)`.

`lift` is in some sense dual to the `zify` tactic. `lift (z : ℤ) to ℕ` will change the type of an
integer `z` (in the supertype) to `ℕ` (the subtype), given a proof that `z ≥ 0`;
propositions concerning `z` will still be over `ℤ`. `zify` changes propositions about `ℕ` (the
subtype) to propositions about `ℤ` (the supertype), without changing the type of any variable.
-/
meta def lift (p : parse texpr) (t : parse to_texpr) (h : parse using_texpr)
  (n : parse with_ident_list) : tactic unit :=
tactic.lift p t h n

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

end interactive
end tactic


import Lean

open Lean Parser.Tactic in
macro "rwa " rws:rwRuleSeq loc:(location)? : tactic =>
  `(tactic| (rw $rws:rwRuleSeq $[$loc:location]?; assumption))

open Lean Meta Elab Term in
elab "unsafe " t:term : term <= expectedType => do
  let mut t ← elabTerm t expectedType
  t ← instantiateMVars t
  if t.hasExprMVar then
    synthesizeSyntheticMVarsNoPostponing
    t ← instantiateMVars t
  if ← logUnassignedUsingErrorInfos (← getMVars t) then throwAbortTerm
  t ← mkAuxDefinitionFor (← mkAuxName `unsafe) t
  let Expr.const unsafeFn unsafeLvls .. := t.getAppFn | unreachable!
  let ConstantInfo.defnInfo unsafeDefn ← getConstInfo unsafeFn | unreachable!
  let implName ← mkAuxName `impl
  addDecl <| Declaration.defnDecl {
    name := implName
    type := unsafeDefn.type
    levelParams := unsafeDefn.levelParams
    value := ← mkOfNonempty unsafeDefn.type
    hints := ReducibilityHints.opaque
    safety := DefinitionSafety.safe
  }
  setImplementedBy implName unsafeFn
  pure $ mkAppN (mkConst implName unsafeLvls) t.getAppArgs

namespace Nat

theorem div_eq_sub_div (h₁ : 0 < b) (h₂ : b ≤ a) : a / b = (a - b) / b + 1 := by
 rw [div_eq a, if_pos]; constructor <;> assumption

@[simp] theorem add_div_right (x : Nat) {z : Nat} (H : 0 < z) : (x + z) / z = succ (x / z) := by
  rw [div_eq_sub_div H (Nat.le_add_left _ _), Nat.add_sub_cancel]

theorem add_mul_div_left (x z : Nat) {y : Nat} (H : 0 < y) : (x + y * z) / y = x / y + z := by
  induction z with
  | zero => rw [Nat.mul_zero, Nat.add_zero, Nat.add_zero]
  | succ z ih => rw [mul_succ, ← Nat.add_assoc, add_div_right _ H, ih]; rfl

theorem add_mul_div_right (x y : Nat) {z : Nat} (H : 0 < z) : (x + y * z) / z = x / z + y := by
  rw [Nat.mul_comm, add_mul_div_left _ _ H]

@[simp] protected theorem zero_div (b : Nat) : 0 / b = 0 :=
  (div_eq 0 b).trans <| if_neg <| And.rec Nat.not_le_of_gt

protected theorem mul_div_cancel (m : Nat) {n : Nat} (H : 0 < n) : m * n / n = m := by
  let t := add_mul_div_right 0 m H
  rwa [Nat.zero_add, Nat.zero_div, Nat.zero_add] at t

end Nat

variable {p q : α → Prop}

@[simp] theorem forall_exists_index {q : (∃ x, p x) → Prop} :
    (∀ h, q h) ↔ ∀ x (h : p x), q ⟨x, h⟩ :=
  ⟨fun h x hpx => h ⟨x, hpx⟩, fun h ⟨x, hpx⟩ => h x hpx⟩

theorem exists_imp : ((∃ x, p x) → b) ↔ ∀ x, p x → b := forall_exists_index

@[simp] theorem not_exists: (¬∃ x, p x) ↔ ∀ x, ¬p x := exists_imp

namespace Option

@[simp] theorem isSome_none : @isSome α none = false := rfl

@[simp] theorem isSome_some : isSome (some a) = true := rfl

theorem isSome_iff_exists : isSome x ↔ ∃ a, x = some a := by
  cases x <;> simp [isSome]
  exists ‹_›

end Option

namespace List

theorem eq_nil_of_length_eq_zero (_ : length l = 0) : l = [] := match l with | [] => rfl

theorem length_eq_zero : length l = 0 ↔ l = [] :=
  ⟨eq_nil_of_length_eq_zero, fun h => h ▸ rfl⟩

end List

@[inline] def decidable_of_iff (a : Prop) (h : a ↔ b) [Decidable a] : Decidable b :=
  decidable_of_decidable_of_iff h

@[inline] def decidable_of_iff' (b : Prop) (h : a ↔ b) [Decidable b] : Decidable a :=
  decidable_of_decidable_of_iff h.symm


/-! # FPL category

From *Basic Category Theory for Computer Scientists* by **Benjamin C. Pierce**, page 7-8.
-/

--- Objects.
inductive O
| int
| real
| bool
| unit
deriving Inhabited, BEq, Repr

--- Arrows.
inductive Arrow : (_dom _cod : O) → Type
  -- identity
  | id : {α : O} → Arrow α α

  -- `α → α` arrows
  | unit : Arrow O.unit O.unit
  | not : Arrow O.bool O.bool
  | succᵢ : Arrow O.int O.int
  | succᵣ : Arrow O.real O.real

  -- plain composition, don't create this directly, use `Arrow.compose` instead
  | comp {α β γ} : Arrow β γ → Arrow α β → Arrow α γ

  -- `unit → bool`
  | tru : Arrow O.unit O.bool
  | fls : Arrow O.unit O.bool
  -- `unit → int`
  | zero : Arrow O.unit O.int
  -- `int → bool`
  | isZero : Arrow O.int O.bool
  -- `int → real`
  | toReal : Arrow O.int O.real
deriving Repr

def Arrow.compose₁ {α β γ : O} :
  Arrow β γ
  → Arrow α β
  → Arrow α γ
| id, g => g
| f, id => f
| comp f₁ f₂, g => comp f₁ (comp f₂ g)
| f, g => comp f g
#print Arrow.compose₁
-- def Arrow.compose₁ : {α β γ : O} → Arrow β γ → Arrow α β → Arrow α γ :=
-- fun {α β γ} x x_1 =>
--   match β, γ, x, x_1 with
--   | β, .(β), Arrow.id, g => g
--   | .(α), γ, f, Arrow.id => f
--   | β, γ, Arrow.comp f₁ f₂, g => Arrow.comp f₁ (Arrow.comp f₂ g)
--   | β, γ, f, g => Arrow.comp f g
#eval Arrow.compose₁ Arrow.unit Arrow.id
-- Arrow.comp (Arrow.unit) (Arrow.id)

def Arrow.compose₂
  : {α β γ : O}
  → Arrow β γ
  → Arrow α β
  → Arrow α γ
| α, β, .(β), id, g => g
| α, .(α), γ, f, id => f
| α, β, γ, comp f₁ f₂, g => comp f₁ (comp f₂ g)
| α, β, γ, f, g => comp f g
#print Arrow.compose₂
-- def Arrow.compose₂ : {α β γ : O} → Arrow β γ → Arrow α β → Arrow α γ :=
-- fun x x_1 x_2 x_3 x_4 =>
--   match x, x_1, x_2, x_3, x_4 with
--   | α, β, .(β), Arrow.id, g => g
--   | α, .(α), γ, f, Arrow.id => f
--   | α, β, γ, Arrow.comp f₁ f₂, g => Arrow.comp f₁ (Arrow.comp f₂ g)
--   | α, β, γ, f, g => Arrow.comp f g
#eval Arrow.compose₂ Arrow.unit Arrow.id
-- Arrow.comp (Arrow.unit) (Arrow.id)

theorem Arrow.id_compose₁
  {α β : O} (f : Arrow α β)
  : Arrow.compose₁ id g = g :=
  by
    simp only [compose₁]

theorem Arrow.compose₁_id
  {α β : O} (f : Arrow α β)
  : @Arrow.compose₁ α α β f id = f :=
  by
    cases f
    <;> simp [compose₁]
    <;> sorry

theorem Arrow.id_compose : Arrow.compose₁ id g = g :=
  by
    simp only [compose₁]
theorem Arrow.compose_id : Arrow.compose₁ f id = f :=
  by
    simp only [compose₁]
-- failed to generate equality theorems for `match` expression `Arrow.compose₁.match_1`
-- case unit
-- motive : (β γ : O) → Arrow β γ → Arrow O.unit β → Sort u_1
-- h_1 : (β : O) → (g : Arrow O.unit β) → motive β β id g
-- h_2 : (γ : O) → (f : Arrow O.unit γ) → motive O.unit γ f id
-- h_3 : (β γ β_1 : O) → (f₁ : Arrow β_1 γ) → (f₂ : Arrow β β_1) → (g : Arrow O.unit β) → motive β γ (comp f₁ f₂) g
-- h_4 : (β γ : O) → (f : Arrow β γ) → (g : Arrow O.unit β) → motive β γ f g
-- : O.unit = O.unit → HEq unit id → False
-- ⊢ h_4 O.unit O.unit unit id = h_2 O.unit unit


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import logic.equiv.basic

/-!
# Functions functorial with respect to equivalences

An `equiv_functor` is a function from `Type → Type` equipped with the additional data of
coherently mapping equivalences to equivalences.

In categorical language, it is an endofunctor of the "core" of the category `Type`.
-/

universes u₀ u₁ u₂ v₀ v₁ v₂

open function

/--
An `equiv_functor` is only functorial with respect to equivalences.

To construct an `equiv_functor`, it suffices to supply just the function `f α → f β` from
an equivalence `α ≃ β`, and then prove the functor laws. It's then a consequence that
this function is part of an equivalence, provided by `equiv_functor.map_equiv`.
-/
class equiv_functor (f : Type u₀ → Type u₁) :=
(map : Π {α β}, (α ≃ β) → (f α → f β))
(map_refl' : Π α, map (equiv.refl α) = @id (f α) . obviously)
(map_trans' : Π {α β γ} (k : α ≃ β) (h : β ≃ γ),
  map (k.trans h) = (map h) ∘ (map k) . obviously)

restate_axiom equiv_functor.map_refl'
restate_axiom equiv_functor.map_trans'
attribute [simp] equiv_functor.map_refl

namespace equiv_functor

section
variables (f : Type u₀ → Type u₁) [equiv_functor f] {α β : Type u₀} (e : α ≃ β)

/-- An `equiv_functor` in fact takes every equiv to an equiv. -/
def map_equiv :
  f α ≃ f β :=
{ to_fun := equiv_functor.map e,
  inv_fun := equiv_functor.map e.symm,
  left_inv := λ x, by { convert (congr_fun (equiv_functor.map_trans e e.symm) x).symm, simp, },
  right_inv := λ y, by { convert (congr_fun (equiv_functor.map_trans e.symm e) y).symm, simp, }, }

@[simp] lemma map_equiv_apply (x : f α) :
  map_equiv f e x = equiv_functor.map e x := rfl

lemma map_equiv_symm_apply (y : f β) :
  (map_equiv f e).symm y = equiv_functor.map e.symm y := rfl

@[simp] lemma map_equiv_refl (α) :
  map_equiv f (equiv.refl α) = equiv.refl (f α) :=
by simpa [equiv_functor.map_equiv]

@[simp] lemma map_equiv_symm :
  (map_equiv f e).symm = map_equiv f e.symm :=
equiv.ext $ map_equiv_symm_apply f e

/--
The composition of `map_equiv`s is carried over the `equiv_functor`.
For plain `functor`s, this lemma is named `map_map` when applied
or `map_comp_map` when not applied.
-/
@[simp] lemma map_equiv_trans {γ : Type u₀} (ab : α ≃ β) (bc : β ≃ γ) :
  (map_equiv f ab).trans (map_equiv f bc) = map_equiv f (ab.trans bc) :=
equiv.ext $ λ x, by simp [map_equiv, map_trans']

end

@[priority 100]
instance of_is_lawful_functor
  (f : Type u₀ → Type u₁) [functor f] [is_lawful_functor f] : equiv_functor f :=
{ map := λ α β e, functor.map e,
  map_refl' := λ α, by { ext, apply is_lawful_functor.id_map, },
  map_trans' := λ α β γ k h, by { ext x, apply (is_lawful_functor.comp_map k h x), } }

lemma map_equiv.injective
  (f : Type u₀ → Type u₁) [applicative f] [is_lawful_applicative f] {α β : Type u₀}
  (h : ∀ γ, function.injective (pure : γ → f γ)) :
  function.injective (@equiv_functor.map_equiv f _ α β) :=
λ e₁ e₂ H, equiv.ext $ λ x, h β (by simpa [equiv_functor.map] using equiv.congr_fun H (pure x))

end equiv_functor


/-
Copyright (c) 2018 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.equiv.basic
import Mathlib.algebra.field
import Mathlib.algebra.module.default
import Mathlib.algebra.algebra.basic
import Mathlib.algebra.group.type_tags
import Mathlib.ring_theory.ideal.basic
import Mathlib.PostPort

universes u v u_1 u_2

namespace Mathlib

/-!
# Transfer algebraic structures across `equiv`s

In this file we prove theorems of the following form: if `β` has a
group structure and `α ≃ β` then `α` has a group structure, and
similarly for monoids, semigroups, rings, integral domains, fields and
so on.

Note that most of these constructions can also be obtained using the `transport` tactic.

## Tags

equiv, group, ring, field, module, algebra
-/

namespace equiv

/-- Transfer `has_one` across an `equiv` -/
protected def has_zero {α : Type u} {β : Type v} (e : α ≃ β) [HasZero β] : HasZero α :=
  { zero := coe_fn (equiv.symm e) 0 }

theorem zero_def {α : Type u} {β : Type v} (e : α ≃ β) [HasZero β] : 0 = coe_fn (equiv.symm e) 0 :=
  rfl

/-- Transfer `has_mul` across an `equiv` -/
protected def has_add {α : Type u} {β : Type v} (e : α ≃ β) [Add β] : Add α :=
  { add := fun (x y : α) => coe_fn (equiv.symm e) (coe_fn e x + coe_fn e y) }

theorem add_def {α : Type u} {β : Type v} (e : α ≃ β) [Add β] (x : α) (y : α) :
    x + y = coe_fn (equiv.symm e) (coe_fn e x + coe_fn e y) :=
  rfl

/-- Transfer `has_div` across an `equiv` -/
protected def has_sub {α : Type u} {β : Type v} (e : α ≃ β) [Sub β] : Sub α :=
  { sub := fun (x y : α) => coe_fn (equiv.symm e) (coe_fn e x - coe_fn e y) }

theorem div_def {α : Type u} {β : Type v} (e : α ≃ β) [Div β] (x : α) (y : α) :
    x / y = coe_fn (equiv.symm e) (coe_fn e x / coe_fn e y) :=
  rfl

/-- Transfer `has_inv` across an `equiv` -/
protected def has_inv {α : Type u} {β : Type v} (e : α ≃ β) [has_inv β] : has_inv α :=
  has_inv.mk fun (x : α) => coe_fn (equiv.symm e) (coe_fn e x⁻¹)

theorem neg_def {α : Type u} {β : Type v} (e : α ≃ β) [Neg β] (x : α) :
    -x = coe_fn (equiv.symm e) (-coe_fn e x) :=
  rfl

/-- Transfer `has_scalar` across an `equiv` -/
protected def has_scalar {α : Type u} {β : Type v} (e : α ≃ β) {R : Type u_1} [has_scalar R β] :
    has_scalar R α :=
  has_scalar.mk fun (r : R) (x : α) => coe_fn (equiv.symm e) (r • coe_fn e x)

theorem smul_def {α : Type u} {β : Type v} (e : α ≃ β) {R : Type u_1} [has_scalar R β] (r : R)
    (x : α) : r • x = coe_fn (equiv.symm e) (r • coe_fn e x) :=
  rfl

/--
An equivalence `e : α ≃ β` gives a multiplicative equivalence `α ≃* β`
where the multiplicative structure on `α` is
the one obtained by transporting a multiplicative structure on `β` back along `e`.
-/
def mul_equiv {α : Type u} {β : Type v} (e : α ≃ β) [Mul β] :
    let _inst : Mul α := equiv.has_mul e;
      α ≃* β :=
  let _inst : Mul α := equiv.has_mul e;
  mul_equiv.mk (to_fun e) (inv_fun e) (left_inv e) (right_inv e) sorry

@[simp] theorem mul_equiv_apply {α : Type u} {β : Type v} (e : α ≃ β) [Mul β] (a : α) :
    coe_fn (mul_equiv e) a = coe_fn e a :=
  rfl

theorem mul_equiv_symm_apply {α : Type u} {β : Type v} (e : α ≃ β) [Mul β] (b : β) :
    coe_fn (mul_equiv.symm (mul_equiv e)) b = coe_fn (equiv.symm e) b :=
  Eq.refl (coe_fn (mul_equiv.symm (mul_equiv e)) b)

/--
An equivalence `e : α ≃ β` gives a ring equivalence `α ≃+* β`
where the ring structure on `α` is
the one obtained by transporting a ring structure on `β` back along `e`.
-/
def ring_equiv {α : Type u} {β : Type v} (e : α ≃ β) [Add β] [Mul β] :
    let _inst : Add α := equiv.has_add e;
      let _inst_3 : Mul α := equiv.has_mul e;
      α ≃+* β :=
  let _inst : Add α := equiv.has_add e;
  let _inst_3 : Mul α := equiv.has_mul e;
  ring_equiv.mk (to_fun e) (inv_fun e) (left_inv e) (right_inv e) sorry sorry

@[simp] theorem ring_equiv_apply {α : Type u} {β : Type v} (e : α ≃ β) [Add β] [Mul β] (a : α) :
    coe_fn (ring_equiv e) a = coe_fn e a :=
  rfl

theorem ring_equiv_symm_apply {α : Type u} {β : Type v} (e : α ≃ β) [Add β] [Mul β] (b : β) :
    coe_fn (ring_equiv.symm (ring_equiv e)) b = coe_fn (equiv.symm e) b :=
  Eq.refl (coe_fn (ring_equiv.symm (ring_equiv e)) b)

/-- Transfer `semigroup` across an `equiv` -/
protected def semigroup {α : Type u} {β : Type v} (e : α ≃ β) [semigroup β] : semigroup α :=
  semigroup.mk Mul.mul sorry

/-- Transfer `comm_semigroup` across an `equiv` -/
protected def comm_semigroup {α : Type u} {β : Type v} (e : α ≃ β) [comm_semigroup β] :
    comm_semigroup α :=
  comm_semigroup.mk semigroup.mul sorry sorry

/-- Transfer `monoid` across an `equiv` -/
protected def monoid {α : Type u} {β : Type v} (e : α ≃ β) [monoid β] : monoid α :=
  monoid.mk semigroup.mul sorry 1 sorry sorry

/-- Transfer `comm_monoid` across an `equiv` -/
protected def add_comm_monoid {α : Type u} {β : Type v} (e : α ≃ β) [add_comm_monoid β] :
    add_comm_monoid α :=
  add_comm_monoid.mk add_comm_semigroup.add sorry add_monoid.zero sorry sorry sorry

/-- Transfer `group` across an `equiv` -/
protected def group {α : Type u} {β : Type v} (e : α ≃ β) [group β] : group α :=
  group.mk monoid.mul sorry monoid.one sorry sorry has_inv.inv Div.div sorry

/-- Transfer `comm_group` across an `equiv` -/
protected def comm_group {α : Type u} {β : Type v} (e : α ≃ β) [comm_group β] : comm_group α :=
  comm_group.mk group.mul sorry group.one sorry sorry group.inv group.div sorry sorry

/-- Transfer `semiring` across an `equiv` -/
protected def semiring {α : Type u} {β : Type v} (e : α ≃ β) [semiring β] : semiring α :=
  semiring.mk Add.add sorry 0 sorry sorry sorry Mul.mul sorry monoid.one sorry sorry sorry sorry
    sorry sorry

/-- Transfer `comm_semiring` across an `equiv` -/
protected def comm_semiring {α : Type u} {β : Type v} (e : α ≃ β) [comm_semiring β] :
    comm_semiring α :=
  comm_semiring.mk semiring.add sorry semiring.zero sorry sorry sorry semiring.mul sorry
    semiring.one sorry sorry sorry sorry sorry sorry sorry

/-- Transfer `ring` across an `equiv` -/
protected def ring {α : Type u} {β : Type v} (e : α ≃ β) [ring β] : ring α :=
  ring.mk semiring.add sorry semiring.zero sorry sorry add_comm_group.neg add_comm_group.sub sorry
    sorry semiring.mul sorry semiring.one sorry sorry sorry sorry

/-- Transfer `comm_ring` across an `equiv` -/
protected def comm_ring {α : Type u} {β : Type v} (e : α ≃ β) [comm_ring β] : comm_ring α :=
  comm_ring.mk ring.add sorry ring.zero sorry sorry ring.neg ring.sub sorry sorry comm_monoid.mul
    sorry comm_monoid.one sorry sorry sorry sorry sorry

/-- Transfer `nonzero` across an `equiv` -/
protected theorem nontrivial {α : Type u} {β : Type v} (e : α ≃ β) [nontrivial β] : nontrivial α :=
  sorry

/-- Transfer `domain` across an `equiv` -/
protected def domain {α : Type u} {β : Type v} (e : α ≃ β) [domain β] : domain α :=
  domain.mk ring.add sorry ring.zero sorry sorry ring.neg ring.sub sorry sorry ring.mul sorry
    ring.one sorry sorry sorry sorry sorry sorry

/-- Transfer `integral_domain` across an `equiv` -/
protected def integral_domain {α : Type u} {β : Type v} (e : α ≃ β) [integral_domain β] :
    integral_domain α :=
  integral_domain.mk domain.add sorry domain.zero sorry sorry domain.neg domain.sub sorry sorry
    domain.mul sorry domain.one sorry sorry sorry sorry sorry sorry sorry

/-- Transfer `division_ring` across an `equiv` -/
protected def division_ring {α : Type u} {β : Type v} (e : α ≃ β) [division_ring β] :
    division_ring α :=
  division_ring.mk domain.add sorry 0 sorry sorry domain.neg domain.sub sorry sorry domain.mul sorry
    1 sorry sorry sorry sorry has_inv.inv Div.div sorry sorry sorry

/-- Transfer `field` across an `equiv` -/
protected def field {α : Type u} {β : Type v} (e : α ≃ β) [field β] : field α :=
  field.mk integral_domain.add sorry integral_domain.zero sorry sorry integral_domain.neg
    integral_domain.sub sorry sorry integral_domain.mul sorry integral_domain.one sorry sorry sorry
    sorry sorry division_ring.inv sorry sorry sorry

/-- Transfer `mul_action` across an `equiv` -/
protected def mul_action {α : Type u} {β : Type v} (R : Type u_1) [monoid R] (e : α ≃ β)
    [mul_action R β] : mul_action R α :=
  mul_action.mk sorry sorry

/-- Transfer `distrib_mul_action` across an `equiv` -/
protected def distrib_mul_action {α : Type u} {β : Type v} (R : Type u_1) [monoid R] (e : α ≃ β)
    [add_comm_monoid β] :
    let _inst : add_comm_monoid α := equiv.add_comm_monoid e;
      [_inst_3 : distrib_mul_action R β] → distrib_mul_action R α :=
  fun (_inst_3 : distrib_mul_action R β) =>
    let _inst_4 : add_comm_monoid α := equiv.add_comm_monoid e;
    distrib_mul_action.mk sorry sorry

/-- Transfer `semimodule` across an `equiv` -/
protected def semimodule {α : Type u} {β : Type v} (R : Type u_1) [semiring R] (e : α ≃ β)
    [add_comm_monoid β] :
    let _inst : add_comm_monoid α := equiv.add_comm_monoid e;
      [_inst_3 : semimodule R β] → semimodule R α :=
  let _inst : add_comm_monoid α := equiv.add_comm_monoid e;
  fun (_inst_3 : semimodule R β) => semimodule.mk sorry sorry

/--
An equivalence `e : α ≃ β` gives a linear equivalence `α ≃ₗ[R] β`
where the `R`-module structure on `α` is
the one obtained by transporting an `R`-module structure on `β` back along `e`.
-/
def linear_equiv {α : Type u} {β : Type v} (R : Type u_1) [semiring R] (e : α ≃ β)
    [add_comm_monoid β] [semimodule R β] :
    let _inst : add_comm_monoid α := equiv.add_comm_monoid e;
      let _inst_4 : semimodule R α := equiv.semimodule R e;
      linear_equiv R α β :=
  let _inst : add_comm_monoid α := equiv.add_comm_monoid e;
  let _inst_4 : semimodule R α := equiv.semimodule R e;
  linear_equiv.mk (add_equiv.to_fun (add_equiv e)) sorry sorry (add_equiv.inv_fun (add_equiv e))
    sorry sorry

/-- Transfer `algebra` across an `equiv` -/
protected def algebra {α : Type u} {β : Type v} (R : Type u_1) [comm_semiring R] (e : α ≃ β)
    [semiring β] :
    let _inst : semiring α := equiv.semiring e;
      [_inst_3 : algebra R β] → algebra R α :=
  let _inst : semiring α := equiv.semiring e;
  fun (_inst_3 : algebra R β) =>
    ring_hom.to_algebra' (ring_hom.comp (↑(ring_equiv.symm (ring_equiv e))) (algebra_map R β)) sorry

/--
An equivalence `e : α ≃ β` gives an algebra equivalence `α ≃ₐ[R] β`
where the `R`-algebra structure on `α` is
the one obtained by transporting an `R`-algebra structure on `β` back along `e`.
-/
def alg_equiv {α : Type u} {β : Type v} (R : Type u_1) [comm_semiring R] (e : α ≃ β) [semiring β]
    [algebra R β] :
    let _inst : semiring α := equiv.semiring e;
      let _inst_4 : algebra R α := equiv.algebra R e;
      alg_equiv R α β :=
  let _inst : semiring α := equiv.semiring e;
  let _inst_4 : algebra R α := equiv.algebra R e;
  alg_equiv.mk (ring_equiv.to_fun (ring_equiv e)) (ring_equiv.inv_fun (ring_equiv e)) sorry sorry
    sorry sorry sorry

end equiv

namespace ring_equiv

protected theorem local_ring {A : Type u_1} {B : Type u_2} [comm_ring A] [local_ring A]
    [comm_ring B] (e : A ≃+* B) : local_ring B :=
  local_of_surjective (↑e) (equiv.surjective (to_equiv e))

end Mathlib