/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Abhimanyu Pallavi Sudhir, Jean Lo, Calle Sönne, Sébastien Gouëzel,
  Rémy Degenne
-/
import analysis.special_functions.pow
import analysis.special_functions.complex.log_deriv
import analysis.calculus.extend_deriv
import analysis.special_functions.log.deriv
import analysis.special_functions.trigonometric.deriv

/-!
# Derivatives of power function on `ℂ`, `ℝ`, `ℝ≥0`, and `ℝ≥0∞`

We also prove differentiability and provide derivatives for the power functions `x ^ y`.
-/

noncomputable theory

open_locale classical real topology nnreal ennreal filter
open filter

namespace complex

lemma has_strict_fderiv_at_cpow {p : ℂ × ℂ} (hp : 0 < p.1.re ∨ p.1.im ≠ 0) :
  has_strict_fderiv_at (λ x : ℂ × ℂ, x.1 ^ x.2)
    ((p.2 * p.1 ^ (p.2 - 1)) • continuous_linear_map.fst ℂ ℂ ℂ +
      (p.1 ^ p.2 * log p.1) • continuous_linear_map.snd ℂ ℂ ℂ) p :=
begin
  have A : p.1 ≠ 0, by { intro h, simpa [h, lt_irrefl] using hp },
  have : (λ x : ℂ × ℂ, x.1 ^ x.2) =ᶠ[𝓝 p] (λ x, exp (log x.1 * x.2)),
    from ((is_open_ne.preimage continuous_fst).eventually_mem A).mono
      (λ p hp, cpow_def_of_ne_zero hp _),
  rw [cpow_sub _ _ A, cpow_one, mul_div_left_comm, mul_smul, mul_smul, ← smul_add],
  refine has_strict_fderiv_at.congr_of_eventually_eq _ this.symm,
  simpa only [cpow_def_of_ne_zero A, div_eq_mul_inv, mul_smul, add_comm]
    using ((has_strict_fderiv_at_fst.clog hp).mul has_strict_fderiv_at_snd).cexp
end

lemma has_strict_fderiv_at_cpow' {x y : ℂ} (hp : 0 < x.re ∨ x.im ≠ 0) :
  has_strict_fderiv_at (λ x : ℂ × ℂ, x.1 ^ x.2)
    ((y * x ^ (y - 1)) • continuous_linear_map.fst ℂ ℂ ℂ +
      (x ^ y * log x) • continuous_linear_map.snd ℂ ℂ ℂ) (x, y) :=
@has_strict_fderiv_at_cpow (x, y) hp

lemma has_strict_deriv_at_const_cpow {x y : ℂ} (h : x ≠ 0 ∨ y ≠ 0) :
  has_strict_deriv_at (λ y, x ^ y) (x ^ y * log x) y :=
begin
  rcases em (x = 0) with rfl|hx,
  { replace h := h.neg_resolve_left rfl,
    rw [log_zero, mul_zero],
    refine (has_strict_deriv_at_const _ 0).congr_of_eventually_eq _,
    exact (is_open_ne.eventually_mem h).mono (λ y hy, (zero_cpow hy).symm) },
  { simpa only [cpow_def_of_ne_zero hx, mul_one]
      using ((has_strict_deriv_at_id y).const_mul (log x)).cexp }
end

lemma has_fderiv_at_cpow {p : ℂ × ℂ} (hp : 0 < p.1.re ∨ p.1.im ≠ 0) :
  has_fderiv_at (λ x : ℂ × ℂ, x.1 ^ x.2)
    ((p.2 * p.1 ^ (p.2 - 1)) • continuous_linear_map.fst ℂ ℂ ℂ +
      (p.1 ^ p.2 * log p.1) • continuous_linear_map.snd ℂ ℂ ℂ) p :=
(has_strict_fderiv_at_cpow hp).has_fderiv_at

end complex

section fderiv

open complex

variables {E : Type*} [normed_add_comm_group E] [normed_space ℂ E] {f g : E → ℂ} {f' g' : E →L[ℂ] ℂ}
  {x : E} {s : set E} {c : ℂ}

