import Mathlib.Tactic.Congr!
import Std.Tactic.GuardExpr
import Mathlib.Algebra.Group.Basic
import Mathlib.Data.Subtype
import Mathlib.Data.List.Defs

theorem ex1 (a b c : Nat) (h : a = b) : a + c = b + c := by
  congr!

theorem ex2 (a b : Nat) (h : a = b) : ∀ c, a + c = b + c := by
  congr!

theorem ex3 (a b : Nat) (h : a = b) : (fun c => a + c) = (fun c => b + c) := by
  congr!

theorem ex4 (a b : Nat) : Fin (a + b) = Fin (b + a) := by
  congr! 1
  guard_target = a + b = b + a
  apply Nat.add_comm

theorem ex5 : ((a : Nat) → Fin (a + 1)) = ((a : Nat) → Fin (1 + a)) := by
  congr!  2
  rename_i a
  guard_target = a + 1 = 1 + a
  apply Nat.add_comm

theorem ex6 : ((a : Nat) × Fin (a + 1)) = ((a : Nat) × Fin (1 + a)) := by
  congr! 3
  rename_i a
  guard_target = a + 1 = 1 + a
  apply Nat.add_comm

theorem ex7 (p : Prop) (h1 h2 : p) : h1 = h2 := by
  congr!

theorem ex8 (p q : Prop) (h1 : p) (h2 : q) : HEq h1 h2 := by
  congr!

theorem ex9 (a b : Nat) (h : a = b) : a + 1 ≤ b + 1 := by
  congr!

theorem ex10 (x y : Unit) : x = y := by
  congr!

theorem ex11 (p q r : Nat → Prop) (h : q = r) : (∀ n, p n → q n) ↔ (∀ n, p n → r n) := by
  congr!

theorem ex12 (p q : Prop) (h : p ↔ q) : p = q := by
  congr!

theorem ex13 (x y : α) (h : x = y) (f : α → Nat) : f x = f y := by
  congr!

theorem ex14 {α : Type} (f : Nat → Nat) (h : ∀ x, f x = 0) (z : α) (hz : HEq z 0) :
    HEq f (fun (_ : α) => z) := by
  congr!
  · guard_target = Nat = α
    exact type_eq_of_heq hz.symm
  next n x _ =>
    guard_target = HEq (f n) z
    rw [h]
    exact hz.symm

/-- Generating type equalities is OK if it's possible they're the same type. -/
example (s t : Set α) : (ℕ × Subtype s) = (ℕ × Subtype t) := by
  congr! 1
  guard_target = Subtype s = Subtype t
  congr! 1
  guard_target = s = t
  sorry

/-- Generating type equalities is not OK if they're not likely to be the same type. -/
example (s : Set α) (t : Set β) : (ℕ × Subtype s) = (ℕ × Subtype t) := by
  congr!
  guard_target = (ℕ × Subtype s) = (ℕ × Subtype t)
  sorry

/-- Congruence here is OK since `Fin m = Fin n` is plausible to prove. -/
example (m n : Nat) (h : m = n) (x : Fin m) (y : Fin n) : HEq (x + x) (y + y) := by
  congr!
  guard_target = HEq x y
  sorry
  guard_target = HEq x y
  sorry

/-- Props are types, but prop equalities are totally plausible. -/
example (p q r : Prop) : p ∧ q ↔ p ∧ r := by
  congr!
  guard_target = q ↔ r
  sorry

/-- Congruence here is not OK by default since `α = β` is not generally plausible. -/
example (α β) [inst1 : Add α] [inst2 : Add β] (x : α) (y : β) : HEq (x + x) (y + y) := by
  congr!
  guard_target = HEq (x + x) (y + y)
  -- But with typeEqs we can get it to generate the congruence anyway:
  have : α = β := sorry
  have : HEq inst1 inst2 := sorry
  congr! (config := { typeEqs := true })
  guard_target = HEq x y
  sorry
  guard_target = HEq x y
  sorry

example (prime : Nat → Prop) (n : Nat) :
    prime (2 * n + 1) = prime (n + n + 1) := by
  congr!
  · guard_target = (HMul.hMul : Nat → Nat → Nat) = HAdd.hAdd
    sorry
  · guard_target = 2 = n
    sorry

example (prime : Nat → Prop) (n : Nat) :
    prime (2 * n + 1) = prime (n + n + 1) := by
  congr! 2
  guard_target = 2 * n = n + n
  sorry

example (prime : Nat → Prop) (n : Nat) :
    prime (2 * n + 1) = prime (n + n + 1) := by
  congr! (config := .unfoldSameFun)
  guard_target = 2 * n = n + n
  sorry

opaque partiallyApplied (p : Prop) [Decidable p] : Nat → Nat

-- Partially applied dependent functions
example : partiallyApplied (True ∧ True) = partiallyApplied True := by
  congr!
  decide

inductive walk (α : Type) : α → α → Type
| nil (n : α) : walk α n n

def walk.map (f : α → β) (w : walk α x y) : walk β (f x) (f y) :=
  match x, y, w with
  | _, _, .nil n => .nil (f n)

