/-
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, Yury Kudryashov
-/
import algebra.big_operators.intervals
import algebra.big_operators.order
import algebra.indicator_function
import order.liminf_limsup
import order.filter.archimedean
import topology.order.basic

/-!
# Lemmas about liminf and limsup in an order topology.

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

open filter
open_locale topology classical

universes u v
variables {α : Type u} {β : Type v}

section liminf_limsup

section order_closed_topology
variables [semilattice_sup α] [topological_space α] [order_topology α]

lemma is_bounded_le_nhds (a : α) : (𝓝 a).is_bounded (≤) :=
(is_top_or_exists_gt a).elim (λ h, ⟨a, eventually_of_forall h⟩) (λ ⟨b, hb⟩, ⟨b, ge_mem_nhds hb⟩)

lemma filter.tendsto.is_bounded_under_le {f : filter β} {u : β → α} {a : α}
  (h : tendsto u f (𝓝 a)) : f.is_bounded_under (≤) u :=
(is_bounded_le_nhds a).mono h

lemma filter.tendsto.bdd_above_range_of_cofinite {u : β → α} {a : α}
  (h : tendsto u cofinite (𝓝 a)) : bdd_above (set.range u) :=
h.is_bounded_under_le.bdd_above_range_of_cofinite

lemma filter.tendsto.bdd_above_range {u : ℕ → α} {a : α}
  (h : tendsto u at_top (𝓝 a)) : bdd_above (set.range u) :=
h.is_bounded_under_le.bdd_above_range

lemma is_cobounded_ge_nhds (a : α) : (𝓝 a).is_cobounded (≥) :=
(is_bounded_le_nhds a).is_cobounded_flip

lemma filter.tendsto.is_cobounded_under_ge {f : filter β} {u : β → α} {a : α}
  [ne_bot f] (h : tendsto u f (𝓝 a)) : f.is_cobounded_under (≥) u :=
h.is_bounded_under_le.is_cobounded_flip

lemma is_bounded_le_at_bot (α : Type*) [hα : nonempty α] [preorder α] :
  (at_bot : filter α).is_bounded (≤) :=
is_bounded_iff.2 ⟨set.Iic hα.some, mem_at_bot _, hα.some, λ x hx, hx⟩

lemma filter.tendsto.is_bounded_under_le_at_bot {α : Type*} [nonempty α] [preorder α]
  {f : filter β} {u : β → α} (h : tendsto u f at_bot) :
  f.is_bounded_under (≤) u :=
(is_bounded_le_at_bot α).mono h

lemma bdd_above_range_of_tendsto_at_top_at_bot {α : Type*} [nonempty α] [semilattice_sup α]
  {u : ℕ → α} (hx : tendsto u at_top at_bot) : bdd_above (set.range u) :=
(filter.tendsto.is_bounded_under_le_at_bot hx).bdd_above_range

end order_closed_topology

section order_closed_topology
variables [semilattice_inf α] [topological_space α] [order_topology α]

lemma is_bounded_ge_nhds (a : α) : (𝓝 a).is_bounded (≥) := @is_bounded_le_nhds αᵒᵈ _ _ _ a

lemma filter.tendsto.is_bounded_under_ge {f : filter β} {u : β → α} {a : α}
  (h : tendsto u f (𝓝 a)) : f.is_bounded_under (≥) u :=
(is_bounded_ge_nhds a).mono h

lemma filter.tendsto.bdd_below_range_of_cofinite {u : β → α} {a : α}
  (h : tendsto u cofinite (𝓝 a)) : bdd_below (set.range u) :=
h.is_bounded_under_ge.bdd_below_range_of_cofinite

lemma filter.tendsto.bdd_below_range {u : ℕ → α} {a : α}
  (h : tendsto u at_top (𝓝 a)) : bdd_below (set.range u) :=
h.is_bounded_under_ge.bdd_below_range

lemma is_cobounded_le_nhds (a : α) : (𝓝 a).is_cobounded (≤) :=
(is_bounded_ge_nhds a).is_cobounded_flip

lemma filter.tendsto.is_cobounded_under_le {f : filter β} {u : β → α} {a : α}
  [ne_bot f] (h : tendsto u f (𝓝 a)) : f.is_cobounded_under (≤) u :=
h.is_bounded_under_ge.is_cobounded_flip

lemma is_bounded_ge_at_top (α : Type*) [hα : nonempty α] [preorder α] :
  (at_top : filter α).is_bounded (≥) :=
is_bounded_le_at_bot αᵒᵈ

lemma filter.tendsto.is_bounded_under_ge_at_top {α : Type*} [nonempty α] [preorder α]
  {f : filter β} {u : β → α} (h : tendsto u f at_top) :
  f.is_bounded_under (≥) u :=
(is_bounded_ge_at_top α).mono h

lemma bdd_below_range_of_tendsto_at_top_at_top {α : Type*} [nonempty α] [semilattice_inf α]
  {u : ℕ → α} (hx : tendsto u at_top at_top) : bdd_below (set.range u) :=
(filter.tendsto.is_bounded_under_ge_at_top hx).bdd_below_range

end order_closed_topology

section conditionally_complete_linear_order
variables [conditionally_complete_linear_order α]

theorem lt_mem_sets_of_Limsup_lt {f : filter α} {b} (h : f.is_bounded (≤)) (l : f.Limsup < b) :
  ∀ᶠ a in f, a < b :=
let ⟨c, (h : ∀ᶠ a in f, a ≤ c), hcb⟩ := exists_lt_of_cInf_lt h l in
mem_of_superset h $ assume a hac, lt_of_le_of_lt hac hcb

theorem gt_mem_sets_of_Liminf_gt : ∀ {f : filter α} {b}, f.is_bounded (≥) → b < f.Liminf →
  ∀ᶠ a in f, b < a :=
@lt_mem_sets_of_Limsup_lt αᵒᵈ _

variables [topological_space α] [order_topology α]

/-- If the liminf and the limsup of a filter coincide, then this filter converges to
their common value, at least if the filter is eventually bounded above and below. -/
theorem le_nhds_of_Limsup_eq_Liminf {f : filter α} {a : α}
  (hl : f.is_bounded (≤)) (hg : f.is_bounded (≥)) (hs : f.Limsup = a) (hi : f.Liminf = a) :
  f ≤ 𝓝 a :=
tendsto_order.2 $ and.intro
  (assume b hb, gt_mem_sets_of_Liminf_gt hg $ hi.symm ▸ hb)
  (assume b hb, lt_mem_sets_of_Limsup_lt hl $ hs.symm ▸ hb)

theorem Limsup_nhds (a : α) : Limsup (𝓝 a) = a :=
cInf_eq_of_forall_ge_of_forall_gt_exists_lt (is_bounded_le_nhds a)
  (assume a' (h : {n : α | n ≤ a'} ∈ 𝓝 a), show a ≤ a', from @mem_of_mem_nhds α _ a _ h)
  (assume b (hba : a < b), show ∃c (h : {n : α | n ≤ c} ∈ 𝓝 a), c < b, from
    match dense_or_discrete a b with
    | or.inl ⟨c, hac, hcb⟩ := ⟨c, ge_mem_nhds hac, hcb⟩
    | or.inr ⟨_, h⟩        := ⟨a, (𝓝 a).sets_of_superset (gt_mem_nhds hba) h, hba⟩
    end)

theorem Liminf_nhds : ∀ (a : α), Liminf (𝓝 a) = a := @Limsup_nhds αᵒᵈ _ _ _

/-- If a filter is converging, its limsup coincides with its limit. -/
theorem Liminf_eq_of_le_nhds {f : filter α} {a : α} [ne_bot f] (h : f ≤ 𝓝 a) : f.Liminf = a :=
have hb_ge : is_bounded (≥) f, from (is_bounded_ge_nhds a).mono h,
have hb_le : is_bounded (≤) f, from (is_bounded_le_nhds a).mono h,
le_antisymm
  (calc f.Liminf ≤ f.Limsup : Liminf_le_Limsup hb_le hb_ge
    ... ≤ (𝓝 a).Limsup :
      Limsup_le_Limsup_of_le h hb_ge.is_cobounded_flip (is_bounded_le_nhds a)
    ... = a : Limsup_nhds a)
  (calc a = (𝓝 a).Liminf : (Liminf_nhds a).symm
    ... ≤ f.Liminf :
      Liminf_le_Liminf_of_le h (is_bounded_ge_nhds a) hb_le.is_cobounded_flip)

/-- If a filter is converging, its liminf coincides with its limit. -/
theorem Limsup_eq_of_le_nhds : ∀ {f : filter α} {a : α} [ne_bot f], f ≤ 𝓝 a → f.Limsup = a :=
@Liminf_eq_of_le_nhds αᵒᵈ _ _ _

/-- If a function has a limit, then its limsup coincides with its limit. -/
theorem filter.tendsto.limsup_eq {f : filter β} {u : β → α} {a : α} [ne_bot f]
  (h : tendsto u f (𝓝 a)) : limsup u f = a :=
Limsup_eq_of_le_nhds h

/-- If a function has a limit, then its liminf coincides with its limit. -/
theorem filter.tendsto.liminf_eq {f : filter β} {u : β → α} {a : α} [ne_bot f]
  (h : tendsto u f (𝓝 a)) : liminf u f = a :=
Liminf_eq_of_le_nhds h