lemma has_strict_fderiv_at.cpow (hf : has_strict_fderiv_at f f' x)
  (hg : has_strict_fderiv_at g g' x) (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  has_strict_fderiv_at (λ x, f x ^ g x)
    ((g x * f x ^ (g x - 1)) • f' + (f x ^ g x * log (f x)) • g') x :=
by convert (@has_strict_fderiv_at_cpow ((λ x, (f x, g x)) x) h0).comp x (hf.prod hg)

lemma has_strict_fderiv_at.const_cpow (hf : has_strict_fderiv_at f f' x) (h0 : c ≠ 0 ∨ f x ≠ 0) :
  has_strict_fderiv_at (λ x, c ^ f x) ((c ^ f x * log c) • f') x :=
(has_strict_deriv_at_const_cpow h0).comp_has_strict_fderiv_at x hf

lemma has_fderiv_at.cpow (hf : has_fderiv_at f f' x) (hg : has_fderiv_at g g' x)
  (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  has_fderiv_at (λ x, f x ^ g x)
    ((g x * f x ^ (g x - 1)) • f' + (f x ^ g x * log (f x)) • g') x :=
by convert (@complex.has_fderiv_at_cpow ((λ x, (f x, g x)) x) h0).comp x (hf.prod hg)

lemma has_fderiv_at.const_cpow (hf : has_fderiv_at f f' x) (h0 : c ≠ 0 ∨ f x ≠ 0) :
  has_fderiv_at (λ x, c ^ f x) ((c ^ f x * log c) • f') x :=
(has_strict_deriv_at_const_cpow h0).has_deriv_at.comp_has_fderiv_at x hf

lemma has_fderiv_within_at.cpow (hf : has_fderiv_within_at f f' s x)
  (hg : has_fderiv_within_at g g' s x) (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  has_fderiv_within_at (λ x, f x ^ g x)
    ((g x * f x ^ (g x - 1)) • f' + (f x ^ g x * log (f x)) • g') s x :=
by convert (@complex.has_fderiv_at_cpow ((λ x, (f x, g x)) x) h0).comp_has_fderiv_within_at x
  (hf.prod hg)

lemma has_fderiv_within_at.const_cpow (hf : has_fderiv_within_at f f' s x) (h0 : c ≠ 0 ∨ f x ≠ 0) :
  has_fderiv_within_at (λ x, c ^ f x) ((c ^ f x * log c) • f') s x :=
(has_strict_deriv_at_const_cpow h0).has_deriv_at.comp_has_fderiv_within_at x hf

lemma differentiable_at.cpow (hf : differentiable_at ℂ f x) (hg : differentiable_at ℂ g x)
  (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  differentiable_at ℂ (λ x, f x ^ g x) x :=
(hf.has_fderiv_at.cpow hg.has_fderiv_at h0).differentiable_at

lemma differentiable_at.const_cpow (hf : differentiable_at ℂ f x) (h0 : c ≠ 0 ∨ f x ≠ 0) :
  differentiable_at ℂ (λ x, c ^ f x) x :=
(hf.has_fderiv_at.const_cpow h0).differentiable_at

lemma differentiable_within_at.cpow (hf : differentiable_within_at ℂ f s x)
  (hg : differentiable_within_at ℂ g s x) (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  differentiable_within_at ℂ (λ x, f x ^ g x) s x :=
(hf.has_fderiv_within_at.cpow hg.has_fderiv_within_at h0).differentiable_within_at

lemma differentiable_within_at.const_cpow (hf : differentiable_within_at ℂ f s x)
  (h0 : c ≠ 0 ∨ f x ≠ 0) :
  differentiable_within_at ℂ (λ x, c ^ f x) s x :=
(hf.has_fderiv_within_at.const_cpow h0).differentiable_within_at

end fderiv

section deriv

open complex

variables {f g : ℂ → ℂ} {s : set ℂ} {f' g' x c : ℂ}

/-- A private lemma that rewrites the output of lemmas like `has_fderiv_at.cpow` to the form
expected by lemmas like `has_deriv_at.cpow`. -/
private lemma aux :
  ((g x * f x ^ (g x - 1)) • (1 : ℂ →L[ℂ] ℂ).smul_right f' +
    (f x ^ g x * log (f x)) • (1 : ℂ →L[ℂ] ℂ).smul_right g') 1 =
    g x * f x ^ (g x - 1) * f' + f x ^ g x * log (f x) * g' :=
by simp only [algebra.id.smul_eq_mul, one_mul, continuous_linear_map.one_apply,
  continuous_linear_map.smul_right_apply, continuous_linear_map.add_apply, pi.smul_apply,
  continuous_linear_map.coe_smul']

lemma has_strict_deriv_at.cpow (hf : has_strict_deriv_at f f' x) (hg : has_strict_deriv_at g g' x)
  (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  has_strict_deriv_at (λ x, f x ^ g x)
    (g x * f x ^ (g x - 1) * f' + f x ^ g x * log (f x) * g') x :=
by simpa only [aux] using (hf.cpow hg h0).has_strict_deriv_at

lemma has_strict_deriv_at.const_cpow (hf : has_strict_deriv_at f f' x) (h : c ≠ 0 ∨ f x ≠ 0) :
  has_strict_deriv_at (λ x, c ^ f x) (c ^ f x * log c * f') x :=
(has_strict_deriv_at_const_cpow h).comp x hf

lemma complex.has_strict_deriv_at_cpow_const (h : 0 < x.re ∨ x.im ≠ 0) :
  has_strict_deriv_at (λ z : ℂ, z ^ c) (c * x ^ (c - 1)) x :=
by simpa only [mul_zero, add_zero, mul_one]
  using (has_strict_deriv_at_id x).cpow (has_strict_deriv_at_const x c) h

lemma has_strict_deriv_at.cpow_const (hf : has_strict_deriv_at f f' x)
  (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  has_strict_deriv_at (λ x, f x ^ c) (c * f x ^ (c - 1) * f') x :=
(complex.has_strict_deriv_at_cpow_const h0).comp x hf

lemma has_deriv_at.cpow (hf : has_deriv_at f f' x) (hg : has_deriv_at g g' x)
  (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  has_deriv_at (λ x, f x ^ g x) (g x * f x ^ (g x - 1) * f' + f x ^ g x * log (f x) * g') x :=
by simpa only [aux] using (hf.has_fderiv_at.cpow hg h0).has_deriv_at

lemma has_deriv_at.const_cpow (hf : has_deriv_at f f' x) (h0 : c ≠ 0 ∨ f x ≠ 0) :
  has_deriv_at (λ x, c ^ f x) (c ^ f x * log c * f') x :=
(has_strict_deriv_at_const_cpow h0).has_deriv_at.comp x hf

lemma has_deriv_at.cpow_const (hf : has_deriv_at f f' x) (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  has_deriv_at (λ x, f x ^ c) (c * f x ^ (c - 1) * f') x :=
(complex.has_strict_deriv_at_cpow_const h0).has_deriv_at.comp x hf

lemma has_deriv_within_at.cpow (hf : has_deriv_within_at f f' s x)
  (hg : has_deriv_within_at g g' s x) (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  has_deriv_within_at (λ x, f x ^ g x)
    (g x * f x ^ (g x - 1) * f' + f x ^ g x * log (f x) * g') s x :=
by simpa only [aux] using (hf.has_fderiv_within_at.cpow hg h0).has_deriv_within_at

lemma has_deriv_within_at.const_cpow (hf : has_deriv_within_at f f' s x) (h0 : c ≠ 0 ∨ f x ≠ 0) :
  has_deriv_within_at (λ x, c ^ f x) (c ^ f x * log c * f') s x :=
(has_strict_deriv_at_const_cpow h0).has_deriv_at.comp_has_deriv_within_at x hf

lemma has_deriv_within_at.cpow_const (hf : has_deriv_within_at f f' s x)
  (h0 : 0 < (f x).re ∨ (f x).im ≠ 0) :
  has_deriv_within_at (λ x, f x ^ c) (c * f x ^ (c - 1) * f') s x :=
(complex.has_strict_deriv_at_cpow_const h0).has_deriv_at.comp_has_deriv_within_at x hf

/-- Although `λ x, x ^ r` for fixed `r` is *not* complex-differentiable along the negative real
line, it is still real-differentiable, and the derivative is what one would formally expect. -/
lemma has_deriv_at_of_real_cpow {x : ℝ} (hx : x ≠ 0) {r : ℂ} (hr : r ≠ -1) :
  has_deriv_at (λ y:ℝ, (y:ℂ) ^ (r + 1) / (r + 1)) (x ^ r) x :=
begin
  rw [ne.def, ←add_eq_zero_iff_eq_neg, ←ne.def] at hr,
  rcases lt_or_gt_of_ne hx.symm with hx | hx,
  { -- easy case : `0 < x`
    convert (((has_deriv_at_id (x:ℂ)).cpow_const _).div_const (r + 1)).comp_of_real,
    { rw [add_sub_cancel, id.def, mul_one, mul_comm, mul_div_cancel _ hr] },
    { rw [id.def, of_real_re], exact or.inl hx } },
  { -- harder case : `x < 0`
    have : ∀ᶠ (y:ℝ) in nhds x, (y:ℂ) ^ (r + 1) / (r + 1) =
      (-y:ℂ) ^ (r + 1) * exp (π * I * (r + 1)) / (r + 1),
    { refine filter.eventually_of_mem (Iio_mem_nhds hx) (λ y hy, _),
      rw of_real_cpow_of_nonpos (le_of_lt hy) },
    refine has_deriv_at.congr_of_eventually_eq _ this,
    rw of_real_cpow_of_nonpos (le_of_lt hx),
    suffices : has_deriv_at (λ (y : ℝ), (-↑y) ^ (r + 1) * exp (↑π * I * (r + 1)))
      ((r + 1) * (-↑x) ^ r * exp (↑π * I * r)) x,
    { convert this.div_const (r + 1) using 1,
      conv_rhs { rw [mul_assoc, mul_comm, mul_div_cancel _ hr] } },
    rw [mul_add ((π:ℂ) * _), mul_one, exp_add, exp_pi_mul_I,
      mul_comm (_ : ℂ) (-1 : ℂ), neg_one_mul],
    simp_rw [mul_neg, ←neg_mul, ←of_real_neg],
    suffices : has_deriv_at (λ (y : ℝ), (↑-y) ^ (r + 1)) (-(r + 1) * (↑-x) ^ r) x,
    { convert this.neg.mul_const _, ring },
    suffices : has_deriv_at (λ (y : ℝ), (↑y) ^ (r + 1)) ((r + 1) * (↑-x) ^ r) (-x),
    { convert @has_deriv_at.scomp ℝ _ ℂ _ _ x ℝ _ _ _ _ _ _ _ _ this (has_deriv_at_neg x) using 1,
      rw [real_smul, of_real_neg 1, of_real_one], ring },
    suffices : has_deriv_at (λ (y : ℂ), y ^ (r + 1)) ((r + 1) * (↑-x) ^ r) (↑-x),
    { exact this.comp_of_real },
    conv in ((↑_) ^ _) { rw (by ring : r = (r + 1) - 1) },
    convert (has_deriv_at_id ((-x : ℝ) : ℂ)).cpow_const _ using 1,
    { simp },
    { left, rwa [id.def, of_real_re, neg_pos] } },
end

end deriv

namespace real

variables {x y z : ℝ}

/-- `(x, y) ↦ x ^ y` is strictly differentiable at `p : ℝ × ℝ` such that `0 < p.fst`. -/
lemma has_strict_fderiv_at_rpow_of_pos (p : ℝ × ℝ) (hp : 0 < p.1) :
  has_strict_fderiv_at (λ x : ℝ × ℝ, x.1 ^ x.2)
    ((p.2 * p.1 ^ (p.2 - 1)) • continuous_linear_map.fst ℝ ℝ ℝ +
      (p.1 ^ p.2 * log p.1) • continuous_linear_map.snd ℝ ℝ ℝ) p :=
begin
  have : (λ x : ℝ × ℝ, x.1 ^ x.2) =ᶠ[𝓝 p] (λ x, exp (log x.1 * x.2)),
    from (continuous_at_fst.eventually (lt_mem_nhds hp)).mono (λ p hp, rpow_def_of_pos hp _),
  refine has_strict_fderiv_at.congr_of_eventually_eq _ this.symm,
  convert ((has_strict_fderiv_at_fst.log hp.ne').mul has_strict_fderiv_at_snd).exp,
  rw [rpow_sub_one hp.ne', ← rpow_def_of_pos hp, smul_add, smul_smul, mul_div_left_comm,
    div_eq_mul_inv, smul_smul, smul_smul, mul_assoc, add_comm]
end

/-- `(x, y) ↦ x ^ y` is strictly differentiable at `p : ℝ × ℝ` such that `p.fst < 0`. -/
lemma has_strict_fderiv_at_rpow_of_neg (p : ℝ × ℝ) (hp : p.1 < 0) :
  has_strict_fderiv_at (λ x : ℝ × ℝ, x.1 ^ x.2)
    ((p.2 * p.1 ^ (p.2 - 1)) • continuous_linear_map.fst ℝ ℝ ℝ +
      (p.1 ^ p.2 * log p.1 - exp (log p.1 * p.2) * sin (p.2 * π) * π) •
        continuous_linear_map.snd ℝ ℝ ℝ) p :=
begin
  have : (λ x : ℝ × ℝ, x.1 ^ x.2) =ᶠ[𝓝 p] (λ x, exp (log x.1 * x.2) * cos (x.2 * π)),
    from (continuous_at_fst.eventually (gt_mem_nhds hp)).mono (λ p hp, rpow_def_of_neg hp _),
  refine has_strict_fderiv_at.congr_of_eventually_eq _ this.symm,
  convert ((has_strict_fderiv_at_fst.log hp.ne).mul has_strict_fderiv_at_snd).exp.mul
    (has_strict_fderiv_at_snd.mul_const _).cos using 1,
  simp_rw [rpow_sub_one hp.ne, smul_add, ← add_assoc, smul_smul, ← add_smul, ← mul_assoc,
    mul_comm (cos _), ← rpow_def_of_neg hp],
  rw [div_eq_mul_inv, add_comm], congr' 2; ring
end

/-- The function `λ (x, y), x ^ y` is infinitely smooth at `(x, y)` unless `x = 0`. -/
lemma cont_diff_at_rpow_of_ne (p : ℝ × ℝ) (hp : p.1 ≠ 0) {n : ℕ∞} :
  cont_diff_at ℝ n (λ p : ℝ × ℝ, p.1 ^ p.2) p :=
begin
  cases hp.lt_or_lt with hneg hpos,
  exacts [(((cont_diff_at_fst.log hneg.ne).mul cont_diff_at_snd).exp.mul
    (cont_diff_at_snd.mul cont_diff_at_const).cos).congr_of_eventually_eq
      ((continuous_at_fst.eventually (gt_mem_nhds hneg)).mono (λ p hp, rpow_def_of_neg hp _)),
    ((cont_diff_at_fst.log hpos.ne').mul cont_diff_at_snd).exp.congr_of_eventually_eq
      ((continuous_at_fst.eventually (lt_mem_nhds hpos)).mono (λ p hp, rpow_def_of_pos hp _))]
end

lemma differentiable_at_rpow_of_ne (p : ℝ × ℝ) (hp : p.1 ≠ 0) :
  differentiable_at ℝ (λ p : ℝ × ℝ, p.1 ^ p.2) p :=
(cont_diff_at_rpow_of_ne p hp).differentiable_at le_rfl

lemma _root_.has_strict_deriv_at.rpow {f g : ℝ → ℝ} {f' g' : ℝ} (hf : has_strict_deriv_at f f' x)
  (hg : has_strict_deriv_at g g' x) (h : 0 < f x) :
  has_strict_deriv_at (λ x, f x ^ g x)
    (f' * g x * (f x) ^ (g x - 1) + g' * f x ^ g x * log (f x)) x :=
begin
  convert (has_strict_fderiv_at_rpow_of_pos ((λ x, (f x, g x)) x) h).comp_has_strict_deriv_at _
    (hf.prod hg) using 1,
  simp [mul_assoc, mul_comm, mul_left_comm]
end

lemma has_strict_deriv_at_rpow_const_of_ne {x : ℝ} (hx : x ≠ 0) (p : ℝ) :
  has_strict_deriv_at (λ x, x ^ p) (p * x ^ (p - 1)) x :=
begin
  cases hx.lt_or_lt with hx hx,
  { have := (has_strict_fderiv_at_rpow_of_neg (x, p) hx).comp_has_strict_deriv_at x
      ((has_strict_deriv_at_id x).prod (has_strict_deriv_at_const _ _)),
    convert this, simp },
  { simpa using (has_strict_deriv_at_id x).rpow (has_strict_deriv_at_const x p) hx }
end

lemma has_strict_deriv_at_const_rpow {a : ℝ} (ha : 0 < a) (x : ℝ) :
  has_strict_deriv_at (λ x, a ^ x) (a ^ x * log a) x :=
by simpa using (has_strict_deriv_at_const _ _).rpow (has_strict_deriv_at_id x) ha

/-- This lemma says that `λ x, a ^ x` is strictly differentiable for `a < 0`. Note that these
values of `a` are outside of the "official" domain of `a ^ x`, and we may redefine `a ^ x`
for negative `a` if some other definition will be more convenient. -/
lemma has_strict_deriv_at_const_rpow_of_neg {a x : ℝ} (ha : a < 0) :
  has_strict_deriv_at (λ x, a ^ x) (a ^ x * log a - exp (log a * x) * sin (x * π) * π) x :=
by simpa using (has_strict_fderiv_at_rpow_of_neg (a, x) ha).comp_has_strict_deriv_at x
  ((has_strict_deriv_at_const _ _).prod (has_strict_deriv_at_id _))

end real

namespace real

variables {z x y : ℝ}

lemma has_deriv_at_rpow_const {x p : ℝ} (h : x ≠ 0 ∨ 1 ≤ p) :
  has_deriv_at (λ x, x ^ p) (p * x ^ (p - 1)) x :=
begin
  rcases ne_or_eq x 0 with hx | rfl,
  { exact (has_strict_deriv_at_rpow_const_of_ne hx _).has_deriv_at },
  replace h : 1 ≤ p := h.neg_resolve_left rfl,
  apply has_deriv_at_of_has_deriv_at_of_ne
    (λ x hx, (has_strict_deriv_at_rpow_const_of_ne hx p).has_deriv_at),
  exacts [continuous_at_id.rpow_const (or.inr (zero_le_one.trans h)),
    continuous_at_const.mul (continuous_at_id.rpow_const (or.inr (sub_nonneg.2 h)))]
end

lemma differentiable_rpow_const {p : ℝ} (hp : 1 ≤ p) :
  differentiable ℝ (λ x : ℝ, x ^ p) :=
λ x, (has_deriv_at_rpow_const (or.inr hp)).differentiable_at

lemma deriv_rpow_const {x p : ℝ} (h : x ≠ 0 ∨ 1 ≤ p) :
  deriv (λ x : ℝ, x ^ p) x = p * x ^ (p - 1) :=
(has_deriv_at_rpow_const h).deriv

lemma deriv_rpow_const' {p : ℝ} (h : 1 ≤ p) :
  deriv (λ x : ℝ, x ^ p) = λ x, p * x ^ (p - 1) :=
funext $ λ x, deriv_rpow_const (or.inr h)

lemma cont_diff_at_rpow_const_of_ne {x p : ℝ} {n : ℕ∞} (h : x ≠ 0) :
  cont_diff_at ℝ n (λ x, x ^ p) x :=
(cont_diff_at_rpow_of_ne (x, p) h).comp x
  (cont_diff_at_id.prod cont_diff_at_const)

lemma cont_diff_rpow_const_of_le {p : ℝ} {n : ℕ} (h : ↑n ≤ p) :
  cont_diff ℝ n (λ x : ℝ, x ^ p) :=
begin
  induction n with n ihn generalizing p,
  { exact cont_diff_zero.2 (continuous_id.rpow_const (λ x, by exact_mod_cast or.inr h)) },
  { have h1 : 1 ≤ p, from le_trans (by simp) h,
    rw [nat.cast_succ, ← le_sub_iff_add_le] at h,
    rw [cont_diff_succ_iff_deriv, deriv_rpow_const' h1],
    refine ⟨differentiable_rpow_const h1, cont_diff_const.mul (ihn h)⟩ }
end

lemma cont_diff_at_rpow_const_of_le {x p : ℝ} {n : ℕ} (h : ↑n ≤ p) :
  cont_diff_at ℝ n (λ x : ℝ, x ^ p) x :=
(cont_diff_rpow_const_of_le h).cont_diff_at

lemma has_strict_deriv_at_rpow_const {x p : ℝ} (hx : x ≠ 0 ∨ 1 ≤ p) :
  has_strict_deriv_at (λ x, x ^ p) (p * x ^ (p - 1)) x :=
cont_diff_at.has_strict_deriv_at'
  (cont_diff_at_rpow_const (by rwa nat.cast_one))
  (has_deriv_at_rpow_const hx) le_rfl

end real

section differentiability
open real

section fderiv

variables {E : Type*} [normed_add_comm_group E] [normed_space ℝ E] {f g : E → ℝ} {f' g' : E →L[ℝ] ℝ}
  {x : E} {s : set E} {c p : ℝ} {n : ℕ∞}

lemma has_fderiv_within_at.rpow (hf : has_fderiv_within_at f f' s x)
  (hg : has_fderiv_within_at g g' s x) (h : 0 < f x) :
  has_fderiv_within_at (λ x, f x ^ g x)
    ((g x * f x ^ (g x - 1)) • f' + (f x ^ g x * log (f x)) • g') s x :=
(has_strict_fderiv_at_rpow_of_pos (f x, g x) h).has_fderiv_at.comp_has_fderiv_within_at x
  (hf.prod hg)

lemma has_fderiv_at.rpow (hf : has_fderiv_at f f' x) (hg : has_fderiv_at g g' x) (h : 0 < f x) :
  has_fderiv_at (λ x, f x ^ g x) ((g x * f x ^ (g x - 1)) • f' + (f x ^ g x * log (f x)) • g') x :=
(has_strict_fderiv_at_rpow_of_pos (f x, g x) h).has_fderiv_at.comp x (hf.prod hg)

lemma has_strict_fderiv_at.rpow (hf : has_strict_fderiv_at f f' x)
  (hg : has_strict_fderiv_at g g' x) (h : 0 < f x) :
  has_strict_fderiv_at (λ x, f x ^ g x)
    ((g x * f x ^ (g x - 1)) • f' + (f x ^ g x * log (f x)) • g') x :=
(has_strict_fderiv_at_rpow_of_pos (f x, g x) h).comp x (hf.prod hg)

lemma differentiable_within_at.rpow (hf : differentiable_within_at ℝ f s x)
  (hg : differentiable_within_at ℝ g s x) (h : f x ≠ 0) :
  differentiable_within_at ℝ (λ x, f x ^ g x) s x :=
(differentiable_at_rpow_of_ne (f x, g x) h).comp_differentiable_within_at x (hf.prod hg)

lemma differentiable_at.rpow (hf : differentiable_at ℝ f x) (hg : differentiable_at ℝ g x)
  (h : f x ≠ 0) :
  differentiable_at ℝ (λ x, f x ^ g x) x :=
(differentiable_at_rpow_of_ne (f x, g x) h).comp x (hf.prod hg)

lemma differentiable_on.rpow (hf : differentiable_on ℝ f s) (hg : differentiable_on ℝ g s)
  (h : ∀ x ∈ s, f x ≠ 0) :
  differentiable_on ℝ (λ x, f x ^ g x) s :=
λ x hx, (hf x hx).rpow (hg x hx) (h x hx)

lemma differentiable.rpow (hf : differentiable ℝ f) (hg : differentiable ℝ g) (h : ∀ x, f x ≠ 0) :
  differentiable ℝ (λ x, f x ^ g x) :=
λ x, (hf x).rpow (hg x) (h x)

lemma has_fderiv_within_at.rpow_const (hf : has_fderiv_within_at f f' s x) (h : f x ≠ 0 ∨ 1 ≤ p) :
  has_fderiv_within_at (λ x, f x ^ p) ((p * f x ^ (p - 1)) • f') s x :=
(has_deriv_at_rpow_const h).comp_has_fderiv_within_at x hf

lemma has_fderiv_at.rpow_const (hf : has_fderiv_at f f' x) (h : f x ≠ 0 ∨ 1 ≤ p) :
  has_fderiv_at (λ x, f x ^ p) ((p * f x ^ (p - 1)) • f') x :=
(has_deriv_at_rpow_const h).comp_has_fderiv_at x hf

lemma has_strict_fderiv_at.rpow_const (hf : has_strict_fderiv_at f f' x) (h : f x ≠ 0 ∨ 1 ≤ p) :
  has_strict_fderiv_at (λ x, f x ^ p) ((p * f x ^ (p - 1)) • f') x :=
(has_strict_deriv_at_rpow_const h).comp_has_strict_fderiv_at x hf

lemma differentiable_within_at.rpow_const (hf : differentiable_within_at ℝ f s x)
  (h : f x ≠ 0 ∨ 1 ≤ p) :
  differentiable_within_at ℝ (λ x, f x ^ p) s x :=
(hf.has_fderiv_within_at.rpow_const h).differentiable_within_at

@[simp] lemma differentiable_at.rpow_const (hf : differentiable_at ℝ f x) (h : f x ≠ 0 ∨ 1 ≤ p) :
  differentiable_at ℝ (λ x, f x ^ p) x :=
(hf.has_fderiv_at.rpow_const h).differentiable_at

lemma differentiable_on.rpow_const (hf : differentiable_on ℝ f s) (h : ∀ x ∈ s, f x ≠ 0 ∨ 1 ≤ p) :
  differentiable_on ℝ (λ x, f x ^ p) s :=
λ x hx, (hf x hx).rpow_const (h x hx)

lemma differentiable.rpow_const (hf : differentiable ℝ f) (h : ∀ x, f x ≠ 0 ∨ 1 ≤ p) :
  differentiable ℝ (λ x, f x ^ p) :=
λ x, (hf x).rpow_const (h x)

lemma has_fderiv_within_at.const_rpow (hf : has_fderiv_within_at f f' s x) (hc : 0 < c) :
  has_fderiv_within_at (λ x, c ^ f x) ((c ^ f x * log c) • f') s x :=
(has_strict_deriv_at_const_rpow hc (f x)).has_deriv_at.comp_has_fderiv_within_at x hf

lemma has_fderiv_at.const_rpow (hf : has_fderiv_at f f' x) (hc : 0 < c) :
  has_fderiv_at (λ x, c ^ f x) ((c ^ f x * log c) • f') x :=
(has_strict_deriv_at_const_rpow hc (f x)).has_deriv_at.comp_has_fderiv_at x hf

lemma has_strict_fderiv_at.const_rpow (hf : has_strict_fderiv_at f f' x) (hc : 0 < c) :
  has_strict_fderiv_at (λ x, c ^ f x) ((c ^ f x * log c) • f') x :=
(has_strict_deriv_at_const_rpow hc (f x)).comp_has_strict_fderiv_at x hf

lemma cont_diff_within_at.rpow (hf : cont_diff_within_at ℝ n f s x)
  (hg : cont_diff_within_at ℝ n g s x) (h : f x ≠ 0) :
  cont_diff_within_at ℝ n (λ x, f x ^ g x) s x :=
(cont_diff_at_rpow_of_ne (f x, g x) h).comp_cont_diff_within_at x (hf.prod hg)

lemma cont_diff_at.rpow (hf : cont_diff_at ℝ n f x) (hg : cont_diff_at ℝ n g x)
  (h : f x ≠ 0) :
  cont_diff_at ℝ n (λ x, f x ^ g x) x :=
(cont_diff_at_rpow_of_ne (f x, g x) h).comp x (hf.prod hg)

lemma cont_diff_on.rpow (hf : cont_diff_on ℝ n f s) (hg : cont_diff_on ℝ n g s)
  (h : ∀ x ∈ s, f x ≠ 0) :
  cont_diff_on ℝ n (λ x, f x ^ g x) s :=
λ x hx, (hf x hx).rpow (hg x hx) (h x hx)

lemma cont_diff.rpow (hf : cont_diff ℝ n f) (hg : cont_diff ℝ n g)
  (h : ∀ x, f x ≠ 0) :
  cont_diff ℝ n (λ x, f x ^ g x) :=
cont_diff_iff_cont_diff_at.mpr $
  λ x, hf.cont_diff_at.rpow hg.cont_diff_at (h x)

lemma cont_diff_within_at.rpow_const_of_ne (hf : cont_diff_within_at ℝ n f s x)
  (h : f x ≠ 0) :
  cont_diff_within_at ℝ n (λ x, f x ^ p) s x :=
hf.rpow cont_diff_within_at_const h

lemma cont_diff_at.rpow_const_of_ne (hf : cont_diff_at ℝ n f x) (h : f x ≠ 0) :
  cont_diff_at ℝ n (λ x, f x ^ p) x :=
hf.rpow cont_diff_at_const h

lemma cont_diff_on.rpow_const_of_ne (hf : cont_diff_on ℝ n f s) (h : ∀ x ∈ s, f x ≠ 0) :
  cont_diff_on ℝ n (λ x, f x ^ p) s :=
λ x hx, (hf x hx).rpow_const_of_ne (h x hx)

lemma cont_diff.rpow_const_of_ne (hf : cont_diff ℝ n f) (h : ∀ x, f x ≠ 0) :
  cont_diff ℝ n (λ x, f x ^ p) :=
hf.rpow cont_diff_const h

variable {m : ℕ}

lemma cont_diff_within_at.rpow_const_of_le (hf : cont_diff_within_at ℝ m f s x)
  (h : ↑m ≤ p) :
  cont_diff_within_at ℝ m (λ x, f x ^ p) s x :=
(cont_diff_at_rpow_const_of_le h).comp_cont_diff_within_at x hf

lemma cont_diff_at.rpow_const_of_le (hf : cont_diff_at ℝ m f x) (h : ↑m ≤ p) :
  cont_diff_at ℝ m (λ x, f x ^ p) x :=
by { rw ← cont_diff_within_at_univ at *, exact hf.rpow_const_of_le h }

lemma cont_diff_on.rpow_const_of_le (hf : cont_diff_on ℝ m f s) (h : ↑m ≤ p) :
  cont_diff_on ℝ m (λ x, f x ^ p) s :=
λ x hx, (hf x hx).rpow_const_of_le h

lemma cont_diff.rpow_const_of_le (hf : cont_diff ℝ m f) (h : ↑m ≤ p) :
  cont_diff ℝ m (λ x, f x ^ p) :=
cont_diff_iff_cont_diff_at.mpr $ λ x, hf.cont_diff_at.rpow_const_of_le h

end fderiv

section deriv

variables {f g : ℝ → ℝ} {f' g' x y p : ℝ} {s : set ℝ}

lemma has_deriv_within_at.rpow (hf : has_deriv_within_at f f' s x)
  (hg : has_deriv_within_at g g' s x) (h : 0 < f x) :
  has_deriv_within_at (λ x, f x ^ g x)
    (f' * g x * (f x) ^ (g x - 1) + g' * f x ^ g x * log (f x)) s x :=
begin
  convert (hf.has_fderiv_within_at.rpow hg.has_fderiv_within_at h).has_deriv_within_at using 1,
  dsimp, ring
end

lemma has_deriv_at.rpow (hf : has_deriv_at f f' x) (hg : has_deriv_at g g' x) (h : 0 < f x) :
  has_deriv_at (λ x, f x ^ g x) (f' * g x * (f x) ^ (g x - 1) + g' * f x ^ g x * log (f x)) x :=
begin
  rw ← has_deriv_within_at_univ at *,
  exact hf.rpow hg h
end

lemma has_deriv_within_at.rpow_const (hf : has_deriv_within_at f f' s x) (hx : f x ≠ 0 ∨ 1 ≤ p) :
  has_deriv_within_at (λ y, (f y)^p) (f' * p * (f x) ^ (p - 1)) s x :=
begin
  convert (has_deriv_at_rpow_const hx).comp_has_deriv_within_at x hf using 1,
  ring
end

lemma has_deriv_at.rpow_const (hf : has_deriv_at f f' x) (hx : f x ≠ 0 ∨ 1 ≤ p) :
  has_deriv_at (λ y, (f y)^p) (f' * p * (f x)^(p-1)) x :=
begin
  rw ← has_deriv_within_at_univ at *,
  exact hf.rpow_const hx
end

lemma deriv_within_rpow_const (hf : differentiable_within_at ℝ f s x) (hx : f x ≠ 0 ∨ 1 ≤ p)
  (hxs : unique_diff_within_at ℝ s x) :
  deriv_within (λx, (f x) ^ p) s x = (deriv_within f s x) * p * (f x) ^ (p - 1) :=
(hf.has_deriv_within_at.rpow_const hx).deriv_within hxs

@[simp] lemma deriv_rpow_const (hf : differentiable_at ℝ f x) (hx : f x ≠ 0 ∨ 1 ≤ p) :
  deriv (λx, (f x)^p) x = (deriv f x) * p * (f x)^(p-1) :=
(hf.has_deriv_at.rpow_const hx).deriv

end deriv

end differentiability

section limits
open real filter

/-- The function `(1 + t/x) ^ x` tends to `exp t` at `+∞`. -/
lemma tendsto_one_plus_div_rpow_exp (t : ℝ) :
  tendsto (λ (x : ℝ), (1 + t / x) ^ x) at_top (𝓝 (exp t)) :=
begin
  apply ((real.continuous_exp.tendsto _).comp (tendsto_mul_log_one_plus_div_at_top t)).congr' _,
  have h₁ : (1:ℝ)/2 < 1 := by linarith,
  have h₂ : tendsto (λ x : ℝ, 1 + t / x) at_top (𝓝 1) :=
    by simpa using (tendsto_inv_at_top_zero.const_mul t).const_add 1,
  refine (eventually_ge_of_tendsto_gt h₁ h₂).mono (λ x hx, _),
  have hx' : 0 < 1 + t / x := by linarith,
  simp [mul_comm x, exp_mul, exp_log hx'],
end

/-- The function `(1 + t/x) ^ x` tends to `exp t` at `+∞` for naturals `x`. -/
lemma tendsto_one_plus_div_pow_exp (t : ℝ) :
  tendsto (λ (x : ℕ), (1 + t / (x:ℝ)) ^ x) at_top (𝓝 (real.exp t)) :=
((tendsto_one_plus_div_rpow_exp t).comp tendsto_coe_nat_at_top_at_top).congr (by simp)

end limits


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

! This file was ported from Lean 3 source module topology.spectral.hom
! 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.Topology.ContinuousFunction.Basic

/-!
# Spectral maps

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

This file defines spectral maps. A map is spectral when it's continuous and the preimage of a
compact open set is compact open.

## Main declarations

* `is_spectral_map`: Predicate for a map to be spectral.
* `spectral_map`: Bundled spectral maps.
* `spectral_map_class`: Typeclass for a type to be a type of spectral maps.

## TODO

Once we have `spectral_space`, `is_spectral_map` should move to `topology.spectral.basic`.
-/

open Function OrderDual

variable {F α β γ δ : Type _}

section Unbundled

variable [TopologicalSpace α] [TopologicalSpace β] [TopologicalSpace γ] {f : α → β} {s : Set β}

#print IsSpectralMap /-
/-- A function between topological spaces is spectral if it is continuous and the preimage of every
compact open set is compact open. -/
structure IsSpectralMap (f : α → β) extends Continuous f : Prop where
  isCompact_preimage_of_isOpen ⦃s : Set β⦄ : IsOpen s → IsCompact s → IsCompact (f ⁻¹' s)
#align is_spectral_map IsSpectralMap
-/

/- warning: is_compact.preimage_of_is_open -> IsCompact.preimage_of_isOpen is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {f : α -> β} {s : Set.{u2} β}, (IsSpectralMap.{u1, u2} α β _inst_1 _inst_2 f) -> (IsCompact.{u2} β _inst_2 s) -> (IsOpen.{u2} β _inst_2 s) -> (IsCompact.{u1} α _inst_1 (Set.preimage.{u1, u2} α β f s))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {f : α -> β} {s : Set.{u1} β}, (IsSpectralMap.{u2, u1} α β _inst_1 _inst_2 f) -> (IsCompact.{u1} β _inst_2 s) -> (IsOpen.{u1} β _inst_2 s) -> (IsCompact.{u2} α _inst_1 (Set.preimage.{u2, u1} α β f s))
Case conversion may be inaccurate. Consider using '#align is_compact.preimage_of_is_open IsCompact.preimage_of_isOpenₓ'. -/
theorem IsCompact.preimage_of_isOpen (hf : IsSpectralMap f) (h₀ : IsCompact s) (h₁ : IsOpen s) :
    IsCompact (f ⁻¹' s) :=
  hf.isCompact_preimage_of_isOpen h₁ h₀
#align is_compact.preimage_of_is_open IsCompact.preimage_of_isOpen

/- warning: is_spectral_map.continuous -> IsSpectralMap.continuous is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {f : α -> β}, (IsSpectralMap.{u1, u2} α β _inst_1 _inst_2 f) -> (Continuous.{u1, u2} α β _inst_1 _inst_2 f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {f : α -> β}, (IsSpectralMap.{u2, u1} α β _inst_1 _inst_2 f) -> (Continuous.{u2, u1} α β _inst_1 _inst_2 f)
Case conversion may be inaccurate. Consider using '#align is_spectral_map.continuous IsSpectralMap.continuousₓ'. -/
theorem IsSpectralMap.continuous {f : α → β} (hf : IsSpectralMap f) : Continuous f :=
  hf.to_continuous
#align is_spectral_map.continuous IsSpectralMap.continuous

#print isSpectralMap_id /-
theorem isSpectralMap_id : IsSpectralMap (@id α) :=
  ⟨continuous_id, fun s _ => id⟩
#align is_spectral_map_id isSpectralMap_id
-/

/- warning: is_spectral_map.comp -> IsSpectralMap.comp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] {f : β -> γ} {g : α -> β}, (IsSpectralMap.{u2, u3} β γ _inst_2 _inst_3 f) -> (IsSpectralMap.{u1, u2} α β _inst_1 _inst_2 g) -> (IsSpectralMap.{u1, u3} α γ _inst_1 _inst_3 (Function.comp.{succ u1, succ u2, succ u3} α β γ f g))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] {f : β -> γ} {g : α -> β}, (IsSpectralMap.{u3, u2} β γ _inst_2 _inst_3 f) -> (IsSpectralMap.{u1, u3} α β _inst_1 _inst_2 g) -> (IsSpectralMap.{u1, u2} α γ _inst_1 _inst_3 (Function.comp.{succ u1, succ u3, succ u2} α β γ f g))
Case conversion may be inaccurate. Consider using '#align is_spectral_map.comp IsSpectralMap.compₓ'. -/
theorem IsSpectralMap.comp {f : β → γ} {g : α → β} (hf : IsSpectralMap f) (hg : IsSpectralMap g) :
    IsSpectralMap (f ∘ g) :=
  ⟨hf.Continuous.comp hg.Continuous, fun s hs₀ hs₁ =>
    (hs₁.preimage_of_isOpen hf hs₀).preimage_of_isOpen hg (hs₀.Preimage hf.Continuous)⟩
#align is_spectral_map.comp IsSpectralMap.comp

end Unbundled

#print SpectralMap /-
/-- The type of spectral maps from `α` to `β`. -/
structure SpectralMap (α β : Type _) [TopologicalSpace α] [TopologicalSpace β] where
  toFun : α → β
  spectral' : IsSpectralMap to_fun
#align spectral_map SpectralMap
-/

section

#print SpectralMapClass /-
/-- `spectral_map_class F α β` states that `F` is a type of spectral maps.

You should extend this class when you extend `spectral_map`. -/
class SpectralMapClass (F : Type _) (α β : outParam <| Type _) [TopologicalSpace α]
  [TopologicalSpace β] extends FunLike F α fun _ => β where
  map_spectral (f : F) : IsSpectralMap f
#align spectral_map_class SpectralMapClass
-/

end

export SpectralMapClass (map_spectral)

attribute [simp] map_spectral

#print SpectralMapClass.toContinuousMapClass /-
-- See note [lower instance priority]
instance (priority := 100) SpectralMapClass.toContinuousMapClass [TopologicalSpace α]
    [TopologicalSpace β] [SpectralMapClass F α β] : ContinuousMapClass F α β :=
  { ‹SpectralMapClass F α β› with map_continuous := fun f => (map_spectral f).Continuous }
#align spectral_map_class.to_continuous_map_class SpectralMapClass.toContinuousMapClass
-/

instance [TopologicalSpace α] [TopologicalSpace β] [SpectralMapClass F α β] :
    CoeTC F (SpectralMap α β) :=
  ⟨fun f => ⟨_, map_spectral f⟩⟩

/-! ### Spectral maps -/

namespace SpectralMap

variable [TopologicalSpace α] [TopologicalSpace β] [TopologicalSpace γ] [TopologicalSpace δ]

#print SpectralMap.toContinuousMap /-
/-- Reinterpret a `spectral_map` as a `continuous_map`. -/
def toContinuousMap (f : SpectralMap α β) : ContinuousMap α β :=
  ⟨_, f.spectral'.Continuous⟩
#align spectral_map.to_continuous_map SpectralMap.toContinuousMap
-/

instance : SpectralMapClass (SpectralMap α β) α β
    where
  coe := SpectralMap.toFun
  coe_injective' f g h := by
    cases f
    cases g
    congr
  map_spectral f := f.spectral'

/-- Helper instance for when there's too many metavariables to apply `fun_like.has_coe_to_fun`
directly. -/
instance : CoeFun (SpectralMap α β) fun _ => α → β :=
  FunLike.hasCoeToFun

/- warning: spectral_map.to_fun_eq_coe -> SpectralMap.toFun_eq_coe is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {f : SpectralMap.{u1, u2} α β _inst_1 _inst_2}, Eq.{max (succ u1) (succ u2)} (α -> β) (SpectralMap.toFun.{u1, u2} α β _inst_1 _inst_2 f) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {f : SpectralMap.{u2, u1} α β _inst_1 _inst_2}, Eq.{max (succ u2) (succ u1)} (α -> β) (SpectralMap.toFun.{u2, u1} α β _inst_1 _inst_2 f) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u2 u1, u2, u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u2, u1} α β _inst_1 _inst_2)) f)
Case conversion may be inaccurate. Consider using '#align spectral_map.to_fun_eq_coe SpectralMap.toFun_eq_coeₓ'. -/
@[simp]
theorem toFun_eq_coe {f : SpectralMap α β} : f.toFun = (f : α → β) :=
  rfl
#align spectral_map.to_fun_eq_coe SpectralMap.toFun_eq_coe

/- warning: spectral_map.ext -> SpectralMap.ext is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] {f : SpectralMap.{u1, u2} α β _inst_1 _inst_2} {g : SpectralMap.{u1, u2} α β _inst_1 _inst_2}, (forall (a : α), Eq.{succ u2} β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f a) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) g a)) -> (Eq.{max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) f g)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] {f : SpectralMap.{u2, u1} α β _inst_1 _inst_2} {g : SpectralMap.{u2, u1} α β _inst_1 _inst_2}, (forall (a : α), Eq.{succ u1} ((fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) a) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u2 u1, u2, u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u2, u1} α β _inst_1 _inst_2)) f a) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u2 u1, u2, u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u2, u1} α β _inst_1 _inst_2)) g a)) -> (Eq.{max (succ u2) (succ u1)} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) f g)
Case conversion may be inaccurate. Consider using '#align spectral_map.ext SpectralMap.extₓ'. -/
@[ext]
theorem ext {f g : SpectralMap α β} (h : ∀ a, f a = g a) : f = g :=
  FunLike.ext f g h
#align spectral_map.ext SpectralMap.ext

/- warning: spectral_map.copy -> SpectralMap.copy is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : SpectralMap.{u1, u2} α β _inst_1 _inst_2) (f' : α -> β), (Eq.{max (succ u1) (succ u2)} (α -> β) f' (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)) -> (SpectralMap.{u1, u2} α β _inst_1 _inst_2)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : SpectralMap.{u1, u2} α β _inst_1 _inst_2) (f' : α -> β), (Eq.{max (succ u1) (succ u2)} (α -> β) f' (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u1 u2, u1, u2} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u2} α β _inst_1 _inst_2)) f)) -> (SpectralMap.{u1, u2} α β _inst_1 _inst_2)
Case conversion may be inaccurate. Consider using '#align spectral_map.copy SpectralMap.copyₓ'. -/
/-- Copy of a `spectral_map` with a new `to_fun` equal to the old one. Useful to fix definitional
equalities. -/
protected def copy (f : SpectralMap α β) (f' : α → β) (h : f' = f) : SpectralMap α β :=
  ⟨f', h.symm.subst f.spectral'⟩
#align spectral_map.copy SpectralMap.copy

/- warning: spectral_map.coe_copy -> SpectralMap.coe_copy is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : SpectralMap.{u1, u2} α β _inst_1 _inst_2) (f' : α -> β) (h : Eq.{max (succ u1) (succ u2)} (α -> β) f' (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)), Eq.{max (succ u1) (succ u2)} (α -> β) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) (SpectralMap.copy.{u1, u2} α β _inst_1 _inst_2 f f' h)) f'
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (f : SpectralMap.{u2, u1} α β _inst_1 _inst_2) (f' : α -> β) (h : Eq.{max (succ u2) (succ u1)} (α -> β) f' (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u2 u1, u2, u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u2, u1} α β _inst_1 _inst_2)) f)), Eq.{max (succ u2) (succ u1)} (forall (ᾰ : α), (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) ᾰ) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u2 u1, u2, u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u2, u1} α β _inst_1 _inst_2)) (SpectralMap.copy.{u2, u1} α β _inst_1 _inst_2 f f' h)) f'
Case conversion may be inaccurate. Consider using '#align spectral_map.coe_copy SpectralMap.coe_copyₓ'. -/
@[simp]
theorem coe_copy (f : SpectralMap α β) (f' : α → β) (h : f' = f) : ⇑(f.copy f' h) = f' :=
  rfl
#align spectral_map.coe_copy SpectralMap.coe_copy

/- warning: spectral_map.copy_eq -> SpectralMap.copy_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : SpectralMap.{u1, u2} α β _inst_1 _inst_2) (f' : α -> β) (h : Eq.{max (succ u1) (succ u2)} (α -> β) f' (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)), Eq.{max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (SpectralMap.copy.{u1, u2} α β _inst_1 _inst_2 f f' h) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (f : SpectralMap.{u2, u1} α β _inst_1 _inst_2) (f' : α -> β) (h : Eq.{max (succ u2) (succ u1)} (α -> β) f' (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u2 u1, u2, u1} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u2, u1} α β _inst_1 _inst_2)) f)), Eq.{max (succ u2) (succ u1)} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) (SpectralMap.copy.{u2, u1} α β _inst_1 _inst_2 f f' h) f
Case conversion may be inaccurate. Consider using '#align spectral_map.copy_eq SpectralMap.copy_eqₓ'. -/
theorem copy_eq (f : SpectralMap α β) (f' : α → β) (h : f' = f) : f.copy f' h = f :=
  FunLike.ext' h
#align spectral_map.copy_eq SpectralMap.copy_eq

variable (α)

#print SpectralMap.id /-
/-- `id` as a `spectral_map`. -/
protected def id : SpectralMap α α :=
  ⟨id, isSpectralMap_id⟩
#align spectral_map.id SpectralMap.id
-/

instance : Inhabited (SpectralMap α α) :=
  ⟨SpectralMap.id α⟩

/- warning: spectral_map.coe_id -> SpectralMap.coe_id is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) [_inst_1 : TopologicalSpace.{u1} α], Eq.{succ u1} (α -> α) (coeFn.{succ u1, succ u1} (SpectralMap.{u1, u1} α α _inst_1 _inst_1) (fun (_x : SpectralMap.{u1, u1} α α _inst_1 _inst_1) => α -> α) (SpectralMap.hasCoeToFun.{u1, u1} α α _inst_1 _inst_1) (SpectralMap.id.{u1} α _inst_1)) (id.{succ u1} α)
but is expected to have type
  forall (α : Type.{u1}) [_inst_1 : TopologicalSpace.{u1} α], Eq.{succ u1} (forall (ᾰ : α), (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => α) ᾰ) (FunLike.coe.{succ u1, succ u1, succ u1} (SpectralMap.{u1, u1} α α _inst_1 _inst_1) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => α) _x) (SpectralMapClass.toFunLike.{u1, u1, u1} (SpectralMap.{u1, u1} α α _inst_1 _inst_1) α α _inst_1 _inst_1 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u1} α α _inst_1 _inst_1)) (SpectralMap.id.{u1} α _inst_1)) (id.{succ u1} α)
Case conversion may be inaccurate. Consider using '#align spectral_map.coe_id SpectralMap.coe_idₓ'. -/
@[simp]
theorem coe_id : ⇑(SpectralMap.id α) = id :=
  rfl
#align spectral_map.coe_id SpectralMap.coe_id

variable {α}

/- warning: spectral_map.id_apply -> SpectralMap.id_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (a : α), Eq.{succ u1} α (coeFn.{succ u1, succ u1} (SpectralMap.{u1, u1} α α _inst_1 _inst_1) (fun (_x : SpectralMap.{u1, u1} α α _inst_1 _inst_1) => α -> α) (SpectralMap.hasCoeToFun.{u1, u1} α α _inst_1 _inst_1) (SpectralMap.id.{u1} α _inst_1) a) a
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : TopologicalSpace.{u1} α] (a : α), Eq.{succ u1} ((fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => α) a) (FunLike.coe.{succ u1, succ u1, succ u1} (SpectralMap.{u1, u1} α α _inst_1 _inst_1) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => α) _x) (SpectralMapClass.toFunLike.{u1, u1, u1} (SpectralMap.{u1, u1} α α _inst_1 _inst_1) α α _inst_1 _inst_1 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u1} α α _inst_1 _inst_1)) (SpectralMap.id.{u1} α _inst_1) a) a
Case conversion may be inaccurate. Consider using '#align spectral_map.id_apply SpectralMap.id_applyₓ'. -/
@[simp]
theorem id_apply (a : α) : SpectralMap.id α a = a :=
  rfl
#align spectral_map.id_apply SpectralMap.id_apply

#print SpectralMap.comp /-
/-- Composition of `spectral_map`s as a `spectral_map`. -/
def comp (f : SpectralMap β γ) (g : SpectralMap α β) : SpectralMap α γ :=
  ⟨f.toContinuousMap.comp g.toContinuousMap, f.spectral'.comp g.spectral'⟩
#align spectral_map.comp SpectralMap.comp
-/

/- warning: spectral_map.coe_comp -> SpectralMap.coe_comp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] (f : SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (g : SpectralMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u3)} ((fun (_x : SpectralMap.{u1, u3} α γ _inst_1 _inst_3) => α -> γ) (SpectralMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 f g)) (coeFn.{max (succ u1) (succ u3), max (succ u1) (succ u3)} (SpectralMap.{u1, u3} α γ _inst_1 _inst_3) (fun (_x : SpectralMap.{u1, u3} α γ _inst_1 _inst_3) => α -> γ) (SpectralMap.hasCoeToFun.{u1, u3} α γ _inst_1 _inst_3) (SpectralMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 f g)) (Function.comp.{succ u1, succ u2, succ u3} α β γ (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (fun (_x : SpectralMap.{u2, u3} β γ _inst_2 _inst_3) => β -> γ) (SpectralMap.hasCoeToFun.{u2, u3} β γ _inst_2 _inst_3) f) (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) g))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] (f : SpectralMap.{u3, u2} β γ _inst_2 _inst_3) (g : SpectralMap.{u1, u3} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u2)} (forall (a : α), (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => γ) a) (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => γ) _x) (SpectralMapClass.toFunLike.{max u1 u2, u1, u2} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u2} α γ _inst_1 _inst_3)) (SpectralMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 f g)) (Function.comp.{succ u1, succ u3, succ u2} α β γ (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β (fun (_x : β) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : β) => γ) _x) (SpectralMapClass.toFunLike.{max u3 u2, u3, u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (SpectralMap.instSpectralMapClassSpectralMap.{u3, u2} β γ _inst_2 _inst_3)) f) (FunLike.coe.{max (succ u1) (succ u3), succ u1, succ u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u1 u3, u1, u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u3} α β _inst_1 _inst_2)) g))
Case conversion may be inaccurate. Consider using '#align spectral_map.coe_comp SpectralMap.coe_compₓ'. -/
@[simp]
theorem coe_comp (f : SpectralMap β γ) (g : SpectralMap α β) : (f.comp g : α → γ) = f ∘ g :=
  rfl
#align spectral_map.coe_comp SpectralMap.coe_comp

/- warning: spectral_map.comp_apply -> SpectralMap.comp_apply is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] (f : SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (g : SpectralMap.{u1, u2} α β _inst_1 _inst_2) (a : α), Eq.{succ u3} γ (coeFn.{max (succ u1) (succ u3), max (succ u1) (succ u3)} (SpectralMap.{u1, u3} α γ _inst_1 _inst_3) (fun (_x : SpectralMap.{u1, u3} α γ _inst_1 _inst_3) => α -> γ) (SpectralMap.hasCoeToFun.{u1, u3} α γ _inst_1 _inst_3) (SpectralMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 f g) a) (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (fun (_x : SpectralMap.{u2, u3} β γ _inst_2 _inst_3) => β -> γ) (SpectralMap.hasCoeToFun.{u2, u3} β γ _inst_2 _inst_3) f (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) g a))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] (f : SpectralMap.{u3, u2} β γ _inst_2 _inst_3) (g : SpectralMap.{u1, u3} α β _inst_1 _inst_2) (a : α), Eq.{succ u2} ((fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => γ) a) (FunLike.coe.{max (succ u1) (succ u2), succ u1, succ u2} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => γ) _x) (SpectralMapClass.toFunLike.{max u1 u2, u1, u2} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u2} α γ _inst_1 _inst_3)) (SpectralMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 f g) a) (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β (fun (_x : β) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : β) => γ) _x) (SpectralMapClass.toFunLike.{max u3 u2, u3, u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (SpectralMap.instSpectralMapClassSpectralMap.{u3, u2} β γ _inst_2 _inst_3)) f (FunLike.coe.{max (succ u1) (succ u3), succ u1, succ u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u1 u3, u1, u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u3} α β _inst_1 _inst_2)) g a))
Case conversion may be inaccurate. Consider using '#align spectral_map.comp_apply SpectralMap.comp_applyₓ'. -/
@[simp]
theorem comp_apply (f : SpectralMap β γ) (g : SpectralMap α β) (a : α) : (f.comp g) a = f (g a) :=
  rfl
#align spectral_map.comp_apply SpectralMap.comp_apply

/- warning: spectral_map.coe_comp_continuous_map -> SpectralMap.coe_comp_continuousMap' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] (f : SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (g : SpectralMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u3)} (ContinuousMap.{u1, u3} α γ _inst_1 _inst_3) ((fun (a : Sort.{max (succ u1) (succ u3)}) (b : Sort.{max (succ u1) (succ u3)}) [self : HasLiftT.{max (succ u1) (succ u3), max (succ u1) (succ u3)} a b] => self.0) (SpectralMap.{u1, u3} α γ _inst_1 _inst_3) (ContinuousMap.{u1, u3} α γ _inst_1 _inst_3) (HasLiftT.mk.{max (succ u1) (succ u3), max (succ u1) (succ u3)} (SpectralMap.{u1, u3} α γ _inst_1 _inst_3) (ContinuousMap.{u1, u3} α γ _inst_1 _inst_3) (CoeTCₓ.coe.{max (succ u1) (succ u3), max (succ u1) (succ u3)} (SpectralMap.{u1, u3} α γ _inst_1 _inst_3) (ContinuousMap.{u1, u3} α γ _inst_1 _inst_3) (ContinuousMap.hasCoeT.{max u1 u3, u1, u3} (SpectralMap.{u1, u3} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (SpectralMapClass.toContinuousMapClass.{max u1 u3, u1, u3} (SpectralMap.{u1, u3} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (SpectralMap.spectralMapClass.{u1, u3} α γ _inst_1 _inst_3))))) (SpectralMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 f g)) (ContinuousMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 ((fun (a : Sort.{max (succ u2) (succ u3)}) (b : Sort.{max (succ u2) (succ u3)}) [self : HasLiftT.{max (succ u2) (succ u3), max (succ u2) (succ u3)} a b] => self.0) (SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (ContinuousMap.{u2, u3} β γ _inst_2 _inst_3) (HasLiftT.mk.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (ContinuousMap.{u2, u3} β γ _inst_2 _inst_3) (CoeTCₓ.coe.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (ContinuousMap.{u2, u3} β γ _inst_2 _inst_3) (ContinuousMap.hasCoeT.{max u2 u3, u2, u3} (SpectralMap.{u2, u3} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (SpectralMapClass.toContinuousMapClass.{max u2 u3, u2, u3} (SpectralMap.{u2, u3} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (SpectralMap.spectralMapClass.{u2, u3} β γ _inst_2 _inst_3))))) f) ((fun (a : Sort.{max (succ u1) (succ u2)}) (b : Sort.{max (succ u1) (succ u2)}) [self : HasLiftT.{max (succ u1) (succ u2), max (succ u1) (succ u2)} a b] => self.0) (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (ContinuousMap.{u1, u2} α β _inst_1 _inst_2) (HasLiftT.mk.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (ContinuousMap.{u1, u2} α β _inst_1 _inst_2) (CoeTCₓ.coe.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (ContinuousMap.{u1, u2} α β _inst_1 _inst_2) (ContinuousMap.hasCoeT.{max u1 u2, u1, u2} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMapClass.toContinuousMapClass.{max u1 u2, u1, u2} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.spectralMapClass.{u1, u2} α β _inst_1 _inst_2))))) g))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] (f : SpectralMap.{u3, u2} β γ _inst_2 _inst_3) (g : SpectralMap.{u1, u3} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u2)} (ContinuousMap.{u1, u2} α γ _inst_1 _inst_3) (ContinuousMap.mk.{u1, u2} α γ _inst_1 _inst_3 (FunLike.coe.{succ (max u1 u2), succ u1, succ u2} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) α (fun (a : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => γ) a) (ContinuousMapClass.toFunLike.{max u1 u2, u1, u2} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (SpectralMapClass.toContinuousMapClass.{max u1 u2, u1, u2} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u2} α γ _inst_1 _inst_3))) (SpectralMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 f g)) (ContinuousMapClass.map_continuous.{max u1 u2, u1, u2} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (SpectralMapClass.toContinuousMapClass.{max u1 u2, u1, u2} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) α γ _inst_1 _inst_3 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u2} α γ _inst_1 _inst_3)) (SpectralMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 f g))) (ContinuousMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 (ContinuousMap.mk.{u3, u2} β γ _inst_2 _inst_3 (FunLike.coe.{succ (max u3 u2), succ u3, succ u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β (fun (a : β) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : β) => γ) a) (ContinuousMapClass.toFunLike.{max u3 u2, u3, u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (SpectralMapClass.toContinuousMapClass.{max u3 u2, u3, u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (SpectralMap.instSpectralMapClassSpectralMap.{u3, u2} β γ _inst_2 _inst_3))) f) (ContinuousMapClass.map_continuous.{max u3 u2, u3, u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (SpectralMapClass.toContinuousMapClass.{max u3 u2, u3, u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (SpectralMap.instSpectralMapClassSpectralMap.{u3, u2} β γ _inst_2 _inst_3)) f)) (ContinuousMap.mk.{u1, u3} α β _inst_1 _inst_2 (FunLike.coe.{succ (max u1 u3), succ u1, succ u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α (fun (a : α) => (fun (x._@.Mathlib.Topology.ContinuousFunction.Basic._hyg.699 : α) => β) a) (ContinuousMapClass.toFunLike.{max u1 u3, u1, u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMapClass.toContinuousMapClass.{max u1 u3, u1, u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u3} α β _inst_1 _inst_2))) g) (ContinuousMapClass.map_continuous.{max u1 u3, u1, u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMapClass.toContinuousMapClass.{max u1 u3, u1, u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u3} α β _inst_1 _inst_2)) g)))
Case conversion may be inaccurate. Consider using '#align spectral_map.coe_comp_continuous_map SpectralMap.coe_comp_continuousMap'ₓ'. -/
@[simp]
theorem coe_comp_continuousMap' (f : SpectralMap β γ) (g : SpectralMap α β) :
    (f.comp g : ContinuousMap α γ) = (f : ContinuousMap β γ).comp g :=
  rfl
#align spectral_map.coe_comp_continuous_map SpectralMap.coe_comp_continuousMap'

/- warning: spectral_map.comp_assoc -> SpectralMap.comp_assoc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] [_inst_4 : TopologicalSpace.{u4} δ] (f : SpectralMap.{u3, u4} γ δ _inst_3 _inst_4) (g : SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (h : SpectralMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u4)} (SpectralMap.{u1, u4} α δ _inst_1 _inst_4) (SpectralMap.comp.{u1, u2, u4} α β δ _inst_1 _inst_2 _inst_4 (SpectralMap.comp.{u2, u3, u4} β γ δ _inst_2 _inst_3 _inst_4 f g) h) (SpectralMap.comp.{u1, u3, u4} α γ δ _inst_1 _inst_3 _inst_4 f (SpectralMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g h))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u4}} {δ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u4} γ] [_inst_4 : TopologicalSpace.{u3} δ] (f : SpectralMap.{u4, u3} γ δ _inst_3 _inst_4) (g : SpectralMap.{u2, u4} β γ _inst_2 _inst_3) (h : SpectralMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u3)} (SpectralMap.{u1, u3} α δ _inst_1 _inst_4) (SpectralMap.comp.{u1, u2, u3} α β δ _inst_1 _inst_2 _inst_4 (SpectralMap.comp.{u2, u4, u3} β γ δ _inst_2 _inst_3 _inst_4 f g) h) (SpectralMap.comp.{u1, u4, u3} α γ δ _inst_1 _inst_3 _inst_4 f (SpectralMap.comp.{u1, u2, u4} α β γ _inst_1 _inst_2 _inst_3 g h))
Case conversion may be inaccurate. Consider using '#align spectral_map.comp_assoc SpectralMap.comp_assocₓ'. -/
@[simp]
theorem comp_assoc (f : SpectralMap γ δ) (g : SpectralMap β γ) (h : SpectralMap α β) :
    (f.comp g).comp h = f.comp (g.comp h) :=
  rfl
