import get_theorems
import testbed.graph_theory
import testbed.dummy

import tactic data.real.basic
open simple_graph expr tactic

--------------------  TACTIC: CHECK WHAT TYPE OF EXPRESSION SOMETHING IS --------------------
meta def print_expr_type : expr →  tactic unit
| e@(var dbi) := tactic.trace "It's a var." -- dbi is the de-Bruijn index of the variable e.g. #0
| e@(sort lev) := tactic.trace "It's a sort." -- used for types
| e@(const nm lev) := tactic.trace "It's a const." -- e.g. simple_graph.degree
| e@(mvar uniq_nm pretty_nm v) := tactic.trace "It's an mvar."
| e@(local_const uniq_nm pretty_nm bi v) := tactic.trace "It's a local const."
| e@(app f x) := do tactic.trace ("It's an app." ++ "\n \t f:"++ to_string f ++ "\n \t x:"++ to_string x)
| e@(lam nm bi d body) := tactic.trace ("It's a lam."
                                        ++ "\n \t d:"++ to_string d
                                        ++ "\n \t body:" ++ to_string body)
| e@(pi nm bi d body) := tactic.trace ("It's a pi."
                                        ++ "\n \t d:"++ to_string d
                                        ++ "\n \t body:" ++ to_string body)
| e@(elet nm v t body) := tactic.trace "It's a elet."
| e@(macro d l) := tactic.trace "It's a macro."

-- example with APP
#eval print_expr_type `(5) --in binary: 1 0 1
#eval bit1 (bit0 1)        -- the natural number 5, as stored in lean

-- example with LAMBDA
#eval print_expr_type `(λ (n : ℕ),  nat.succ n)

--------------------  TACTIC: CHECK IF AN EXPRESSION IS A NAT  --------------------
meta def is_nat (e : expr) : tactic bool :=
do {
   t ← infer_type e,
   if t = `(ℕ) then return tt else return ff
}

#eval trace (is_nat `(5)) -- tt for true
#eval trace (is_nat `(5.3 : ℝ)) -- ff for false
#eval trace (is_nat `(tt)) -- ff for false

--------------------  TACTIC: CHECK IF AN EXPRESSION CAN BE COMPARED WITH ≤ e.g. ℕ or ℝ  --------------------
-- for all terms, find their types (e.g. ℕ)
-- for all those types (e.g. ℕ), see if t.has_le (e.g. ℕ.has_le)
-- todo

--------------------  TACTIC: CHECK IF AN EXPRESSION BEGINS WITH A QUANTIFIER  --------------------

meta def begins_with_forall_quantifier (e : expr) : bool :=
  if (e.is_pi) then tt else ff

meta def begins_with_exists_quantifier (e : expr) : bool :=
  if (e.is_app_of `Exists) then tt else ff

meta def begins_with_quantifier (e : expr) : bool :=
  if begins_with_forall_quantifier e || begins_with_exists_quantifier e then tt else ff

#eval (get_thm_statement `forall_exists_greater) >>= λ e, return (begins_with_forall_quantifier e) >>= trace --tt
#eval (get_thm_statement `exists_one) >>= λ e, return (begins_with_forall_quantifier e) >>= trace -- ff

#eval (get_thm_statement `forall_exists_greater) >>= λ e, return (begins_with_exists_quantifier e) >>= trace --ff
#eval (get_thm_statement `exists_one) >>= λ e, return (begins_with_exists_quantifier e) >>= trace --tt

--------------------  TACTIC: CHECK IF AN EXPRESSION CONTAINS A PARTICULAR CONSTANT  --------------------

meta def contains_const (e : expr) (const_name : name) : tactic bool :=
do {
  let constants_in_e := e.list_constant,
  return (constants_in_e.contains const_name)
}

#eval (get_thm_statement `degree_sum) >>= (λ e, contains_const e ``degree) >>= trace -- tt
#eval (get_thm_statement `edge_bound) >>= (λ e, contains_const e ``degree) >>= trace -- ff

--------------------  TACTIC: CHECK IF AN EXPRESSION IS AN INEQUALITY  --------------------
meta def is_inequality (e : expr) :  bool :=
match e with
  | `(_ ≤  _) := tt
  | `(_ ≥ _) := tt
  | `(_ < _) := tt
  | `(_ > _) := tt
  | _        := ff
end

#eval is_inequality `(5+3)
#eval is_inequality `(3≤5)


/-
Copyright (c) 2022 Jun Yoshida. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-/

import Algdata.Init.LawfulLT
import Algdata.Data.KVChain.Basic
import Algdata.Data.KVChain.MergeWith

/-!

# Free commutative monoids on linearly ordered bases

Given type `α` with instance `LinearLT α`, the free commutative monoid generated by `α` is represented as the set of ascending chains of `α × Nat`, which are implemented in terms of `KVChain`
The unit is the empty list, and the multiplication is defined using `KVChain.mergeWith`.

-/

universe u v

set_option autoImplicit false

/-!
## Some abbreviations
-/

@[inline]
abbrev FreeCMonoid.valType := Subtype (Nat.lt 0)

@[inline]
abbrev FreeCMonoid.valAdd : valType → valType → valType :=
  λ m n => ⟨m.val + n.val, Nat.add_lt_add m.property n.property⟩

instance : DecidableEq FreeCMonoid.valType :=
  inferInstance

instance : LinearLT FreeCMonoid.valType where
  lt := InvImage LT.lt Subtype.val
  trans h₁ h₂ := trans (r:=Nat.lt) (s:=Nat.lt) h₁ h₂
  irrefl m := Irreflective.irrefl (r:=Nat.lt) m.val
  trichot m n := Or.map Subtype.eq id $ Trichotomous.trichot (r:=Nat.lt) m.val n.val

instance : DecidableRel (α:=FreeCMonoid.valType) LT.lt :=
  inferInstanceAs (DecidableRel (α:=Subtype (Nat.lt 0)) (InvImage LT.lt Subtype.val))

/-!
## Definition
-/

--- The free commutative monoid generated by a linearly ordered type
--- @warning Alghouth it only required `LT α`, the monoid structure, which is lately defined, requires `LinearLT α`.
structure FreeCMonoid (α : Type u) [LT α] where
  vars : KVChain (r:=LT.lt (α:=α)) (β:=λ _=>FreeCMonoid.valType)
  deriving DecidableEq

namespace FreeCMonoid

variable {α : Type u}

/-!
## Basic constructions and instances
-/

protected
theorem eq [LT α] {x y : FreeCMonoid α} : x.vars = y.vars → x = y :=
  match x, y with
  | mk xvar, mk yvar => λ h => by simp at h; cases h; rfl

instance instHasOneFreeCMonoid [LT α]: OfNat (FreeCMonoid α) (nat_lit 1) where
  ofNat := ⟨KVChain.nil⟩

instance instMulFreeMonoid [LinearLT α] [DecidableRel (α:=α) LT.lt] : Mul (FreeCMonoid α) where
  mul x y := ⟨KVChain.mergeWith (λ _ a b => Option.some (valAdd a b)) x.vars y.vars⟩

-- Notation to construct monomials
-- cf. in `Prelude`:
--   infixl:65 " + "   => HAdd.hAdd
--   infixl:70 " * "   => HMul.hMul
--   infixr:80 " ^ "   => HPow.hPow
protected
def reprPrec [LT α] [Repr α] : FreeCMonoid α → Nat → Std.Format
| mk xs, prec =>
  match xs.toList with
  | [] => "1"
  | (t::ts) =>
    let f : Sigma (α:=α) (λ _=>valType) → Std.Format :=
      λ t => ite (t.2.val=1) (reprPrec t.1 70) (reprPrec t.1 80 ++ "^{" ++ t.2.val.repr ++ "}")
    (ite (prec > 70) Std.Format.paren id) $ ts.foldl (λ s t => s ++ f t) (f t)

protected
def repr [LT α] [Repr α] : FreeCMonoid α → Std.Format :=
  λ x => FreeCMonoid.reprPrec x 0

instance instReprFreeCMonoid [LT α] [Repr α] : Repr (FreeCMonoid α) where
  reprPrec := FreeCMonoid.reprPrec

--- Use super-scripts instead of `^`
def reprPrettyPrec [LT α] [Repr α] : FreeCMonoid α → Nat → Std.Format
| mk xs, prec =>
  match xs.toList with
  | [] => "1"
  | (t::ts) =>
    let f : Sigma (λ _ : α => valType) → Std.Format :=
      λ t => ite (t.2.val=1) (reprPrec t.1 70) (reprPrec t.1 80 ++ (Nat.toSuperscriptString t.2.val))
    (ite (prec > 70) Std.Format.paren id) $ ts.foldl (λ s t => s ++ f t) (f t)

def reprPretty [LT α] [Repr α] : FreeCMonoid α → Std.Format := λ x => FreeCMonoid.reprPrettyPrec x 0

protected
def fromList [LinearLT α] [DecidableRel (α:=α) LT.lt] : List (α × Nat) → FreeCMonoid α :=
  List.foldl (λ x t => dite (t.2>0) (λ ht => ⟨x.vars.insertWith (λ _ a b => Option.some (valAdd a b)) t.1 ⟨t.2,ht⟩⟩) (λ _ => x)) 1

--- Define a monoid homomorphism in terms of generators.
def elim [LT α] {β γ : Type _} [OfNat γ (nat_lit 1)] [HMul γ γ γ] [HPow β Nat γ] (f : α → β) (xs : FreeCMonoid α): γ :=
  xs.vars.foldl (λ c a n => c * f a ^ n.1) 1

/-!
## Algebraic properties
-/

theorem nil_eq_one [LT α] : FreeCMonoid.mk (α:=α) KVChain.nil = 1 := rfl

@[simp]
theorem one_mul [LinearLT α] [DecidableRel (α:=α) LT.lt] : ∀ (x : FreeCMonoid α), 1 * x = x
| mk xvars => by
  dsimp [OfNat.ofNat, HMul.hMul, Mul.mul]
  rw [KVChain.nil_mergeWith]

theorem mul_one [LinearLT α] [DecidableRel (α:=α) LT.lt] : ∀ (x : FreeCMonoid α), x * 1 = x
| mk xvars => by
  dsimp [OfNat.ofNat, HMul.hMul, Mul.mul]
  rw [KVChain.mergeWith_nil]

section Commutative

variable [LinearLT α] [DecidableRel (α:=α) LT.lt]

theorem valAdd_comm (a b : valType) : valAdd a b = valAdd b a := by
  cases a; cases b; apply Subtype.eq; dsimp [valAdd]; rw [Nat.add_comm]

theorem mul_comm (x y : FreeCMonoid α) : x*y = y*x := by
  dsimp [HMul.hMul, Mul.mul]
  apply FreeCMonoid.eq; dsimp
  rw [KVChain.mergeWith_flip]
  dsimp [flip]
  apply KVChain.mergeWith_congrFun
  intros; rw [valAdd_comm]

end Commutative

/-!
# Calculus on degrees
-/

def degree [LT α] (deg : α → Nat := λ _ => 1): FreeCMonoid α → Nat
| mk vars => vars.foldl (λ d a n => d + deg a * n.val) 0

@[simp]
theorem degree_one [LT α] {deg : α → Nat}: degree deg (1 : FreeCMonoid α) = 0 := rfl

@[simp]
theorem degree_cons [LinearLT α] [DecidableRel (α:=α) LT.lt] {deg : α → Nat} (a : α) (n : valType) (xs : KVChain LT.lt (λ _ : α => valType)) (haxs : xs.boundLeft a) : degree deg {vars := xs.cons a n haxs} = deg a*n.val + degree deg {vars := xs} := by
  dsimp [degree]
  dsimp [KVChain.foldl, List.foldl]
  rw [Nat.add_comm 0]
  apply List.foldl_comm
  intros; rw [Nat.add_assoc]

section degree

variable [LinearLT α] [DecidableRel (α:=α) LT.lt] {deg : α → Nat}

theorem eq_one_of_degree_zero {xs : FreeCMonoid α} (hxs : xs.degree deg = 0) (hdeg_pos : ∀ a, deg a > 0 := by intros; trivial) : xs = 1 := by
  revert hxs
  cases xs with | mk xvars =>
  apply xvars.recOnList (motive:=λ xvars => degree deg ⟨xvars⟩ = 0 → FreeCMonoid.mk xvars = 1)
  case mk.nil => intros; rfl
  case mk.cons =>
    intro a b xvars _ _ heq0
    rw [degree_cons] at heq0
    have : deg a * b.1 > 0 := Nat.mul_pos (hdeg_pos a) b.2
    have : deg a * b.val + degree deg ⟨xvars⟩ > 0 :=
      calc
        0 < deg a * b.val := Nat.mul_pos (hdeg_pos a) b.2
        _ ≤ deg a * b.val + degree deg ⟨xvars⟩ := Nat.le_add_right _ _
    rw [heq0] at this
    cases this

theorem degree_mul [LinearLT α] [DecidableRel (α:=α) LT.lt] : ∀ (x y: FreeCMonoid α), (x*y).degree deg = x.degree deg + y.degree deg
| mk xvars, mk yvars =>
  xvars.recOnListOn
    (λ _ => by rw [nil_eq_one, one_mul, degree_one]; exact (Nat.zero_add _).symm)
    (λ x₁ x₂ xs hxs h_ind_xs yvars =>
      yvars.recOnListOn
        (by rw [nil_eq_one, mul_one, degree_one]; exact (Nat.add_zero _).symm)
        (λ y₁ y₂ ys hys h_ind_ys => by
          dsimp [HMul.hMul, Mul.mul] at *
          apply dite (x₁ < y₁) <;> intro hx₁y₁
          . rw [KVChain.cons_mergeWith_cons_rel hx₁y₁]
            rw [degree_cons x₁ x₂, degree_cons x₁ x₂]
            rw [h_ind_xs, Nat.add_assoc]
          apply dite (y₁ < x₁) <;> intro hy₁x₁
          . rw [KVChain.cons_mergeWith_cons_flip hy₁x₁]
            rw [degree_cons y₁ y₂, degree_cons y₁ y₂]
            rw [h_ind_ys]
            conv =>
              lhs; rw [←Nat.add_assoc, Nat.add_comm (deg y₁ * y₂.val), Nat.add_assoc]
          have : x₁ = y₁ := Trichotomous.eq_of_incomp ⟨hx₁y₁,hy₁x₁⟩
          cases this
          rw [KVChain.cons_mergeWith_cons_rfl]
          rw [degree_cons, degree_cons, degree_cons]
          dsimp
          rw [h_ind_xs]
          conv =>
            rhs;
            rw [
              Nat.add_assoc,
              ←Nat.add_assoc _ (deg x₁ * y₂.val),
              Nat.add_comm _ (deg x₁ * y₂.val),
              Nat.add_assoc,
              ←Nat.add_assoc,
              ←Nat.mul_add (deg x₁) x₂.val y₂.val
            ]
        )
    )
    yvars

end degree

end FreeCMonoid


