import data.fin.basic
import data.nat.basic
import data.real.basic
import data.multiset.basic
import algebra.group.basic
import .dists

variables {n m : ℕ} {α β : Type*} [decidable_eq α] [decidable_eq β] (f : α → β)

def clamp (ub : ℕ) (v : ℕ) : fin (ub + 1) := fin.clamp v ub

@[simp] def clamp_vec (s : multiset ℕ) (ub : ℕ) : multiset (fin (ub + 1))
  := s.map (clamp ub)

theorem clamp_stability (s₁ s₂ : multiset ℕ) (ub : ℕ) :
  (clamp_vec s₁ ub) |Δ| (clamp_vec s₂ ub) ≤ s₁ |Δ| s₂
:= multiset.map_sym_dist_le_sym_dist  _ _ _

theorem is_equal_stability (s₁ s₂ : multiset α) (v : α) :
  (s₁.map (eq v)) |Δ| (s₂.map (eq v)) ≤ s₁ |Δ| s₂
:= multiset.map_sym_dist_le_sym_dist _ _ _


import lambda_calculus.utlc.basic
import lambda_calculus.utlc.reduction
import lambda_calculus.utlc.beta.basic
import logic.relation

namespace lambda_calculus
namespace utlc
namespace β
namespace parallel

local notation a `[` b `:=` c  `]` : 70 := has_substitution.substitution a b c

section
def step : utlc → utlc → Prop
| (↓n) := λ x, x = (↓n)
| (Λ f) := λ x, ∃ g, step f g ∧ x = Λ g
| (f·f') := λ x,
  (∃ g g', step f g ∧ step f' g' ∧ x = g·g') ∨
  ∃ g g', step f (Λ g) ∧  step f' g' ∧ x = g[0:=g']

instance : has_parallel_reduction utlc := ⟨ step ⟩

@[simp] theorem down_step_down_iff {n m: ℕ}: (↓n:utlc) →∥ ↓m ↔ n = m :=
by simp [has_parallel_reduction.step, step, eq_comm]

@[simp] theorem not_down_step_lambda (n: ℕ) (f: utlc): ¬ (↓n:utlc) →∥ Λ f :=
by simp [has_parallel_reduction.step, step]

@[simp] theorem not_down_step_dot (n: ℕ) (f g: utlc): ¬ (↓n:utlc) →∥ f·g :=
by simp [has_parallel_reduction.step, step]

@[simp] theorem lambda_step_lambda_iff {f g: utlc}: (Λ f) →∥ (Λ g) ↔ f →∥ g :=
by simp [has_parallel_reduction.step, step]

@[simp] theorem not_lambda_step_down (f: utlc) (n: ℕ): ¬ Λ f →∥ ↓n :=
by simp [has_parallel_reduction.step, step]

@[simp] theorem not_lambda_step_dot (f g g': utlc): ¬ Λ f →∥ g·g' :=
by simp [has_parallel_reduction.step, step]

theorem dot_step_dot {f f' g g': utlc}: f →∥ f' → g →∥ g' → (f·g) →∥ (f'·g') :=
begin
  simp only [has_parallel_reduction.step, step],
  exact λ p q, or.inl ⟨f', g', p, q, rfl⟩,
end

theorem dot_step_substitution {f f' g g': utlc} (x: utlc): f →∥ f' → g →∥ g' → f' = Λ x → f·g →∥ x[0:=g'] :=
begin
  simp only [has_parallel_reduction.step, step],
  intros p q fx,
  rw [fx] at p,
  refine or.inr ⟨x, g', p, q, rfl⟩
end

theorem dot_step_substitution' {f f' g g' y: utlc} (x: utlc): f →∥ f' → g →∥ g' → f' = Λ x → y = x[0:=g'] → f·g →∥ y :=
begin
  simp only [has_parallel_reduction.step, step],
  intros p q fx fy,
  rw [fx] at p,
  rw [fy],
  refine or.inr ⟨x, g', p, q, rfl⟩
end

theorem lambda_dot_step_substitution {f f' g g': utlc}: f →∥ f' → g →∥ g' → (Λ f)·g →∥ f'[0:=g'] :=
begin
  simp only [has_parallel_reduction.step, step],
  intros p q,
  exact or.inr ⟨f', g', ⟨f', p, rfl⟩, q, rfl⟩,
end