example (w : walk α x y) (w' : walk α x' y') (f : α → β) : HEq (w.map f) (w'.map f) := by
  congr!
  guard_target = x = x'
  sorry
  guard_target = y = y'
  sorry
  -- get x = y and y = y' in context for `HEq w w'` goal.
  have : x = x' := by assumption
  have : y = y' := by assumption
  sorry

def MySet (α : Type _) := α → Prop
def MySet.image (f : α → β) (s : MySet α) : MySet β := fun y => ∃ x, s x ∧ f x = y

-- Testing for equality between what are technically partially applied functions
example (s t : MySet α) (f g : α → β) (h1 : s = t) (h2 : f = g) :
    MySet.image f s = MySet.image g t := by
  congr!

example (c : Prop → Prop → Prop → Prop) (x x' y z z' : Prop)
    (h₀ : x ↔ x') (h₁ : z ↔ z') : c x y z ↔ c x' y z' := by
  congr!

example {α β γ δ} {F : ∀{α β}, (α → β) → γ → δ} {f g : α → β} {s : γ} (h : ∀ (x : α), f x = g x) :
    F f s = F g s := by
  congr!
  funext
  apply h

example {α β} {f : _ → β} {x y : {x : {x : α // x = x} // x = x} } (h : x.1 = y.1) :
    f x = f y := by
  congr! 1
  ext1
  exact h

example {α β} {F : _ → β} {f g : {f : α → β // f = f} }
    (h : ∀ x : α, (f : α → β) x = (g : α → β) x) :
    F f = F g := by
  congr!
  ext x
  apply h

example {ls : List ℕ} :
    ls.map (fun x => (ls.map (fun y => 1 + y)).sum + 1) =
      ls.map (fun x => (ls.map (fun y => Nat.succ y)).sum + 1) := by
  congr! 6
  rename_i y
  guard_target = 1 + y = y.succ
  rw [Nat.add_comm]

example {ls : List ℕ} {f g : ℕ → ℕ} {h : ∀ x, f x = g x} :
    ls.map (fun x => f x + 3) = ls.map (fun x => g x + 3) := by
  congr! 3 -- it's a little too powerful and will get to `f = g`
  rename_i x
  exact h x

-- succeed when either `ext` or `congr` can close the goal
example : () = () := by congr!
example : 0 = 0 := by congr!

example {α} (a : α) : a = a := by congr!

example {α} (a b : α) (h : false) : a = b := by
  fail_if_success { congr! }
  cases h

def g (x : Nat) : Nat := x + 1

example (x y z : Nat) (h : x = z) (hy : y = 2) : 1 + x + y = g z + 2 := by
  congr!
  guard_target = HAdd.hAdd 1 = g
  funext
  simp [g, Nat.add_comm]

example (Fintype : Type → Type)
    (α β : Type) (inst : Fintype α) (inst' : Fintype β) : HEq inst inst' := by
  congr!
  guard_target = HEq inst inst'
  sorry

example (Fintype : Type → Type) [∀ γ, Subsingleton (Fintype γ)]
    (α β : Type) (inst : Fintype α) (inst' : Fintype β) : HEq inst inst' := by
  congr!
  guard_target = Fintype α = Fintype β
  congr! (config := { typeEqs := true })
  guard_target = α = β
  sorry


inductive BinOp where
  | plus
  | times

inductive Expr where
  | const : Nat → Expr
  | binOp : BinOp → Expr → Expr → Expr

def BinOp.denote (op : BinOp) : Nat → Nat → Nat :=
  match op with
  | plus  => Nat.add
  | times => Nat.mul

def Expr.denote (e : Expr) : Nat :=
  match e with
  | const v      => v
  | binOp op a b => op.denote a.denote b.denote

#eval Expr.denote (Expr.const 42)
#eval Expr.denote (.const 42)
#eval Expr.denote <| .const 42
#eval Expr.denote <| .binOp .plus (.const 2) (.const 3)
#eval Expr.denote <| .binOp .times (.const 2) (.const 3)

#eval (.binOp .times (.binOp .plus (.const 4) (.const 2)) (.const 3) : Expr).denote

def e₁ : Expr := .binOp .times (.binOp .plus (.const 4) (.const 2)) (.const 3)

example : e₁.denote = 18 :=
  rfl

inductive Instr where
  | const : Nat → Instr
  | binOp : BinOp → Instr

abbrev Prog := List Instr
abbrev Stack := List Nat

def Instr.denote (i : Instr) (s : Stack) : Option Stack :=
  match i, s with
  | const v, s         => some (v :: s)
  | binOp op, a::b::s' => some (op.denote a b :: s')
  | _, _               => none

def Prog.denote (p : Prog) (s : Stack) : Option Stack :=
  match p with
  | [] => some s
  | i :: (p' : Prog) =>
    match i.denote s with
    | none    => none
    | some s' => p'.denote s'

def Expr.compile (e : Expr) : Prog :=
  match e with
  | const v      => [.const v]
  | binOp op a b => b.compile ++ a.compile ++ [.binOp op]

theorem compile_correct' (e : Expr) (p : Prog) (s : Stack) : (e.compile ++ p : Prog).denote s = p.denote (e.denote :: s) := by
  induction e generalizing p s with
  | const => rfl
  | binOp => simp [Expr.compile, Expr.denote, Prog.denote, Instr.denote, List.append_assoc, *]

theorem compile_correct (e : Expr) : e.compile.denote [] = some [e.denote] := by
  have := compile_correct' e [] []
  simp [Prog.denote] at this
  assumption


/-
The cases tactic applies the elimination
rules to a term, appropriate to it's type.

For example, if h is a proof of P ∧ Q, the
cases tactic will "eliminate" h to a proof
of p and proof of q; and that will be the
only case. Why? Because there's only one
way that the proof, h, could have been
constructed, namely by the application of
and.intro to two arguments, a proof of P
and a proof of Q.
-/

variables P Q R : Prop

/-
You *could* apply the elimination rules
"manually."
-/
example : P ∧ Q → P :=
begin
assume h,
let p : P := and.elim_left h,
exact p,
end

/-
Or you could just use the cases tactic
-/
example : P ∧ Q → P :=
begin
assume h,
cases h,
exact h_left,
end

/-
If you don't specify names for the
results of the elimination, Lean will
make up names for you. It's better to
provide more meaningful names. It can
make a big difference in being able
to think about what you've got.
-/
example : P ∧ Q → P :=
begin
assume h,
cases h with p q, -- *with p q*
exact p,
end

/-
Now suppose you've got a proof of a type
for which there are two introduction rules.
There are now *two ways* that an assumed
proof could have been constructed. What you
generally want to know is that no matter
how the proof was constructive, the goal
remains true. To prove that, you need to
consider each *case*.

Consider a proof of P ∨ Q. There are two
introduction rules: or.intro_left Q p and
or.intro_right P q. Note that if Lean is
able to infer the P and Q arguments, you
can use the simpler or.inl p and or.inr q
constructors.
-/

example : P ∨ Q → R :=
begin
assume h,
cases h with p q,
/-
What remain are two subgoals, one
for each case. If the proof, h, of
P ∨ Q was constructed using or.inl,
then you need to show that given P,
R follows (basically P → R); and if
P ∨ Q was proven using or.inr q, you
need to complete the proof of Q → R.
(Given the assumed proof of Q, show
that R is true by provind a proof of
it.)
-/
end

/-
If we want to reason "classically"
rather than constructively, then we
can use the classical.em rule to
convert and proposition, P, into a
proof of P ∨ ¬P, *on which we can
then do case analysis*.
-/

example : ¬¬P → P :=
begin
assume nnp,
cases (classical.em P) with p np, -- here!
-- make sure you can finish this proof!
end

/-
How is a proof of P ↔ Q constructed? It's by
applying iff.intro to a proof of P → Q and to
a proof of Q → P. That's the only way to do
it. When we apply case analysis to a proof of
P ↔ Q we should thus expect one case, where
iff.intro was applied to two assumed argments
of the right types. That's just what we get.
-/

example : (P ↔ Q) → (Q → P) :=
begin
assume h,
cases h with pq qp,
assumption, -- uses proof in context
end

/-
As an aside, we can even do case analysis
on data values. The bool type, for example,
has two values.
-/

def my_not : bool → bool :=
begin
assume b,
cases b,
exact tt, -- in case b = tt, return ff
exact ff, -- in case b = ff, return tt
end

#eval (my_not tt)
#eval (my_not ff)

-- Whoa: we defined a function by cases!

/-
Finally, case analysis works only on data
values, including proof values. It can't
be applied to functions. In the following
example, we assume we have a function that
takes a nat and returns a nat, along with
a nat. We'll first show how you can "prove
this type" by giving a particular function
value of this type; then we'll get to the
main point, which is to show that you will
get an error if you try to do case analysis
on the function argument.
-/

example : (ℕ → ℕ) → ℕ → ℕ :=
begin
assume f n,
exact (f n),
end

/-
What you have just proved is that if
f takes and returns a nat, and n is
a nat, then f applied to n is a nat.
What happens if we try "cases f"? It's
a no-go.
-/

example : (ℕ → ℕ) → ℕ → ℕ :=
begin
assume f n,
cases f,
/-
Error: "cases tactic failed, it is not
applicable to the given hypothesis." You
can't do case analysis on a function, or
on a proof of an implication or universal
generalization, because these things are
basically functions.
-/
end

example : (P → Q) → P → Q :=
begin
assume i p,
exact (i p),
end

example : (P → Q) → P → Q :=
begin
assume i p,
cases i,      -- Nope
end

/-
Similarly you can't do case analysis
on a data *type* (such as bool) or on
a proposition (a logical *type*). These
things aren't data values, so "cases"
does not work. Independent of Lean,
you can't do "case analysis" on things
of these kind in predicate logic.
-/

variables x y : nat
example : (x = y → 0 = 1) → ¬(x=y) :=
begin
assume h,
show x = y → false,
assume xy,
let zo := h xy,
cases zo,
end


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

/-!
# Neighborhoods and continuity relative to a subset

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

This file defines relative versions

* `nhds_within`           of `nhds`
* `continuous_on`         of `continuous`
* `continuous_within_at`  of `continuous_at`

and proves their basic properties, including the relationships between
these restricted notions and the corresponding notions for the subtype
equipped with the subspace topology.

## Notation

* `𝓝 x`: the filter of neighborhoods of a point `x`;
* `𝓟 s`: the principal filter of a set `s`;
* `𝓝[s] x`: the filter `nhds_within x s` of neighborhoods of a point `x` within a set `s`.

-/

open set filter function
open_locale topology filter

variables {α : Type*} {β : Type*} {γ : Type*} {δ : Type*}
variables [topological_space α]

@[simp] lemma nhds_bind_nhds_within {a : α} {s : set α} :
  (𝓝 a).bind (λ x, 𝓝[s] x) = 𝓝[s] a :=
bind_inf_principal.trans $ congr_arg2 _ nhds_bind_nhds rfl

@[simp] lemma eventually_nhds_nhds_within {a : α} {s : set α} {p : α → Prop} :
  (∀ᶠ y in 𝓝 a, ∀ᶠ x in 𝓝[s] y, p x) ↔ ∀ᶠ x in 𝓝[s] a, p x :=
filter.ext_iff.1 nhds_bind_nhds_within {x | p x}

lemma eventually_nhds_within_iff {a : α} {s : set α} {p : α → Prop} :
  (∀ᶠ x in 𝓝[s] a, p x) ↔ ∀ᶠ x in 𝓝 a, x ∈ s → p x :=
eventually_inf_principal

lemma frequently_nhds_within_iff {z : α} {s : set α} {p : α → Prop} :
  (∃ᶠ x in 𝓝[s] z, p x) ↔ (∃ᶠ x in 𝓝 z, p x ∧ x ∈ s) :=
iff.not (by simp [eventually_nhds_within_iff, not_and'])

lemma mem_closure_ne_iff_frequently_within {z : α} {s : set α} :
  z ∈ closure (s \ {z}) ↔ ∃ᶠ x in 𝓝[≠] z, x ∈ s :=
by simp [mem_closure_iff_frequently, frequently_nhds_within_iff]

@[simp] lemma eventually_nhds_within_nhds_within {a : α} {s : set α} {p : α → Prop} :
  (∀ᶠ y in 𝓝[s] a, ∀ᶠ x in 𝓝[s] y, p x) ↔ ∀ᶠ x in 𝓝[s] a, p x :=
begin
  refine ⟨λ h, _, λ h, (eventually_nhds_nhds_within.2 h).filter_mono inf_le_left⟩,
  simp only [eventually_nhds_within_iff] at h ⊢,
  exact h.mono (λ x hx hxs, (hx hxs).self_of_nhds hxs)
end

theorem nhds_within_eq (a : α) (s : set α) :
  𝓝[s] a = ⨅ t ∈ {t : set α | a ∈ t ∧ is_open t}, 𝓟 (t ∩ s) :=
((nhds_basis_opens a).inf_principal s).eq_binfi

theorem nhds_within_univ (a : α) : 𝓝[set.univ] a = 𝓝 a :=
by rw [nhds_within, principal_univ, inf_top_eq]

lemma nhds_within_has_basis {p : β → Prop} {s : β → set α} {a : α} (h : (𝓝 a).has_basis p s)
  (t : set α) :
  (𝓝[t] a).has_basis p (λ i, s i ∩ t) :=
h.inf_principal t

lemma nhds_within_basis_open (a : α) (t : set α) :
  (𝓝[t] a).has_basis (λ u, a ∈ u ∧ is_open u) (λ u, u ∩ t) :=
nhds_within_has_basis (nhds_basis_opens a) t

theorem mem_nhds_within {t : set α} {a : α} {s : set α} :
  t ∈ 𝓝[s] a ↔ ∃ u, is_open u ∧ a ∈ u ∧ u ∩ s ⊆ t  :=
by simpa only [exists_prop, and_assoc, and_comm] using (nhds_within_basis_open a s).mem_iff

lemma mem_nhds_within_iff_exists_mem_nhds_inter {t : set α} {a : α} {s : set α} :
  t ∈ 𝓝[s] a ↔ ∃ u ∈ 𝓝 a, u ∩ s ⊆ t :=
(nhds_within_has_basis (𝓝 a).basis_sets s).mem_iff

lemma diff_mem_nhds_within_compl {x : α} {s : set α} (hs : s ∈ 𝓝 x) (t : set α) :
  s \ t ∈ 𝓝[tᶜ] x :=
diff_mem_inf_principal_compl hs t

lemma diff_mem_nhds_within_diff {x : α} {s t : set α} (hs : s ∈ 𝓝[t] x) (t' : set α) :
  s \ t' ∈ 𝓝[t \ t'] x :=
begin
  rw [nhds_within, diff_eq, diff_eq, ← inf_principal, ← inf_assoc],
  exact inter_mem_inf hs (mem_principal_self _)
end

lemma nhds_of_nhds_within_of_nhds
  {s t : set α} {a : α} (h1 : s ∈ 𝓝 a) (h2 : t ∈ 𝓝[s] a) : (t ∈ 𝓝 a) :=
begin
  rcases mem_nhds_within_iff_exists_mem_nhds_inter.mp h2 with ⟨_, Hw, hw⟩,
  exact (nhds a).sets_of_superset ((nhds a).inter_sets Hw h1) hw,
end

lemma mem_nhds_within_iff_eventually {s t : set α} {x : α} :
  t ∈ 𝓝[s] x ↔ ∀ᶠ y in 𝓝 x, y ∈ s → y ∈ t :=
begin
  rw [mem_nhds_within_iff_exists_mem_nhds_inter],
  split,
  { rintro ⟨u, hu, hut⟩, exact eventually_of_mem hu (λ x hxu hxs, hut ⟨hxu, hxs⟩) },
  { refine λ h, ⟨_, h, λ y hy, hy.1 hy.2⟩ }
end

lemma mem_nhds_within_iff_eventually_eq {s t : set α} {x : α} :
  t ∈ 𝓝[s] x ↔ s =ᶠ[𝓝 x] (s ∩ t : set α) :=
by simp_rw [mem_nhds_within_iff_eventually, eventually_eq_set, mem_inter_iff, iff_self_and]

lemma nhds_within_eq_iff_eventually_eq {s t : set α} {x : α} : 𝓝[s] x = 𝓝[t] x ↔ s =ᶠ[𝓝 x] t :=
begin
  simp_rw [filter.ext_iff, mem_nhds_within_iff_eventually, eventually_eq_set],
  split,
  { intro h,
    filter_upwards [(h t).mpr (eventually_of_forall $ λ x, id),
      (h s).mp (eventually_of_forall $ λ x, id)],
    exact λ x, iff.intro, },
  { refine λ h u, eventually_congr (h.mono $ λ x h, _), rw [h] }
end

lemma nhds_within_le_iff {s t : set α} {x : α} : 𝓝[s] x ≤ 𝓝[t] x ↔ t ∈ 𝓝[s] x :=
begin
  simp_rw [filter.le_def, mem_nhds_within_iff_eventually],
  split,
  { exact λ h, (h t $ eventually_of_forall (λ x, id)).mono (λ x, id) },
  { exact λ h u hu, (h.and hu).mono (λ x hx h, hx.2 $ hx.1 h) }
end

lemma preimage_nhds_within_coinduced' {π : α → β} {s : set β} {t : set α} {a : α}
  (h : a ∈ t) (ht : is_open t)
  (hs : s ∈ @nhds β (topological_space.coinduced (λ x : t, π x) subtype.topological_space) (π a)) :
  π ⁻¹' s ∈ 𝓝[t] a :=
begin
  letI := topological_space.coinduced (λ x : t, π x) subtype.topological_space,
  rcases mem_nhds_iff.mp hs with ⟨V, hVs, V_op, mem_V⟩,
  refine mem_nhds_within_iff_exists_mem_nhds_inter.mpr ⟨π ⁻¹' V, mem_nhds_iff.mpr ⟨t ∩ π ⁻¹' V,
    inter_subset_right t (π ⁻¹' V), _, mem_sep h mem_V⟩, subset.trans (inter_subset_left _ _)
    (preimage_mono hVs)⟩,
  obtain ⟨u, hu1, hu2⟩ := is_open_induced_iff.mp (is_open_coinduced.1 V_op),
  rw [preimage_comp] at hu2,
  rw [set.inter_comm, ←(subtype.preimage_coe_eq_preimage_coe_iff.mp hu2)],
  exact hu1.inter ht,
end

lemma mem_nhds_within_of_mem_nhds {s t : set α} {a : α} (h : s ∈ 𝓝 a) :
  s ∈ 𝓝[t] a :=
mem_inf_of_left h

theorem self_mem_nhds_within {a : α} {s : set α} : s ∈ 𝓝[s] a :=
mem_inf_of_right (mem_principal_self s)

theorem eventually_mem_nhds_within {a : α} {s : set α} : ∀ᶠ x in 𝓝[s] a, x ∈ s :=
self_mem_nhds_within

theorem inter_mem_nhds_within (s : set α) {t : set α} {a : α} (h : t ∈ 𝓝 a) :
  s ∩ t ∈ 𝓝[s] a :=
inter_mem self_mem_nhds_within (mem_inf_of_left h)

theorem nhds_within_mono (a : α) {s t : set α} (h : s ⊆ t) : 𝓝[s] a ≤ 𝓝[t] a :=
inf_le_inf_left _ (principal_mono.mpr h)

lemma pure_le_nhds_within {a : α} {s : set α} (ha : a ∈ s) : pure a ≤ 𝓝[s] a :=
le_inf (pure_le_nhds a) (le_principal_iff.2 ha)

lemma mem_of_mem_nhds_within {a : α} {s t : set α} (ha : a ∈ s) (ht : t ∈ 𝓝[s] a) :
  a ∈ t :=
pure_le_nhds_within ha ht

lemma filter.eventually.self_of_nhds_within {p : α → Prop} {s : set α} {x : α}
  (h : ∀ᶠ y in 𝓝[s] x, p y) (hx : x ∈ s) : p x :=
mem_of_mem_nhds_within hx h

lemma tendsto_const_nhds_within {l : filter β} {s : set α} {a : α} (ha : a ∈ s) :
  tendsto (λ x : β, a) l (𝓝[s] a) :=
tendsto_const_pure.mono_right $ pure_le_nhds_within ha

theorem nhds_within_restrict'' {a : α} (s : set α) {t : set α} (h : t ∈ 𝓝[s] a) :
  𝓝[s] a = 𝓝[s ∩ t] a :=
le_antisymm
  (le_inf inf_le_left (le_principal_iff.mpr (inter_mem self_mem_nhds_within h)))
  (inf_le_inf_left _ (principal_mono.mpr (set.inter_subset_left _ _)))

theorem nhds_within_restrict' {a : α} (s : set α) {t : set α} (h : t ∈ 𝓝 a) :
  𝓝[s] a = 𝓝[s ∩ t] a :=
nhds_within_restrict'' s $ mem_inf_of_left h

theorem nhds_within_restrict {a : α} (s : set α) {t : set α} (h₀ : a ∈ t) (h₁ : is_open t) :
  𝓝[s] a = 𝓝[s ∩ t] a :=
nhds_within_restrict' s (is_open.mem_nhds h₁ h₀)

theorem nhds_within_le_of_mem {a : α} {s t : set α} (h : s ∈ 𝓝[t] a) :
  𝓝[t] a ≤ 𝓝[s] a :=
nhds_within_le_iff.mpr h

theorem nhds_within_le_nhds {a : α} {s : set α} : 𝓝[s] a ≤ 𝓝 a :=
by { rw ← nhds_within_univ, apply nhds_within_le_of_mem, exact univ_mem }

lemma nhds_within_eq_nhds_within' {a : α} {s t u : set α}
  (hs : s ∈ 𝓝 a) (h₂ : t ∩ s = u ∩ s) : 𝓝[t] a = 𝓝[u] a :=
by rw [nhds_within_restrict' t hs, nhds_within_restrict' u hs, h₂]

theorem nhds_within_eq_nhds_within {a : α} {s t u : set α}
    (h₀ : a ∈ s) (h₁ : is_open s) (h₂ : t ∩ s = u ∩ s) :
  𝓝[t] a = 𝓝[u] a :=
by rw [nhds_within_restrict t h₀ h₁, nhds_within_restrict u h₀ h₁, h₂]

@[simp] theorem nhds_within_eq_nhds {a : α} {s : set α} : 𝓝[s] a = 𝓝 a ↔ s ∈ 𝓝 a :=
by rw [nhds_within, inf_eq_left, le_principal_iff]

theorem is_open.nhds_within_eq {a : α} {s : set α} (h : is_open s) (ha : a ∈ s) :
  𝓝[s] a = 𝓝 a :=
nhds_within_eq_nhds.2 $ is_open.mem_nhds h ha

lemma preimage_nhds_within_coinduced {π : α → β} {s : set β} {t : set α} {a : α}
  (h : a ∈ t) (ht : is_open t)
  (hs : s ∈ @nhds β (topological_space.coinduced (λ x : t, π x) subtype.topological_space) (π a)) :
  π ⁻¹' s ∈ 𝓝 a :=
by { rw ← ht.nhds_within_eq h, exact preimage_nhds_within_coinduced' h ht hs }

@[simp] theorem nhds_within_empty (a : α) : 𝓝[∅] a = ⊥ :=
by rw [nhds_within, principal_empty, inf_bot_eq]

theorem nhds_within_union (a : α) (s t : set α) :
  𝓝[s ∪ t] a = 𝓝[s] a ⊔ 𝓝[t] a :=
by { delta nhds_within, rw [←inf_sup_left, sup_principal] }

theorem nhds_within_bUnion {ι} {I : set ι} (hI : I.finite) (s : ι → set α) (a : α) :
  𝓝[⋃ i ∈ I, s i] a = ⨆ i ∈ I, 𝓝[s i] a :=
set.finite.induction_on hI (by simp) $ λ t T _ _ hT,
  by simp only [hT, nhds_within_union, supr_insert, bUnion_insert]

theorem nhds_within_sUnion {S : set (set α)} (hS : S.finite) (a : α) :
  𝓝[⋃₀ S] a = ⨆ s ∈ S, 𝓝[s] a :=
by rw [sUnion_eq_bUnion, nhds_within_bUnion hS]

theorem nhds_within_Union {ι} [finite ι] (s : ι → set α) (a : α) : 𝓝[⋃ i, s i] a = ⨆ i, 𝓝[s i] a :=
by rw [← sUnion_range, nhds_within_sUnion (finite_range s), supr_range]

theorem nhds_within_inter (a : α) (s t : set α) :
  𝓝[s ∩ t] a = 𝓝[s] a ⊓ 𝓝[t] a :=
by { delta nhds_within, rw [inf_left_comm, inf_assoc, inf_principal, ←inf_assoc, inf_idem] }

theorem nhds_within_inter' (a : α) (s t : set α) :
  𝓝[s ∩ t] a = (𝓝[s] a) ⊓ 𝓟 t :=
by { delta nhds_within, rw [←inf_principal, inf_assoc] }

theorem nhds_within_inter_of_mem {a : α} {s t : set α} (h : s ∈ 𝓝[t] a) :
  𝓝[s ∩ t] a = 𝓝[t] a :=
by { rw [nhds_within_inter, inf_eq_right], exact nhds_within_le_of_mem h }

theorem nhds_within_inter_of_mem' {a : α} {s t : set α} (h : s ∈ 𝓝[t] a) :
  𝓝[t ∩ s] a = 𝓝[t] a :=
by rw [inter_comm, nhds_within_inter_of_mem h]

@[simp] theorem nhds_within_singleton (a : α) : 𝓝[{a}] a = pure a :=
by rw [nhds_within, principal_singleton, inf_eq_right.2 (pure_le_nhds a)]

@[simp] theorem nhds_within_insert (a : α) (s : set α) :
  𝓝[insert a s] a = pure a ⊔ 𝓝[s] a :=
by rw [← singleton_union, nhds_within_union, nhds_within_singleton]

lemma mem_nhds_within_insert {a : α} {s t : set α} :
  t ∈ 𝓝[insert a s] a ↔ a ∈ t ∧ t ∈ 𝓝[s] a :=
by simp

lemma insert_mem_nhds_within_insert {a : α} {s t : set α} (h : t ∈ 𝓝[s] a) :
  insert a t ∈ 𝓝[insert a s] a :=
by simp [mem_of_superset h]

lemma insert_mem_nhds_iff {a : α} {s : set α} : insert a s ∈ 𝓝 a ↔ s ∈ 𝓝[≠] a :=
by simp only [nhds_within, mem_inf_principal, mem_compl_iff, mem_singleton_iff,
  or_iff_not_imp_left, insert_def]

@[simp] theorem nhds_within_compl_singleton_sup_pure (a : α) : 𝓝[≠] a ⊔ pure a = 𝓝 a :=
by rw [← nhds_within_singleton, ← nhds_within_union, compl_union_self, nhds_within_univ]

lemma nhds_within_prod_eq {α : Type*} [topological_space α] {β : Type*} [topological_space β]
  (a : α) (b : β) (s : set α) (t : set β) :
  𝓝[s ×ˢ t] (a, b) = 𝓝[s] a ×ᶠ 𝓝[t] b :=
by { delta nhds_within, rw [nhds_prod_eq, ←filter.prod_inf_prod, filter.prod_principal_principal] }

lemma nhds_within_prod {α : Type*} [topological_space α] {β : Type*} [topological_space β]
  {s u : set α} {t v : set β} {a : α} {b : β}
  (hu : u ∈ 𝓝[s] a) (hv : v ∈ 𝓝[t] b) :
  (u ×ˢ v) ∈ 𝓝[s ×ˢ t] (a, b) :=
by { rw nhds_within_prod_eq, exact prod_mem_prod hu hv, }

lemma nhds_within_pi_eq' {ι : Type*} {α : ι → Type*} [Π i, topological_space (α i)]
  {I : set ι} (hI : I.finite) (s : Π i, set (α i)) (x : Π i, α i) :
  𝓝[pi I s] x = ⨅ i, comap (λ x, x i) (𝓝 (x i) ⊓ ⨅ (hi : i ∈ I), 𝓟 (s i)) :=
by simp only [nhds_within, nhds_pi, filter.pi, comap_inf, comap_infi, pi_def, comap_principal,
  ← infi_principal_finite hI, ← infi_inf_eq]

lemma nhds_within_pi_eq {ι : Type*} {α : ι → Type*} [Π i, topological_space (α i)]
  {I : set ι} (hI : I.finite) (s : Π i, set (α i)) (x : Π i, α i) :
  𝓝[pi I s] x = (⨅ i ∈ I, comap (λ x, x i) (𝓝[s i] (x i))) ⊓
    ⨅ (i ∉ I), comap (λ x, x i) (𝓝 (x i)) :=
begin
  simp only [nhds_within, nhds_pi, filter.pi, pi_def, ← infi_principal_finite hI, comap_inf,
    comap_principal, eval],
  rw [infi_split _ (λ i, i ∈ I), inf_right_comm],
  simp only [infi_inf_eq]
end

lemma nhds_within_pi_univ_eq {ι : Type*} {α : ι → Type*} [finite ι] [Π i, topological_space (α i)]
  (s : Π i, set (α i)) (x : Π i, α i) :
  𝓝[pi univ s] x = ⨅ i, comap (λ x, x i) 𝓝[s i] (x i) :=
by simpa [nhds_within] using nhds_within_pi_eq finite_univ s x

lemma nhds_within_pi_eq_bot {ι : Type*} {α : ι → Type*} [Π i, topological_space (α i)]
  {I : set ι} {s : Π i, set (α i)} {x : Π i, α i} :
  𝓝[pi I s] x = ⊥ ↔ ∃ i ∈ I, 𝓝[s i] (x i) = ⊥ :=
by simp only [nhds_within, nhds_pi, pi_inf_principal_pi_eq_bot]

lemma nhds_within_pi_ne_bot {ι : Type*} {α : ι → Type*} [Π i, topological_space (α i)]
  {I : set ι} {s : Π i, set (α i)} {x : Π i, α i} :
  (𝓝[pi I s] x).ne_bot ↔ ∀ i ∈ I, (𝓝[s i] (x i)).ne_bot :=
by simp [ne_bot_iff, nhds_within_pi_eq_bot]

theorem filter.tendsto.piecewise_nhds_within {f g : α → β} {t : set α} [∀ x, decidable (x ∈ t)]
  {a : α} {s : set α} {l : filter β}
  (h₀ : tendsto f (𝓝[s ∩ t] a) l) (h₁ : tendsto g (𝓝[s ∩ tᶜ] a) l) :
  tendsto (piecewise t f g) (𝓝[s] a) l :=
by apply tendsto.piecewise; rwa ← nhds_within_inter'

theorem filter.tendsto.if_nhds_within {f g : α → β} {p : α → Prop} [decidable_pred p]
    {a : α} {s : set α} {l : filter β}
    (h₀ : tendsto f (𝓝[s ∩ {x | p x}] a) l)
    (h₁ : tendsto g (𝓝[s ∩ {x | ¬ p x}] a) l) :
  tendsto (λ x, if p x then f x else g x) (𝓝[s] a) l :=
h₀.piecewise_nhds_within h₁

lemma map_nhds_within (f : α → β) (a : α) (s : set α) :
  map f (𝓝[s] a) = ⨅ t ∈ {t : set α | a ∈ t ∧ is_open t}, 𝓟 (f  '' (t ∩ s)) :=
((nhds_within_basis_open a s).map f).eq_binfi

theorem tendsto_nhds_within_mono_left {f : α → β} {a : α}
    {s t : set α} {l : filter β} (hst : s ⊆ t) (h : tendsto f (𝓝[t] a) l) :
  tendsto f (𝓝[s] a) l :=
h.mono_left $ nhds_within_mono a hst

theorem tendsto_nhds_within_mono_right {f : β → α} {l : filter β}
    {a : α} {s t : set α} (hst : s ⊆ t) (h : tendsto f l (𝓝[s] a)) :
  tendsto f l (𝓝[t] a) :=
h.mono_right (nhds_within_mono a hst)

theorem tendsto_nhds_within_of_tendsto_nhds {f : α → β} {a : α}
    {s : set α} {l : filter β} (h : tendsto f (𝓝 a) l) :
  tendsto f (𝓝[s] a) l :=
h.mono_left inf_le_left

lemma eventually_mem_of_tendsto_nhds_within {f : β → α} {a : α}
  {s : set α} {l : filter β} (h : tendsto f l (𝓝[s] a)) :
  ∀ᶠ i in l, f i ∈ s :=
begin
  simp_rw [nhds_within_eq, tendsto_infi, mem_set_of_eq, tendsto_principal, mem_inter_iff,
    eventually_and] at h,
  exact (h univ ⟨mem_univ a, is_open_univ⟩).2,
end

lemma tendsto_nhds_of_tendsto_nhds_within {f : β → α} {a : α}
  {s : set α} {l : filter β} (h : tendsto f l (𝓝[s] a)) :
  tendsto f l (𝓝 a) :=
h.mono_right nhds_within_le_nhds

theorem principal_subtype {α : Type*} (s : set α) (t : set {x // x ∈ s}) :
  𝓟 t = comap coe (𝓟 ((coe : s → α) '' t)) :=
by rw [comap_principal, set.preimage_image_eq _ subtype.coe_injective]

lemma nhds_within_ne_bot_of_mem {s : set α} {x : α} (hx : x ∈ s) :
  ne_bot (𝓝[s] x) :=
mem_closure_iff_nhds_within_ne_bot.1 $ subset_closure hx

lemma is_closed.mem_of_nhds_within_ne_bot {s : set α} (hs : is_closed s)
  {x : α} (hx : ne_bot $ 𝓝[s] x) : x ∈ s :=
by simpa only [hs.closure_eq] using mem_closure_iff_nhds_within_ne_bot.2 hx

lemma dense_range.nhds_within_ne_bot {ι : Type*} {f : ι → α} (h : dense_range f) (x : α) :
  ne_bot (𝓝[range f] x) :=
mem_closure_iff_cluster_pt.1 (h x)

lemma mem_closure_pi {ι : Type*} {α : ι → Type*} [Π i, topological_space (α i)]
  {I : set ι} {s : Π i, set (α i)} {x : Π i, α i} :
  x ∈ closure (pi I s) ↔ ∀ i ∈ I, x i ∈ closure (s i) :=
by simp only [mem_closure_iff_nhds_within_ne_bot, nhds_within_pi_ne_bot]

lemma closure_pi_set {ι : Type*} {α : ι → Type*} [Π i, topological_space (α i)]
  (I : set ι) (s : Π i, set (α i)) :
  closure (pi I s) = pi I (λ i, closure (s i)) :=
set.ext $ λ x, mem_closure_pi

lemma dense_pi {ι : Type*} {α : ι → Type*} [Π i, topological_space (α i)] {s : Π i, set (α i)}
  (I : set ι) (hs : ∀ i ∈ I, dense (s i)) :
  dense (pi I s) :=
by simp only [dense_iff_closure_eq, closure_pi_set,
  pi_congr rfl (λ i hi, (hs i hi).closure_eq), pi_univ]

lemma eventually_eq_nhds_within_iff {f g : α → β} {s : set α} {a : α} :
  (f =ᶠ[𝓝[s] a] g) ↔ ∀ᶠ x in 𝓝 a, x ∈ s → f x = g x :=
mem_inf_principal

lemma eventually_eq_nhds_within_of_eq_on {f g : α → β} {s : set α} {a : α} (h : eq_on f g s) :
  f =ᶠ[𝓝[s] a] g :=
mem_inf_of_right h

lemma set.eq_on.eventually_eq_nhds_within {f g : α → β} {s : set α} {a : α} (h : eq_on f g s) :
  f =ᶠ[𝓝[s] a] g :=
eventually_eq_nhds_within_of_eq_on h

lemma tendsto_nhds_within_congr {f g : α → β} {s : set α} {a : α} {l : filter β}
  (hfg : ∀ x ∈ s, f x = g x) (hf : tendsto f (𝓝[s] a) l) : tendsto g (𝓝[s] a) l :=
(tendsto_congr' $ eventually_eq_nhds_within_of_eq_on hfg).1 hf

lemma eventually_nhds_within_of_forall {s : set α} {a : α} {p : α → Prop} (h : ∀ x ∈ s, p x) :
  ∀ᶠ x in 𝓝[s] a, p x :=
mem_inf_of_right h

lemma tendsto_nhds_within_of_tendsto_nhds_of_eventually_within {a : α} {l : filter β}
  {s : set α} (f : β → α) (h1 : tendsto f l (𝓝 a)) (h2 : ∀ᶠ x in l, f x ∈ s) :
  tendsto f l (𝓝[s] a) :=
tendsto_inf.2 ⟨h1, tendsto_principal.2 h2⟩

lemma tendsto_nhds_within_iff {a : α} {l : filter β} {s : set α} {f : β → α} :
  tendsto f l (𝓝[s] a) ↔ tendsto f l (𝓝 a) ∧ ∀ᶠ n in l, f n ∈ s :=
⟨λ h, ⟨tendsto_nhds_of_tendsto_nhds_within h, eventually_mem_of_tendsto_nhds_within h⟩,
  λ h, tendsto_nhds_within_of_tendsto_nhds_of_eventually_within _ h.1 h.2⟩

@[simp] lemma tendsto_nhds_within_range {a : α} {l : filter β} {f : β → α} :
  tendsto f l (𝓝[range f] a) ↔ tendsto f l (𝓝 a) :=
⟨λ h, h.mono_right inf_le_left, λ h, tendsto_inf.2
  ⟨h, tendsto_principal.2 $ eventually_of_forall mem_range_self⟩⟩

lemma filter.eventually_eq.eq_of_nhds_within {s : set α} {f g : α → β} {a : α}
  (h : f =ᶠ[𝓝[s] a] g) (hmem : a ∈ s) : f a = g a :=
h.self_of_nhds_within hmem

lemma eventually_nhds_within_of_eventually_nhds {α : Type*} [topological_space α]
  {s : set α} {a : α} {p : α → Prop} (h : ∀ᶠ x in 𝓝 a, p x) :
  ∀ᶠ x in 𝓝[s] a, p x :=
mem_nhds_within_of_mem_nhds h

/-!
### `nhds_within` and subtypes
-/

theorem mem_nhds_within_subtype {s : set α} {a : {x // x ∈ s}} {t u : set {x // x ∈ s}} :
  t ∈ 𝓝[u] a ↔ t ∈ comap (coe : s → α) (𝓝[coe '' u] a) :=
by rw [nhds_within, nhds_subtype, principal_subtype, ←comap_inf, ←nhds_within]

theorem nhds_within_subtype (s : set α) (a : {x // x ∈ s}) (t : set {x // x ∈ s}) :
  𝓝[t] a = comap (coe : s → α) (𝓝[coe '' t] a) :=
filter.ext $ λ u, mem_nhds_within_subtype

theorem nhds_within_eq_map_subtype_coe {s : set α} {a : α} (h : a ∈ s) :
  𝓝[s] a = map (coe : s → α) (𝓝 ⟨a, h⟩) :=
by simpa only [subtype.range_coe] using (embedding_subtype_coe.map_nhds_eq ⟨a, h⟩).symm

theorem mem_nhds_subtype_iff_nhds_within {s : set α} {a : s} {t : set s} :
  t ∈ 𝓝 a ↔ coe '' t ∈ 𝓝[s] (a : α) :=
by rw [nhds_within_eq_map_subtype_coe a.coe_prop, mem_map,
  preimage_image_eq _ subtype.coe_injective, subtype.coe_eta]

theorem preimage_coe_mem_nhds_subtype {s t : set α} {a : s} :
  coe ⁻¹' t ∈ 𝓝 a ↔ t ∈ 𝓝[s] ↑a :=
by simp only [mem_nhds_subtype_iff_nhds_within, subtype.image_preimage_coe, inter_mem_iff,
  self_mem_nhds_within, and_true]

theorem tendsto_nhds_within_iff_subtype {s : set α} {a : α} (h : a ∈ s) (f : α → β) (l : filter β) :
  tendsto f (𝓝[s] a) l ↔ tendsto (s.restrict f) (𝓝 ⟨a, h⟩) l :=
by simp only [tendsto, nhds_within_eq_map_subtype_coe h, filter.map_map, restrict]

variables [topological_space β] [topological_space γ] [topological_space δ]

/-- A function between topological spaces is continuous at a point `x₀` within a subset `s`
if `f x` tends to `f x₀` when `x` tends to `x₀` while staying within `s`. -/
def continuous_within_at (f : α → β) (s : set α) (x : α) : Prop :=
tendsto f (𝓝[s] x) (𝓝 (f x))

/-- If a function is continuous within `s` at `x`, then it tends to `f x` within `s` by definition.
We register this fact for use with the dot notation, especially to use `tendsto.comp` as
`continuous_within_at.comp` will have a different meaning. -/
lemma continuous_within_at.tendsto {f : α → β} {s : set α} {x : α}
  (h : continuous_within_at f s x) :
  tendsto f (𝓝[s] x) (𝓝 (f x)) := h

/-- A function between topological spaces is continuous on a subset `s`
when it's continuous at every point of `s` within `s`. -/
def continuous_on (f : α → β) (s : set α) : Prop := ∀ x ∈ s, continuous_within_at f s x

lemma continuous_on.continuous_within_at {f : α → β} {s : set α} {x : α} (hf : continuous_on f s)
  (hx : x ∈ s) : continuous_within_at f s x :=
hf x hx

theorem continuous_within_at_univ (f : α → β) (x : α) :
  continuous_within_at f set.univ x ↔ continuous_at f x :=
by rw [continuous_at, continuous_within_at, nhds_within_univ]

theorem continuous_within_at_iff_continuous_at_restrict (f : α → β) {x : α} {s : set α}
  (h : x ∈ s) :
  continuous_within_at f s x ↔ continuous_at (s.restrict f) ⟨x, h⟩ :=
tendsto_nhds_within_iff_subtype h f _

theorem continuous_within_at.tendsto_nhds_within {f : α → β} {x : α} {s : set α} {t : set β}
  (h : continuous_within_at f s x) (ht : maps_to f s t) :
  tendsto f (𝓝[s] x) (𝓝[t] (f x)) :=
tendsto_inf.2 ⟨h, tendsto_principal.2 $ mem_inf_of_right $ mem_principal.2 $ ht⟩

theorem continuous_within_at.tendsto_nhds_within_image {f : α → β} {x : α} {s : set α}
  (h : continuous_within_at f s x) :
  tendsto f (𝓝[s] x) (𝓝[f '' s] (f x)) :=
h.tendsto_nhds_within (maps_to_image _ _)

lemma continuous_within_at.prod_map {f : α → γ} {g : β → δ} {s : set α} {t : set β}
  {x : α} {y : β}
  (hf : continuous_within_at f s x) (hg : continuous_within_at g t y) :
  continuous_within_at (prod.map f g) (s ×ˢ t) (x, y) :=
begin
  unfold continuous_within_at at *,
  rw [nhds_within_prod_eq, prod.map, nhds_prod_eq],
  exact hf.prod_map hg,
end

lemma continuous_within_at_pi {ι : Type*} {π : ι → Type*} [∀ i, topological_space (π i)]
  {f : α → Π i, π i} {s : set α} {x : α} :
  continuous_within_at f s x ↔ ∀ i, continuous_within_at (λ y, f y i) s x :=
tendsto_pi_nhds

lemma continuous_on_pi {ι : Type*} {π : ι → Type*} [∀ i, topological_space (π i)]
  {f : α → Π i, π i} {s : set α} :
  continuous_on f s ↔ ∀ i, continuous_on (λ y, f y i) s :=
⟨λ h i x hx, tendsto_pi_nhds.1 (h x hx) i, λ h x hx, tendsto_pi_nhds.2 (λ i, h i x hx)⟩

lemma continuous_within_at.fin_insert_nth {n} {π : fin (n + 1) → Type*}
  [Π i, topological_space (π i)] (i : fin (n + 1)) {f : α → π i} {a : α} {s : set α}
  (hf : continuous_within_at f s a)
  {g : α → Π j : fin n, π (i.succ_above j)} (hg : continuous_within_at g s a) :
  continuous_within_at (λ a, i.insert_nth (f a) (g a)) s a :=
hf.fin_insert_nth i hg

lemma continuous_on.fin_insert_nth {n} {π : fin (n + 1) → Type*} [Π i, topological_space (π i)]
  (i : fin (n + 1)) {f : α → π i} {s : set α} (hf : continuous_on f s)
  {g : α → Π j : fin n, π (i.succ_above j)} (hg : continuous_on g s) :
  continuous_on (λ a, i.insert_nth (f a) (g a)) s :=
λ a ha, (hf a ha).fin_insert_nth i (hg a ha)

theorem continuous_on_iff {f : α → β} {s : set α} :
  continuous_on f s ↔ ∀ x ∈ s, ∀ t : set β, is_open t → f x ∈ t → ∃ u, is_open u ∧ x ∈ u ∧
    u ∩ s ⊆ f ⁻¹' t :=
by simp only [continuous_on, continuous_within_at, tendsto_nhds, mem_nhds_within]

theorem continuous_on_iff_continuous_restrict {f : α → β} {s : set α} :
  continuous_on f s ↔ continuous (s.restrict f) :=
begin
  rw [continuous_on, continuous_iff_continuous_at], split,
  { rintros h ⟨x, xs⟩,
    exact (continuous_within_at_iff_continuous_at_restrict f xs).mp (h x xs) },
  intros h x xs,
  exact (continuous_within_at_iff_continuous_at_restrict f xs).mpr (h ⟨x, xs⟩)
end

theorem continuous_on_iff' {f : α → β} {s : set α} :
  continuous_on f s ↔ ∀ t : set β, is_open t → ∃ u, is_open u ∧ f ⁻¹' t ∩ s = u ∩ s :=
have ∀ t, is_open (s.restrict f ⁻¹' t) ↔ ∃ (u : set α), is_open u ∧ f ⁻¹' t ∩ s = u ∩ s,
  begin
    intro t,
    rw [is_open_induced_iff, set.restrict_eq, set.preimage_comp],
    simp only [subtype.preimage_coe_eq_preimage_coe_iff],
    split; { rintros ⟨u, ou, useq⟩, exact ⟨u, ou, useq.symm⟩ }
  end,
by rw [continuous_on_iff_continuous_restrict, continuous_def]; simp only [this]

/-- If a function is continuous on a set for some topologies, then it is
continuous on the same set with respect to any finer topology on the source space. -/
lemma continuous_on.mono_dom {α β : Type*} {t₁ t₂ : topological_space α} {t₃ : topological_space β}
  (h₁ : t₂ ≤ t₁) {s : set α} {f : α → β} (h₂ : @continuous_on α β t₁ t₃ f s) :
  @continuous_on α β t₂ t₃ f s :=
begin
  rw continuous_on_iff' at h₂ ⊢,
  assume t ht,
  rcases h₂ t ht with ⟨u, hu, h'u⟩,
  exact ⟨u, h₁ u hu, h'u⟩
end

/-- If a function is continuous on a set for some topologies, then it is
continuous on the same set with respect to any coarser topology on the target space. -/
lemma continuous_on.mono_rng {α β : Type*} {t₁ : topological_space α} {t₂ t₃ : topological_space β}
  (h₁ : t₂ ≤ t₃) {s : set α} {f : α → β} (h₂ : @continuous_on α β t₁ t₂ f s) :
  @continuous_on α β t₁ t₃ f s :=
begin
  rw continuous_on_iff' at h₂ ⊢,
  assume t ht,
  exact h₂ t (h₁ t ht)
end

theorem continuous_on_iff_is_closed {f : α → β} {s : set α} :
  continuous_on f s ↔ ∀ t : set β, is_closed t → ∃ u, is_closed u ∧ f ⁻¹' t ∩ s = u ∩ s :=
have ∀ t, is_closed (s.restrict f ⁻¹' t) ↔ ∃ (u : set α), is_closed u ∧ f ⁻¹' t ∩ s = u ∩ s,
  begin
    intro t,
    rw [is_closed_induced_iff, set.restrict_eq, set.preimage_comp],
    simp only [subtype.preimage_coe_eq_preimage_coe_iff, eq_comm]
  end,
by rw [continuous_on_iff_continuous_restrict, continuous_iff_is_closed]; simp only [this]

lemma continuous_on.prod_map {f : α → γ} {g : β → δ} {s : set α} {t : set β}
  (hf : continuous_on f s) (hg : continuous_on g t) :
  continuous_on (prod.map f g) (s ×ˢ t) :=
λ ⟨x, y⟩ ⟨hx, hy⟩, continuous_within_at.prod_map (hf x hx) (hg y hy)

lemma continuous_of_cover_nhds {ι : Sort*} {f : α → β} {s : ι → set α}
  (hs : ∀ x : α, ∃ i, s i ∈ 𝓝 x) (hf : ∀ i, continuous_on f (s i)) :
  continuous f :=
continuous_iff_continuous_at.mpr $ λ x, let ⟨i, hi⟩ := hs x in
  by { rw [continuous_at, ← nhds_within_eq_nhds.2 hi], exact hf _ _ (mem_of_mem_nhds hi) }

lemma continuous_on_empty (f : α → β) : continuous_on f ∅ :=
λ x, false.elim

lemma continuous_on_singleton (f : α → β) (a : α) : continuous_on f {a} :=
forall_eq.2 $ by simpa only [continuous_within_at, nhds_within_singleton, tendsto_pure_left]
  using λ s, mem_of_mem_nhds

lemma set.subsingleton.continuous_on {s : set α} (hs : s.subsingleton) (f : α → β) :
  continuous_on f s :=
hs.induction_on (continuous_on_empty f) (continuous_on_singleton f)

theorem nhds_within_le_comap {x : α} {s : set α} {f : α → β} (ctsf : continuous_within_at f s x) :
  𝓝[s] x ≤ comap f (𝓝[f '' s] (f x)) :=
ctsf.tendsto_nhds_within_image.le_comap

@[simp] lemma comap_nhds_within_range {α} (f : α → β) (y : β) :
  comap f (𝓝[range f] y) = comap f (𝓝 y) :=
comap_inf_principal_range

lemma continuous_iff_continuous_on_univ {f : α → β} : continuous f ↔ continuous_on f univ :=
by simp [continuous_iff_continuous_at, continuous_on, continuous_at, continuous_within_at,
         nhds_within_univ]

lemma continuous_within_at.mono {f : α → β} {s t : set α} {x : α} (h : continuous_within_at f t x)
  (hs : s ⊆ t) : continuous_within_at f s x :=
h.mono_left (nhds_within_mono x hs)

lemma continuous_within_at.mono_of_mem {f : α → β} {s t : set α} {x : α}
  (h : continuous_within_at f t x) (hs : t ∈ 𝓝[s] x) : continuous_within_at f s x :=
h.mono_left (nhds_within_le_of_mem hs)

lemma continuous_within_at_inter' {f : α → β} {s t : set α} {x : α} (h : t ∈ 𝓝[s] x) :
  continuous_within_at f (s ∩ t) x ↔ continuous_within_at f s x :=
by simp [continuous_within_at, nhds_within_restrict'' s h]

lemma continuous_within_at_inter {f : α → β} {s t : set α} {x : α} (h : t ∈ 𝓝 x) :
  continuous_within_at f (s ∩ t) x ↔ continuous_within_at f s x :=
by simp [continuous_within_at, nhds_within_restrict' s h]

lemma continuous_within_at_union {f : α → β} {s t : set α} {x : α} :
  continuous_within_at f (s ∪ t) x ↔ continuous_within_at f s x ∧ continuous_within_at f t x :=
by simp only [continuous_within_at, nhds_within_union, tendsto_sup]

lemma continuous_within_at.union {f : α → β} {s t : set α} {x : α}
  (hs : continuous_within_at f s x) (ht : continuous_within_at f t x) :
  continuous_within_at f (s ∪ t) x :=
continuous_within_at_union.2 ⟨hs, ht⟩

lemma continuous_within_at.mem_closure_image  {f : α → β} {s : set α} {x : α}
  (h : continuous_within_at f s x) (hx : x ∈ closure s) : f x ∈ closure (f '' s) :=
by haveI := (mem_closure_iff_nhds_within_ne_bot.1 hx);
exact (mem_closure_of_tendsto h $
  mem_of_superset self_mem_nhds_within (subset_preimage_image f s))

lemma continuous_within_at.mem_closure {f : α → β} {s : set α} {x : α} {A : set β}
  (h : continuous_within_at f s x) (hx : x ∈ closure s) (hA : maps_to f s A) : f x ∈ closure A :=
closure_mono (image_subset_iff.2 hA) (h.mem_closure_image hx)

lemma set.maps_to.closure_of_continuous_within_at {f : α → β} {s : set α} {t : set β}
  (h : maps_to f s t) (hc : ∀ x ∈ closure s, continuous_within_at f s x) :
  maps_to f (closure s) (closure t) :=
λ x hx, (hc x hx).mem_closure hx h

lemma set.maps_to.closure_of_continuous_on {f : α → β} {s : set α} {t : set β}
  (h : maps_to f s t) (hc : continuous_on f (closure s)) :
  maps_to f (closure s) (closure t) :=
h.closure_of_continuous_within_at $ λ x hx, (hc x hx).mono subset_closure

lemma continuous_within_at.image_closure {f : α → β} {s : set α}
  (hf : ∀ x ∈ closure s, continuous_within_at f s x) :
  f '' (closure s) ⊆ closure (f '' s) :=
maps_to'.1 $ (maps_to_image f s).closure_of_continuous_within_at hf

lemma continuous_on.image_closure {f : α → β} {s : set α} (hf : continuous_on f (closure s)) :
  f '' (closure s) ⊆ closure (f '' s) :=
continuous_within_at.image_closure $ λ x hx, (hf x hx).mono subset_closure

@[simp] lemma continuous_within_at_singleton {f : α → β} {x : α} : continuous_within_at f {x} x :=
by simp only [continuous_within_at, nhds_within_singleton, tendsto_pure_nhds]

@[simp] lemma continuous_within_at_insert_self {f : α → β} {x : α} {s : set α} :
  continuous_within_at f (insert x s) x ↔ continuous_within_at f s x :=
by simp only [← singleton_union, continuous_within_at_union, continuous_within_at_singleton,
  true_and]

alias continuous_within_at_insert_self ↔ _ continuous_within_at.insert_self

lemma continuous_within_at.diff_iff {f : α → β} {s t : set α} {x : α}
  (ht : continuous_within_at f t x) :
  continuous_within_at f (s \ t) x ↔ continuous_within_at f s x :=
⟨λ h, (h.union ht).mono $ by simp only [diff_union_self, subset_union_left],
  λ h, h.mono (diff_subset _ _)⟩

@[simp] lemma continuous_within_at_diff_self {f : α → β} {s : set α} {x : α} :
  continuous_within_at f (s \ {x}) x ↔ continuous_within_at f s x :=
continuous_within_at_singleton.diff_iff

@[simp] lemma continuous_within_at_compl_self {f : α → β} {a : α} :
  continuous_within_at f {a}ᶜ a ↔ continuous_at f a :=
by rw [compl_eq_univ_diff, continuous_within_at_diff_self, continuous_within_at_univ]

@[simp] lemma continuous_within_at_update_same [decidable_eq α] {f : α → β}
  {s : set α} {x : α} {y : β} :
  continuous_within_at (update f x y) s x ↔ tendsto f (𝓝[s \ {x}] x) (𝓝 y) :=
calc continuous_within_at (update f x y) s x ↔ tendsto (update f x y) (𝓝[s \ {x}] x) (𝓝 y) :
  by rw [← continuous_within_at_diff_self, continuous_within_at, function.update_same]
... ↔ tendsto f (𝓝[s \ {x}] x) (𝓝 y) :
  tendsto_congr' $ eventually_nhds_within_iff.2 $ eventually_of_forall $
    λ z hz, update_noteq hz.2 _ _

@[simp] lemma continuous_at_update_same [decidable_eq α] {f : α → β} {x : α} {y : β} :
  continuous_at (function.update f x y) x ↔ tendsto f (𝓝[≠] x) (𝓝 y) :=
by rw [← continuous_within_at_univ, continuous_within_at_update_same, compl_eq_univ_diff]

theorem is_open_map.continuous_on_image_of_left_inv_on {f : α → β} {s : set α}
  (h : is_open_map (s.restrict f)) {finv : β → α} (hleft : left_inv_on finv f s) :
  continuous_on finv (f '' s) :=
begin
  refine continuous_on_iff'.2 (λ t ht, ⟨f '' (t ∩ s), _, _⟩),
  { rw ← image_restrict, exact h _ (ht.preimage continuous_subtype_coe) },
  { rw [inter_eq_self_of_subset_left (image_subset f (inter_subset_right t s)),
      hleft.image_inter'] },
end

theorem is_open_map.continuous_on_range_of_left_inverse {f : α → β} (hf : is_open_map f)
  {finv : β → α} (hleft : function.left_inverse finv f) :
  continuous_on finv (range f) :=
begin
  rw [← image_univ],
  exact (hf.restrict is_open_univ).continuous_on_image_of_left_inv_on (λ x _, hleft x)
end

lemma continuous_on.congr_mono {f g : α → β} {s s₁ : set α} (h : continuous_on f s)
  (h' : eq_on g f s₁) (h₁ : s₁ ⊆ s) : continuous_on g s₁ :=
begin
  assume x hx,
  unfold continuous_within_at,
  have A := (h x (h₁ hx)).mono h₁,
  unfold continuous_within_at at A,
  rw ← h' hx at A,
  exact A.congr' h'.eventually_eq_nhds_within.symm
end

lemma continuous_on.congr {f g : α → β} {s : set α} (h : continuous_on f s) (h' : eq_on g f s) :
  continuous_on g s :=
h.congr_mono h' (subset.refl _)

lemma continuous_on_congr {f g : α → β} {s : set α} (h' : eq_on g f s) :
  continuous_on g s ↔ continuous_on f s :=
⟨λ h, continuous_on.congr h h'.symm, λ h, h.congr h'⟩

lemma continuous_at.continuous_within_at {f : α → β} {s : set α} {x : α} (h : continuous_at f x) :
  continuous_within_at f s x :=
continuous_within_at.mono ((continuous_within_at_univ f x).2 h) (subset_univ _)

lemma continuous_within_at_iff_continuous_at {f : α → β} {s : set α} {x : α} (h : s ∈ 𝓝 x) :
  continuous_within_at f s x ↔ continuous_at f x :=
by rw [← univ_inter s, continuous_within_at_inter h, continuous_within_at_univ]

lemma continuous_within_at.continuous_at {f : α → β} {s : set α} {x : α}
  (h : continuous_within_at f s x) (hs : s ∈ 𝓝 x) : continuous_at f x :=
(continuous_within_at_iff_continuous_at hs).mp h

lemma is_open.continuous_on_iff {f : α → β} {s : set α} (hs : is_open s) :
  continuous_on f s ↔ ∀ ⦃a⦄, a ∈ s → continuous_at f a :=
ball_congr $ λ _, continuous_within_at_iff_continuous_at ∘ hs.mem_nhds

lemma continuous_on.continuous_at {f : α → β} {s : set α} {x : α}
  (h : continuous_on f s) (hx : s ∈ 𝓝 x) : continuous_at f x :=
(h x (mem_of_mem_nhds hx)).continuous_at hx

lemma continuous_at.continuous_on {f : α → β} {s : set α} (hcont : ∀ x ∈ s, continuous_at f x) :
  continuous_on f s :=
λ x hx, (hcont x hx).continuous_within_at

lemma continuous_within_at.comp {g : β → γ} {f : α → β} {s : set α} {t : set β} {x : α}
  (hg : continuous_within_at g t (f x)) (hf : continuous_within_at f s x) (h : maps_to f s t) :
  continuous_within_at (g ∘ f) s x :=
hg.tendsto.comp (hf.tendsto_nhds_within h)

lemma continuous_within_at.comp' {g : β → γ} {f : α → β} {s : set α} {t : set β} {x : α}
  (hg : continuous_within_at g t (f x)) (hf : continuous_within_at f s x) :
  continuous_within_at (g ∘ f) (s ∩ f⁻¹' t) x :=
hg.comp (hf.mono (inter_subset_left _ _)) (inter_subset_right _ _)

lemma continuous_at.comp_continuous_within_at {g : β → γ} {f : α → β} {s : set α} {x : α}
  (hg : continuous_at g (f x)) (hf : continuous_within_at f s x) :
  continuous_within_at (g ∘ f) s x :=
hg.continuous_within_at.comp hf (maps_to_univ _ _)

lemma continuous_on.comp {g : β → γ} {f : α → β} {s : set α} {t : set β}
  (hg : continuous_on g t) (hf : continuous_on f s) (h : maps_to f s t) :
  continuous_on (g ∘ f) s :=
λx hx, continuous_within_at.comp (hg _ (h hx)) (hf x hx) h

lemma continuous_on.mono {f : α → β} {s t : set α} (hf : continuous_on f s) (h : t ⊆ s)  :
  continuous_on f t :=
λx hx, (hf x (h hx)).mono_left (nhds_within_mono _ h)

lemma antitone_continuous_on {f : α → β} : antitone (continuous_on f) :=
λ s t hst hf, hf.mono hst

lemma continuous_on.comp' {g : β → γ} {f : α → β} {s : set α} {t : set β}
  (hg : continuous_on g t) (hf : continuous_on f s) :
  continuous_on (g ∘ f) (s ∩ f⁻¹' t) :=
hg.comp (hf.mono (inter_subset_left _ _)) (inter_subset_right _ _)

lemma continuous.continuous_on {f : α → β} {s : set α} (h : continuous f) :
  continuous_on f s :=
begin
  rw continuous_iff_continuous_on_univ at h,
  exact h.mono (subset_univ _)
end

lemma continuous.continuous_within_at {f : α → β} {s : set α} {x : α} (h : continuous f) :
  continuous_within_at f s x :=
h.continuous_at.continuous_within_at

lemma continuous.comp_continuous_on {g : β → γ} {f : α → β} {s : set α}
  (hg : continuous g) (hf : continuous_on f s) :
  continuous_on (g ∘ f) s :=
hg.continuous_on.comp hf (maps_to_univ _ _)

lemma continuous_on.comp_continuous {g : β → γ} {f : α → β} {s : set β}
  (hg : continuous_on g s) (hf : continuous f) (hs : ∀ x, f x ∈ s) : continuous (g ∘ f) :=
begin
  rw continuous_iff_continuous_on_univ at *,
  exact hg.comp hf (λ x _, hs x),
end

lemma continuous_within_at.preimage_mem_nhds_within {f : α → β} {x : α} {s : set α} {t : set β}
  (h : continuous_within_at f s x) (ht : t ∈ 𝓝 (f x)) : f ⁻¹' t ∈ 𝓝[s] x :=
h ht

lemma set.left_inv_on.map_nhds_within_eq {f : α → β} {g : β → α} {x : β} {s : set β}
  (h : left_inv_on f g s) (hx : f (g x) = x) (hf : continuous_within_at f (g '' s) (g x))
  (hg : continuous_within_at g s x) :
  map g (𝓝[s] x) = 𝓝[g '' s] (g x) :=
begin
  apply le_antisymm,
  { exact hg.tendsto_nhds_within (maps_to_image _ _) },
  { have A : g ∘ f =ᶠ[𝓝[g '' s] (g x)] id,
      from h.right_inv_on_image.eq_on.eventually_eq_of_mem self_mem_nhds_within,
    refine le_map_of_right_inverse A _,
    simpa only [hx] using hf.tendsto_nhds_within (h.maps_to (surj_on_image _ _)) }
end

lemma function.left_inverse.map_nhds_eq {f : α → β} {g : β → α} {x : β}
  (h : function.left_inverse f g) (hf : continuous_within_at f (range g) (g x))
  (hg : continuous_at g x) :
  map g (𝓝 x) = 𝓝[range g] (g x) :=
by simpa only [nhds_within_univ, image_univ]
  using (h.left_inv_on univ).map_nhds_within_eq (h x) (by rwa image_univ) hg.continuous_within_at

lemma continuous_within_at.preimage_mem_nhds_within' {f : α → β} {x : α} {s : set α} {t : set β}
  (h : continuous_within_at f s x) (ht : t ∈ 𝓝[f '' s] (f x)) :
  f ⁻¹' t ∈ 𝓝[s] x :=
h.tendsto_nhds_within (maps_to_image _ _) ht

lemma filter.eventually_eq.congr_continuous_within_at {f g : α → β} {s : set α} {x : α}
  (h : f =ᶠ[𝓝[s] x] g) (hx : f x = g x) :
  continuous_within_at f s x ↔ continuous_within_at g s x :=
by rw [continuous_within_at, hx, tendsto_congr' h, continuous_within_at]

lemma continuous_within_at.congr_of_eventually_eq {f f₁ : α → β} {s : set α} {x : α}
  (h : continuous_within_at f s x) (h₁ : f₁ =ᶠ[𝓝[s] x] f) (hx : f₁ x = f x) :
  continuous_within_at f₁ s x :=
(h₁.congr_continuous_within_at hx).2 h

lemma continuous_within_at.congr {f f₁ : α → β} {s : set α} {x : α}
  (h : continuous_within_at f s x) (h₁ : ∀y∈s, f₁ y = f y) (hx : f₁ x = f x) :
  continuous_within_at f₁ s x :=
h.congr_of_eventually_eq (mem_of_superset self_mem_nhds_within h₁) hx

lemma continuous_within_at.congr_mono {f g : α → β} {s s₁ : set α} {x : α}
  (h : continuous_within_at f s x) (h' : eq_on g f s₁) (h₁ : s₁ ⊆ s) (hx : g x = f x):
  continuous_within_at g s₁ x :=
(h.mono h₁).congr h' hx

lemma continuous_on_const {s : set α} {c : β} : continuous_on (λx, c) s :=
continuous_const.continuous_on

lemma continuous_within_at_const {b : β} {s : set α} {x : α} :
  continuous_within_at (λ _:α, b) s x :=
continuous_const.continuous_within_at

lemma continuous_on_id {s : set α} : continuous_on id s :=
continuous_id.continuous_on

lemma continuous_within_at_id {s : set α} {x : α} : continuous_within_at id s x :=
continuous_id.continuous_within_at

lemma continuous_on_open_iff {f : α → β} {s : set α} (hs : is_open s) :
  continuous_on f s ↔ (∀t, is_open t → is_open (s ∩ f⁻¹' t)) :=
begin
  rw continuous_on_iff',
  split,
  { assume h t ht,
    rcases h t ht with ⟨u, u_open, hu⟩,
    rw [inter_comm, hu],
    apply is_open.inter u_open hs },
  { assume h t ht,
    refine ⟨s ∩ f ⁻¹' t, h t ht, _⟩,
    rw [@inter_comm _ s (f ⁻¹' t), inter_assoc, inter_self] }
end

lemma continuous_on.preimage_open_of_open {f : α → β} {s : set α} {t : set β}
  (hf : continuous_on f s) (hs : is_open s) (ht : is_open t) : is_open (s ∩ f⁻¹' t) :=
(continuous_on_open_iff hs).1 hf t ht

lemma continuous_on.is_open_preimage {f : α → β} {s : set α} {t : set β} (h : continuous_on f s)
  (hs : is_open s) (hp : f ⁻¹' t ⊆ s) (ht : is_open t) : is_open (f ⁻¹' t) :=
begin
  convert (continuous_on_open_iff hs).mp h t ht,
  rw [inter_comm, inter_eq_self_of_subset_left hp],
end

lemma continuous_on.preimage_closed_of_closed {f : α → β} {s : set α} {t : set β}
  (hf : continuous_on f s) (hs : is_closed s) (ht : is_closed t) : is_closed (s ∩ f⁻¹' t) :=
begin
  rcases continuous_on_iff_is_closed.1 hf t ht with ⟨u, hu⟩,
  rw [inter_comm, hu.2],
  apply is_closed.inter hu.1 hs
end

lemma continuous_on.preimage_interior_subset_interior_preimage {f : α → β} {s : set α} {t : set β}
  (hf : continuous_on f s) (hs : is_open s) : s ∩ f⁻¹' (interior t) ⊆ s ∩ interior (f⁻¹' t) :=
calc s ∩ f ⁻¹' (interior t) ⊆ interior (s ∩ f ⁻¹' t) :
  interior_maximal (inter_subset_inter (subset.refl _) (preimage_mono interior_subset))
    (hf.preimage_open_of_open hs is_open_interior)
... = s ∩ interior (f ⁻¹' t) : by rw [interior_inter, hs.interior_eq]

lemma continuous_on_of_locally_continuous_on {f : α → β} {s : set α}
  (h : ∀x∈s, ∃t, is_open t ∧ x ∈ t ∧ continuous_on f (s ∩ t)) : continuous_on f s :=
begin
  assume x xs,
  rcases h x xs with ⟨t, open_t, xt, ct⟩,
  have := ct x ⟨xs, xt⟩,
  rwa [continuous_within_at, ← nhds_within_restrict _ xt open_t] at this
end

lemma continuous_on_open_of_generate_from {β : Type*} {s : set α} {T : set (set β)} {f : α → β}
  (hs : is_open s) (h : ∀t ∈ T, is_open (s ∩ f⁻¹' t)) :
  @continuous_on α β _ (topological_space.generate_from T) f s :=
begin
  rw continuous_on_open_iff,
  assume t ht,
  induction ht with u hu u v Tu Tv hu hv U hU hU',
  { exact h u hu },
  { simp only [preimage_univ, inter_univ], exact hs },
  { have : s ∩ f ⁻¹' (u ∩ v) = (s ∩ f ⁻¹' u) ∩ (s ∩ f ⁻¹' v),
      by rw [preimage_inter, inter_assoc, inter_left_comm _ s, ← inter_assoc s s, inter_self],
    rw this,
    exact hu.inter hv },
  { rw [preimage_sUnion, inter_Union₂],
    exact is_open_bUnion hU' },
  { exact hs }
end

lemma continuous_within_at.prod {f : α → β} {g : α → γ} {s : set α} {x : α}
  (hf : continuous_within_at f s x) (hg : continuous_within_at g s x) :
  continuous_within_at (λx, (f x, g x)) s x :=
hf.prod_mk_nhds hg

lemma continuous_on.prod {f : α → β} {g : α → γ} {s : set α}
  (hf : continuous_on f s) (hg : continuous_on g s) : continuous_on (λx, (f x, g x)) s :=
λx hx, continuous_within_at.prod (hf x hx) (hg x hx)

lemma inducing.continuous_within_at_iff {f : α → β} {g : β → γ} (hg : inducing g) {s : set α}
  {x : α} : continuous_within_at f s x ↔ continuous_within_at (g ∘ f) s x :=
by simp_rw [continuous_within_at, inducing.tendsto_nhds_iff hg]

lemma inducing.continuous_on_iff {f : α → β} {g : β → γ} (hg : inducing g) {s : set α} :
  continuous_on f s ↔ continuous_on (g ∘ f) s :=
by simp_rw [continuous_on, hg.continuous_within_at_iff]

lemma embedding.continuous_on_iff {f : α → β} {g : β → γ} (hg : embedding g) {s : set α} :
  continuous_on f s ↔ continuous_on (g ∘ f) s :=
inducing.continuous_on_iff hg.1

lemma embedding.map_nhds_within_eq {f : α → β} (hf : embedding f) (s : set α) (x : α) :
  map f (𝓝[s] x) = 𝓝[f '' s] (f x) :=
by rw [nhds_within, map_inf hf.inj, hf.map_nhds_eq, map_principal, ← nhds_within_inter',
  inter_eq_self_of_subset_right (image_subset_range _ _)]

lemma open_embedding.map_nhds_within_preimage_eq {f : α → β} (hf : open_embedding f)
  (s : set β) (x : α) :
  map f (𝓝[f ⁻¹' s] x) = 𝓝[s] (f x) :=
begin
  rw [hf.to_embedding.map_nhds_within_eq, image_preimage_eq_inter_range],
  apply nhds_within_eq_nhds_within (mem_range_self _) hf.open_range,
  rw [inter_assoc, inter_self]
end

lemma continuous_within_at_of_not_mem_closure {f : α → β} {s : set α} {x : α} :
  x ∉ closure s → continuous_within_at f s x :=
begin
  intros hx,
  rw [mem_closure_iff_nhds_within_ne_bot, ne_bot_iff, not_not] at hx,
  rw [continuous_within_at, hx],
  exact tendsto_bot,
end

lemma continuous_on.piecewise' {s t : set α} {f g : α → β} [∀ a, decidable (a ∈ t)]
  (hpf : ∀ a ∈ s ∩ frontier t, tendsto f (𝓝[s ∩ t] a) (𝓝 (piecewise t f g a)))
  (hpg : ∀ a ∈ s ∩ frontier t, tendsto g (𝓝[s ∩ tᶜ] a) (𝓝 (piecewise t f g a)))
  (hf : continuous_on f $ s ∩ t) (hg : continuous_on g $ s ∩ tᶜ) :
  continuous_on (piecewise t f g) s :=
begin
  intros x hx,
  by_cases hx' : x ∈ frontier t,
  { exact (hpf x ⟨hx, hx'⟩).piecewise_nhds_within (hpg x ⟨hx, hx'⟩) },
  { rw [← inter_univ s, ← union_compl_self t, inter_union_distrib_left] at hx ⊢,
    cases hx,
    { apply continuous_within_at.union,
      { exact (hf x hx).congr (λ y hy, piecewise_eq_of_mem _ _ _ hy.2)
          (piecewise_eq_of_mem _ _ _ hx.2) },
      { have : x ∉ closure tᶜ,
          from λ h, hx' ⟨subset_closure hx.2, by rwa closure_compl at h⟩,
        exact continuous_within_at_of_not_mem_closure
          (λ h, this (closure_inter_subset_inter_closure _ _ h).2) } },
    { apply continuous_within_at.union,
      { have : x ∉ closure t,
          from (λ h, hx' ⟨h, (λ (h' : x ∈ interior t), hx.2 (interior_subset h'))⟩),
        exact continuous_within_at_of_not_mem_closure
          (λ h, this (closure_inter_subset_inter_closure _ _ h).2) },
      { exact (hg x hx).congr
          (λ y hy, piecewise_eq_of_not_mem _ _ _ hy.2)
          (piecewise_eq_of_not_mem _ _ _ hx.2) } } }
end

lemma continuous_on.if' {s : set α} {p : α → Prop} {f g : α → β} [∀ a, decidable (p a)]
  (hpf : ∀ a ∈ s ∩ frontier {a | p a},
    tendsto f (𝓝[s ∩ {a | p a}] a) (𝓝 $ if p a then f a else g a))
  (hpg : ∀ a ∈ s ∩ frontier {a | p a},
    tendsto g (𝓝[s ∩ {a | ¬p a}] a) (𝓝 $ if p a then f a else g a))
  (hf : continuous_on f $ s ∩ {a | p a}) (hg : continuous_on g $ s ∩ {a | ¬p a}) :
  continuous_on (λ a, if p a then f a else g a) s :=
hf.piecewise' hpf hpg hg

lemma continuous_on.if {α β : Type*} [topological_space α] [topological_space β] {p : α → Prop}
  [∀ a, decidable (p a)] {s : set α} {f g : α → β}
  (hp : ∀ a ∈ s ∩ frontier {a | p a}, f a = g a) (hf : continuous_on f $ s ∩ closure {a | p a})
  (hg : continuous_on g $ s ∩ closure {a | ¬ p a}) :
  continuous_on (λa, if p a then f a else g a) s :=
begin
  apply continuous_on.if',
  { rintros a ha,
    simp only [← hp a ha, if_t_t],
    apply tendsto_nhds_within_mono_left (inter_subset_inter_right s subset_closure),
    exact hf a ⟨ha.1, ha.2.1⟩ },
  { rintros a ha,
    simp only [hp a ha, if_t_t],
    apply tendsto_nhds_within_mono_left (inter_subset_inter_right s subset_closure),
    rcases ha with ⟨has, ⟨_, ha⟩⟩,
    rw [← mem_compl_iff, ← closure_compl] at ha,
    apply hg a ⟨has, ha⟩ },
  { exact hf.mono (inter_subset_inter_right s subset_closure) },
  { exact hg.mono (inter_subset_inter_right s subset_closure) }
end

lemma continuous_on.piecewise {s t : set α} {f g : α → β} [∀ a, decidable (a ∈ t)]
  (ht : ∀ a ∈ s ∩ frontier t, f a = g a) (hf : continuous_on f $ s ∩ closure t)
  (hg : continuous_on g $ s ∩ closure tᶜ) :
  continuous_on (piecewise t f g) s :=
hf.if ht hg

lemma continuous_if' {p : α → Prop} {f g : α → β} [∀ a, decidable (p a)]
  (hpf : ∀ a ∈ frontier {x | p x}, tendsto f (𝓝[{x | p x}] a) (𝓝 $ ite (p a) (f a) (g a)))
  (hpg : ∀ a ∈ frontier {x | p x}, tendsto g (𝓝[{x | ¬p x}] a) (𝓝 $ ite (p a) (f a) (g a)))
  (hf : continuous_on f {x | p x}) (hg : continuous_on g {x | ¬p x}) :
  continuous (λ a, ite (p a) (f a) (g a)) :=
begin
  rw continuous_iff_continuous_on_univ,
  apply continuous_on.if'; simp *; assumption
end

lemma continuous_if {p : α → Prop} {f g : α → β} [∀ a, decidable (p a)]
  (hp : ∀ a ∈ frontier {x | p x}, f a = g a) (hf : continuous_on f (closure {x | p x}))
  (hg : continuous_on g (closure {x | ¬p x})) :
  continuous (λ a, if p a then f a else g a) :=
begin
  rw continuous_iff_continuous_on_univ,
  apply continuous_on.if; simp; assumption
end

lemma continuous.if {p : α → Prop} {f g : α → β} [∀ a, decidable (p a)]
  (hp : ∀ a ∈ frontier {x | p x}, f a = g a) (hf : continuous f) (hg : continuous g) :
  continuous (λ a, if p a then f a else g a) :=
continuous_if hp hf.continuous_on hg.continuous_on

lemma continuous_if_const (p : Prop) {f g : α → β} [decidable p]
  (hf : p → continuous f) (hg : ¬ p → continuous g) :
  continuous (λ a, if p then f a else g a) :=
by { split_ifs, exact hf h, exact hg h }

lemma continuous.if_const (p : Prop) {f g : α → β} [decidable p]
  (hf : continuous f) (hg : continuous g) :
  continuous (λ a, if p then f a else g a) :=
continuous_if_const p (λ _, hf) (λ _, hg)

lemma continuous_piecewise {s : set α} {f g : α → β} [∀ a, decidable (a ∈ s)]
  (hs : ∀ a ∈ frontier s, f a = g a) (hf : continuous_on f (closure s))
  (hg : continuous_on g (closure sᶜ)) :
  continuous (piecewise s f g) :=
continuous_if hs hf hg

lemma continuous.piecewise {s : set α} {f g : α → β} [∀ a, decidable (a ∈ s)]
  (hs : ∀ a ∈ frontier s, f a = g a) (hf : continuous f) (hg : continuous g) :
  continuous (piecewise s f g) :=
hf.if hs hg

lemma is_open.ite' {s s' t : set α}
  (hs : is_open s) (hs' : is_open s') (ht : ∀ x ∈ frontier t, x ∈ s ↔ x ∈ s') :
  is_open (t.ite s s') :=
begin
  classical,
  simp only [is_open_iff_continuous_mem, set.ite] at *,
  convert continuous_piecewise (λ x hx, propext (ht x hx)) hs.continuous_on hs'.continuous_on,
  ext x, by_cases hx : x ∈ t; simp [hx]
end

lemma is_open.ite {s s' t : set α} (hs : is_open s) (hs' : is_open s')
  (ht : s ∩ frontier t = s' ∩ frontier t) :
  is_open (t.ite s s') :=
hs.ite' hs' $ λ x hx, by simpa [hx] using ext_iff.1 ht x

lemma ite_inter_closure_eq_of_inter_frontier_eq {s s' t : set α}
  (ht : s ∩ frontier t = s' ∩ frontier t) :
  t.ite s s' ∩ closure t = s ∩ closure t :=
by rw [closure_eq_self_union_frontier, inter_union_distrib_left, inter_union_distrib_left,
  ite_inter_self, ite_inter_of_inter_eq _ ht]

lemma ite_inter_closure_compl_eq_of_inter_frontier_eq {s s' t : set α}
  (ht : s ∩ frontier t = s' ∩ frontier t) :
  t.ite s s' ∩ closure tᶜ = s' ∩ closure tᶜ :=
by { rw [← ite_compl, ite_inter_closure_eq_of_inter_frontier_eq], rwa [frontier_compl, eq_comm] }

lemma continuous_on_piecewise_ite' {s s' t : set α} {f f' : α → β} [∀ x, decidable (x ∈ t)]
  (h : continuous_on f (s ∩ closure t)) (h' : continuous_on f' (s' ∩ closure tᶜ))
  (H : s ∩ frontier t = s' ∩ frontier t) (Heq : eq_on f f' (s ∩ frontier t)) :
  continuous_on (t.piecewise f f') (t.ite s s') :=
begin
  apply continuous_on.piecewise,
  { rwa ite_inter_of_inter_eq _ H },
  { rwa ite_inter_closure_eq_of_inter_frontier_eq H },
  { rwa ite_inter_closure_compl_eq_of_inter_frontier_eq H }
end

lemma continuous_on_piecewise_ite {s s' t : set α} {f f' : α → β} [∀ x, decidable (x ∈ t)]
  (h : continuous_on f s) (h' : continuous_on f' s')
  (H : s ∩ frontier t = s' ∩ frontier t) (Heq : eq_on f f' (s ∩ frontier t)) :
  continuous_on (t.piecewise f f') (t.ite s s') :=
continuous_on_piecewise_ite' (h.mono (inter_subset_left _ _)) (h'.mono (inter_subset_left _ _))
  H Heq

lemma frontier_inter_open_inter {s t : set α} (ht : is_open t) :
  frontier (s ∩ t) ∩ t = frontier s ∩ t :=
by simp only [← subtype.preimage_coe_eq_preimage_coe_iff,
  ht.is_open_map_subtype_coe.preimage_frontier_eq_frontier_preimage continuous_subtype_coe,
  subtype.preimage_coe_inter_self]

lemma continuous_on_fst {s : set (α × β)} : continuous_on prod.fst s :=
continuous_fst.continuous_on

lemma continuous_within_at_fst {s : set (α × β)} {p : α × β} :
  continuous_within_at prod.fst s p :=
continuous_fst.continuous_within_at

lemma continuous_on.fst {f : α → β × γ} {s : set α} (hf : continuous_on f s) :
  continuous_on (λ x, (f x).1) s :=
continuous_fst.comp_continuous_on hf

lemma continuous_within_at.fst {f : α → β × γ} {s : set α} {a : α}
  (h : continuous_within_at f s a) : continuous_within_at (λ x, (f x).fst) s a :=
continuous_at_fst.comp_continuous_within_at h

lemma continuous_on_snd {s : set (α × β)} : continuous_on prod.snd s :=
continuous_snd.continuous_on

lemma continuous_within_at_snd {s : set (α × β)} {p : α × β} :
  continuous_within_at prod.snd s p :=
continuous_snd.continuous_within_at

lemma continuous_on.snd {f : α → β × γ} {s : set α} (hf : continuous_on f s) :
  continuous_on (λ x, (f x).2) s :=
continuous_snd.comp_continuous_on hf

lemma continuous_within_at.snd {f : α → β × γ} {s : set α} {a : α}
  (h : continuous_within_at f s a) : continuous_within_at (λ x, (f x).snd) s a :=
continuous_at_snd.comp_continuous_within_at h

lemma continuous_within_at_prod_iff {f : α → β × γ} {s : set α} {x : α} :
  continuous_within_at f s x ↔ continuous_within_at (prod.fst ∘ f) s x ∧
  continuous_within_at (prod.snd ∘ f) s x :=
⟨λ h, ⟨h.fst, h.snd⟩, by { rintro ⟨h1, h2⟩, convert h1.prod h2, ext, refl, refl }⟩


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

! This file was ported from Lean 3 source module algebra.is_prime_pow
! leanprover-community/mathlib commit 13a5329a8625701af92e9a96ffc90fa787fff24d
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Associated
import Mathbin.NumberTheory.Divisors

/-!
# Prime powers

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

This file deals with prime powers: numbers which are positive integer powers of a single prime.
-/

variable {R : Type _} [CommMonoidWithZero R] (n p : R) (k : ℕ)

#print IsPrimePow /-
/-- `n` is a prime power if there is a prime `p` and a positive natural `k` such that `n` can be
written as `p^k`. -/
def IsPrimePow : Prop :=
  ∃ (p : R)(k : ℕ), Prime p ∧ 0 < k ∧ p ^ k = n
#align is_prime_pow IsPrimePow
-/

#print isPrimePow_def /-
theorem isPrimePow_def : IsPrimePow n ↔ ∃ (p : R)(k : ℕ), Prime p ∧ 0 < k ∧ p ^ k = n :=
  Iff.rfl
#align is_prime_pow_def isPrimePow_def
-/

#print isPrimePow_iff_pow_succ /-
/-- An equivalent definition for prime powers: `n` is a prime power iff there is a prime `p` and a
natural `k` such that `n` can be written as `p^(k+1)`. -/
theorem isPrimePow_iff_pow_succ : IsPrimePow n ↔ ∃ (p : R)(k : ℕ), Prime p ∧ p ^ (k + 1) = n :=
  (isPrimePow_def _).trans
    ⟨fun ⟨p, k, hp, hk, hn⟩ => ⟨_, _, hp, by rwa [Nat.sub_add_cancel hk]⟩, fun ⟨p, k, hp, hn⟩ =>
      ⟨_, _, hp, Nat.succ_pos', hn⟩⟩
#align is_prime_pow_iff_pow_succ isPrimePow_iff_pow_succ
-/

/- warning: not_is_prime_pow_zero -> not_isPrimePow_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommMonoidWithZero.{u1} R] [_inst_2 : NoZeroDivisors.{u1} R (MulZeroClass.toHasMul.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))) (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))], Not (IsPrimePow.{u1} R _inst_1 (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommMonoidWithZero.{u1} R] [_inst_2 : NoZeroDivisors.{u1} R (MulZeroClass.toMul.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))) (CommMonoidWithZero.toZero.{u1} R _inst_1)], Not (IsPrimePow.{u1} R _inst_1 (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align not_is_prime_pow_zero not_isPrimePow_zeroₓ'. -/
theorem not_isPrimePow_zero [NoZeroDivisors R] : ¬IsPrimePow (0 : R) :=
  by
  simp only [isPrimePow_def, not_exists, not_and', and_imp]
  intro x n hn hx
  rw [pow_eq_zero hx]
  simp
#align not_is_prime_pow_zero not_isPrimePow_zero

#print IsPrimePow.not_unit /-
theorem IsPrimePow.not_unit {n : R} (h : IsPrimePow n) : ¬IsUnit n :=
  let ⟨p, k, hp, hk, hn⟩ := h
  hn ▸ (isUnit_pow_iff hk.ne').Not.mpr hp.not_unit
#align is_prime_pow.not_unit IsPrimePow.not_unit
-/

#print IsUnit.not_isPrimePow /-
theorem IsUnit.not_isPrimePow {n : R} (h : IsUnit n) : ¬IsPrimePow n := fun h' => h'.not_unit h
#align is_unit.not_is_prime_pow IsUnit.not_isPrimePow
-/

/- warning: not_is_prime_pow_one -> not_isPrimePow_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommMonoidWithZero.{u1} R], Not (IsPrimePow.{u1} R _inst_1 (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (MulOneClass.toHasOne.{u1} R (MulZeroOneClass.toMulOneClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommMonoidWithZero.{u1} R], Not (IsPrimePow.{u1} R _inst_1 (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (Monoid.toOne.{u1} R (MonoidWithZero.toMonoid.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))))
Case conversion may be inaccurate. Consider using '#align not_is_prime_pow_one not_isPrimePow_oneₓ'. -/
theorem not_isPrimePow_one : ¬IsPrimePow (1 : R) :=
  isUnit_one.not_isPrimePow
#align not_is_prime_pow_one not_isPrimePow_one

#print Prime.isPrimePow /-
theorem Prime.isPrimePow {p : R} (hp : Prime p) : IsPrimePow p :=
  ⟨p, 1, hp, zero_lt_one, by simp⟩
#align prime.is_prime_pow Prime.isPrimePow
-/

#print IsPrimePow.pow /-
theorem IsPrimePow.pow {n : R} (hn : IsPrimePow n) {k : ℕ} (hk : k ≠ 0) : IsPrimePow (n ^ k) :=
  let ⟨p, k', hp, hk', hn⟩ := hn
  ⟨p, k * k', hp, mul_pos hk.bot_lt hk', by rw [pow_mul', hn]⟩
#align is_prime_pow.pow IsPrimePow.pow
-/

/- warning: is_prime_pow.ne_zero -> IsPrimePow.ne_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommMonoidWithZero.{u1} R] [_inst_2 : NoZeroDivisors.{u1} R (MulZeroClass.toHasMul.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))) (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))] {n : R}, (IsPrimePow.{u1} R _inst_1 n) -> (Ne.{succ u1} R n (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommMonoidWithZero.{u1} R] [_inst_2 : NoZeroDivisors.{u1} R (MulZeroClass.toMul.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))) (CommMonoidWithZero.toZero.{u1} R _inst_1)] {n : R}, (IsPrimePow.{u1} R _inst_1 n) -> (Ne.{succ u1} R n (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (CommMonoidWithZero.toZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align is_prime_pow.ne_zero IsPrimePow.ne_zeroₓ'. -/
theorem IsPrimePow.ne_zero [NoZeroDivisors R] {n : R} (h : IsPrimePow n) : n ≠ 0 := fun t =>
  Eq.ndrec not_isPrimePow_zero t.symm h
#align is_prime_pow.ne_zero IsPrimePow.ne_zero

/- warning: is_prime_pow.ne_one -> IsPrimePow.ne_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommMonoidWithZero.{u1} R] {n : R}, (IsPrimePow.{u1} R _inst_1 n) -> (Ne.{succ u1} R n (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (MulOneClass.toHasOne.{u1} R (MulZeroOneClass.toMulOneClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommMonoidWithZero.{u1} R] {n : R}, (IsPrimePow.{u1} R _inst_1 n) -> (Ne.{succ u1} R n (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (Monoid.toOne.{u1} R (MonoidWithZero.toMonoid.{u1} R (CommMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))))
Case conversion may be inaccurate. Consider using '#align is_prime_pow.ne_one IsPrimePow.ne_oneₓ'. -/
theorem IsPrimePow.ne_one {n : R} (h : IsPrimePow n) : n ≠ 1 := fun t =>
  Eq.ndrec not_isPrimePow_one t.symm h
#align is_prime_pow.ne_one IsPrimePow.ne_one

section Nat

#print isPrimePow_nat_iff /-
theorem isPrimePow_nat_iff (n : ℕ) : IsPrimePow n ↔ ∃ p k : ℕ, Nat.Prime p ∧ 0 < k ∧ p ^ k = n := by
  simp only [isPrimePow_def, Nat.prime_iff]
#align is_prime_pow_nat_iff isPrimePow_nat_iff
-/

#print Nat.Prime.isPrimePow /-
theorem Nat.Prime.isPrimePow {p : ℕ} (hp : p.Prime) : IsPrimePow p :=
  hp.Prime.IsPrimePow
#align nat.prime.is_prime_pow Nat.Prime.isPrimePow
-/

#print isPrimePow_nat_iff_bounded /-
theorem isPrimePow_nat_iff_bounded (n : ℕ) :
    IsPrimePow n ↔ ∃ p : ℕ, p ≤ n ∧ ∃ k : ℕ, k ≤ n ∧ p.Prime ∧ 0 < k ∧ p ^ k = n :=
  by
  rw [isPrimePow_nat_iff]
  refine' Iff.symm ⟨fun ⟨p, _, k, _, hp, hk, hn⟩ => ⟨p, k, hp, hk, hn⟩, _⟩
  rintro ⟨p, k, hp, hk, rfl⟩
  refine' ⟨p, _, k, (Nat.lt_pow_self hp.one_lt _).le, hp, hk, rfl⟩
  simpa using Nat.pow_le_pow_of_le_right hp.pos hk
#align is_prime_pow_nat_iff_bounded isPrimePow_nat_iff_bounded
-/

instance {n : ℕ} : Decidable (IsPrimePow n) :=
  decidable_of_iff' _ (isPrimePow_nat_iff_bounded n)

#print IsPrimePow.dvd /-
theorem IsPrimePow.dvd {n m : ℕ} (hn : IsPrimePow n) (hm : m ∣ n) (hm₁ : m ≠ 1) : IsPrimePow m :=
  by
  rw [isPrimePow_nat_iff] at hn⊢
  rcases hn with ⟨p, k, hp, hk, rfl⟩
  obtain ⟨i, hik, rfl⟩ := (Nat.dvd_prime_pow hp).1 hm
  refine' ⟨p, i, hp, _, rfl⟩
  apply Nat.pos_of_ne_zero
  rintro rfl
  simpa using hm₁
#align is_prime_pow.dvd IsPrimePow.dvd
-/

/- warning: nat.disjoint_divisors_filter_prime_pow -> Nat.disjoint_divisors_filter_isPrimePow is a dubious translation:
lean 3 declaration is
  forall {a : Nat} {b : Nat}, (Nat.coprime a b) -> (Disjoint.{0} (Finset.{0} Nat) (Finset.partialOrder.{0} Nat) (Finset.orderBot.{0} Nat) (Finset.filter.{0} Nat (IsPrimePow.{0} Nat (LinearOrderedCommMonoidWithZero.toCommMonoidWithZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (fun (a : Nat) => IsPrimePow.decidable a) (Nat.divisors a)) (Finset.filter.{0} Nat (IsPrimePow.{0} Nat (LinearOrderedCommMonoidWithZero.toCommMonoidWithZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (fun (a : Nat) => IsPrimePow.decidable a) (Nat.divisors b)))
but is expected to have type
  forall {a : Nat} {b : Nat}, (Nat.coprime a b) -> (Disjoint.{0} (Finset.{0} Nat) (Finset.partialOrder.{0} Nat) (Finset.instOrderBotFinsetToLEToPreorderPartialOrder.{0} Nat) (Finset.filter.{0} Nat (IsPrimePow.{0} Nat (LinearOrderedCommMonoidWithZero.toCommMonoidWithZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (fun (a : Nat) => instDecidableIsPrimePowNatToCommMonoidWithZeroLinearOrderedCommMonoidWithZero a) (Nat.divisors a)) (Finset.filter.{0} Nat (IsPrimePow.{0} Nat (LinearOrderedCommMonoidWithZero.toCommMonoidWithZero.{0} Nat Nat.linearOrderedCommMonoidWithZero)) (fun (a : Nat) => instDecidableIsPrimePowNatToCommMonoidWithZeroLinearOrderedCommMonoidWithZero a) (Nat.divisors b)))
Case conversion may be inaccurate. Consider using '#align nat.disjoint_divisors_filter_prime_pow Nat.disjoint_divisors_filter_isPrimePowₓ'. -/
theorem Nat.disjoint_divisors_filter_isPrimePow {a b : ℕ} (hab : a.coprime b) :
    Disjoint (a.divisors.filterₓ IsPrimePow) (b.divisors.filterₓ IsPrimePow) :=
  by
  simp only [Finset.disjoint_left, Finset.mem_filter, and_imp, Nat.mem_divisors, not_and]
  rintro n han ha hn hbn hb -
  exact hn.ne_one (Nat.eq_one_of_dvd_coprimes hab han hbn)
#align nat.disjoint_divisors_filter_prime_pow Nat.disjoint_divisors_filter_isPrimePow

#print IsPrimePow.two_le /-
theorem IsPrimePow.two_le : ∀ {n : ℕ}, IsPrimePow n → 2 ≤ n
  | 0, h => (not_isPrimePow_zero h).elim
  | 1, h => (not_isPrimePow_one h).elim
  | n + 2, _ => le_add_self
#align is_prime_pow.two_le IsPrimePow.two_le
-/

#print IsPrimePow.pos /-
theorem IsPrimePow.pos {n : ℕ} (hn : IsPrimePow n) : 0 < n :=
  pos_of_gt hn.two_le
#align is_prime_pow.pos IsPrimePow.pos
-/

#print IsPrimePow.one_lt /-
theorem IsPrimePow.one_lt {n : ℕ} (h : IsPrimePow n) : 1 < n :=
  h.two_le
#align is_prime_pow.one_lt IsPrimePow.one_lt
-/

end Nat


/-
Copyright (c) 2020 Reid Barton. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Reid Barton
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.set.finite
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Infinitude of intervals

Bounded intervals in dense orders are infinite, as are unbounded intervals
in orders that are unbounded on the appropriate side.
-/

namespace set

theorem Ioo.infinite {α : Type u_1} [preorder α] [densely_ordered α] {a : α} {b : α} (h : a < b) : infinite (Ioo a b) := sorry

theorem Ico.infinite {α : Type u_1} [preorder α] [densely_ordered α] {a : α} {b : α} (h : a < b) : infinite (Ico a b) :=
  infinite_mono Ioo_subset_Ico_self (Ioo.infinite h)

theorem Ioc.infinite {α : Type u_1} [preorder α] [densely_ordered α] {a : α} {b : α} (h : a < b) : infinite (Ioc a b) :=
  infinite_mono Ioo_subset_Ioc_self (Ioo.infinite h)

theorem Icc.infinite {α : Type u_1} [preorder α] [densely_ordered α] {a : α} {b : α} (h : a < b) : infinite (Icc a b) :=
  infinite_mono Ioo_subset_Icc_self (Ioo.infinite h)

theorem Iio.infinite {α : Type u_1} [preorder α] [no_bot_order α] {b : α} : infinite (Iio b) := sorry

theorem Iic.infinite {α : Type u_1} [preorder α] [no_bot_order α] {b : α} : infinite (Iic b) :=
  infinite_mono Iio_subset_Iic_self Iio.infinite

theorem Ioi.infinite {α : Type u_1} [preorder α] [no_top_order α] {a : α} : infinite (Ioi a) :=
  Iio.infinite

theorem Ici.infinite {α : Type u_1} [preorder α] [no_top_order α] {a : α} : infinite (Ici a) :=
  infinite_mono Ioi_subset_Ici_self Ioi.infinite


/-
Copyright (c) 2021 Bolton Bailey. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bolton Bailey
-/
import analysis.special_functions.log.basic
import analysis.special_functions.pow

/-!
# Logarithm Tonality

In this file we describe the tonality of the logarithm function when multiplied by functions of the
form `x ^ a`.

## Tags

logarithm, tonality
-/

open set filter function
open_locale topological_space
noncomputable theory

namespace real
variables {x y : ℝ}

lemma log_mul_self_monotone_on : monotone_on (λ x : ℝ, log x * x) {x | 1 ≤ x} :=
begin
  -- TODO: can be strengthened to exp (-1) ≤ x
  simp only [monotone_on, mem_set_of_eq],
  intros x hex y hey hxy,
  have x_pos : 0 < x := lt_of_lt_of_le zero_lt_one hex,
  have y_pos : 0 < y := lt_of_lt_of_le zero_lt_one hey,
  refine mul_le_mul ((log_le_log x_pos y_pos).mpr hxy) hxy (le_of_lt x_pos) _,
  rwa [le_log_iff_exp_le y_pos, real.exp_zero],
end

lemma log_div_self_antitone_on : antitone_on (λ x : ℝ, log x / x) {x | exp 1 ≤ x} :=
begin
  simp only [antitone_on, mem_set_of_eq],
  intros x hex y hey hxy,
  have x_pos : 0 < x := (exp_pos 1).trans_le hex,
  have y_pos : 0 < y := (exp_pos 1).trans_le hey,
  have hlogx : 1 ≤ log x := by rwa le_log_iff_exp_le x_pos,
  have hyx : 0 ≤ y / x - 1 := by rwa [le_sub_iff_add_le, le_div_iff x_pos, zero_add, one_mul],
  rw [div_le_iff y_pos, ←sub_le_sub_iff_right (log x)],
  calc
    log y - log x = log (y / x)           : by rw [log_div (y_pos.ne') (x_pos.ne')]
    ...           ≤ (y / x) - 1           : log_le_sub_one_of_pos (div_pos y_pos x_pos)
    ...           ≤ log x * (y / x - 1)   : le_mul_of_one_le_left hyx hlogx
    ...           = log x / x * y - log x : by ring,
end

lemma log_div_self_rpow_antitone_on {a : ℝ} (ha : 0 < a) :
  antitone_on (λ x : ℝ, log x / x ^ a) {x | exp (1 / a) ≤ x} :=
begin
  simp only [antitone_on, mem_set_of_eq],
  intros x hex y hey hxy,
  have x_pos : 0 < x := lt_of_lt_of_le (exp_pos (1 / a)) hex,
  have y_pos : 0 < y := by linarith,
  have x_nonneg : 0 ≤ x := le_trans (le_of_lt (exp_pos (1 / a))) hex,
  have y_nonneg : 0 ≤ y := by linarith,
  nth_rewrite 0 ←rpow_one y,
  nth_rewrite 0 ←rpow_one x,
  rw [←div_self (ne_of_lt ha).symm, div_eq_mul_one_div a a, rpow_mul y_nonneg, rpow_mul x_nonneg,
    log_rpow (rpow_pos_of_pos y_pos a), log_rpow (rpow_pos_of_pos x_pos a), mul_div_assoc,
    mul_div_assoc, mul_le_mul_left (one_div_pos.mpr ha)],
  { refine log_div_self_antitone_on _ _ _,
    { simp only [set.mem_set_of_eq],
      convert rpow_le_rpow _ hex (le_of_lt ha),
      rw ←exp_mul,
      simp only [real.exp_eq_exp],
      field_simp [(ne_of_lt ha).symm],
      exact le_of_lt (exp_pos (1 / a)), },
    { simp only [set.mem_set_of_eq],
      convert rpow_le_rpow _ (trans hex hxy) (le_of_lt ha),
      rw ←exp_mul,
      simp only [real.exp_eq_exp],
      field_simp [(ne_of_lt ha).symm],
      exact le_of_lt (exp_pos (1 / a)), },
    exact rpow_le_rpow x_nonneg hxy (le_of_lt ha), },
end

lemma log_div_sqrt_antitone_on :
  antitone_on (λ x : ℝ, log x / sqrt x) {x | exp 2 ≤ x} :=
begin
  simp_rw sqrt_eq_rpow,
  convert @log_div_self_rpow_antitone_on (1 / 2) (by norm_num),
  norm_num,
end

end real