/-
Copyright 2020 Google LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 -/
import measure_theory.measurable_space
import measure_theory.measure_space
import measure_theory.outer_measure

namespace measure_theory

namespace measure

lemma inter_add_inter_compl {α:Type*} [measurable_space α]
  (μ : measure α) (s t : set α)  (A2 : measurable_set s) (A1 : measurable_set t) :
  (μ (s ∩ t) + μ (s ∩ tᶜ)) = μ s :=
begin
  rw ← measure_union _
       (measurable_set.inter A2 A1) (measurable_set.inter A2 (measurable_set.compl A1)),
  rw set.inter_union_compl,
  apply set.disjoint_of_subset (set.inter_subset_right s t) (set.inter_subset_right s tᶜ),
  apply @disjoint_compl_right (set α) t _,
end

lemma restrict_sub_eq_restrict_sub_restrict {Ω : Type*} [M : measurable_space Ω]
  (μ ν : measure Ω) {s : set Ω} (h_meas_s : measurable_set s) :
  (μ - ν).restrict s = (μ.restrict s) - (ν.restrict s) :=
begin
  repeat {rw sub_def},
  have h_nonempty : {d | μ ≤ d + ν}.nonempty,
  { apply @set.nonempty_of_mem _ _ μ, simp, intros t h_meas,
    apply le_add_right (le_refl (μ t)) },
  rw restrict_Inf_eq_Inf_restrict h_nonempty h_meas_s,
  have h_Inf_le_Inf : ∀ s' t' : set (measure Ω),
                      (∀ b ∈ t', ∃ a ∈ s', a ≤ b) → Inf s' ≤ Inf t',
  { intros s' t' h,
    rw le_Inf_iff, intros b h_b_in_t',
    have h_exists_a := h b h_b_in_t', cases h_exists_a with a h_a,
    cases h_a with h_a_in_s' h_a_le_b,
    apply Inf_le_of_le h_a_in_s' h_a_le_b },
  apply le_antisymm,
  { apply h_Inf_le_Inf,
    intros ν' h_ν'_in, simp at h_ν'_in, apply exists.intro (ν'.restrict s),
    split,
    { simp, apply exists.intro (ν' + (⊤ : measure_theory.measure Ω).restrict sᶜ),
      split,
      { rw [add_assoc, add_comm _ ν, ← add_assoc, measure_theory.measure.le_iff],
        intros t h_meas_t,
        have h_inter_inter_eq_inter : ∀ t' : set Ω , t ∩ t' ∩ t' = t ∩ t',
        { intro t', rw set.inter_eq_self_of_subset_left, apply set.inter_subset_right t t' },
        have h_meas_t_inter_s : measurable_set (t ∩ s) :=
        measurable_set.inter h_meas_t h_meas_s,
        rw [← inter_add_inter_compl μ t s h_meas_t h_meas_s,
            ← inter_add_inter_compl
              (ν' + ν + (⊤ : measure Ω).restrict sᶜ) t s h_meas_t h_meas_s],
        apply add_le_add _ _; rw add_apply,
        { have h_restrict : ∀ μ₂ : measure Ω, μ₂ (t ∩ s) = μ₂.restrict s (t ∩ s),
          { intro μ₂, rw [restrict_apply h_meas_t_inter_s],
            rw [(h_inter_inter_eq_inter s)] },
          apply le_add_right _,
          rw [add_apply, h_restrict μ, h_restrict ν], apply h_ν'_in _ h_meas_t_inter_s },
        cases (@set.eq_empty_or_nonempty _ (t ∩ sᶜ)) with h_inter_empty h_inter_nonempty,
        { simp [h_inter_empty] },
        { have h_meas_inter_compl :=
          measurable_set.inter h_meas_t (measurable_set.compl h_meas_s),
          rw [restrict_apply h_meas_inter_compl, h_inter_inter_eq_inter sᶜ],
          have h_mu_le_add_top : μ ≤ ν' + ν + ⊤,
          { rw add_comm,
            have h_le_top : μ ≤ ⊤ := le_top,
            apply (λ t₂ h_meas, le_add_right (h_le_top t₂ h_meas)) },
          apply h_mu_le_add_top _ h_meas_inter_compl } },
      { ext1 t h_meas_t,
        simp [restrict_apply h_meas_t,
              restrict_apply (measurable_set.inter h_meas_t h_meas_s),
              set.inter_assoc] } },
    { apply restrict_le_self } },
  { apply h_Inf_le_Inf,
    intros s h_s_in, cases h_s_in with t h_t, cases h_t with h_t_in h_t_eq, subst s,
    apply exists.intro (t.restrict s), split,
    { rw [set.mem_set_of_eq, ← restrict_add],
      apply restrict_mono (set.subset.refl _) h_t_in },
    { apply le_refl _ } },
end

lemma restrict_apply_self {α : Type*} [measurable_space α]
  (μ : measure α) {s : set α} (h_meas_s : measurable_set s) :
  (μ.restrict s) s = μ s :=
begin
  rw [restrict_apply h_meas_s, set.inter_self],
end

lemma sub_apply_eq_zero_of_restrict_le_restrict {Ω:Type*} [measurable_space Ω]
    (μ ν:measure_theory.measure Ω) (s:set Ω)
  (h_le : μ.restrict s ≤ ν.restrict s) (h_meas_s : measurable_set s) :
  (μ - ν) s = 0 :=
begin
  rw [← restrict_apply_self _ h_meas_s, restrict_sub_eq_restrict_sub_restrict,
      sub_eq_zero_of_le],
  repeat {simp [*]},
end

end measure

end measure_theory

def measure_theory.finite_measure_of_le {Ω:Type*} [M:measurable_space Ω]
  (μ ν : measure_theory.measure Ω) [measure_theory.finite_measure ν] (H:μ ≤ ν):
  measure_theory.finite_measure μ :=
begin
  apply measure_theory.finite_measure.mk (lt_of_le_of_lt (H set.univ measurable_set.univ) _),
  apply measure_theory.measure_lt_top ν,
end

def measure_theory.finite_measure_restrict {Ω:Type*} [M:measurable_space Ω]
  (μ:measure_theory.measure Ω) [measure_theory.finite_measure μ] (S:set Ω):
  measure_theory.finite_measure (μ.restrict S) :=
begin
  have A1:= @measure_theory.measure.restrict_le_self Ω M μ S,
  apply measure_theory.finite_measure_of_le (μ.restrict S) μ A1,
end

--This works with EITHER ν or μ being finite, or even ν S < ⊤.
lemma jordan_decomposition_junior_apply {Ω:Type*} [measurable_space Ω]
    (μ ν:measure_theory.measure Ω) (S:set Ω) [AX:measure_theory.finite_measure ν]:
  (ν.restrict S ≤ μ.restrict S) → (measurable_set S) →
  (μ - ν) S = μ S - ν S :=
begin
  intros A1 B1,
  rw ← measure_theory.measure.restrict_apply_self _ B1,
  rw measure_theory.measure.restrict_sub_eq_restrict_sub_restrict _ _,
  rw @measure_theory.measure.sub_apply Ω _ _ _ S (measure_theory.finite_measure_restrict ν S) B1 A1,
  repeat {rw measure_theory.measure.restrict_apply_self},
  repeat {exact B1},
end

lemma measure_theory.measure.restrict_apply_subset {α : Type*} [measurable_space α]
  (μ : measure_theory.measure α) {S T : set α} (h₁ : measurable_set S)
  (h₂ : S ⊆ T) : (μ.restrict T) S = μ S :=
begin
  rw measure_theory.measure.restrict_apply h₁,
  simp [set.inter_eq_self_of_subset_left, h₂],
end

lemma le_of_subset_of_restrict_le_restrict {α:Type*} [measurable_space α]
  {μ ν:measure_theory.measure α} {S T:set α}:
  T ⊆ S →
  (μ.restrict S) ≤ ν.restrict S →
  measurable_set S →
  measurable_set T →  μ T ≤ ν T :=
begin
  intros A3 A2 A1 A4,
  rw measure_theory.measure.le_iff at A2,
  have B3 := A2 T A4,
  rw measure_theory.measure.restrict_apply_subset μ A4 A3 at B3,
  rw measure_theory.measure.restrict_apply_subset ν A4 A3 at B3,
  apply B3,
end

lemma restrict_le_restrict_of_restrict_le_restrict_of_subset {α:Type*} [measurable_space α]
    {μ ν:measure_theory.measure α} {X Y:set α}:
    μ.restrict X ≤ ν.restrict X →
    Y ⊆ X →
    measurable_set X →
    measurable_set Y →
    μ.restrict Y ≤ ν.restrict Y :=
begin
  intros A1 A2 A3 A4,
  rw measure_theory.measure.le_iff,
  intros S A5,
  rw measure_theory.measure.restrict_apply,
  rw measure_theory.measure.restrict_apply,
  have A6:S ∩ Y ⊆ X,
  {apply set.subset.trans _ A2, simp},
  apply le_of_subset_of_restrict_le_restrict A6 A1 A3,
  repeat {simp [measurable_set.inter,*]},
end

/--
  A jordan decomposition of subtraction.
 -/
lemma jordan_decomposition_nonneg_sub {Ω:Type*} [M:measurable_space Ω]
    (μ ν:measure_theory.measure Ω) (S T:set Ω) [A1:measure_theory.finite_measure μ]:
    measurable_set T → measurable_set S → μ.restrict S ≤ ν.restrict S →
    ν.restrict Sᶜ ≤ μ.restrict Sᶜ →
    (ν - μ) T = ν (T ∩ S) - μ (T ∩ S) :=
begin
  intros A3 A2 A5 A6,
  rw ← measure_theory.measure.inter_add_inter_compl (ν - μ) _ _ A3 A2,
  rw jordan_decomposition_junior_apply ν μ (T ∩ S),
  rw measure_theory.measure.sub_apply_eq_zero_of_restrict_le_restrict ν μ (T ∩ Sᶜ),
  rw add_zero,
  apply @restrict_le_restrict_of_restrict_le_restrict_of_subset Ω M ν μ Sᶜ (T ∩ Sᶜ) A6,
  repeat {simp [A2, A3]},
  apply @restrict_le_restrict_of_restrict_le_restrict_of_subset Ω M μ ν S (T ∩ S) A5,
  repeat {simp [A2, A3]},
end

lemma restrict_le_restrict_of_le_subset {α:Type*} [measurable_space α]
  {μ ν:measure_theory.measure α} {S:set α} (H:measurable_set S):
  (∀ T:set α, T ⊆ S → measurable_set T → μ T ≤ ν T) →
  (μ.restrict S) ≤ ν.restrict S :=
begin
  intros A1,
  rw measure_theory.measure.le_iff,
  intros s A2,
  repeat {rw measure_theory.measure.restrict_apply A2},
  apply A1,
  {simp},
  {simp [measurable_set.inter,H,A2]},
end

lemma restrict_le_restrict_union {α:Type*} [measurable_space α]
    {μ ν:measure_theory.measure α} {X Y:set α}:
    μ.restrict X ≤ ν.restrict X →
    μ.restrict Y ≤ ν.restrict Y →
    measurable_set X →
    measurable_set Y →
    μ.restrict (X ∪ Y) ≤ ν.restrict (X ∪ Y) :=
begin
  intros A1 A2 A3 A4,
  have A6:X ∪ (Y \ X) = X ∪ Y := set.union_diff_self,
  rw ← A6,
  have A7:disjoint X (Y \ X) := set.disjoint_diff,
  repeat {rw measure_theory.measure.restrict_union},
  apply measure_theory.measure.add_le_add A1 _,
  apply restrict_le_restrict_of_restrict_le_restrict_of_subset A2,
  apply set.diff_subset,
  repeat {simp [set.disjoint_diff, measurable_set.inter, measurable_set.diff,*]},
end

lemma set.directed_has_subset_of_monotone {α:Type*} {f:ℕ → set α}:
 monotone f → directed has_subset.subset f :=
begin
  intros A1,
  unfold directed,
  intros x y,
  apply exists.intro (max x y),
  split,
  {
    rw ← set.le_eq_subset,
    apply @A1 x (max x y),
    apply le_max_left,
  },
  {
    rw ← set.le_eq_subset,
    apply @A1 y (max x y),
    apply le_max_right,
  },
end

lemma restrict_le_restrict_m_Union {α:Type*} [measurable_space α]
    (μ ν:measure_theory.measure α) {f:ℕ → set α}:
    monotone f →
    (∀ n:ℕ, measurable_set (f n)) →
    (∀ n:ℕ, μ.restrict (f n) ≤ ν.restrict (f n)) →
    (μ.restrict (set.Union f) ≤  ν.restrict (set.Union f)) :=
begin
  intros A1 A2 A3 S A5,
  rw measure_theory.measure.restrict_Union_apply_eq_supr,
  rw measure_theory.measure.restrict_Union_apply_eq_supr,
  rw supr_le_iff,
  intro i,
  have B1:(μ.restrict (f i)) S ≤ (ν.restrict (f i)) S,
  {apply A3, apply A5},
  apply le_trans B1,

  apply @le_supr ennreal _ _,
  repeat {simp [set.directed_has_subset_of_monotone,*]},
end

lemma nnreal.add_sub_add_eq_sub_add_sub {a b c d:nnreal} : c ≤ a → d ≤ b →
  a + b - (c + d) = (a - c) + (b - d) :=
begin
  intros A1 A2,
  have A3:c + d ≤ a + b,
  { apply add_le_add A1 A2 },
  repeat {rw ← nnreal.eq_iff <|> rw nnreal.coe_sub <|> rw nnreal.coe_add},
  repeat {assumption},
  linarith,
end

lemma ennreal.add_sub_add_eq_sub_add_sub {a b c d:ennreal}:c < ⊤ → d < ⊤ →
    c ≤ a → d ≤ b →
    a + b - (c + d) = (a - c) + (b - d) :=
begin
  cases c;cases b;cases a;cases d;
  simp [ennreal.top_sub_coe, ennreal.none_eq_top, ennreal.some_eq_coe,
    ennreal.add_top, ← ennreal.coe_add, ← ennreal.coe_sub],
  apply nnreal.add_sub_add_eq_sub_add_sub,
end

lemma le_measurable_add {α:Type*} [M:measurable_space α]
    (μ ν:measure_theory.measure α) {X Y:set α}:
    μ X < ⊤ →
    μ Y < ⊤ →
    measurable_set X →
    measurable_set Y →
    μ X ≤ ν X →
    μ Y ≤ ν Y →
    disjoint X Y →
    ν (X ∪ Y) - μ (X ∪ Y) = (ν X - μ X) +  (ν Y - μ Y) :=
begin
  intros A1 A2 A3 A4 A5 A6 A7,
  repeat {rw measure_theory.measure_union A7 A3 A4},
  rw ennreal.add_sub_add_eq_sub_add_sub A1 A2 A5 A6,
end

lemma measure_theory.measure.le_of_restrict_le_restrict_self {α:Type*} [measurable_space α]
  (μ ν:measure_theory.measure α) {S:set α} (H:measurable_set S):
  (μ.restrict S) ≤ ν.restrict S  → μ S ≤  ν S :=
begin
  intro A1,
  apply le_of_subset_of_restrict_le_restrict (set.subset.refl S) A1 H H,
end

lemma restrict_le_restrict_add {α:Type*} [M:measurable_space α]
    (μ ν:measure_theory.measure α) {X Y:set α}:
    μ X < ⊤ →
    μ Y < ⊤ →
    measurable_set X →
    measurable_set Y →
    μ.restrict X ≤ ν.restrict X →
    μ.restrict Y ≤ ν.restrict Y →
    disjoint X Y →
    ν (X ∪ Y) - μ (X ∪ Y) = (ν X - μ X) +  (ν Y - μ Y) :=
begin
  intros A1 A2 A3 A4 A5 A6 A7,
  apply le_measurable_add μ ν A1 A2 A3 A4
      (measure_theory.measure.le_of_restrict_le_restrict_self _ _ A3 A5)
      (measure_theory.measure.le_of_restrict_le_restrict_self _ _ A4 A6) A7,
end


/-
Copyright (c) 2018 Johan Commelin All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Chris Hughes, Kevin Buzzard
Ported by: Winston Yin

! This file was ported from Lean 3 source module algebra.hom.units
! leanprover-community/mathlib commit dc6c365e751e34d100e80fe6e314c3c3e0fd2988
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Hom.Group
import Mathlib.Algebra.Group.Units

/-!
# Monoid homomorphisms and units

This file allows to lift monoid homomorphisms to group homomorphisms of their units subgroups. It
also contains unrelated results about `Units` that depend on `MonoidHom`.

## Main declarations

* `Units.map`: Turn an homomorphism from `α` to `β` monoids into an homomorphism from `αˣ` to `βˣ`.
* `MonoidHom.toHomUnits`: Turn an homomorphism from a group `α` to `β` into an homomorphism from
  `α` to `βˣ`.

## TODO

The results that don't mention homomorphisms should be proved (earlier?) in a different file and be
used to golf the basic `Group` lemmas.
-/

open Function

universe u v w

@[to_additive]
theorem Group.isUnit {G} [Group G] (g : G) : IsUnit g :=
  ⟨⟨g, g⁻¹, mul_inv_self g, inv_mul_self g⟩, rfl⟩
#align group.is_unit Group.isUnit
#align add_group.is_add_unit AddGroup.isAddUnit

section MonoidHomClass

/-- If two homomorphisms from a division monoid to a monoid are equal at a unit `x`, then they are
equal at `x⁻¹`. -/
@[to_additive
  "If two homomorphisms from a subtraction monoid to an additive monoid are equal at an
  additive unit `x`, then they are equal at `-x`."]
theorem IsUnit.eq_on_inv {F G N} [DivisionMonoid G] [Monoid N] [MonoidHomClass F G N]
  {x : G} (hx : IsUnit x) (f g : F) (h : f x = g x) : f x⁻¹ = g x⁻¹ :=
left_inv_eq_right_inv (map_mul_eq_one f hx.inv_mul_cancel)
  (h.symm ▸ map_mul_eq_one g (hx.mul_inv_cancel))
#align is_unit.eq_on_inv IsUnit.eq_on_inv
#align is_add_unit.eq_on_neg IsAddUnit.eq_on_neg

/-- If two homomorphism from a group to a monoid are equal at `x`, then they are equal at `x⁻¹`. -/
@[to_additive
    "If two homomorphism from an additive group to an additive monoid are equal at `x`,
    then they are equal at `-x`."]
theorem eq_on_inv {F G M} [Group G] [Monoid M] [MonoidHomClass F G M]
  (f g : F) {x : G} (h : f x = g x) : f x⁻¹ = g x⁻¹ :=
  (Group.isUnit x).eq_on_inv f g h
#align eq_on_inv eq_on_inv
#align eq_on_neg eq_on_neg

end MonoidHomClass

namespace Units

variable {α : Type _} {M : Type u} {N : Type v} {P : Type w} [Monoid M] [Monoid N] [Monoid P]

/-- The group homomorphism on units induced by a `MonoidHom`. -/
@[to_additive "The additive homomorphism on `AddUnit`s induced by an `AddMonoidHom`."]
def map (f : M →* N) : Mˣ →* Nˣ :=
  MonoidHom.mk'
    (fun u => ⟨f u.val, f u.inv,
      by rw [← f.map_mul, u.val_inv, f.map_one],
      by rw [← f.map_mul, u.inv_val, f.map_one]⟩)
    fun x y => ext (f.map_mul x y)
#align units.map Units.map
#align add_units.map AddUnits.map

@[to_additive (attr := simp)]
theorem coe_map (f : M →* N) (x : Mˣ) : ↑(map f x) = f x := rfl
#align units.coe_map Units.coe_map
#align add_units.coe_map AddUnits.coe_map

@[to_additive (attr := simp)]
theorem coe_map_inv (f : M →* N) (u : Mˣ) : ↑(map f u)⁻¹ = f ↑u⁻¹ := rfl
#align units.coe_map_inv Units.coe_map_inv
#align add_units.coe_map_neg AddUnits.coe_map_neg

@[to_additive (attr := simp)]
theorem map_comp (f : M →* N) (g : N →* P) : map (g.comp f) = (map g).comp (map f) := rfl
#align units.map_comp Units.map_comp
#align add_units.map_comp AddUnits.map_comp

variable (M)

@[to_additive (attr := simp)]
theorem map_id : map (MonoidHom.id M) = MonoidHom.id Mˣ := by ext; rfl
#align units.map_id Units.map_id
#align add_units.map_id AddUnits.map_id

/-- Coercion `Mˣ → M` as a monoid homomorphism. -/
@[to_additive "Coercion `AddUnits M → M` as an AddMonoid homomorphism."]
def coeHom : Mˣ →* M :=
  ⟨⟨Units.val, val_one⟩, val_mul⟩
#align units.coe_hom Units.coeHom
#align add_units.coe_hom AddUnits.coeHom

variable {M}

@[to_additive (attr := simp)]
theorem coeHom_apply (x : Mˣ) : coeHom M x = ↑x := rfl
#align units.coe_hom_apply Units.coeHom_apply
#align add_units.coe_hom_apply AddUnits.coeHom_apply

@[to_additive (attr := simp, norm_cast)]
theorem val_pow_eq_pow_val (u : Mˣ) (n : ℕ) : ((u ^ n : Mˣ) : M) = (u : M) ^ n :=
  (Units.coeHom M).map_pow u n
#align units.coe_pow Units.val_pow_eq_pow_val
#align add_units.coe_nsmul AddUnits.val_nsmul_eq_nsmul_val

section DivisionMonoid

variable [DivisionMonoid α]

@[to_additive (attr := simp, norm_cast)]
theorem val_div_eq_div_val : ∀ u₁ u₂ : αˣ, ↑(u₁ / u₂) = (u₁ / u₂ : α) :=
  (Units.coeHom α).map_div
#align units.coe_div Units.val_div_eq_div_val
#align add_units.coe_sub AddUnits.val_neg_eq_neg_val

@[to_additive (attr := simp, norm_cast)]
theorem val_zpow_eq_zpow_val : ∀ (u : αˣ) (n : ℤ), ((u ^ n : αˣ) : α) = (u : α) ^ n :=
  (Units.coeHom α).map_zpow
#align units.coe_zpow Units.val_zpow_eq_zpow_val
#align add_units.coe_zsmul AddUnits.val_zsmul_eq_zsmul_val

@[field_simps]
theorem _root_.divp_eq_div (a : α) (u : αˣ) : a /ₚ u = a / u :=
  by rw [div_eq_mul_inv, divp, u.val_inv_eq_inv_val]
#align divp_eq_div divp_eq_div

@[to_additive (attr := simp)]
theorem _root_.map_units_inv {F : Type _} [MonoidHomClass F M α] (f : F) (u : Units M) :
  f ↑u⁻¹ = (f u)⁻¹ := ((f : M →* α).comp (Units.coeHom M)).map_inv u
#align map_units_inv map_units_inv
#align map_add_units_neg map_addUnits_neg

end DivisionMonoid

/-- If a map `g : M → Nˣ` agrees with a homomorphism `f : M →* N`, then
this map is a monoid homomorphism too. -/
@[to_additive
  "If a map `g : M → AddUnits N` agrees with a homomorphism `f : M →+ N`, then this map
  is an AddMonoid homomorphism too."]
def liftRight (f : M →* N) (g : M → Nˣ) (h : ∀ x, ↑(g x) = f x) : M →* Nˣ where
  toFun := g
  map_one' := by ext; dsimp only; rw [h 1]; exact f.map_one -- Porting note: why is `dsimp` needed?
  map_mul' x y := Units.ext <| by simp only [h, val_mul, f.map_mul]
#align units.lift_right Units.liftRight
#align add_units.lift_right AddUnits.liftRight

@[to_additive (attr := simp)]
theorem coe_liftRight {f : M →* N} {g : M → Nˣ} (h : ∀ x, ↑(g x) = f x) (x) :
  (liftRight f g h x : N) = f x := h x
#align units.coe_lift_right Units.coe_liftRight
#align add_units.coe_lift_right AddUnits.coe_liftRight

@[to_additive (attr := simp)]
theorem mul_liftRight_inv {f : M →* N} {g : M → Nˣ} (h : ∀ x, ↑(g x) = f x) (x) :
  f x * ↑(liftRight f g h x)⁻¹ = 1 :=
  by rw [Units.mul_inv_eq_iff_eq_mul, one_mul, coe_liftRight]
#align units.mul_lift_right_inv Units.mul_liftRight_inv
#align add_units.add_lift_right_neg AddUnits.add_liftRight_neg

@[to_additive (attr := simp)]
theorem liftRight_inv_mul {f : M →* N} {g : M → Nˣ} (h : ∀ x, ↑(g x) = f x) (x) :
  ↑(liftRight f g h x)⁻¹ * f x = 1 :=
  by rw [Units.inv_mul_eq_iff_eq_mul, mul_one, coe_liftRight]
#align units.lift_right_inv_mul Units.liftRight_inv_mul
#align add_units.lift_right_neg_add AddUnits.liftRight_neg_add

end Units

namespace MonoidHom

/-- If `f` is a homomorphism from a group `G` to a monoid `M`,
then its image lies in the units of `M`,
and `f.toHomUnits` is the corresponding monoid homomorphism from `G` to `Mˣ`. -/
@[to_additive
  "If `f` is a homomorphism from an additive group `G` to an additive monoid `M`,
  then its image lies in the `AddUnits` of `M`,
  and `f.toHomUnits` is the corresponding homomorphism from `G` to `AddUnits M`."]
def toHomUnits {G M : Type _} [Group G] [Monoid M] (f : G →* M) : G →* Mˣ :=
  Units.liftRight f (fun g => ⟨f g, f g⁻¹, map_mul_eq_one f (mul_inv_self _),
    map_mul_eq_one f (inv_mul_self _)⟩)
    fun _ => rfl
#align monoid_hom.to_hom_units MonoidHom.toHomUnits
#align add_monoid_hom.to_hom_add_units AddMonoidHom.toHomAddUnits

@[to_additive (attr := simp)]
theorem coe_toHomUnits {G M : Type _} [Group G] [Monoid M] (f : G →* M) (g : G) :
  (f.toHomUnits g : M) = f g := rfl
#align monoid_hom.coe_to_hom_units MonoidHom.coe_toHomUnits
#align add_monoid_hom.coe_to_hom_add_units AddMonoidHom.coe_toHomAddUnits

end MonoidHom

namespace IsUnit

variable {F G α M N : Type _}

section Monoid

variable [Monoid M] [Monoid N]

@[to_additive]
theorem map [MonoidHomClass F M N] (f : F) {x : M} (h : IsUnit x) : IsUnit (f x) := by
  rcases h with ⟨y, rfl⟩; exact (Units.map (f : M →* N) y).isUnit
#align is_unit.map IsUnit.map
#align is_add_unit.map IsAddUnit.map

@[to_additive]
theorem of_leftInverse [MonoidHomClass F M N] [MonoidHomClass G N M] {f : F} {x : M} (g : G)
  (hfg : Function.LeftInverse g f) (h : IsUnit (f x)) : IsUnit x :=
  by simpa only [hfg x] using h.map g
#align is_unit.of_left_inverse IsUnit.of_leftInverse
#align is_add_unit.of_left_inverse IsAddUnit.of_leftInverse

@[to_additive]
theorem _root_.isUnit_map_of_leftInverse [MonoidHomClass F M N] [MonoidHomClass G N M]
  {f : F} {x : M} (g : G) (hfg : Function.LeftInverse g f) :
  IsUnit (f x) ↔ IsUnit x := ⟨of_leftInverse g hfg, map _⟩
#align is_unit_map_of_left_inverse isUnit_map_of_leftInverse
#align is_add_unit_map_of_left_inverse isAddUnit_map_of_leftInverse

/-- If a homomorphism `f : M →* N` sends each element to an `IsUnit`, then it can be lifted
to `f : M →* Nˣ`. See also `Units.liftRight` for a computable version. -/
@[to_additive
  "If a homomorphism `f : M →+ N` sends each element to an `IsAddUnit`, then it can be
  lifted to `f : M →+ AddUnits N`. See also `AddUnits.liftRight` for a computable version."]
noncomputable def liftRight (f : M →* N) (hf : ∀ x, IsUnit (f x)) : M →* Nˣ :=
  (Units.liftRight f fun x => (hf x).unit) fun _ => rfl
#align is_unit.lift_right IsUnit.liftRight
#align is_add_unit.lift_right IsAddUnit.liftRight

@[to_additive]
theorem coe_liftRight (f : M →* N) (hf : ∀ x, IsUnit (f x)) (x) :
  (IsUnit.liftRight f hf x : N) = f x := rfl
#align is_unit.coe_lift_right IsUnit.coe_liftRight
#align is_add_unit.coe_lift_right IsAddUnit.coe_liftRight

@[to_additive (attr := simp)]
theorem mul_liftRight_inv (f : M →* N) (h : ∀ x, IsUnit (f x)) (x) :
  f x * ↑(IsUnit.liftRight f h x)⁻¹ = 1 := Units.mul_liftRight_inv (by intro; rfl) x
#align is_unit.mul_lift_right_inv IsUnit.mul_liftRight_inv
#align is_add_unit.add_lift_right_neg IsAddUnit.add_liftRight_neg

@[to_additive (attr := simp)]
theorem liftRight_inv_mul (f : M →* N) (h : ∀ x, IsUnit (f x)) (x) :
  ↑(IsUnit.liftRight f h x)⁻¹ * f x = 1 := Units.liftRight_inv_mul (by intro; rfl) x
#align is_unit.lift_right_inv_mul IsUnit.liftRight_inv_mul
#align is_add_unit.lift_right_neg_add IsAddUnit.liftRight_neg_add

end Monoid

section DivisionMonoid

variable [DivisionMonoid α] {a b c : α}

/-- The element of the group of units, corresponding to an element of a monoid which is a unit. As
opposed to `IsUnit.unit`, the inverse is computable and comes from the inversion on `α`. This is
useful to transfer properties of inversion in `Units α` to `α`. See also `toUnits`. -/
@[to_additive (attr := simps)
  "The element of the additive group of additive units, corresponding to an element of
  an additive monoid which is an additive unit. As opposed to `IsAddUnit.addUnit`, the negation is
  computable and comes from the negation on `α`. This is useful to transfer properties of negation
  in `AddUnits α` to `α`. See also `toAddUnits`."]
def unit' (h : IsUnit a) : αˣ :=
  ⟨a, a⁻¹, h.mul_inv_cancel, h.inv_mul_cancel⟩
#align is_unit.unit' IsUnit.unit'
#align is_add_unit.add_unit' IsAddUnit.addUnit'

@[to_additive (attr := simp)]
protected theorem mul_inv_cancel_left (h : IsUnit a) : ∀ b, a * (a⁻¹ * b) = b :=
  h.unit'.mul_inv_cancel_left
#align is_unit.mul_inv_cancel_left IsUnit.mul_inv_cancel_left
#align is_add_unit.add_neg_cancel_left IsAddUnit.add_neg_cancel_left

@[to_additive (attr := simp)]
protected theorem inv_mul_cancel_left (h : IsUnit a) : ∀ b, a⁻¹ * (a * b) = b :=
  h.unit'.inv_mul_cancel_left
#align is_unit.inv_mul_cancel_left IsUnit.inv_mul_cancel_left
#align is_add_unit.neg_add_cancel_left IsAddUnit.neg_add_cancel_left

@[to_additive (attr := simp)]
protected theorem mul_inv_cancel_right (h : IsUnit b) (a : α) : a * b * b⁻¹ = a :=
  h.unit'.mul_inv_cancel_right _
#align is_unit.mul_inv_cancel_right IsUnit.mul_inv_cancel_right
#align is_add_unit.add_neg_cancel_right IsAddUnit.add_neg_cancel_right

@[to_additive (attr := simp)]
protected theorem inv_mul_cancel_right (h : IsUnit b) (a : α) : a * b⁻¹ * b = a :=
  h.unit'.inv_mul_cancel_right _
#align is_unit.inv_mul_cancel_right IsUnit.inv_mul_cancel_right
#align is_add_unit.neg_add_cancel_right IsAddUnit.neg_add_cancel_right

@[to_additive]
protected theorem div_self (h : IsUnit a) : a / a = 1 := by rw [div_eq_mul_inv, h.mul_inv_cancel]
#align is_unit.div_self IsUnit.div_self
#align is_add_unit.sub_self IsAddUnit.sub_self

@[to_additive]
protected theorem eq_mul_inv_iff_mul_eq (h : IsUnit c) : a = b * c⁻¹ ↔ a * c = b :=
  h.unit'.eq_mul_inv_iff_mul_eq
#align is_unit.eq_mul_inv_iff_mul_eq IsUnit.eq_mul_inv_iff_mul_eq
#align is_add_unit.eq_add_neg_iff_add_eq IsAddUnit.eq_add_neg_iff_add_eq

@[to_additive]
protected theorem eq_inv_mul_iff_mul_eq (h : IsUnit b) : a = b⁻¹ * c ↔ b * a = c :=
  h.unit'.eq_inv_mul_iff_mul_eq
#align is_unit.eq_inv_mul_iff_mul_eq IsUnit.eq_inv_mul_iff_mul_eq
#align is_add_unit.eq_neg_add_iff_add_eq IsAddUnit.eq_neg_add_iff_add_eq

@[to_additive]
protected theorem inv_mul_eq_iff_eq_mul (h : IsUnit a) : a⁻¹ * b = c ↔ b = a * c :=
  h.unit'.inv_mul_eq_iff_eq_mul
#align is_unit.inv_mul_eq_iff_eq_mul IsUnit.inv_mul_eq_iff_eq_mul
#align is_add_unit.neg_add_eq_iff_eq_add IsAddUnit.neg_add_eq_iff_eq_add

@[to_additive]
protected theorem mul_inv_eq_iff_eq_mul (h : IsUnit b) : a * b⁻¹ = c ↔ a = c * b :=
  h.unit'.mul_inv_eq_iff_eq_mul
#align is_unit.mul_inv_eq_iff_eq_mul IsUnit.mul_inv_eq_iff_eq_mul
#align is_add_unit.add_neg_eq_iff_eq_add IsAddUnit.add_neg_eq_iff_eq_add

@[to_additive]
protected theorem mul_inv_eq_one (h : IsUnit b) : a * b⁻¹ = 1 ↔ a = b :=
  @Units.mul_inv_eq_one _ _ h.unit' _
#align is_unit.mul_inv_eq_one IsUnit.mul_inv_eq_one
#align is_add_unit.add_neg_eq_zero IsAddUnit.add_neg_eq_zero

@[to_additive]
protected theorem inv_mul_eq_one (h : IsUnit a) : a⁻¹ * b = 1 ↔ a = b :=
  @Units.inv_mul_eq_one _ _ h.unit' _
#align is_unit.inv_mul_eq_one IsUnit.inv_mul_eq_one
#align is_add_unit.neg_add_eq_zero IsAddUnit.neg_add_eq_zero

@[to_additive]
protected theorem mul_eq_one_iff_eq_inv (h : IsUnit b) : a * b = 1 ↔ a = b⁻¹ :=
  @Units.mul_eq_one_iff_eq_inv _ _ h.unit' _
#align is_unit.mul_eq_one_iff_eq_inv IsUnit.mul_eq_one_iff_eq_inv
#align is_add_unit.add_eq_zero_iff_eq_neg IsAddUnit.add_eq_zero_iff_eq_neg

@[to_additive]
protected theorem mul_eq_one_iff_inv_eq (h : IsUnit a) : a * b = 1 ↔ a⁻¹ = b :=
  @Units.mul_eq_one_iff_inv_eq _ _ h.unit' _
#align is_unit.mul_eq_one_iff_inv_eq IsUnit.mul_eq_one_iff_inv_eq
#align is_add_unit.add_eq_zero_iff_neg_eq IsAddUnit.add_eq_zero_iff_neg_eq

@[to_additive (attr := simp)]
protected theorem div_mul_cancel (h : IsUnit b) (a : α) : a / b * b = a := by
  rw [div_eq_mul_inv, h.inv_mul_cancel_right]
#align is_unit.div_mul_cancel IsUnit.div_mul_cancel
#align is_add_unit.sub_add_cancel IsAddUnit.sub_add_cancel

@[to_additive (attr := simp)]
protected theorem mul_div_cancel (h : IsUnit b) (a : α) : a * b / b = a := by
  rw [div_eq_mul_inv, h.mul_inv_cancel_right]
#align is_unit.mul_div_cancel IsUnit.mul_div_cancel
#align is_add_unit.add_sub_cancel IsAddUnit.add_sub_cancel

@[to_additive]
protected theorem mul_one_div_cancel (h : IsUnit a) : a * (1 / a) = 1 := by simp [h]
#align is_unit.mul_one_div_cancel IsUnit.mul_one_div_cancel
#align is_add_unit.add_zero_sub_cancel IsAddUnit.add_zero_sub_cancel

@[to_additive]
protected theorem one_div_mul_cancel (h : IsUnit a) : 1 / a * a = 1 := by simp [h]
#align is_unit.one_div_mul_cancel IsUnit.one_div_mul_cancel
#align is_add_unit.zero_sub_add_cancel IsAddUnit.zero_sub_add_cancel

@[to_additive]
theorem inv (h : IsUnit a) : IsUnit a⁻¹ := by
  rcases h with ⟨u, hu⟩
  rw [←hu, ← Units.val_inv_eq_inv_val]
  exact Units.isUnit _
#align is_unit.inv IsUnit.inv
#align is_add_unit.neg IsAddUnit.neg

@[to_additive]
theorem div (ha : IsUnit a) (hb : IsUnit b) : IsUnit (a / b) := by
  rw [div_eq_mul_inv]
  exact ha.mul hb.inv
#align is_unit.div IsUnit.div
#align is_add_unit.sub IsAddUnit.sub

@[to_additive]
protected theorem div_left_inj (h : IsUnit c) : a / c = b / c ↔ a = b := by
  simp only [div_eq_mul_inv]
  exact Units.mul_left_inj h.inv.unit'
#align is_unit.div_left_inj IsUnit.div_left_inj
#align is_add_unit.sub_left_inj IsAddUnit.sub_left_inj

@[to_additive]
protected theorem div_eq_iff (h : IsUnit b) : a / b = c ↔ a = c * b := by
  rw [div_eq_mul_inv, h.mul_inv_eq_iff_eq_mul]
#align is_unit.div_eq_iff IsUnit.div_eq_iff
#align is_add_unit.sub_eq_iff IsAddUnit.sub_eq_iff

@[to_additive]
protected theorem eq_div_iff (h : IsUnit c) : a = b / c ↔ a * c = b := by
  rw [div_eq_mul_inv, h.eq_mul_inv_iff_mul_eq]
#align is_unit.eq_div_iff IsUnit.eq_div_iff
#align is_add_unit.eq_sub_iff IsAddUnit.eq_sub_iff

@[to_additive]
protected theorem div_eq_of_eq_mul (h : IsUnit b) : a = c * b → a / b = c :=
  h.div_eq_iff.2
#align is_unit.div_eq_of_eq_mul IsUnit.div_eq_of_eq_mul
#align is_add_unit.sub_eq_of_eq_add IsAddUnit.sub_eq_of_eq_add

@[to_additive]
protected theorem eq_div_of_mul_eq (h : IsUnit c) : a * c = b → a = b / c :=
  h.eq_div_iff.2
#align is_unit.eq_div_of_mul_eq IsUnit.eq_div_of_mul_eq
#align is_add_unit.eq_sub_of_add_eq IsAddUnit.eq_sub_of_add_eq

@[to_additive]
protected theorem div_eq_one_iff_eq (h : IsUnit b) : a / b = 1 ↔ a = b :=
  ⟨eq_of_div_eq_one, fun hab => hab.symm ▸ h.div_self⟩
#align is_unit.div_eq_one_iff_eq IsUnit.div_eq_one_iff_eq
#align is_add_unit.sub_eq_zero_iff_eq IsAddUnit.sub_eq_zero_iff_eq

@[to_additive]
protected theorem div_mul_left (h : IsUnit b) : b / (a * b) = 1 / a := by
  rw [div_eq_mul_inv, mul_inv_rev, h.mul_inv_cancel_left, one_div]
#align is_unit.div_mul_left IsUnit.div_mul_left
#align is_add_unit.sub_add_left IsAddUnit.sub_add_left

@[to_additive]
protected theorem mul_div_mul_right (h : IsUnit c) (a b : α) : a * c / (b * c) = a / b := by
  simp only [div_eq_mul_inv, mul_inv_rev, mul_assoc, h.mul_inv_cancel_left]
#align is_unit.mul_div_mul_right IsUnit.mul_div_mul_right
#align is_add_unit.add_sub_add_right IsAddUnit.add_sub_add_right

@[to_additive]
protected

end DivisionMonoid

section DivisionCommMonoid

variable [DivisionCommMonoid α] {a b c d : α}

@[to_additive]
protected theorem div_mul_right (h : IsUnit a) (b : α) : a / (a * b) = 1 / b := by
  rw [mul_comm, h.div_mul_left]
#align is_unit.div_mul_right IsUnit.div_mul_right
#align is_add_unit.sub_add_right IsAddUnit.sub_add_right

@[to_additive]
protected theorem mul_div_cancel_left (h : IsUnit a) (b : α) : a * b / a = b := by
  rw [mul_comm, h.mul_div_cancel]
#align is_unit.mul_div_cancel_left IsUnit.mul_div_cancel_left
#align is_add_unit.add_sub_cancel_left IsAddUnit.add_sub_cancel_left

@[to_additive]
protected theorem mul_div_cancel' (h : IsUnit a) (b : α) : a * (b / a) = b := by
  rw [mul_comm, h.div_mul_cancel]
#align is_unit.mul_div_cancel' IsUnit.mul_div_cancel'
#align is_add_unit.add_sub_cancel' IsAddUnit.add_sub_cancel'

@[to_additive]
protected theorem mul_div_mul_left (h : IsUnit c) (a b : α) : c * a / (c * b) = a / b := by
  rw [mul_comm c, mul_comm c, h.mul_div_mul_right]
#align is_unit.mul_div_mul_left IsUnit.mul_div_mul_left
#align is_add_unit.add_sub_add_left IsAddUnit.add_sub_add_left

@[to_additive]
protected theorem mul_eq_mul_of_div_eq_div (hb : IsUnit b) (hd : IsUnit d)
  (a c : α) (h : a / b = c / d) : a * d = c * b := by
  rw [← mul_one a, ← hb.div_self, ← mul_comm_div, h, div_mul_eq_mul_div, hd.div_mul_cancel]
#align is_unit.mul_eq_mul_of_div_eq_div IsUnit.mul_eq_mul_of_div_eq_div
#align is_add_unit.add_eq_add_of_sub_eq_sub IsAddUnit.add_eq_add_of_sub_eq_sub

@[to_additive]
protected theorem div_eq_div_iff (hb : IsUnit b) (hd : IsUnit d) :
  a / b = c / d ↔ a * d = c * b := by
  rw [← (hb.mul hd).mul_left_inj, ← mul_assoc, hb.div_mul_cancel, ← mul_assoc, mul_right_comm,
    hd.div_mul_cancel]
#align is_unit.div_eq_div_iff IsUnit.div_eq_div_iff
#align is_add_unit.sub_eq_sub_iff IsAddUnit.sub_eq_sub_iff

@[to_additive]
protected theorem div_div_cancel (h : IsUnit a) : a / (a / b) = b := by
  rw [div_div_eq_mul_div, h.mul_div_cancel_left]
#align is_unit.div_div_cancel IsUnit.div_div_cancel
#align is_add_unit.sub_sub_cancel IsAddUnit.sub_sub_cancel

@[to_additive]
protected theorem div_div_cancel_left (h : IsUnit a) : a / b / a = b⁻¹ := by
  rw [div_eq_mul_inv, div_eq_mul_inv, mul_right_comm, h.mul_inv_cancel, one_mul]
#align is_unit.div_div_cancel_left IsUnit.div_div_cancel_left
#align is_add_unit.sub_sub_cancel_left IsAddUnit.sub_sub_cancel_left

end DivisionCommMonoid

end IsUnit


/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Robert Y. Lewis
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.ring_theory.witt_vector.structure_polynomial
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Witt vectors

In this file we define the type of `p`-typical Witt vectors and ring operations on it.
The ring axioms are verified in `ring_theory/witt_vector/basic.lean`.

For a fixed commutative ring `R` and prime `p`,
a Witt vector `x : 𝕎 R` is an infinite sequence `ℕ → R` of elements of `R`.
However, the ring operations `+` and `*` are not defined in the obvious component-wise way.
Instead, these operations are defined via certain polynomials
using the machinery in `structure_polynomial.lean`.
The `n`th value of the sum of two Witt vectors can depend on the `0`-th through `n`th values
of the summands. This effectively simulates a “carrying” operation.

## Main definitions

* `witt_vector p R`: the type of `p`-typical Witt vectors with coefficients in `R`.
* `witt_vector.coeff x n`: projects the `n`th value of the Witt vector `x`.

## Notation

We use notation `𝕎 R`, entered `\bbW`, for the Witt vectors over `R`.

-/

/-- `witt_vector p R` is the ring of `p`-typical Witt vectors over the commutative ring `R`,
where `p` is a prime number.

If `p` is invertible in `R`, this ring is isomorphic to `ℕ → R` (the product of `ℕ` copies of `R`).
If `R` is a ring of characteristic `p`, then `witt_vector p R` is a ring of characteristic `0`.
The canonical example is `witt_vector p (zmod p)`,
which is isomorphic to the `p`-adic integers `ℤ_[p]`. -/
def witt_vector (p : ℕ) (R : Type u_1) := ℕ → R

/- We cannot make this `localized` notation, because the `p` on the RHS doesn't occur on the left
Hiding the `p` in the notation is very convenient, so we opt for repeating the `local notation`
in other files that use Witt vectors. -/

namespace witt_vector

/-- Construct a Witt vector `mk p x : 𝕎 R` from a sequence `x` of elements of `R`. -/
def mk (p : ℕ) {R : Type u_1} (x : ℕ → R) : witt_vector p R := x

protected instance inhabited {p : ℕ} {R : Type u_1} [Inhabited R] : Inhabited (witt_vector p R) :=
  { default := mk p fun (_x : ℕ) => Inhabited.default }

/--
`x.coeff n` is the `n`th coefficient of the Witt vector `x`.

This concept does not have a standard name in the literature.
-/
def coeff {p : ℕ} {R : Type u_1} (x : witt_vector p R) (n : ℕ) : R := x n

theorem ext {p : ℕ} {R : Type u_1} {x : witt_vector p R} {y : witt_vector p R}
    (h : ∀ (n : ℕ), coeff x n = coeff y n) : x = y :=
  funext fun (n : ℕ) => h n

theorem ext_iff {p : ℕ} {R : Type u_1} {x : witt_vector p R} {y : witt_vector p R} :
    x = y ↔ ∀ (n : ℕ), coeff x n = coeff y n :=
  { mp :=
      fun (h : x = y) (n : ℕ) =>
        eq.mpr (id (Eq._oldrec (Eq.refl (coeff x n = coeff y n)) h)) (Eq.refl (coeff y n)),
    mpr := ext }

@[simp] theorem coeff_mk (p : ℕ) {R : Type u_1} (x : ℕ → R) : coeff (mk p x) = x := rfl

/- These instances are not needed for the rest of the development,
but it is interesting to establish early on that `witt_vector p` is a lawful functor. -/

protected instance functor (p : ℕ) : Functor (witt_vector p) :=
  { map := fun (α β : Type u_1) (f : α → β) (v : witt_vector p α) => f ∘ v,
    mapConst := fun (α β : Type u_1) (a : α) (v : witt_vector p β) (_x : ℕ) => a }

protected instance is_lawful_functor (p : ℕ) : is_lawful_functor (witt_vector p) :=
  is_lawful_functor.mk (fun (α : Type u_1) (v : witt_vector p α) => rfl)
    fun (α β γ : Type u_1) (f : α → β) (g : β → γ) (v : witt_vector p α) => rfl

/-- The polynomials used for defining the element `0` of the ring of Witt vectors. -/
def witt_zero (p : ℕ) [hp : fact (nat.prime p)] : ℕ → mv_polynomial (fin 0 × ℕ) ℤ :=
  witt_structure_int p 0

/-- The polynomials used for defining the element `1` of the ring of Witt vectors. -/
def witt_one (p : ℕ) [hp : fact (nat.prime p)] : ℕ → mv_polynomial (fin 0 × ℕ) ℤ :=
  witt_structure_int p 1

/-- The polynomials used for defining the addition of the ring of Witt vectors. -/
def witt_add (p : ℕ) [hp : fact (nat.prime p)] : ℕ → mv_polynomial (fin (bit0 1) × ℕ) ℤ :=
  witt_structure_int p (mv_polynomial.X 0 + mv_polynomial.X 1)

/-- The polynomials used for describing the subtraction of the ring of Witt vectors. -/
def witt_sub (p : ℕ) [hp : fact (nat.prime p)] : ℕ → mv_polynomial (fin (bit0 1) × ℕ) ℤ :=
  witt_structure_int p (mv_polynomial.X 0 - mv_polynomial.X 1)

/-- The polynomials used for defining the multiplication of the ring of Witt vectors. -/
def witt_mul (p : ℕ) [hp : fact (nat.prime p)] : ℕ → mv_polynomial (fin (bit0 1) × ℕ) ℤ :=
  witt_structure_int p (mv_polynomial.X 0 * mv_polynomial.X 1)

/-- The polynomials used for defining the negation of the ring of Witt vectors. -/
def witt_neg (p : ℕ) [hp : fact (nat.prime p)] : ℕ → mv_polynomial (fin 1 × ℕ) ℤ :=
  witt_structure_int p (-mv_polynomial.X 0)

/-- An auxiliary definition used in `witt_vector.eval`.
Evaluates a polynomial whose variables come from the disjoint union of `k` copies of `ℕ`,
with a curried evaluation `x`.
This can be defined more generally but we use only a specific instance here. -/
def peval {R : Type u_1} [comm_ring R] {k : ℕ} (φ : mv_polynomial (fin k × ℕ) ℤ)
    (x : fin k → ℕ → R) : R :=
  coe_fn (mv_polynomial.aeval (function.uncurry x)) φ

/--
Let `φ` be a family of polynomials, indexed by natural numbers, whose variables come from the
disjoint union of `k` copies of `ℕ`, and let `xᵢ` be a Witt vector for `0 ≤ i < k`.

`eval φ x` evaluates `φ` mapping the variable `X_(i, n)` to the `n`th coefficient of `xᵢ`.

Instantiating `φ` with certain polynomials defined in `structure_polynomial.lean` establishes the
ring operations on `𝕎 R`. For example, `witt_vector.witt_add` is such a `φ` with `k = 2`;
evaluating this at `(x₀, x₁)` gives us the sum of two Witt vectors `x₀ + x₁`.
-/
def eval {p : ℕ} {R : Type u_1} [comm_ring R] {k : ℕ} (φ : ℕ → mv_polynomial (fin k × ℕ) ℤ)
    (x : fin k → witt_vector p R) : witt_vector p R :=
  mk p fun (n : ℕ) => peval (φ n) fun (i : fin k) => coeff (x i)

protected instance has_zero {p : ℕ} (R : Type u_1) [comm_ring R] [fact (nat.prime p)] :
    HasZero (witt_vector p R) :=
  { zero := eval (witt_zero p) matrix.vec_empty }

protected instance has_one {p : ℕ} (R : Type u_1) [comm_ring R] [fact (nat.prime p)] :
    HasOne (witt_vector p R) :=
  { one := eval (witt_one p) matrix.vec_empty }

protected instance has_add {p : ℕ} (R : Type u_1) [comm_ring R] [fact (nat.prime p)] :
    Add (witt_vector p R) :=
  { add :=
      fun (x y : witt_vector p R) =>
        eval (witt_add p) (matrix.vec_cons x (matrix.vec_cons y matrix.vec_empty)) }

protected instance has_sub {p : ℕ} (R : Type u_1) [comm_ring R] [fact (nat.prime p)] :
    Sub (witt_vector p R) :=
  { sub :=
      fun (x y : witt_vector p R) =>
        eval (witt_sub p) (matrix.vec_cons x (matrix.vec_cons y matrix.vec_empty)) }

protected instance has_mul {p : ℕ} (R : Type u_1) [comm_ring R] [fact (nat.prime p)] :
    Mul (witt_vector p R) :=
  { mul :=
      fun (x y : witt_vector p R) =>
        eval (witt_mul p) (matrix.vec_cons x (matrix.vec_cons y matrix.vec_empty)) }

protected instance has_neg {p : ℕ} (R : Type u_1) [comm_ring R] [fact (nat.prime p)] :
    Neg (witt_vector p R) :=
  { neg := fun (x : witt_vector p R) => eval (witt_neg p) (matrix.vec_cons x matrix.vec_empty) }

@[simp] theorem witt_zero_eq_zero (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) : witt_zero p n = 0 :=
  sorry

@[simp] theorem witt_one_zero_eq_one (p : ℕ) [hp : fact (nat.prime p)] : witt_one p 0 = 1 := sorry

@[simp] theorem witt_one_pos_eq_zero (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) (hn : 0 < n) :
    witt_one p n = 0 :=
  sorry

@[simp] theorem witt_add_zero (p : ℕ) [hp : fact (nat.prime p)] :
    witt_add p 0 = mv_polynomial.X (0, 0) + mv_polynomial.X (1, 0) :=
  sorry

@[simp] theorem witt_sub_zero (p : ℕ) [hp : fact (nat.prime p)] :
    witt_sub p 0 = mv_polynomial.X (0, 0) - mv_polynomial.X (1, 0) :=
  sorry

@[simp] theorem witt_mul_zero (p : ℕ) [hp : fact (nat.prime p)] :
    witt_mul p 0 = mv_polynomial.X (0, 0) * mv_polynomial.X (1, 0) :=
  sorry

@[simp] theorem witt_neg_zero (p : ℕ) [hp : fact (nat.prime p)] :
    witt_neg p 0 = -mv_polynomial.X (0, 0) :=
  sorry

@[simp] theorem constant_coeff_witt_add (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) :
    coe_fn mv_polynomial.constant_coeff (witt_add p n) = 0 :=
  sorry

@[simp] theorem constant_coeff_witt_sub (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) :
    coe_fn mv_polynomial.constant_coeff (witt_sub p n) = 0 :=
  sorry

@[simp] theorem constant_coeff_witt_mul (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) :
    coe_fn mv_polynomial.constant_coeff (witt_mul p n) = 0 :=
  sorry

@[simp] theorem constant_coeff_witt_neg (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) :
    coe_fn mv_polynomial.constant_coeff (witt_neg p n) = 0 :=
  sorry

@[simp] theorem zero_coeff (p : ℕ) (R : Type u_1) [hp : fact (nat.prime p)] [comm_ring R] (n : ℕ) :
    coeff 0 n = 0 :=
  sorry

@[simp] theorem one_coeff_zero (p : ℕ) (R : Type u_1) [hp : fact (nat.prime p)] [comm_ring R] :
    coeff 1 0 = 1 :=
  sorry

@[simp] theorem one_coeff_eq_of_pos (p : ℕ) (R : Type u_1) [hp : fact (nat.prime p)] [comm_ring R]
    (n : ℕ) (hn : 0 < n) : coeff 1 n = 0 :=
  sorry

theorem add_coeff {p : ℕ} {R : Type u_1} [hp : fact (nat.prime p)] [comm_ring R]
    (x : witt_vector p R) (y : witt_vector p R) (n : ℕ) :
    coeff (x + y) n =
        peval (witt_add p n)
          (matrix.vec_cons (coeff x) (matrix.vec_cons (coeff y) matrix.vec_empty)) :=
  rfl

theorem sub_coeff {p : ℕ} {R : Type u_1} [hp : fact (nat.prime p)] [comm_ring R]
    (x : witt_vector p R) (y : witt_vector p R) (n : ℕ) :
    coeff (x - y) n =
        peval (witt_sub p n)
          (matrix.vec_cons (coeff x) (matrix.vec_cons (coeff y) matrix.vec_empty)) :=
  rfl

theorem mul_coeff {p : ℕ} {R : Type u_1} [hp : fact (nat.prime p)] [comm_ring R]
    (x : witt_vector p R) (y : witt_vector p R) (n : ℕ) :
    coeff (x * y) n =
        peval (witt_mul p n)
          (matrix.vec_cons (coeff x) (matrix.vec_cons (coeff y) matrix.vec_empty)) :=
  rfl

theorem neg_coeff {p : ℕ} {R : Type u_1} [hp : fact (nat.prime p)] [comm_ring R]
    (x : witt_vector p R) (n : ℕ) :
    coeff (-x) n = peval (witt_neg p n) (matrix.vec_cons (coeff x) matrix.vec_empty) :=
  rfl

theorem witt_add_vars (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) :
    mv_polynomial.vars (witt_add p n) ⊆ finset.product finset.univ (finset.range (n + 1)) :=
  witt_structure_int_vars p (mv_polynomial.X 0 + mv_polynomial.X 1) n

theorem witt_sub_vars (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) :
    mv_polynomial.vars (witt_sub p n) ⊆ finset.product finset.univ (finset.range (n + 1)) :=
  witt_structure_int_vars p (mv_polynomial.X 0 - mv_polynomial.X 1) n

theorem witt_mul_vars (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) :
    mv_polynomial.vars (witt_mul p n) ⊆ finset.product finset.univ (finset.range (n + 1)) :=
  witt_structure_int_vars p (mv_polynomial.X 0 * mv_polynomial.X 1) n

theorem witt_neg_vars (p : ℕ) [hp : fact (nat.prime p)] (n : ℕ) :
    mv_polynomial.vars (witt_neg p n) ⊆ finset.product finset.univ (finset.range (n + 1)) :=
  witt_structure_int_vars p (-mv_polynomial.X 0) n

end Mathlib


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

! This file was ported from Lean 3 source module topology.locally_finite
! leanprover-community/mathlib commit 55d771df074d0dd020139ee1cd4b95521422df9f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.ContinuousOn
import Mathbin.Order.Filter.SmallSets

/-!
### Locally finite families of sets

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

We say that a family of sets in a topological space is *locally finite* if at every point `x : X`,
there is a neighborhood of `x` which meets only finitely many sets in the family.

In this file we give the definition and prove basic properties of locally finite families of sets.
-/

-- locally finite family [General Topology (Bourbaki, 1995)]
open Set Function Filter

open Topology Filter

universe u

variable {ι : Type u} {ι' α X Y : Type _} [TopologicalSpace X] [TopologicalSpace Y]
  {f g : ι → Set X}

#print LocallyFinite /-
/-- A family of sets in `set X` is locally finite if at every point `x : X`,
there is a neighborhood of `x` which meets only finitely many sets in the family. -/
def LocallyFinite (f : ι → Set X) :=
  ∀ x : X, ∃ t ∈ 𝓝 x, { i | (f i ∩ t).Nonempty }.Finite
#align locally_finite LocallyFinite
-/

/- warning: locally_finite_of_finite -> locallyFinite_of_finite is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_3 : Finite.{succ u1} ι] (f : ι -> (Set.{u2} X)), LocallyFinite.{u1, u2} ι X _inst_1 f
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_3 : Finite.{succ u2} ι] (f : ι -> (Set.{u1} X)), LocallyFinite.{u2, u1} ι X _inst_1 f
Case conversion may be inaccurate. Consider using '#align locally_finite_of_finite locallyFinite_of_finiteₓ'. -/
theorem locallyFinite_of_finite [Finite ι] (f : ι → Set X) : LocallyFinite f := fun x =>
  ⟨univ, univ_mem, toFinite _⟩
#align locally_finite_of_finite locallyFinite_of_finite

namespace LocallyFinite

/- warning: locally_finite.point_finite -> LocallyFinite.point_finite is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall (x : X), Set.Finite.{u1} ι (setOf.{u1} ι (fun (b : ι) => Membership.Mem.{u2, u2} X (Set.{u2} X) (Set.hasMem.{u2} X) x (f b))))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (forall (x : X), Set.Finite.{u2} ι (setOf.{u2} ι (fun (b : ι) => Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) x (f b))))
Case conversion may be inaccurate. Consider using '#align locally_finite.point_finite LocallyFinite.point_finiteₓ'. -/
theorem point_finite (hf : LocallyFinite f) (x : X) : { b | x ∈ f b }.Finite :=
  let ⟨t, hxt, ht⟩ := hf x
  ht.Subset fun b hb => ⟨x, hb, mem_of_mem_nhds hxt⟩
#align locally_finite.point_finite LocallyFinite.point_finite

/- warning: locally_finite.subset -> LocallyFinite.subset is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)} {g : ι -> (Set.{u2} X)}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall (i : ι), HasSubset.Subset.{u2} (Set.{u2} X) (Set.hasSubset.{u2} X) (g i) (f i)) -> (LocallyFinite.{u1, u2} ι X _inst_1 g)
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)} {g : ι -> (Set.{u1} X)}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (forall (i : ι), HasSubset.Subset.{u1} (Set.{u1} X) (Set.instHasSubsetSet.{u1} X) (g i) (f i)) -> (LocallyFinite.{u2, u1} ι X _inst_1 g)
Case conversion may be inaccurate. Consider using '#align locally_finite.subset LocallyFinite.subsetₓ'. -/
protected theorem subset (hf : LocallyFinite f) (hg : ∀ i, g i ⊆ f i) : LocallyFinite g := fun a =>
  let ⟨t, ht₁, ht₂⟩ := hf a
  ⟨t, ht₁, ht₂.Subset fun i hi => hi.mono <| inter_subset_inter (hg i) Subset.rfl⟩
#align locally_finite.subset LocallyFinite.subset

/- warning: locally_finite.comp_inj_on -> LocallyFinite.comp_injOn is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {ι' : Type.{u2}} {X : Type.{u3}} [_inst_1 : TopologicalSpace.{u3} X] {f : ι -> (Set.{u3} X)} {g : ι' -> ι}, (LocallyFinite.{u1, u3} ι X _inst_1 f) -> (Set.InjOn.{u2, u1} ι' ι g (setOf.{u2} ι' (fun (i : ι') => Set.Nonempty.{u3} X (f (g i))))) -> (LocallyFinite.{u2, u3} ι' X _inst_1 (Function.comp.{succ u2, succ u1, succ u3} ι' ι (Set.{u3} X) f g))
but is expected to have type
  forall {ι : Type.{u3}} {ι' : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)} {g : ι' -> ι}, (LocallyFinite.{u3, u2} ι X _inst_1 f) -> (Set.InjOn.{u1, u3} ι' ι g (setOf.{u1} ι' (fun (i : ι') => Set.Nonempty.{u2} X (f (g i))))) -> (LocallyFinite.{u1, u2} ι' X _inst_1 (Function.comp.{succ u1, succ u3, succ u2} ι' ι (Set.{u2} X) f g))
Case conversion may be inaccurate. Consider using '#align locally_finite.comp_inj_on LocallyFinite.comp_injOnₓ'. -/
theorem comp_injOn {g : ι' → ι} (hf : LocallyFinite f) (hg : InjOn g { i | (f (g i)).Nonempty }) :
    LocallyFinite (f ∘ g) := fun x =>
  let ⟨t, htx, htf⟩ := hf x
  ⟨t, htx, htf.Preimage <| hg.mono fun i hi => hi.out.mono <| inter_subset_left _ _⟩
#align locally_finite.comp_inj_on LocallyFinite.comp_injOn

/- warning: locally_finite.comp_injective -> LocallyFinite.comp_injective is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {ι' : Type.{u2}} {X : Type.{u3}} [_inst_1 : TopologicalSpace.{u3} X] {f : ι -> (Set.{u3} X)} {g : ι' -> ι}, (LocallyFinite.{u1, u3} ι X _inst_1 f) -> (Function.Injective.{succ u2, succ u1} ι' ι g) -> (LocallyFinite.{u2, u3} ι' X _inst_1 (Function.comp.{succ u2, succ u1, succ u3} ι' ι (Set.{u3} X) f g))
but is expected to have type
  forall {ι : Type.{u3}} {ι' : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)} {g : ι' -> ι}, (LocallyFinite.{u3, u2} ι X _inst_1 f) -> (Function.Injective.{succ u1, succ u3} ι' ι g) -> (LocallyFinite.{u1, u2} ι' X _inst_1 (Function.comp.{succ u1, succ u3, succ u2} ι' ι (Set.{u2} X) f g))
Case conversion may be inaccurate. Consider using '#align locally_finite.comp_injective LocallyFinite.comp_injectiveₓ'. -/
theorem comp_injective {g : ι' → ι} (hf : LocallyFinite f) (hg : Injective g) :
    LocallyFinite (f ∘ g) :=
  hf.comp_injOn (hg.InjOn _)
#align locally_finite.comp_injective LocallyFinite.comp_injective

/- warning: locally_finite_iff_small_sets -> locallyFinite_iff_smallSets is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)}, Iff (LocallyFinite.{u1, u2} ι X _inst_1 f) (forall (x : X), Filter.Eventually.{u2} (Set.{u2} X) (fun (s : Set.{u2} X) => Set.Finite.{u1} ι (setOf.{u1} ι (fun (i : ι) => Set.Nonempty.{u2} X (Inter.inter.{u2} (Set.{u2} X) (Set.hasInter.{u2} X) (f i) s)))) (Filter.smallSets.{u2} X (nhds.{u2} X _inst_1 x)))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)}, Iff (LocallyFinite.{u2, u1} ι X _inst_1 f) (forall (x : X), Filter.Eventually.{u1} (Set.{u1} X) (fun (s : Set.{u1} X) => Set.Finite.{u2} ι (setOf.{u2} ι (fun (i : ι) => Set.Nonempty.{u1} X (Inter.inter.{u1} (Set.{u1} X) (Set.instInterSet.{u1} X) (f i) s)))) (Filter.smallSets.{u1} X (nhds.{u1} X _inst_1 x)))
Case conversion may be inaccurate. Consider using '#align locally_finite_iff_small_sets locallyFinite_iff_smallSetsₓ'. -/
theorem locallyFinite_iff_smallSets :
    LocallyFinite f ↔ ∀ x, ∀ᶠ s in (𝓝 x).smallSets, { i | (f i ∩ s).Nonempty }.Finite :=
  forall_congr' fun x =>
    Iff.symm <|
      eventually_small_sets' fun s t hst ht =>
        ht.Subset fun i hi => hi.mono <| inter_subset_inter_right _ hst
#align locally_finite_iff_small_sets locallyFinite_iff_smallSets

/- warning: locally_finite.eventually_small_sets -> LocallyFinite.eventually_smallSets is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall (x : X), Filter.Eventually.{u2} (Set.{u2} X) (fun (s : Set.{u2} X) => Set.Finite.{u1} ι (setOf.{u1} ι (fun (i : ι) => Set.Nonempty.{u2} X (Inter.inter.{u2} (Set.{u2} X) (Set.hasInter.{u2} X) (f i) s)))) (Filter.smallSets.{u2} X (nhds.{u2} X _inst_1 x)))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (forall (x : X), Filter.Eventually.{u1} (Set.{u1} X) (fun (s : Set.{u1} X) => Set.Finite.{u2} ι (setOf.{u2} ι (fun (i : ι) => Set.Nonempty.{u1} X (Inter.inter.{u1} (Set.{u1} X) (Set.instInterSet.{u1} X) (f i) s)))) (Filter.smallSets.{u1} X (nhds.{u1} X _inst_1 x)))
Case conversion may be inaccurate. Consider using '#align locally_finite.eventually_small_sets LocallyFinite.eventually_smallSetsₓ'. -/
protected theorem eventually_smallSets (hf : LocallyFinite f) (x : X) :
    ∀ᶠ s in (𝓝 x).smallSets, { i | (f i ∩ s).Nonempty }.Finite :=
  locallyFinite_iff_smallSets.mp hf x
#align locally_finite.eventually_small_sets LocallyFinite.eventually_smallSets

/- warning: locally_finite.exists_mem_basis -> LocallyFinite.exists_mem_basis is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)} {ι' : Sort.{u3}}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall {p : ι' -> Prop} {s : ι' -> (Set.{u2} X)} {x : X}, (Filter.HasBasis.{u2, u3} X ι' (nhds.{u2} X _inst_1 x) p s) -> (Exists.{u3} ι' (fun (i : ι') => Exists.{0} (p i) (fun (hi : p i) => Set.Finite.{u1} ι (setOf.{u1} ι (fun (j : ι) => Set.Nonempty.{u2} X (Inter.inter.{u2} (Set.{u2} X) (Set.hasInter.{u2} X) (f j) (s i))))))))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)} {ι' : Sort.{u3}}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (forall {p : ι' -> Prop} {s : ι' -> (Set.{u1} X)} {x : X}, (Filter.HasBasis.{u1, u3} X ι' (nhds.{u1} X _inst_1 x) p s) -> (Exists.{u3} ι' (fun (i : ι') => And (p i) (Set.Finite.{u2} ι (setOf.{u2} ι (fun (j : ι) => Set.Nonempty.{u1} X (Inter.inter.{u1} (Set.{u1} X) (Set.instInterSet.{u1} X) (f j) (s i))))))))
Case conversion may be inaccurate. Consider using '#align locally_finite.exists_mem_basis LocallyFinite.exists_mem_basisₓ'. -/
theorem exists_mem_basis {ι' : Sort _} (hf : LocallyFinite f) {p : ι' → Prop} {s : ι' → Set X}
    {x : X} (hb : (𝓝 x).HasBasis p s) : ∃ (i : _)(hi : p i), { j | (f j ∩ s i).Nonempty }.Finite :=
  let ⟨i, hpi, hi⟩ := hb.smallSets.eventually_iff.mp (hf.eventually_smallSets x)
  ⟨i, hpi, hi Subset.rfl⟩
#align locally_finite.exists_mem_basis LocallyFinite.exists_mem_basis

/- warning: locally_finite.nhds_within_Union -> LocallyFinite.nhdsWithin_unionᵢ is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall (a : X), Eq.{succ u2} (Filter.{u2} X) (nhdsWithin.{u2} X _inst_1 a (Set.unionᵢ.{u2, succ u1} X ι (fun (i : ι) => f i))) (supᵢ.{u2, succ u1} (Filter.{u2} X) (ConditionallyCompleteLattice.toHasSup.{u2} (Filter.{u2} X) (CompleteLattice.toConditionallyCompleteLattice.{u2} (Filter.{u2} X) (Filter.completeLattice.{u2} X))) ι (fun (i : ι) => nhdsWithin.{u2} X _inst_1 a (f i))))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (forall (a : X), Eq.{succ u1} (Filter.{u1} X) (nhdsWithin.{u1} X _inst_1 a (Set.unionᵢ.{u1, succ u2} X ι (fun (i : ι) => f i))) (supᵢ.{u1, succ u2} (Filter.{u1} X) (ConditionallyCompleteLattice.toSupSet.{u1} (Filter.{u1} X) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} X) (Filter.instCompleteLatticeFilter.{u1} X))) ι (fun (i : ι) => nhdsWithin.{u1} X _inst_1 a (f i))))
Case conversion may be inaccurate. Consider using '#align locally_finite.nhds_within_Union LocallyFinite.nhdsWithin_unionᵢₓ'. -/
protected theorem nhdsWithin_unionᵢ (hf : LocallyFinite f) (a : X) :
    𝓝[⋃ i, f i] a = ⨆ i, 𝓝[f i] a :=
  by
  rcases hf a with ⟨U, haU, hfin⟩
  refine' le_antisymm _ (supᵢ_le fun i => nhdsWithin_mono _ (subset_Union _ _))
  calc
    𝓝[⋃ i, f i] a = 𝓝[⋃ i, f i ∩ U] a := by
      rw [← Union_inter, ← nhdsWithin_inter_of_mem' (nhdsWithin_le_nhds haU)]
    _ = 𝓝[⋃ i ∈ { j | (f j ∩ U).Nonempty }, f i ∩ U] a := by
      simp only [mem_set_of_eq, Union_nonempty_self]
    _ = ⨆ i ∈ { j | (f j ∩ U).Nonempty }, 𝓝[f i ∩ U] a := (nhdsWithin_bunionᵢ hfin _ _)
    _ ≤ ⨆ i, 𝓝[f i ∩ U] a := (supᵢ₂_le_supᵢ _ _)
    _ ≤ ⨆ i, 𝓝[f i] a := supᵢ_mono fun i => nhdsWithin_mono _ <| inter_subset_left _ _

#align locally_finite.nhds_within_Union LocallyFinite.nhdsWithin_unionᵢ

/- warning: locally_finite.continuous_on_Union' -> LocallyFinite.continuousOn_unionᵢ' is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} {Y : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u3} Y] {f : ι -> (Set.{u2} X)} {g : X -> Y}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall (i : ι) (x : X), (Membership.Mem.{u2, u2} X (Set.{u2} X) (Set.hasMem.{u2} X) x (closure.{u2} X _inst_1 (f i))) -> (ContinuousWithinAt.{u2, u3} X Y _inst_1 _inst_2 g (f i) x)) -> (ContinuousOn.{u2, u3} X Y _inst_1 _inst_2 g (Set.unionᵢ.{u2, succ u1} X ι (fun (i : ι) => f i)))
but is expected to have type
  forall {ι : Type.{u3}} {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] {f : ι -> (Set.{u2} X)} {g : X -> Y}, (LocallyFinite.{u3, u2} ι X _inst_1 f) -> (forall (i : ι) (x : X), (Membership.mem.{u2, u2} X (Set.{u2} X) (Set.instMembershipSet.{u2} X) x (closure.{u2} X _inst_1 (f i))) -> (ContinuousWithinAt.{u2, u1} X Y _inst_1 _inst_2 g (f i) x)) -> (ContinuousOn.{u2, u1} X Y _inst_1 _inst_2 g (Set.unionᵢ.{u2, succ u3} X ι (fun (i : ι) => f i)))