theorem dot_step_iff {f f' g: utlc}: f·f' →∥ g ↔
  (∃ x y, f →∥ x ∧ f' →∥ y ∧ g = x·y ) ∨ (∃ x y,  f →∥ (Λ x) ∧ f' →∥ y ∧ g = x[0:=y]) :=
by  simp [has_parallel_reduction.step, step]

@[simp]
theorem step_notation {f g: utlc}: step f g ↔ f →∥ g := by refl
end -- shouldn't need to use has_parallel_reduction.step below here

@[simp] theorem down_step_down {n m: ℕ}: n = m → (↓n:utlc) →∥ ↓m :=
by simp

theorem lambda_step_lambda {f g: utlc}: f →∥ g → (Λ f) →∥ (Λ g) :=
by simp

theorem dot_step_cases {f f' g: utlc}: f·f' →∥ g →
  (∃ x y, f →∥ x ∧ f' →∥ y ∧ g = x·y ) ∨ (∃ x y,  f →∥ (Λ x) ∧ f' →∥ y ∧ g = x[0:=y]) :=
  dot_step_iff.mp

@[refl]
theorem step_refl (f : utlc): f →∥ f :=
begin
  induction f using lambda_calculus.utlc.notation_induction_on,
  { exact down_step_down rfl },
  { exact lambda_step_lambda f_ih },
  { exact dot_step_dot f_ih_f f_ih_g }
end

@[simp] theorem down_step_iff {n: ℕ} {g: utlc}: (↓n:utlc) →∥ g ↔ ↓n = g :=
by cases g; simp

@[simp] theorem down_dot_step (n: ℕ) {f g: utlc}: (↓n·f) →∥ g ↔
  (∃ x, f →∥ x ∧ g = ↓n·x ) :=
by simp [dot_step_iff]

theorem dot_dot_step {a b c g: utlc}: (a·b·c) →∥ g →
  (∃ x y, (a·b) →∥ x ∧ c →∥ y ∧ x·y →∥ g ) :=
begin
  intro h,
  have h := dot_step_cases h,
  cases h with h h,
  { rcases h with ⟨x, y, habx, hcy, hgxy⟩,
    refine ⟨x, y, habx, hcy, _⟩,
    rw [hgxy] },
  rcases h with ⟨x, y, habx, hcy, hgxy⟩,
  refine ⟨Λ x, y, habx, hcy, _⟩,
  rw [hgxy],
  apply lambda_dot_step_substitution (step_refl _) (step_refl _),
end

theorem dot_dot_step' {a b c g: utlc}: (a·b·c) →∥ g →
  (∃ x, (a·b) →∥ x ∧ x·c →∥ g ) :=
begin
  intro h,
  have h := dot_step_cases h,
  cases h with h h,
  { rcases h with ⟨x, y, habx, hcy, hgxy⟩,
    rw [hgxy],
    refine ⟨x, habx, dot_step_dot (step_refl _) hcy⟩ },
  rcases h with ⟨x, y, habx, hcy, hgxy⟩,
  rw [hgxy],
  refine ⟨ Λ x, habx, lambda_dot_step_substitution (step_refl _) hcy⟩,
end

theorem shift_step_shift {f g: utlc} (n: ℕ): f →∥ g → f ↑¹ n →∥ g ↑¹ n :=
begin
  induction f using lambda_calculus.utlc.notation_induction_on generalizing g n,
  { cases g; simp [down_shift] },
  { cases g,
    { simp },
    { simpa using f_ih (n+1) },
    { simp } },
  { rw [dot_step_iff, dot_shift],
    intro h,
    cases h;
    rcases h with ⟨x, y, hf, hg, h⟩; rw [h],
    { rw [dot_shift x],
      apply dot_step_dot (f_ih_f _ hf) (f_ih_g _ hg) },
    { rw [← substitution_shift_zero],
      apply dot_step_substitution _ (f_ih_f _ hf) (f_ih_g _ hg),
      simp }
  }
end

theorem substitution_step_substitution_succ {f f' g g': utlc} {n : ℕ}: f →∥ f' → g →∥ g' → g'.uses 0 = 0 → f[n+1:=g] →∥ f'[n+1:=g'] :=
begin
  induction f generalizing f' g g' n,
  { intros hf hg _,
    simp at hf,
    rw [← hf],
    simp,
    split_ifs,
    any_goals { refl },
    assumption },
  { simp,
    cases f',
    all_goals { simp },
    intros hf hg ug',
    apply f_ih hf (shift_step_shift _ hg) (shift_uses_self _ _) },
  simp,
  intros hf hg ug',
  cases dot_step_cases hf with h h,
  all_goals { rcases h with ⟨x, y, hfx, hfy, hfxy⟩,  rw [hfxy] },
  any_goals {
    rw [dot_substitution],
    apply dot_step_dot },
  any_goals {
    rw [substitution_dist_lt (nat.zero_lt_succ _)],
    apply dot_step_substitution },
  any_goals { apply f_ih_f },
  any_goals { apply f_ih_g },
  any_goals { apply hfx },
  any_goals { apply hfy },
  any_goals { apply hg },
  any_goals { apply ug' },
  simp,
end

theorem substitution_step_substitution {f f' g g': utlc}: f →∥ f' → g →∥ g' → f[0:=g] →∥ f'[0:=g'] :=
begin
  induction f generalizing f' g g',
  all_goals { simp },
  { intro hf,
    simp [← hf],
    split_ifs,
    all_goals { simp } },
  { cases f',
    all_goals { simp },
    intros hf hg,
    exact substitution_step_substitution_succ hf (shift_step_shift _ hg) (shift_uses_self _ _) },
  intros hf hg,
  cases dot_step_cases hf with h h,
  all_goals { rcases h with ⟨x, y, hfx, hfy, hfxy⟩,  rw [hfxy] },
  rw [dot_substitution],
  apply dot_step_dot,
  any_goals {
    rw [substitution_dist_eq],
    apply dot_step_substitution },
  any_goals { apply f_ih_f hfx hg },
  any_goals { apply f_ih_g hfy hg },
  simp,
end

theorem step_church_rosser {a b c :utlc}: a →∥ b → a →∥ c → ∃ d, b →∥ d ∧ c →∥ d :=
begin
  induction a generalizing b c,
  all_goals { simp },
  { intros hab hac,
    use ↓a,
    rw [←hab, ←hac],
    exact ⟨by refl, by refl⟩},
  { cases b,
    all_goals { simp },
    cases c,
    all_goals { simp },
    intros hab hac,
    cases a_ih hab hac with d ih,
    use Λ d,
    simp [ih] },
  { intros hab hac,
    cases dot_step_cases hab with hab hab,
    all_goals { cases dot_step_cases hac with hac hac },
    all_goals { rcases hab with ⟨f, g, haf, hag, hbfg⟩ },
    all_goals { rcases hac with ⟨m, n, ham, han, hcmn⟩ },
    all_goals {
      rcases a_ih_f haf ham with ⟨x, hfx, hmx⟩,
      rcases a_ih_g hag han with ⟨y, hgy, hny⟩ },
    all_goals { rw [hbfg, hcmn] },
    { use x·y,
      refine ⟨dot_step_dot hfx hgy, dot_step_dot hmx hny⟩ },
    all_goals {
      cases x,
      all_goals { simp at hfx hmx },
      any_goals { contradiction },
      use x[0:=y] },
    { refine ⟨dot_step_substitution _ hfx hgy rfl, substitution_step_substitution hmx hny⟩ },
    { refine ⟨substitution_step_substitution hfx hgy, dot_step_substitution _ hmx hny rfl⟩ },
    { refine ⟨substitution_step_substitution hfx hgy, substitution_step_substitution hmx hny⟩ } }
end

theorem church_rosser {a b c : utlc} (hab : a ↠∥ b) (hac : a ↠∥ c)
  : b ≡∥ c :=
begin
  refine relation.church_rosser _ hab hac,
  intros x y z xy xz,
  rcases step_church_rosser xy xz with ⟨d, yd, zd⟩,
  exact ⟨d, relation.refl_gen.single yd, relation.refl_trans_gen.single zd⟩
end
end parallel

end β
end utlc
end lambda_calculus


/-
Copyright (c) 2021 Yaël Dillies, Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies, Bhavik Mehta
-/
import .increment
import analysis.special_functions.pow
import order.iterate

/-!
# Szemerédi's Regularity Lemma

In this file, we prove Szemerédi's Regularity Lemma.
-/

open_locale classical
open finpartition finset fintype function

variables {α : Type*} [fintype α] {P : finpartition (univ : finset α)} (hP : P.is_equipartition)
  (G : simple_graph α) (ε : ℝ) (l : ℕ)

/-- The size of the equipartition by which we start blowing. -/
noncomputable def initial_bound : ℕ := max 7 $ max l (⌊real.log (100/ε^5) / real.log 4⌋₊ + 1)

lemma le_initial_bound : l ≤ initial_bound ε l := (le_max_left _ _).trans (le_max_right _ _)
lemma seven_le_initial_bound : 7 ≤ initial_bound ε l := le_max_left _ _
lemma initial_bound_pos : 0 < initial_bound ε l :=
nat.succ_pos'.trans_le $ seven_le_initial_bound _ _

lemma const_lt_pow_initial_bound_mul {ε : ℝ} (hε : 0 < ε) (l : ℕ) :
  100 < 4^initial_bound ε l * ε^5 :=
begin
  rw [←real.rpow_nat_cast 4, ←div_lt_iff (pow_pos hε 5), real.lt_rpow_iff_log_lt _ zero_lt_four,
    ←div_lt_iff, initial_bound, nat.cast_max, nat.cast_max],
  { exact lt_max_of_lt_right (lt_max_of_lt_right $ nat.lt_floor_add_one _) },
  { apply real.log_pos,
    norm_num },
  { exact div_pos (by norm_num) (pow_pos hε 5) }
end

/-- An explicit bound on the size of the equipartition in the proof of Szemerédi's Regularity Lemma
-/
noncomputable def szemeredi_bound : ℕ :=
(exp_bound^[⌊4 / ε^5⌋₊] (initial_bound ε l)) * 16^(exp_bound^[⌊4 / ε^5⌋₊] (initial_bound ε l))

lemma initial_bound_le_szemeredi_bound (ε l) : initial_bound ε l ≤ szemeredi_bound ε l :=
(id_le_iterate_of_id_le le_exp_bound _ _).trans $ nat.le_mul_of_pos_right (pow_pos (by norm_num) _)

lemma le_szemeredi_bound : l ≤ szemeredi_bound ε l :=
(le_initial_bound ε l).trans $ initial_bound_le_szemeredi_bound ε l

/-- Effective Szemerédi Regularity Lemma: For any sufficiently big graph, there is an ε-uniform
equipartition of bounded size (where the bound does not depend on the graph). -/
theorem szemeredi_regularity {ε : ℝ} (l : ℕ) (hε : 0 < ε) (hl : l ≤ card α) :
  ∃ (P : finpartition univ),
    P.is_equipartition ∧ l ≤ P.parts.card ∧ P.parts.card ≤ szemeredi_bound ε l ∧ P.is_uniform G ε :=
begin
  obtain hα | hα := le_total (card α) (szemeredi_bound ε l),
  { refine ⟨⊥, bot_is_equipartition _, _⟩,
    rw [card_bot, card_univ],
    exact ⟨hl, hα, bot_is_uniform _ hε⟩ },
  let t := initial_bound ε l,
  have ht : 0 < t := initial_bound_pos _ _,
  have htα : t ≤ (univ : finset α).card :=
    (initial_bound_le_szemeredi_bound _ _).trans (by rwa finset.card_univ),
  obtain ⟨dum, hdum₁, hdum₂⟩ := dummy_equipartition (univ : finset α) ht htα,
  obtain hε₁ | hε₁ := le_total 1 ε,
  { exact ⟨dum, hdum₁, (le_initial_bound ε l).trans hdum₂.ge,
      hdum₂.le.trans (initial_bound_le_szemeredi_bound ε l), dum.is_uniform_of_one_le G hε₁⟩ },
  haveI : nonempty α,
  { rw ←fintype.card_pos_iff,
    apply ht.trans_le ((initial_bound_le_szemeredi_bound _ _).trans hα) },
  suffices h : ∀ i, ∃ (P : finpartition (univ : finset α)), P.is_equipartition ∧
    t ≤ P.parts.card ∧ P.parts.card ≤ (exp_bound^[i]) t ∧
      (P.is_uniform G ε ∨ ε^5 / 4 * i ≤ P.energy G),
  { obtain ⟨P, hP₁, hP₂, hP₃, hP₄⟩ := h (⌊4 / ε^5⌋₊ + 1),
    refine ⟨P, hP₁, (le_initial_bound _ _).trans hP₂, hP₃.trans _, _⟩,
    { rw function.iterate_succ_apply',
      exact mul_le_mul_left' (pow_le_pow_of_le_left (by norm_num) (by norm_num) _) _ },
    apply hP₄.resolve_right,
    rintro hPenergy,
    apply lt_irrefl (1 : ℝ),
    calc
      1 = ε ^ 5 / 4 * (4 / ε ^ 5)
          : by { rw [mul_comm, div_mul_div_cancel 4 (pow_pos hε 5).ne'], norm_num }
      ... < ε ^ 5 / 4 * (⌊4 / ε ^ 5⌋₊ + 1)
          : (mul_lt_mul_left (div_pos (pow_pos hε 5) (by norm_num))).2 (nat.lt_floor_add_one _)
      ... ≤ P.energy G : hPenergy
      ... ≤ 1 : P.energy_le_one G },
  intro i,
  induction i with i ih,
  { refine ⟨dum, hdum₁, hdum₂.ge, hdum₂.le, or.inr _⟩,
    rw [nat.cast_zero, mul_zero],
    exact dum.energy_nonneg G },
  obtain ⟨P, hP₁, hP₂, hP₃, hP₄⟩ := ih,
  by_cases huniform : P.is_uniform G ε,
  { refine ⟨P, hP₁, hP₂, _, or.inl huniform⟩,
    rw function.iterate_succ_apply',
    exact hP₃.trans (le_exp_bound _) },
  replace hP₄ := hP₄.resolve_left huniform,
  have hεl' : 100 < 4 ^ P.parts.card * ε ^ 5,
  { apply (const_lt_pow_initial_bound_mul hε l).trans_le,
    rw mul_le_mul_right (pow_pos hε 5),
    refine pow_le_pow (by norm_num) hP₂ },
  have hi : (i : ℝ) ≤ 4 / ε^5,
  { have hi := hP₄.trans (P.energy_le_one G),
    rw [div_mul_eq_mul_div, div_le_iff (show (0:ℝ) < 4, by norm_num)] at hi,
    norm_num at hi,
    rwa le_div_iff' (pow_pos hε _) },
  have hsize : P.parts.card ≤ (exp_bound^[⌊4 / ε^5⌋₊] t) :=
    hP₃.trans (monotone_iterate_of_id_le le_exp_bound (nat.le_floor hi) _),
  have hPα : P.parts.card * 16^P.parts.card ≤ card α :=
    (nat.mul_le_mul hsize (nat.pow_le_pow_of_le_right (by norm_num) hsize)).trans hα,
  refine ⟨hP₁.increment G ε, increment_is_equipartition hP₁ G ε, _, _,
    or.inr (le_trans _ (energy_increment hP₁ ((seven_le_initial_bound ε l).trans hP₂)
      hεl' hPα huniform hε₁))⟩,
  { rw card_increment hPα huniform,
    exact hP₂.trans (le_exp_bound _) },
  { rw [card_increment hPα huniform, function.iterate_succ_apply'],
    exact exp_bound_mono hP₃ },
  { rw [nat.cast_succ, mul_add, mul_one],
    exact add_le_add_right hP₄ _ }
end

theorem szemeredi_regularity' :
  ∀ {ε : ℝ} (l : ℕ), 0 < ε → ∃ (L : ℕ),
    ∀ (α : Type*) [fintype α] (G : simple_graph α), by exactI
      l ≤ card α →
        ∃ (P : finpartition univ),
          P.is_equipartition ∧ l ≤ P.parts.card ∧ P.parts.card ≤ L ∧ P.is_uniform G ε :=
begin
  intros ε l hε,
  use szemeredi_bound ε l,
  introsI α hα G hl,
  apply szemeredi_regularity G l hε hl,
end


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

/-!
# Small types

A type is `w`-small if there exists an equivalence to some `S : Type w`.

We provide a noncomputable model `shrink α : Type w`, and `equiv_shrink α : α ≃ shrink α`.

A subsingleton type is `w`-small for any `w`.

If `α ≃ β`, then `small.{w} α ↔ small.{w} β`.
-/

universes u w v

/--
A type is `small.{w}` if there exists an equivalence to some `S : Type w`.
-/
class small (α : Type v) : Prop :=
(equiv_small : ∃ (S : Type w), nonempty (α ≃ S))

/--
Constructor for `small α` from an explicit witness type and equivalence.
-/
lemma small.mk' {α : Type v} {S : Type w} (e : α ≃ S) : small.{w} α :=
⟨⟨S, ⟨e⟩⟩⟩

/--
An arbitrarily chosen model in `Type w` for a `w`-small type.
-/
@[nolint has_inhabited_instance]
def shrink (α : Type v) [small.{w} α] : Type w :=
classical.some (@small.equiv_small α _)

/--
The noncomputable equivalence between a `w`-small type and a model.
-/
noncomputable
def equiv_shrink (α : Type v) [small.{w} α] : α ≃ shrink α :=
nonempty.some (classical.some_spec (@small.equiv_small α _))

@[priority 100]
instance small_self (α : Type v) : small.{v} α :=
small.mk' $ equiv.refl α

theorem small_map {α : Type*} {β : Type*} [hβ : small.{w} β] (e : α ≃ β) : small.{w} α :=
let ⟨γ, ⟨f⟩⟩ := hβ.equiv_small in small.mk' (e.trans f)

theorem small_lift (α : Type u) [hα : small.{v} α] : small.{max v w} α :=
let ⟨⟨γ, ⟨f⟩⟩⟩ := hα in small.mk' $ f.trans equiv.ulift.symm

@[priority 100]
instance small_max (α : Type v) : small.{max w v} α :=
small_lift.{v w} α

instance small_ulift (α : Type u) [small.{v} α] : small.{v} (ulift.{w} α) :=
small_map equiv.ulift

theorem small_type : small.{max (u+1) v} (Type u) := small_max.{max (u+1) v} _

section
open_locale classical

theorem small_congr {α : Type*} {β : Type*} (e : α ≃ β) : small.{w} α ↔ small.{w} β :=
⟨λ h, @small_map _ _ h e.symm, λ h, @small_map _ _ h e⟩

instance small_subtype (α : Type v) [small.{w} α] (P : α → Prop) : small.{w} { x // P x } :=
small_map (equiv_shrink α).subtype_equiv_of_subtype'

theorem small_of_injective {α : Type v} {β : Type w} [small.{u} β] {f : α → β}
  (hf : function.injective f) : small.{u} α :=
small_map (equiv.of_injective f hf)

theorem small_of_surjective {α : Type v} {β : Type w} [small.{u} α] {f : α → β}
  (hf : function.surjective f) : small.{u} β :=
small_of_injective (function.injective_surj_inv hf)

theorem small_subset {α : Type v} {s t : set α} (hts : t ⊆ s) [small.{u} s] : small.{u} t :=
let f : t → s := λ x, ⟨x, hts x.prop⟩ in
  @small_of_injective _ _ _ f (λ x y hxy, subtype.ext (subtype.mk.inj hxy))

@[priority 100]
instance small_subsingleton (α : Type v) [subsingleton α] : small.{w} α :=
begin
  rcases is_empty_or_nonempty α; resetI,
  { apply small_map (equiv.equiv_pempty α) },
  { apply small_map equiv.punit_of_nonempty_of_subsingleton, assumption' },
end

/-!
We don't define `small_of_fintype` or `small_of_encodable` in this file,
to keep imports to `logic` to a minimum.
-/

instance small_Pi {α} (β : α → Type*) [small.{w} α] [∀ a, small.{w} (β a)] :
  small.{w} (Π a, β a) :=
⟨⟨Π a' : shrink α, shrink (β ((equiv_shrink α).symm a')),
  ⟨equiv.Pi_congr (equiv_shrink α) (λ a, by simpa using equiv_shrink (β a))⟩⟩⟩

instance small_sigma {α} (β : α → Type*) [small.{w} α] [∀ a, small.{w} (β a)] :
  small.{w} (Σ a, β a) :=
⟨⟨Σ a' : shrink α, shrink (β ((equiv_shrink α).symm a')),
  ⟨equiv.sigma_congr (equiv_shrink α) (λ a, by simpa using equiv_shrink (β a))⟩⟩⟩

instance small_prod {α β} [small.{w} α] [small.{w} β] : small.{w} (α × β) :=
⟨⟨shrink α × shrink β,
  ⟨equiv.prod_congr (equiv_shrink α) (equiv_shrink β)⟩⟩⟩

instance small_sum {α β} [small.{w} α] [small.{w} β] : small.{w} (α ⊕ β) :=
⟨⟨shrink α ⊕ shrink β,
  ⟨equiv.sum_congr (equiv_shrink α) (equiv_shrink β)⟩⟩⟩

instance small_set {α} [small.{w} α] : small.{w} (set α) :=
⟨⟨set (shrink α), ⟨equiv.set.congr (equiv_shrink α)⟩⟩⟩

instance small_range {α : Type v} {β : Type w} (f : α → β) [small.{u} α] :
  small.{u} (set.range f) :=
small_of_surjective set.surjective_onto_range

instance small_image {α : Type v} {β : Type w} (f : α → β) (S : set α) [small.{u} S] :
  small.{u} (f '' S) :=
small_of_surjective set.surjective_onto_image

theorem not_small_type : ¬ small.{u} (Type (max u v))
| ⟨⟨S, ⟨e⟩⟩⟩ := @function.cantor_injective (Σ α, e.symm α)
  (λ a, ⟨_, cast (e.3 _).symm a⟩)
  (λ a b e, (cast_inj _).1 $ eq_of_heq (sigma.mk.inj e).2)

instance small_vector {α : Type v} {n : ℕ} [small.{u} α] :
  small.{u} (vector α n) :=
small_of_injective (equiv.vector_equiv_fin α n).injective

instance small_list {α : Type v} [small.{u} α] :
  small.{u} (list α) :=
begin
  let e : (Σ n, vector α n) ≃ list α := equiv.sigma_fiber_equiv list.length,
  exact small_of_surjective e.surjective,
end

end


import .lovelib

/-! # LoVe Homework 7: Metaprogramming

Homework must be done individually. -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/-! ## Question 1 (9 points): A `harmless` Tactic

We develop a tactic that applies all harmless introduction and elimination
rules for the connectives and quantifiers exhaustively. A rule is said to be
__harmless__ if, given a provable goal, it always gives rise to provable
subgoals. In addition, we will require that harmless rules do not introduce
metavariables (since these can easily be instantiated accidentally with the wrong
terms).

We proceed in three steps.

1.1 (3 points). Develop a `harmless_intros` tactic that applies the introduction
rules for `true`, `∧`, and `↔` and that invokes `tactic.intro` (or
`tactic.intro1`) for `→`/`∀`. The tactic generalizes `intro_ands` from the
exercise.

Hint: You can use the `<|>` operator between the tactics used for different
logical symbols. -/

#check tactic.repeat
#check tactic.applyc
#check tactic.intro
#check tactic.intro1

meta def harmless_intros : tactic unit :=
sorry

lemma abcd (a b c d : Prop) :
  a → ¬ b ∧ (c ↔ d) :=
begin
  harmless_intros,
  /- The proof state should be roughly as follows:

  a b c d : Prop,
  a_1 : a,
  a_2 : b
  ⊢ false

  a b c d : Prop,
  a_1 : a,
  a_2 : c
  ⊢ d

  a b c d : Prop,
  a_1 : a,
  a_2 : d
  ⊢ c -/
  repeat { sorry }
end

/-! 1.2 (4 points). Develop a `harmless_destructs` tactic that eliminates
`false`, `∧`, `∨`, `↔`, and `∃`. The tactic generalizes `destruct_ands` from the
exercise. -/

meta def harmless_destructs : tactic unit :=
sorry

lemma abcdef (a b c d e f : Prop) (p : ℕ → Prop)
    (hneg: ¬ a) (hand : a ∧ b ∧ c) (hor : c ∨ d) (himp : b → e) (hiff : e ↔ f)
    (hex : ∃x, p x) :
  false :=
begin
  harmless_destructs,
  /- The proof state should be roughly as follows:

  2 goals
  a b c d e f : Prop,
  p : ℕ → Prop,
  hneg : ¬a,
  himp : b → e,
  hand_left : a,
  hor : c,
  hiff_mp : e → f,
  hiff_mpr : f → e,
  hex_w : ℕ,
  hex_h : p hex_w,
  hand_right_left : b,
  hand_right_right : c
  ⊢ false

  a b c d e f : Prop,
  p : ℕ → Prop,
  hneg : ¬a,
  himp : b → e,
  hand_left : a,
  hor : d,
  hiff_mp : e → f,
  hiff_mpr : f → e,
  hex_w : ℕ,
  hex_h : p hex_w,
  hand_right_left : b,
  hand_right_right : c
  ⊢ false -/
  repeat { sorry }
end

/-! 1.3 (2 points). Implement a `harmless` tactic that first performs
introduction, then elimination, and finally proves all the subgoals that can be
discharged directly by `tactic.assumption`. The tactic generalizes `destro_and`
from the exercise.

Hint: Depending on how you structure your code, the `tactic.try` combinator
might be useful. -/

meta def harmless : tactic unit :=
sorry

lemma abcdef₂ (a b c d e f : Prop) (p : ℕ → Prop)
    (hneg: ¬ a) (hand : a ∧ b ∧ c) (hor : c ∨ d) (himp : b → e) (hiff : e ↔ f)
    (hex : ∃x, p x) :
  a → ¬ b ∧ (c ↔ d) :=
begin
  harmless,
  /- The proof state should be roughly as follows:

  3 goals
  a b c d e f : Prop,
  p : ℕ → Prop,
  hneg : ¬a,
  himp : b → e,
  a_1 : a,
  a_2 : b,
  hand_left : a,
  hor : c,
  hiff_mp : e → f,
  hiff_mpr : f → e,
  hex_w : ℕ,
  hex_h : p hex_w,
  hand_right_left : b,
  hand_right_right : c
  ⊢ false

  a b c d e f : Prop,
  p : ℕ → Prop,
  hneg : ¬a,
  himp : b → e,
  a_1 : a,
  a_2 : b,
  hand_left : a,
  hor : d,
  hiff_mp : e → f,
  hiff_mpr : f → e,
  hex_w : ℕ,
  hex_h : p hex_w,
  hand_right_left : b,
  hand_right_right : c
  ⊢ false

  a b c d e f : Prop,
  p : ℕ → Prop,
  hneg : ¬a,
  himp : b → e,
  a_1 : a,
  a_2 : c,
  hand_left : a,
  hor : c,
  hiff_mp : e → f,
  hiff_mpr : f → e,
  hex_w : ℕ,
  hex_h : p hex_w,
  hand_right_left : b,
  hand_right_right : c
  ⊢ d -/
  repeat { sorry }
end

/-! ## Question 2 (2 bonus points): An `auto` Tactic

2.1 (1 bonus point). Develop an Isabelle-style `auto` tactic.

This tactic would apply all harmless introduction and elimination rules. In
addition, it would try potentially harmful rules (such as `or.intro_left` and
`false.elim`) but backtrack at some point (or try several possibilities in
parallel). Iterative deepening may be a valid approach, or best-first search, or
breadth-first search. The tactic should also try to apply assumptions whose
conclusion matches the goal, but backtrack if necessary.

See also "Automatic Proof and Disproof in Isabelle/HOL"
(https://www.cs.vu.nl/~jbe248/frocos2011-dis-proof.pdf) by Blanchette, Bulwahn,
and Nipkow, and the references they give.

2.2 (1 bonus point). Test your tactic on some benchmarks.

You can try your tactic on logic puzzles of the kinds we proved in exercise 2
and homework 2. Please include these below. -/

end LoVe


-- Copyright (C) 2020 by @ljt12138

import propositional.used_formula
import propositional.complete_extension

namespace prop_logic

def decidable_in {α : Type} (φ : sentence α) (Γ : list (sentence α)) :=
  φ ∈ Γ ∨ (~φ) ∈ Γ

lemma consistent_decidable_good {α : Type} (φ : sentence α) (Γ : list (sentence α)) :
  consistent Γ → decidable_in φ Γ → Γ ⊢ φ → φ ∈ Γ :=
begin
  intros c a a',
  cases a, assumption,
  simp at *,
  have ht : Γ ⊢ φ ↣ ⊥, { existsi proof.assum _, prover },
  exfalso, unfold consistent at c,
  apply c, cases a', cases ht,
  existsi proof.mp _ _, prover
end

lemma complete_extension_on_used {α : Type} {Γ : list (sentence α)} (a : consistent Γ) :
  ∃ Γ', Γ ⊆ Γ' ∧ (∀ φ, φ ∈ ⦃Γ⦄ → decidable_in φ Γ') ∧ consistent Γ' :=
begin
  unfold decidable_in, apply complete_extension, assumption
end

lemma consistent_to_satisfiable {α : Type} {Γ : list (sentence α)}
  (a : consistent Γ) : satisfiable_set Γ :=
begin
  cases (complete_extension_on_used a) with Γ' h,
  existsi (λ (p : α), ⟦p⟧ ∈ Γ'),
  intros φ a,
  have gres : ∀ φ, φ ∈ ⦃Γ⦄ → (evaluate (λ (p : α), ⟦p⟧ ∈ Γ') φ ↔ φ ∈ Γ'), {
    intros φ, induction φ,
    case atom_prop { simp },
    case perp {
      simp, intros a' contra,
      have ht := h.right.right, unfold consistent at ht, apply ht,
      existsi proof.assum _, prover
    },
    case imply {
      simp at *, intros a₁,
      cases used_set_chl _ _ _ a₁ with a₁' a₁'',
      have ih₁ := φ_ih_a a₁',
      have ih₂ := φ_ih_a_1 a₁'',
      have ih' := h.right.left _ a₁,
      have ih₃ := h.right.left _ a₁',
      have ih₄ := h.right.left _ a₁'',
      split,
      {
        intros a₂, cases ih',
        { assumption },
        {
          cases ih₃,
          {
            have res := inside_provable (ih₂.mp (a₂ (ih₁.mpr ih₃))),
            apply consistent_decidable_good, tauto, tauto,
            apply useless_condition, assumption
          },
          {
            simp at *,
            exfalso, apply h.right.right,
            have pf_ih' := inside_provable ih',
            have pf_ih₃ := inside_provable ih₃,
            exact false_condition pf_ih' pf_ih₃
          }
        }
      },
      {
        intros a₂ a₃, apply ih₂.mpr,
        have a₃' := inside_provable (ih₁.mp a₃),
        have a₂' := inside_provable a₂,
        apply consistent_decidable_good, tauto, tauto,
        cases a₂', cases a₃',
        existsi proof.mp _ _, prover
      }
    }
  },
  have ins : φ ∈ ⦃Γ⦄ := by exact used_set_contains _ _ a,
  apply (gres φ ins).mpr,
  exact h.left a,
end

theorem completeness {α : Type} {Γ : _} {φ : sentence α} :
  Γ ⊨ φ → Γ ⊢ φ :=
begin
  intros a,
  unfold logical_consequence at *,
  have h := contra_of_not_satis a,
  apply provable_contra.mpr, simp,
  have ht : ¬ (consistent ((φ↣⊥)::Γ)),
  { intros contra, apply h, apply consistent_to_satisfiable, assumption },
  unfold consistent at ht, classical, tauto
end

end prop_logic


import analysis.inner_product_space.pi_L2

variables {E : Type*}
[inner_product_space ℂ E]
[finite_dimensional ℂ E]

variables {S : submodule ℂ E} {L : S →ₗᵢ[ℂ] E}

open finite_dimensional

/-- Let `S` be a subspace of a finite-dimensional complex inner product space `E`.  A linear
isometry mapping `S` into `E` can be extended to a full isometry of `E`. -/
noncomputable def linear_isometry.extend (L : S →ₗᵢ[ℂ] E): E →ₗᵢ[ℂ] E :=
  begin
    -- Build an isometry from Sᗮ to L(S)ᗮ through euclidean_space
    let d := finrank ℂ Sᗮ,
    have dim_S : finrank ℂ Sᗮ = d := rfl,
    let LS := L.to_linear_map.range,
    have dim_LS_perp : finrank ℂ LSᗮ = d,
    calc  finrank ℂ LSᗮ = finrank ℂ E - finrank ℂ LS : by simp only
        [← LS.finrank_add_finrank_orthogonal, add_tsub_cancel_left]
      ...               = finrank ℂ E - finrank ℂ S : by simp only
        [linear_map.finrank_range_of_inj L.injective]
      ...               = finrank ℂ Sᗮ : by simp only
        [← S.finrank_add_finrank_orthogonal, add_tsub_cancel_left]
      ...               = d : dim_S,
    let L1 := ((fin_std_orthonormal_basis dim_S).to_orthonormal_basis
      (fin_std_orthonormal_basis_orthonormal dim_S)).repr.to_linear_isometry,
    let L2 := ((fin_std_orthonormal_basis dim_LS_perp).to_orthonormal_basis
      (fin_std_orthonormal_basis_orthonormal dim_LS_perp)).repr.symm.to_linear_isometry,
    let L3 := (LS)ᗮ.subtypeₗᵢ.comp (L2.comp L1),
    -- Project onto S and Sᗮ
    let p1 := (orthogonal_projection S).to_linear_map,
    let p2 := (orthogonal_projection Sᗮ).to_linear_map,
    -- Build a linear map from the isometries on S and Sᗮ
    let M := L.to_linear_map.comp p1 + L3.to_linear_map.comp p2,
    -- Prove that M is an isometry
    have M_norm_map : ∀ (x : E), ∥M x∥ = ∥x∥,
    { intro x,
      -- Apply M to the orthogonal decomposition of x
      have Mx_decomp : M x = L (p1 x) + L3 (p2 x),
      { simp only [linear_map.add_apply, linear_map.comp_apply, linear_map.comp_apply,
        linear_isometry.coe_to_linear_map]},
      -- Mx_decomp is the orthogonal decomposition of M x
      have Mx_orth : ⟪ L (p1 x), L3 (p2 x) ⟫_ℂ = 0,
      { have Lp1x : L (p1 x) ∈ L.to_linear_map.range := L.to_linear_map.mem_range_self (p1 x),
        have Lp2x : L3 (p2 x) ∈ (L.to_linear_map.range)ᗮ,
        { simp only [L3, linear_isometry.coe_comp, function.comp_app, submodule.coe_subtypeₗᵢ,
            ← submodule.range_subtype (LSᗮ)],
          apply linear_map.mem_range_self},
        apply submodule.inner_right_of_mem_orthogonal Lp1x Lp2x},
      -- Apply the Pythagorean theorem and simplify
      rw ← sq_eq_sq (norm_nonneg _) (norm_nonneg _),
      rw norm_sq_eq_add_norm_sq_projection x S,
      simp only [sq, Mx_decomp],
      rw norm_add_sq_eq_norm_sq_add_norm_sq_of_inner_eq_zero (L (p1 x)) (L3 (p2 x)) Mx_orth,
      simp only [linear_isometry.norm_map, p1, p2, continuous_linear_map.to_linear_map_eq_coe,
        add_left_inj, mul_eq_mul_left_iff, norm_eq_zero, true_or, eq_self_iff_true,
        continuous_linear_map.coe_coe, submodule.coe_norm, submodule.coe_eq_zero]},
    exact {
      to_linear_map := M,
      norm_map' := M_norm_map,
    },
  end

lemma linear_isometry.extend_apply (L : S →ₗᵢ[ℂ] E) (s : S):
  L.extend s = L s :=
begin
  simp only [linear_isometry.extend, continuous_linear_map.to_linear_map_eq_coe,
    ←linear_isometry.coe_to_linear_map],
  simp only [add_right_eq_self, linear_isometry.coe_to_linear_map,
    linear_isometry_equiv.coe_to_linear_isometry, linear_isometry.coe_comp, function.comp_app,
    orthogonal_projection_mem_subspace_eq_self, linear_map.coe_comp, continuous_linear_map.coe_coe,
    submodule.coe_subtype, linear_map.add_apply, submodule.coe_eq_zero,
    linear_isometry_equiv.map_eq_zero_iff, submodule.coe_subtypeₗᵢ,
    orthogonal_projection_mem_subspace_orthogonal_complement_eq_zero,
    submodule.orthogonal_orthogonal, submodule.coe_mem],
end


/-

   I am a .lean file about the real numbers. You can interact with me online at
   TinyURL.com/real-numbers-in-lean

   or if you have lean and mathlib installed on your computer,
   you can download me from https://github.com/kbuzzard/real-numbers-course
   and open me in VS Code on your computer, for a faster experience.

   See the currently non-existing documentation at the link above;
   until it's written nobody will have a chance of being
   able to do anything.
-/

--  Import mathlib's real number module.
import data.real.basic

-- Now we have a version of the real numbers which mathematicians
-- can learn to use. It's sometimes fun and sometimes frustrating
-- to play with. At Zulip we're trying to make it more fun and
-- less frustrating for mathematicians.

import tactic.linarith

-- Now we have some really cool tactics

lemma question1 : (2 : ℝ) + (2 : ℝ) = (4 : ℝ) :=
begin
  sorry
end

lemma question2 (x y : ℝ) : x + y = y + x :=
begin
  sorry
end

lemma question3 : ∀ x y : ℝ, (x + y) ^ 3 = x ^ 3 + 3 * x ^ 2 + 3 * x + 1 :=
begin
  sorry
end

lemma question4 : ∀ (a b c : ℝ), (c ≠ 0) → a / c + b / c = (a + b) / c :=
begin
  sorry
end

lemma question5 : ∀ (a b c : ℝ), a / c + b / c = (a + b) / c :=
begin
  sorry
end

-- need basic questions about sequences, but I don't know the idiomatic way
-- to talk about limits of sequences in Lean

#check limit.is_limit


/-
https://leanprover.zulipchat.com/#narrow/stream/113489-new-members/topic/Types.20that.20should.20be.20equal
https://gist.github.com/adamtopaz/84315ae5b11319013707b2d0804fb37e
-/

import algebra.algebra.basic
import linear_algebra

variables (R : Type*) [comm_ring R]
variables (M : Type*) [add_comm_group M] [module R M]

inductive pre
| of : M → pre
| zero : pre
| one : pre
| mul : pre → pre → pre
| add : pre → pre → pre
| smul : R → pre → pre

namespace tensoralg
def lift_fun {A : Type*} [ring A] [algebra R A] (f : M →ₗ[R] A) : pre R M → A :=
  λ t, pre.rec_on t f 0 1 (λ _ _, (*)) (λ _ _, (+)) (λ x _ a, x • a)

def rel : (pre R M) → (pre R M) → Prop := λ x y,
  ∀ (A : Type*) (h1 : ring A), by letI := h1; exact
  ∀ (h2 : algebra R A), by letI := h2; exact
  ∀ (f : M →ₗ[R] A),
  (lift_fun R M f) x = (lift_fun R M f) y

def setoid : setoid (pre R M) := ⟨rel R M,
begin
  refine ⟨_,_,_⟩,
  { intros x _ _ _ _ _ _ , refl, },
  { intros x y h _ _ _ _  _ _, symmetry, apply h, },
  { intros x y z h1 h2 _ _ _ _ _ _, rw [h1,h2], },
end⟩
end tensoralg

def tensoralg := quotient (tensoralg.setoid R M) -- the tensor algebra.

namespace tensoralg
instance : ring (tensoralg R M) :=
{ zero := quotient.mk' pre.zero,
  one := quotient.mk' pre.one,
  add := λ x y, quotient.lift_on₂' x y
  (λ a b, quotient.mk' $ pre.add a b)
  begin
    intros a1 a2 b1 b2 h1 h2,
    dsimp only [],
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h1_1,
    change (lift_fun R M g) a1 + (lift_fun R M g) a2 = (lift_fun R M g) b1 + (lift_fun R M g) b2,
    specialize h1 B _ _ g,
    specialize h2 B _ _ g,
    rw [h1, h2],
  end,
  mul := λ x y, quotient.lift_on₂' x y
  (λ a b, quotient.mk' $ pre.mul a b)
  begin
    intros a1 a2 b1 b2 h1 h2,
    dsimp only [],
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h1_1,
    change (lift_fun R M g) a1 * (lift_fun R M g) a2 = (lift_fun R M g) b1 * (lift_fun R M g) b2,
    specialize h1 B _ _ g,
    specialize h2 B _ _ g,
    rw [h1, h2],
  end,
  neg := λ x, quotient.lift_on' x
  (λ a, quotient.mk' $ pre.smul (-1 : R) a)
  begin
    intros a b h,
    dsimp only [],
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    change (-1 : R) • ((lift_fun R M g) a) = (-1 : R) • ((lift_fun R M g) b),
    specialize h B _ _ g,
    rw h,
  end,
  add_assoc :=
  begin
    intros a b c,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    rcases quot.exists_rep b with ⟨b,rfl⟩,
    rcases quot.exists_rep c with ⟨c,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change G a + G b + G c = G a + (G b + G c),
    rw add_assoc,
  end,
  zero_add :=
  begin
    intros a,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change 0 + G a = G a,
    rw zero_add,
  end,
  add_zero :=
  begin
    intros a,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change G a + 0 = G a,
    rw add_zero,
  end,
  add_left_neg :=
  begin
    intros a,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change (-1 : R) • G a + G a = 0,
    simp,
  end,
  add_comm :=
  begin
    intros a b,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    rcases quot.exists_rep b with ⟨b,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change G a + G b = G b + G a, rw add_comm,
  end,
  mul_assoc :=
  begin
    intros a b c,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    rcases quot.exists_rep b with ⟨b,rfl⟩,
    rcases quot.exists_rep c with ⟨c,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change G a * G b * G c = G a * (G b * G c),
    rw mul_assoc,
  end,
  one_mul :=
  begin
    intros a,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change 1 * G a = G a,
    rw one_mul,
  end,
  mul_one :=
  begin
    intros a,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change G a * 1 = G a,
    rw mul_one,
  end,
  left_distrib :=
  begin
    intros a b c,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    rcases quot.exists_rep b with ⟨b,rfl⟩,
    rcases quot.exists_rep c with ⟨c,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change G a * (G b + G c) = _,
    rw left_distrib, refl,
  end,
  right_distrib :=
  begin
    intros a b c,
    rcases quot.exists_rep a with ⟨a,rfl⟩,
    rcases quot.exists_rep b with ⟨b,rfl⟩,
    rcases quot.exists_rep c with ⟨c,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    change (G a + G b) * G c = _,
    rw right_distrib, refl,
  end, }

instance : has_scalar R (tensoralg R M) := ⟨λ x y, quotient.lift_on' y
  (λ a, quotient.mk' $ pre.smul x a)
  begin
    intros a b h,
    dsimp only [],
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    change x • ((lift_fun R M g) a) = x • ((lift_fun R M g) b),
    specialize h B _ _ g,
    rw h,
  end⟩

instance : algebra R (tensoralg R M) :=
{ to_fun := λ r, r • 1,
  map_one' :=
  begin
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    letI := h1,
    change (1 : R) • (G pre.one) = G pre.one,
    rw one_smul,
  end,
  map_mul' :=
  begin
    intros x y,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    letI := h1,
    change (x * y) • (1 : B) = (x • 1) * (y • 1),
    simp [mul_smul],
  end,
  map_zero' :=
  begin
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    letI := h1,
    change (0 : R) • (1 : B) = 0,
    rw zero_smul,
  end,
  map_add' :=
  begin
    intros x y,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    letI := h1,
    change (x + y) • (1 : B) = (x • 1) + (y • 1),
    rw add_smul,
  end,
  commutes' :=
  begin
    intros r x,
    dsimp only [],
    rcases quot.exists_rep x with ⟨x,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    letI := h1,
    change r • (1 : B) * G x = G x * (r • 1),
    simp,
  end,
  smul_def' :=
  begin
    intros r x,
    dsimp only [],
    rcases quot.exists_rep x with ⟨x,rfl⟩,
    apply quotient.sound',
    intros B _ _ _ _ g,
    letI := h2,
    let G := lift_fun R M g,
    letI := h1,
    change r • G x = (r • 1) * G x,
    simp,
  end,
  ..show has_scalar R (tensoralg R M), by apply_instance }

-- def lift {A : Type*} [ring A] [algebra R A] (f : M →ₗ[R] A) : tensoralg R M →ₐ[R] A :=
-- { to_fun := λ a, quotient.lift_on' a (lift_fun _ _ f)
--   begin
--     intros a b h,
--     apply h,
--   end,
--   map_one' := rfl,
--   map_mul' :=
--   begin
--     intros x y,
--     letI := tensoralg.setoid R M,
--     rcases quotient.exists_rep x with ⟨x,rfl⟩,
--     rcases quotient.exists_rep y with ⟨y,rfl⟩,
--     change quotient.lift_on _ _ _ = quotient.lift_on _ _ _ * quotient.lift_on _ _ _,
--     simp_rw quotient.lift_on_beta,
--     refl,
--   end,
--   map_zero' := rfl,
--   map_add' :=
--   begin
--     intros x y,
--     letI := tensoralg.setoid R M,
--     rcases quotient.exists_rep x with ⟨x,rfl⟩,
--     rcases quotient.exists_rep y with ⟨y,rfl⟩,
--     change quotient.lift_on _ _ _ = quotient.lift_on _ _ _ + quotient.lift_on _ _ _,
--     simp_rw quotient.lift_on_beta,
--     refl,
--   end,
--   commutes' :=
--   begin
--     intros r,
--     letI := tensoralg.setoid R M,
--     change quotient.lift_on (r • 1) _ _ = _,
--     change r • ((lift_fun R M f) pre.one) = _,
--     have : (algebra_map R A) r = r • 1, by refine algebra.algebra_map_eq_smul_one r,
--     rw this, clear this,
--     refl,
--   end }

-- def univ : M →ₗ[R] (tensoralg R M) :=
-- { to_fun := λ m, quotient.mk' $ pre.of m,
--   map_add' :=
--   begin
--     intros x y,
--     apply quotient.sound',
--     intros B _ _ _ _ g,
--     letI := h2,
--     let G := lift_fun R M g,
--     change g (x + y) = g x + g y,
--     refine is_add_hom.map_add ⇑g x y,
--   end,
--   map_smul' :=
--   begin
--     intros x y,
--     apply quotient.sound',
--     intros B _ _ _ _ g,
--     letI := h2,
--     let G := lift_fun R M g,
--     change g (x • y) = x • g y,
--     refine linear_map.map_smul g x y,
--   end }

-- theorem univ_comp_lift {A : Type*} [ring A] [algebra R A] (f : M →ₗ[R] A) :
--   (lift R M f) ∘ (univ R M) = f := rfl

-- theorem lift_unique {A : Type*} [ring A] [algebra R A] (f : M →ₗ[R] A)
--   (g : tensoralg R M →ₐ[R] A) : g ∘ (univ R M) = f → g = lift R M f :=
-- begin
--   intro hyp,
--   ext,
--   letI := tensoralg.setoid R M,
--   rcases quotient.exists_rep x with ⟨x,rfl⟩,
--   let G := lift_fun R M f,
--   induction x,
--   { change (g ∘ (univ R M)) _ = _,
--     rw hyp,
--     refl },
--   { change g 0 = 0,
--     exact alg_hom.map_zero g },
--   { change g 1 = 1,
--     exact alg_hom.map_one g },
--   { change g (⟦x_a⟧ * ⟦x_a_1⟧) = _,
--     rw alg_hom.map_mul,
--     rw x_ih_a, rw x_ih_a_1,
--     refl },
--   { change g (⟦x_a⟧ + ⟦x_a_1⟧) = _,
--     rw alg_hom.map_add,
--     rw x_ih_a, rw x_ih_a_1,
--     refl },
--   { change g (x_a • ⟦x_a_1⟧) = _,
--     rw alg_hom.map_smul,
--     rw x_ih, refl },
-- end

end tensoralg


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

! This file was ported from Lean 3 source module set_theory.cardinal.schroeder_bernstein
! leanprover-community/mathlib commit c3291da49cfa65f0d43b094750541c0731edc932
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.FixedPoints
import Mathbin.Order.Zorn

/-!
# Schröder-Bernstein theorem, well-ordering of cardinals

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

This file proves the Schröder-Bernstein theorem (see `schroeder_bernstein`), the well-ordering of
cardinals (see `min_injective`) and the totality of their order (see `total`).

## Notes

Cardinals are naturally ordered by `α ≤ β ↔ ∃ f : a → β, injective f`:
* `schroeder_bernstein` states that, given injections `α → β` and `β → α`, one can get a
  bijection `α → β`. This corresponds to the antisymmetry of the order.
* The order is also well-founded: any nonempty set of cardinals has a minimal element.
  `min_injective` states that by saying that there exists an element of the set that injects into
  all others.

Cardinals are defined and further developed in the file `set_theory.cardinal`.
-/

open Set Function

open Classical

universe u v

namespace Function

namespace Embedding

section antisymm

variable {α : Type u} {β : Type v}

#print Function.Embedding.schroeder_bernstein /-
/-- **The Schröder-Bernstein Theorem**:
Given injections `α → β` and `β → α`, we can get a bijection `α → β`. -/
theorem schroeder_bernstein {f : α → β} {g : β → α} (hf : Function.Injective f)
    (hg : Function.Injective g) : ∃ h : α → β, Bijective h :=
  by
  cases' isEmpty_or_nonempty β with hβ hβ
  · have : IsEmpty α := Function.isEmpty f
    exact ⟨_, ((Equiv.equivEmpty α).trans (Equiv.equivEmpty β).symm).Bijective⟩
  set F : Set α →o Set α :=
    { toFun := fun s => (g '' (f '' s)ᶜ)ᶜ
      monotone' := fun s t hst =>
        compl_subset_compl.mpr <| image_subset _ <| compl_subset_compl.mpr <| image_subset _ hst }
  set s : Set α := F.lfp
  have hs : (g '' (f '' s)ᶜ)ᶜ = s := F.map_lfp
  have hns : g '' (f '' s)ᶜ = sᶜ := compl_injective (by simp [hs])
  set g' := inv_fun g
  have g'g : left_inverse g' g := left_inverse_inv_fun hg
  have hg'ns : g' '' sᶜ = (f '' s)ᶜ := by rw [← hns, g'g.image_image]
  set h : α → β := s.piecewise f g'
  have : surjective h := by rw [← range_iff_surjective, range_piecewise, hg'ns, union_compl_self]
  have : injective h :=
    by
    refine' (injective_piecewise_iff _).2 ⟨hf.inj_on _, _, _⟩
    · intro x hx y hy hxy
      obtain ⟨x', hx', rfl⟩ : x ∈ g '' (f '' s)ᶜ := by rwa [hns]
      obtain ⟨y', hy', rfl⟩ : y ∈ g '' (f '' s)ᶜ := by rwa [hns]
      rw [g'g _, g'g _] at hxy
      rw [hxy]
    · intro x hx y hy hxy
      obtain ⟨y', hy', rfl⟩ : y ∈ g '' (f '' s)ᶜ := by rwa [hns]
      rw [g'g _] at hxy
      exact hy' ⟨x, hx, hxy⟩
  exact ⟨h, ‹injective h›, ‹surjective h›⟩
#align function.embedding.schroeder_bernstein Function.Embedding.schroeder_bernstein
-/

#print Function.Embedding.antisymm /-
/-- **The Schröder-Bernstein Theorem**: Given embeddings `α ↪ β` and `β ↪ α`, there exists an
equivalence `α ≃ β`. -/
theorem antisymm : (α ↪ β) → (β ↪ α) → Nonempty (α ≃ β)
  | ⟨e₁, h₁⟩, ⟨e₂, h₂⟩ =>
    let ⟨f, hf⟩ := schroeder_bernstein h₁ h₂
    ⟨Equiv.ofBijective f hf⟩
#align function.embedding.antisymm Function.Embedding.antisymm
-/

end antisymm

section Wo

parameter {ι : Type u}(β : ι → Type v)

@[reducible]
private def sets :=
  { s : Set (∀ i, β i) | ∀ x ∈ s, ∀ y ∈ s, ∀ (i), (x : ∀ i, β i) i = y i → x = y }
#align function.embedding.sets function.embedding.sets

#print Function.Embedding.min_injective /-
/-- The cardinals are well-ordered. We express it here by the fact that in any set of cardinals
there is an element that injects into the others. See `cardinal.linear_order` for (one of) the
lattice instances. -/
theorem min_injective [I : Nonempty ι] : ∃ i, Nonempty (∀ j, β i ↪ β j) :=
  let ⟨s, hs, ms⟩ :=
    show ∃ s ∈ sets, ∀ a ∈ sets, s ⊆ a → a = s from
      zorn_subset sets fun c hc hcc =>
        ⟨⋃₀ c, fun x ⟨p, hpc, hxp⟩ y ⟨q, hqc, hyq⟩ i hi =>
          (hcc.Total hpc hqc).elim (fun h => hc hqc x (h hxp) y hyq i hi) fun h =>
            hc hpc x hxp y (h hyq) i hi,
          fun _ => subset_unionₛ_of_mem⟩
  let ⟨i, e⟩ :=
    show ∃ i, ∀ y, ∃ x ∈ s, (x : ∀ i, β i) i = y from
      by_contradiction fun h =>
        have h : ∀ i, ∃ y, ∀ x ∈ s, (x : ∀ i, β i) i ≠ y := by
          simpa only [not_exists, not_forall] using h
        let ⟨f, hf⟩ := Classical.axiom_of_choice h
        have : f ∈ s :=
          have : insert f s ∈ sets := fun x hx y hy =>
            by
            cases hx <;> cases hy; · simp [hx, hy]
            · subst x
              exact fun i e => (hf i y hy e.symm).elim
            · subst y
              exact fun i e => (hf i x hx e).elim
            · exact hs x hx y hy
          ms _ this (subset_insert f s) ▸ mem_insert _ _
        let ⟨i⟩ := I
        hf i f this rfl
  let ⟨f, hf⟩ := Classical.axiom_of_choice e
  ⟨i,
    ⟨fun j =>
      ⟨fun a => f a j, fun a b e' => by
        let ⟨sa, ea⟩ := hf a
        let ⟨sb, eb⟩ := hf b
        rw [← ea, ← eb, hs _ sa _ sb _ e']⟩⟩⟩
#align function.embedding.min_injective Function.Embedding.min_injective
-/

end Wo

#print Function.Embedding.total /-
/-- The cardinals are totally ordered. See `cardinal.linear_order` for (one of) the lattice
instance. -/
theorem total (α : Type u) (β : Type v) : Nonempty (α ↪ β) ∨ Nonempty (β ↪ α) :=
  match @min_injective Bool (fun b => cond b (ULift α) (ULift.{max u v, v} β)) ⟨true⟩ with
  | ⟨tt, ⟨h⟩⟩ =>
    let ⟨f, hf⟩ := h false
    Or.inl ⟨Embedding.congr Equiv.ulift Equiv.ulift ⟨f, hf⟩⟩
  | ⟨ff, ⟨h⟩⟩ =>
    let ⟨f, hf⟩ := h true
    Or.inr ⟨Embedding.congr Equiv.ulift Equiv.ulift ⟨f, hf⟩⟩
#align function.embedding.total Function.Embedding.total
-/

end Embedding

end Function


/-
Copyright (c) 2017 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Stephen Morgan, Scott Morrison, Johannes Hölzl, Reid Barton

Defines a category, as a typeclass parametrised by the type of objects.
Introduces notations
  `X ⟶ Y` for the morphism spaces,
  `f ≫ g` for composition in the 'arrows' convention.

Users may like to add `f ⊚ g` for composition in the standard convention, using
```
local notation f ` ⊚ `:80 g:80 := category.comp g f    -- type as \oo
```
-/

import tactic.restate_axiom
import tactic.replacer
import tactic.interactive
import tactic.tidy

namespace category_theory

universes v u  -- The order in this declaration matters: v often needs to be explicitly specified while u often can be omitted

/-
The propositional fields of `category` are annotated with the auto_param `obviously`,
which is defined here as a
[`replacer` tactic](https://github.com/leanprover/mathlib/blob/master/docs/tactics.md#def_replacer).
We then immediately set up `obviously` to call `tidy`. Later, this can be replaced with more
powerful tactics.
-/
def_replacer obviously
@[obviously] meta def obviously' := tactic.tidy

class has_hom (obj : Type u) : Type (max u (v+1)) :=
(hom : obj → obj → Type v)

infixr ` ⟶ `:10 := has_hom.hom -- type as \h

class category_struct (obj : Type u)
extends has_hom.{v} obj :=
(id       : Π X : obj, hom X X)
(comp     : Π {X Y Z : obj}, (X ⟶ Y) → (Y ⟶ Z) → (X ⟶ Z))

notation `𝟙` := category_struct.id -- type as \b1
infixr ` ≫ `:80 := category_struct.comp -- type as \gg

/--
The typeclass `category C` describes morphisms associated to objects of type `C`.
The universe levels of the objects and morphisms are unconstrained, and will often need to be
specified explicitly, as `category.{v} C`. (See also `large_category` and `small_category`.)
-/
class category (obj : Type u)
extends category_struct.{v} obj :=
(id_comp' : ∀ {X Y : obj} (f : hom X Y), 𝟙 X ≫ f = f . obviously)
(comp_id' : ∀ {X Y : obj} (f : hom X Y), f ≫ 𝟙 Y = f . obviously)
(assoc'   : ∀ {W X Y Z : obj} (f : hom W X) (g : hom X Y) (h : hom Y Z),
  (f ≫ g) ≫ h = f ≫ (g ≫ h) . obviously)

-- `restate_axiom` is a command that creates a lemma from a structure field,
-- discarding any auto_param wrappers from the type.
-- (It removes a backtick from the name, if it finds one, and otherwise adds "_lemma".)
restate_axiom category.id_comp'
restate_axiom category.comp_id'
restate_axiom category.assoc'
attribute [simp] category.id_comp category.comp_id category.assoc
attribute [trans] category_struct.comp

lemma category.assoc_symm {C : Type u} [category.{v} C] {W X Y Z : C} (f : W ⟶ X) (g : X ⟶ Y) (h : Y ⟶ Z) :
  f ≫ (g ≫ h) = (f ≫ g) ≫ h :=
by rw ←category.assoc

/--
A `large_category` has objects in one universe level higher than the universe level of
the morphisms. It is useful for examples such as the category of types, or the category
of groups, etc.
-/
abbreviation large_category (C : Type (u+1)) : Type (u+1) := category.{u} C
/--
A `small_category` has objects and morphisms in the same universe level.
-/
abbreviation small_category (C : Type u)     : Type (u+1) := category.{u} C

section
variables {C : Type u} [𝒞 : category.{v} C] {X Y Z : C}
include 𝒞

class epi  (f : X ⟶ Y) : Prop :=
(left_cancellation : Π {Z : C} (g h : Y ⟶ Z) (w : f ≫ g = f ≫ h), g = h)
class mono (f : X ⟶ Y) : Prop :=
(right_cancellation : Π {Z : C} (g h : Z ⟶ X) (w : g ≫ f = h ≫ f), g = h)

@[simp] lemma cancel_epi  (f : X ⟶ Y) [epi f]  (g h : Y ⟶ Z) : (f ≫ g = f ≫ h) ↔ g = h :=
⟨ λ p, epi.left_cancellation g h p, begin intro a, subst a end ⟩
@[simp] lemma cancel_mono (f : X ⟶ Y) [mono f] (g h : Z ⟶ X) : (g ≫ f = h ≫ f) ↔ g = h :=
⟨ λ p, mono.right_cancellation g h p, begin intro a, subst a end ⟩
end

section
variable (C : Type u)
variable [category.{v} C]

universe u'

instance ulift_category : category.{v} (ulift.{u'} C) :=
{ hom  := λ X Y, (X.down ⟶ Y.down),
  id   := λ X, 𝟙 X.down,
  comp := λ _ _ _ f g, f ≫ g }

-- We verify that this previous instance can lift small categories to large categories.
example (D : Type u) [small_category D] : large_category (ulift.{u+1} D) := by apply_instance
end

variables (α : Type u)

instance [preorder α] : small_category α :=
{ hom  := λ U V, ulift (plift (U ≤ V)),
  id   := λ X, ⟨ ⟨ le_refl X ⟩ ⟩,
  comp := λ X Y Z f g, ⟨ ⟨ le_trans f.down.down g.down.down ⟩ ⟩ }

section
variables {C : Type u}

def End [has_hom.{v} C] (X : C) := X ⟶ X

instance End.has_one [category_struct.{v} C] {X : C} : has_one (End X) := by refine { one := 𝟙 X }
instance End.has_mul [category_struct.{v} C] {X : C} : has_mul (End X) := by refine { mul := λ x y, x ≫ y }
instance End.monoid [category.{v} C] {X : C} : monoid (End X) :=
by refine { .. End.has_one, .. End.has_mul, .. }; dsimp [has_mul.mul,has_one.one]; obviously

@[simp] lemma End.one_def {C : Type u} [category_struct.{v} C] {X : C} : (1 : End X) = 𝟙 X := rfl

@[simp] lemma End.mul_def {C : Type u} [category_struct.{v} C] {X : C} (xs ys : End X) : xs * ys = xs ≫ ys := rfl

end

end category_theory


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

! This file was ported from Lean 3 source module data.list.forall2
! leanprover-community/mathlib commit 5a3e819569b0f12cbec59d740a2613018e7b8eec
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.List.Infix

/-!
# Double universal quantification on a list

This file provides an API for `List.Forall₂` (definition in `Data.List.Defs`).
`Forall₂ R l₁ l₂` means that `l₁` and `l₂` have the same length, and whenever `a` is the nth element
of `l₁`, and `b` is the nth element of `l₂`, then `R a b` is satisfied.
-/

open Nat Function

namespace List

variable {α β γ δ : Type _} {R S : α → β → Prop} {P : γ → δ → Prop} {Rₐ : α → α → Prop}

open Relator

mk_iff_of_inductive_prop List.Forall₂ List.forall₂_iff
#align list.forall₂_iff List.forall₂_iff

#align list.forall₂.nil List.Forall₂.nil
#align list.forall₂.cons List.Forall₂.cons

@[simp]
theorem forall₂_cons {a b l₁ l₂} : Forall₂ R (a :: l₁) (b :: l₂) ↔ R a b ∧ Forall₂ R l₁ l₂ :=
  ⟨fun h => by cases' h with h₁ h₂; constructor <;> assumption, fun ⟨h₁, h₂⟩ =>
    Forall₂.cons h₁ h₂⟩
#align list.forall₂_cons List.forall₂_cons

theorem Forall₂.imp (H : ∀ a b, R a b → S a b) {l₁ l₂} (h : Forall₂ R l₁ l₂) : Forall₂ S l₁ l₂ := by
  induction h <;> constructor <;> solve_by_elim
#align list.forall₂.imp List.Forall₂.imp

theorem Forall₂.mp {Q : α → β → Prop} (h : ∀ a b, Q a b → R a b → S a b) :
    ∀ {l₁ l₂}, Forall₂ Q l₁ l₂ → Forall₂ R l₁ l₂ → Forall₂ S l₁ l₂
  | [], [], Forall₂.nil, Forall₂.nil => Forall₂.nil
  | a :: _, b :: _, Forall₂.cons hr hrs, Forall₂.cons hq hqs =>
    Forall₂.cons (h a b hr hq) (Forall₂.mp h hrs hqs)
#align list.forall₂.mp List.Forall₂.mp

theorem Forall₂.flip : ∀ {a b}, Forall₂ (flip R) b a → Forall₂ R a b
  | _, _, Forall₂.nil => Forall₂.nil
  | _ :: _, _ :: _, Forall₂.cons h₁ h₂ => Forall₂.cons h₁ h₂.flip
#align list.forall₂.flip List.Forall₂.flip

@[simp]
theorem forall₂_same : ∀ {l : List α}, Forall₂ Rₐ l l ↔ ∀ x ∈ l, Rₐ x x
  | [] => by simp
  | a :: l => by simp [@forall₂_same l]
#align list.forall₂_same List.forall₂_same

theorem forall₂_refl [IsRefl α Rₐ] (l : List α) : Forall₂ Rₐ l l :=
  forall₂_same.2 fun _ _ => refl _
#align list.forall₂_refl List.forall₂_refl

@[simp]
theorem forall₂_eq_eq_eq : Forall₂ ((· = ·) : α → α → Prop) = Eq := by
  funext a b; apply propext
  constructor
  · intro h
    induction h
    · rfl
    simp only [*]
  · rintro rfl
    exact forall₂_refl _
#align list.forall₂_eq_eq_eq List.forall₂_eq_eq_eq

@[simp]
theorem forall₂_nil_left_iff {l} : Forall₂ R nil l ↔ l = nil :=
  ⟨fun H => by cases H; rfl, by rintro rfl; exact Forall₂.nil⟩
#align list.forall₂_nil_left_iff List.forall₂_nil_left_iff

@[simp]
theorem forall₂_nil_right_iff {l} : Forall₂ R l nil ↔ l = nil :=
  ⟨fun H => by cases H; rfl, by rintro rfl; exact Forall₂.nil⟩
#align list.forall₂_nil_right_iff List.forall₂_nil_right_iff

theorem forall₂_cons_left_iff {a l u} :
    Forall₂ R (a :: l) u ↔ ∃ b u', R a b ∧ Forall₂ R l u' ∧ u = b :: u' :=
  Iff.intro
    (fun h =>
      match u, h with
      | b :: u', Forall₂.cons h₁ h₂ => ⟨b, u', h₁, h₂, rfl⟩)
    fun h =>
    match u, h with
    | _, ⟨_, _, h₁, h₂, rfl⟩ => Forall₂.cons h₁ h₂
#align list.forall₂_cons_left_iff List.forall₂_cons_left_iff

theorem forall₂_cons_right_iff {b l u} :
    Forall₂ R u (b :: l) ↔ ∃ a u', R a b ∧ Forall₂ R u' l ∧ u = a :: u' :=
  Iff.intro
    (fun h =>
      match u, h with
      | b :: u', Forall₂.cons h₁ h₂ => ⟨b, u', h₁, h₂, rfl⟩)
    fun h =>
    match u, h with
    | _, ⟨_, _, h₁, h₂, rfl⟩ => Forall₂.cons h₁ h₂
#align list.forall₂_cons_right_iff List.forall₂_cons_right_iff

theorem forall₂_and_left {p : α → Prop} :
    ∀ l u, Forall₂ (fun a b => p a ∧ R a b) l u ↔ (∀ a ∈ l, p a) ∧ Forall₂ R l u
  | [], u => by
    simp only [forall₂_nil_left_iff, forall_prop_of_false (not_mem_nil _), imp_true_iff,
      true_and_iff]
  | a :: l, u => by
    simp only [forall₂_and_left l, forall₂_cons_left_iff, forall_mem_cons, and_assoc,
      @and_comm _ (p a), @and_left_comm _ (p a), exists_and_left]
    simp only [and_comm, and_assoc, and_left_comm, ← exists_and_right]

#align list.forall₂_and_left List.forall₂_and_left

@[simp]
theorem forall₂_map_left_iff {f : γ → α} :
    ∀ {l u}, Forall₂ R (map f l) u ↔ Forall₂ (fun c b => R (f c) b) l u
  | [], _ => by simp only [map, forall₂_nil_left_iff]
  | a :: l, _ => by simp only [map, forall₂_cons_left_iff, forall₂_map_left_iff]
#align list.forall₂_map_left_iff List.forall₂_map_left_iff

@[simp]
theorem forall₂_map_right_iff {f : γ → β} :
    ∀ {l u}, Forall₂ R l (map f u) ↔ Forall₂ (fun a c => R a (f c)) l u
  | _, [] => by simp only [map, forall₂_nil_right_iff]
  | _, b :: u => by simp only [map, forall₂_cons_right_iff, forall₂_map_right_iff]
#align list.forall₂_map_right_iff List.forall₂_map_right_iff

theorem left_unique_forall₂' (hr : LeftUnique R) : ∀ {a b c}, Forall₂ R a c → Forall₂ R b c → a = b
  | _, _, _, Forall₂.nil, Forall₂.nil => rfl
  | _, _, _, Forall₂.cons ha₀ h₀, Forall₂.cons ha₁ h₁ =>
    hr ha₀ ha₁ ▸ left_unique_forall₂' hr h₀ h₁ ▸ rfl
#align list.left_unique_forall₂' List.left_unique_forall₂'

theorem _root_.Relator.LeftUnique.forall₂ (hr : LeftUnique R) : LeftUnique (Forall₂ R) :=
  @left_unique_forall₂' _ _ _ hr
#align relator.left_unique.forall₂ Relator.LeftUnique.forall₂

theorem right_unique_forall₂' (hr : RightUnique R) :
    ∀ {a b c}, Forall₂ R a b → Forall₂ R a c → b = c
  | _, _, _, Forall₂.nil, Forall₂.nil => rfl
  | _, _, _, Forall₂.cons ha₀ h₀, Forall₂.cons ha₁ h₁ =>
    hr ha₀ ha₁ ▸ right_unique_forall₂' hr h₀ h₁ ▸ rfl
#align list.right_unique_forall₂' List.right_unique_forall₂'

theorem _root_.Relator.RightUnique.forall₂ (hr : RightUnique R) : RightUnique (Forall₂ R) :=
  @right_unique_forall₂' _ _ _ hr
#align relator.right_unique.forall₂ Relator.RightUnique.forall₂

theorem _root_.Relator.BiUnique.forall₂ (hr : BiUnique R) : BiUnique (Forall₂ R) :=
  ⟨hr.left.forall₂, hr.right.forall₂⟩
#align relator.bi_unique.forall₂ Relator.BiUnique.forall₂

theorem Forall₂.length_eq : ∀ {l₁ l₂}, Forall₂ R l₁ l₂ → length l₁ = length l₂
  | _, _, Forall₂.nil => rfl
  | _, _, Forall₂.cons _ h₂ => congr_arg succ (Forall₂.length_eq h₂)
#align list.forall₂.length_eq List.Forall₂.length_eq

theorem Forall₂.nthLe :
    ∀ {x : List α} {y : List β} (_ : Forall₂ R x y) ⦃i : ℕ⦄ (hx : i < x.length) (hy : i < y.length),
      R (x.nthLe i hx) (y.nthLe i hy)
  | _, _, Forall₂.cons ha _, 0, _, _ => ha
  | _, _, Forall₂.cons _ hl, succ _, _, _ => hl.nthLe _ _
#align list.forall₂.nth_le List.Forall₂.nthLe

theorem forall₂_of_length_eq_of_nthLe :
    ∀ {x : List α} {y : List β},
      x.length = y.length → (∀ i h₁ h₂, R (x.nthLe i h₁) (y.nthLe i h₂)) → Forall₂ R x y
  | [], [], _, _ => Forall₂.nil
  | _ :: _, _ :: _, hl, h =>
    Forall₂.cons (h 0 (Nat.zero_lt_succ _) (Nat.zero_lt_succ _))
      (forall₂_of_length_eq_of_nthLe (succ.inj hl) fun i h₁ h₂ =>
        h i.succ (succ_lt_succ h₁) (succ_lt_succ h₂))
#align list.forall₂_of_length_eq_of_nth_le List.forall₂_of_length_eq_of_nthLe

theorem forall₂_iff_nthLe {l₁ : List α} {l₂ : List β} :
    Forall₂ R l₁ l₂ ↔ l₁.length = l₂.length ∧ ∀ i h₁ h₂, R (l₁.nthLe i h₁) (l₂.nthLe i h₂) :=
  ⟨fun h => ⟨h.length_eq, h.nthLe⟩, fun h => forall₂_of_length_eq_of_nthLe h.1 h.2⟩
#align list.forall₂_iff_nth_le List.forall₂_iff_nthLe

theorem forall₂_zip : ∀ {l₁ l₂}, Forall₂ R l₁ l₂ → ∀ {a b}, (a, b) ∈ zip l₁ l₂ → R a b
  | _, _, Forall₂.cons h₁ h₂, x, y, hx => by
    rw [zip, zipWith, mem_cons] at hx
    match hx with
    | Or.inl rfl => exact h₁
    | Or.inr h₃ => exact forall₂_zip h₂ h₃
#align list.forall₂_zip List.forall₂_zip

theorem forall₂_iff_zip {l₁ l₂} :
    Forall₂ R l₁ l₂ ↔ length l₁ = length l₂ ∧ ∀ {a b}, (a, b) ∈ zip l₁ l₂ → R a b :=
  ⟨fun h => ⟨Forall₂.length_eq h, @forall₂_zip _ _ _ _ _ h⟩, fun h =>
    by
    cases' h with h₁ h₂
    induction' l₁ with a l₁ IH generalizing l₂
    · cases length_eq_zero.1 h₁.symm
      constructor
    · cases' l₂ with b l₂
      . simp at h₁
      . simp only [length_cons, succ.injEq] at h₁
        exact Forall₂.cons (h₂ <| by simp [zip])
          (IH h₁ <| fun h => h₂ <| by
            simp only [zip, zipWith, find?, mem_cons, Prod.mk.injEq]; right
            simpa [zip] using h)⟩
#align list.forall₂_iff_zip List.forall₂_iff_zip

theorem forall₂_take : ∀ (n) {l₁ l₂}, Forall₂ R l₁ l₂ → Forall₂ R (take n l₁) (take n l₂)
  | 0, _, _, _ => by simp only [Forall₂.nil, take]
  | _ + 1, _, _, Forall₂.nil => by simp only [Forall₂.nil, take]
  | n + 1, _, _, Forall₂.cons h₁ h₂ => by simp [And.intro h₁ h₂, forall₂_take n]
#align list.forall₂_take List.forall₂_take

theorem forall₂_drop : ∀ (n) {l₁ l₂}, Forall₂ R l₁ l₂ → Forall₂ R (drop n l₁) (drop n l₂)
  | 0, _, _, h => by simp only [drop, h]
  | _ + 1, _, _, Forall₂.nil => by simp only [Forall₂.nil, drop]
  | n + 1, _, _, Forall₂.cons h₁ h₂ => by simp [And.intro h₁ h₂, forall₂_drop n]
#align list.forall₂_drop List.forall₂_drop

theorem forall₂_take_append (l : List α) (l₁ : List β) (l₂ : List β) (h : Forall₂ R l (l₁ ++ l₂)) :
    Forall₂ R (List.take (length l₁) l) l₁ := by
  have h' : Forall₂ R (take (length l₁) l) (take (length l₁) (l₁ ++ l₂)) :=
    forall₂_take (length l₁) h
  rwa [take_left] at h'
#align list.forall₂_take_append List.forall₂_take_append

theorem forall₂_drop_append (l : List α) (l₁ : List β) (l₂ : List β) (h : Forall₂ R l (l₁ ++ l₂)) :
    Forall₂ R (List.drop (length l₁) l) l₂ := by
  have h' : Forall₂ R (drop (length l₁) l) (drop (length l₁) (l₁ ++ l₂)) :=
    forall₂_drop (length l₁) h
  rwa [drop_left] at h'
#align list.forall₂_drop_append List.forall₂_drop_append

theorem rel_mem (hr : BiUnique R) : (R ⇒ Forall₂ R ⇒ Iff) (· ∈ ·) (· ∈ ·)
  | a, b, _, [], [], Forall₂.nil => by simp only [not_mem_nil]
  | a, b, h, a' :: as, b' :: bs, Forall₂.cons h₁ h₂ => by
    simp only [mem_cons]
    exact rel_or (rel_eq hr h h₁) (rel_mem hr h h₂)
#align list.rel_mem List.rel_mem

theorem rel_map : ((R ⇒ P) ⇒ Forall₂ R ⇒ Forall₂ P) map map
  | _, _, _, [], [], Forall₂.nil => Forall₂.nil
  | _, _, h, _ :: _, _ :: _, Forall₂.cons h₁ h₂ => Forall₂.cons (h h₁) (rel_map (@h) h₂)
#align list.rel_map List.rel_map

theorem rel_append : (Forall₂ R ⇒ Forall₂ R ⇒ Forall₂ R) (. ++ .) (. ++ .)
  | [], [], _, _, _, hl => hl
  | _, _, Forall₂.cons h₁ h₂, _, _, hl => Forall₂.cons h₁ (rel_append h₂ hl)
#align list.rel_append List.rel_append

theorem rel_reverse : (Forall₂ R ⇒ Forall₂ R) reverse reverse
  | [], [], Forall₂.nil => Forall₂.nil
  | _, _, Forall₂.cons h₁ h₂ =>
    by
    simp only [reverse_cons]
    exact rel_append (rel_reverse h₂) (Forall₂.cons h₁ Forall₂.nil)
#align list.rel_reverse List.rel_reverse

@[simp]
theorem forall₂_reverse_iff {l₁ l₂} : Forall₂ R (reverse l₁) (reverse l₂) ↔ Forall₂ R l₁ l₂ :=
  Iff.intro
    (fun h => by
      rw [← reverse_reverse l₁, ← reverse_reverse l₂]
      exact rel_reverse h)
    fun h => rel_reverse h
#align list.forall₂_reverse_iff List.forall₂_reverse_iff

theorem rel_join : (Forall₂ (Forall₂ R) ⇒ Forall₂ R) join join
  | [], [], Forall₂.nil => Forall₂.nil
  | _, _, Forall₂.cons h₁ h₂ => rel_append h₁ (rel_join h₂)
#align list.rel_join List.rel_join

theorem rel_bind : (Forall₂ R ⇒ (R ⇒ Forall₂ P) ⇒ Forall₂ P) List.bind List.bind :=
  fun _ _ h₁ _ _ h₂ => rel_join (rel_map (@h₂) h₁)
#align list.rel_bind List.rel_bind

theorem rel_foldl : ((P ⇒ R ⇒ P) ⇒ P ⇒ Forall₂ R ⇒ P) foldl foldl
  | _, _, _, _, _, h, _, _, Forall₂.nil => h
  | _, _, hfg, _, _, hxy, _, _, Forall₂.cons hab hs => rel_foldl (@hfg) (hfg hxy hab) hs
#align list.rel_foldl List.rel_foldl

theorem rel_foldr : ((R ⇒ P ⇒ P) ⇒ P ⇒ Forall₂ R ⇒ P) foldr foldr
  | _, _, _, _, _, h, _, _, Forall₂.nil => h
  | _, _, hfg, _, _, hxy, _, _, Forall₂.cons hab hs => hfg hab (rel_foldr (@hfg) hxy hs)
#align list.rel_foldr List.rel_foldr

theorem rel_filter {p : α → Bool} {q : β → Bool}
    (hpq : (R ⇒ (· ↔ ·)) (fun x => p x) (fun x => q x)) :
    (Forall₂ R ⇒ Forall₂ R) (filter p) (filter q)
  | _, _, Forall₂.nil => Forall₂.nil
  | a :: as, b :: bs, Forall₂.cons h₁ h₂ => by
    dsimp [LiftFun] at hpq
    by_cases h : p a
    · have : q b := by rwa [← hpq h₁]
      simp only [filter_cons_of_pos _ h, filter_cons_of_pos _ this, forall₂_cons, h₁, true_and_iff,
        rel_filter hpq h₂]
    · have : ¬q b := by rwa [← hpq h₁]
      simp only [filter_cons_of_neg _ h, filter_cons_of_neg _ this, rel_filter hpq h₂]
#align list.rel_filter List.rel_filter

theorem rel_filterMap : ((R ⇒ Option.Rel P) ⇒ Forall₂ R ⇒ Forall₂ P) filterMap filterMap
  | _, _, _, _, _, Forall₂.nil => Forall₂.nil
  | f, g, hfg, a :: as, b :: bs, Forall₂.cons h₁ h₂ => by
    rw [filterMap_cons, filterMap_cons]
    exact
      match f a, g b, hfg h₁ with
      | _, _, Option.Rel.none => rel_filterMap (@hfg) h₂
      | _, _, Option.Rel.some h => Forall₂.cons h (rel_filterMap (@hfg) h₂)
#align list.rel_filter_map List.rel_filterMap

@[to_additive]
theorem rel_prod [Monoid α] [Monoid β] (h : R 1 1) (hf : (R ⇒ R ⇒ R) (· * ·) (· * ·)) :
    (Forall₂ R ⇒ R) prod prod :=
  rel_foldl hf h
#align list.rel_prod List.rel_prod
#align list.rel_sum List.rel_sum

/-- Given a relation `R`, `sublist_forall₂ r l₁ l₂` indicates that there is a sublist of `l₂` such
  that `forall₂ r l₁ l₂`. -/
inductive SublistForall₂ (R : α → β → Prop) : List α → List β → Prop
  | nil {l} : SublistForall₂ R [] l
  | cons {a₁ a₂ l₁ l₂} : R a₁ a₂ → SublistForall₂ R l₁ l₂ → SublistForall₂ R (a₁ :: l₁) (a₂ :: l₂)
  | cons_right {a l₁ l₂} : SublistForall₂ R l₁ l₂ → SublistForall₂ R l₁ (a :: l₂)
#align list.sublist_forall₂ List.SublistForall₂
#align list.sublist_forall₂.nil List.SublistForall₂.nil
#align list.sublist_forall₂.cons List.SublistForall₂.cons
#align list.sublist_forall₂.cons_right List.SublistForall₂.cons_right

theorem sublistForall₂_iff {l₁ : List α} {l₂ : List β} :
    SublistForall₂ R l₁ l₂ ↔ ∃ l, Forall₂ R l₁ l ∧ l <+ l₂ := by
  constructor <;> intro h
  · induction' h with _ a b l1 l2 rab _ ih b l1 l2 _ ih
    · exact ⟨nil, Forall₂.nil, nil_sublist _⟩
    · obtain ⟨l, hl1, hl2⟩ := ih
      refine' ⟨b :: l, Forall₂.cons rab hl1, hl2.cons_cons b⟩
    · obtain ⟨l, hl1, hl2⟩ := ih
      exact ⟨l, hl1, hl2.trans (Sublist.cons _ (Sublist.refl _))⟩
  · obtain ⟨l, hl1, hl2⟩ := h
    revert l₁
    induction' hl2 with _ _ _ _ ih _ _ _ _ ih <;> intro l₁ hl1
    · rw [forall₂_nil_right_iff.1 hl1]
      exact SublistForall₂.nil
    · exact SublistForall₂.cons_right (ih hl1)
    · cases' hl1 with _ _ _ _ hr hl _
      exact SublistForall₂.cons hr (ih hl)
#align list.sublist_forall₂_iff List.sublistForall₂_iff

instance SublistForall₂.is_refl [IsRefl α Rₐ] : IsRefl (List α) (SublistForall₂ Rₐ) :=
  ⟨fun l => sublistForall₂_iff.2 ⟨l, forall₂_refl l, Sublist.refl l⟩⟩
#align list.sublist_forall₂.is_refl List.SublistForall₂.is_refl

instance SublistForall₂.is_trans [IsTrans α Rₐ] : IsTrans (List α) (SublistForall₂ Rₐ) :=
  ⟨fun a b c => by
    revert a b
    induction' c with _ _ ih
    · rintro _ _ h1 h2
      cases h2
      exact h1
    · rintro a b h1 h2
      cases' h2 with _ _ _ _ _ hbc tbc _ _ y1 btc
      · cases h1
        exact SublistForall₂.nil
      · cases' h1 with _ _ _ _ _ hab tab _ _ _ atb
        · exact SublistForall₂.nil
        · exact SublistForall₂.cons (_root_.trans hab hbc) (ih _ _ tab tbc)
        · exact SublistForall₂.cons_right (ih _ _ atb tbc)
      · exact SublistForall₂.cons_right (ih _ _ h1 btc)⟩
#align list.sublist_forall₂.is_trans List.SublistForall₂.is_trans

theorem Sublist.sublistForall₂ {l₁ l₂ : List α} (h : l₁ <+ l₂) [IsRefl α Rₐ] :
    SublistForall₂ Rₐ l₁ l₂ :=
  sublistForall₂_iff.2 ⟨l₁, forall₂_refl l₁, h⟩
#align list.sublist.sublist_forall₂ List.Sublist.sublistForall₂

theorem tail_sublistForall₂_self [IsRefl α Rₐ] (l : List α) : SublistForall₂ Rₐ l.tail l :=
  l.tail_sublist.sublistForall₂
#align list.tail_sublist_forall₂_self List.tail_sublistForall₂_self

end List


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov, Mario Carneiro, Sean Leather

! This file was ported from Lean 3 source module data.finset.option
! leanprover-community/mathlib commit c227d107bbada5d0d9d20287e3282c0a7f1651a0
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Finset.Card

/-!
# Finite sets in `option α`

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

In this file we define

* `option.to_finset`: construct an empty or singleton `finset α` from an `option α`;
* `finset.insert_none`: given `s : finset α`, lift it to a finset on `option α` using `option.some`
  and then insert `option.none`;
* `finset.erase_none`: given `s : finset (option α)`, returns `t : finset α` such that
  `x ∈ t ↔ some x ∈ s`.

Then we prove some basic lemmas about these definitions.

## Tags

finset, option
-/

variable {α β : Type _}

open Function

namespace Option

#print Option.toFinset /-
/-- Construct an empty or singleton finset from an `option` -/
def toFinset (o : Option α) : Finset α :=
  o.elim ∅ singleton
#align option.to_finset Option.toFinset
-/

#print Option.toFinset_none /-
@[simp]
theorem toFinset_none : none.toFinset = (∅ : Finset α) :=
  rfl
#align option.to_finset_none Option.toFinset_none
-/

#print Option.toFinset_some /-
@[simp]
theorem toFinset_some {a : α} : (some a).toFinset = {a} :=
  rfl
#align option.to_finset_some Option.toFinset_some
-/

#print Option.mem_toFinset /-
@[simp]
theorem mem_toFinset {a : α} {o : Option α} : a ∈ o.toFinset ↔ a ∈ o := by
  cases o <;> simp [eq_comm]
#align option.mem_to_finset Option.mem_toFinset
-/

#print Option.card_toFinset /-
theorem card_toFinset (o : Option α) : o.toFinset.card = o.elim 0 1 := by cases o <;> rfl
#align option.card_to_finset Option.card_toFinset
-/

end Option

namespace Finset

#print Finset.insertNone /-
/-- Given a finset on `α`, lift it to being a finset on `option α`
using `option.some` and then insert `option.none`. -/
def insertNone : Finset α ↪o Finset (Option α) :=
  OrderEmbedding.ofMapLEIff (fun s => cons none (s.map Embedding.some) <| by simp) fun s t =>
    cons_subset_cons.trans map_subset_map
#align finset.insert_none Finset.insertNone
-/

/- warning: finset.mem_insert_none -> Finset.mem_insertNone is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Finset.{u1} α} {o : Option.{u1} α}, Iff (Membership.Mem.{u1, u1} (Option.{u1} α) (Finset.{u1} (Option.{u1} α)) (Finset.hasMem.{u1} (Option.{u1} α)) o (coeFn.{succ u1, succ u1} (OrderEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))))) (fun (_x : RelEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) => (Finset.{u1} α) -> (Finset.{u1} (Option.{u1} α))) (RelEmbedding.hasCoeToFun.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) (Finset.insertNone.{u1} α) s)) (forall (a : α), (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) a o) -> (Membership.Mem.{u1, u1} α (Finset.{u1} α) (Finset.hasMem.{u1} α) a s))
but is expected to have type
  forall {α : Type.{u1}} {s : Finset.{u1} α} {o : Option.{u1} α}, Iff (Membership.mem.{u1, u1} (Option.{u1} α) ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Finset.{u1} α) => Finset.{u1} (Option.{u1} α)) s) (Finset.instMembershipFinset.{u1} (Option.{u1} α)) o (FunLike.coe.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (fun (_x : Finset.{u1} α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Finset.{u1} α) => Finset.{u1} (Option.{u1} α)) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Function.instEmbeddingLikeEmbedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)))) (RelEmbedding.toEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Finset.{u1} α) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Finset.{u1} α) => LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Finset.{u1} (Option.{u1} α)) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Finset.{u1} (Option.{u1} α)) => LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Finset.insertNone.{u1} α)) s)) (forall (a : α), (Membership.mem.{u1, u1} α (Option.{u1} α) (Option.instMembershipOption.{u1} α) a o) -> (Membership.mem.{u1, u1} α (Finset.{u1} α) (Finset.instMembershipFinset.{u1} α) a s))
Case conversion may be inaccurate. Consider using '#align finset.mem_insert_none Finset.mem_insertNoneₓ'. -/
/-⟨none ::ₘ s.1.map some, multiset.nodup_cons.2
  ⟨by simp, s.nodup.map $ λ a b, option.some.inj⟩⟩-/
@[simp]
theorem mem_insertNone {s : Finset α} : ∀ {o : Option α}, o ∈ s.insertNone ↔ ∀ a ∈ o, a ∈ s
  | none => iff_of_true (Multiset.mem_cons_self _ _) fun a h => by cases h
  | some a => Multiset.mem_cons.trans <| by simp <;> rfl
#align finset.mem_insert_none Finset.mem_insertNone

/- warning: finset.some_mem_insert_none -> Finset.some_mem_insertNone is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Finset.{u1} α} {a : α}, Iff (Membership.Mem.{u1, u1} (Option.{u1} α) (Finset.{u1} (Option.{u1} α)) (Finset.hasMem.{u1} (Option.{u1} α)) (Option.some.{u1} α a) (coeFn.{succ u1, succ u1} (OrderEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))))) (fun (_x : RelEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) => (Finset.{u1} α) -> (Finset.{u1} (Option.{u1} α))) (RelEmbedding.hasCoeToFun.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) (Finset.insertNone.{u1} α) s)) (Membership.Mem.{u1, u1} α (Finset.{u1} α) (Finset.hasMem.{u1} α) a s)
but is expected to have type
  forall {α : Type.{u1}} {s : Finset.{u1} α} {a : α}, Iff (Membership.mem.{u1, u1} (Option.{u1} α) ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Finset.{u1} α) => Finset.{u1} (Option.{u1} α)) s) (Finset.instMembershipFinset.{u1} (Option.{u1} α)) (Option.some.{u1} α a) (FunLike.coe.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (fun (_x : Finset.{u1} α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Finset.{u1} α) => Finset.{u1} (Option.{u1} α)) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Function.instEmbeddingLikeEmbedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)))) (RelEmbedding.toEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Finset.{u1} α) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Finset.{u1} α) => LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Finset.{u1} (Option.{u1} α)) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Finset.{u1} (Option.{u1} α)) => LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Finset.insertNone.{u1} α)) s)) (Membership.mem.{u1, u1} α (Finset.{u1} α) (Finset.instMembershipFinset.{u1} α) a s)