#align spectral_map.comp_assoc SpectralMap.comp_assoc

/- warning: spectral_map.comp_id -> SpectralMap.comp_id is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : SpectralMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (SpectralMap.comp.{u1, u1, u2} α α β _inst_1 _inst_1 _inst_2 f (SpectralMap.id.{u1} α _inst_1)) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (f : SpectralMap.{u2, u1} α β _inst_1 _inst_2), Eq.{max (succ u2) (succ u1)} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) (SpectralMap.comp.{u2, u2, u1} α α β _inst_1 _inst_1 _inst_2 f (SpectralMap.id.{u2} α _inst_1)) f
Case conversion may be inaccurate. Consider using '#align spectral_map.comp_id SpectralMap.comp_idₓ'. -/
@[simp]
theorem comp_id (f : SpectralMap α β) : f.comp (SpectralMap.id α) = f :=
  ext fun a => rfl
#align spectral_map.comp_id SpectralMap.comp_id

/- warning: spectral_map.id_comp -> SpectralMap.id_comp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] (f : SpectralMap.{u1, u2} α β _inst_1 _inst_2), Eq.{max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (SpectralMap.comp.{u1, u2, u2} α β β _inst_1 _inst_2 _inst_2 (SpectralMap.id.{u2} β _inst_2) f) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : TopologicalSpace.{u1} β] (f : SpectralMap.{u2, u1} α β _inst_1 _inst_2), Eq.{max (succ u2) (succ u1)} (SpectralMap.{u2, u1} α β _inst_1 _inst_2) (SpectralMap.comp.{u2, u1, u1} α β β _inst_1 _inst_2 _inst_2 (SpectralMap.id.{u1} β _inst_2) f) f
Case conversion may be inaccurate. Consider using '#align spectral_map.id_comp SpectralMap.id_compₓ'. -/
@[simp]
theorem id_comp (f : SpectralMap α β) : (SpectralMap.id β).comp f = f :=
  ext fun a => rfl