Case conversion may be inaccurate. Consider using '#align locally_finite.continuous_on_Union' LocallyFinite.continuousOn_unionᵢ'ₓ'. -/
theorem continuousOn_unionᵢ' {g : X → Y} (hf : LocallyFinite f)
    (hc : ∀ i x, x ∈ closure (f i) → ContinuousWithinAt g (f i) x) : ContinuousOn g (⋃ i, f i) :=
  by
  rintro x -
  rw [ContinuousWithinAt, hf.nhds_within_Union, tendsto_supr]
  intro i
  by_cases hx : x ∈ closure (f i)
  · exact hc i _ hx
  · rw [mem_closure_iff_nhdsWithin_neBot, not_ne_bot] at hx
    rw [hx]
    exact tendsto_bot
#align locally_finite.continuous_on_Union' LocallyFinite.continuousOn_unionᵢ'

/- warning: locally_finite.continuous_on_Union -> LocallyFinite.continuousOn_unionᵢ is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} {Y : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u3} Y] {f : ι -> (Set.{u2} X)} {g : X -> Y}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall (i : ι), IsClosed.{u2} X _inst_1 (f i)) -> (forall (i : ι), ContinuousOn.{u2, u3} X Y _inst_1 _inst_2 g (f i)) -> (ContinuousOn.{u2, u3} X Y _inst_1 _inst_2 g (Set.unionᵢ.{u2, succ u1} X ι (fun (i : ι) => f i)))
but is expected to have type
  forall {ι : Type.{u3}} {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] {f : ι -> (Set.{u2} X)} {g : X -> Y}, (LocallyFinite.{u3, u2} ι X _inst_1 f) -> (forall (i : ι), IsClosed.{u2} X _inst_1 (f i)) -> (forall (i : ι), ContinuousOn.{u2, u1} X Y _inst_1 _inst_2 g (f i)) -> (ContinuousOn.{u2, u1} X Y _inst_1 _inst_2 g (Set.unionᵢ.{u2, succ u3} X ι (fun (i : ι) => f i)))