Case conversion may be inaccurate. Consider using '#align finset.some_mem_insert_none Finset.some_mem_insertNoneₓ'. -/
theorem some_mem_insertNone {s : Finset α} {a : α} : some a ∈ s.insertNone ↔ a ∈ s := by simp
#align finset.some_mem_insert_none Finset.some_mem_insertNone

/- warning: finset.card_insert_none -> Finset.card_insertNone is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (s : Finset.{u1} α), Eq.{1} Nat (Finset.card.{u1} (Option.{u1} α) (coeFn.{succ u1, succ u1} (OrderEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))))) (fun (_x : RelEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) => (Finset.{u1} α) -> (Finset.{u1} (Option.{u1} α))) (RelEmbedding.hasCoeToFun.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) (Finset.insertNone.{u1} α) s)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (Finset.card.{u1} α s) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))
but is expected to have type
  forall {α : Type.{u1}} (s : Finset.{u1} α), Eq.{1} Nat (Finset.card.{u1} (Option.{u1} α) (FunLike.coe.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (fun (_x : Finset.{u1} α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Finset.{u1} α) => Finset.{u1} (Option.{u1} α)) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Function.instEmbeddingLikeEmbedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)))) (RelEmbedding.toEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Finset.{u1} α) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Finset.{u1} α) => LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Finset.{u1} (Option.{u1} α)) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Finset.{u1} (Option.{u1} α)) => LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Finset.insertNone.{u1} α)) s)) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (Finset.card.{u1} α s) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))