#align spectral_map.id_comp SpectralMap.id_comp

/- warning: spectral_map.cancel_right -> SpectralMap.cancel_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] {g₁ : SpectralMap.{u2, u3} β γ _inst_2 _inst_3} {g₂ : SpectralMap.{u2, u3} β γ _inst_2 _inst_3} {f : SpectralMap.{u1, u2} α β _inst_1 _inst_2}, (Function.Surjective.{succ u1, succ u2} α β (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) (fun (_x : SpectralMap.{u1, u2} α β _inst_1 _inst_2) => α -> β) (SpectralMap.hasCoeToFun.{u1, u2} α β _inst_1 _inst_2) f)) -> (Iff (Eq.{max (succ u1) (succ u3)} (SpectralMap.{u1, u3} α γ _inst_1 _inst_3) (SpectralMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g₁ f) (SpectralMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g₂ f)) (Eq.{max (succ u2) (succ u3)} (SpectralMap.{u2, u3} β γ _inst_2 _inst_3) g₁ g₂))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] {g₁ : SpectralMap.{u3, u2} β γ _inst_2 _inst_3} {g₂ : SpectralMap.{u3, u2} β γ _inst_2 _inst_3} {f : SpectralMap.{u1, u3} α β _inst_1 _inst_2}, (Function.Surjective.{succ u1, succ u3} α β (FunLike.coe.{max (succ u1) (succ u3), succ u1, succ u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α (fun (_x : α) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : α) => β) _x) (SpectralMapClass.toFunLike.{max u1 u3, u1, u3} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) α β _inst_1 _inst_2 (SpectralMap.instSpectralMapClassSpectralMap.{u1, u3} α β _inst_1 _inst_2)) f)) -> (Iff (Eq.{max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) (SpectralMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 g₁ f) (SpectralMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 g₂ f)) (Eq.{max (succ u3) (succ u2)} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) g₁ g₂))
Case conversion may be inaccurate. Consider using '#align spectral_map.cancel_right SpectralMap.cancel_rightₓ'. -/
theorem cancel_right {g₁ g₂ : SpectralMap β γ} {f : SpectralMap α β} (hf : Surjective f) :
    g₁.comp f = g₂.comp f ↔ g₁ = g₂ :=
  ⟨fun h => ext <| hf.forall.2 <| FunLike.ext_iff.1 h, congr_arg _⟩