Case conversion may be inaccurate. Consider using '#align locally_finite.continuous_on_Union LocallyFinite.continuousOn_unionᵢₓ'. -/
theorem continuousOn_unionᵢ {g : X → Y} (hf : LocallyFinite f) (h_cl : ∀ i, IsClosed (f i))
    (h_cont : ∀ i, ContinuousOn g (f i)) : ContinuousOn g (⋃ i, f i) :=
  hf.continuousOn_unionᵢ' fun i x hx => h_cont i x <| (h_cl i).closure_subset hx
#align locally_finite.continuous_on_Union LocallyFinite.continuousOn_unionᵢ

/- warning: locally_finite.continuous' -> LocallyFinite.continuous' is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} {Y : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u3} Y] {f : ι -> (Set.{u2} X)} {g : X -> Y}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (Eq.{succ u2} (Set.{u2} X) (Set.unionᵢ.{u2, succ u1} X ι (fun (i : ι) => f i)) (Set.univ.{u2} X)) -> (forall (i : ι) (x : X), (Membership.Mem.{u2, u2} X (Set.{u2} X) (Set.hasMem.{u2} X) x (closure.{u2} X _inst_1 (f i))) -> (ContinuousWithinAt.{u2, u3} X Y _inst_1 _inst_2 g (f i) x)) -> (Continuous.{u2, u3} X Y _inst_1 _inst_2 g)
but is expected to have type
  forall {ι : Type.{u3}} {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] {f : ι -> (Set.{u2} X)} {g : X -> Y}, (LocallyFinite.{u3, u2} ι X _inst_1 f) -> (Eq.{succ u2} (Set.{u2} X) (Set.unionᵢ.{u2, succ u3} X ι (fun (i : ι) => f i)) (Set.univ.{u2} X)) -> (forall (i : ι) (x : X), (Membership.mem.{u2, u2} X (Set.{u2} X) (Set.instMembershipSet.{u2} X) x (closure.{u2} X _inst_1 (f i))) -> (ContinuousWithinAt.{u2, u1} X Y _inst_1 _inst_2 g (f i) x)) -> (Continuous.{u2, u1} X Y _inst_1 _inst_2 g)