Case conversion may be inaccurate. Consider using '#align finset.card_insert_none Finset.card_insertNoneₓ'. -/
@[simp]
theorem card_insertNone (s : Finset α) : s.insertNone.card = s.card + 1 := by simp [insert_none]
#align finset.card_insert_none Finset.card_insertNone

#print Finset.eraseNone /-
/-- Given `s : finset (option α)`, `s.erase_none : finset α` is the set of `x : α` such that
`some x ∈ s`. -/
def eraseNone : Finset (Option α) →o Finset α :=
  (Finset.mapEmbedding (Equiv.optionIsSomeEquiv α).toEmbedding).toOrderHom.comp
    ⟨Finset.subtype _, subtype_mono⟩
#align finset.erase_none Finset.eraseNone
-/

#print Finset.mem_eraseNone /-
@[simp]
theorem mem_eraseNone {s : Finset (Option α)} {x : α} : x ∈ s.eraseNone ↔ some x ∈ s := by
  simp [erase_none]
#align finset.mem_erase_none Finset.mem_eraseNone
-/

#print Finset.eraseNone_eq_bunionᵢ /-
theorem eraseNone_eq_bunionᵢ [DecidableEq α] (s : Finset (Option α)) :
    s.eraseNone = s.bunionᵢ Option.toFinset := by
  ext
  simp
