/-
Copyright (c) 2020 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Mario Carneiro
-/
import tactic.by_contra
import data.set.image

/-!
# Well-founded relations

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

A relation is well-founded if it can be used for induction: for each `x`, `(∀ y, r y x → P y) → P x`
implies `P x`. Well-founded relations can be used for induction and recursion, including
construction of fixed points in the space of dependent functions `Π x : α , β x`.

The predicate `well_founded` is defined in the core library. In this file we prove some extra lemmas
and provide a few new definitions: `well_founded.min`, `well_founded.sup`, and `well_founded.succ`,
and an induction principle `well_founded.induction_bot`.
-/

variables {α : Type*}

namespace well_founded

protected theorem is_asymm {α : Sort*} {r : α → α → Prop} (h : well_founded r) : is_asymm α r :=
⟨h.asymmetric⟩

instance {α : Sort*} [has_well_founded α] : is_asymm α has_well_founded.r :=
has_well_founded.wf.is_asymm

protected theorem is_irrefl {α : Sort*} {r : α → α → Prop} (h : well_founded r) : is_irrefl α r :=
(@is_asymm.is_irrefl α r h.is_asymm)

instance {α : Sort*} [has_well_founded α] : is_irrefl α has_well_founded.r :=
is_asymm.is_irrefl

/-- If `r` is a well-founded relation, then any nonempty set has a minimal element
with respect to `r`. -/
theorem has_min {α} {r : α → α → Prop} (H : well_founded r)
  (s : set α) : s.nonempty → ∃ a ∈ s, ∀ x ∈ s, ¬ r x a
| ⟨a, ha⟩ := (acc.rec_on (H.apply a) $ λ x _ IH, not_imp_not.1 $ λ hne hx, hne $
  ⟨x, hx, λ y hy hyx, hne $ IH y hyx hy⟩) ha

/-- A minimal element of a nonempty set in a well-founded order.

If you're working with a nonempty linear order, consider defining a
`conditionally_complete_linear_order_bot` instance via
`well_founded.conditionally_complete_linear_order_with_bot` and using `Inf` instead. -/
noncomputable def min {r : α → α → Prop} (H : well_founded r)
  (s : set α) (h : s.nonempty) : α :=
classical.some (H.has_min s h)

theorem min_mem {r : α → α → Prop} (H : well_founded r)
  (s : set α) (h : s.nonempty) : H.min s h ∈ s :=
let ⟨h, _⟩ := classical.some_spec (H.has_min s h) in h

theorem not_lt_min {r : α → α → Prop} (H : well_founded r)
  (s : set α) (h : s.nonempty) {x} (hx : x ∈ s) : ¬ r x (H.min s h) :=
let ⟨_, h'⟩ := classical.some_spec (H.has_min s h) in h' _ hx

theorem well_founded_iff_has_min {r : α → α → Prop} : (well_founded r) ↔
  ∀ (s : set α), s.nonempty → ∃ m ∈ s, ∀ x ∈ s, ¬ r x m :=
begin
  refine ⟨λ h, h.has_min, λ h, ⟨λ x, _⟩⟩,
  by_contra hx,
  obtain ⟨m, hm, hm'⟩ := h _ ⟨x, hx⟩,
  refine hm ⟨_, λ y hy, _⟩,
  by_contra hy',
  exact hm' y hy' hy
end

open set
/-- The supremum of a bounded, well-founded order -/
protected noncomputable def sup {r : α → α → Prop} (wf : well_founded r) (s : set α)
  (h : bounded r s) : α :=
wf.min { x | ∀a ∈ s, r a x } h

protected lemma lt_sup {r : α → α → Prop} (wf : well_founded r) {s : set α} (h : bounded r s)
  {x} (hx : x ∈ s) : r x (wf.sup s h) :=
min_mem wf { x | ∀a ∈ s, r a x } h x hx

section
open_locale classical
/-- A successor of an element `x` in a well-founded order is a minimal element `y` such that
`x < y` if one exists. Otherwise it is `x` itself. -/
protected noncomputable def succ {r : α → α → Prop} (wf : well_founded r) (x : α) : α :=
if h : ∃y, r x y then wf.min { y | r x y } h else x

protected lemma lt_succ {r : α → α → Prop} (wf : well_founded r) {x : α} (h : ∃y, r x y) :
  r x (wf.succ x) :=
by { rw [well_founded.succ, dif_pos h], apply min_mem }
end

protected lemma lt_succ_iff {r : α → α → Prop} [wo : is_well_order α r] {x : α} (h : ∃y, r x y)
  (y : α) : r y (wo.wf.succ x) ↔ r y x ∨ y = x :=