Case conversion may be inaccurate. Consider using '#align locally_finite.continuous' LocallyFinite.continuous'ₓ'. -/
protected theorem continuous' {g : X → Y} (hf : LocallyFinite f) (h_cov : (⋃ i, f i) = univ)
    (hc : ∀ i x, x ∈ closure (f i) → ContinuousWithinAt g (f i) x) : Continuous g :=
  continuous_iff_continuousOn_univ.2 <| h_cov ▸ hf.continuousOn_unionᵢ' hc
#align locally_finite.continuous' LocallyFinite.continuous'

/- warning: locally_finite.continuous -> LocallyFinite.continuous is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} {Y : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u3} Y] {f : ι -> (Set.{u2} X)} {g : X -> Y}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (Eq.{succ u2} (Set.{u2} X) (Set.unionᵢ.{u2, succ u1} X ι (fun (i : ι) => f i)) (Set.univ.{u2} X)) -> (forall (i : ι), IsClosed.{u2} X _inst_1 (f i)) -> (forall (i : ι), ContinuousOn.{u2, u3} X Y _inst_1 _inst_2 g (f i)) -> (Continuous.{u2, u3} X Y _inst_1 _inst_2 g)
but is expected to have type
  forall {ι : Type.{u3}} {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] {f : ι -> (Set.{u2} X)} {g : X -> Y}, (LocallyFinite.{u3, u2} ι X _inst_1 f) -> (Eq.{succ u2} (Set.{u2} X) (Set.unionᵢ.{u2, succ u3} X ι (fun (i : ι) => f i)) (Set.univ.{u2} X)) -> (forall (i : ι), IsClosed.{u2} X _inst_1 (f i)) -> (forall (i : ι), ContinuousOn.{u2, u1} X Y _inst_1 _inst_2 g (f i)) -> (Continuous.{u2, u1} X Y _inst_1 _inst_2 g)