#align finset.erase_none_eq_bUnion Finset.eraseNone_eq_bunionᵢ
-/

#print Finset.eraseNone_map_some /-
@[simp]
theorem eraseNone_map_some (s : Finset α) : (s.map Embedding.some).eraseNone = s :=
  by
  ext
  simp
#align finset.erase_none_map_some Finset.eraseNone_map_some
-/

#print Finset.eraseNone_image_some /-
@[simp]
theorem eraseNone_image_some [DecidableEq (Option α)] (s : Finset α) :
    (s.image some).eraseNone = s := by simpa only [map_eq_image] using erase_none_map_some s
#align finset.erase_none_image_some Finset.eraseNone_image_some
-/

#print Finset.coe_eraseNone /-
@[simp]
theorem coe_eraseNone (s : Finset (Option α)) : (s.eraseNone : Set α) = some ⁻¹' s :=
  Set.ext fun x => mem_eraseNone
#align finset.coe_erase_none Finset.coe_eraseNone
-/

#print Finset.eraseNone_union /-
@[simp]
theorem eraseNone_union [DecidableEq (Option α)] [DecidableEq α] (s t : Finset (Option α)) :
    (s ∪ t).eraseNone = s.eraseNone ∪ t.eraseNone :=
  by
  ext
  simp