begin
  split,
  { intro h', have : ¬r x y,
    { intro hy, rw [well_founded.succ, dif_pos] at h',
      exact wo.wf.not_lt_min _ h hy h' },
    rcases trichotomous_of r x y with hy | hy | hy,
    exfalso, exact this hy,
    right, exact hy.symm,
    left, exact hy },
  rintro (hy | rfl), exact trans hy (wo.wf.lt_succ h), exact wo.wf.lt_succ h
end

section linear_order

variables {β : Type*} [linear_order β] (h : well_founded ((<) : β → β → Prop))
  {γ : Type*} [partial_order γ]

theorem min_le {x : β} {s : set β} (hx : x ∈ s) (hne : s.nonempty := ⟨x, hx⟩) :
  h.min s hne ≤ x :=
not_lt.1 $ h.not_lt_min _ _ hx

private theorem eq_strict_mono_iff_eq_range_aux {f g : β → γ} (hf : strict_mono f)
  (hg : strict_mono g) (hfg : set.range f = set.range g) {b : β} (H : ∀ a < b, f a = g a) :
  f b ≤ g b :=
begin
  obtain ⟨c, hc⟩ : g b ∈ set.range f := by { rw hfg, exact set.mem_range_self b },
  cases lt_or_le c b with hcb hbc,
  { rw [H c hcb] at hc,
    rw hg.injective hc at hcb,
    exact hcb.false.elim },
  { rw ←hc,
    exact hf.monotone hbc }
end

include h
theorem eq_strict_mono_iff_eq_range {f g : β → γ} (hf : strict_mono f)
  (hg : strict_mono g) : set.range f = set.range g ↔ f = g :=
⟨λ hfg, begin
  funext a,
  apply h.induction a,
  exact λ b H, le_antisymm
    (eq_strict_mono_iff_eq_range_aux hf hg hfg H)
    (eq_strict_mono_iff_eq_range_aux hg hf hfg.symm (λ a hab, (H a hab).symm))
end, congr_arg _⟩

theorem self_le_of_strict_mono {f : β → β} (hf : strict_mono f) : ∀ n, n ≤ f n :=
by { by_contra' h₁, have h₂ := h.min_mem _ h₁, exact h.not_lt_min _ h₁ (hf h₂) h₂ }

end linear_order

end well_founded

namespace function

variables {β : Type*} (f : α → β)

section has_lt

variables [has_lt β] (h : well_founded ((<) : β → β → Prop))

/-- Given a function `f : α → β` where `β` carries a well-founded `<`, this is an element of `α`
whose image under `f` is minimal in the sense of `function.not_lt_argmin`. -/
noncomputable def argmin [nonempty α] : α :=
well_founded.min (inv_image.wf f h) set.univ set.univ_nonempty

lemma not_lt_argmin [nonempty α] (a : α) : ¬ f a < f (argmin f h) :=
well_founded.not_lt_min (inv_image.wf f h) _ _ (set.mem_univ a)

/-- Given a function `f : α → β` where `β` carries a well-founded `<`, and a non-empty subset `s`
of `α`, this is an element of `s` whose image under `f` is minimal in the sense of
`function.not_lt_argmin_on`. -/
noncomputable def argmin_on (s : set α) (hs : s.nonempty) : α :=
well_founded.min (inv_image.wf f h) s hs

@[simp] lemma argmin_on_mem (s : set α) (hs : s.nonempty) :
  argmin_on f h s hs ∈ s :=
well_founded.min_mem _ _ _

@[simp] lemma not_lt_argmin_on (s : set α) {a : α} (ha : a ∈ s)
  (hs : s.nonempty := set.nonempty_of_mem ha) :
  ¬ f a < f (argmin_on f h s hs) :=
well_founded.not_lt_min (inv_image.wf f h) s hs ha

end has_lt

section linear_order

variables [linear_order β] (h : well_founded ((<) : β → β → Prop))

@[simp] lemma argmin_le (a : α) [nonempty α] : f (argmin f h) ≤ f a :=
not_lt.mp $ not_lt_argmin f h a

@[simp] lemma argmin_on_le (s : set α) {a : α} (ha : a ∈ s)
  (hs : s.nonempty := set.nonempty_of_mem ha) : f (argmin_on f h s hs) ≤ f a :=
not_lt.mp $ not_lt_argmin_on f h s ha hs

end linear_order

end function

section induction

/-- Let `r` be a relation on `α`, let `f : α → β` be a function, let `C : β → Prop`, and
let `bot : α`. This induction principle shows that `C (f bot)` holds, given that
* some `a` that is accessible by `r` satisfies `C (f a)`, and
* for each `b` such that `f b ≠ f bot` and `C (f b)` holds, there is `c`
  satisfying `r c b` and `C (f c)`. -/
lemma acc.induction_bot' {α β} {r : α → α → Prop} {a bot : α} (ha : acc r a) {C : β → Prop}
  {f : α → β} (ih : ∀ b, f b ≠ f bot → C (f b) → ∃ c, r c b ∧ C (f c)) : C (f a) → C (f bot) :=
@acc.rec_on _ _ (λ x, C (f x) → C (f bot)) _ ha $ λ x ac ih' hC,
  (eq_or_ne (f x) (f bot)).elim (λ h, h ▸ hC)
    (λ h, let ⟨y, hy₁, hy₂⟩ := ih x h hC in ih' y hy₁ hy₂)

/-- Let `r` be a relation on `α`, let `C : α → Prop` and let `bot : α`.
This induction principle shows that `C bot` holds, given that
* some `a` that is accessible by `r` satisfies `C a`, and
* for each `b ≠ bot` such that `C b` holds, there is `c` satisfying `r c b` and `C c`. -/
lemma acc.induction_bot {α} {r : α → α → Prop} {a bot : α} (ha : acc r a)
  {C : α → Prop} (ih : ∀ b, b ≠ bot → C b → ∃ c, r c b ∧ C c) : C a → C bot :=
ha.induction_bot' ih

/-- Let `r` be a well-founded relation on `α`, let `f : α → β` be a function,
let `C : β → Prop`, and  let `bot : α`.
This induction principle shows that `C (f bot)` holds, given that
* some `a` satisfies `C (f a)`, and
* for each `b` such that `f b ≠ f bot` and `C (f b)` holds, there is `c`
  satisfying `r c b` and `C (f c)`. -/
lemma well_founded.induction_bot' {α β} {r : α → α → Prop} (hwf : well_founded r) {a bot : α}
  {C : β → Prop} {f : α → β} (ih : ∀ b, f b ≠ f bot → C (f b) → ∃ c, r c b ∧ C (f c)) :
  C (f a) → C (f bot) :=
(hwf.apply a).induction_bot' ih

/-- Let `r` be a well-founded relation on `α`, let `C : α → Prop`, and let `bot : α`.
This induction principle shows that `C bot` holds, given that
* some `a` satisfies `C a`, and
* for each `b` that satisfies `C b`, there is `c` satisfying `r c b` and `C c`.

The naming is inspired by the fact that when `r` is transitive, it follows that `bot` is
the smallest element w.r.t. `r` that satisfies `C`. -/
lemma well_founded.induction_bot {α} {r : α → α → Prop} (hwf : well_founded r) {a bot : α}
  {C : α → Prop} (ih : ∀ b, b ≠ bot → C b → ∃ c, r c b ∧ C c) : C a → C bot :=
hwf.induction_bot' ih

end induction


variables (α : Type) (p q : α → Prop)
variable r : Prop

---- Exercise 1
example : (∀ x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x) :=
iff.intro (
  assume hx : ∀ x, p x ∧ q x,
  have hp: ∀ x, p x, from (assume x : α, (hx x).left),
  have hq: ∀ x, q x, from (assume x: α, (hx x).right),
  ⟨hp, hq⟩
) (
  assume hpq : (∀ x, p x) ∧ (∀ x, q x),
  assume x : α,
  ⟨hpq.left x, hpq.right x⟩
)
example : (∀ x, p x → q x) → (∀ x, p x) → (∀ x, q x) :=
  assume hpq: ∀ x, p x → q x,
  assume hp: ∀ x, p x,
  assume x: α,
  have p x → q x, from hpq x,
  this (hp x)
example : (∀ x, p x) ∨ (∀ x, q x) → ∀ x, p x ∨ q x :=
  assume hpq : (∀ x, p x) ∨ (∀ x, q x),
  assume x : α,
  show p x ∨ q x, from (
    hpq.elim
      (assume hp: ∀ x, p x, or.inl (hp x))
      (assume hq: ∀ x, q x, or.inr (hq x))
  )

--- Exercise 2
example : α → ((∀ x : α, r) ↔ r) :=
  assume x: α,
  show (∀ x: α, r) ↔ r, from iff.intro (
    assume : ∀ x : α, r,
    this x
  ) (
    assume : r,
    assume x: α, this
  )
example : (∀ x, p x ∨ r) ↔ (∀ x, p x) ∨ r :=
iff.intro (
  assume pxr : ∀ x, p x ∨ r,
  classical.by_cases (
    assume : r, or.inr this
  ) (
    assume nr : ¬r,
    have ∀ x, p x, from (
      assume x : α,
      or.elim (pxr x)
        (assume : p x, this)
        (assume hr: r, absurd hr nr)
    ),
    or.inl this
  )
) (
  assume : (∀ x, p x) ∨ r,
  this.elim (
    assume : ∀ x, p x,
    assume x: α, or.inl (this x)
  ) (
    assume : r,
    assume x, or.inr this
  )
)
example : (∀ x, r → p x) ↔ (r → ∀ x, p x) :=
iff.intro (
  assume hx : ∀ x, r → p x,
  assume hr : r,
  assume x, (hx x) hr
) (
  assume hr : r → ∀ x, p x,
  assume x, assume r,
  (hr r) x
)

--- Exercise 3
variables (men : Type) (barber : men)
variable  (shaves : men → men → Prop)

lemma contra_equiv (p : Prop) : ¬(p ↔ ¬p) :=
  assume pnp : (p ↔ ¬p),
  have np: ¬p, from (
    assume hp : p,
    have np: ¬p, from pnp.mp hp,
    np hp
  ),
  np (pnp.mpr np)
example (h : ∀ x : men, shaves barber x ↔ ¬ shaves x x) : false :=
  have shaves barber barber ↔ ¬shaves barber barber, from h barber,
  (contra_equiv (shaves barber barber)) this

--- Exercise 4
namespace hidden
  def divides (m n : ℕ) : Prop := ∃ k, m * k = n
  def even (n : ℕ): Prop := ∃ k, 2 * k = n
  def prime (n : ℕ) : Prop :=
    n ≥ 2 ∧ (∀ m : ℕ, (divides m n → (m = 1 ∨ m = n)))
  def infinitely_many_primes : Prop :=
    ∀ n : ℕ, ∃ m: ℕ, m > n ∧ prime m
  def Fermat_prime (n : ℕ) : Prop :=
    ∃ m : ℕ, 2^(2^m) = n ∧ prime n
  def infinitely_many_Fermat_primes : Prop :=
    ∀ n: ℕ, ∃ m: ℕ, m > n ∧ Fermat_prime n
  def goldbach_conjecture : Prop :=
    ∀ n: ℕ, (n > 2 ∧ even n) → (∃ a b, prime a ∧ prime b ∧ n = a + b)
  def Goldbach's_weak_conjecture : Prop :=
    ∀ n: ℕ,
      (n > 5 ∧ ¬even n) →
      (∃ a b c, prime a ∧ prime b ∧ prime c ∧ n = a + b + c)
  def Fermat's_last_theorem : Prop :=
    ∀ n : ℕ, n > 2 → (∀ a b c : ℕ, a^n + b^n ≠ c^n)
end hidden

--- Exercise 5
variables a : α
example : (∃ x : α, r) → r :=
  assume ⟨x, r⟩, r
example : r → (∃ x : α, r) :=
  assume r,
  ⟨a, r⟩

example : (∃ x, p x ∧ r) ↔ (∃ x, p x) ∧ r :=
iff.intro (
  assume : ∃ x, p x ∧ r,
  exists.elim this (
    assume x: α,
    assume : p x ∧ r,
    and.intro ⟨x, this.left⟩ this.right
  )
) (
  assume h: (∃ x, p x) ∧ r,
  have hr: r, from h.right,
  exists.elim h.left (
    assume x: α,
    assume px : p x,
    ⟨x, and.intro px hr⟩
  )
)
example : (∃ x, p x ∨ q x) ↔ (∃ x, p x) ∨ (∃ x, q x) :=
iff.intro (
  assume ⟨x, (pqx: p x ∨ q x)⟩,
  pqx.elim
    (assume : p x, or.inl ⟨x, this⟩)
    (assume : q x, or.inr ⟨x, this⟩)
) (
  assume : (∃ x, p x) ∨ (∃ x, q x),
  this.elim
    (assume ⟨x, (px: p x)⟩, ⟨x, or.inl px⟩)
    ( assume ⟨x, (qx : q x)⟩, ⟨x, or.inr qx⟩)
)

example : (∀ x, p x) ↔ ¬ (∃ x, ¬ p x) :=
iff.intro (
  assume px : ∀ x, p x,
  assume : ∃ x, ¬ p x,
  show false, from exists.elim this (
    assume x, assume : ¬p x,
    this (px x)
  )
) (
  assume npx : ¬(∃ x, ¬p x),
  assume x,
  classical.by_contradiction (
    assume : ¬p x,
    have ∃ x, ¬p x, from ⟨x, this⟩,
    npx this
  )
)

example : (∃ x, p x) ↔ ¬ (∀ x, ¬ p x) :=
iff.intro (
  assume : ∃ x, p x,
  exists.elim this (
    assume x,
    assume px : p x,
    assume : ∀ x, ¬p x, (this x) px
  )
) (
  assume npx : ¬(∀ x, ¬p x),
  classical.by_contradiction (
    assume Expx : ¬∃ x, p x,
    have ∀ x, ¬p x, from (
      assume x,
      assume : p x,
      Expx ⟨x, this⟩
    ),
    npx this
  )
)
example : (¬ ∃ x, p x) ↔ (∀ x, ¬ p x) :=
iff.intro (
  assume npx : ¬ ∃ x, p x,
  assume x,
  show ¬p x, from (
    assume : p x, npx ⟨x, this⟩
  )
) (
  assume npx : ∀ x, ¬ p x,
  assume : ∃ x, p x,
  show false, from exists.elim this (
    assume x,
     assume : p x, (npx x) this
  )
)
example : (¬ ∀ x, p x) ↔ (∃ x, ¬ p x) :=
iff.intro (
  assume npx: ¬∀ x, p x,
  classical.by_contradiction(
    assume enpx : ¬∃ x, ¬ p x,
    have ∀ x, p x, from (
      assume x, classical.by_contradiction(
        assume : ¬p x, enpx ⟨x, this⟩
      )
    ),
    npx this
  )
) (
  assume epx: ∃ x, ¬ p x,
  assume px: ∀ x, p x,
  show false, from exists.elim epx (
    assume x,
    assume npx : ¬p x,
    npx (px x)
  )
)

example : (∀ x, p x → r) ↔ (∃ x, p x) → r :=
iff.intro (
  assume pxr : ∀ x, p x → r,
  assume : ∃ x, p x,
  exists.elim this (
    assume x, pxr x
  )
) (
  assume epx : (∃ x, p x) → r,
  assume x,
  assume px,
  epx ⟨x, px⟩
)
example : (∃ x, p x → r) ↔ (∀ x, p x) → r :=
iff.intro (
  assume ⟨x, (hx : p x → r)⟩,
  assume : ∀ x, p x,
  have p x, from this x,
  hx this
) (
  assume apxr : (∀ x, p x) → r,
  classical.by_cases (
    assume : ∀ x, p x,
    have r, from apxr this,
    ⟨a, λ a, this⟩
  ) (
    assume napx : ¬∀ x, p x,
    classical.by_contradiction (
      assume nepxr : ¬∃ x, p x → r,
      have ∀ x, p x, from (
        assume x, classical.by_contradiction (
          assume npx : ¬ p x,
          have ∃x, p x → r, from ⟨x, (assume : p x, absurd this npx)⟩,
          nepxr this
        )
      ),
      napx this
    )
  )
)
example : (∃ x, r → p x) ↔ (r → ∃ x, p x) :=
iff.intro (
  assume ⟨x, (rpx: r → p x)⟩,
  assume hr: r, ⟨x, rpx hr⟩
) (
  assume repx : r → ∃ x, p x, classical.by_cases (
    assume : r,
    have epx: ∃ x, p x, from repx this,
    exists.elim epx (
      assume x,
      assume : p x,
      ⟨x, λ r, this⟩
    )
  ) (
    assume : ¬r,
    ⟨a, assume r, absurd r this⟩
  )
)

--- Exercise 6
variables (real : Type) [ordered_ring real]
variables (log exp : real → real)
variable  log_exp_eq : ∀ x, log (exp x) = x
variable  exp_log_eq : ∀ {x}, x > 0 → exp (log x) = x
variable  exp_pos    : ∀ x, exp x > 0
variable  exp_add    : ∀ x y, exp (x + y) = exp x * exp y

-- this ensures the assumptions are available in tactic proofs
include log_exp_eq exp_log_eq exp_pos exp_add

example (x y z : real) :
  exp (x + y + z) = exp x * exp y * exp z :=
by rw [exp_add, exp_add]

example (y : real) (h : y > 0)  : exp (log y) = y :=
exp_log_eq h

theorem log_mul {x y : real} (hx : x > 0) (hy : y > 0) :
  log (x * y) = log x + log y :=
calc
  log (x * y) = log (exp (log x) * y)           : by rw exp_log_eq hx
          ... = log (exp (log x) * exp (log y)) : by rw exp_log_eq hy
          ... = log (exp (log x + log y))       : by rw exp_add (log x) (log y)
          ... = log x + log y                   : log_exp_eq (log x + log y)

--- Exercise 7
example (x : ℤ) : x * 0 = 0 :=
calc
  x * 0 = x * (1 - 1)       : by rw sub_self (1 : ℤ)
    ... = (x * 1) - (x * 1) : by rw mul_sub x 1 1
    ... = 0                 : sub_self (x * 1)


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

! This file was ported from Lean 3 source module analysis.analytic.uniqueness
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Analytic.Linear
import Mathbin.Analysis.Analytic.Composition
import Mathbin.Analysis.NormedSpace.Completion

/-!
# Uniqueness principle for analytic functions

We show that two analytic functions which coincide around a point coincide on whole connected sets,
in `analytic_on.eq_on_of_preconnected_of_eventually_eq`.
-/

variable {𝕜 : Type _} [NontriviallyNormedField 𝕜] {E : Type _} [NormedAddCommGroup E]
  [NormedSpace 𝕜 E] {F : Type _} [NormedAddCommGroup F] [NormedSpace 𝕜 F]

open Set

open Topology ENNReal

namespace AnalyticOn

/-- If an analytic function vanishes around a point, then it is uniformly zero along
a connected set. Superseded by `eq_on_zero_of_preconnected_of_locally_zero` which does not assume
completeness of the target space. -/
theorem eqOn_zero_of_preconnected_of_eventuallyEq_zero_aux [CompleteSpace F] {f : E → F} {U : Set E}
    (hf : AnalyticOn 𝕜 f U) (hU : IsPreconnected U) {z₀ : E} (h₀ : z₀ ∈ U) (hfz₀ : f =ᶠ[𝓝 z₀] 0) :
    EqOn f 0 U :=
  by
  /- Let `u` be the set of points around which `f` vanishes. It is clearly open. We have to show
    that its limit points in `U` still belong to it, from which the inclusion `U ⊆ u` will follow
    by connectedness. -/
  let u := { x | f =ᶠ[𝓝 x] 0 }
  suffices main : closure u ∩ U ⊆ u
  · have Uu : U ⊆ u :=
      hU.subset_of_closure_inter_subset isOpen_setOf_eventually_nhds ⟨z₀, h₀, hfz₀⟩ main
    intro z hz
    simpa using mem_of_mem_nhds (Uu hz)
  /- Take a limit point `x`, then a ball `B (x, r)` on which it has a power series expansion, and
    then `y ∈ B (x, r/2) ∩ u`. Then `f` has a power series expansion on `B (y, r/2)` as it is
    contained in `B (x, r)`. All the coefficients in this series expansion vanish, as `f` is zero on a
    neighborhood of `y`. Therefore, `f` is zero on `B (y, r/2)`. As this ball contains `x`, it follows
    that `f` vanishes on a neighborhood of `x`, proving the claim. -/
  rintro x ⟨xu, xU⟩
  rcases hf x xU with ⟨p, r, hp⟩
  obtain ⟨y, yu, hxy⟩ : ∃ y ∈ u, edist x y < r / 2
  exact EMetric.mem_closure_iff.1 xu (r / 2) (ENNReal.half_pos hp.r_pos.ne')
  let q := p.change_origin (y - x)
  have has_series : HasFpowerSeriesOnBall f q y (r / 2) :=
    by
    have A : (‖y - x‖₊ : ℝ≥0∞) < r / 2 := by rwa [edist_comm, edist_eq_coe_nnnorm_sub] at hxy
    have := hp.change_origin (A.trans_le ENNReal.half_le_self)
    simp only [add_sub_cancel'_right] at this
    apply this.mono (ENNReal.half_pos hp.r_pos.ne')
    apply ENNReal.le_sub_of_add_le_left ENNReal.coe_ne_top
    apply (add_le_add A.le (le_refl (r / 2))).trans (le_of_eq _)
    exact ENNReal.add_halves _
  have M : EMetric.ball y (r / 2) ∈ 𝓝 x := emetric.is_open_ball.mem_nhds hxy
  filter_upwards [M]with z hz
  have A : HasSum (fun n : ℕ => q n fun i : Fin n => z - y) (f z) := has_series.has_sum_sub hz
  have B : HasSum (fun n : ℕ => q n fun i : Fin n => z - y) 0 :=
    by
    have : HasFpowerSeriesAt 0 q y := has_series.has_fpower_series_at.congr yu
    convert hasSum_zero
    ext n
    exact this.apply_eq_zero n _
  exact HasSum.unique A B
#align analytic_on.eq_on_zero_of_preconnected_of_eventually_eq_zero_aux AnalyticOn.eqOn_zero_of_preconnected_of_eventuallyEq_zero_aux

/-- The *identity principle* for analytic functions: If an analytic function vanishes in a whole
neighborhood of a point `z₀`, then it is uniformly zero along a connected set. For a one-dimensional
version assuming only that the function vanishes at some points arbitrarily close to `z₀`, see
`eq_on_zero_of_preconnected_of_frequently_eq_zero`. -/
theorem eqOn_zero_of_preconnected_of_eventuallyEq_zero {f : E → F} {U : Set E}
    (hf : AnalyticOn 𝕜 f U) (hU : IsPreconnected U) {z₀ : E} (h₀ : z₀ ∈ U) (hfz₀ : f =ᶠ[𝓝 z₀] 0) :
    EqOn f 0 U := by
  let F' := UniformSpace.Completion F
  set e : F →L[𝕜] F' := UniformSpace.Completion.toComplL
  have : AnalyticOn 𝕜 (e ∘ f) U := fun x hx => (e.analytic_at _).comp (hf x hx)
  have A : eq_on (e ∘ f) 0 U :=
    by
    apply eq_on_zero_of_preconnected_of_eventually_eq_zero_aux this hU h₀
    filter_upwards [hfz₀]with x hx
    simp only [hx, Function.comp_apply, Pi.zero_apply, map_zero]
  intro z hz
  have : e (f z) = e 0 := by simpa only using A hz
  exact UniformSpace.Completion.coe_injective F this
#align analytic_on.eq_on_zero_of_preconnected_of_eventually_eq_zero AnalyticOn.eqOn_zero_of_preconnected_of_eventuallyEq_zero

/-- The *identity principle* for analytic functions: If two analytic function coincide in a whole
neighborhood of a point `z₀`, then they coincide globally along a connected set.
For a one-dimensional version assuming only that the functions coincide at some points
arbitrarily close to `z₀`, see `eq_on_of_preconnected_of_frequently_eq`. -/
theorem eqOn_of_preconnected_of_eventuallyEq {f g : E → F} {U : Set E} (hf : AnalyticOn 𝕜 f U)
    (hg : AnalyticOn 𝕜 g U) (hU : IsPreconnected U) {z₀ : E} (h₀ : z₀ ∈ U) (hfg : f =ᶠ[𝓝 z₀] g) :
    EqOn f g U := by
  have hfg' : f - g =ᶠ[𝓝 z₀] 0 := hfg.mono fun z h => by simp [h]
  simpa [sub_eq_zero] using fun z hz =>
    (hf.sub hg).eqOn_zero_of_preconnected_of_eventuallyEq_zero hU h₀ hfg' hz
#align analytic_on.eq_on_of_preconnected_of_eventually_eq AnalyticOn.eqOn_of_preconnected_of_eventuallyEq

end AnalyticOn


import lib init.algebra.order

universes u v

namespace landau_natation

variables (α : Type u) [has_mul α] [linear_order α]

def eventually (p : ℕ → Prop) : Prop := ∃ n : ℕ, ∀ m ≥ n, p m

local notation `Eventually` binders `, ` r:(scoped p, eventually p) := r

def frequently (p : ℕ → Prop) : Prop := ∀ n : ℕ, ∃ m ≥ n, p m

local notation `Frequently` binders `, ` r:(scoped p, frequently p) := r

lemma eventually_def (p : ℕ → Prop) : (Eventually x, p x) ↔ (∃ n : ℕ, ∀ m ≥ n, p m) := by refl

lemma eventually_of_all (p : ℕ → Prop) (h : ∀ x, p x) : Eventually x, p x := ⟨0, λ m _, h m⟩

lemma eventually_and {p q : ℕ → Prop} : (Eventually x, p x ∧ q x) ↔ (Eventually x, p x) ∧ (Eventually x, q x) :=
⟨by { rintros ⟨M, h⟩, refine ⟨⟨M, λ m le, (h m le).left⟩, ⟨M, λ m le, (h m le).right⟩⟩,
 },
 by { rintros ⟨⟨M₁, h₁⟩, ⟨M₂, h₂⟩⟩, refine ⟨max M₁ M₂, _⟩, simp,
      intros m le₁ le₂, exact ⟨h₁ m le₁, h₂ m le₂⟩ }⟩

lemma eventually_imply {p q : ℕ → Prop} (I : ∀ x, p x → q x) : (Eventually x, p x) → (Eventually x, q x) :=
by rintros ⟨c, h⟩; refine ⟨c, λ m le, I _ (h m le)⟩

lemma frequently_def (p : ℕ → Prop) : (Frequently x, p x) ↔ (∀ n : ℕ, ∃ m ≥ n, p m) := by refl

def bigO (f : ℕ → ℕ) : set (ℕ → ℕ) := {g | ∃ c : ℕ , Eventually x, g x ≤ c * (f x)}

notation `O[` f `]` := bigO f

def bigO_symm (f g : ℕ → ℕ) : Prop := g ∈ O[f] ∧ f ∈ O[g]

infix ` ≃Θ `:60 := bigO_symm

notation `O[` f `]` := bigO f

lemma bigO_def (f g : ℕ → ℕ) : g ∈ O[f] ↔ ∃ c, Eventually x, g x ≤ c * (f x) := by refl

@[refl, simp] protected lemma refl {f : ℕ → ℕ} : f ∈ O[f] :=
⟨1, 0, λ m _, by simp⟩

@[trans] lemma trans {f g h : ℕ → ℕ} :
  f ∈ O[g] → g ∈ O[h] → f ∈ O[h] :=
begin
  simp[bigO_def],
  intros c₁ h₁ c₂ h₂,
  refine ⟨c₁ * c₂, _⟩,
  have : (Eventually x, f x ≤ c₁ * g x ∧ g x ≤ c₂ * h x), refine eventually_and.mpr ⟨h₁, h₂⟩,
  refine eventually_imply _ this,
  { rintros x ⟨le₁, le₂⟩,
    have : c₁ * g x ≤ c₁ * c₂ * h x, by simpa[mul_assoc] using mul_le_mul_left' le₂ c₁,
    exact le_trans le₁ this }
end

-- lemma mul_iff {f g : ℕ → ℕ} (k : ℕ) (hk : 0 ≠ k) : O[f] (λ x, k * g x) ↔ O[f] g :=
-- ⟨by { rintros ⟨c, h⟩, refine ⟨c, eventually_imply (λ x, _) h⟩, simp,  }, by {  }⟩

end landau_natation


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

! This file was ported from Lean 3 source module data.set.enumerate
! leanprover-community/mathlib commit 92ca63f0fb391a9ca5f22d2409a6080e786d99f7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Lattice
import Mathbin.Data.Nat.Order.Basic
import Mathbin.Tactic.Wlog

/-!
# Set enumeration

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

This file allows enumeration of sets given a choice function.

The definition does not assume `sel` actually is a choice function, i.e. `sel s ∈ s` and
`sel s = none ↔ s = ∅`. These assumptions are added to the lemmas needing them.
-/

noncomputable section

open Function

namespace Set

section Enumerate

parameter {α : Type _}(sel : Set α → Option α)

#print Set.enumerate /-
/-- Given a choice function `sel`, enumerates the elements of a set in the order
`a 0 = sel s`, `a 1 = sel (s \ {a 0})`, `a 2 = sel (s \ {a 0, a 1})`, ... and stops when
`sel (s \ {a 0, ..., a n}) = none`. Note that we don't require `sel` to be a choice function. -/
def enumerate : Set α → ℕ → Option α
  | s, 0 => sel s
  | s, n + 1 => do
    let a ← sel s
    enumerate (s \ {a}) n
#align set.enumerate Set.enumerate
-/

#print Set.enumerate_eq_none_of_sel /-
theorem enumerate_eq_none_of_sel {s : Set α} (h : sel s = none) : ∀ {n}, enumerate s n = none
  | 0 => by simp [h, enumerate] <;> rfl
  | n + 1 => by simp [h, enumerate] <;> rfl
#align set.enumerate_eq_none_of_sel Set.enumerate_eq_none_of_sel
-/

#print Set.enumerate_eq_none /-
theorem enumerate_eq_none : ∀ {s n₁ n₂}, enumerate s n₁ = none → n₁ ≤ n₂ → enumerate s n₂ = none
  | s, 0, m => fun h _ => enumerate_eq_none_of_sel h
  | s, n + 1, m => fun h hm => by
    cases hs : sel s
    · exact enumerate_eq_none_of_sel sel hs
    · cases m
      case zero =>
        have : n + 1 = 0 := Nat.eq_zero_of_le_zero hm
        contradiction
      case succ m' =>
        simp [hs, enumerate] at h⊢
        have hm : n ≤ m' := Nat.le_of_succ_le_succ hm
        exact enumerate_eq_none h hm
#align set.enumerate_eq_none Set.enumerate_eq_none
-/

#print Set.enumerate_mem /-
theorem enumerate_mem (h_sel : ∀ s a, sel s = some a → a ∈ s) :
    ∀ {s n a}, enumerate s n = some a → a ∈ s
  | s, 0, a => h_sel s a
  | s, n + 1, a => by
    cases h : sel s
    case none => simp [enumerate_eq_none_of_sel, h]
    case some a' =>
      simp [enumerate, h]
      exact fun h' : enumerate _ (s \ {a'}) n = some a =>
        have : a ∈ s \ {a'} := enumerate_mem h'
        this.left
#align set.enumerate_mem Set.enumerate_mem
-/

#print Set.enumerate_inj /-
theorem enumerate_inj {n₁ n₂ : ℕ} {a : α} {s : Set α} (h_sel : ∀ s a, sel s = some a → a ∈ s)
    (h₁ : enumerate s n₁ = some a) (h₂ : enumerate s n₂ = some a) : n₁ = n₂ :=
  by
  wlog hn : n₁ ≤ n₂
  · cases' le_total n₁ n₂ with H H <;> [skip, symm] <;> apply_assumption <;> assumption
  · rcases Nat.le.dest hn with ⟨m, rfl⟩
    clear hn
    induction n₁ generalizing s
    case zero =>
      cases m
      case zero => rfl
      case succ m =>
        have : enumerate sel (s \ {a}) m = some a := by simp_all [enumerate]
        have : a ∈ s \ {a} := enumerate_mem _ h_sel this
        · simpa
    case succ => cases h : sel s <;> simp_all [enumerate, Nat.add_succ, add_comm] <;> tauto
#align set.enumerate_inj Set.enumerate_inj
-/

end Enumerate

end Set


/-
Copyright (c) 2022 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import analysis.normed_space.basic
import topology.metric_space.pi_nat
import topology.metric_space.isometry
import topology.metric_space.gluing

/-!
# Polish spaces

A topological space is Polish if its topology is second-countable and there exists a compatible
complete metric. This is the class of spaces that is well-behaved with respect to measure theory.
In this file, we establish the basic properties of Polish spaces.

## Main definitions and results

* `polish_space α` is a mixin typeclass on a topological space, requiring that the topology is
  second-countable and compatible with a complete metric. To endow the space with such a metric,
  use in a proof `letI := upgrade_polish_space α`.
  We register an instance from complete second-countable metric spaces to Polish spaces, not the
  other way around.
* We register that countable products and sums of Polish spaces are Polish.
* `is_closed.polish_space`: a closed subset of a Polish space is Polish.
* `is_open.polish_space`: an open subset of a Polish space is Polish.
* `exists_nat_nat_continuous_surjective`: any nonempty Polish space is the continuous image
  of the fundamental Polish space `ℕ → ℕ`.

A fundamental property of Polish spaces is that one can put finer topologies, still Polish,
with additional properties:

* `exists_polish_space_forall_le`: on a topological space, consider countably many topologies
  `t n`, all Polish and finer than the original topology. Then there exists another Polish
  topology which is finer than all the `t n`.
* `is_clopenable s` is a property of a subset `s` of a topological space, requiring that there
  exists a finer topology, which is Polish, for which `s` becomes open and closed. We show that
  this property is satisfied for open sets, closed sets, for complements, and for countable unions.
  Once Borel-measurable sets are defined in later files, it will follow that any Borel-measurable
  set is clopenable. Once the Lusin-Souslin theorem is proved using analytic sets, we will even
  show that a set is clopenable if and only if it is Borel-measurable, see
  `is_clopenable_iff_measurable_set`.
-/

noncomputable theory
open_locale classical topological_space filter
open topological_space set metric filter function

variables {α : Type*} {β : Type*}

/-! ### Basic properties of Polish spaces -/

/-- A Polish space is a topological space with second countable topology, that can be endowed
with a metric for which it is complete.
We register an instance from complete second countable metric space to polish space, and not the
other way around as this is the most common use case.

To endow a Polish space with a complete metric space structure, do `letI := upgrade_polish_space α`.
-/
class polish_space (α : Type*) [h : topological_space α] : Prop :=
(second_countable [] : second_countable_topology α)
(complete : ∃ m : metric_space α, m.to_uniform_space.to_topological_space = h ∧
  @complete_space α m.to_uniform_space)

/-- A convenience class, for a Polish space endowed with a complete metric. No instance of this
class should be registered: It should be used as `letI := upgrade_polish_space α` to endow a Polish
space with a complete metric. -/
class upgraded_polish_space (α : Type*) extends metric_space α, second_countable_topology α,
  complete_space α

@[priority 100]
instance polish_space_of_complete_second_countable
  [m : metric_space α] [h : second_countable_topology α] [h' : complete_space α] :
  polish_space α :=
{ second_countable := h,
  complete := ⟨m, rfl, h'⟩ }

/-- Construct on a Polish space a metric (compatible with the topology) which is complete. -/
def polish_space_metric (α : Type*) [ht : topological_space α] [h : polish_space α] :
  metric_space α :=
h.complete.some.replace_topology h.complete.some_spec.1.symm

lemma complete_polish_space_metric (α : Type*) [ht : topological_space α] [h : polish_space α] :
  @complete_space α (polish_space_metric α).to_uniform_space :=
begin
  convert h.complete.some_spec.2,
  exact metric_space.replace_topology_eq _ _
end

/-- This definition endows a Polish space with a complete metric. Use it as:
`letI := upgrade_polish_space α`. -/
def upgrade_polish_space (α : Type*) [ht : topological_space α] [h : polish_space α] :
  upgraded_polish_space α :=
begin
  letI := polish_space_metric α,
  exact { .. complete_polish_space_metric α, .. polish_space.second_countable α }
end

namespace polish_space

@[priority 100]
instance t2_space (α : Type*) [topological_space α] [polish_space α] : t2_space α :=
by { letI := upgrade_polish_space α, apply_instance }

/-- A countable product of Polish spaces is Polish. -/
instance pi_countable {ι : Type*} [encodable ι] {E : ι → Type*}
  [∀ i, topological_space (E i)] [∀ i, polish_space (E i)] :
  polish_space (Π i, E i) :=
begin
  letI := λ i, upgrade_polish_space (E i),
  letI : metric_space (Π i, E i) := pi_countable.metric_space,
  apply_instance,
end

/-- Without this instance, `polish_space (ℕ → ℕ)` is not found by typeclass inference. -/
instance nat_fun [topological_space α] [polish_space α] :
  polish_space (ℕ → α) :=
by apply_instance

/-- A countable disjoint union of Polish spaces is Polish. -/
instance sigma {ι : Type*} [encodable ι]
  {E : ι → Type*} [∀ n, topological_space (E n)] [∀ n, polish_space (E n)] :
  polish_space (Σ n, E n) :=
begin
  letI := λ n, upgrade_polish_space (E n),
  letI : metric_space (Σ n, E n) := sigma.metric_space,
  haveI : complete_space (Σ n, E n) := sigma.complete_space,
  apply_instance
end

/-- The disjoint union of two Polish spaces is Polish. -/
instance sum [topological_space α] [polish_space α] [topological_space β] [polish_space β] :
  polish_space (α ⊕ β) :=
begin
  letI := upgrade_polish_space α,
  letI := upgrade_polish_space β,
  letI : metric_space (α ⊕ β) := metric_space_sum,
  apply_instance
end

/-- Any nonempty Polish space is the continuous image of the fundamental space `ℕ → ℕ`. -/
lemma exists_nat_nat_continuous_surjective
  (α : Type*) [topological_space α] [polish_space α] [nonempty α] :
  ∃ (f : (ℕ → ℕ) → α), continuous f ∧ surjective f :=
begin
  letI := upgrade_polish_space α,
  exact exists_nat_nat_continuous_surjective_of_complete_space α
end

/-- Given a closed embedding into a Polish space, the source space is also Polish. -/
lemma _root_.closed_embedding.polish_space [topological_space α] [topological_space β]
  [polish_space β] {f : α → β} (hf : closed_embedding f) :
  polish_space α :=
begin
  letI := upgrade_polish_space β,
  letI : metric_space α := hf.to_embedding.comap_metric_space f,
  haveI : second_countable_topology α := hf.to_embedding.second_countable_topology,
  haveI : complete_space α,
  { rw complete_space_iff_is_complete_range hf.to_embedding.to_isometry.uniform_inducing,
    apply is_closed.is_complete,
    exact hf.closed_range },
  apply_instance
end

/-- Pulling back a Polish topology under an equiv gives again a Polish topology. -/
lemma _root_.equiv.polish_space_induced [t : topological_space β] [polish_space β]
  (f : α ≃ β) :
  @polish_space α (t.induced f) :=
begin
  letI : topological_space α := t.induced f,
  exact (f.to_homeomorph_of_inducing ⟨rfl⟩).closed_embedding.polish_space,
end

/-- A closed subset of a Polish space is also Polish. -/
lemma _root_.is_closed.polish_space {α : Type*} [topological_space α] [polish_space α] {s : set α}
  (hs : is_closed s) :
  polish_space s :=
(is_closed.closed_embedding_subtype_coe hs).polish_space

/-- A sequence of type synonyms of a given type `α`, useful in the proof of
`exists_polish_space_forall_le` to endow each copy with a different topology. -/
@[nolint unused_arguments has_inhabited_instance]
def aux_copy (α : Type*) {ι : Type*} (i : ι) : Type* := α

/-- Given a Polish space, and countably many finer Polish topologies, there exists another Polish
topology which is finer than all of them. -/
lemma exists_polish_space_forall_le {ι : Type*} [encodable ι]
  [t : topological_space α] [p : polish_space α]
  (m : ι → topological_space α) (hm : ∀ n, m n ≤ t) (h'm : ∀ n, @polish_space α (m n)) :
  ∃ (t' : topological_space α), (∀ n, t' ≤ m n) ∧ (t' ≤ t) ∧ @polish_space α t' :=
begin
  rcases is_empty_or_nonempty ι with hι|hι,
  { exact ⟨t, λ i, (is_empty.elim hι i : _), le_rfl, p⟩ },
  unfreezingI { inhabit ι },
  /- Consider the product of infinitely many copies of `α`, each endowed with the topology `m n`.
  This is a Polish space, as a product of Polish spaces. Pulling back this topology under the
  diagonal embedding of `α`, one gets a Polish topology which is finer than all the `m n`. -/
  letI : ∀ (n : ι), topological_space (aux_copy α n) := λ n, m n,
  haveI : ∀ (n : ι), polish_space (aux_copy α n) := λ n, h'm n,
  letI T : topological_space (Π (n : ι), aux_copy α n) := by apply_instance,
  let f : α → Π (n : ι), aux_copy α n := λ x n, x,
  -- show that the induced topology is finer than all the `m n`.
  have T_le_m : ∀ n, T.induced f ≤ m n,
  { assume n s hs,
    refine ⟨set.pi ({n} : set ι) (λ i, s), _, _⟩,
    { apply is_open_set_pi (finite_singleton _),
      assume a ha,
      rw mem_singleton_iff.1 ha,
      exact hs },
    { ext x,
      simp only [singleton_pi, mem_preimage] } },
  refine ⟨T.induced f, λ n, T_le_m n, (T_le_m default).trans (hm default), _⟩,
  -- show that the new topology is Polish, as the pullback of a Polish topology under a closed
  -- embedding.
  have A : range f = ⋂ n, {x | x n = x default},
  { ext x,
    split,
    { rintros ⟨y, rfl⟩,
      exact mem_Inter.2 (λ n, by simp only [mem_set_of_eq]) },
    { assume hx,
      refine ⟨x default, _⟩,
      ext1 n,
      symmetry,
      exact (mem_Inter.1 hx n : _) } },
  have f_closed : is_closed (range f),
  { rw A,
    apply is_closed_Inter (λ n, _),
    have C : ∀ (i : ι), continuous (λ (x : Π n, aux_copy α n), (id (x i) : α)),
    { assume i,
      apply continuous.comp _ (continuous_apply i),
      apply continuous_def.2 (λ s hs, _),
      exact hm i s hs },
    apply is_closed_eq (C n) (C default) },
  have K : @_root_.embedding _ _ (T.induced f) T f,
  { apply function.injective.embedding_induced,
    assume x y hxy,
    have : f x default = f y default, by rw hxy,
    exact this },
  have L : @closed_embedding _ _ (T.induced f) T f,
  { split,
    { exact K },
    { exact f_closed } },
  exact @closed_embedding.polish_space _ _ (T.induced f) T (by apply_instance) _ L
end

/-!
### An open subset of a Polish space is Polish

To prove this fact, one needs to construct another metric, giving rise to the same topology,
for which the open subset is complete. This is not obvious, as for instance `(0,1) ⊆ ℝ` is not
complete for the usual metric of `ℝ`: one should build a new metric that blows up close to the
boundary.
-/

section complete_copy

variables [metric_space α] {s : set α}

/-- A type synonym for a subset `s` of a metric space, on which we will construct another metric
for which it will be complete. -/
@[nolint has_inhabited_instance]
def complete_copy {α : Type*} (s : set α) : Type* := s

/-- A distance on a subset `s` of a metric space, designed to make it complete if `s` is open.
It is given by `dist' x y = dist x y + |1 / dist x sᶜ - 1 / dist y sᶜ|`, where the second term
blows up close to the boundary to ensure that Cauchy sequences for `dist'` remain well
inside `s`. -/
def has_dist_complete_copy (s : set α) : has_dist (complete_copy s) :=
⟨λ x y, dist x.1 y.1 + abs (1 / inf_dist x.1 sᶜ - 1 / inf_dist y.1 sᶜ)⟩

local attribute [instance] has_dist_complete_copy

lemma dist_complete_copy_eq (x y : complete_copy s) :
  dist x y = dist x.1 y.1 + abs (1/inf_dist x.1 sᶜ - 1 / inf_dist y.1 sᶜ) := rfl

lemma dist_le_dist_complete_copy (x y : complete_copy s) :
  dist x.1 y.1 ≤ dist x y :=
(le_add_iff_nonneg_right _).2 (abs_nonneg _)

/-- A metric space structure on a subset `s` of a metric space, designed to make it complete
if `s` is open. It is given by `dist' x y = dist x y + |1 / dist x sᶜ - 1 / dist y sᶜ|`, where the
second term blows up close to the boundary to ensure that Cauchy sequences for `dist'` remain well
inside `s`. -/
def complete_copy_metric_space (s : set α) : metric_space (complete_copy s) :=
{ dist_self := λ x, by simp [dist_complete_copy_eq],
  dist_comm := λ x y, by simp [dist_complete_copy_eq, dist_comm, abs_sub_comm],
  dist_triangle := λ x y z, calc
    dist x z = dist x.1 z.1 + abs (1 / inf_dist x.1 sᶜ - 1 / inf_dist z.1 sᶜ) : rfl
    ... ≤ (dist x.1 y.1 + dist y.1 z.1) +
      (abs (1 / inf_dist x.1 sᶜ - 1 / inf_dist y.1 sᶜ)
      + abs (1 / inf_dist y.1 sᶜ - 1 / inf_dist z.1 sᶜ)) :
    begin
      rw [← real.dist_eq, ← real.dist_eq, ← real.dist_eq],
      exact add_le_add (dist_triangle _ _ _) (dist_triangle _ _ _)
    end
    ... = dist x y + dist y z : by { rw [dist_complete_copy_eq, dist_complete_copy_eq], abel },
  eq_of_dist_eq_zero :=
  begin
    assume x y hxy,
    apply subtype.coe_injective,
    refine dist_le_zero.1 _,
    rw ← hxy,
    exact dist_le_dist_complete_copy x y
  end }

local attribute [instance] complete_copy_metric_space

/-- The identity between the type synonym `complete_copy s` (with its modified metric) and the
original subtype `s` is a homeomorphism. -/
def complete_copy_id_homeo (hs : is_open s) (h's : sᶜ.nonempty) : complete_copy s ≃ₜ s :=
{ to_fun := id,
  inv_fun := id,
  left_inv := λ x, rfl,
  right_inv := λ x, rfl,
  continuous_to_fun :=
  begin
    have : lipschitz_with 1 (λ (x : complete_copy s), (id x : s)),
    { apply lipschitz_with.mk_one,
      exact dist_le_dist_complete_copy },
    exact this.continuous,
  end,
  continuous_inv_fun :=
  begin
    apply continuous_iff_continuous_at.2 (λ x, _),
    suffices H : tendsto (λ (b : s), dist b.1 x.1
      + |1 / inf_dist b.1 sᶜ - 1 / inf_dist x.1 sᶜ|) (𝓝 x)
      (𝓝 (dist x.1 x.1 + abs (1 / inf_dist x.1 sᶜ - 1 / inf_dist x.1 sᶜ))),
    { rw [continuous_at, tendsto_iff_dist_tendsto_zero],
      simpa only [sub_self, abs_zero, add_zero, dist_self] using H },
    have I : 0 < inf_dist x.val sᶜ,
    { rw ← hs.is_closed_compl.not_mem_iff_inf_dist_pos h's,
      simp },
    apply tendsto.add,
    { apply continuous.tendsto, exact continuous_subtype_coe.dist continuous_const },
    { refine (tendsto.sub_const _ _).abs,
      refine tendsto.div tendsto_const_nhds _ I.ne',
      exact ((continuous_inf_dist_pt _).comp continuous_subtype_coe).tendsto _ }
  end }

lemma complete_space_complete_copy [complete_space α] (hs : is_open s) (h's : sᶜ.nonempty) :
  complete_space (complete_copy s) :=
begin
  refine metric.complete_of_convergent_controlled_sequences (λ n, (1/2)^n) (by simp) _,
  assume u hu,
  have A : cauchy_seq (λ n, (u n).1),
  { apply cauchy_seq_of_le_tendsto_0 (λ (n : ℕ), (1/2)^n) (λ n m N hNn hNm, _) _,
    { exact (dist_le_dist_complete_copy (u n) (u m)).trans (hu N n m hNn hNm).le },
    { exact tendsto_pow_at_top_nhds_0_of_lt_1 (by norm_num) (by norm_num) } },
  obtain ⟨x, xlim⟩ : ∃ x, tendsto (λ n, (u n).1) at_top (𝓝 x),
  { haveI : nonempty α := ⟨(u 0).1⟩,
    exact ⟨_, A.tendsto_lim⟩ },
  suffices xs : x ∈ s,
  { refine ⟨⟨x, xs⟩, _⟩,
    have L : tendsto (λ n, (id ⟨(u n).1, (u n).2⟩ : s)) at_top (𝓝 (⟨x, xs⟩)),
    { apply embedding_subtype_coe.tendsto_nhds_iff.2, exact xlim },
    convert ((complete_copy_id_homeo hs h's).symm.continuous.tendsto _).comp L,
    ext1 n,
    simp [complete_copy_id_homeo] },
  obtain ⟨C, hC⟩ : ∃ C, ∀ n, 1 / inf_dist (u n).1 sᶜ < C,
  { refine ⟨(1/2)^0 + dist (1 / inf_dist (u 0).1 sᶜ) 0, λ n, _⟩,
    calc 1 / inf_dist (u n).val sᶜ ≤ dist (1 / inf_dist (u n).val sᶜ) 0 :
      by { rw real.dist_0_eq_abs, exact le_abs_self _ }
    ... ≤ dist (1 / inf_dist (u n).1 sᶜ) (1 / inf_dist (u 0).1 sᶜ)
          + dist (1 / inf_dist (u 0).1 sᶜ) 0 : dist_triangle _ _ _
    ... ≤ (dist (u n).1 (u 0).1 + dist (1 / inf_dist (u n).1 sᶜ) (1 / inf_dist (u 0).1 sᶜ))
          + dist (1 / inf_dist (u 0).1 sᶜ) 0 :
      add_le_add (le_add_of_nonneg_left dist_nonneg) le_rfl
    ... = dist (u n) (u 0) +  dist (1 / inf_dist (u 0).1 sᶜ) 0 : rfl
    ... < (1/2)^0 + dist (1 / inf_dist (u 0).1 sᶜ) 0 :
      add_lt_add_right (hu 0 n 0 (zero_le _) le_rfl) _ },
  have Cpos : 0 < C,
  { apply lt_of_le_of_lt _ (hC 0),
    simp [inf_dist_nonneg] },
  have I : ∀ n, 1/C ≤ inf_dist (u n).1 sᶜ,
  { assume n,
    have : 0 < inf_dist (u n).val sᶜ,
    { apply (hs.is_closed_compl.not_mem_iff_inf_dist_pos h's).1, simp },
    rw div_le_iff' Cpos,
    exact (div_le_iff this).1 (hC n).le },
  have I' : 1/C ≤ inf_dist x sᶜ,
  { have : tendsto (λ n, inf_dist (u n).1 sᶜ) at_top (𝓝 (inf_dist x sᶜ)) :=
      ((continuous_inf_dist_pt sᶜ).tendsto x).comp xlim,
    exact ge_of_tendsto' this I },
  suffices : x ∉ sᶜ, by simpa,
  apply (hs.is_closed_compl.not_mem_iff_inf_dist_pos h's).2 (lt_of_lt_of_le _ I'),
  simp [Cpos],
end

/-- An open subset of a Polish space is also Polish. -/
lemma _root_.is_open.polish_space {α : Type*} [topological_space α] [polish_space α] {s : set α}
  (hs : is_open s) :
  polish_space s :=
begin
  rcases eq_empty_or_nonempty sᶜ with h's|h's,
  { simp at h's,
    apply is_closed.polish_space,
    rw h's,
    exact is_closed_univ },
  { letI := upgrade_polish_space α,
    haveI : complete_space (complete_copy s) := complete_space_complete_copy hs h's,
    haveI : second_countable_topology (complete_copy s) :=
      (complete_copy_id_homeo hs h's).embedding.second_countable_topology,
    exact (complete_copy_id_homeo hs h's).symm.closed_embedding.polish_space }
end

end complete_copy

/-! ### Clopenable sets in Polish spaces -/

/-- A set in a topological space is clopenable if there exists a finer Polish topology for which
this set is open and closed. It turns out that this notion is equivalent to being Borel-measurable,
but this is nontrivial (see `is_clopenable_iff_measurable_set`). -/
def is_clopenable [t : topological_space α] (s : set α) : Prop :=
∃ (t' : topological_space α), t' ≤ t ∧ @polish_space α t' ∧ @is_closed α t' s ∧ @is_open α t' s

/-- Given a closed set `s` in a Polish space, one can construct a finer Polish topology for
which `s` is both open and closed. -/
lemma _root_.is_closed.is_clopenable [topological_space α] [polish_space α] {s : set α}
  (hs : is_closed s) : is_clopenable s :=
begin
  /- Both sets `s` and `sᶜ` admit a Polish topology. So does their disjoint union `s ⊕ sᶜ`.
  Pulling back this topology by the canonical bijection with `α` gives the desired Polish
  topology in which `s` is both open and closed. -/
  haveI : polish_space s := hs.polish_space,
  let t : set α := sᶜ,
  haveI : polish_space t := hs.is_open_compl.polish_space,
  let f : α ≃ (s ⊕ t) := (equiv.set.sum_compl s).symm,
  letI T : topological_space (s ⊕ t) := by apply_instance,
  let t' : topological_space α := T.induced f,
  let g := @equiv.to_homeomorph_of_inducing  _ _ t' T f { induced := rfl },
  have A : g ⁻¹' (range (sum.inl : s → s ⊕ t)) = s,
  { ext x,
    by_cases h : x ∈ s,
    { simp only [equiv.set.sum_compl_symm_apply_of_mem, h, mem_preimage, equiv.to_fun_as_coe,
        mem_range_self, equiv.to_homeomorph_of_inducing_apply]},
    { simp only [equiv.set.sum_compl_symm_apply_of_not_mem, h, not_false_iff, mem_preimage,
        equiv.to_homeomorph_of_inducing_apply, equiv.to_fun_as_coe, mem_range, exists_false]} },
  refine ⟨t', _, f.polish_space_induced, _, _⟩,
  { assume u hu,
    change ∃ (s' : set (↥s ⊕ ↥t)), T.is_open s' ∧ f ⁻¹' s' = u,
    refine ⟨f.symm ⁻¹' u, _, by simp only [equiv.symm_symm, equiv.symm_preimage_preimage]⟩,
    refine is_open_sum_iff.2 ⟨_, _⟩,
    { have : is_open ((coe : s → α) ⁻¹' u) := is_open.preimage continuous_subtype_coe hu,
      have : sum.inl ⁻¹' (⇑(f.symm) ⁻¹' u) = (coe : s → α) ⁻¹' u,
        by { ext x, simp only [equiv.symm_symm, mem_preimage, equiv.set.sum_compl_apply_inl] },
      rwa this },
    { have : is_open ((coe : t → α) ⁻¹' u) := is_open.preimage continuous_subtype_coe hu,
      have : sum.inr ⁻¹' (⇑(f.symm) ⁻¹' u) = (coe : t → α) ⁻¹' u,
        by { ext x, simp only [equiv.symm_symm, mem_preimage, equiv.set.sum_compl_apply_inr] },
      rwa this } },
  { have : @is_closed α t' (g ⁻¹' (range (sum.inl : s → s ⊕ t))),
    { apply is_closed.preimage,
      { exact @homeomorph.continuous _ _ t' _ g },
      { exact is_closed_range_inl } },
    convert this,
    exact A.symm },
  { have : @is_open α t' (g ⁻¹' (range (sum.inl : s → s ⊕ t))),
    { apply is_open.preimage,
      { exact @homeomorph.continuous _ _ t' _ g },
      { exact is_open_range_inl } },
    convert this,
    exact A.symm },
end

lemma is_clopenable.compl [topological_space α] {s : set α} (hs : is_clopenable s) :
  is_clopenable sᶜ :=
begin
  rcases hs with ⟨t, t_le, t_polish, h, h'⟩,
  exact ⟨t, t_le, t_polish, @is_open.is_closed_compl α t s h', @is_closed.is_open_compl α t s h⟩,
end

lemma _root_.is_open.is_clopenable [topological_space α] [polish_space α] {s : set α}
  (hs : is_open s) : is_clopenable s :=
by simpa using hs.is_closed_compl.is_clopenable.compl

lemma is_clopenable.Union [t : topological_space α] [polish_space α] {s : ℕ → set α}
  (hs : ∀ n, is_clopenable (s n)) :
  is_clopenable (⋃ n, s n) :=
begin
  choose m mt m_polish m_closed m_open using hs,
  obtain ⟨t', t'm, -, t'_polish⟩ :
    ∃ (t' : topological_space α), (∀ (n : ℕ), t' ≤ m n) ∧ (t' ≤ t) ∧ @polish_space α t' :=
      exists_polish_space_forall_le m mt m_polish,
  have A : @is_open α t' (⋃ n, s n),
  { apply is_open_Union,
    assume n,
    apply t'm n,
    exact m_open n },
  obtain ⟨t'', t''_le, t''_polish, h1, h2⟩ :
    ∃ (t'' : topological_space α), t'' ≤ t' ∧ @polish_space α t''
      ∧ @is_closed α t'' (⋃ n, s n) ∧ @is_open α t'' (⋃ n, s n) :=
        @is_open.is_clopenable α t' t'_polish _ A,
  exact ⟨t'', t''_le.trans ((t'm 0).trans (mt 0)), t''_polish, h1, h2⟩,
end

end polish_space


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

! This file was ported from Lean 3 source module data.fin.tuple.nat_antidiagonal
! leanprover-community/mathlib commit 34ee86e6a59d911a8e4f89b68793ee7577ae79c7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.BigOperators.Fin
import Mathbin.Data.Finset.NatAntidiagonal
import Mathbin.Data.Fin.VecNotation
import Mathbin.Logic.Equiv.Fin

/-!
# Collections of tuples of naturals with the same sum

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

This file generalizes `list.nat.antidiagonal n`, `multiset.nat.antidiagonal n`, and
`finset.nat.antidiagonal n` from the pair of elements `x : ℕ × ℕ` such that `n = x.1 + x.2`, to
the sequence of elements `x : fin k → ℕ` such that `n = ∑ i, x i`.

## Main definitions

* `list.nat.antidiagonal_tuple`
* `multiset.nat.antidiagonal_tuple`
* `finset.nat.antidiagonal_tuple`

## Main results

* `antidiagonal_tuple 2 n` is analogous to `antidiagonal n`:

  * `list.nat.antidiagonal_tuple_two`
  * `multiset.nat.antidiagonal_tuple_two`
  * `finset.nat.antidiagonal_tuple_two`

## Implementation notes

While we could implement this by filtering `(fintype.pi_finset $ λ _, range (n + 1))` or similar,
this implementation would be much slower.

In the future, we could consider generalizing `finset.nat.antidiagonal_tuple` further to
support finitely-supported functions, as is done with `cut` in
`archive/100-theorems-list/45_partition.lean`.
-/

open BigOperators

/-! ### Lists -/

namespace List.Nat

#print List.Nat.antidiagonalTuple /-
/-- `list.antidiagonal_tuple k n` is a list of all `k`-tuples which sum to `n`.

This list contains no duplicates (`list.nat.nodup_antidiagonal_tuple`), and is sorted
lexicographically (`list.nat.antidiagonal_tuple_pairwise_pi_lex`), starting with `![0, ..., n]`
and ending with `![n, ..., 0]`.

```
#eval antidiagonal_tuple 3 2
-- [![0, 0, 2], ![0, 1, 1], ![0, 2, 0], ![1, 0, 1], ![1, 1, 0], ![2, 0, 0]]
```
-/
def antidiagonalTuple : ∀ k, ℕ → List (Fin k → ℕ)
  | 0, 0 => [![]]
  | 0, n + 1 => []
  | k + 1, n =>
    (List.Nat.antidiagonal n).bind fun ni =>
      (antidiagonal_tuple k ni.2).map fun x => Fin.cons ni.1 x
#align list.nat.antidiagonal_tuple List.Nat.antidiagonalTuple
-/

#print List.Nat.antidiagonalTuple_zero_zero /-
@[simp]
theorem antidiagonalTuple_zero_zero : antidiagonalTuple 0 0 = [![]] :=
  rfl
#align list.nat.antidiagonal_tuple_zero_zero List.Nat.antidiagonalTuple_zero_zero
-/

#print List.Nat.antidiagonalTuple_zero_succ /-
@[simp]
theorem antidiagonalTuple_zero_succ (n : ℕ) : antidiagonalTuple 0 n.succ = [] :=
  rfl
#align list.nat.antidiagonal_tuple_zero_succ List.Nat.antidiagonalTuple_zero_succ
-/

#print List.Nat.mem_antidiagonalTuple /-
theorem mem_antidiagonalTuple {n : ℕ} {k : ℕ} {x : Fin k → ℕ} :
    x ∈ antidiagonalTuple k n ↔ (∑ i, x i) = n :=
  by
  revert n
  refine' Fin.consInduction _ _ x
  · intro n
    cases n
    · simp
    · simp [eq_comm]
  · intro k x₀ x ih n
    simp_rw [Fin.sum_cons, antidiagonal_tuple, List.mem_bind, List.mem_map,
      List.Nat.mem_antidiagonal, Fin.cons_eq_cons, exists_eq_right_right, ih,
      @eq_comm _ _ (Prod.snd _), and_comm' (Prod.snd _ = _), ← Prod.mk.inj_iff, Prod.mk.eta,
      exists_prop, exists_eq_right]
#align list.nat.mem_antidiagonal_tuple List.Nat.mem_antidiagonalTuple
-/

#print List.Nat.nodup_antidiagonalTuple /-
/-- The antidiagonal of `n` does not contain duplicate entries. -/
theorem nodup_antidiagonalTuple (k n : ℕ) : List.Nodup (antidiagonalTuple k n) :=
  by
  induction' k with k ih generalizing n
  · cases n
    · simp
    · simp [eq_comm]
  simp_rw [antidiagonal_tuple, List.nodup_bind]
  constructor
  · intro i hi
    exact (ih i.snd).map (Fin.cons_right_injective (i.fst : (fun _ => ℕ) 0))
  induction n
  · exact List.pairwise_singleton _ _
  · rw [List.Nat.antidiagonal_succ]
    refine' List.Pairwise.cons (fun a ha x hx₁ hx₂ => _) (n_ih.map _ fun a b h x hx₁ hx₂ => _)
    · rw [List.mem_map] at hx₁ hx₂ ha
      obtain ⟨⟨a, -, rfl⟩, ⟨x₁, -, rfl⟩, ⟨x₂, -, h⟩⟩ := ha, hx₁, hx₂
      rw [Fin.cons_eq_cons] at h
      injection h.1
    · rw [List.mem_map] at hx₁ hx₂
      obtain ⟨⟨x₁, hx₁, rfl⟩, ⟨x₂, hx₂, h₁₂⟩⟩ := hx₁, hx₂
      dsimp at h₁₂
      rw [Fin.cons_eq_cons, Nat.succ_inj'] at h₁₂
      obtain ⟨h₁₂, rfl⟩ := h₁₂
      rw [h₁₂] at h
      exact h (List.mem_map_of_mem _ hx₁) (List.mem_map_of_mem _ hx₂)
#align list.nat.nodup_antidiagonal_tuple List.Nat.nodup_antidiagonalTuple
-/

#print List.Nat.antidiagonalTuple_zero_right /-
theorem antidiagonalTuple_zero_right : ∀ k, antidiagonalTuple k 0 = [0]
  | 0 => (congr_arg fun x => [x]) <| Subsingleton.elim _ _
  | k + 1 =>
    by
    rw [antidiagonal_tuple, antidiagonal_zero, List.bind_singleton, antidiagonal_tuple_zero_right k,
      List.map_singleton]
    exact congr_arg (fun x => [x]) Matrix.cons_zero_zero
#align list.nat.antidiagonal_tuple_zero_right List.Nat.antidiagonalTuple_zero_right
-/

#print List.Nat.antidiagonalTuple_one /-
@[simp]
theorem antidiagonalTuple_one (n : ℕ) : antidiagonalTuple 1 n = [![n]] :=
  by
  simp_rw [antidiagonal_tuple, antidiagonal, List.range_succ, List.map_append, List.map_singleton,
    tsub_self, List.bind_append, List.bind_singleton, antidiagonal_tuple_zero_zero,
    List.map_singleton, List.map_bind]
  conv_rhs => rw [← List.nil_append [![n]]]
  congr 1
  simp_rw [List.bind_eq_nil, List.mem_range, List.map_eq_nil]
  intro x hx
  obtain ⟨m, rfl⟩ := Nat.exists_eq_add_of_lt hx
  rw [add_assoc, add_tsub_cancel_left, antidiagonal_tuple_zero_succ]
#align list.nat.antidiagonal_tuple_one List.Nat.antidiagonalTuple_one
-/

#print List.Nat.antidiagonalTuple_two /-
theorem antidiagonalTuple_two (n : ℕ) :
    antidiagonalTuple 2 n = (antidiagonal n).map fun i => ![i.1, i.2] :=
  by
  rw [antidiagonal_tuple]
  simp_rw [antidiagonal_tuple_one, List.map_singleton]
  rw [List.map_eq_bind]
  rfl
#align list.nat.antidiagonal_tuple_two List.Nat.antidiagonalTuple_two
-/

#print List.Nat.antidiagonalTuple_pairwise_pi_lex /-
theorem antidiagonalTuple_pairwise_pi_lex :
    ∀ k n, (antidiagonalTuple k n).Pairwise (Pi.Lex (· < ·) fun _ => (· < ·))
  | 0, 0 => List.pairwise_singleton _ _
  | 0, n + 1 => List.Pairwise.nil
  | k + 1, n =>
    by
    simp_rw [antidiagonal_tuple, List.pairwise_bind, List.pairwise_map', List.mem_map,
      forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]
    simp only [mem_antidiagonal, Prod.forall, and_imp, forall_apply_eq_imp_iff₂]
    simp only [Fin.pi_lex_lt_cons_cons, eq_self_iff_true, true_and_iff, lt_self_iff_false,
      false_or_iff]
    refine' ⟨fun _ _ _ => antidiagonal_tuple_pairwise_pi_lex k _, _⟩
    induction n
    · rw [antidiagonal_zero]
      exact List.pairwise_singleton _ _
    · rw [antidiagonal_succ, List.pairwise_cons, List.pairwise_map']
      refine' ⟨fun p hp x hx y hy => _, _⟩
      · rw [List.mem_map, Prod.exists] at hp
        obtain ⟨a, b, hab, rfl : (Nat.succ a, b) = p⟩ := hp
        exact Or.inl (Nat.zero_lt_succ _)
      dsimp
      simp_rw [Nat.succ_inj', Nat.succ_lt_succ_iff]
      exact n_ih
#align list.nat.antidiagonal_tuple_pairwise_pi_lex List.Nat.antidiagonalTuple_pairwise_pi_lex
-/

end List.Nat

/-! ### Multisets -/

namespace Multiset.Nat

#print Multiset.Nat.antidiagonalTuple /-
/-- `multiset.antidiagonal_tuple k n` is a multiset of `k`-tuples summing to `n` -/
def antidiagonalTuple (k n : ℕ) : Multiset (Fin k → ℕ) :=
  List.Nat.antidiagonalTuple k n
#align multiset.nat.antidiagonal_tuple Multiset.Nat.antidiagonalTuple
-/

#print Multiset.Nat.antidiagonalTuple_zero_zero /-
@[simp]
theorem antidiagonalTuple_zero_zero : antidiagonalTuple 0 0 = {![]} :=
  rfl
#align multiset.nat.antidiagonal_tuple_zero_zero Multiset.Nat.antidiagonalTuple_zero_zero
-/

#print Multiset.Nat.antidiagonalTuple_zero_succ /-
@[simp]
theorem antidiagonalTuple_zero_succ (n : ℕ) : antidiagonalTuple 0 n.succ = 0 :=
  rfl
#align multiset.nat.antidiagonal_tuple_zero_succ Multiset.Nat.antidiagonalTuple_zero_succ
-/

#print Multiset.Nat.mem_antidiagonalTuple /-
theorem mem_antidiagonalTuple {n : ℕ} {k : ℕ} {x : Fin k → ℕ} :
    x ∈ antidiagonalTuple k n ↔ (∑ i, x i) = n :=
  List.Nat.mem_antidiagonalTuple
#align multiset.nat.mem_antidiagonal_tuple Multiset.Nat.mem_antidiagonalTuple
-/

#print Multiset.Nat.nodup_antidiagonalTuple /-
theorem nodup_antidiagonalTuple (k n : ℕ) : (antidiagonalTuple k n).Nodup :=
  List.Nat.nodup_antidiagonalTuple _ _
#align multiset.nat.nodup_antidiagonal_tuple Multiset.Nat.nodup_antidiagonalTuple
-/

#print Multiset.Nat.antidiagonalTuple_zero_right /-
theorem antidiagonalTuple_zero_right (k : ℕ) : antidiagonalTuple k 0 = {0} :=
  congr_arg _ (List.Nat.antidiagonalTuple_zero_right k)
#align multiset.nat.antidiagonal_tuple_zero_right Multiset.Nat.antidiagonalTuple_zero_right
-/

#print Multiset.Nat.antidiagonalTuple_one /-
@[simp]
theorem antidiagonalTuple_one (n : ℕ) : antidiagonalTuple 1 n = {![n]} :=
  congr_arg _ (List.Nat.antidiagonalTuple_one n)
#align multiset.nat.antidiagonal_tuple_one Multiset.Nat.antidiagonalTuple_one
-/

#print Multiset.Nat.antidiagonalTuple_two /-
theorem antidiagonalTuple_two (n : ℕ) :
    antidiagonalTuple 2 n = (antidiagonal n).map fun i => ![i.1, i.2] :=
  congr_arg _ (List.Nat.antidiagonalTuple_two n)
#align multiset.nat.antidiagonal_tuple_two Multiset.Nat.antidiagonalTuple_two
-/

end Multiset.Nat

/-! ### Finsets -/

namespace Finset.Nat

#print Finset.Nat.antidiagonalTuple /-
/-- `finset.antidiagonal_tuple k n` is a finset of `k`-tuples summing to `n` -/
def antidiagonalTuple (k n : ℕ) : Finset (Fin k → ℕ) :=
  ⟨Multiset.Nat.antidiagonalTuple k n, Multiset.Nat.nodup_antidiagonalTuple k n⟩
#align finset.nat.antidiagonal_tuple Finset.Nat.antidiagonalTuple
-/

#print Finset.Nat.antidiagonalTuple_zero_zero /-
@[simp]
theorem antidiagonalTuple_zero_zero : antidiagonalTuple 0 0 = {![]} :=
  rfl
#align finset.nat.antidiagonal_tuple_zero_zero Finset.Nat.antidiagonalTuple_zero_zero
-/

#print Finset.Nat.antidiagonalTuple_zero_succ /-
@[simp]
theorem antidiagonalTuple_zero_succ (n : ℕ) : antidiagonalTuple 0 n.succ = ∅ :=
  rfl
#align finset.nat.antidiagonal_tuple_zero_succ Finset.Nat.antidiagonalTuple_zero_succ
-/

#print Finset.Nat.mem_antidiagonalTuple /-
theorem mem_antidiagonalTuple {n : ℕ} {k : ℕ} {x : Fin k → ℕ} :
    x ∈ antidiagonalTuple k n ↔ (∑ i, x i) = n :=
  List.Nat.mem_antidiagonalTuple
#align finset.nat.mem_antidiagonal_tuple Finset.Nat.mem_antidiagonalTuple
-/

#print Finset.Nat.antidiagonalTuple_zero_right /-
theorem antidiagonalTuple_zero_right (k : ℕ) : antidiagonalTuple k 0 = {0} :=
  Finset.eq_of_veq (Multiset.Nat.antidiagonalTuple_zero_right k)
#align finset.nat.antidiagonal_tuple_zero_right Finset.Nat.antidiagonalTuple_zero_right
-/

#print Finset.Nat.antidiagonalTuple_one /-
@[simp]
theorem antidiagonalTuple_one (n : ℕ) : antidiagonalTuple 1 n = {![n]} :=
  Finset.eq_of_veq (Multiset.Nat.antidiagonalTuple_one n)
#align finset.nat.antidiagonal_tuple_one Finset.Nat.antidiagonalTuple_one
-/

#print Finset.Nat.antidiagonalTuple_two /-
theorem antidiagonalTuple_two (n : ℕ) :
    antidiagonalTuple 2 n = (antidiagonal n).map (piFinTwoEquiv fun _ => ℕ).symm.toEmbedding :=
  Finset.eq_of_veq (Multiset.Nat.antidiagonalTuple_two n)
#align finset.nat.antidiagonal_tuple_two Finset.Nat.antidiagonalTuple_two
-/

section EquivProd

#print Finset.Nat.sigmaAntidiagonalTupleEquivTuple /-
/-- The disjoint union of antidiagonal tuples `Σ n, antidiagonal_tuple k n` is equivalent to the
`k`-tuple `fin k → ℕ`. This is such an equivalence, obtained by mapping `(n, x)` to `x`.

This is the tuple version of `finset.nat.sigma_antidiagonal_equiv_prod`. -/
@[simps]
def sigmaAntidiagonalTupleEquivTuple (k : ℕ) : (Σn, antidiagonalTuple k n) ≃ (Fin k → ℕ)
    where
  toFun x := x.2
  invFun x := ⟨∑ i, x i, x, mem_antidiagonalTuple.mpr rfl⟩
  left_inv := fun ⟨n, t, h⟩ => Sigma.subtype_ext (mem_antidiagonalTuple.mp h) rfl
  right_inv x := rfl
#align finset.nat.sigma_antidiagonal_tuple_equiv_tuple Finset.Nat.sigmaAntidiagonalTupleEquivTuple
-/

end EquivProd

end Finset.Nat


/-
Copyright (c) 2019 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Buzzard
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.real.basic
import Mathlib.PostPort

namespace Mathlib

/-!
# The extended reals [-∞, ∞].

This file defines `ereal`, the real numbers together with a top and bottom element,
referred to as ⊤ and ⊥. It is implemented as `with_top (with_bot ℝ)`

Addition and multiplication are problematic in the presence of ±∞, but
negation has a natural definition and satisfies the usual properties.
An addition is derived, but `ereal` is not even a monoid (there is no identity).

`ereal` is a `complete_lattice`; this is now deduced by type class inference from
the fact that `with_top (with_bot L)` is a complete lattice if `L` is
a conditionally complete lattice.

## Tags

real, ereal, complete lattice

## TODO

abs : ereal → ennreal

In Isabelle they define + - * and / (making junk choices for things like -∞ + ∞)
and then prove whatever bits of the ordered ring/field axioms still hold. They
also do some limits stuff (liminf/limsup etc).
See https://isabelle.in.tum.de/dist/library/HOL/HOL-Library/Extended_Real.html
-/

/-- ereal : The type `[-∞, ∞]` -/
def ereal := with_top (with_bot ℝ)

namespace ereal

protected instance has_coe : has_coe ℝ ereal := has_coe.mk (some ∘ some)

@[simp] protected theorem coe_real_le {x : ℝ} {y : ℝ} : ↑x ≤ ↑y ↔ x ≤ y := sorry

@[simp] protected theorem coe_real_lt {x : ℝ} {y : ℝ} : ↑x < ↑y ↔ x < y := sorry

@[simp] protected theorem coe_real_inj' {x : ℝ} {y : ℝ} : ↑x = ↑y ↔ x = y := sorry

protected instance has_zero : HasZero ereal := { zero := ↑0 }

protected instance inhabited : Inhabited ereal := { default := 0 }

/-! ### Negation -/

/-- negation on ereal -/
protected def neg : ereal → ereal := sorry

protected instance has_neg : Neg ereal := { neg := ereal.neg }

protected theorem neg_def (x : ℝ) : ↑(-x) = -↑x := rfl

/-- - -a = a on ereal -/
protected theorem neg_neg (a : ereal) : --a = a := sorry

theorem neg_inj (a : ereal) (b : ereal) (h : -a = -b) : a = b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a = b)) (Eq.symm (ereal.neg_neg a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ( --a = b)) h))
      (eq.mpr (id (Eq._oldrec (Eq.refl ( --b = b)) (ereal.neg_neg b))) (Eq.refl b)))

/-- Even though ereal is not an additive group, -a = b ↔ -b = a still holds -/
theorem neg_eq_iff_neg_eq {a : ereal} {b : ereal} : -a = b ↔ -b = a :=
  { mp :=
      fun (h : -a = b) => eq.mpr (id (Eq._oldrec (Eq.refl (-b = a)) (Eq.symm h))) (ereal.neg_neg a),
    mpr :=
      fun (h : -b = a) =>
        eq.mpr (id (Eq._oldrec (Eq.refl (-a = b)) (Eq.symm h))) (ereal.neg_neg b) }

/-- if -a ≤ b then -b ≤ a on ereal -/
protected theorem neg_le_of_neg_le {a : ereal} {b : ereal} (h : -a ≤ b) : -b ≤ a := sorry

/-- -a ≤ b ↔ -b ≤ a on ereal-/
protected theorem neg_le {a : ereal} {b : ereal} : -a ≤ b ↔ -b ≤ a :=
  { mp := ereal.neg_le_of_neg_le, mpr := ereal.neg_le_of_neg_le }

/-- a ≤ -b → b ≤ -a on ereal -/
theorem le_neg_of_le_neg {a : ereal} {b : ereal} (h : a ≤ -b) : b ≤ -a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b ≤ -a)) (Eq.symm (ereal.neg_neg b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl ( --b ≤ -a)) (propext ereal.neg_le)))
      (eq.mpr (id (Eq._oldrec (Eq.refl ( --a ≤ -b)) (ereal.neg_neg a))) h))

end Mathlib


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Shing Tak Lam, Mario Carneiro

! This file was ported from Lean 3 source module data.nat.digits
! leanprover-community/mathlib commit 369525b73f229ccd76a6ec0e0e0bf2be57599768
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Int.ModEq
import Mathlib.Data.Nat.Bits
import Mathlib.Data.Nat.Log
import Mathlib.Data.List.BigOperators.Lemmas
import Mathlib.Data.List.Indexes
import Mathlib.Data.List.Palindrome
import Mathlib.Algebra.Parity
import Mathlib.Tactic.IntervalCases
import Mathlib.Tactic.Linarith

/-!
# Digits of a natural number

This provides a basic API for extracting the digits of a natural number in a given base,
and reconstructing numbers from their digits.

We also prove some divisibility tests based on digits, in particular completing
Theorem #85 from https://www.cs.ru.nl/~freek/100/.

A basic `norm_digits` tactic is also provided for proving goals of the form
`Nat.digits a b = l` where `a` and `b` are numerals.
-/

namespace Nat

variable {n : ℕ}

/-- (Impl.) An auxiliary definition for `digits`, to help get the desired definitional unfolding. -/
def digitsAux0 : ℕ → List ℕ
  | 0 => []
  | n + 1 => [n + 1]
#align nat.digits_aux_0 Nat.digitsAux0

/-- (Impl.) An auxiliary definition for `digits`, to help get the desired definitional unfolding. -/
def digitsAux1 (n : ℕ) : List ℕ :=
  List.replicate n 1
#align nat.digits_aux_1 Nat.digitsAux1

/-- (Impl.) An auxiliary definition for `digits`, to help get the desired definitional unfolding. -/
def digitsAux (b : ℕ) (h : 2 ≤ b) : ℕ → List ℕ
  | 0 => []
  | n + 1 =>
    ((n + 1) % b) :: digitsAux b h ((n + 1) / b)
decreasing_by exact Nat.div_lt_self (Nat.succ_pos _) h
#align nat.digits_aux Nat.digitsAux

@[simp]
theorem digitsAux_zero (b : ℕ) (h : 2 ≤ b) : digitsAux b h 0 = [] := by rw [digitsAux]
#align nat.digits_aux_zero Nat.digitsAux_zero

theorem digitsAux_def (b : ℕ) (h : 2 ≤ b) (n : ℕ) (w : 0 < n) :
    digitsAux b h n = (n % b) :: digitsAux b h (n / b) := by
  cases n
  · cases w
  · rw [digitsAux]
#align nat.digits_aux_def Nat.digitsAux_def

/-- `digits b n` gives the digits, in little-endian order,
of a natural number `n` in a specified base `b`.

In any base, we have `ofDigits b L = L.foldr (λ x y, x + b * y) 0`.
* For any `2 ≤ b`, we have `l < b` for any `l ∈ digits b n`,
  and the last digit is not zero.
  This uniquely specifies the behaviour of `digits b`.
* For `b = 1`, we define `digits 1 n = List.replicate n 1`.
* For `b = 0`, we define `digits 0 n = [n]`, except `digits 0 0 = []`.

Note this differs from the existing `Nat.to_digits` in core, which is used for printing numerals.
In particular, `Nat.to_digits b 0 = [0]`, while `digits b 0 = []`.
-/
def digits : ℕ → ℕ → List ℕ
  | 0 => digitsAux0
  | 1 => digitsAux1
  | b + 2 => digitsAux (b + 2) (by norm_num)
#align nat.digits Nat.digits

@[simp]
theorem digits_zero (b : ℕ) : digits b 0 = [] := by
  rcases b with (_ | ⟨_ | ⟨_⟩⟩) <;> simp [digits, digitsAux0, digitsAux1]
#align nat.digits_zero Nat.digits_zero

-- @[simp] -- Porting note: simp can prove this
theorem digits_zero_zero : digits 0 0 = [] :=
  rfl
#align nat.digits_zero_zero Nat.digits_zero_zero

@[simp]
theorem digits_zero_succ (n : ℕ) : digits 0 n.succ = [n + 1] :=
  rfl
#align nat.digits_zero_succ Nat.digits_zero_succ

theorem digits_zero_succ' : ∀ {n : ℕ}, n ≠ 0 → digits 0 n = [n]
  | 0, h => (h rfl).elim
  | _ + 1, _ => rfl
#align nat.digits_zero_succ' Nat.digits_zero_succ'

@[simp]
theorem digits_one (n : ℕ) : digits 1 n = List.replicate n 1 :=
  rfl
#align nat.digits_one Nat.digits_one

-- @[simp] -- Porting note: dsimp can prove this
theorem digits_one_succ (n : ℕ) : digits 1 (n + 1) = 1 :: digits 1 n :=
  rfl
#align nat.digits_one_succ Nat.digits_one_succ

@[simp]
theorem digits_add_two_add_one (b n : ℕ) :
    digits (b + 2) (n + 1) = ((n + 1) % (b + 2)) :: digits (b + 2) ((n + 1) / (b + 2)) := by
  simp [digits, digitsAux_def]
#align nat.digits_add_two_add_one Nat.digits_add_two_add_one

theorem digits_def' :
    ∀ {b : ℕ} (_ : 1 < b) {n : ℕ} (_ : 0 < n), digits b n = (n % b) :: digits b (n / b)
  | 0, h => absurd h (by decide)
  | 1, h => absurd h (by decide)
  | b + 2, _ => digitsAux_def _ (by simp) _
#align nat.digits_def' Nat.digits_def'

@[simp]
theorem digits_of_lt (b x : ℕ) (hx : x ≠ 0) (hxb : x < b) : digits b x = [x] := by
  rcases exists_eq_succ_of_ne_zero hx with ⟨x, rfl⟩
  rcases exists_eq_add_of_le' ((Nat.le_add_left 1 x).trans_lt hxb) with ⟨b, rfl⟩
  rw [digits_add_two_add_one, div_eq_of_lt hxb, digits_zero, mod_eq_of_lt hxb]
#align nat.digits_of_lt Nat.digits_of_lt

theorem digits_add (b : ℕ) (h : 1 < b) (x y : ℕ) (hxb : x < b) (hxy : x ≠ 0 ∨ y ≠ 0) :
    digits b (x + b * y) = x :: digits b y := by
  rcases exists_eq_add_of_le' h with ⟨b, rfl : _ = _ + 2⟩
  cases y
  · simp [hxb, hxy.resolve_right (absurd rfl)]
  dsimp [digits]
  rw [digitsAux_def]
  · congr
    · simp [Nat.add_mod, mod_eq_of_lt hxb]
    · simp [add_mul_div_left, div_eq_of_lt hxb]
  · apply Nat.succ_pos
#align nat.digits_add Nat.digits_add

-- If we had a function converting a list into a polynomial,
-- and appropriate lemmas about that function,
-- we could rewrite this in terms of that.
/-- `ofDigits b L` takes a list `L` of natural numbers, and interprets them
as a number in semiring, as the little-endian digits in base `b`.
-/
def ofDigits {α : Type _} [Semiring α] (b : α) : List ℕ → α
  | [] => 0
  | h :: t => h + b * ofDigits b t
#align nat.of_digits Nat.ofDigits

theorem ofDigits_eq_foldr {α : Type _} [Semiring α] (b : α) (L : List ℕ) :
    ofDigits b L = List.foldr (fun x y => ↑x + b * y) 0 L := by
  induction' L with d L ih
  · rfl
  · dsimp [ofDigits]
    rw [ih]
#align nat.of_digits_eq_foldr Nat.ofDigits_eq_foldr

theorem ofDigits_eq_sum_map_with_index_aux (b : ℕ) (l : List ℕ) :
    ((List.range l.length).zipWith ((fun i a : ℕ => a * b ^ i) ∘ succ) l).sum =
      b * ((List.range l.length).zipWith (fun i a => a * b ^ i) l).sum := by
  suffices
    (List.range l.length).zipWith ((fun i a : ℕ => a * b ^ i) ∘ succ) l =
      (List.range l.length).zipWith (fun i a => b * (a * b ^ i)) l
    by simp [this]
  congr; ext; simp [pow_succ]; ring
#align nat.of_digits_eq_sum_map_with_index_aux Nat.ofDigits_eq_sum_map_with_index_aux

theorem ofDigits_eq_sum_mapIdx (b : ℕ) (L : List ℕ) :
    ofDigits b L = (L.mapIdx fun i a => a * b ^ i).sum := by
  rw [List.mapIdx_eq_enum_map, List.enum_eq_zip_range, List.map_uncurry_zip_eq_zipWith,
    ofDigits_eq_foldr]
  induction' L with hd tl hl
  · simp
  · simpa [List.range_succ_eq_map, List.zipWith_map_left, ofDigits_eq_sum_map_with_index_aux] using
      Or.inl hl
#align nat.of_digits_eq_sum_map_with_index Nat.ofDigits_eq_sum_mapIdx

@[simp]
theorem ofDigits_singleton {b n : ℕ} : ofDigits b [n] = n := by simp [ofDigits]
#align nat.of_digits_singleton Nat.ofDigits_singleton

@[simp]
theorem ofDigits_one_cons {α : Type _} [Semiring α] (h : ℕ) (L : List ℕ) :
    ofDigits (1 : α) (h :: L) = h + ofDigits 1 L := by simp [ofDigits]
#align nat.of_digits_one_cons Nat.ofDigits_one_cons

theorem ofDigits_append {b : ℕ} {l1 l2 : List ℕ} :
    ofDigits b (l1 ++ l2) = ofDigits b l1 + b ^ l1.length * ofDigits b l2 := by
  induction' l1 with hd tl IH
  · simp [ofDigits]
  · rw [ofDigits, List.cons_append, ofDigits, IH, List.length_cons, pow_succ']
    ring
#align nat.of_digits_append Nat.ofDigits_append

@[norm_cast]
theorem coe_ofDigits (α : Type _) [Semiring α] (b : ℕ) (L : List ℕ) :
    ((ofDigits b L : ℕ) : α) = ofDigits (b : α) L := by
  induction' L with d L ih
  · simp [ofDigits]
  · dsimp [ofDigits]; push_cast; rw [ih]
#align nat.coe_of_digits Nat.coe_ofDigits

@[norm_cast]
theorem coe_int_ofDigits (b : ℕ) (L : List ℕ) : ((ofDigits b L : ℕ) : ℤ) = ofDigits (b : ℤ) L := by
  induction' L with d L _
  · rfl
  · dsimp [ofDigits]; push_cast; simp only
#align nat.coe_int_of_digits Nat.coe_int_ofDigits

theorem digits_zero_of_eq_zero {b : ℕ} (h : b ≠ 0) :
    ∀ {L : List ℕ} (_ : ofDigits b L = 0), ∀ l ∈ L, l = 0
  | _ :: _, h0, _, List.Mem.head .. => Nat.eq_zero_of_add_eq_zero_right h0
  | _ :: _, h0, _, List.Mem.tail _ hL =>
    digits_zero_of_eq_zero h (mul_right_injective₀ h (Nat.eq_zero_of_add_eq_zero_left h0)) _ hL
#align nat.digits_zero_of_eq_zero Nat.digits_zero_of_eq_zero

theorem digits_ofDigits (b : ℕ) (h : 1 < b) (L : List ℕ) (w₁ : ∀ l ∈ L, l < b)
    (w₂ : ∀ h : L ≠ [], L.getLast h ≠ 0) : digits b (ofDigits b L) = L := by
  induction' L with d L ih
  · dsimp [ofDigits]
    simp
  · dsimp [ofDigits]
    replace w₂ := w₂ (by simp)
    rw [digits_add b h]
    · rw [ih]
      · intro l m
        apply w₁
        exact List.mem_cons_of_mem _ m
      · intro h
        · rw [List.getLast_cons h] at w₂
          convert w₂
    · exact w₁ d (List.mem_cons_self _ _)
    · by_cases h' : L = []
      · rcases h' with rfl
        left
        simpa using w₂
      · right
        contrapose! w₂
        refine' digits_zero_of_eq_zero h.ne_bot w₂ _ _
        rw [List.getLast_cons h']
        exact List.getLast_mem h'
#align nat.digits_of_digits Nat.digits_ofDigits

theorem ofDigits_digits (b n : ℕ) : ofDigits b (digits b n) = n := by
  cases' b with b
  · cases' n with n
    · rfl
    · change ofDigits 0 [n + 1] = n + 1
      dsimp [ofDigits]
  · cases' b with b
    · induction' n with n ih
      · rfl
      · rw[show succ zero = 1 by rfl] at ih ⊢
        simp only [ih, add_comm 1, ofDigits_one_cons, Nat.cast_id, digits_one_succ]
    · apply Nat.strongInductionOn n _
      clear n
      intro n h
      cases n
      · rw [digits_zero]
        rfl
      · simp only [Nat.succ_eq_add_one, digits_add_two_add_one]
        dsimp [ofDigits]
        rw [h _ (Nat.div_lt_self' _ b)]
        rw [Nat.mod_add_div]
#align nat.of_digits_digits Nat.ofDigits_digits

theorem ofDigits_one (L : List ℕ) : ofDigits 1 L = L.sum := by
  induction' L with _ _ ih
  · rfl
  · simp [ofDigits, List.sum_cons, ih]
#align nat.of_digits_one Nat.ofDigits_one

/-!
### Properties

This section contains various lemmas of properties relating to `digits` and `ofDigits`.
-/

theorem digits_eq_nil_iff_eq_zero {b n : ℕ} : digits b n = [] ↔ n = 0 := by
  constructor
  · intro h
    have : ofDigits b (digits b n) = ofDigits b [] := by rw [h]
    convert this
    rw [ofDigits_digits]
  · rintro rfl
    simp
#align nat.digits_eq_nil_iff_eq_zero Nat.digits_eq_nil_iff_eq_zero

theorem digits_ne_nil_iff_ne_zero {b n : ℕ} : digits b n ≠ [] ↔ n ≠ 0 :=
  not_congr digits_eq_nil_iff_eq_zero
#align nat.digits_ne_nil_iff_ne_zero Nat.digits_ne_nil_iff_ne_zero

theorem digits_eq_cons_digits_div {b n : ℕ} (h : 1 < b) (w : n ≠ 0) :
    digits b n = (n % b) :: digits b (n / b) := by
  rcases b with (_ | _ | b)
  · rw [digits_zero_succ' w, Nat.mod_zero, Nat.div_zero, Nat.digits_zero_zero]
  · norm_num at h
  rcases n with (_ | n)
  · norm_num at w
  . simp only [digits_add_two_add_one, ne_eq]
#align nat.digits_eq_cons_digits_div Nat.digits_eq_cons_digits_div

theorem digits_getLast {b : ℕ} (m : ℕ) (h : 1 < b) (p q) :
    (digits b m).getLast p = (digits b (m / b)).getLast q := by
  by_cases hm : m = 0
  · simp [hm]
  simp only [digits_eq_cons_digits_div h hm]
  rw [List.getLast_cons]
#align nat.digits_last Nat.digits_getLast

theorem digits.injective (b : ℕ) : Function.Injective b.digits :=
  Function.LeftInverse.injective (ofDigits_digits b)
#align nat.digits.injective Nat.digits.injective

@[simp]
theorem digits_inj_iff {b n m : ℕ} : b.digits n = b.digits m ↔ n = m :=
  (digits.injective b).eq_iff
#align nat.digits_inj_iff Nat.digits_inj_iff

theorem digits_len (b n : ℕ) (hb : 1 < b) (hn : n ≠ 0) : (b.digits n).length = b.log n + 1 := by
  induction' n using Nat.strong_induction_on with n IH
  rw [digits_eq_cons_digits_div hb hn, List.length]
  by_cases h : n / b = 0
  · have hb0 : b ≠ 0 := (Nat.succ_le_iff.1 hb).ne_bot
    simp [h, log_eq_zero_iff, ← Nat.div_eq_zero_iff hb0.bot_lt]
  · have : n / b < n := div_lt_self (Nat.pos_of_ne_zero hn) hb
    rw [IH _ this h, log_div_base, tsub_add_cancel_of_le]
    refine' Nat.succ_le_of_lt (log_pos hb _)
    contrapose! h
    exact div_eq_of_lt h
#align nat.digits_len Nat.digits_len

theorem getLast_digit_ne_zero (b : ℕ) {m : ℕ} (hm : m ≠ 0) :
    (digits b m).getLast (digits_ne_nil_iff_ne_zero.mpr hm) ≠ 0 := by
  rcases b with (_ | _ | b)
  · cases m
    · cases hm rfl
    · simp
  · cases m
    · cases hm rfl
    rename ℕ => m
    -- Porting note: Added `have`
    have : ∀ v, List.getLast (digits (succ zero) (succ v)) (by simp [digits, digitsAux1]) = 1 := by
      intros v; induction v <;> simp; assumption
    simp only [digits_one, List.getLast_replicate_succ m 1, this]
  revert hm
  apply Nat.strongInductionOn m
  intro n IH hn
  by_cases hnb : n < b + 2
  · simpa only [digits_of_lt (b + 2) n hn hnb]
  · rw [digits_getLast n (le_add_left 2 b)]
    refine' IH _ (Nat.div_lt_self hn.bot_lt (one_lt_succ_succ b)) _
    · rw [← pos_iff_ne_zero]
      exact Nat.div_pos (le_of_not_lt hnb) (zero_lt_succ (succ b))
#align nat.last_digit_ne_zero Nat.getLast_digit_ne_zero

/-- The digits in the base b+2 expansion of n are all less than b+2 -/
theorem digits_lt_base' {b m : ℕ} : ∀ {d}, d ∈ digits (b + 2) m → d < b + 2 := by
  apply Nat.strongInductionOn m
  intro n IH d hd
  cases' n with n
  · rw [digits_zero] at hd
    cases hd
  -- base b+2 expansion of 0 has no digits
  rw [digits_add_two_add_one] at hd
  cases hd
  · exact n.succ.mod_lt (by simp)
  -- Porting note: Previous code (single line) contained linarith.
  -- . exact IH _ (Nat.div_lt_self (Nat.succ_pos _) (by linarith)) hd
  · apply IH ((n + 1) / (b + 2))
    . apply Nat.div_lt_self <;> simp
    . assumption
#align nat.digits_lt_base' Nat.digits_lt_base'

/-- The digits in the base b expansion of n are all less than b, if b ≥ 2 -/
theorem digits_lt_base {b m d : ℕ} (hb : 1 < b) (hd : d ∈ digits b m) : d < b := by
  rcases b with (_ | _ | b) <;> try simp_all
  exact digits_lt_base' hd
#align nat.digits_lt_base Nat.digits_lt_base

/-- an n-digit number in base b + 2 is less than (b + 2)^n -/
theorem ofDigits_lt_base_pow_length' {b : ℕ} {l : List ℕ} (hl : ∀ x ∈ l, x < b + 2) :
    ofDigits (b + 2) l < (b + 2) ^ l.length := by
  induction' l with hd tl IH
  · simp [ofDigits]
  · rw [ofDigits, List.length_cons, pow_succ]
    have : (ofDigits (b + 2) tl + 1) * (b + 2) ≤ (b + 2) ^ tl.length * (b + 2) :=
      mul_le_mul (IH fun x hx => hl _ (List.mem_cons_of_mem _ hx)) (by rfl) (by simp only [zero_le])
        (Nat.zero_le _)
    suffices ↑hd < b + 2 by linarith
    norm_cast
    exact hl hd (List.mem_cons_self _ _)
#align nat.of_digits_lt_base_pow_length' Nat.ofDigits_lt_base_pow_length'

/-- an n-digit number in base b is less than b^n if b > 1 -/
theorem ofDigits_lt_base_pow_length {b : ℕ} {l : List ℕ} (hb : 1 < b) (hl : ∀ x ∈ l, x < b) :
    ofDigits b l < b ^ l.length := by
  rcases b with (_ | _ | b) <;> try simp_all
  exact ofDigits_lt_base_pow_length' hl
#align nat.of_digits_lt_base_pow_length Nat.ofDigits_lt_base_pow_length

/-- Any number m is less than (b+2)^(number of digits in the base b + 2 representation of m) -/
theorem lt_base_pow_length_digits' {b m : ℕ} : m < (b + 2) ^ (digits (b + 2) m).length := by
  convert @ofDigits_lt_base_pow_length' b (digits (b + 2) m) fun _ => digits_lt_base'
  rw [ofDigits_digits (b + 2) m]
#align nat.lt_base_pow_length_digits' Nat.lt_base_pow_length_digits'

/-- Any number m is less than b^(number of digits in the base b representation of m) -/
theorem lt_base_pow_length_digits {b m : ℕ} (hb : 1 < b) : m < b ^ (digits b m).length := by
  rcases b with (_ | _ | b) <;> try simp_all
  exact lt_base_pow_length_digits'
#align nat.lt_base_pow_length_digits Nat.lt_base_pow_length_digits

theorem ofDigits_digits_append_digits {b m n : ℕ} :
    ofDigits b (digits b n ++ digits b m) = n + b ^ (digits b n).length * m := by
  rw [ofDigits_append, ofDigits_digits, ofDigits_digits]
#align nat.of_digits_digits_append_digits Nat.ofDigits_digits_append_digits

theorem digits_len_le_digits_len_succ (b n : ℕ) : (digits b n).length ≤ (digits b (n + 1)).length :=
  by
  rcases Decidable.eq_or_ne n 0 with (rfl | hn)
  · simp
  cases' le_or_lt b 1 with hb hb
  · interval_cases b <;> simp_arith [digits_zero_succ', hn]
  simpa [digits_len, hb, hn] using log_mono_right (le_succ _)
#align nat.digits_len_le_digits_len_succ Nat.digits_len_le_digits_len_succ

theorem le_digits_len_le (b n m : ℕ) (h : n ≤ m) : (digits b n).length ≤ (digits b m).length :=
  monotone_nat_of_le_succ (digits_len_le_digits_len_succ b) h
#align nat.le_digits_len_le Nat.le_digits_len_le

theorem pow_length_le_mul_ofDigits {b : ℕ} {l : List ℕ} (hl : l ≠ []) (hl2 : l.getLast hl ≠ 0) :
    (b + 2) ^ l.length ≤ (b + 2) * ofDigits (b + 2) l := by
  rw [← List.dropLast_append_getLast hl]
  simp only [List.length_append, List.length, zero_add, List.length_dropLast, ofDigits_append,
    List.length_dropLast, ofDigits_singleton, add_comm (l.length - 1), pow_add, pow_one]
  apply Nat.mul_le_mul_left
  refine' le_trans _ (Nat.le_add_left _ _)
  have : 0 < l.getLast hl := by rwa [pos_iff_ne_zero]
  convert Nat.mul_le_mul_left ((b + 2) ^ (l.length - 1)) this using 1
  rw [Nat.mul_one]
#align nat.pow_length_le_mul_of_digits Nat.pow_length_le_mul_ofDigits

/-- Any non-zero natural number `m` is greater than
(b+2)^((number of digits in the base (b+2) representation of m) - 1)
-/
theorem base_pow_length_digits_le' (b m : ℕ) (hm : m ≠ 0) :
    (b + 2) ^ (digits (b + 2) m).length ≤ (b + 2) * m := by
  have : digits (b + 2) m ≠ [] := digits_ne_nil_iff_ne_zero.mpr hm
  convert @pow_length_le_mul_ofDigits b (digits (b+2) m)
    this (getLast_digit_ne_zero _ hm)
  rw [ofDigits_digits]
#align nat.base_pow_length_digits_le' Nat.base_pow_length_digits_le'

/-- Any non-zero natural number `m` is greater than
b^((number of digits in the base b representation of m) - 1)
-/
theorem base_pow_length_digits_le (b m : ℕ) (hb : 1 < b) :
    m ≠ 0 → b ^ (digits b m).length ≤ b * m := by
  rcases b with (_ | _ | b) <;> try simp_all
  exact base_pow_length_digits_le' b m
#align nat.base_pow_length_digits_le Nat.base_pow_length_digits_le

/-! ### Binary -/

theorem digits_two_eq_bits (n : ℕ) : digits 2 n = n.bits.map fun b => cond b 1 0 := by
  induction' n using Nat.binaryRecFromOne with b n h ih
  · simp
  · simp; trivial
  rw [bits_append_bit _ _ fun hn => absurd hn h]
  cases b
  · rw [digits_def' one_lt_two]
    · simpa [Nat.bit, Nat.bit0_val n]
    · simpa [pos_iff_ne_zero, bit_eq_zero_iff]
  · simpa [Nat.bit, Nat.bit1_val n, add_comm, digits_add 2 one_lt_two 1 n]
#align nat.digits_two_eq_bits Nat.digits_two_eq_bits

/-! ### Modular Arithmetic -/

-- This is really a theorem about polynomials.
theorem dvd_ofDigits_sub_ofDigits {α : Type _} [CommRing α] {a b k : α} (h : k ∣ a - b)
    (L : List ℕ) : k ∣ ofDigits a L - ofDigits b L := by
  induction' L with d L ih
  · change k ∣ 0 - 0
    simp
  · simp only [ofDigits, add_sub_add_left_eq_sub]
    exact dvd_mul_sub_mul h ih
#align nat.dvd_of_digits_sub_of_digits Nat.dvd_ofDigits_sub_ofDigits

theorem ofDigits_modEq' (b b' : ℕ) (k : ℕ) (h : b ≡ b' [MOD k]) (L : List ℕ) :
    ofDigits b L ≡ ofDigits b' L [MOD k] := by
  induction' L with d L ih
  · rfl
  · dsimp [ofDigits]
    dsimp [Nat.ModEq] at *
    conv_lhs => rw [Nat.add_mod, Nat.mul_mod, h, ih]
    conv_rhs => rw [Nat.add_mod, Nat.mul_mod]
#align nat.of_digits_modeq' Nat.ofDigits_modEq'

theorem ofDigits_modEq (b k : ℕ) (L : List ℕ) : ofDigits b L ≡ ofDigits (b % k) L [MOD k] :=
  ofDigits_modEq' b (b % k) k (b.mod_modEq k).symm L
#align nat.of_digits_modeq Nat.ofDigits_modEq

theorem ofDigits_mod (b k : ℕ) (L : List ℕ) : ofDigits b L % k = ofDigits (b % k) L % k :=
  ofDigits_modEq b k L
#align nat.of_digits_mod Nat.ofDigits_mod

theorem ofDigits_zmodeq' (b b' : ℤ) (k : ℕ) (h : b ≡ b' [ZMOD k]) (L : List ℕ) :
    ofDigits b L ≡ ofDigits b' L [ZMOD k] := by
  induction' L with d L ih
  · rfl
  · dsimp [ofDigits]
    dsimp [Int.ModEq] at *
    conv_lhs => rw [Int.add_emod, Int.mul_emod, h, ih]
    conv_rhs => rw [Int.add_emod, Int.mul_emod]
#align nat.of_digits_zmodeq' Nat.ofDigits_zmodeq'

theorem ofDigits_zmodeq (b : ℤ) (k : ℕ) (L : List ℕ) : ofDigits b L ≡ ofDigits (b % k) L [ZMOD k] :=
  ofDigits_zmodeq' b (b % k) k (b.mod_modEq ↑k).symm L
#align nat.of_digits_zmodeq Nat.ofDigits_zmodeq

theorem ofDigits_zmod (b : ℤ) (k : ℕ) (L : List ℕ) : ofDigits b L % k = ofDigits (b % k) L % k :=
  ofDigits_zmodeq b k L
#align nat.of_digits_zmod Nat.ofDigits_zmod

theorem modEq_digits_sum (b b' : ℕ) (h : b' % b = 1) (n : ℕ) : n ≡ (digits b' n).sum [MOD b] := by
  rw [← ofDigits_one]
  conv =>
    congr
    · skip
    · rw [← ofDigits_digits b' n]
  convert ofDigits_modEq b' b (digits b' n)
  exact h.symm
#align nat.modeq_digits_sum Nat.modEq_digits_sum

theorem modEq_three_digits_sum (n : ℕ) : n ≡ (digits 10 n).sum [MOD 3] :=
  modEq_digits_sum 3 10 (by norm_num) n
#align nat.modeq_three_digits_sum Nat.modEq_three_digits_sum

theorem modEq_nine_digits_sum (n : ℕ) : n ≡ (digits 10 n).sum [MOD 9] :=
  modEq_digits_sum 9 10 (by norm_num) n
#align nat.modeq_nine_digits_sum Nat.modEq_nine_digits_sum

theorem zmodeq_ofDigits_digits (b b' : ℕ) (c : ℤ) (h : b' ≡ c [ZMOD b]) (n : ℕ) :
    n ≡ ofDigits c (digits b' n) [ZMOD b] := by
  conv =>
    congr
    · skip
    · rw [← ofDigits_digits b' n]
  rw [coe_int_ofDigits]
  apply ofDigits_zmodeq' _ _ _ h
#align nat.zmodeq_of_digits_digits Nat.zmodeq_ofDigits_digits

theorem ofDigits_neg_one :
    ∀ L : List ℕ, ofDigits (-1 : ℤ) L = (L.map fun n : ℕ => (n : ℤ)).alternatingSum
  | [] => rfl
  | [n] => by simp [ofDigits, List.alternatingSum]
  | a :: b :: t =>
    by
    simp only [ofDigits, List.alternatingSum, List.map_cons, ofDigits_neg_one t]
    ring
#align nat.of_digits_neg_one Nat.ofDigits_neg_one

theorem modEq_eleven_digits_sum (n : ℕ) :
    n ≡ ((digits 10 n).map fun n : ℕ => (n : ℤ)).alternatingSum [ZMOD 11] := by
  have t := zmodeq_ofDigits_digits 11 10 (-1 : ℤ) (by unfold Int.ModEq; norm_num) n
  rwa [ofDigits_neg_one] at t
#align nat.modeq_eleven_digits_sum Nat.modEq_eleven_digits_sum

/-! ## Divisibility  -/

theorem dvd_iff_dvd_digits_sum (b b' : ℕ) (h : b' % b = 1) (n : ℕ) :
    b ∣ n ↔ b ∣ (digits b' n).sum := by
  rw [← ofDigits_one]
  conv_lhs => rw [← ofDigits_digits b' n]
  rw [Nat.dvd_iff_mod_eq_zero, Nat.dvd_iff_mod_eq_zero, ofDigits_mod, h]
#align nat.dvd_iff_dvd_digits_sum Nat.dvd_iff_dvd_digits_sum

/-- **Divisibility by 3 Rule** -/
theorem three_dvd_iff (n : ℕ) : 3 ∣ n ↔ 3 ∣ (digits 10 n).sum :=
  dvd_iff_dvd_digits_sum 3 10 (by norm_num) n
#align nat.three_dvd_iff Nat.three_dvd_iff

theorem nine_dvd_iff (n : ℕ) : 9 ∣ n ↔ 9 ∣ (digits 10 n).sum :=
  dvd_iff_dvd_digits_sum 9 10 (by norm_num) n
#align nat.nine_dvd_iff Nat.nine_dvd_iff

theorem eleven_dvd_iff :
    11 ∣ n ↔ (11 : ℤ) ∣ ((digits 10 n).map fun n : ℕ => (n : ℤ)).alternatingSum := by
  have t := dvd_iff_dvd_ofDigits 11 10 (-1 : ℤ) (by norm_num) n
  rw [ofDigits_neg_one] at t
  exact t
#align nat.eleven_dvd_iff Nat.eleven_dvd_iff

theorem eleven_dvd_of_palindrome (p : (digits 10 n).Palindrome) (h : Even (digits 10 n).length) :
    11 ∣ n := by
  let dig := (digits 10 n).map (Coe.coe : ℕ → ℤ)
  replace h : Even dig.length := by rwa [List.length_map]
  refine' eleven_dvd_iff.2 ⟨0, (_ : dig.alternatingSum = 0)⟩
  have := dig.alternatingSum_reverse
  rw [(p.map _).reverse_eq, _root_.pow_succ, h.neg_one_pow, mul_one, neg_one_zsmul] at this
  exact eq_zero_of_neg_eq this.symm
#align nat.eleven_dvd_of_palindrome Nat.eleven_dvd_of_palindrome

/-! ### `norm_digits` tactic -/

namespace NormDigits

theorem digits_succ (b n m r l) (e : r + b * m = n) (hr : r < b)
    (h : Nat.digits b m = l ∧ 1 < b ∧ 0 < m) : (Nat.digits b n = r :: l) ∧ 1 < b ∧ 0 < n := by
  rcases h with ⟨h, b2, m0⟩
  -- Porting note: Below line was proved by `by linarith`
  have b0 : 0 < b := by simp_arith [lt_iff_le_and_ne.mp b2]
  -- Porting note: Below line was proved by `by linarith [mul_pos b0 m0]`
  have n0 : 0 < n := le_trans (lt_iff_add_one_le.mp (mul_pos b0 m0)) (e.subst (le_add_left _ r))
  refine' ⟨_, b2, n0⟩
  obtain ⟨rfl, rfl⟩ := (Nat.div_mod_unique b0).2 ⟨e, hr⟩
  subst h; exact Nat.digits_def' b2 n0
#align nat.norm_digits.digits_succ Nat.NormDigits.digits_succ

theorem digits_one (b n) (n0 : 0 < n) (nb : n < b) : Nat.digits b n = [n] ∧ 1 < b ∧ 0 < n := by
  have b2 : 1 < b :=
    lt_iff_add_one_le.mpr (le_trans (add_le_add_right (lt_iff_add_one_le.mp n0) 1) nb)
  refine' ⟨_, b2, n0⟩
  rw [Nat.digits_def' b2 n0, Nat.mod_eq_of_lt nb,
    (Nat.div_eq_zero_iff ((zero_le n).trans_lt nb)).2 nb, Nat.digits_zero]
#align nat.norm_digits.digits_one Nat.NormDigits.digits_one

/-
Porting note: this part of the file is tactic related.

open Tactic
-- failed to format: unknown constant 'term.pseudo.antiquot'
/-- Helper function for the `norm_digits` tactic. -/ unsafe
  def
    eval_aux
    ( eb : expr ) ( b : ℕ ) : expr → ℕ → instance_cache → tactic ( instance_cache × expr × expr )
    |
      en , n , ic
      =>
      do
        let m := n / b
          let r := n % b
          let ( ic , er ) ← ic . ofNat r
          let ( ic , pr ) ← norm_num.prove_lt_nat ic er eb
          if
            m = 0
            then
            do
              let ( _ , pn0 ) ← norm_num.prove_pos ic en
                return
                  (
                    ic
                      ,
                      q( ( [ $ ( en ) ] : List Nat ) )
                        ,
                        q( digits_one $ ( eb ) $ ( en ) $ ( pn0 ) $ ( pr ) )
                    )
            else
            do
              let em ← expr.of_nat q( ℕ ) m
                let ( _ , pe ) ← norm_num.derive q( ( $ ( er ) + $ ( eb ) * $ ( em ) : ℕ ) )
                let ( ic , el , p ) ← eval_aux em m ic
                return
                  (
                    ic
                      ,
                      q( @ List.cons ℕ $ ( er ) $ ( el ) )
                        ,
                        q(
                          digits_succ
                            $ ( eb ) $ ( en ) $ ( em ) $ ( er ) $ ( el ) $ ( pe ) $ ( pr ) $ ( p )
                          )
                    )
#align nat.norm_digits.eval_aux Nat.NormDigits.eval_aux

/-- A tactic for normalizing expressions of the form `Nat.digits a b = l` where
`a` and `b` are numerals.

```
example : nat.digits 10 123 = [3,2,1] := by norm_num
```
-/
@[norm_num]
unsafe def eval : expr → tactic (expr × expr)
  | q(Nat.digits $(eb) $(en)) => do
    let b ← expr.to_nat eb
    let n ← expr.to_nat en
    if n = 0 then return (q(([] : List ℕ)), q(Nat.digits_zero $(eb)))
      else
        if b = 0 then do
          let ic ← mk_instance_cache q(ℕ)
          let (_, pn0) ← norm_num.prove_ne_zero' ic en
          return (q(([$(en)] : List ℕ)), q(@Nat.digits_zero_succ' $(en) $(pn0)))
        else
          if b = 1 then do
            let ic ← mk_instance_cache q(ℕ)
            let s ← simp_lemmas.add_simp simp_lemmas.mk `list.replicate
            let (rhs, p2, _) ← simplify s [] q(List.replicate $(en) 1)
            let p ← mk_eq_trans q(Nat.digits_one $(en)) p2
            return (rhs, p)
          else do
            let ic ← mk_instance_cache q(ℕ)
            let (_, l, p) ← eval_aux eb b en n ic
            let p ← mk_app `` And.left [p]
            return (l, p)
  | _ => failed
#align nat.norm_digits.eval Nat.NormDigits.eval
-/

end NormDigits

end Nat