Case conversion may be inaccurate. Consider using '#align locally_finite.continuous LocallyFinite.continuousₓ'. -/
protected theorem continuous {g : X → Y} (hf : LocallyFinite f) (h_cov : (⋃ i, f i) = univ)
    (h_cl : ∀ i, IsClosed (f i)) (h_cont : ∀ i, ContinuousOn g (f i)) : Continuous g :=
  continuous_iff_continuousOn_univ.2 <| h_cov ▸ hf.continuousOn_unionᵢ h_cl h_cont
#align locally_finite.continuous LocallyFinite.continuous

/- warning: locally_finite.closure -> LocallyFinite.closure is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (LocallyFinite.{u1, u2} ι X _inst_1 (fun (i : ι) => closure.{u2} X _inst_1 (f i)))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (LocallyFinite.{u2, u1} ι X _inst_1 (fun (i : ι) => closure.{u1} X _inst_1 (f i)))
Case conversion may be inaccurate. Consider using '#align locally_finite.closure LocallyFinite.closureₓ'. -/
protected theorem closure (hf : LocallyFinite f) : LocallyFinite fun i => closure (f i) :=
  by
  intro x
  rcases hf x with ⟨s, hsx, hsf⟩
  refine' ⟨interior s, interior_mem_nhds.2 hsx, hsf.subset fun i hi => _⟩
  exact
    (hi.mono is_open_interior.closure_inter).of_closure.mono
      (inter_subset_inter_right _ interior_subset)