#align finset.erase_none_union Finset.eraseNone_union
-/

#print Finset.eraseNone_inter /-
@[simp]
theorem eraseNone_inter [DecidableEq (Option α)] [DecidableEq α] (s t : Finset (Option α)) :
    (s ∩ t).eraseNone = s.eraseNone ∩ t.eraseNone :=
  by
  ext
  simp
#align finset.erase_none_inter Finset.eraseNone_inter
-/

#print Finset.eraseNone_empty /-
@[simp]
theorem eraseNone_empty : (∅ : Finset (Option α)).eraseNone = ∅ :=
  by
  ext
  simp
#align finset.erase_none_empty Finset.eraseNone_empty
-/

#print Finset.eraseNone_none /-
@[simp]
theorem eraseNone_none : ({none} : Finset (Option α)).eraseNone = ∅ :=
  by
  ext
  simp
#align finset.erase_none_none Finset.eraseNone_none
-/

#print Finset.image_some_eraseNone /-
@[simp]
theorem image_some_eraseNone [DecidableEq (Option α)] (s : Finset (Option α)) :
    s.eraseNone.image some = s.eraseₓ none := by ext (_ | x) <;> simp
#align finset.image_some_erase_none Finset.image_some_eraseNone
-/

#print Finset.map_some_eraseNone /-
@[simp]
theorem map_some_eraseNone [DecidableEq (Option α)] (s : Finset (Option α)) :
    s.eraseNone.map Embedding.some = s.eraseₓ none := by
  rw [map_eq_image, embedding.some_apply, image_some_erase_none]