/-- If the liminf and the limsup of a function coincide, then the limit of the function
exists and has the same value -/
theorem tendsto_of_liminf_eq_limsup {f : filter β} {u : β → α} {a : α}
  (hinf : liminf u f = a) (hsup : limsup u f = a)
  (h : f.is_bounded_under (≤) u . is_bounded_default)
  (h' : f.is_bounded_under (≥) u . is_bounded_default) :
  tendsto u f (𝓝 a) :=
le_nhds_of_Limsup_eq_Liminf h h' hsup hinf

/-- If a number `a` is less than or equal to the `liminf` of a function `f` at some filter
and is greater than or equal to the `limsup` of `f`, then `f` tends to `a` along this filter. -/
theorem tendsto_of_le_liminf_of_limsup_le {f : filter β} {u : β → α} {a : α}
  (hinf : a ≤ liminf u f) (hsup : limsup u f ≤ a)
  (h : f.is_bounded_under (≤) u . is_bounded_default)
  (h' : f.is_bounded_under (≥) u . is_bounded_default) :
  tendsto u f (𝓝 a) :=
if hf : f = ⊥ then hf.symm ▸ tendsto_bot
else by haveI : ne_bot f := ⟨hf⟩; exact tendsto_of_liminf_eq_limsup
  (le_antisymm (le_trans (liminf_le_limsup h h') hsup) hinf)
  (le_antisymm hsup (le_trans hinf (liminf_le_limsup h h'))) h h'

/-- Assume that, for any `a < b`, a sequence can not be infinitely many times below `a` and
above `b`. If it is also ultimately bounded above and below, then it has to converge. This even
works if `a` and `b` are restricted to a dense subset.
-/
lemma tendsto_of_no_upcrossings [densely_ordered α]
  {f : filter β} {u : β → α} {s : set α} (hs : dense s)
  (H : ∀ (a ∈ s) (b ∈ s), a < b → ¬((∃ᶠ n in f, u n < a) ∧ (∃ᶠ n in f, b < u n)))
  (h : f.is_bounded_under (≤) u . is_bounded_default)
  (h' : f.is_bounded_under (≥) u . is_bounded_default) :
  ∃ (c : α), tendsto u f (𝓝 c) :=
begin
  by_cases hbot : f = ⊥, { rw hbot, exact ⟨Inf ∅, tendsto_bot⟩ },
  haveI : ne_bot f := ⟨hbot⟩,
  refine ⟨limsup u f, _⟩,
  apply tendsto_of_le_liminf_of_limsup_le _ le_rfl h h',
  by_contra' hlt,
  obtain ⟨a, ⟨⟨la, au⟩, as⟩⟩ : ∃ a, (f.liminf u < a ∧ a < f.limsup u) ∧ a ∈ s :=
    dense_iff_inter_open.1 hs (set.Ioo (f.liminf u) (f.limsup u)) is_open_Ioo
    (set.nonempty_Ioo.2 hlt),
  obtain ⟨b, ⟨⟨ab, bu⟩, bs⟩⟩ : ∃ b, (a < b ∧ b < f.limsup u) ∧ b ∈ s :=
    dense_iff_inter_open.1 hs (set.Ioo a (f.limsup u)) is_open_Ioo
    (set.nonempty_Ioo.2 au),
  have A : ∃ᶠ n in f, u n < a :=
    frequently_lt_of_liminf_lt (is_bounded.is_cobounded_ge h) la,
  have B : ∃ᶠ n in f, b < u n :=
    frequently_lt_of_lt_limsup (is_bounded.is_cobounded_le h') bu,
  exact H a as b bs ab ⟨A, B⟩,
end

end conditionally_complete_linear_order

end liminf_limsup

section monotone

variables {ι R S : Type*} {F : filter ι} [ne_bot F]
  [complete_linear_order R] [topological_space R] [order_topology R]
  [complete_linear_order S] [topological_space S] [order_topology S]

/-- An antitone function between complete linear ordered spaces sends a `filter.Limsup`
to the `filter.liminf` of the image if it is continuous at the `Limsup`. -/
lemma antitone.map_Limsup_of_continuous_at {F : filter R} [ne_bot F]
  {f : R → S} (f_decr : antitone f) (f_cont : continuous_at f (F.Limsup)) :
  f (F.Limsup) = F.liminf f :=
begin
  apply le_antisymm,
  { have A : {a : R | ∀ᶠ (n : R) in F, n ≤ a}.nonempty, from ⟨⊤, by simp⟩,
    rw [Limsup, (f_decr.map_Inf_of_continuous_at' f_cont A)],
    apply le_of_forall_lt,
    assume c hc,
    simp only [liminf, Liminf, lt_Sup_iff, eventually_map, set.mem_set_of_eq, exists_prop,
      set.mem_image, exists_exists_and_eq_and] at hc ⊢,
    rcases hc with ⟨d, hd, h'd⟩,
    refine ⟨f d, _, h'd⟩,
    filter_upwards [hd] with x hx using f_decr hx },
  { rcases eq_or_lt_of_le (bot_le : ⊥ ≤ F.Limsup) with h|Limsup_ne_bot,
    { rw ← h,
      apply liminf_le_of_frequently_le,
      apply frequently_of_forall,
      assume x,
      exact f_decr bot_le },
    by_cases h' : ∃ c, c < F.Limsup ∧ set.Ioo c F.Limsup = ∅,
    { rcases h' with ⟨c, c_lt, hc⟩,
      have B : ∃ᶠ n in F, F.Limsup ≤ n,
      { apply (frequently_lt_of_lt_Limsup (by is_bounded_default) c_lt).mono,
        assume x hx,
        by_contra',
        have : (set.Ioo c F.Limsup).nonempty := ⟨x, ⟨hx, this⟩⟩,
        simpa [hc] },
      apply liminf_le_of_frequently_le,
      exact B.mono (λ x hx, f_decr hx) },
    by_contra' H,
    obtain ⟨l, l_lt, h'l⟩ : ∃ l < F.Limsup, set.Ioc l F.Limsup ⊆ {x : R | f x < F.liminf f},
      from exists_Ioc_subset_of_mem_nhds ((tendsto_order.1 f_cont.tendsto).2 _ H)
        ⟨⊥, Limsup_ne_bot⟩,
    obtain ⟨m, l_m, m_lt⟩  : (set.Ioo l F.Limsup).nonempty,
    { contrapose! h',
      refine ⟨l, l_lt, by rwa set.not_nonempty_iff_eq_empty at h'⟩ },
    have B : F.liminf f ≤ f m,
    { apply liminf_le_of_frequently_le,
      apply (frequently_lt_of_lt_Limsup (by is_bounded_default) m_lt).mono,
      assume x hx,
      exact f_decr hx.le },
    have I : f m < F.liminf f := h'l ⟨l_m, m_lt.le⟩,
    exact lt_irrefl _ (B.trans_lt I) }
end

/-- A continuous antitone function between complete linear ordered spaces sends a `filter.limsup`
to the `filter.liminf` of the images. -/
lemma antitone.map_limsup_of_continuous_at
  {f : R → S} (f_decr : antitone f) (a : ι → R) (f_cont : continuous_at f (F.limsup a)) :
  f (F.limsup a) = F.liminf (f ∘ a) :=
f_decr.map_Limsup_of_continuous_at f_cont

/-- An antitone function between complete linear ordered spaces sends a `filter.Liminf`
to the `filter.limsup` of the image if it is continuous at the `Liminf`. -/
lemma antitone.map_Liminf_of_continuous_at {F : filter R} [ne_bot F]
  {f : R → S} (f_decr : antitone f) (f_cont : continuous_at f (F.Liminf)) :
  f (F.Liminf) = F.limsup f :=
@antitone.map_Limsup_of_continuous_at
  (order_dual R) (order_dual S) _ _ _ _ _ _ _ _ f f_decr.dual f_cont

/-- A continuous antitone function between complete linear ordered spaces sends a `filter.liminf`
to the `filter.limsup` of the images. -/
lemma antitone.map_liminf_of_continuous_at
  {f : R → S} (f_decr : antitone f) (a : ι → R) (f_cont : continuous_at f (F.liminf a)) :
  f (F.liminf a) = F.limsup (f ∘ a) :=
f_decr.map_Liminf_of_continuous_at f_cont

/-- A monotone function between complete linear ordered spaces sends a `filter.Limsup`
to the `filter.limsup` of the image if it is continuous at the `Limsup`. -/
lemma monotone.map_Limsup_of_continuous_at {F : filter R} [ne_bot F]
  {f : R → S} (f_incr : monotone f) (f_cont : continuous_at f (F.Limsup)) :
  f (F.Limsup) = F.limsup f :=
@antitone.map_Limsup_of_continuous_at R (order_dual S) _ _ _ _ _ _ _ _ f f_incr f_cont

/-- A continuous monotone function between complete linear ordered spaces sends a `filter.limsup`
to the `filter.limsup` of the images. -/
lemma monotone.map_limsup_of_continuous_at
  {f : R → S} (f_incr : monotone f) (a : ι → R) (f_cont : continuous_at f (F.limsup a)) :
  f (F.limsup a) = F.limsup (f ∘ a) :=
f_incr.map_Limsup_of_continuous_at f_cont

/-- A monotone function between complete linear ordered spaces sends a `filter.Liminf`
to the `filter.liminf` of the image if it is continuous at the `Liminf`. -/
lemma monotone.map_Liminf_of_continuous_at {F : filter R} [ne_bot F]
  {f : R → S} (f_incr : monotone f) (f_cont : continuous_at f (F.Liminf)) :
  f (F.Liminf) = F.liminf f :=
@antitone.map_Liminf_of_continuous_at R (order_dual S) _ _ _ _ _ _ _ _ f f_incr f_cont

/-- A continuous monotone function between complete linear ordered spaces sends a `filter.liminf`
to the `filter.liminf` of the images. -/
lemma monotone.map_liminf_of_continuous_at
  {f : R → S} (f_incr : monotone f) (a : ι → R) (f_cont : continuous_at f (F.liminf a)) :
  f (F.liminf a) = F.liminf (f ∘ a) :=
f_incr.map_Liminf_of_continuous_at f_cont

end monotone

section infi_and_supr

open_locale topology

open filter set

variables {ι : Type*} {R : Type*} [complete_linear_order R] [topological_space R] [order_topology R]

lemma infi_eq_of_forall_le_of_tendsto {x : R} {as : ι → R}
  (x_le : ∀ i, x ≤ as i) {F : filter ι} [filter.ne_bot F] (as_lim : filter.tendsto as F (𝓝 x)) :
  (⨅ i, as i) = x :=
begin
  refine infi_eq_of_forall_ge_of_forall_gt_exists_lt (λ i, x_le i) _,
  apply λ w x_lt_w, ‹filter.ne_bot F›.nonempty_of_mem (eventually_lt_of_tendsto_lt x_lt_w as_lim),
end

lemma supr_eq_of_forall_le_of_tendsto {x : R} {as : ι → R}
  (le_x : ∀ i, as i ≤ x) {F : filter ι} [filter.ne_bot F] (as_lim : filter.tendsto as F (𝓝 x)) :
  (⨆ i, as i) = x :=
@infi_eq_of_forall_le_of_tendsto ι (order_dual R) _ _ _ x as le_x F _ as_lim

lemma Union_Ici_eq_Ioi_of_lt_of_tendsto {ι : Type*} (x : R) {as : ι → R} (x_lt : ∀ i, x < as i)
  {F : filter ι} [filter.ne_bot F] (as_lim : filter.tendsto as F (𝓝 x)) :
  (⋃ (i : ι), Ici (as i)) = Ioi x :=
begin
  have obs : x ∉ range as,
  { intro maybe_x_is,
    rcases mem_range.mp maybe_x_is with ⟨i, hi⟩,
    simpa only [hi, lt_self_iff_false] using x_lt i, } ,
  rw ← infi_eq_of_forall_le_of_tendsto (λ i, (x_lt i).le) as_lim at *,
  exact Union_Ici_eq_Ioi_infi obs,
end

lemma Union_Iic_eq_Iio_of_lt_of_tendsto {ι : Type*} (x : R) {as : ι → R} (lt_x : ∀ i, as i < x)
  {F : filter ι} [filter.ne_bot F] (as_lim : filter.tendsto as F (𝓝 x)) :
  (⋃ (i : ι), Iic (as i)) = Iio x :=
@Union_Ici_eq_Ioi_of_lt_of_tendsto (order_dual R) _ _ _ ι x as lt_x F _ as_lim

end infi_and_supr

section indicator

open_locale big_operators

lemma limsup_eq_tendsto_sum_indicator_nat_at_top (s : ℕ → set α) :
  limsup s at_top =
    {ω | tendsto (λ n, ∑ k in finset.range n, (s (k + 1)).indicator (1 : α → ℕ) ω) at_top at_top} :=
begin
  ext ω,
  simp only [limsup_eq_infi_supr_of_nat, ge_iff_le, set.supr_eq_Union,
      set.infi_eq_Inter, set.mem_Inter, set.mem_Union, exists_prop],
  split,
  { intro hω,
    refine tendsto_at_top_at_top_of_monotone' (λ n m hnm, finset.sum_mono_set_of_nonneg
      (λ i, set.indicator_nonneg (λ _ _, zero_le_one) _) (finset.range_mono hnm)) _,
    rintro ⟨i, h⟩,
    simp only [mem_upper_bounds, set.mem_range, forall_exists_index, forall_apply_eq_imp_iff'] at h,
    induction i with k hk,
    { obtain ⟨j, hj₁, hj₂⟩ := hω 1,
      refine not_lt.2 (h $ j + 1) (lt_of_le_of_lt
        (finset.sum_const_zero.symm : 0 = ∑ k in finset.range (j + 1), 0).le _),
      refine finset.sum_lt_sum (λ m _, set.indicator_nonneg (λ _ _, zero_le_one) _)
        ⟨j - 1, finset.mem_range.2 (lt_of_le_of_lt (nat.sub_le _ _) j.lt_succ_self), _⟩,
      rw [nat.sub_add_cancel hj₁, set.indicator_of_mem hj₂],
      exact zero_lt_one },
    { rw imp_false at hk,
      push_neg at hk,
      obtain ⟨i, hi⟩ := hk,
      obtain ⟨j, hj₁, hj₂⟩ := hω (i + 1),
      replace hi : ∑ k in finset.range i, (s (k + 1)).indicator 1 ω = k + 1 := le_antisymm (h i) hi,
      refine not_lt.2 (h $ j + 1) _,
      rw [← finset.sum_range_add_sum_Ico _ (i.le_succ.trans (hj₁.trans j.le_succ)), hi],
      refine lt_add_of_pos_right _ _,
      rw (finset.sum_const_zero.symm : 0 = ∑ k in finset.Ico i (j + 1), 0),
      refine finset.sum_lt_sum (λ m _, set.indicator_nonneg (λ _ _, zero_le_one) _)
        ⟨j - 1, finset.mem_Ico.2
        ⟨(nat.le_sub_iff_right (le_trans ((le_add_iff_nonneg_left _).2 zero_le') hj₁)).2 hj₁,
          lt_of_le_of_lt (nat.sub_le _ _) j.lt_succ_self⟩, _⟩,
      rw [nat.sub_add_cancel (le_trans ((le_add_iff_nonneg_left _).2 zero_le') hj₁),
        set.indicator_of_mem hj₂],
      exact zero_lt_one } },
  { rintro hω i,
    rw [set.mem_set_of_eq, tendsto_at_top_at_top] at hω,
    by_contra hcon,
    push_neg at hcon,
    obtain ⟨j, h⟩ := hω (i + 1),
    have : ∑ k in finset.range j, (s (k + 1)).indicator 1 ω ≤ i,
    { have hle : ∀ j ≤ i, ∑ k in finset.range j, (s (k + 1)).indicator 1 ω ≤ i,
      { refine λ j hij, (finset.sum_le_card_nsmul _ _ _ _ : _ ≤ (finset.range j).card • 1).trans _,
        { exact λ m hm, set.indicator_apply_le' (λ _, le_rfl) (λ _, zero_le_one) },
        { simpa only [finset.card_range, smul_eq_mul, mul_one] } },
      by_cases hij : j < i,
      { exact hle _ hij.le },
      { rw ← finset.sum_range_add_sum_Ico _ (not_lt.1 hij),
        suffices : ∑ k in finset.Ico i j, (s (k + 1)).indicator 1 ω = 0,
        { rw [this, add_zero],
          exact hle _ le_rfl },
        rw finset.sum_eq_zero (λ m hm, _),
        exact set.indicator_of_not_mem (hcon _ $ (finset.mem_Ico.1 hm).1.trans m.le_succ) _ } },
    exact not_le.2 (lt_of_lt_of_le i.lt_succ_self $ h _ le_rfl) this }
end

lemma limsup_eq_tendsto_sum_indicator_at_top
  (R : Type*) [strict_ordered_semiring R] [archimedean R] (s : ℕ → set α) :
  limsup s at_top =
    {ω | tendsto (λ n, ∑ k in finset.range n, (s (k + 1)).indicator (1 : α → R) ω) at_top at_top} :=
begin
  rw limsup_eq_tendsto_sum_indicator_nat_at_top s,
  ext ω,
  simp only [set.mem_set_of_eq],
  rw (_ : (λ n, ∑ k in finset.range n, (s (k + 1)).indicator (1 : α → R) ω) =
    (λ n, ↑(∑ k in finset.range n, (s (k + 1)).indicator (1 : α → ℕ) ω))),
  { exact tendsto_coe_nat_at_top_iff.symm },
  { ext n,
    simp only [set.indicator, pi.one_apply, finset.sum_boole, nat.cast_id] }
end

end indicator


/-
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

! This file was ported from Lean 3 source module control.basic
! leanprover-community/mathlib commit 08aeb33b5b693fb1392a7568ae2c0b253516535e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Control.SimpSet
import Mathlib.Tactic.CasesM
import Mathlib.Init.Control.Combinators

/-!
Extends the theory on functors, applicatives and monads.
-/

universe u v w

variable {α β γ : Type u}

section Functor

variable {f : Type u → Type v} [Functor f] [LawfulFunctor f]
@[functor_norm]
theorem Functor.map_map (m : α → β) (g : β → γ) (x : f α) : g <$> m <$> x = (g ∘ m) <$> x :=
  (comp_map _ _ _).symm
#align functor.map_map Functor.map_mapₓ
-- order of implicits

attribute [simp] id_map'
#align id_map' id_map'ₓ
-- order of implicits

end Functor

section Applicative

variable {F : Type u → Type v} [Applicative F]

/-- A generalization of `List.zipWith` which combines list elements with an `Applicative`. -/
def zipWithM {α₁ α₂ φ : Type u} (f : α₁ → α₂ → F φ) : ∀ (_ : List α₁) (_ : List α₂), F (List φ)
  | x :: xs, y :: ys => (· :: ·) <$> f x y <*> zipWithM f xs ys
  | _, _ => pure []
#align mzip_with zipWithM

/-- Like `zipWithM` but evaluates the result as it traverses the lists using `*>`. -/
def zipWithM' (f : α → β → F γ) : List α → List β → F PUnit
  | x :: xs, y :: ys => f x y *> zipWithM' f xs ys
  | [], _ => pure PUnit.unit
  | _, [] => pure PUnit.unit
#align mzip_with' zipWithM'

variable [LawfulApplicative F]

attribute [functor_norm] seq_assoc pure_seq

@[simp]
theorem pure_id'_seq (x : F α) : (pure fun x => x) <*> x = x :=
  pure_id_seq x
#align pure_id'_seq pure_id'_seq

attribute [functor_norm] seq_assoc pure_seq

@[functor_norm]
theorem seq_map_assoc (x : F (α → β)) (f : γ → α) (y : F γ) :
    x <*> f <$> y = (· ∘ f) <$> x <*> y := by
  simp [← pure_seq]
  simp [seq_assoc, ← comp_map, (· ∘ ·)]
  simp [pure_seq]
#align seq_map_assoc seq_map_assoc

@[functor_norm]
theorem map_seq (f : β → γ) (x : F (α → β)) (y : F α) :
    f <$> (x <*> y) = (f ∘ ·) <$> x <*> y := by
  simp only [← pure_seq]; simp [seq_assoc]
#align map_seq map_seq

end Applicative

-- TODO: setup `functor_norm` for `monad` laws
attribute [functor_norm] pure_bind bind_assoc bind_pure

section Monad

variable {m : Type u → Type v} [Monad m] [LawfulMonad m]

open List

/-- A generalization of `List.partition` which partitions the list according to a monadic
predicate. `List.partition` corresponds to the case where `f = Id`. -/
def List.partitionM {f : Type → Type} [Monad f] {α : Type} (p : α → f Bool) :
    List α → f (List α × List α)
  | [] => pure ([], [])
  | x :: xs => condM (p x)
    (Prod.map (cons x) id <$> List.partitionM p xs)
    (Prod.map id (cons x) <$> List.partitionM p xs)
#align list.mpartition List.partitionM

theorem map_bind (x : m α) {g : α → m β} {f : β → γ} :
    f <$> (x >>= g) = x >>= fun a => f <$> g a := by
  rw [← bind_pure_comp, bind_assoc] <;> simp [bind_pure_comp]
#align map_bind map_bind

theorem seq_bind_eq (x : m α) {g : β → m γ} {f : α → β} :
    f <$> x >>= g = x >>= g ∘ f :=
  show bind (f <$> x) g = bind x (g ∘ f)
  by rw [← bind_pure_comp, bind_assoc] <;> simp [pure_bind, (· ∘ ·)]
#align seq_bind_eq seq_bind_eq

#align seq_eq_bind_map seq_eq_bind_mapₓ
-- order of implicits and `Seq.seq` has a lazily evaluated second argument using `Unit`

@[functor_norm]
theorem fish_pure {α β} (f : α → m β) : f >=> pure = f := by simp only [(· >=> ·), functor_norm]
#align fish_pure fish_pure

@[functor_norm]
theorem fish_pipe {α β} (f : α → m β) : pure >=> f = f := by simp only [(· >=> ·), functor_norm]
#align fish_pipe fish_pipe

-- note: in Lean 3 `>=>` is left-associative, but in Lean 4 it is right-associative.
@[functor_norm]
theorem fish_assoc {α β γ φ} (f : α → m β) (g : β → m γ) (h : γ → m φ) :
    (f >=> g) >=> h = f >=> g >=> h := by
  simp only [(· >=> ·), functor_norm]
#align fish_assoc fish_assoc

variable {β' γ' : Type v}

variable {m' : Type v → Type w} [Monad m']

/-- Takes a value `β` and `List α` and accumulates pairs according to a monadic function `f`.
Accumulation occurs from the right (i.e., starting from the tail of the list). -/
def List.mapAccumRM (f : α → β' → m' (β' × γ')) : β' → List α → m' (β' × List γ')
  | a, [] => pure (a, [])
  | a, x :: xs => do
    let (a', ys) ← List.mapAccumRM f a xs
    let (a'', y) ← f x a'
    pure (a'', y :: ys)
#align list.mmap_accumr List.mapAccumRM

/-- Takes a value `β` and `List α` and accumulates pairs according to a monadic function `f`.
Accumulation occurs from the left (i.e., starting from the head of the list). -/
def List.mapAccumLM (f : β' → α → m' (β' × γ')) : β' → List α → m' (β' × List γ')
  | a, [] => pure (a, [])
  | a, x :: xs => do
    let (a', y) ← f a x
    let (a'', ys) ← List.mapAccumLM f a' xs
    pure (a'', y :: ys)
#align list.mmap_accuml List.mapAccumLM

end Monad

section

variable {m : Type u → Type u} [Monad m] [LawfulMonad m]

theorem joinM_map_map {α β : Type u} (f : α → β) (a : m (m α)) :
  joinM (Functor.map f <$> a) = f <$> joinM a := by
  simp only [joinM, (· ∘ ·), id.def, ← bind_pure_comp, bind_assoc, map_bind, pure_bind]
#align mjoin_map_map joinM_map_map

theorem joinM_map_joinM {α : Type u} (a : m (m (m α))) : joinM (joinM <$> a) = joinM (joinM a) := by
  simp only [joinM, (· ∘ ·), id.def, map_bind, ← bind_pure_comp, bind_assoc, pure_bind]
#align mjoin_map_mjoin joinM_map_joinM

@[simp]
theorem joinM_map_pure {α : Type u} (a : m α) : joinM (pure <$> a) = a := by
  simp only [joinM, (· ∘ ·), id.def, map_bind, ← bind_pure_comp, bind_assoc, pure_bind, bind_pure]
#align mjoin_map_pure joinM_map_pure

@[simp]
theorem joinM_pure {α : Type u} (a : m α) : joinM (pure a) = a :=
  LawfulMonad.pure_bind a id
#align mjoin_pure joinM_pure

end

section Alternative

variable {F : Type → Type v} [Alternative F]

-- [todo] add notation for `Functor.mapConst` and port `functor.map_const_rev`
/-- Returns `pure true` if the computation succeeds and `pure false` otherwise. -/
def succeeds {α} (x : F α) : F Bool :=
  Functor.mapConst true x <|> pure false
#align succeeds succeeds

/-- Attempts to perform the computation, but fails silently if it doesn't succeed. -/
def tryM {α} (x : F α) : F Unit :=
  Functor.mapConst () x <|> pure ()
#align mtry tryM

/-- Attempts to perform the computation, and returns `none` if it doesn't succeed. -/
def try? {α} (x : F α) : F (Option α) :=
  some <$> x <|> pure none

@[simp]
theorem guard_true {h : Decidable True} : @guard F _ True h = pure () := by simp [guard, if_pos]
#align guard_true guard_true

@[simp]
theorem guard_false {h : Decidable False} : @guard F _ False h = failure :=
  by simp [guard, if_neg not_false]
#align guard_false guard_false

end Alternative

namespace Sum

variable {e : Type v}

/-- The monadic `bind` operation for `Sum`. -/
protected def bind {α β} : Sum e α → (α → Sum e β) → Sum e β
  | inl x, _ => inl x
  | inr x, f => f x
#align sum.bind Sum.bind
-- incorrectly marked as a bad translation by mathport, so we do not mark with `ₓ`.

instance : Monad (Sum.{v, u} e) where
  pure := @Sum.inr e
  bind := @Sum.bind e

instance : LawfulFunctor (Sum.{v, u} e) := by
  refine' { .. } <;> intros <;> (try casesm Sum _ _) <;> rfl

instance : LawfulMonad (Sum.{v, u} e) where
  seqRight_eq := by
    intros
    casesm Sum _ _ <;> casesm Sum _ _ <;> rfl
  seqLeft_eq := by
    intros
    casesm Sum _ _ <;> rfl
  pure_seq := by
    intros
    rfl
  bind_assoc := by
    intros
    casesm Sum _ _ <;> rfl
  pure_bind := by
    intros
    rfl
  bind_pure_comp := by
    intros
    casesm Sum _ _ <;> rfl
  bind_map := by
    intros
    casesm Sum _ _ <;> rfl

end Sum

/-- A `CommApplicative` functor `m` is a (lawful) applicative functor which behaves identically on
`α × β` and `β × α`, so computations can occur in either order. -/
class CommApplicative (m : Type u → Type v) [Applicative m] extends LawfulApplicative m : Prop where
  /-- Computations performed first on `a : α` and then on `b : β` are equal to those performed in
  the reverse order. -/
  commutative_prod : ∀ {α β} (a : m α) (b : m β),
    Prod.mk <$> a <*> b = (fun (b : β) a => (a, b)) <$> b <*> a
#align is_comm_applicative CommApplicative

open Functor

variable {m}

theorem CommApplicative.commutative_map {m : Type u → Type v} [h : Applicative m]
    [CommApplicative m] {α β γ} (a : m α) (b : m β) {f : α → β → γ} :
  f <$> a <*> b = flip f <$> b <*> a :=
  calc
    f <$> a <*> b = (fun p : α × β => f p.1 p.2) <$> (Prod.mk <$> a <*> b) :=
      by
        simp [seq_map_assoc, map_seq, seq_assoc, seq_pure, map_map] <;> rfl
    _ = (fun b a => f a b) <$> b <*> a :=
      by
        rw [@CommApplicative.commutative_prod m h] <;>
        simp [seq_map_assoc, map_seq, seq_assoc, seq_pure, map_map, (· ∘ ·)]

#align is_comm_applicative.commutative_map CommApplicative.commutative_map


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

import analysis.normed_space.basic
import topology.sequences
import topology.metric_space.isometry

/-!
# Normed groups homomorphisms

This file gathers definitions and elementary constructions about bounded group homomorphisms
between normed (abelian) groups (abbreviated to "normed group homs").

The main lemmas relate the boundedness condition to continuity and Lipschitzness.

The main construction is to endow the type of normed group homs between two given normed groups
with a group structure and a norm, giving rise to a normed group structure.

Some easy other constructions are related to subgroups of normed groups.

Since a lot of elementary properties don't require `∥x∥ = 0 → x = 0` we start setting up the
theory of `semi_normed_group_hom` and we specialize to `normed_group_hom` when needed.
-/

noncomputable theory
open_locale nnreal big_operators

/-- A morphism of seminormed abelian groups is a bounded group homomorphism. -/
structure normed_group_hom (V W : Type*) [semi_normed_group V] [semi_normed_group W] :=
(to_fun : V → W)
(map_add' : ∀ v₁ v₂, to_fun (v₁ + v₂) = to_fun v₁ + to_fun v₂)
(bound' : ∃ C, ∀ v, ∥to_fun v∥ ≤ C * ∥v∥)

namespace add_monoid_hom

variables {V W : Type*} [semi_normed_group V] [semi_normed_group W] {f g : normed_group_hom V W}

/-- Associate to a group homomorphism a bounded group homomorphism under a norm control condition.

See `add_monoid_hom.mk_normed_group_hom'` for a version that uses `ℝ≥0` for the bound. -/
def mk_normed_group_hom (f : V →+ W)
  (C : ℝ) (h : ∀ v, ∥f v∥ ≤ C * ∥v∥) : normed_group_hom V W :=
{ bound' := ⟨C, h⟩, ..f }

/-- Associate to a group homomorphism a bounded group homomorphism under a norm control condition.

See `add_monoid_hom.mk_normed_group_hom` for a version that uses `ℝ` for the bound. -/
def mk_normed_group_hom' (f : V →+ W) (C : ℝ≥0) (hC : ∀ x, nnnorm (f x) ≤ C * nnnorm x) :
  normed_group_hom V W :=
{ bound' := ⟨C, hC⟩ .. f}

end add_monoid_hom

lemma exists_pos_bound_of_bound {V W : Type*} [semi_normed_group V] [semi_normed_group W]
  {f : V → W} (M : ℝ) (h : ∀x, ∥f x∥ ≤ M * ∥x∥) :
  ∃ N, 0 < N ∧ ∀x, ∥f x∥ ≤ N * ∥x∥ :=
⟨max M 1, lt_of_lt_of_le zero_lt_one (le_max_right _ _), λx, calc
  ∥f x∥ ≤ M * ∥x∥ : h x
  ... ≤ max M 1 * ∥x∥ : mul_le_mul_of_nonneg_right (le_max_left _ _) (norm_nonneg _) ⟩

namespace normed_group_hom

variables {V V₁ V₂ V₃ : Type*}
variables [semi_normed_group V] [semi_normed_group V₁] [semi_normed_group V₂] [semi_normed_group V₃]
variables {f g : normed_group_hom V₁ V₂}

instance : has_coe_to_fun (normed_group_hom V₁ V₂) := ⟨_, normed_group_hom.to_fun⟩

initialize_simps_projections normed_group_hom (to_fun → apply)

lemma coe_inj (H : ⇑f = g) : f = g :=
by cases f; cases g; congr'; exact funext H

lemma coe_injective : @function.injective (normed_group_hom V₁ V₂) (V₁ → V₂) coe_fn :=
by apply coe_inj

lemma coe_inj_iff : f = g ↔ ⇑f = g := ⟨congr_arg _, coe_inj⟩

@[ext] lemma ext (H : ∀ x, f x = g x) : f = g := coe_inj $ funext H

lemma ext_iff : f = g ↔ ∀ x, f x = g x := ⟨by rintro rfl x; refl, ext⟩

variables (f g)

@[simp] lemma to_fun_eq_coe : f.to_fun = f := rfl

@[simp] lemma coe_mk (f) (h₁) (h₂) (h₃) : ⇑(⟨f, h₁, h₂, h₃⟩ : normed_group_hom V₁ V₂) = f := rfl

@[simp] lemma coe_mk_normed_group_hom (f : V₁ →+ V₂) (C) (hC) :
  ⇑(f.mk_normed_group_hom C hC) = f := rfl

@[simp] lemma coe_mk_normed_group_hom' (f : V₁ →+ V₂) (C) (hC) :
  ⇑(f.mk_normed_group_hom' C hC) = f := rfl

/-- The group homomorphism underlying a bounded group homomorphism. -/
def to_add_monoid_hom (f : normed_group_hom V₁ V₂) : V₁ →+ V₂ :=
add_monoid_hom.mk' f f.map_add'

@[simp] lemma coe_to_add_monoid_hom : ⇑f.to_add_monoid_hom = f := rfl

lemma to_add_monoid_hom_injective :
  function.injective (@normed_group_hom.to_add_monoid_hom V₁ V₂ _ _) :=
λ f g h, coe_inj $ show ⇑f.to_add_monoid_hom = g, by { rw h, refl }

@[simp] lemma mk_to_add_monoid_hom (f) (h₁) (h₂) :
  (⟨f, h₁, h₂⟩ : normed_group_hom V₁ V₂).to_add_monoid_hom = add_monoid_hom.mk' f h₁ := rfl

@[simp] lemma map_zero : f 0 = 0 := f.to_add_monoid_hom.map_zero

@[simp] lemma map_add (x y) : f (x + y) = f x + f y := f.to_add_monoid_hom.map_add _ _

@[simp] lemma map_sum {ι : Type*} (v : ι → V₁) (s : finset ι) :
  f (∑ i in s, v i) = ∑ i in s, f (v i) :=
f.to_add_monoid_hom.map_sum _ _

@[simp] lemma map_sub (x y) : f (x - y) = f x - f y := f.to_add_monoid_hom.map_sub _ _

@[simp] lemma map_neg (x) : f (-x) = -(f x) := f.to_add_monoid_hom.map_neg _

/-- Predicate asserting a norm bound on a normed group hom. -/
def bound_by (f : normed_group_hom V₁ V₂) (C : ℝ≥0) : Prop := ∀ x, ∥f x∥ ≤ C * ∥x∥

lemma mk_normed_group_hom'_bound_by (f : V₁ →+ V₂) (C) (hC) :
  (f.mk_normed_group_hom' C hC).bound_by C := hC

lemma bound : ∃ C, 0 < C ∧ f.bound_by C :=
begin
  obtain ⟨C, hC⟩ := f.bound',
  rcases exists_pos_bound_of_bound _ hC with ⟨C', C'pos, hC'⟩,
  exact ⟨⟨C', C'pos.le⟩, C'pos, hC'⟩,
end

lemma lipschitz_of_bound_by (C : ℝ≥0) (h : f.bound_by C) :
  lipschitz_with (nnreal.of_real C) f :=
lipschitz_with.of_dist_le' $ λ x y, by simpa only [dist_eq_norm, f.map_sub] using h (x - y)

theorem antilipschitz_of_bound_by {K : ℝ≥0} (h : ∀ x, ∥x∥ ≤ K * ∥f x∥) :
  antilipschitz_with K f :=
antilipschitz_with.of_le_mul_dist $
λ x y, by simpa only [dist_eq_norm, f.map_sub] using h (x - y)

protected lemma uniform_continuous (f : normed_group_hom V₁ V₂) :
  uniform_continuous f :=
let ⟨C, C_pos, hC⟩ := f.bound in (lipschitz_of_bound_by f C hC).uniform_continuous

@[continuity]
protected lemma continuous (f : normed_group_hom V₁ V₂) : continuous f :=
f.uniform_continuous.continuous

/-! ### The operator norm -/

/-- The operator norm of a seminormed group homomorphism is the inf of all its bounds. -/
def op_norm (f : normed_group_hom V₁ V₂) := Inf {c | 0 ≤ c ∧ ∀ x, ∥f x∥ ≤ c * ∥x∥}
instance has_op_norm : has_norm (normed_group_hom V₁ V₂) := ⟨op_norm⟩

lemma norm_def : ∥f∥ = Inf {c | 0 ≤ c ∧ ∀ x, ∥f x∥ ≤ c * ∥x∥} := rfl

-- So that invocations of `real.Inf_le` make sense: we show that the set of
-- bounds is nonempty and bounded below.
lemma bounds_nonempty {f : normed_group_hom V₁ V₂} :
  ∃ c, c ∈ { c | 0 ≤ c ∧ ∀ x, ∥f x∥ ≤ c * ∥x∥ } :=
let ⟨M, hMp, hMb⟩ := f.bound in ⟨M, le_of_lt hMp, hMb⟩

lemma bounds_bdd_below {f : normed_group_hom V₁ V₂} :
  bdd_below {c | 0 ≤ c ∧ ∀ x, ∥f x∥ ≤ c * ∥x∥} :=
⟨0, λ _ ⟨hn, _⟩, hn⟩

lemma op_norm_nonneg : 0 ≤ ∥f∥ :=
real.lb_le_Inf _ bounds_nonempty (λ _ ⟨hx, _⟩, hx)

/-- The fundamental property of the operator norm: `∥f x∥ ≤ ∥f∥ * ∥x∥`. -/
theorem le_op_norm (x : V₁) : ∥f x∥ ≤ ∥f∥ * ∥x∥ :=
begin
  obtain ⟨C, Cpos, hC⟩ := f.bound,
  replace hC := hC x,
  by_cases h : ∥x∥ = 0,
  { rwa [h, mul_zero] at ⊢ hC },
  have hlt : 0 < ∥x∥ := lt_of_le_of_ne (norm_nonneg x) (ne.symm h),
  exact  (div_le_iff hlt).mp ((real.le_Inf _ bounds_nonempty bounds_bdd_below).2 (λ c ⟨_, hc⟩,
    (div_le_iff hlt).mpr $ by { apply hc })),
end

theorem le_op_norm_of_le {c : ℝ} {x} (h : ∥x∥ ≤ c) : ∥f x∥ ≤ ∥f∥ * c :=
le_trans (f.le_op_norm x) (mul_le_mul_of_nonneg_left h f.op_norm_nonneg)

theorem le_of_op_norm_le {c : ℝ} (h : ∥f∥ ≤ c) (x : V₁) : ∥f x∥ ≤ c * ∥x∥ :=
(f.le_op_norm x).trans (mul_le_mul_of_nonneg_right h (norm_nonneg x))

/-- continuous linear maps are Lipschitz continuous. -/
theorem lipschitz : lipschitz_with ⟨∥f∥, op_norm_nonneg f⟩ f :=
lipschitz_with.of_dist_le_mul $ λ x y,
  by { rw [dist_eq_norm, dist_eq_norm, ←map_sub], apply le_op_norm }

lemma ratio_le_op_norm (x : V₁) : ∥f x∥ / ∥x∥ ≤ ∥f∥ :=
div_le_of_nonneg_of_le_mul (norm_nonneg _) f.op_norm_nonneg (le_op_norm _ _)

/-- If one controls the norm of every `f x`, then one controls the norm of `f`. -/
lemma op_norm_le_bound {M : ℝ} (hMp: 0 ≤ M) (hM : ∀ x, ∥f x∥ ≤ M * ∥x∥) :
  ∥f∥ ≤ M :=
real.Inf_le _ bounds_bdd_below ⟨hMp, hM⟩

theorem op_norm_le_of_lipschitz {f : normed_group_hom V₁ V₂} {K : ℝ≥0} (hf : lipschitz_with K f) :
  ∥f∥ ≤ K :=
f.op_norm_le_bound K.2 $ λ x, by simpa only [dist_zero_right, f.map_zero] using hf.dist_le_mul x 0

/-- If a bounded group homomorphism map is constructed from a group homomorphism via the constructor
`mk_normed_group_hom`, then its norm is bounded by the bound given to the constructor if it is
nonnegative. -/
lemma mk_normed_group_hom_norm_le (f : V₁ →+ V₂) {C : ℝ} (hC : 0 ≤ C) (h : ∀x, ∥f x∥ ≤ C * ∥x∥) :
  ∥f.mk_normed_group_hom C h∥ ≤ C :=
op_norm_le_bound _ hC h

/-- If a bounded group homomorphism map is constructed from a group homomorphism
via the constructor `mk_normed_group_hom`, then its norm is bounded by the bound
given to the constructor or zero if this bound is negative. -/
lemma mk_normed_group_hom_norm_le' (f : V₁ →+ V₂) {C : ℝ} (h : ∀x, ∥f x∥ ≤ C * ∥x∥) :
  ∥f.mk_normed_group_hom C h∥ ≤ max C 0 :=
op_norm_le_bound _ (le_max_right _ _) $ λ x, (h x).trans $
  mul_le_mul_of_nonneg_right (le_max_left _ _) (norm_nonneg x)

alias mk_normed_group_hom_norm_le ← add_monoid_hom.mk_normed_group_hom_norm_le
alias mk_normed_group_hom_norm_le' ← add_monoid_hom.mk_normed_group_hom_norm_le'

/-! ### Addition of normed group homs -/

/-- Addition of normed group homs. -/
instance : has_add (normed_group_hom V₁ V₂) :=
⟨λ f g, (f.to_add_monoid_hom + g.to_add_monoid_hom).mk_normed_group_hom (∥f∥ + ∥g∥) $ λ v, calc
  ∥f v + g v∥
      ≤ ∥f v∥ + ∥g v∥ : norm_add_le _ _
  ... ≤ ∥f∥ * ∥v∥ + ∥g∥ * ∥v∥ : add_le_add (le_op_norm f v) (le_op_norm g v)
  ... = (∥f∥ + ∥g∥) * ∥v∥ : by rw add_mul⟩

/-- The operator norm satisfies the triangle inequality. -/
theorem op_norm_add_le : ∥f + g∥ ≤ ∥f∥ + ∥g∥ :=
mk_normed_group_hom_norm_le _ (add_nonneg (op_norm_nonneg _) (op_norm_nonneg _)) _

/--
Terms containing `@has_add.add (has_coe_to_fun.F ...) pi.has_add`
seem to cause leanchecker to [crash due to an out-of-memory
condition](https://github.com/leanprover-community/lean/issues/543).
As a workaround, we add a type annotation: `(f + g : V₁ → V₂)`
-/
library_note "addition on function coercions"

-- see Note [addition on function coercions]
@[simp] lemma coe_add (f g : normed_group_hom V₁ V₂) : ⇑(f + g) = (f + g : V₁ → V₂) := rfl
@[simp] lemma add_apply (f g : normed_group_hom V₁ V₂) (v : V₁) :
  (f + g : normed_group_hom V₁ V₂) v = f v + g v := rfl

/-! ### The zero normed group hom -/

instance : has_zero (normed_group_hom V₁ V₂) :=
⟨(0 : V₁ →+ V₂).mk_normed_group_hom 0 (by simp)⟩

instance : inhabited (normed_group_hom V₁ V₂) := ⟨0⟩

/-- The norm of the `0` operator is `0`. -/
theorem op_norm_zero : ∥(0 : normed_group_hom V₁ V₂)∥ = 0 :=
le_antisymm (real.Inf_le _ bounds_bdd_below
    ⟨ge_of_eq rfl, λ _, le_of_eq (by { rw [zero_mul], exact norm_zero })⟩)
    (op_norm_nonneg _)

/-- For normed groups, an operator is zero iff its norm vanishes. -/
theorem op_norm_zero_iff {V₁ V₂ : Type*} [normed_group V₁] [normed_group V₂]
  {f : normed_group_hom V₁ V₂} : ∥f∥ = 0 ↔ f = 0 :=
iff.intro
  (λ hn, ext (λ x, norm_le_zero_iff.1
    (calc _ ≤ ∥f∥ * ∥x∥ : le_op_norm _ _
     ...     = _ : by rw [hn, zero_mul])))
  (λ hf, by rw [hf, op_norm_zero] )

-- see Note [addition on function coercions]
@[simp] lemma coe_zero : ⇑(0 : normed_group_hom V₁ V₂) = (0 : V₁ → V₂) := rfl
@[simp] lemma zero_apply (v : V₁) : (0 : normed_group_hom V₁ V₂) v = 0 := rfl

variables {f g}

/-! ### The identity normed group hom -/

/-- The identity as a continuous normed group hom. -/
@[simps]
def id : normed_group_hom V V :=
(add_monoid_hom.id V).mk_normed_group_hom 1 (by simp [le_refl])

/-- The norm of the identity is at most `1`. It is in fact `1`, except when the norm of every
element vanishes, where it is `0`. (Since we are working with seminorms this can happen even if the
space is non-trivial.) It means that one can not do better than an inequality in general. -/
lemma norm_id_le : ∥(id : normed_group_hom V V)∥ ≤ 1 :=
op_norm_le_bound _ zero_le_one (λx, by simp)

/-- If there is an element with norm different from `0`, then the norm of the identity equals `1`.
(Since we are working with seminorms supposing that the space is non-trivial is not enough.) -/
lemma norm_id_of_nontrivial_seminorm (h : ∃ (x : V), ∥x∥ ≠ 0 ) :
  ∥(id : normed_group_hom V V)∥ = 1 :=
le_antisymm norm_id_le $ let ⟨x, hx⟩ := h in
have _ := (id : normed_group_hom V V).ratio_le_op_norm x,
by rwa [id_apply, div_self hx] at this

/-- If a normed space is non-trivial, then the norm of the identity equals `1`. -/
lemma norm_id {V : Type*} [normed_group V] [nontrivial V] : ∥(id : normed_group_hom V V)∥ = 1 :=
begin
  refine norm_id_of_nontrivial_seminorm _,
  obtain ⟨x, hx⟩ := exists_ne (0 : V),
  exact ⟨x, ne_of_gt (norm_pos_iff.2 hx)⟩,
end

/-! ### The negation of a normed group hom -/

/-- Opposite of a normed group hom. -/
instance : has_neg (normed_group_hom V₁ V₂) :=
⟨λ f, (-f.to_add_monoid_hom).mk_normed_group_hom (∥f∥) (λ v, by simp [le_op_norm f v])⟩

-- see Note [addition on function coercions]
@[simp] lemma coe_neg (f : normed_group_hom V₁ V₂) : ⇑(-f) = (-f : V₁ → V₂) := rfl
@[simp] lemma neg_apply (f : normed_group_hom V₁ V₂) (v : V₁) :
  (-f : normed_group_hom V₁ V₂) v = - (f v) := rfl

lemma op_norm_neg (f : normed_group_hom V₁ V₂) : ∥-f∥ = ∥f∥ :=
by simp only [norm_def, coe_neg, norm_neg, pi.neg_apply]

/-! ### Subtraction of normed group homs -/

/-- Subtraction of normed group homs. -/
instance : has_sub (normed_group_hom V₁ V₂) :=
⟨λ f g,
{ bound' :=
  begin
    simp only [add_monoid_hom.sub_apply, add_monoid_hom.to_fun_eq_coe, sub_eq_add_neg],
    exact (f + -g).bound'
  end,
  .. (f.to_add_monoid_hom - g.to_add_monoid_hom) }⟩

-- see Note [addition on function coercions]
@[simp] lemma coe_sub (f g : normed_group_hom V₁ V₂) : ⇑(f - g) = (f - g : V₁ → V₂) := rfl
@[simp] lemma sub_apply (f g : normed_group_hom V₁ V₂) (v : V₁) :
  (f - g : normed_group_hom V₁ V₂) v = f v - g v := rfl

/-! ### Normed group structure on normed group homs -/

/-- Homs between two given normed groups form a commutative additive group. -/
instance : add_comm_group (normed_group_hom V₁ V₂) :=
coe_injective.add_comm_group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl)

/-- Normed group homomorphisms themselves form a seminormed group with respect to
    the operator norm. -/
instance to_semi_normed_group : semi_normed_group (normed_group_hom V₁ V₂) :=
semi_normed_group.of_core _ ⟨op_norm_zero, op_norm_add_le, op_norm_neg⟩

/-- Normed group homomorphisms themselves form a normed group with respect to
    the operator norm. -/
instance to_normed_group {V₁ V₂ : Type*} [normed_group V₁] [normed_group V₂] :
  normed_group (normed_group_hom V₁ V₂) :=
normed_group.of_core _ ⟨λ f, op_norm_zero_iff, op_norm_add_le, op_norm_neg⟩

/-- Coercion of a `normed_group_hom` is an `add_monoid_hom`. Similar to `add_monoid_hom.coe_fn` -/
@[simps]
def coe_fn_add_hom : normed_group_hom V₁ V₂ →+ (V₁ → V₂) :=
{ to_fun := coe_fn, map_zero' := coe_zero, map_add' := coe_add}

@[simp] lemma coe_sum {ι : Type*} (s : finset ι) (f : ι → normed_group_hom V₁ V₂) :
  ⇑(∑ i in s, f i) = ∑ i in s, (f i) :=
(coe_fn_add_hom : _ →+ (V₁ → V₂)).map_sum f s

lemma sum_apply {ι : Type*} (s : finset ι) (f : ι → normed_group_hom V₁ V₂) (v : V₁) :
  (∑ i in s, f i) v = ∑ i in s, (f i v) :=
by simp only [coe_sum, finset.sum_apply]

/-! ### Composition of normed group homs -/

/-- The composition of continuous normed group homs. -/
@[simps]
protected def comp (g : normed_group_hom V₂ V₃) (f : normed_group_hom V₁ V₂) :
  normed_group_hom V₁ V₃ :=
(g.to_add_monoid_hom.comp f.to_add_monoid_hom).mk_normed_group_hom (∥g∥ * ∥f∥) $ λ v, calc
∥g (f v)∥ ≤ ∥g∥ * ∥f v∥ : le_op_norm _ _
... ≤ ∥g∥ * (∥f∥ * ∥v∥) : mul_le_mul_of_nonneg_left (le_op_norm _ _) (op_norm_nonneg _)
... = ∥g∥ * ∥f∥ * ∥v∥   : by rw mul_assoc

lemma norm_comp_le (g : normed_group_hom V₂ V₃) (f : normed_group_hom V₁ V₂) :
  ∥g.comp f∥ ≤ ∥g∥ * ∥f∥ :=
mk_normed_group_hom_norm_le _ (mul_nonneg (op_norm_nonneg _) (op_norm_nonneg _)) _

/-- Composition of normed groups hom as an additive group morphism. -/
def comp_hom : (normed_group_hom V₂ V₃) →+ (normed_group_hom V₁ V₂) →+ (normed_group_hom V₁ V₃) :=
add_monoid_hom.mk' (λ g, add_monoid_hom.mk' (λ f, g.comp f)
  (by { intros, ext, exact g.map_add _ _ }))
  (by { intros, ext, simp only [comp_apply, pi.add_apply, function.comp_app,
                                add_monoid_hom.add_apply, add_monoid_hom.mk'_apply, coe_add] })

@[simp] lemma comp_zero (f : normed_group_hom V₂ V₃) : f.comp (0 : normed_group_hom V₁ V₂) = 0 :=
by { ext, exact f.map_zero }

@[simp] lemma zero_comp (f : normed_group_hom V₁ V₂) : (0 : normed_group_hom V₂ V₃).comp f = 0 :=
by { ext, refl }

end normed_group_hom

namespace normed_group_hom

variables {V W V₁ V₂ V₃ : Type*}
variables [semi_normed_group V] [semi_normed_group W] [semi_normed_group V₁] [semi_normed_group V₂]
[semi_normed_group V₃]

/-- The inclusion of an `add_subgroup`, as bounded group homomorphism. -/
@[simps] def incl (s : add_subgroup V) : normed_group_hom s V :=
{ to_fun := (coe : s → V),
  map_add' := λ v w, add_subgroup.coe_add _ _ _,
  bound' := ⟨1, λ v, by { rw [one_mul], refl }⟩ }

/-!### Kernel -/
section kernels
variables (f : normed_group_hom V₁ V₂) (g : normed_group_hom V₂ V₃)

/-- The kernel of a bounded group homomorphism. Naturally endowed with a
`semi_normed_group` instance. -/
def ker : add_subgroup V₁ := f.to_add_monoid_hom.ker

lemma mem_ker (v : V₁) : v ∈ f.ker ↔ f v = 0 :=
by { erw f.to_add_monoid_hom.mem_ker, refl }

/-- Given a normed group hom `f : V₁ → V₂` satisfying `g.comp f = 0` for some `g : V₂ → V₃`,
    the corestriction of `f` to the kernel of `g`. -/
@[simps] def ker.lift (h : g.comp f = 0) :
  normed_group_hom V₁ g.ker :=
{ to_fun := λ v, ⟨f v, by { erw g.mem_ker, show (g.comp f) v = 0, rw h, refl }⟩,
  map_add' := λ v w, by { simp only [map_add], refl },
  bound' := f.bound' }

@[simp] lemma ker.incl_comp_lift (h : g.comp f = 0) :
  (incl g.ker).comp (ker.lift f g h) = f :=
by { ext, refl }

end kernels

/-! ### Range -/
section range

variables (f : normed_group_hom V₁ V₂) (g : normed_group_hom V₂ V₃)

/-- The image of a bounded group homomorphism. Naturally endowed with a
`semi_normed_group` instance. -/
def range : add_subgroup V₂ := f.to_add_monoid_hom.range

lemma mem_range (v : V₂) : v ∈ f.range ↔ ∃ w, f w = v :=
by { rw [range, add_monoid_hom.mem_range], refl }

end range

variables {f : normed_group_hom V W}

/-- A `normed_group_hom` is *norm-nonincreasing* if `∥f v∥ ≤ ∥v∥` for all `v`. -/
def norm_noninc (f : normed_group_hom V W) : Prop :=
∀ v, ∥f v∥ ≤ ∥v∥

namespace norm_noninc

lemma bound_by_one (hf : f.norm_noninc) : f.bound_by 1 :=
λ v, by simpa only [one_mul, nnreal.coe_one] using hf v

lemma id : (id : normed_group_hom V V).norm_noninc :=
λ v, le_rfl

lemma comp {g : normed_group_hom V₂ V₃} {f : normed_group_hom V₁ V₂}
  (hg : g.norm_noninc) (hf : f.norm_noninc) :
  (g.comp f).norm_noninc :=
λ v, (hg (f v)).trans (hf v)

end norm_noninc

section isometry

lemma isometry_iff_norm (f : normed_group_hom V W) :
  isometry f ↔ ∀ v, ∥f v∥ = ∥v∥ :=
add_monoid_hom.isometry_iff_norm f.to_add_monoid_hom

lemma isometry_of_norm (f : normed_group_hom V W) (hf : ∀ v, ∥f v∥ = ∥v∥) :
  isometry f :=
f.isometry_iff_norm.mpr hf

lemma norm_eq_of_isometry {f : normed_group_hom V W} (hf : isometry f) (v : V) :
  ∥f v∥ = ∥v∥ :=
f.isometry_iff_norm.mp hf v

lemma isometry_id : @isometry V V _ _ (id : normed_group_hom V V) :=
isometry_id

lemma isometry_comp {g : normed_group_hom V₂ V₃} {f : normed_group_hom V₁ V₂}
  (hg : isometry g) (hf : isometry f) :
  isometry (g.comp f) :=
hg.comp hf

lemma norm_noninc_of_isometry (hf : isometry f) : f.norm_noninc :=
λ v, le_of_eq $ norm_eq_of_isometry hf v

lemma bound_by_one_of_isometry (hf : isometry f) : f.bound_by 1 :=
(norm_noninc_of_isometry hf).bound_by_one

end isometry

end normed_group_hom


/-
Copyright (c) 2014 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Yury Kudryashov, Neil Strickland
-/
import algebra.group.semiconj
import algebra.ring.defs

/-!
# Semirings and rings

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

This file gives lemmas about semirings, rings and domains.
This is analogous to `algebra.group.basic`,
the difference being that the former is about `+` and `*` separately, while
the present file is about their interaction.

For the definitions of semirings and rings see `algebra.ring.defs`.

-/
universes u v w x
variables {α : Type u} {β : Type v} {γ : Type w} {R : Type x}

open function

namespace semiconj_by

@[simp] lemma add_right [distrib R] {a x y x' y' : R}
  (h : semiconj_by a x y) (h' : semiconj_by a x' y') :
  semiconj_by a (x + x') (y + y') :=
by simp only [semiconj_by, left_distrib, right_distrib, h.eq, h'.eq]

@[simp] lemma add_left [distrib R] {a b x y : R}
  (ha : semiconj_by a x y) (hb : semiconj_by b x y) :
  semiconj_by (a + b) x y :=
by simp only [semiconj_by, left_distrib, right_distrib, ha.eq, hb.eq]

section
variables [has_mul R] [has_distrib_neg R] {a x y : R}

lemma neg_right (h : semiconj_by a x y) : semiconj_by a (-x) (-y) :=
by simp only [semiconj_by, h.eq, neg_mul, mul_neg]

@[simp] lemma neg_right_iff : semiconj_by a (-x) (-y) ↔ semiconj_by a x y :=
⟨λ h, neg_neg x ▸ neg_neg y ▸ h.neg_right, semiconj_by.neg_right⟩

lemma neg_left (h : semiconj_by a x y) : semiconj_by (-a) x y :=
by simp only [semiconj_by, h.eq, neg_mul, mul_neg]

@[simp] lemma neg_left_iff : semiconj_by (-a) x y ↔ semiconj_by a x y :=
⟨λ h, neg_neg a ▸ h.neg_left, semiconj_by.neg_left⟩

end

section
variables [mul_one_class R] [has_distrib_neg R] {a x y : R}

@[simp] lemma neg_one_right (a : R) : semiconj_by a (-1) (-1) :=
(one_right a).neg_right

@[simp] lemma neg_one_left (x : R) : semiconj_by (-1) x x :=
(semiconj_by.one_left x).neg_left

end

section
variables [non_unital_non_assoc_ring R] {a b x y x' y' : R}

@[simp] lemma sub_right (h : semiconj_by a x y) (h' : semiconj_by a x' y') :
  semiconj_by a (x - x') (y - y') :=
by simpa only [sub_eq_add_neg] using h.add_right h'.neg_right

@[simp] lemma sub_left (ha : semiconj_by a x y) (hb : semiconj_by b x y) :
  semiconj_by (a - b) x y :=
by simpa only [sub_eq_add_neg] using ha.add_left hb.neg_left

end

end semiconj_by


/-
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura, Mario Carneiro

Type class for encodable Types.
Note that every encodable Type is countable.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.equiv.nat
import Mathlib.order.rel_iso
import Mathlib.order.directed
import Mathlib.PostPort

universes u_1 l u_2 u_3

namespace Mathlib

/-- An encodable type is a "constructively countable" type. This is where
  we have an explicit injection `encode : α → nat` and a partial inverse
  `decode : nat → option α`. This makes the range of `encode` decidable,
  although it is not decidable if `α` is finite or not. -/
class encodable (α : Type u_1)
where
  encode : α → ℕ
  decode : ℕ → Option α
  encodek : ∀ (a : α), decode (encode a) = some a

namespace encodable

theorem encode_injective {α : Type u_1} [encodable α] : function.injective encode := sorry

/- This is not set as an instance because this is usually not the best way
  to infer decidability. -/

def decidable_eq_of_encodable (α : Type u_1) [encodable α] : DecidableEq α :=
  sorry

def of_left_injection {α : Type u_1} {β : Type u_2} [encodable α] (f : β → α) (finv : α → Option β) (linv : ∀ (b : β), finv (f b) = some b) : encodable β :=
  mk (fun (b : β) => encode (f b)) (fun (n : ℕ) => option.bind (decode α n) finv) sorry

def of_left_inverse {α : Type u_1} {β : Type u_2} [encodable α] (f : β → α) (finv : α → β) (linv : ∀ (b : β), finv (f b) = b) : encodable β :=
  of_left_injection f (some ∘ finv) sorry

/-- If `α` is encodable and `β ≃ α`, then so is `β` -/
def of_equiv {β : Type u_2} (α : Type u_1) [encodable α] (e : β ≃ α) : encodable β :=
  of_left_inverse (⇑e) (⇑(equiv.symm e)) (equiv.left_inv e)

@[simp] theorem encode_of_equiv {α : Type u_1} {β : Type u_2} [encodable α] (e : β ≃ α) (b : β) : encode b = encode (coe_fn e b) :=
  rfl

@[simp] theorem decode_of_equiv {α : Type u_1} {β : Type u_2} [encodable α] (e : β ≃ α) (n : ℕ) : decode β n = option.map (⇑(equiv.symm e)) (decode α n) :=
  rfl

protected instance nat : encodable ℕ :=
  mk id some sorry

@[simp] theorem encode_nat (n : ℕ) : encode n = n :=
  rfl

@[simp] theorem decode_nat (n : ℕ) : decode ℕ n = some n :=
  rfl

protected instance empty : encodable empty :=
  mk (fun (a : empty) => empty.rec (fun (a : empty) => ℕ) a) (fun (n : ℕ) => none) sorry

protected instance unit : encodable PUnit :=
  mk (fun (_x : PUnit) => 0) (fun (n : ℕ) => nat.cases_on n (some PUnit.unit) fun (_x : ℕ) => none) sorry

@[simp] theorem encode_star : encode PUnit.unit = 0 :=
  rfl

@[simp] theorem decode_unit_zero : decode PUnit 0 = some PUnit.unit :=
  rfl

@[simp] theorem decode_unit_succ (n : ℕ) : decode PUnit (Nat.succ n) = none :=
  rfl

protected instance option {α : Type u_1} [h : encodable α] : encodable (Option α) :=
  mk (fun (o : Option α) => option.cases_on o 0 fun (a : α) => Nat.succ (encode a))
    (fun (n : ℕ) => nat.cases_on n (some none) fun (m : ℕ) => option.map some (decode α m)) sorry

@[simp] theorem encode_none {α : Type u_1} [encodable α] : encode none = 0 :=
  rfl

@[simp] theorem encode_some {α : Type u_1} [encodable α] (a : α) : encode (some a) = Nat.succ (encode a) :=
  rfl

@[simp] theorem decode_option_zero {α : Type u_1} [encodable α] : decode (Option α) 0 = some none :=
  rfl

@[simp] theorem decode_option_succ {α : Type u_1} [encodable α] (n : ℕ) : decode (Option α) (Nat.succ n) = option.map some (decode α n) :=
  rfl

def decode2 (α : Type u_1) [encodable α] (n : ℕ) : Option α :=
  option.bind (decode α n) (option.guard fun (a : α) => encode a = n)

theorem mem_decode2' {α : Type u_1} [encodable α] {n : ℕ} {a : α} : a ∈ decode2 α n ↔ a ∈ decode α n ∧ encode a = n := sorry

theorem mem_decode2 {α : Type u_1} [encodable α] {n : ℕ} {a : α} : a ∈ decode2 α n ↔ encode a = n :=
  iff.trans mem_decode2' (and_iff_right_of_imp fun (e : encode a = n) => e ▸ encodek a)

theorem decode2_is_partial_inv {α : Type u_1} [encodable α] : function.is_partial_inv encode (decode2 α) :=
  fun (a : α) (n : ℕ) => mem_decode2

theorem decode2_inj {α : Type u_1} [encodable α] {n : ℕ} {a₁ : α} {a₂ : α} (h₁ : a₁ ∈ decode2 α n) (h₂ : a₂ ∈ decode2 α n) : a₁ = a₂ :=
  encode_injective (Eq.trans (iff.mp mem_decode2 h₁) (Eq.symm (iff.mp mem_decode2 h₂)))

theorem encodek2 {α : Type u_1} [encodable α] (a : α) : decode2 α (encode a) = some a :=
  iff.mpr mem_decode2 rfl

def decidable_range_encode (α : Type u_1) [encodable α] : decidable_pred (set.range encode) :=
  fun (x : ℕ) => decidable_of_iff ↥(option.is_some (decode2 α x)) sorry

def equiv_range_encode (α : Type u_1) [encodable α] : α ≃ ↥(set.range encode) :=
  equiv.mk (fun (a : α) => { val := encode a, property := sorry }) (fun (n : ↥(set.range encode)) => option.get sorry)
    sorry sorry

def encode_sum {α : Type u_1} {β : Type u_2} [encodable α] [encodable β] : α ⊕ β → ℕ :=
  sorry

def decode_sum {α : Type u_1} {β : Type u_2} [encodable α] [encodable β] (n : ℕ) : Option (α ⊕ β) :=
  sorry

protected instance sum {α : Type u_1} {β : Type u_2} [encodable α] [encodable β] : encodable (α ⊕ β) :=
  mk encode_sum decode_sum sorry

@[simp] theorem encode_inl {α : Type u_1} {β : Type u_2} [encodable α] [encodable β] (a : α) : encode (sum.inl a) = bit0 (encode a) :=
  rfl

@[simp] theorem encode_inr {α : Type u_1} {β : Type u_2} [encodable α] [encodable β] (b : β) : encode (sum.inr b) = bit1 (encode b) :=
  rfl

@[simp] theorem decode_sum_val {α : Type u_1} {β : Type u_2} [encodable α] [encodable β] (n : ℕ) : decode (α ⊕ β) n = decode_sum n :=
  rfl

protected instance bool : encodable Bool :=
  of_equiv (Unit ⊕ Unit) equiv.bool_equiv_punit_sum_punit

@[simp] theorem encode_tt : encode tt = 1 :=
  rfl

@[simp] theorem encode_ff : encode false = 0 :=
  rfl

@[simp] theorem decode_zero : decode Bool 0 = some false :=
  rfl

@[simp] theorem decode_one : decode Bool 1 = some tt :=
  rfl

theorem decode_ge_two (n : ℕ) (h : bit0 1 ≤ n) : decode Bool n = none := sorry

def encode_sigma {α : Type u_1} {γ : α → Type u_3} [encodable α] [(a : α) → encodable (γ a)] : sigma γ → ℕ :=
  sorry

def decode_sigma {α : Type u_1} {γ : α → Type u_3} [encodable α] [(a : α) → encodable (γ a)] (n : ℕ) : Option (sigma γ) :=
  sorry

protected instance sigma {α : Type u_1} {γ : α → Type u_3} [encodable α] [(a : α) → encodable (γ a)] : encodable (sigma γ) :=
  mk encode_sigma decode_sigma sorry

@[simp] theorem decode_sigma_val {α : Type u_1} {γ : α → Type u_3} [encodable α] [(a : α) → encodable (γ a)] (n : ℕ) : decode (sigma γ) n =
  option.bind (decode α (prod.fst (nat.unpair n)))
    fun (a : α) => option.map (sigma.mk a) (decode (γ a) (prod.snd (nat.unpair n))) := sorry

@[simp] theorem encode_sigma_val {α : Type u_1} {γ : α → Type u_3} [encodable α] [(a : α) → encodable (γ a)] (a : α) (b : γ a) : encode (sigma.mk a b) = nat.mkpair (encode a) (encode b) :=
  rfl

protected instance prod {α : Type u_1} {β : Type u_2} [encodable α] [encodable β] : encodable (α × β) :=
  of_equiv (sigma fun (_x : α) => β) (equiv.symm (equiv.sigma_equiv_prod α β))

@[simp] theorem decode_prod_val {α : Type u_1} {β : Type u_2} [encodable α] [encodable β] (n : ℕ) : decode (α × β) n =
  option.bind (decode α (prod.fst (nat.unpair n)))
    fun (a : α) => option.map (Prod.mk a) (decode β (prod.snd (nat.unpair n))) := sorry

@[simp] theorem encode_prod_val {α : Type u_1} {β : Type u_2} [encodable α] [encodable β] (a : α) (b : β) : encode (a, b) = nat.mkpair (encode a) (encode b) :=
  rfl

def encode_subtype {α : Type u_1} {P : α → Prop} [encA : encodable α] : (Subtype fun (a : α) => P a) → ℕ :=
  sorry

def decode_subtype {α : Type u_1} {P : α → Prop} [encA : encodable α] [decP : decidable_pred P] (v : ℕ) : Option (Subtype fun (a : α) => P a) :=
  option.bind (decode α v)
    fun (a : α) => dite (P a) (fun (h : P a) => some { val := a, property := h }) fun (h : ¬P a) => none

protected instance subtype {α : Type u_1} {P : α → Prop} [encA : encodable α] [decP : decidable_pred P] : encodable (Subtype fun (a : α) => P a) :=
  mk encode_subtype decode_subtype sorry

theorem subtype.encode_eq {α : Type u_1} {P : α → Prop} [encA : encodable α] [decP : decidable_pred P] (a : Subtype P) : encode a = encode (subtype.val a) :=
  subtype.cases_on a fun (a_val : α) (a_property : P a_val) => Eq.refl (encode { val := a_val, property := a_property })

protected instance fin (n : ℕ) : encodable (fin n) :=
  of_equiv (Subtype fun (m : ℕ) => m < n) (equiv.fin_equiv_subtype n)

protected instance int : encodable ℤ :=
  of_equiv ℕ equiv.int_equiv_nat

protected instance ulift {α : Type u_1} [encodable α] : encodable (ulift α) :=
  of_equiv α equiv.ulift

protected instance plift {α : Type u_1} [encodable α] : encodable (plift α) :=
  of_equiv α equiv.plift

def of_inj {α : Type u_1} {β : Type u_2} [encodable β] (f : α → β) (hf : function.injective f) : encodable α :=
  of_left_injection f (function.partial_inv f) sorry

end encodable

/--
`ulower α : Type 0` is an equivalent type in the lowest universe, given `encodable α`.
-/
def ulower (α : Type u_1) [encodable α] :=
  ↥(set.range encodable.encode)

namespace ulower

/--
The equivalence between the encodable type `α` and `ulower α : Type 0`.
-/
def equiv (α : Type u_1) [encodable α] : α ≃ ulower α :=
  encodable.equiv_range_encode α

/--
Lowers an `a : α` into `ulower α`.
-/
def down {α : Type u_1} [encodable α] (a : α) : ulower α :=
  coe_fn (equiv α) a

protected instance inhabited {α : Type u_1} [encodable α] [Inhabited α] : Inhabited (ulower α) :=
  { default := down Inhabited.default }

/--
Lifts an `a : ulower α` into `α`.
-/
def up {α : Type u_1} [encodable α] (a : ulower α) : α :=
  coe_fn (equiv.symm (equiv α)) a

@[simp] theorem down_up {α : Type u_1} [encodable α] {a : ulower α} : down (up a) = a :=
  equiv.right_inv (equiv α) a

@[simp] theorem up_down {α : Type u_1} [encodable α] {a : α} : up (down a) = a :=
  equiv.left_inv (equiv α) a

@[simp] theorem up_eq_up {α : Type u_1} [encodable α] {a : ulower α} {b : ulower α} : up a = up b ↔ a = b :=
  equiv.apply_eq_iff_eq (equiv.symm (equiv α))

@[simp] theorem down_eq_down {α : Type u_1} [encodable α] {a : α} {b : α} : down a = down b ↔ a = b :=
  equiv.apply_eq_iff_eq (equiv α)

protected theorem ext {α : Type u_1} [encodable α] {a : ulower α} {b : ulower α} : up a = up b → a = b :=
  iff.mp up_eq_up

end ulower

/-
Choice function for encodable types and decidable predicates.
We provide the following API

choose      {α : Type*} {p : α → Prop} [c : encodable α] [d : decidable_pred p] : (∃ x, p x) → α :=
choose_spec {α : Type*} {p : α → Prop} [c : encodable α] [d : decidable_pred p] (ex : ∃ x, p x) :
  p (choose ex) :=
-/

namespace encodable

def choose_x {α : Type u_1} {p : α → Prop} [encodable α] [decidable_pred p] (h : ∃ (x : α), p x) : Subtype fun (x : α) => p x :=
  (fun (this : ∃ (n : ℕ), good p (decode α n)) => sorry) sorry

def choose {α : Type u_1} {p : α → Prop} [encodable α] [decidable_pred p] (h : ∃ (x : α), p x) : α :=
  subtype.val (choose_x h)

theorem choose_spec {α : Type u_1} {p : α → Prop} [encodable α] [decidable_pred p] (h : ∃ (x : α), p x) : p (choose h) :=
  subtype.property (choose_x h)

theorem axiom_of_choice {α : Type u_1} {β : α → Type u_2} {R : (x : α) → β x → Prop} [(a : α) → encodable (β a)] [(x : α) → (y : β x) → Decidable (R x y)] (H : ∀ (x : α), ∃ (y : β x), R x y) : ∃ (f : (a : α) → β a), ∀ (x : α), R x (f x) :=
  Exists.intro (fun (x : α) => choose (H x)) fun (x : α) => choose_spec (H x)

theorem skolem {α : Type u_1} {β : α → Type u_2} {P : (x : α) → β x → Prop} [c : (a : α) → encodable (β a)] [d : (x : α) → (y : β x) → Decidable (P x y)] : (∀ (x : α), ∃ (y : β x), P x y) ↔ ∃ (f : (a : α) → β a), ∀ (x : α), P x (f x) := sorry

/-
There is a total ordering on the elements of an encodable type, induced by the map to ℕ.
-/

/-- The `encode` function, viewed as an embedding. -/
def encode' (α : Type u_1) [encodable α] : α ↪ ℕ :=
  function.embedding.mk encode encode_injective

protected instance order.preimage.is_trans {α : Type u_1} [encodable α] : is_trans α (⇑(encode' α) ⁻¹'o LessEq) :=
  rel_embedding.is_trans (rel_embedding.preimage (encode' α) LessEq)

protected instance order.preimage.is_antisymm {α : Type u_1} [encodable α] : is_antisymm α (⇑(encode' α) ⁻¹'o LessEq) :=
  rel_embedding.is_antisymm (rel_embedding.preimage (encode' α) LessEq)

protected instance order.preimage.is_total {α : Type u_1} [encodable α] : is_total α (⇑(encode' α) ⁻¹'o LessEq) :=
  rel_embedding.is_total (rel_embedding.preimage (encode' α) LessEq)

end encodable

namespace directed

/-- Given a `directed r` function `f : α → β` defined on an encodable inhabited type,
construct a noncomputable sequence such that `r (f (x n)) (f (x (n + 1)))`
and `r (f a) (f (x (encode a + 1))`. -/
protected def sequence {α : Type u_1} {β : Type u_2} [encodable α] [Inhabited α] {r : β → β → Prop} (f : α → β) (hf : directed r f) : ℕ → α :=
  sorry

theorem sequence_mono_nat {α : Type u_1} {β : Type u_2} [encodable α] [Inhabited α] {r : β → β → Prop} {f : α → β} (hf : directed r f) (n : ℕ) : r (f (directed.sequence f hf n)) (f (directed.sequence f hf (n + 1))) := sorry

theorem rel_sequence {α : Type u_1} {β : Type u_2} [encodable α] [Inhabited α] {r : β → β → Prop} {f : α → β} (hf : directed r f) (a : α) : r (f a) (f (directed.sequence f hf (encodable.encode a + 1))) := sorry

theorem sequence_mono {α : Type u_1} {β : Type u_2} [encodable α] [Inhabited α] [preorder β] {f : α → β} (hf : directed LessEq f) : monotone (f ∘ directed.sequence f hf) :=
  monotone_of_monotone_nat (sequence_mono_nat hf)

theorem le_sequence {α : Type u_1} {β : Type u_2} [encodable α] [Inhabited α] [preorder β] {f : α → β} (hf : directed LessEq f) (a : α) : f a ≤ f (directed.sequence f hf (encodable.encode a + 1)) :=
  rel_sequence hf a

end directed

/-- Representative of an equivalence class. This is a computable version of `quot.out` for a setoid
on an encodable type. -/
def quotient.rep {α : Type u_1} {s : setoid α} [DecidableRel has_equiv.equiv] [encodable α] (q : quotient s) : α :=
  encodable.choose (quotient.exists_rep q)

theorem quotient.rep_spec {α : Type u_1} {s : setoid α} [DecidableRel has_equiv.equiv] [encodable α] (q : quotient s) : quotient.mk (quotient.rep q) = q :=
  encodable.choose_spec (quotient.exists_rep q)

/-- The quotient of an encodable space by a decidable equivalence relation is encodable. -/
def encodable_quotient {α : Type u_1} {s : setoid α} [DecidableRel has_equiv.equiv] [encodable α] : encodable (quotient s) :=
  encodable.mk (fun (q : quotient s) => encodable.encode (quotient.rep q))
    (fun (n : ℕ) => quotient.mk <$> encodable.decode α n) sorry


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Kenny Lau
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.list.pairwise
import Mathlib.data.list.forall2
import Mathlib.PostPort

universes u v u_1

namespace Mathlib

/-!
# Lists with no duplicates

`list.nodup` is defined in `data/list/defs`. In this file we prove various properties of this
predicate.
-/

namespace list

@[simp] theorem forall_mem_ne {α : Type u} {a : α} {l : List α} : (∀ (a' : α), a' ∈ l → ¬a = a') ↔ ¬a ∈ l :=
  { mp := fun (h : ∀ (a' : α), a' ∈ l → ¬a = a') (m : a ∈ l) => h a m rfl,
    mpr := fun (h : ¬a ∈ l) (a' : α) (m : a' ∈ l) (e : a = a') => h (Eq.symm e ▸ m) }

@[simp] theorem nodup_nil {α : Type u} : nodup [] :=
  pairwise.nil

@[simp] theorem nodup_cons {α : Type u} {a : α} {l : List α} : nodup (a :: l) ↔ ¬a ∈ l ∧ nodup l := sorry

protected theorem pairwise.nodup {α : Type u} {l : List α} {r : α → α → Prop} [is_irrefl α r] (h : pairwise r l) : nodup l :=
  pairwise.imp (fun (a b : α) => ne_of_irrefl) h

theorem rel_nodup {α : Type u} {β : Type v} {r : α → β → Prop} (hr : relator.bi_unique r) : relator.lift_fun (forall₂ r) Iff nodup nodup := sorry

theorem nodup_cons_of_nodup {α : Type u} {a : α} {l : List α} (m : ¬a ∈ l) (n : nodup l) : nodup (a :: l) :=
  iff.mpr nodup_cons { left := m, right := n }

theorem nodup_singleton {α : Type u} (a : α) : nodup [a] :=
  nodup_cons_of_nodup (not_mem_nil a) nodup_nil

theorem nodup_of_nodup_cons {α : Type u} {a : α} {l : List α} (h : nodup (a :: l)) : nodup l :=
  and.right (iff.mp nodup_cons h)

theorem not_mem_of_nodup_cons {α : Type u} {a : α} {l : List α} (h : nodup (a :: l)) : ¬a ∈ l :=
  and.left (iff.mp nodup_cons h)

theorem not_nodup_cons_of_mem {α : Type u} {a : α} {l : List α} : a ∈ l → ¬nodup (a :: l) :=
  iff.mp imp_not_comm not_mem_of_nodup_cons

theorem nodup_of_sublist {α : Type u} {l₁ : List α} {l₂ : List α} : l₁ <+ l₂ → nodup l₂ → nodup l₁ :=
  pairwise_of_sublist

theorem not_nodup_pair {α : Type u} (a : α) : ¬nodup [a, a] :=
  not_nodup_cons_of_mem (mem_singleton_self a)

theorem nodup_iff_sublist {α : Type u} {l : List α} : nodup l ↔ ∀ (a : α), ¬[a, a] <+ l := sorry

theorem nodup_iff_nth_le_inj {α : Type u} {l : List α} : nodup l ↔ ∀ (i j : ℕ) (h₁ : i < length l) (h₂ : j < length l), nth_le l i h₁ = nth_le l j h₂ → i = j := sorry

@[simp] theorem nth_le_index_of {α : Type u} [DecidableEq α] {l : List α} (H : nodup l) (n : ℕ) (h : n < length l) : index_of (nth_le l n h) l = n :=
  iff.mp nodup_iff_nth_le_inj H (index_of (nth_le l n h) l) n (iff.mpr index_of_lt_length (nth_le_mem l n h)) h
    (index_of_nth_le (iff.mpr index_of_lt_length (nth_le_mem l n h)))

theorem nodup_iff_count_le_one {α : Type u} [DecidableEq α] {l : List α} : nodup l ↔ ∀ (a : α), count a l ≤ 1 := sorry

theorem nodup_repeat {α : Type u} (a : α) {n : ℕ} : nodup (repeat a n) ↔ n ≤ 1 := sorry

@[simp] theorem count_eq_one_of_mem {α : Type u} [DecidableEq α] {a : α} {l : List α} (d : nodup l) (h : a ∈ l) : count a l = 1 :=
  le_antisymm (iff.mp nodup_iff_count_le_one d a) (iff.mpr count_pos h)

theorem nodup_of_nodup_append_left {α : Type u} {l₁ : List α} {l₂ : List α} : nodup (l₁ ++ l₂) → nodup l₁ :=
  nodup_of_sublist (sublist_append_left l₁ l₂)

theorem nodup_of_nodup_append_right {α : Type u} {l₁ : List α} {l₂ : List α} : nodup (l₁ ++ l₂) → nodup l₂ :=
  nodup_of_sublist (sublist_append_right l₁ l₂)

theorem nodup_append {α : Type u} {l₁ : List α} {l₂ : List α} : nodup (l₁ ++ l₂) ↔ nodup l₁ ∧ nodup l₂ ∧ disjoint l₁ l₂ := sorry

theorem disjoint_of_nodup_append {α : Type u} {l₁ : List α} {l₂ : List α} (d : nodup (l₁ ++ l₂)) : disjoint l₁ l₂ :=
  and.right (and.right (iff.mp nodup_append d))

theorem nodup_append_of_nodup {α : Type u} {l₁ : List α} {l₂ : List α} (d₁ : nodup l₁) (d₂ : nodup l₂) (dj : disjoint l₁ l₂) : nodup (l₁ ++ l₂) :=
  iff.mpr nodup_append { left := d₁, right := { left := d₂, right := dj } }

theorem nodup_append_comm {α : Type u} {l₁ : List α} {l₂ : List α} : nodup (l₁ ++ l₂) ↔ nodup (l₂ ++ l₁) := sorry

theorem nodup_middle {α : Type u} {a : α} {l₁ : List α} {l₂ : List α} : nodup (l₁ ++ a :: l₂) ↔ nodup (a :: (l₁ ++ l₂)) := sorry

theorem nodup_of_nodup_map {α : Type u} {β : Type v} (f : α → β) {l : List α} : nodup (map f l) → nodup l :=
  pairwise_of_pairwise_map f fun (a b : α) => mt (congr_arg f)

theorem nodup_map_on {α : Type u} {β : Type v} {f : α → β} {l : List α} (H : ∀ (x : α), x ∈ l → ∀ (y : α), y ∈ l → f x = f y → x = y) (d : nodup l) : nodup (map f l) := sorry

theorem nodup_map {α : Type u} {β : Type v} {f : α → β} {l : List α} (hf : function.injective f) : nodup l → nodup (map f l) :=
  nodup_map_on fun (x : α) (_x : x ∈ l) (y : α) (_x : y ∈ l) (h : f x = f y) => hf h

theorem nodup_map_iff {α : Type u} {β : Type v} {f : α → β} {l : List α} (hf : function.injective f) : nodup (map f l) ↔ nodup l :=
  { mp := nodup_of_nodup_map f, mpr := nodup_map hf }

@[simp] theorem nodup_attach {α : Type u} {l : List α} : nodup (attach l) ↔ nodup l := sorry

theorem nodup_pmap {α : Type u} {β : Type v} {p : α → Prop} {f : (a : α) → p a → β} {l : List α} {H : ∀ (a : α), a ∈ l → p a} (hf : ∀ (a : α) (ha : p a) (b : α) (hb : p b), f a ha = f b hb → a = b) (h : nodup l) : nodup (pmap f l H) := sorry

theorem nodup_filter {α : Type u} (p : α → Prop) [decidable_pred p] {l : List α} : nodup l → nodup (filter p l) :=
  pairwise_filter_of_pairwise p

@[simp] theorem nodup_reverse {α : Type u} {l : List α} : nodup (reverse l) ↔ nodup l := sorry

theorem nodup_erase_eq_filter {α : Type u} [DecidableEq α] (a : α) {l : List α} (d : nodup l) : list.erase l a = filter (fun (_x : α) => _x ≠ a) l := sorry

theorem nodup_erase_of_nodup {α : Type u} [DecidableEq α] (a : α) {l : List α} : nodup l → nodup (list.erase l a) :=
  nodup_of_sublist (erase_sublist a l)

theorem nodup_diff {α : Type u} [DecidableEq α] {l₁ : List α} {l₂ : List α} (h : nodup l₁) : nodup (list.diff l₁ l₂) := sorry

theorem mem_erase_iff_of_nodup {α : Type u} [DecidableEq α] {a : α} {b : α} {l : List α} (d : nodup l) : a ∈ list.erase l b ↔ a ≠ b ∧ a ∈ l := sorry

theorem mem_erase_of_nodup {α : Type u} [DecidableEq α] {a : α} {l : List α} (h : nodup l) : ¬a ∈ list.erase l a :=
  fun (H : a ∈ list.erase l a) => and.left (iff.mp (mem_erase_iff_of_nodup h) H) rfl

theorem nodup_join {α : Type u} {L : List (List α)} : nodup (join L) ↔ (∀ (l : List α), l ∈ L → nodup l) ∧ pairwise disjoint L := sorry

theorem nodup_bind {α : Type u} {β : Type v} {l₁ : List α} {f : α → List β} : nodup (list.bind l₁ f) ↔ (∀ (x : α), x ∈ l₁ → nodup (f x)) ∧ pairwise (fun (a b : α) => disjoint (f a) (f b)) l₁ := sorry

theorem nodup_product {α : Type u} {β : Type v} {l₁ : List α} {l₂ : List β} (d₁ : nodup l₁) (d₂ : nodup l₂) : nodup (product l₁ l₂) := sorry

theorem nodup_sigma {α : Type u} {σ : α → Type u_1} {l₁ : List α} {l₂ : (a : α) → List (σ a)} (d₁ : nodup l₁) (d₂ : ∀ (a : α), nodup (l₂ a)) : nodup (list.sigma l₁ l₂) := sorry

theorem nodup_filter_map {α : Type u} {β : Type v} {f : α → Option β} {l : List α} (H : ∀ (a a' : α) (b : β), b ∈ f a → b ∈ f a' → a = a') : nodup l → nodup (filter_map f l) :=
  pairwise_filter_map_of_pairwise f
    fun (a a' : α) (n : a ≠ a') (b : β) (bm : b ∈ f a) (b' : β) (bm' : b' ∈ f a') (e : b = b') =>
      n (H a a' b' (e ▸ bm) bm')

theorem nodup_concat {α : Type u} {a : α} {l : List α} (h : ¬a ∈ l) (h' : nodup l) : nodup (concat l a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (nodup (concat l a))) (concat_eq_append a l)))
    (nodup_append_of_nodup h' (nodup_singleton a) (iff.mpr disjoint_singleton h))

theorem nodup_insert {α : Type u} [DecidableEq α] {a : α} {l : List α} (h : nodup l) : nodup (insert a l) := sorry

theorem nodup_union {α : Type u} [DecidableEq α] (l₁ : List α) {l₂ : List α} (h : nodup l₂) : nodup (l₁ ∪ l₂) := sorry

theorem nodup_inter_of_nodup {α : Type u} [DecidableEq α] {l₁ : List α} (l₂ : List α) : nodup l₁ → nodup (l₁ ∩ l₂) :=
  nodup_filter fun (_x : α) => _x ∈ l₂

@[simp] theorem nodup_sublists {α : Type u} {l : List α} : nodup (sublists l) ↔ nodup l := sorry

@[simp] theorem nodup_sublists' {α : Type u} {l : List α} : nodup (sublists' l) ↔ nodup l := sorry

theorem nodup_sublists_len {α : Type u_1} (n : ℕ) {l : List α} (nd : nodup l) : nodup (sublists_len n l) :=
  nodup_of_sublist (sublists_len_sublist_sublists' n l) (iff.mpr nodup_sublists' nd)

theorem diff_eq_filter_of_nodup {α : Type u} [DecidableEq α] {l₁ : List α} {l₂ : List α} (hl₁ : nodup l₁) : list.diff l₁ l₂ = filter (fun (_x : α) => ¬_x ∈ l₂) l₁ := sorry

theorem mem_diff_iff_of_nodup {α : Type u} [DecidableEq α] {l₁ : List α} {l₂ : List α} (hl₁ : nodup l₁) {a : α} : a ∈ list.diff l₁ l₂ ↔ a ∈ l₁ ∧ ¬a ∈ l₂ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ∈ list.diff l₁ l₂ ↔ a ∈ l₁ ∧ ¬a ∈ l₂)) (diff_eq_filter_of_nodup hl₁)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ∈ filter (fun (_x : α) => ¬_x ∈ l₂) l₁ ↔ a ∈ l₁ ∧ ¬a ∈ l₂)) (propext mem_filter)))
      (iff.refl (a ∈ l₁ ∧ ¬a ∈ l₂)))

theorem nodup_update_nth {α : Type u} {l : List α} {n : ℕ} {a : α} (hl : nodup l) (ha : ¬a ∈ l) : nodup (update_nth l n a) := sorry

theorem nodup.map_update {α : Type u} {β : Type v} [DecidableEq α] {l : List α} (hl : nodup l) (f : α → β) (x : α) (y : β) : map (function.update f x y) l = ite (x ∈ l) (update_nth (map f l) (index_of x l) y) (map f l) := sorry

end list

theorem option.to_list_nodup {α : Type u_1} (o : Option α) : list.nodup (option.to_list o) :=
  option.cases_on o (idRhs (list.nodup []) list.nodup_nil) fun (o : α) => idRhs (list.nodup [o]) (list.nodup_singleton o)