#align locally_finite.closure LocallyFinite.closure

/- warning: locally_finite.closure_Union -> LocallyFinite.closure_unionᵢ is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (Eq.{succ u2} (Set.{u2} X) (closure.{u2} X _inst_1 (Set.unionᵢ.{u2, succ u1} X ι (fun (i : ι) => f i))) (Set.unionᵢ.{u2, succ u1} X ι (fun (i : ι) => closure.{u2} X _inst_1 (f i))))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (Eq.{succ u1} (Set.{u1} X) (closure.{u1} X _inst_1 (Set.unionᵢ.{u1, succ u2} X ι (fun (i : ι) => f i))) (Set.unionᵢ.{u1, succ u2} X ι (fun (i : ι) => closure.{u1} X _inst_1 (f i))))
Case conversion may be inaccurate. Consider using '#align locally_finite.closure_Union LocallyFinite.closure_unionᵢₓ'. -/
theorem closure_unionᵢ (h : LocallyFinite f) : closure (⋃ i, f i) = ⋃ i, closure (f i) :=
  by
  ext x
  simp only [mem_closure_iff_nhdsWithin_neBot, h.nhds_within_Union, supr_ne_bot, mem_Union]
#align locally_finite.closure_Union LocallyFinite.closure_unionᵢ

/- warning: locally_finite.is_closed_Union -> LocallyFinite.isClosed_unionᵢ is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall (i : ι), IsClosed.{u2} X _inst_1 (f i)) -> (IsClosed.{u2} X _inst_1 (Set.unionᵢ.{u2, succ u1} X ι (fun (i : ι) => f i)))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (forall (i : ι), IsClosed.{u1} X _inst_1 (f i)) -> (IsClosed.{u1} X _inst_1 (Set.unionᵢ.{u1, succ u2} X ι (fun (i : ι) => f i)))
Case conversion may be inaccurate. Consider using '#align locally_finite.is_closed_Union LocallyFinite.isClosed_unionᵢₓ'. -/
theorem isClosed_unionᵢ (hf : LocallyFinite f) (hc : ∀ i, IsClosed (f i)) : IsClosed (⋃ i, f i) :=
  by simp only [← closure_eq_iff_isClosed, hf.closure_Union, (hc _).closure_eq]
#align locally_finite.is_closed_Union LocallyFinite.isClosed_unionᵢ

/- warning: locally_finite.Inter_compl_mem_nhds -> LocallyFinite.interᵢ_compl_mem_nhds is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall (i : ι), IsClosed.{u2} X _inst_1 (f i)) -> (forall (x : X), Membership.Mem.{u2, u2} (Set.{u2} X) (Filter.{u2} X) (Filter.hasMem.{u2} X) (Set.interᵢ.{u2, succ u1} X ι (fun (i : ι) => Set.interᵢ.{u2, 0} X (Not (Membership.Mem.{u2, u2} X (Set.{u2} X) (Set.hasMem.{u2} X) x (f i))) (fun (hi : Not (Membership.Mem.{u2, u2} X (Set.{u2} X) (Set.hasMem.{u2} X) x (f i))) => HasCompl.compl.{u2} (Set.{u2} X) (BooleanAlgebra.toHasCompl.{u2} (Set.{u2} X) (Set.booleanAlgebra.{u2} X)) (f i)))) (nhds.{u2} X _inst_1 x))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (forall (i : ι), IsClosed.{u1} X _inst_1 (f i)) -> (forall (x : X), Membership.mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (instMembershipSetFilter.{u1} X) (Set.interᵢ.{u1, succ u2} X ι (fun (i : ι) => Set.interᵢ.{u1, 0} X (Not (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) x (f i))) (fun (hi : Not (Membership.mem.{u1, u1} X (Set.{u1} X) (Set.instMembershipSet.{u1} X) x (f i))) => HasCompl.compl.{u1} (Set.{u1} X) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} X) (Set.instBooleanAlgebraSet.{u1} X)) (f i)))) (nhds.{u1} X _inst_1 x))
Case conversion may be inaccurate. Consider using '#align locally_finite.Inter_compl_mem_nhds LocallyFinite.interᵢ_compl_mem_nhdsₓ'. -/
/-- If `f : β → set α` is a locally finite family of closed sets, then for any `x : α`, the
intersection of the complements to `f i`, `x ∉ f i`, is a neighbourhood of `x`. -/
theorem interᵢ_compl_mem_nhds (hf : LocallyFinite f) (hc : ∀ i, IsClosed (f i)) (x : X) :
    (⋂ (i) (hi : x ∉ f i), f iᶜ) ∈ 𝓝 x :=
  by
  refine' IsOpen.mem_nhds _ (mem_Inter₂.2 fun i => id)
  suffices IsClosed (⋃ i : { i // x ∉ f i }, f i) by
    rwa [← isOpen_compl_iff, compl_Union, Inter_subtype] at this
  exact (hf.comp_injective Subtype.coe_injective).isClosed_unionᵢ fun i => hc _
#align locally_finite.Inter_compl_mem_nhds LocallyFinite.interᵢ_compl_mem_nhds

#print LocallyFinite.exists_forall_eventually_eq_prod /-
/-- Let `f : ℕ → Π a, β a` be a sequence of (dependent) functions on a topological space. Suppose
that the family of sets `s n = {x | f (n + 1) x ≠ f n x}` is locally finite. Then there exists a
function `F : Π a, β a` such that for any `x`, we have `f n x = F x` on the product of an infinite
interval `[N, +∞)` and a neighbourhood of `x`.

We formulate the conclusion in terms of the product of filter `filter.at_top` and `𝓝 x`. -/
theorem exists_forall_eventually_eq_prod {π : X → Sort _} {f : ℕ → ∀ x : X, π x}
    (hf : LocallyFinite fun n => { x | f (n + 1) x ≠ f n x }) :
    ∃ F : ∀ x : X, π x, ∀ x, ∀ᶠ p : ℕ × X in atTop ×ᶠ 𝓝 x, f p.1 p.2 = F p.2 :=
  by
  choose U hUx hU using hf
  choose N hN using fun x => (hU x).BddAbove
  replace hN : ∀ (x), ∀ n > N x, ∀ y ∈ U x, f (n + 1) y = f n y
  exact fun x n hn y hy => by_contra fun hne => hn.lt.not_le <| hN x ⟨y, hne, hy⟩
  replace hN : ∀ (x), ∀ n ≥ N x + 1, ∀ y ∈ U x, f n y = f (N x + 1) y
  exact fun x n hn y hy => Nat.le_induction rfl (fun k hle => (hN x _ hle _ hy).trans) n hn
  refine' ⟨fun x => f (N x + 1) x, fun x => _⟩
  filter_upwards [Filter.prod_mem_prod (eventually_gt_at_top (N x)) (hUx x)]
  rintro ⟨n, y⟩ ⟨hn : N x < n, hy : y ∈ U x⟩
  calc
    f n y = f (N x + 1) y := hN _ _ hn _ hy
    _ = f (max (N x + 1) (N y + 1)) y := (hN _ _ (le_max_left _ _) _ hy).symm
    _ = f (N y + 1) y := hN _ _ (le_max_right _ _) _ (mem_of_mem_nhds <| hUx y)

#align locally_finite.exists_forall_eventually_eq_prod LocallyFinite.exists_forall_eventually_eq_prod
-/

#print LocallyFinite.exists_forall_eventually_atTop_eventually_eq' /-
/-- Let `f : ℕ → Π a, β a` be a sequence of (dependent) functions on a topological space. Suppose
that the family of sets `s n = {x | f (n + 1) x ≠ f n x}` is locally finite. Then there exists a
function `F : Π a, β a` such that for any `x`, for sufficiently large values of `n`, we have
`f n y = F y` in a neighbourhood of `x`. -/
theorem exists_forall_eventually_atTop_eventually_eq' {π : X → Sort _} {f : ℕ → ∀ x : X, π x}
    (hf : LocallyFinite fun n => { x | f (n + 1) x ≠ f n x }) :
    ∃ F : ∀ x : X, π x, ∀ x, ∀ᶠ n : ℕ in atTop, ∀ᶠ y : X in 𝓝 x, f n y = F y :=
  hf.exists_forall_eventually_eq_prod.imp fun F hF x => (hF x).curry
#align locally_finite.exists_forall_eventually_at_top_eventually_eq' LocallyFinite.exists_forall_eventually_atTop_eventually_eq'
-/

#print LocallyFinite.exists_forall_eventually_atTop_eventuallyEq /-
/-- Let `f : ℕ → α → β` be a sequence of functions on a topological space. Suppose
that the family of sets `s n = {x | f (n + 1) x ≠ f n x}` is locally finite. Then there exists a
function `F :  α → β` such that for any `x`, for sufficiently large values of `n`, we have
`f n =ᶠ[𝓝 x] F`. -/
theorem exists_forall_eventually_atTop_eventuallyEq {f : ℕ → X → α}
    (hf : LocallyFinite fun n => { x | f (n + 1) x ≠ f n x }) :
    ∃ F : X → α, ∀ x, ∀ᶠ n : ℕ in atTop, f n =ᶠ[𝓝 x] F :=
  hf.exists_forall_eventually_atTop_eventually_eq'
#align locally_finite.exists_forall_eventually_at_top_eventually_eq LocallyFinite.exists_forall_eventually_atTop_eventuallyEq
-/

/- warning: locally_finite.preimage_continuous -> LocallyFinite.preimage_continuous is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} {Y : Type.{u3}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u3} Y] {f : ι -> (Set.{u2} X)} {g : Y -> X}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (Continuous.{u3, u2} Y X _inst_2 _inst_1 g) -> (LocallyFinite.{u1, u3} ι Y _inst_2 (fun (i : ι) => Set.preimage.{u3, u2} Y X g (f i)))
but is expected to have type
  forall {ι : Type.{u3}} {X : Type.{u2}} {Y : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : TopologicalSpace.{u1} Y] {f : ι -> (Set.{u2} X)} {g : Y -> X}, (LocallyFinite.{u3, u2} ι X _inst_1 f) -> (Continuous.{u1, u2} Y X _inst_2 _inst_1 g) -> (LocallyFinite.{u3, u1} ι Y _inst_2 (fun (i : ι) => Set.preimage.{u1, u2} Y X g (f i)))