#align spectral_map.cancel_right SpectralMap.cancel_right

/- warning: spectral_map.cancel_left -> SpectralMap.cancel_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} β] [_inst_3 : TopologicalSpace.{u3} γ] {g : SpectralMap.{u2, u3} β γ _inst_2 _inst_3} {f₁ : SpectralMap.{u1, u2} α β _inst_1 _inst_2} {f₂ : SpectralMap.{u1, u2} α β _inst_1 _inst_2}, (Function.Injective.{succ u2, succ u3} β γ (coeFn.{max (succ u2) (succ u3), max (succ u2) (succ u3)} (SpectralMap.{u2, u3} β γ _inst_2 _inst_3) (fun (_x : SpectralMap.{u2, u3} β γ _inst_2 _inst_3) => β -> γ) (SpectralMap.hasCoeToFun.{u2, u3} β γ _inst_2 _inst_3) g)) -> (Iff (Eq.{max (succ u1) (succ u3)} (SpectralMap.{u1, u3} α γ _inst_1 _inst_3) (SpectralMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g f₁) (SpectralMap.comp.{u1, u2, u3} α β γ _inst_1 _inst_2 _inst_3 g f₂)) (Eq.{max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α β _inst_1 _inst_2) f₁ f₂))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u3} β] [_inst_3 : TopologicalSpace.{u2} γ] {g : SpectralMap.{u3, u2} β γ _inst_2 _inst_3} {f₁ : SpectralMap.{u1, u3} α β _inst_1 _inst_2} {f₂ : SpectralMap.{u1, u3} α β _inst_1 _inst_2}, (Function.Injective.{succ u3, succ u2} β γ (FunLike.coe.{max (succ u3) (succ u2), succ u3, succ u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β (fun (_x : β) => (fun (x._@.Mathlib.Topology.Spectral.Hom._hyg.348 : β) => γ) _x) (SpectralMapClass.toFunLike.{max u3 u2, u3, u2} (SpectralMap.{u3, u2} β γ _inst_2 _inst_3) β γ _inst_2 _inst_3 (SpectralMap.instSpectralMapClassSpectralMap.{u3, u2} β γ _inst_2 _inst_3)) g)) -> (Iff (Eq.{max (succ u1) (succ u2)} (SpectralMap.{u1, u2} α γ _inst_1 _inst_3) (SpectralMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 g f₁) (SpectralMap.comp.{u1, u3, u2} α β γ _inst_1 _inst_2 _inst_3 g f₂)) (Eq.{max (succ u1) (succ u3)} (SpectralMap.{u1, u3} α β _inst_1 _inst_2) f₁ f₂))
Case conversion may be inaccurate. Consider using '#align spectral_map.cancel_left SpectralMap.cancel_leftₓ'. -/
theorem cancel_left {g : SpectralMap β γ} {f₁ f₂ : SpectralMap α β} (hg : Injective g) :
    g.comp f₁ = g.comp f₂ ↔ f₁ = f₂ :=
  ⟨fun h => ext fun a => hg <| by rw [← comp_apply, h, comp_apply], congr_arg _⟩
#align spectral_map.cancel_left SpectralMap.cancel_left

end SpectralMap


/-
Copyright (c) 2020 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel, Scott Morrison
-/
import category_theory.abelian.exact
import category_theory.preadditive.projective_resolution

/-!
# Abelian categories with enough projectives have projective resolutions

When `C` is abelian `projective.d f` and `f` are exact.
Hence, starting from an epimorphism `P ⟶ X`, where `P` is projective,
we can apply `projective.d` repeatedly to obtain a projective resolution of `X`.
-/

noncomputable theory

open category_theory
open category_theory.limits

universes v u

namespace category_theory

open category_theory.projective

variables {C : Type u} [category.{v} C]

section
variables [enough_projectives C] [abelian C]

/--
When `C` is abelian, `projective.d f` and `f` are exact.
-/
lemma exact_d_f {X Y : C} (f : X ⟶ Y) : exact (d f) f :=
(abelian.exact_iff _ _).2 $
  ⟨by simp, zero_of_epi_comp (π _) $ by rw [←category.assoc, cokernel.condition]⟩

end

namespace ProjectiveResolution

/-!
Our goal is to define `ProjectiveResolution.of Z : ProjectiveResolution Z`.
The `0`-th object in this resolution will just be `projective.over Z`,
i.e. an arbitrarily chosen projective object with a map to `Z`.
After that, we build the `n+1`-st object as `projective.syzygies`
applied to the previously constructed morphism,
and the map to the `n`-th object as `projective.d`.
-/

variables [abelian C] [enough_projectives C]

/-- Auxiliary definition for `ProjectiveResolution.of`. -/
@[simps]
def of_complex (Z : C) : chain_complex C ℕ :=
chain_complex.mk'
  (projective.over Z) (projective.syzygies (projective.π Z)) (projective.d (projective.π Z))
  (λ ⟨X, Y, f⟩, ⟨projective.syzygies f, projective.d f, (exact_d_f f).w⟩)

/--
In any abelian category with enough projectives,
`ProjectiveResolution.of Z` constructs a projection resolution of the object `Z`.
-/
@[irreducible] def of (Z : C) : ProjectiveResolution Z :=
{ complex := of_complex Z,
  π := chain_complex.mk_hom _ _ (projective.π Z) 0
    (by { simp, exact (exact_d_f (projective.π Z)).w.symm, })
    (λ n _, ⟨0, by ext⟩),
  projective := by { rintros (_|_|_|n); apply projective.projective_over, },
  exact₀ := by simpa using exact_d_f (projective.π Z),
  exact := by { rintros (_|n); { simp, apply exact_d_f, }, },
  epi := projective.π_epi Z, }

@[priority 100]
instance (Z : C) : has_projective_resolution Z :=
{ out := ⟨of Z⟩ }

@[priority 100]
instance : has_projective_resolutions C :=
{ out := λ Z, by apply_instance }

end ProjectiveResolution

end category_theory


import assign
import general_assign

def get_unit_clause_op : clause → option literal
| [l] := option.some l
| _ := option.none

lemma is_none_on_multiple (c : clause) (a b : literal) :
get_unit_clause_op (a :: b :: c) = option.none :=
by rw get_unit_clause_op

def get_unit_clauses := list.filter_map get_unit_clause_op

#eval get_unit_clauses example_unsat_formula
#eval get_unit_clauses example_sat_formula
#eval get_unit_clauses example_complex_formula

def assign_unit_clauses (f : formula) : formula :=
assign_all f (get_unit_clauses f)

#eval assign_unit_clauses example_unsat_formula
#eval assign_unit_clauses example_sat_formula
#eval assign_unit_clauses example_complex_formula

lemma unit_clause_not_removed (f : formula) (l a : literal)
: [a] ∈ f → l.atom ≠ a.atom → [a] ∈ assign_lit l f := begin
  intros h_a h_neq,
  induction' f;
  rw assign_lit;
  simp [h_a],
  cases' classical.em ([a] ∈ f),
  {
    apply or.inr,
    apply ih,
    assumption,
    assumption,
  },
  {
    have eq : [a] = hd := begin
      simp [h] at h_a,
      exact h_a
    end,
    apply or.inl,
    have n_in : l ∉ [a] := begin
      simp,
      intro h,
      have h := atom_must_be_equal _ _ h,
      contradiction,
    end,
    rw ←eq,
    simp [n_in],

    have eq : [a].remove_all [l_not l] = [a] := begin
      rw list.remove_all,
      rw list.filter_eq_self,
      intros b h,
      simp at h,
      simp,
      intro eq,
      rw h at eq,
      rw eq at h_neq,
      simp at h_neq,
      exact h_neq
    end,
    rw eq,
  },
end

def unit_or_missing (f : formula) (l : literal) : bool :=
  [l] ∈ f ∨ (l ∉ f.join ∧ l_not l ∉ f.join)

lemma unit_clause_impl_correct (f : formula) (units : list literal)
: ↥(units.all (unit_or_missing f)) →
(sat f ↔ sat (assign_all f units)) :=
begin
  intro h,
  rw assign_all,
  induction' units;
  simp,

  have h_f : ↥(units.all (unit_or_missing (assign_lit hd f))) := begin
    rw list.all_iff_forall,
    rw list.all_iff_forall at h,
    intros a h_in,
    have h := h a,
    rw unit_or_missing at h,
    simp [h_in] at h,
    cases' h,
    {
      cases' classical.em (hd.atom = a.atom);
      rw unit_or_missing;
      simp,
      {
        apply or.inr,
        have removes := assign_removes f hd,
        simp at removes,
        rw eq_atom_iff at h_2,
        cases' h_2,
        {
          rw ←h_2,
          apply removes,
        },
        {
          have eq : a = l_not (l_not a) := by simp,
          rw ←h_2 at eq,
          rw eq,
          simp,
          exact and.comm.mp removes,
        },
      },
      {
        apply or.inl,
        apply unit_clause_not_removed,
        exact h_1,
        exact h_2,
      },
    },
    {
      cases' h_1,
      have n_in : a ∉ f.join := begin
        simp,
        exact left,
      end,
      have n_n_in : l_not a ∉ f.join := begin
        simp,
        exact right,
      end,
      have sub : (assign_lit hd f).join ⊆ f.join := by apply assign_subset,
      rw unit_or_missing,
      have not_in : a ∉ list.join (assign_lit hd f) := begin
        apply mt,
        apply sub,
        exact n_in,
      end,
      have n_not_in : l_not a ∉ list.join (assign_lit hd f) := begin
        apply mt,
        apply sub,
        exact n_n_in,
      end,
      simp [not_in, n_not_in],
    },
  end,

  have ih := ih (assign_lit hd f) h_f,
  rw ←ih,

  rw list.all_iff_forall at h,
  have h := h hd,
  rw unit_or_missing at h,
  simp at h,
  cases' h,
  {
    apply general_assign,
    apply or.inl h_1,
  },
  {
    have eq := assign_without_present_eq f hd,
    cases' h_1,
    simp at eq,
    have eq := eq left right,
    rw eq,
  },
end

lemma unit_clause_correct (f : formula) :
sat f ↔ sat (assign_unit_clauses f) := begin
  apply unit_clause_impl_correct,
  rw list.all_iff_forall,
  rw get_unit_clauses,
  intros a ha,
  rw unit_or_missing,
  simp,
  apply or.inl,
  rw list.mem_filter_map at ha,
  cases' ha,
  cases' h,
  cases' w,
  {
    rw get_unit_clause_op at right,
    simp at right,
    contradiction,
  },
  {
    cases' w,
    {
      rw get_unit_clause_op at right,
      simp at right,
      rw right at left,
      exact left,
    },
    {
      have is_none := is_none_on_multiple w hd hd_1,
      rw is_none at right,
      simp at right,
      contradiction,
    },
  },
end

lemma unit_clauses_leq_size (f : formula)
: formula_size (assign_unit_clauses f) ≤ formula_size f :=
by apply assign_all_leq_size


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/
import algebra.direct_sum.basic
import linear_algebra.dfinsupp

/-!
# Direct sum of modules

The first part of the file provides constructors for direct sums of modules. It provides a
construction of the direct sum using the universal property and proves its uniqueness
(`direct_sum.to_module.unique`).

The second part of the file covers the special case of direct sums of submodules of a fixed module
`M`.  There is a canonical linear map from this direct sum to `M`, and the construction is
of particular importance when this linear map is an equivalence; that is, when the submodules
provide an internal decomposition of `M`.  The property is defined as
`direct_sum.submodule_is_internal`, and its basic consequences are established.

-/

universes u v w u₁

namespace direct_sum
open_locale direct_sum

section general
variables {R : Type u} [semiring R]
variables {ι : Type v} [dec_ι : decidable_eq ι]
include R
variables {M : ι → Type w} [Π i, add_comm_monoid (M i)] [Π i, module R (M i)]

instance : module R (⨁ i, M i) := dfinsupp.module
instance {S : Type*} [semiring S] [Π i, module S (M i)] [Π i, smul_comm_class R S (M i)] :
  smul_comm_class R S (⨁ i, M i) := dfinsupp.smul_comm_class
instance {S : Type*} [semiring S] [has_scalar R S] [Π i, module S (M i)]
  [Π i, is_scalar_tower R S (M i)] :
  is_scalar_tower R S (⨁ i, M i) := dfinsupp.is_scalar_tower
instance [Π i, module Rᵐᵒᵖ (M i)] [Π i, is_central_scalar R (M i)] :
  is_central_scalar R (⨁ i, M i) := dfinsupp.is_central_scalar

lemma smul_apply (b : R) (v : ⨁ i, M i) (i : ι) :
  (b • v) i = b • (v i) := dfinsupp.smul_apply _ _ _

include dec_ι

variables R ι M
/-- Create the direct sum given a family `M` of `R` modules indexed over `ι`. -/
def lmk : Π s : finset ι, (Π i : (↑s : set ι), M i.val) →ₗ[R] (⨁ i, M i) :=
dfinsupp.lmk

/-- Inclusion of each component into the direct sum. -/
def lof : Π i : ι, M i →ₗ[R] (⨁ i, M i) :=
dfinsupp.lsingle

variables {ι M}

lemma single_eq_lof (i : ι) (b : M i) :
  dfinsupp.single i b = lof R ι M i b := rfl

/-- Scalar multiplication commutes with direct sums. -/
theorem mk_smul (s : finset ι) (c : R) (x) : mk M s (c • x) = c • mk M s x :=
(lmk R ι M s).map_smul c x

/-- Scalar multiplication commutes with the inclusion of each component into the direct sum. -/
theorem of_smul (i : ι) (c : R) (x) : of M i (c • x) = c • of M i x :=
(lof R ι M i).map_smul c x

variables {R}
lemma support_smul [Π (i : ι) (x : M i), decidable (x ≠ 0)]
  (c : R) (v : ⨁ i, M i) : (c • v).support ⊆ v.support := dfinsupp.support_smul _ _

variables {N : Type u₁} [add_comm_monoid N] [module R N]
variables (φ : Π i, M i →ₗ[R] N)

variables (R ι N φ)
/-- The linear map constructed using the universal property of the coproduct. -/
def to_module : (⨁ i, M i) →ₗ[R] N :=
dfinsupp.lsum ℕ φ

/-- Coproducts in the categories of modules and additive monoids commute with the forgetful functor
from modules to additive monoids. -/
lemma coe_to_module_eq_coe_to_add_monoid :
  (to_module R ι N φ : (⨁ i, M i) → N) = to_add_monoid (λ i, (φ i).to_add_monoid_hom) :=
rfl

variables {ι N φ}

/-- The map constructed using the universal property gives back the original maps when
restricted to each component. -/
@[simp] lemma to_module_lof (i) (x : M i) : to_module R ι N φ (lof R ι M i x) = φ i x :=
to_add_monoid_of (λ i, (φ i).to_add_monoid_hom) i x

variables (ψ : (⨁ i, M i) →ₗ[R] N)

/-- Every linear map from a direct sum agrees with the one obtained by applying
the universal property to each of its components. -/
theorem to_module.unique (f : ⨁ i, M i) : ψ f = to_module R ι N (λ i, ψ.comp $ lof R ι M i) f :=
to_add_monoid.unique ψ.to_add_monoid_hom f

variables {ψ} {ψ' : (⨁ i, M i) →ₗ[R] N}

/-- Two `linear_map`s out of a direct sum are equal if they agree on the generators.

See note [partially-applied ext lemmas]. -/
@[ext]
theorem linear_map_ext ⦃ψ ψ' : (⨁ i, M i) →ₗ[R] N⦄
  (H : ∀ i, ψ.comp (lof R ι M i) = ψ'.comp (lof R ι M i)) : ψ = ψ' :=
dfinsupp.lhom_ext' H

/--
The inclusion of a subset of the direct summands
into a larger subset of the direct summands, as a linear map.
-/
def lset_to_set (S T : set ι) (H : S ⊆ T) :
  (⨁ (i : S), M i) →ₗ[R] (⨁ (i : T), M i) :=
to_module R _ _ $ λ i, lof R T (λ (i : subtype T), M i) ⟨i, H i.prop⟩

omit dec_ι

variables (ι M)
/-- Given `fintype α`, `linear_equiv_fun_on_fintype R` is the natural `R`-linear equivalence
between `⨁ i, M i` and `Π i, M i`. -/
@[simps apply] def linear_equiv_fun_on_fintype [fintype ι] :
  (⨁ i, M i) ≃ₗ[R] (Π i, M i) :=
{ to_fun := coe_fn,
  map_add' := λ f g, by { ext, simp only [add_apply, pi.add_apply] },
  map_smul' := λ c f, by { ext, simp only [dfinsupp.coe_smul, ring_hom.id_apply] },
  .. dfinsupp.equiv_fun_on_fintype }

variables {ι M}
@[simp] lemma linear_equiv_fun_on_fintype_lof [fintype ι] [decidable_eq ι] (i : ι) (m : M i) :
  (linear_equiv_fun_on_fintype R ι M) (lof R ι M i m) = pi.single i m :=
begin
  ext a,
  change (dfinsupp.equiv_fun_on_fintype (lof R ι M i m)) a = _,
  convert _root_.congr_fun (dfinsupp.equiv_fun_on_fintype_single i m) a,
end

@[simp] lemma linear_equiv_fun_on_fintype_symm_single [fintype ι] [decidable_eq ι]
  (i : ι) (m : M i) :
  (linear_equiv_fun_on_fintype R ι M).symm (pi.single i m) = lof R ι M i m :=
begin
  ext a,
  change (dfinsupp.equiv_fun_on_fintype.symm (pi.single i m)) a = _,
  rw (dfinsupp.equiv_fun_on_fintype_symm_single i m),
  refl
end

@[simp] lemma linear_equiv_fun_on_fintype_symm_coe [fintype ι] (f : ⨁ i, M i) :
  (linear_equiv_fun_on_fintype R ι M).symm f = f :=
by { ext, simp [linear_equiv_fun_on_fintype], }

/-- The natural linear equivalence between `⨁ _ : ι, M` and `M` when `unique ι`. -/
protected def lid (M : Type v) (ι : Type* := punit) [add_comm_monoid M] [module R M]
  [unique ι] :
  (⨁ (_ : ι), M) ≃ₗ[R] M :=
{ .. direct_sum.id M ι,
  .. to_module R ι M (λ i, linear_map.id) }

variables (ι M)
/-- The projection map onto one component, as a linear map. -/
def component (i : ι) : (⨁ i, M i) →ₗ[R] M i :=
dfinsupp.lapply i

variables {ι M}

lemma apply_eq_component (f : ⨁ i, M i) (i : ι) :
  f i = component R ι M i f := rfl

@[ext] lemma ext {f g : ⨁ i, M i}
  (h : ∀ i, component R ι M i f = component R ι M i g) : f = g :=
dfinsupp.ext h

lemma ext_iff {f g : ⨁ i, M i} : f = g ↔
  ∀ i, component R ι M i f = component R ι M i g :=
⟨λ h _, by rw h, ext R⟩

include dec_ι

@[simp] lemma lof_apply (i : ι) (b : M i) : ((lof R ι M i) b) i = b :=
dfinsupp.single_eq_same

@[simp] lemma component.lof_self (i : ι) (b : M i) :
  component R ι M i ((lof R ι M i) b) = b :=
lof_apply R i b

lemma component.of (i j : ι) (b : M j) :
  component R ι M i ((lof R ι M j) b) =
  if h : j = i then eq.rec_on h b else 0 :=
dfinsupp.single_apply

end general

section submodule

section semiring
variables {R : Type u} [semiring R]
variables {ι : Type v} [dec_ι : decidable_eq ι]
include dec_ι
variables {M : Type*} [add_comm_monoid M] [module R M]
variables (A : ι → submodule R M)

/-- The canonical embedding from `⨁ i, A i` to `M`  where `A` is a collection of `submodule R M`
indexed by `ι`-/
def submodule_coe : (⨁ i, A i) →ₗ[R] M := to_module R ι M (λ i, (A i).subtype)

@[simp] lemma submodule_coe_of (i : ι) (x : A i) : submodule_coe A (of (λ i, A i) i x) = x :=
to_add_monoid_of _ _ _

lemma coe_of_submodule_apply (i j : ι) (x : A i) :
  (direct_sum.of _ i x j : M) = if i = j then x else 0 :=
begin
  obtain rfl | h := decidable.eq_or_ne i j,
  { rw [direct_sum.of_eq_same, if_pos rfl], },
  { rw [direct_sum.of_eq_of_ne _ _ _ _ h, if_neg h, submodule.coe_zero], },
end

/-- The `direct_sum` formed by a collection of `submodule`s of `M` is said to be internal if the
canonical map `(⨁ i, A i) →ₗ[R] M` is bijective.

For the alternate statement in terms of independence and spanning, see
`direct_sum.submodule_is_internal_iff_independent_and_supr_eq_top`. -/
def submodule_is_internal : Prop := function.bijective (submodule_coe A)

lemma submodule_is_internal.to_add_submonoid :
  submodule_is_internal A ↔ add_submonoid_is_internal (λ i, (A i).to_add_submonoid) :=
iff.rfl

variables {A}

/-- If a direct sum of submodules is internal then the submodules span the module. -/
lemma submodule_is_internal.supr_eq_top (h : submodule_is_internal A) : supr A = ⊤ :=
begin
  rw [submodule.supr_eq_range_dfinsupp_lsum, linear_map.range_eq_top],
  exact function.bijective.surjective h,
end

/-- If a direct sum of submodules is internal then the submodules are independent. -/
lemma submodule_is_internal.independent (h : submodule_is_internal A) :
  complete_lattice.independent A :=
complete_lattice.independent_of_dfinsupp_lsum_injective _ h.injective

/-- Given an internal direct sum decomposition of a module `M`, and a basis for each of the
components of the direct sum, the disjoint union of these bases is a basis for `M`. -/
noncomputable def submodule_is_internal.collected_basis
  (h : submodule_is_internal A) {α : ι → Type*} (v : Π i, basis (α i) R (A i)) :
  basis (Σ i, α i) R M :=
{ repr := (linear_equiv.of_bijective _ h.injective h.surjective).symm ≪≫ₗ
      (dfinsupp.map_range.linear_equiv (λ i, (v i).repr)) ≪≫ₗ
      (sigma_finsupp_lequiv_dfinsupp R).symm }

@[simp] lemma submodule_is_internal.collected_basis_coe
  (h : submodule_is_internal A) {α : ι → Type*} (v : Π i, basis (α i) R (A i)) :
  ⇑(h.collected_basis v) = λ a : Σ i, (α i), ↑(v a.1 a.2) :=
begin
  funext a,
  simp only [submodule_is_internal.collected_basis, to_module, submodule_coe,
    add_equiv.to_fun_eq_coe, basis.coe_of_repr, basis.repr_symm_apply, dfinsupp.lsum_apply_apply,
    dfinsupp.map_range.linear_equiv_apply, dfinsupp.map_range.linear_equiv_symm,
    dfinsupp.map_range_single, finsupp.total_single, linear_equiv.of_bijective_apply,
    linear_equiv.symm_symm, linear_equiv.symm_trans_apply, one_smul,
    sigma_finsupp_add_equiv_dfinsupp_apply, sigma_finsupp_equiv_dfinsupp_single,
    sigma_finsupp_lequiv_dfinsupp_apply],
  convert dfinsupp.sum_add_hom_single (λ i, (A i).subtype.to_add_monoid_hom) a.1 (v a.1 a.2),
end

lemma submodule_is_internal.collected_basis_mem
  (h : submodule_is_internal A) {α : ι → Type*} (v : Π i, basis (α i) R (A i)) (a : Σ i, α i) :
  h.collected_basis v a ∈ A a.1 :=
by simp

end semiring

section ring
variables {R : Type u} [ring R]
variables {ι : Type v} [dec_ι : decidable_eq ι]
include dec_ι
variables {M : Type*} [add_comm_group M] [module R M]

lemma submodule_is_internal.to_add_subgroup (A : ι → submodule R M) :
  submodule_is_internal A ↔ add_subgroup_is_internal (λ i, (A i).to_add_subgroup) :=
iff.rfl

/-- Note that this is not generally true for `[semiring R]`; see
`complete_lattice.independent.dfinsupp_lsum_injective` for details. -/
lemma submodule_is_internal_of_independent_of_supr_eq_top {A : ι → submodule R M}
  (hi : complete_lattice.independent A) (hs : supr A = ⊤) : submodule_is_internal A :=
⟨hi.dfinsupp_lsum_injective, linear_map.range_eq_top.1 $
  (submodule.supr_eq_range_dfinsupp_lsum _).symm.trans hs⟩

/-- `iff` version of `direct_sum.submodule_is_internal_of_independent_of_supr_eq_top`,
`direct_sum.submodule_is_internal.independent`, and `direct_sum.submodule_is_internal.supr_eq_top`.
-/
lemma submodule_is_internal_iff_independent_and_supr_eq_top (A : ι → submodule R M) :
    submodule_is_internal A ↔ complete_lattice.independent A ∧ supr A = ⊤ :=
⟨λ i, ⟨i.independent, i.supr_eq_top⟩,
 and.rec submodule_is_internal_of_independent_of_supr_eq_top⟩

/-! Now copy the lemmas for subgroup and submonoids. -/

lemma add_submonoid_is_internal.independent {M : Type*} [add_comm_monoid M]
  {A : ι → add_submonoid M} (h : add_submonoid_is_internal A) :
  complete_lattice.independent A :=
complete_lattice.independent_of_dfinsupp_sum_add_hom_injective _ h.injective

lemma add_subgroup_is_internal.independent {M : Type*} [add_comm_group M]
  {A : ι → add_subgroup M} (h : add_subgroup_is_internal A) :
  complete_lattice.independent A :=
complete_lattice.independent_of_dfinsupp_sum_add_hom_injective' _ h.injective

end ring

end submodule

end direct_sum