#align finset.map_some_erase_none Finset.map_some_eraseNone
-/

/- warning: finset.insert_none_erase_none -> Finset.insertNone_eraseNone is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} (Option.{u1} α)] (s : Finset.{u1} (Option.{u1} α)), Eq.{succ u1} (Finset.{u1} (Option.{u1} α)) (coeFn.{succ u1, succ u1} (OrderEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))))) (fun (_x : RelEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) => (Finset.{u1} α) -> (Finset.{u1} (Option.{u1} α))) (RelEmbedding.hasCoeToFun.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) (Finset.insertNone.{u1} α) (coeFn.{succ u1, succ u1} (OrderHom.{u1, u1} (Finset.{u1} (Option.{u1} α)) (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) (fun (_x : OrderHom.{u1, u1} (Finset.{u1} (Option.{u1} α)) (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) => (Finset.{u1} (Option.{u1} α)) -> (Finset.{u1} α)) (OrderHom.hasCoeToFun.{u1, u1} (Finset.{u1} (Option.{u1} α)) (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) (Finset.eraseNone.{u1} α) s)) (Insert.insert.{u1, u1} (Option.{u1} α) (Finset.{u1} (Option.{u1} α)) (Finset.hasInsert.{u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => _inst_1 a b)) (Option.none.{u1} α) s)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : DecidableEq.{succ u1} (Option.{u1} α)] (s : Finset.{u1} (Option.{u1} α)), Eq.{succ u1} ((fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Finset.{u1} α) => Finset.{u1} (Option.{u1} α)) (OrderHom.toFun.{u1, u1} (Finset.{u1} (Option.{u1} α)) (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)) (Finset.eraseNone.{u1} α) s)) (FunLike.coe.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (fun (_x : Finset.{u1} α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Finset.{u1} α) => Finset.{u1} (Option.{u1} α)) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Function.instEmbeddingLikeEmbedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)))) (RelEmbedding.toEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Finset.{u1} α) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Finset.{u1} α) => LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Finset.{u1} (Option.{u1} α)) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Finset.{u1} (Option.{u1} α)) => LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Finset.insertNone.{u1} α)) (OrderHom.toFun.{u1, u1} (Finset.{u1} (Option.{u1} α)) (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)) (Finset.eraseNone.{u1} α) s)) (Insert.insert.{u1, u1} (Option.{u1} α) (Finset.{u1} (Option.{u1} α)) (Finset.instInsertFinset.{u1} (Option.{u1} α) (fun (a : Option.{u1} α) (b : Option.{u1} α) => _inst_1 a b)) (Option.none.{u1} α) s)
Case conversion may be inaccurate. Consider using '#align finset.insert_none_erase_none Finset.insertNone_eraseNoneₓ'. -/
@[simp]
theorem insertNone_eraseNone [DecidableEq (Option α)] (s : Finset (Option α)) :
    insertNone (eraseNone s) = insert none s := by ext (_ | x) <;> simp