Case conversion may be inaccurate. Consider using '#align locally_finite.preimage_continuous LocallyFinite.preimage_continuousₓ'. -/
theorem preimage_continuous {g : Y → X} (hf : LocallyFinite f) (hg : Continuous g) :
    LocallyFinite fun i => g ⁻¹' f i := fun x =>
  let ⟨s, hsx, hs⟩ := hf (g x)
  ⟨g ⁻¹' s, hg.ContinuousAt hsx, hs.Subset fun i ⟨y, hy⟩ => ⟨g y, hy⟩⟩
#align locally_finite.preimage_continuous LocallyFinite.preimage_continuous

end LocallyFinite

/- warning: equiv.locally_finite_comp_iff -> Equiv.locallyFinite_comp_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {ι' : Type.{u2}} {X : Type.{u3}} [_inst_1 : TopologicalSpace.{u3} X] {f : ι -> (Set.{u3} X)} (e : Equiv.{succ u2, succ u1} ι' ι), Iff (LocallyFinite.{u2, u3} ι' X _inst_1 (Function.comp.{succ u2, succ u1, succ u3} ι' ι (Set.{u3} X) f (coeFn.{max 1 (max (succ u2) (succ u1)) (succ u1) (succ u2), max (succ u2) (succ u1)} (Equiv.{succ u2, succ u1} ι' ι) (fun (_x : Equiv.{succ u2, succ u1} ι' ι) => ι' -> ι) (Equiv.hasCoeToFun.{succ u2, succ u1} ι' ι) e))) (LocallyFinite.{u1, u3} ι X _inst_1 f)
but is expected to have type
  forall {ι : Type.{u2}} {ι' : Type.{u3}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)} (e : Equiv.{succ u3, succ u2} ι' ι), Iff (LocallyFinite.{u3, u1} ι' X _inst_1 (Function.comp.{succ u3, succ u2, succ u1} ι' ι (Set.{u1} X) f (FunLike.coe.{max (succ u2) (succ u3), succ u3, succ u2} (Equiv.{succ u3, succ u2} ι' ι) ι' (fun (_x : ι') => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : ι') => ι) _x) (Equiv.instFunLikeEquiv.{succ u3, succ u2} ι' ι) e))) (LocallyFinite.{u2, u1} ι X _inst_1 f)
Case conversion may be inaccurate. Consider using '#align equiv.locally_finite_comp_iff Equiv.locallyFinite_comp_iffₓ'. -/
@[simp]
theorem Equiv.locallyFinite_comp_iff (e : ι' ≃ ι) : LocallyFinite (f ∘ e) ↔ LocallyFinite f :=
  ⟨fun h => by simpa only [(· ∘ ·), e.apply_symm_apply] using h.comp_injective e.symm.injective,
    fun h => h.comp_injective e.Injective⟩
#align equiv.locally_finite_comp_iff Equiv.locallyFinite_comp_iff

/- warning: locally_finite_sum -> locallyFinite_sum is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {ι' : Type.{u2}} {X : Type.{u3}} [_inst_1 : TopologicalSpace.{u3} X] {f : (Sum.{u1, u2} ι ι') -> (Set.{u3} X)}, Iff (LocallyFinite.{max u1 u2, u3} (Sum.{u1, u2} ι ι') X _inst_1 f) (And (LocallyFinite.{u1, u3} ι X _inst_1 (Function.comp.{succ u1, max (succ u1) (succ u2), succ u3} ι (Sum.{u1, u2} ι ι') (Set.{u3} X) f (Sum.inl.{u1, u2} ι ι'))) (LocallyFinite.{u2, u3} ι' X _inst_1 (Function.comp.{succ u2, max (succ u1) (succ u2), succ u3} ι' (Sum.{u1, u2} ι ι') (Set.{u3} X) f (Sum.inr.{u1, u2} ι ι'))))
but is expected to have type
  forall {ι : Type.{u3}} {ι' : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : (Sum.{u3, u2} ι ι') -> (Set.{u1} X)}, Iff (LocallyFinite.{max u3 u2, u1} (Sum.{u3, u2} ι ι') X _inst_1 f) (And (LocallyFinite.{u3, u1} ι X _inst_1 (Function.comp.{succ u3, max (succ u3) (succ u2), succ u1} ι (Sum.{u3, u2} ι ι') (Set.{u1} X) f (Sum.inl.{u3, u2} ι ι'))) (LocallyFinite.{u2, u1} ι' X _inst_1 (Function.comp.{succ u2, max (succ u3) (succ u2), succ u1} ι' (Sum.{u3, u2} ι ι') (Set.{u1} X) f (Sum.inr.{u3, u2} ι ι'))))
Case conversion may be inaccurate. Consider using '#align locally_finite_sum locallyFinite_sumₓ'. -/
theorem locallyFinite_sum {f : Sum ι ι' → Set X} :
    LocallyFinite f ↔ LocallyFinite (f ∘ Sum.inl) ∧ LocallyFinite (f ∘ Sum.inr) := by
  simp only [locallyFinite_iff_smallSets, ← forall_and, ← finite_preimage_inl_and_inr,
    preimage_set_of_eq, (· ∘ ·), eventually_and]
#align locally_finite_sum locallyFinite_sum

/- warning: locally_finite.sum_elim -> LocallyFinite.sum_elim is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {ι' : Type.{u2}} {X : Type.{u3}} [_inst_1 : TopologicalSpace.{u3} X] {f : ι -> (Set.{u3} X)} {g : ι' -> (Set.{u3} X)}, (LocallyFinite.{u1, u3} ι X _inst_1 f) -> (LocallyFinite.{u2, u3} ι' X _inst_1 g) -> (LocallyFinite.{max u1 u2, u3} (Sum.{u1, u2} ι ι') X _inst_1 (Sum.elim.{u1, u2, succ u3} ι ι' (Set.{u3} X) f g))
but is expected to have type
  forall {ι : Type.{u2}} {ι' : Type.{u1}} {X : Type.{u3}} [_inst_1 : TopologicalSpace.{u3} X] {f : ι -> (Set.{u3} X)} {g : ι' -> (Set.{u3} X)}, (LocallyFinite.{u2, u3} ι X _inst_1 f) -> (LocallyFinite.{u1, u3} ι' X _inst_1 g) -> (LocallyFinite.{max u1 u2, u3} (Sum.{u2, u1} ι ι') X _inst_1 (Sum.elim.{u2, u1, succ u3} ι ι' (Set.{u3} X) f g))
Case conversion may be inaccurate. Consider using '#align locally_finite.sum_elim LocallyFinite.sum_elimₓ'. -/
theorem LocallyFinite.sum_elim {g : ι' → Set X} (hf : LocallyFinite f) (hg : LocallyFinite g) :
    LocallyFinite (Sum.elim f g) :=
  locallyFinite_sum.mpr ⟨hf, hg⟩
#align locally_finite.sum_elim LocallyFinite.sum_elim

/- warning: locally_finite_option -> locallyFinite_option is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : (Option.{u1} ι) -> (Set.{u2} X)}, Iff (LocallyFinite.{u1, u2} (Option.{u1} ι) X _inst_1 f) (LocallyFinite.{u1, u2} ι X _inst_1 (Function.comp.{succ u1, succ u1, succ u2} ι (Option.{u1} ι) (Set.{u2} X) f (Option.some.{u1} ι)))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : (Option.{u2} ι) -> (Set.{u1} X)}, Iff (LocallyFinite.{u2, u1} (Option.{u2} ι) X _inst_1 f) (LocallyFinite.{u2, u1} ι X _inst_1 (Function.comp.{succ u2, succ u2, succ u1} ι (Option.{u2} ι) (Set.{u1} X) f (Option.some.{u2} ι)))
Case conversion may be inaccurate. Consider using '#align locally_finite_option locallyFinite_optionₓ'. -/
theorem locallyFinite_option {f : Option ι → Set X} : LocallyFinite f ↔ LocallyFinite (f ∘ some) :=
  by
  simp only [← (Equiv.optionEquivSumPUnit.{u} ι).symm.locallyFinite_comp_iff, locallyFinite_sum,
    locallyFinite_of_finite, and_true_iff]
  rfl
#align locally_finite_option locallyFinite_option

/- warning: locally_finite.option_elim -> LocallyFinite.option_elim' is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {X : Type.{u2}} [_inst_1 : TopologicalSpace.{u2} X] {f : ι -> (Set.{u2} X)}, (LocallyFinite.{u1, u2} ι X _inst_1 f) -> (forall (s : Set.{u2} X), LocallyFinite.{u1, u2} (Option.{u1} ι) X _inst_1 (Option.elim'.{u1, u2} ι (Set.{u2} X) s f))
but is expected to have type
  forall {ι : Type.{u2}} {X : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} X] {f : ι -> (Set.{u1} X)}, (LocallyFinite.{u2, u1} ι X _inst_1 f) -> (forall (s : Set.{u1} X), LocallyFinite.{u2, u1} (Option.{u2} ι) X _inst_1 (Option.elim'.{u2, u1} ι (Set.{u1} X) s f))
Case conversion may be inaccurate. Consider using '#align locally_finite.option_elim LocallyFinite.option_elim'ₓ'. -/
theorem LocallyFinite.option_elim' (hf : LocallyFinite f) (s : Set X) :
    LocallyFinite (Option.elim' s f) :=
  locallyFinite_option.2 hf
#align locally_finite.option_elim LocallyFinite.option_elim'


-- Pruebas de ∃x∃yP(x,y) ↔ ∃y∃xP(x,y)
-- ==================================

import tactic

section

variable {U : Type}
variable {P : U -> U -> Prop}

-- ----------------------------------------------------
-- Ej. 1. Demostrar que
--    ∃x∃yP(x,y) → ∃y∃xP(x,y)
-- ----------------------------------------------------

-- 1ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
assume h1 : ∃ x, ∃ y, P x y,
exists.elim h1
  ( assume a (h2 : ∃ y, P a y),
    exists.elim h2
      ( assume b (h3 : P a b),
        have h4 : ∃ x, P x b, from exists.intro a h3,
        show ∃ y, ∃ x, P x y,  from exists.intro b h4))

-- 2ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
assume ⟨a, b, (h1 : P a b)⟩,
have h2 : ∃ x, P x b,  from ⟨a, h1⟩,
show (∃ y, ∃ x, P x y), from ⟨b, h2⟩

-- 3ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
assume ⟨a, b, (h1 : P a b)⟩,
show (∃ y, ∃ x, P x y), from ⟨b, ⟨a, h1⟩⟩

-- 4ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
assume ⟨a, b, (h1 : P a b)⟩,
show (∃ y, ∃ x, P x y), from ⟨b, a, h1⟩

-- 5ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
assume ⟨a, b, (h1 : P a b)⟩,
⟨b, a, h1⟩

-- 6ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
λ ⟨a, b, h1⟩, ⟨b, a, h1⟩

-- 7ª demostración
example :
  (∃ x y, P x y) → (∃ y x, P x y) :=
-- by library_search
exists_comm.mp

-- 8ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
begin
  intro h1,
  cases h1 with a h2,
  cases h2 with b h3,
  use b,
  use a,
  exact h3,
end

-- 9ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
begin
  intro h1,
  cases h1 with a h2,
  cases h2 with b h3,
  use [b, a],
  exact h3,
end

-- 10ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
begin
  intro h1,
  rcases h1 with ⟨a, b, h2⟩,
  use [b, a],
  exact h2,
end

-- 11ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
begin
  intro h1,
  rcases h1 with ⟨a, b, h2⟩,
  exact ⟨b, a, h2⟩,
end

-- 12ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
begin
  rintro ⟨a, b, h2⟩,
  exact ⟨b, a, h2⟩,
end

-- 13ª demostración
example :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
-- by hint
by tauto

-- 14ª demostración
lemma aux :
  (∃ x, ∃ y, P x y) → (∃ y, ∃ x, P x y) :=
by finish

-- ----------------------------------------------------
-- Ej. 2. Demostrar que
--    ∃x∃yP(x,y) ↔ ∃y∃xP(x,y)
-- ----------------------------------------------------

-- 1ª demostración
example :
  (∃ x y, P x y) ↔ (∃ y x, P x y) :=
iff.intro aux aux

-- 2ª demostración
example :
  (∃ x y, P x y) ↔ (∃ y x, P x y) :=
⟨aux, aux⟩

-- 3ª demostración
example :
  (∃ x y, P x y) ↔ (∃ y x, P x y) :=
-- by library_search
exists_comm

-- 4ª demostración
example :
  (∃ x y, P x y) ↔ (∃ y x, P x y) :=
-- by hint
by tauto

end