#align finset.insert_none_erase_none Finset.insertNone_eraseNone

/- warning: finset.erase_none_insert_none -> Finset.eraseNone_insertNone is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (s : Finset.{u1} α), Eq.{succ u1} (Finset.{u1} α) (coeFn.{succ u1, succ u1} (OrderHom.{u1, u1} (Finset.{u1} (Option.{u1} α)) (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) (fun (_x : OrderHom.{u1, u1} (Finset.{u1} (Option.{u1} α)) (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) => (Finset.{u1} (Option.{u1} α)) -> (Finset.{u1} α)) (OrderHom.hasCoeToFun.{u1, u1} (Finset.{u1} (Option.{u1} α)) (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) (Finset.eraseNone.{u1} α) (coeFn.{succ u1, succ u1} (OrderEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))))) (fun (_x : RelEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) => (Finset.{u1} α) -> (Finset.{u1} (Option.{u1} α))) (RelEmbedding.hasCoeToFun.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)))) (LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))))) (Finset.insertNone.{u1} α) s)) s
but is expected to have type
  forall {α : Type.{u1}} (s : Finset.{u1} α), Eq.{succ u1} (Finset.{u1} α) (OrderHom.toFun.{u1, u1} (Finset.{u1} (Option.{u1} α)) (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α))) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α)) (Finset.eraseNone.{u1} α) (FunLike.coe.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (fun (_x : Finset.{u1} α) => (fun (x._@.Mathlib.Data.FunLike.Embedding._hyg.19 : Finset.{u1} α) => Finset.{u1} (Option.{u1} α)) _x) (EmbeddingLike.toFunLike.{succ u1, succ u1, succ u1} (Function.Embedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α))) (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (Function.instEmbeddingLikeEmbedding.{succ u1, succ u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)))) (RelEmbedding.toEmbedding.{u1, u1} (Finset.{u1} α) (Finset.{u1} (Option.{u1} α)) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.680 : Finset.{u1} α) (x._@.Mathlib.Order.Hom.Basic._hyg.682 : Finset.{u1} α) => LE.le.{u1} (Finset.{u1} α) (Preorder.toLE.{u1} (Finset.{u1} α) (PartialOrder.toPreorder.{u1} (Finset.{u1} α) (Finset.partialOrder.{u1} α))) x._@.Mathlib.Order.Hom.Basic._hyg.680 x._@.Mathlib.Order.Hom.Basic._hyg.682) (fun (x._@.Mathlib.Order.Hom.Basic._hyg.695 : Finset.{u1} (Option.{u1} α)) (x._@.Mathlib.Order.Hom.Basic._hyg.697 : Finset.{u1} (Option.{u1} α)) => LE.le.{u1} (Finset.{u1} (Option.{u1} α)) (Preorder.toLE.{u1} (Finset.{u1} (Option.{u1} α)) (PartialOrder.toPreorder.{u1} (Finset.{u1} (Option.{u1} α)) (Finset.partialOrder.{u1} (Option.{u1} α)))) x._@.Mathlib.Order.Hom.Basic._hyg.695 x._@.Mathlib.Order.Hom.Basic._hyg.697) (Finset.insertNone.{u1} α)) s)) s
Case conversion may be inaccurate. Consider using '#align finset.erase_none_insert_none Finset.eraseNone_insertNoneₓ'. -/
@[simp]
theorem eraseNone_insertNone (s : Finset α) : eraseNone (insertNone s) = s :=
  by
  ext
  simp
#align finset.erase_none_insert_none Finset.eraseNone_insertNone

end Finset