/-
Copyright (c) 2018 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Johannes Hölzl
-/
import order.liminf_limsup
import topology.algebra.uniform_group
import topology.metric_space.algebra
import topology.metric_space.isometry
import topology.sequences

/-!
# Normed (semi)groups

In this file we define four classes:

* `has_norm`, `has_nnnorm`: auxiliary classes endowing a type `α` with a function `norm : α → ℝ`
  (notation: `∥x∥`) and `nnnorm : α → ℝ≥0` (notation: `∥x∥₊`), respectively;
* `semi_normed_group`: a seminormed group is an additive group with a norm and a pseudo metric space
  structures that agree with each other: `∀ x y, dist x y = ∥x - y∥`;
* `normed_group`: a normed group is an additive group with a norm and a metric space structures that
  agree with each other: `∀ x y, dist x y = ∥x - y∥`.

We also prove basic properties of (semi)normed groups and provide some instances.

## Tags

normed group
-/

variables {α ι E F : Type*}

open filter metric
open_locale topological_space big_operators nnreal ennreal uniformity pointwise

/-- Auxiliary class, endowing a type `E` with a function `norm : E → ℝ` with notation `∥x∥`. This
class is designed to be extended in more interesting classes specifying the properties of the norm.
-/
class has_norm (E : Type*) := (norm : E → ℝ)

export has_norm (norm)

notation `∥` e `∥` := norm e

/-- A seminormed group is an additive group endowed with a norm for which `dist x y = ∥x - y∥`
defines a pseudometric space structure. -/
class semi_normed_group (E : Type*) extends has_norm E, add_comm_group E, pseudo_metric_space E :=
(dist_eq : ∀ x y : E, dist x y = norm (x - y))

/-- A normed group is an additive group endowed with a norm for which `dist x y = ∥x - y∥` defines
a metric space structure. -/
class normed_group (E : Type*) extends has_norm E, add_comm_group E, metric_space E :=
(dist_eq : ∀ x y : E, dist x y = norm (x - y))

/-- A normed group is a seminormed group. -/
@[priority 100] -- see Note [lower instance priority]
instance normed_group.to_semi_normed_group [h : normed_group E] : semi_normed_group E :=
{ .. h }

/-- Construct a seminormed group from a translation invariant pseudodistance. -/
def semi_normed_group.of_add_dist [has_norm E] [add_comm_group E] [pseudo_metric_space E]
  (H1 : ∀ x : E, ∥x∥ = dist x 0)
  (H2 : ∀ x y z : E, dist x y ≤ dist (x + z) (y + z)) : semi_normed_group E :=
{ dist_eq := λ x y, begin
    rw H1, apply le_antisymm,
    { rw [sub_eq_add_neg, ← add_right_neg y], apply H2 },
    { have := H2 (x - y) 0 y, rwa [sub_add_cancel, zero_add] at this }
  end }

/-- Construct a seminormed group from a translation invariant pseudodistance -/
def semi_normed_group.of_add_dist' [has_norm E] [add_comm_group E] [pseudo_metric_space E]
  (H1 : ∀ x : E, ∥x∥ = dist x 0)
  (H2 : ∀ x y z : E, dist (x + z) (y + z) ≤ dist x y) : semi_normed_group E :=
{ dist_eq := λ x y, begin
    rw H1, apply le_antisymm,
    { have := H2 (x - y) 0 y, rwa [sub_add_cancel, zero_add] at this },
    { rw [sub_eq_add_neg, ← add_right_neg y], apply H2 }
  end }

/-- A seminormed group can be built from a seminorm that satisfies algebraic properties. This is
formalised in this structure. -/
structure semi_normed_group.core (E : Type*) [add_comm_group E] [has_norm E] : Prop :=
(norm_zero : ∥(0 : E)∥ = 0)
(triangle : ∀ x y : E, ∥x + y∥ ≤ ∥x∥ + ∥y∥)
(norm_neg : ∀ x : E, ∥-x∥ = ∥x∥)

/-- Constructing a seminormed group from core properties of a seminorm, i.e., registering the
pseudodistance and the pseudometric space structure from the seminorm properties. Note that in most
cases this instance creates bad definitional equalities (e.g., it does not take into account
a possibly existing `uniform_space` instance on `E`). -/
def semi_normed_group.of_core (E : Type*) [add_comm_group E] [has_norm E]
  (C : semi_normed_group.core E) : semi_normed_group E :=
{ dist := λ x y, ∥x - y∥,
  dist_eq := assume x y, by refl,
  dist_self := assume x, by simp [C.norm_zero],
  dist_triangle := assume x y z,
    calc ∥x - z∥ = ∥x - y + (y - z)∥ : by rw sub_add_sub_cancel
            ... ≤ ∥x - y∥ + ∥y - z∥  : C.triangle _ _,
  dist_comm := assume x y,
    calc ∥x - y∥ = ∥ -(y - x)∥ : by simp
             ... = ∥y - x∥ : by { rw [C.norm_neg] } }

instance : normed_group punit :=
{ norm := function.const _ 0,
  dist_eq := λ _ _, rfl, }

@[simp] lemma punit.norm_eq_zero (r : punit) : ∥r∥ = 0 := rfl

noncomputable instance : normed_group ℝ :=
{ norm := λ x, |x|,
  dist_eq := assume x y, rfl }

lemma real.norm_eq_abs (r : ℝ) : ∥r∥ = |r| := rfl

section semi_normed_group
variables [semi_normed_group E] [semi_normed_group F]

lemma dist_eq_norm (g h : E) : dist g h = ∥g - h∥ :=
semi_normed_group.dist_eq _ _

lemma dist_eq_norm' (g h : E) : dist g h = ∥h - g∥ :=
by rw [dist_comm, dist_eq_norm]

@[simp] lemma dist_zero_right (g : E) : dist g 0 = ∥g∥ :=
by rw [dist_eq_norm, sub_zero]

@[simp] lemma dist_zero_left : dist (0 : E) = norm :=
funext $ λ g, by rw [dist_comm, dist_zero_right]

lemma tendsto_norm_cocompact_at_top [proper_space E] :
  tendsto norm (cocompact E) at_top :=
by simpa only [dist_zero_right] using tendsto_dist_right_cocompact_at_top (0 : E)

lemma norm_sub_rev (g h : E) : ∥g - h∥ = ∥h - g∥ :=
by simpa only [dist_eq_norm] using dist_comm g h

@[simp] lemma norm_neg (g : E) : ∥-g∥ = ∥g∥ :=
by simpa using norm_sub_rev 0 g

@[simp] lemma dist_add_left (g h₁ h₂ : E) : dist (g + h₁) (g + h₂) = dist h₁ h₂ :=
by simp [dist_eq_norm]

@[simp] lemma dist_add_right (g₁ g₂ h : E) : dist (g₁ + h) (g₂ + h) = dist g₁ g₂ :=
by simp [dist_eq_norm]

@[simp] lemma dist_neg_neg (g h : E) : dist (-g) (-h) = dist g h :=
by simp only [dist_eq_norm, neg_sub_neg, norm_sub_rev]

@[simp] lemma dist_sub_left (g h₁ h₂ : E) : dist (g - h₁) (g - h₂) = dist h₁ h₂ :=
by simp only [sub_eq_add_neg, dist_add_left, dist_neg_neg]

@[simp] lemma dist_sub_right (g₁ g₂ h : E) : dist (g₁ - h) (g₂ - h) = dist g₁ g₂ :=
by simpa only [sub_eq_add_neg] using dist_add_right _ _ _

/-- **Triangle inequality** for the norm. -/
lemma norm_add_le (g h : E) : ∥g + h∥ ≤ ∥g∥ + ∥h∥ :=
by simpa [dist_eq_norm] using dist_triangle g 0 (-h)

lemma norm_add_le_of_le {g₁ g₂ : E} {n₁ n₂ : ℝ} (H₁ : ∥g₁∥ ≤ n₁) (H₂ : ∥g₂∥ ≤ n₂) :
  ∥g₁ + g₂∥ ≤ n₁ + n₂ :=
le_trans (norm_add_le g₁ g₂) (add_le_add H₁ H₂)

lemma dist_add_add_le (g₁ g₂ h₁ h₂ : E) :
  dist (g₁ + g₂) (h₁ + h₂) ≤ dist g₁ h₁ + dist g₂ h₂ :=
by simpa only [dist_add_left, dist_add_right] using dist_triangle (g₁ + g₂) (h₁ + g₂) (h₁ + h₂)

lemma dist_add_add_le_of_le {g₁ g₂ h₁ h₂ : E} {d₁ d₂ : ℝ}
  (H₁ : dist g₁ h₁ ≤ d₁) (H₂ : dist g₂ h₂ ≤ d₂) :
  dist (g₁ + g₂) (h₁ + h₂) ≤ d₁ + d₂ :=
le_trans (dist_add_add_le g₁ g₂ h₁ h₂) (add_le_add H₁ H₂)

lemma dist_sub_sub_le (g₁ g₂ h₁ h₂ : E) :
  dist (g₁ - g₂) (h₁ - h₂) ≤ dist g₁ h₁ + dist g₂ h₂ :=
by simpa only [sub_eq_add_neg, dist_neg_neg] using dist_add_add_le g₁ (-g₂) h₁ (-h₂)

lemma dist_sub_sub_le_of_le {g₁ g₂ h₁ h₂ : E} {d₁ d₂ : ℝ}
  (H₁ : dist g₁ h₁ ≤ d₁) (H₂ : dist g₂ h₂ ≤ d₂) :
  dist (g₁ - g₂) (h₁ - h₂) ≤ d₁ + d₂ :=
le_trans (dist_sub_sub_le g₁ g₂ h₁ h₂) (add_le_add H₁ H₂)

lemma abs_dist_sub_le_dist_add_add (g₁ g₂ h₁ h₂ : E) :
  |dist g₁ h₁ - dist g₂ h₂| ≤ dist (g₁ + g₂) (h₁ + h₂) :=
by simpa only [dist_add_left, dist_add_right, dist_comm h₂]
  using abs_dist_sub_le (g₁ + g₂) (h₁ + h₂) (h₁ + g₂)

@[simp] lemma norm_nonneg (g : E) : 0 ≤ ∥g∥ :=
by { rw[←dist_zero_right], exact dist_nonneg }

@[simp] lemma norm_zero : ∥(0 : E)∥ = 0 :=  by rw [← dist_zero_right, dist_self]

lemma ne_zero_of_norm_ne_zero {g : E} : ∥g∥ ≠ 0 → g ≠ 0 := mt $ by { rintro rfl, exact norm_zero }

@[nontriviality] lemma norm_of_subsingleton [subsingleton E] (x : E) : ∥x∥ = 0 :=
by rw [subsingleton.elim x 0, norm_zero]

lemma norm_sum_le (s : finset ι) (f : ι → E) : ∥∑ i in s, f i∥ ≤ ∑ i in s, ∥f i∥ :=
s.le_sum_of_subadditive norm norm_zero norm_add_le f

lemma norm_sum_le_of_le (s : finset ι) {f : ι → E} {n : ι → ℝ} (h : ∀ b ∈ s, ∥f b∥ ≤ n b) :
  ∥∑ b in s, f b∥ ≤ ∑ b in s, n b :=
le_trans (norm_sum_le s f) (finset.sum_le_sum h)

lemma dist_sum_sum_le_of_le (s : finset ι) {f g : ι → E} {d : ι → ℝ}
  (h : ∀ b ∈ s, dist (f b) (g b) ≤ d b) :
  dist (∑ b in s, f b) (∑ b in s, g b) ≤ ∑ b in s, d b :=
begin
  simp only [dist_eq_norm, ← finset.sum_sub_distrib] at *,
  exact norm_sum_le_of_le s h
end

lemma dist_sum_sum_le (s : finset ι) (f g : ι → E) :
  dist (∑ b in s, f b) (∑ b in s, g b) ≤ ∑ b in s, dist (f b) (g b) :=
dist_sum_sum_le_of_le s (λ _ _, le_rfl)

lemma norm_sub_le (g h : E) : ∥g - h∥ ≤ ∥g∥ + ∥h∥ :=
by simpa [dist_eq_norm] using dist_triangle g 0 h

lemma norm_sub_le_of_le {g₁ g₂ : E} {n₁ n₂ : ℝ} (H₁ : ∥g₁∥ ≤ n₁) (H₂ : ∥g₂∥ ≤ n₂) :
  ∥g₁ - g₂∥ ≤ n₁ + n₂ :=
le_trans (norm_sub_le g₁ g₂) (add_le_add H₁ H₂)

lemma dist_le_norm_add_norm (g h : E) : dist g h ≤ ∥g∥ + ∥h∥ :=
by { rw dist_eq_norm, apply norm_sub_le }

lemma abs_norm_sub_norm_le (g h : E) : |∥g∥ - ∥h∥| ≤ ∥g - h∥ :=
by simpa [dist_eq_norm] using abs_dist_sub_le g h 0

lemma norm_sub_norm_le (g h : E) : ∥g∥ - ∥h∥ ≤ ∥g - h∥ :=
le_trans (le_abs_self _) (abs_norm_sub_norm_le g h)

lemma dist_norm_norm_le (g h : E) : dist ∥g∥ ∥h∥ ≤ ∥g - h∥ :=
abs_norm_sub_norm_le g h

lemma norm_le_insert (u v : E) : ∥v∥ ≤ ∥u∥ + ∥u - v∥ :=
calc ∥v∥ = ∥u - (u - v)∥ : by abel
... ≤ ∥u∥ + ∥u - v∥ : norm_sub_le u _

lemma norm_le_insert' (u v : E) : ∥u∥ ≤ ∥v∥ + ∥u - v∥ :=
by { rw norm_sub_rev, exact norm_le_insert v u }

lemma norm_le_add_norm_add (u v : E) :
  ∥u∥ ≤ ∥u + v∥ + ∥v∥ :=
calc ∥u∥ = ∥u + v - v∥ : by rw add_sub_cancel
... ≤ ∥u + v∥ + ∥v∥ : norm_sub_le _ _

lemma ball_zero_eq (ε : ℝ) : ball (0 : E) ε = {x | ∥x∥ < ε} :=
set.ext $ assume a, by simp

lemma mem_ball_iff_norm {g h : E} {r : ℝ} :
  h ∈ ball g r ↔ ∥h - g∥ < r :=
by rw [mem_ball, dist_eq_norm]

lemma add_mem_ball_iff_norm {g h : E} {r : ℝ} :
  g + h ∈ ball g r ↔ ∥h∥ < r :=
by rw [mem_ball_iff_norm, add_sub_cancel']

lemma mem_ball_iff_norm' {g h : E} {r : ℝ} :
  h ∈ ball g r ↔ ∥g - h∥ < r :=
by rw [mem_ball', dist_eq_norm]

@[simp] lemma mem_ball_zero_iff {ε : ℝ} {x : E} : x ∈ ball (0 : E) ε ↔ ∥x∥ < ε :=
by rw [mem_ball, dist_zero_right]

lemma mem_closed_ball_iff_norm {g h : E} {r : ℝ} :
  h ∈ closed_ball g r ↔ ∥h - g∥ ≤ r :=
by rw [mem_closed_ball, dist_eq_norm]

@[simp] lemma mem_closed_ball_zero_iff {ε : ℝ} {x : E} : x ∈ closed_ball (0 : E) ε ↔ ∥x∥ ≤ ε :=
by rw [mem_closed_ball, dist_zero_right]

lemma add_mem_closed_ball_iff_norm {g h : E} {r : ℝ} :
  g + h ∈ closed_ball g r ↔ ∥h∥ ≤ r :=
by rw [mem_closed_ball_iff_norm, add_sub_cancel']

lemma mem_closed_ball_iff_norm' {g h : E} {r : ℝ} :
  h ∈ closed_ball g r ↔ ∥g - h∥ ≤ r :=
by rw [mem_closed_ball', dist_eq_norm]

lemma norm_le_of_mem_closed_ball {g h : E} {r : ℝ} (H : h ∈ closed_ball g r) :
  ∥h∥ ≤ ∥g∥ + r :=
calc
  ∥h∥ = ∥g + (h - g)∥ : by rw [add_sub_cancel'_right]
  ... ≤ ∥g∥ + ∥h - g∥  : norm_add_le _ _
  ... ≤ ∥g∥ + r : by { apply add_le_add_left, rw ← dist_eq_norm, exact H }

lemma norm_le_norm_add_const_of_dist_le {a b : E} {c : ℝ} (h : dist a b ≤ c) :
  ∥a∥ ≤ ∥b∥ + c :=
norm_le_of_mem_closed_ball h

lemma norm_lt_of_mem_ball {g h : E} {r : ℝ} (H : h ∈ ball g r) :
  ∥h∥ < ∥g∥ + r :=
calc
  ∥h∥ = ∥g + (h - g)∥ : by rw [add_sub_cancel'_right]
  ... ≤ ∥g∥ + ∥h - g∥  : norm_add_le _ _
  ... < ∥g∥ + r : by { apply add_lt_add_left, rw ← dist_eq_norm, exact H }

lemma norm_lt_norm_add_const_of_dist_lt {a b : E} {c : ℝ} (h : dist a b < c) :
  ∥a∥ < ∥b∥ + c :=
norm_lt_of_mem_ball h

lemma bounded_iff_forall_norm_le {s : set E} : bounded s ↔ ∃ C, ∀ x ∈ s, ∥x∥ ≤ C :=
by simpa only [set.subset_def, mem_closed_ball_iff_norm, sub_zero]
  using bounded_iff_subset_ball (0 : E)

@[simp] lemma preimage_add_ball (x y : E) (r : ℝ) : ((+) y) ⁻¹' (ball x r) = ball (x - y) r :=
begin
  ext z,
  simp only [dist_eq_norm, set.mem_preimage, mem_ball],
  abel
end

@[simp] lemma preimage_add_closed_ball (x y : E) (r : ℝ) :
  ((+) y) ⁻¹' (closed_ball x r) = closed_ball (x - y) r :=
begin
  ext z,
  simp only [dist_eq_norm, set.mem_preimage, mem_closed_ball],
  abel
end

@[simp] lemma mem_sphere_iff_norm (v w : E) (r : ℝ) : w ∈ sphere v r ↔ ∥w - v∥ = r :=
by simp [dist_eq_norm]

@[simp] lemma mem_sphere_zero_iff_norm {w : E} {r : ℝ} : w ∈ sphere (0:E) r ↔ ∥w∥ = r :=
by simp [dist_eq_norm]

@[simp] lemma norm_eq_of_mem_sphere {r : ℝ} (x : sphere (0:E) r) : ∥(x:E)∥ = r :=
mem_sphere_zero_iff_norm.mp x.2

lemma preimage_add_sphere (x y : E) (r : ℝ) :
  ((+) y) ⁻¹' (sphere x r) = sphere (x - y) r :=
begin
  ext z,
  simp only [set.mem_preimage, mem_sphere_iff_norm],
  abel
end

lemma ne_zero_of_mem_sphere {r : ℝ} (hr : r ≠ 0) (x : sphere (0 : E) r) : (x : E) ≠ 0 :=
ne_zero_of_norm_ne_zero $ by rwa norm_eq_of_mem_sphere x

lemma ne_zero_of_mem_unit_sphere (x : sphere (0:E) 1) : (x:E) ≠ 0 :=
ne_zero_of_mem_sphere one_ne_zero _

/-- We equip the sphere, in a seminormed group, with a formal operation of negation, namely the
antipodal map. -/
instance {r : ℝ} : has_neg (sphere (0:E) r) :=
{ neg := λ w, ⟨-↑w, by simp⟩ }

@[simp] lemma coe_neg_sphere {r : ℝ} (v : sphere (0:E) r) :
  (((-v) : sphere _ _) : E) = - (v:E) :=
rfl

namespace isometric
-- TODO This material is superseded by similar constructions such as
-- `affine_isometry_equiv.const_vadd`; deduplicate

/-- Addition `y ↦ y + x` as an `isometry`. -/
protected def add_right (x : E) : E ≃ᵢ E :=
{ isometry_to_fun := isometry_emetric_iff_metric.2 $ λ y z, dist_add_right _ _ _,
  .. equiv.add_right x }

@[simp] lemma add_right_to_equiv (x : E) :
  (isometric.add_right x).to_equiv = equiv.add_right x := rfl

@[simp] lemma coe_add_right (x : E) : (isometric.add_right x : E → E) = λ y, y + x := rfl

lemma add_right_apply (x y : E) : (isometric.add_right x : E → E) y = y + x := rfl

@[simp] lemma add_right_symm (x : E) :
  (isometric.add_right x).symm = isometric.add_right (-x) :=
ext $ λ y, rfl

/-- Addition `y ↦ x + y` as an `isometry`. -/
protected def add_left (x : E) : E ≃ᵢ E :=
{ isometry_to_fun := isometry_emetric_iff_metric.2 $ λ y z, dist_add_left _ _ _,
  to_equiv := equiv.add_left x }

@[simp] lemma add_left_to_equiv (x : E) :
  (isometric.add_left x).to_equiv = equiv.add_left x := rfl

@[simp] lemma coe_add_left (x : E) : ⇑(isometric.add_left x) = (+) x := rfl

@[simp] lemma add_left_symm (x : E) :
  (isometric.add_left x).symm = isometric.add_left (-x) :=
ext $ λ y, rfl

variable (E)

/-- Negation `x ↦ -x` as an `isometry`. -/
protected def neg : E ≃ᵢ E :=
{ isometry_to_fun := isometry_emetric_iff_metric.2 $ λ x y, dist_neg_neg _ _,
  to_equiv := equiv.neg E }

variable {E}

@[simp] lemma neg_symm : (isometric.neg E).symm = isometric.neg E := rfl

@[simp] lemma neg_to_equiv : (isometric.neg E).to_equiv = equiv.neg E := rfl

@[simp] lemma coe_neg : ⇑(isometric.neg E) = has_neg.neg := rfl

end isometric

theorem normed_group.tendsto_nhds_zero {f : α → E} {l : filter α} :
  tendsto f l (𝓝 0) ↔ ∀ ε > 0, ∀ᶠ x in l, ∥ f x ∥ < ε :=
metric.tendsto_nhds.trans $ by simp only [dist_zero_right]

lemma normed_group.tendsto_nhds_nhds {f : E → F} {x : E} {y : F} :
  tendsto f (𝓝 x) (𝓝 y) ↔ ∀ ε > 0, ∃ δ > 0, ∀ x', ∥x' - x∥ < δ → ∥f x' - y∥ < ε :=
by simp_rw [metric.tendsto_nhds_nhds, dist_eq_norm]

lemma normed_group.cauchy_seq_iff [nonempty α] [semilattice_sup α] {u : α → E} :
  cauchy_seq u ↔ ∀ ε > 0, ∃ N, ∀ m, N ≤ m → ∀ n, N ≤ n → ∥u m - u n∥ < ε :=
by simp [metric.cauchy_seq_iff, dist_eq_norm]

lemma normed_group.uniformity_basis_dist :
  (𝓤 E).has_basis (λ (ε : ℝ), 0 < ε) (λ ε, {p : E × E | ∥p.fst - p.snd∥ < ε}) :=
begin
  convert metric.uniformity_basis_dist,
  simp [dist_eq_norm]
end

open finset

/-- A homomorphism `f` of seminormed groups is Lipschitz, if there exists a constant `C` such that
for all `x`, one has `∥f x∥ ≤ C * ∥x∥`. The analogous condition for a linear map of
(semi)normed spaces is in `normed_space.operator_norm`. -/
lemma add_monoid_hom.lipschitz_of_bound (f : E →+ F) (C : ℝ) (h : ∀x, ∥f x∥ ≤ C * ∥x∥) :
  lipschitz_with (real.to_nnreal C) f :=
lipschitz_with.of_dist_le' $ λ x y, by simpa only [dist_eq_norm, f.map_sub] using h (x - y)

lemma lipschitz_on_with_iff_norm_sub_le {f : E → F} {C : ℝ≥0} {s : set E} :
  lipschitz_on_with C f s ↔  ∀ (x ∈ s) (y ∈ s), ∥f x - f y∥ ≤ C * ∥x - y∥ :=
by simp only [lipschitz_on_with_iff_dist_le_mul, dist_eq_norm]

lemma lipschitz_on_with.norm_sub_le {f : E → F} {C : ℝ≥0} {s : set E} (h : lipschitz_on_with C f s)
  {x y : E} (x_in : x ∈ s) (y_in : y ∈ s) : ∥f x - f y∥ ≤ C * ∥x - y∥ :=
lipschitz_on_with_iff_norm_sub_le.mp h x x_in y y_in

lemma lipschitz_on_with.norm_sub_le_of_le {f : E → F} {C : ℝ≥0} {s : set E}
  (h : lipschitz_on_with C f s){x y : E} (x_in : x ∈ s) (y_in : y ∈ s) {d : ℝ} (hd : ∥x - y∥ ≤ d) :
  ∥f x - f y∥ ≤ C * d :=
(h.norm_sub_le x_in y_in).trans $ mul_le_mul_of_nonneg_left hd C.2

lemma lipschitz_with_iff_norm_sub_le {f : E → F} {C : ℝ≥0} :
  lipschitz_with C f ↔ ∀ x y, ∥f x - f y∥ ≤ C * ∥x - y∥ :=
by simp only [lipschitz_with_iff_dist_le_mul, dist_eq_norm]

alias lipschitz_with_iff_norm_sub_le ↔ lipschitz_with.norm_sub_le _

lemma lipschitz_with.norm_sub_le_of_le {f : E → F} {C : ℝ≥0} (h : lipschitz_with C f)
  {x y : E} {d : ℝ} (hd : ∥x - y∥ ≤ d) :
  ∥f x - f y∥ ≤ C * d :=
(h.norm_sub_le x y).trans $ mul_le_mul_of_nonneg_left hd C.2

/-- A homomorphism `f` of seminormed groups is continuous, if there exists a constant `C` such that
for all `x`, one has `∥f x∥ ≤ C * ∥x∥`.
The analogous condition for a linear map of normed spaces is in `normed_space.operator_norm`. -/
lemma add_monoid_hom.continuous_of_bound (f : E →+ F) (C : ℝ) (h : ∀x, ∥f x∥ ≤ C * ∥x∥) :
  continuous f :=
(f.lipschitz_of_bound C h).continuous

lemma is_compact.exists_bound_of_continuous_on [topological_space α]
  {s : set α} (hs : is_compact s) {f : α → E} (hf : continuous_on f s) :
  ∃ C, ∀ x ∈ s, ∥f x∥ ≤ C :=
begin
  have : bounded (f '' s) := (hs.image_of_continuous_on hf).bounded,
  rcases bounded_iff_forall_norm_le.1 this with ⟨C, hC⟩,
  exact ⟨C, λ x hx, hC _ (set.mem_image_of_mem _ hx)⟩,
end

lemma add_monoid_hom.isometry_iff_norm (f : E →+ F) : isometry f ↔ ∀ x, ∥f x∥ = ∥x∥ :=
begin
  simp only [isometry_emetric_iff_metric, dist_eq_norm, ← f.map_sub],
  refine ⟨λ h x, _, λ h x y, h _⟩,
  simpa using h x 0
end

lemma add_monoid_hom.isometry_of_norm (f : E →+ F) (hf : ∀ x, ∥f x∥ = ∥x∥) : isometry f :=
f.isometry_iff_norm.2 hf

lemma controlled_sum_of_mem_closure {s : add_subgroup E} {g : E}
  (hg : g ∈ closure (s : set E)) {b : ℕ → ℝ} (b_pos : ∀ n, 0 < b n) :
  ∃ v : ℕ → E,
    tendsto (λ n, ∑ i in range (n+1), v i) at_top (𝓝 g) ∧
    (∀ n, v n ∈ s) ∧
    ∥v 0 - g∥ < b 0 ∧
    ∀ n > 0, ∥v n∥ < b n :=
begin
  obtain ⟨u : ℕ → E, u_in : ∀ n, u n ∈ s, lim_u : tendsto u at_top (𝓝 g)⟩ :=
    mem_closure_iff_seq_limit.mp hg,
  obtain ⟨n₀, hn₀⟩ : ∃ n₀, ∀ n ≥ n₀, ∥u n - g∥ < b 0,
  { have : {x | ∥x - g∥ < b 0} ∈ 𝓝 g,
    { simp_rw ← dist_eq_norm,
      exact metric.ball_mem_nhds _ (b_pos _) },
    exact filter.tendsto_at_top'.mp lim_u _ this },
  set z : ℕ → E := λ n, u (n + n₀),
  have lim_z : tendsto z at_top (𝓝 g) := lim_u.comp (tendsto_add_at_top_nat n₀),
  have mem_𝓤 : ∀ n, {p : E × E | ∥p.1 - p.2∥ < b (n + 1)} ∈ 𝓤 E :=
  λ n, by simpa [← dist_eq_norm] using metric.dist_mem_uniformity (b_pos $ n+1),
  obtain ⟨φ : ℕ → ℕ, φ_extr : strict_mono φ,
          hφ : ∀ n, ∥z (φ $ n + 1) - z (φ n)∥ < b (n + 1)⟩ :=
    lim_z.cauchy_seq.subseq_mem mem_𝓤,
  set w : ℕ → E := z ∘ φ,
  have hw : tendsto w at_top (𝓝 g),
    from lim_z.comp φ_extr.tendsto_at_top,
  set v : ℕ → E := λ i, if i = 0 then w 0 else w i - w (i - 1),
  refine ⟨v, tendsto.congr (finset.eq_sum_range_sub' w) hw , _,
          hn₀ _ (n₀.le_add_left _), _⟩,
  { rintro ⟨⟩,
    { change w 0 ∈ s,
      apply u_in },
    { apply s.sub_mem ; apply u_in }, },
  { intros l hl,
    obtain ⟨k, rfl⟩ : ∃ k, l = k+1, exact nat.exists_eq_succ_of_ne_zero (ne_of_gt hl),
    apply hφ },
end

lemma controlled_sum_of_mem_closure_range {j : E →+ F} {h : F}
  (Hh : h ∈ (closure $ (j.range : set F))) {b : ℕ → ℝ} (b_pos : ∀ n, 0 < b n) :
  ∃ g : ℕ → E,
    tendsto (λ n, ∑ i in range (n+1), j (g i)) at_top (𝓝 h) ∧
    ∥j (g 0) - h∥ < b 0 ∧
    ∀ n > 0, ∥j (g n)∥ < b n :=
begin
  rcases controlled_sum_of_mem_closure Hh b_pos with ⟨v, sum_v, v_in, hv₀, hv_pos⟩,
  choose g hg using v_in,
  change ∀ (n : ℕ), j (g n) = v n at hg,
  refine ⟨g, by simpa [← hg] using sum_v, by simpa [hg 0] using hv₀, λ n hn,
          by simpa [hg] using hv_pos n hn⟩
end

section nnnorm

/-- Auxiliary class, endowing a type `α` with a function `nnnorm : α → ℝ≥0` with notation `∥x∥₊`. -/
class has_nnnorm (E : Type*) := (nnnorm : E → ℝ≥0)

export has_nnnorm (nnnorm)

notation `∥`e`∥₊` := nnnorm e

@[priority 100] -- see Note [lower instance priority]
instance semi_normed_group.to_has_nnnorm : has_nnnorm E := ⟨λ a, ⟨norm a, norm_nonneg a⟩⟩

@[simp, norm_cast] lemma coe_nnnorm (a : E) : (∥a∥₊ : ℝ) = norm a := rfl

lemma norm_to_nnreal {a : E} : ∥a∥.to_nnreal = ∥a∥₊ :=
@real.to_nnreal_coe ∥a∥₊

lemma nndist_eq_nnnorm (a b : E) : nndist a b = ∥a - b∥₊ := nnreal.eq $ dist_eq_norm _ _

@[simp] lemma nnnorm_zero : ∥(0 : E)∥₊ = 0 :=
nnreal.eq norm_zero

lemma ne_zero_of_nnnorm_ne_zero {g : E} : ∥g∥₊ ≠ 0 → g ≠ 0 :=
mt $ by { rintro rfl, exact nnnorm_zero }

lemma nnnorm_add_le (g h : E) : ∥g + h∥₊ ≤ ∥g∥₊ + ∥h∥₊ :=
nnreal.coe_le_coe.1 $ norm_add_le g h

@[simp] lemma nnnorm_neg (g : E) : ∥-g∥₊ = ∥g∥₊ :=
nnreal.eq $ norm_neg g

lemma nndist_nnnorm_nnnorm_le (g h : E) : nndist ∥g∥₊ ∥h∥₊ ≤ ∥g - h∥₊ :=
nnreal.coe_le_coe.1 $ dist_norm_norm_le g h

lemma of_real_norm_eq_coe_nnnorm (x : E) : ennreal.of_real ∥x∥ = (∥x∥₊ : ℝ≥0∞) :=
ennreal.of_real_eq_coe_nnreal _

lemma edist_eq_coe_nnnorm_sub (x y : E) : edist x y = (∥x - y∥₊ : ℝ≥0∞) :=
by rw [edist_dist, dist_eq_norm, of_real_norm_eq_coe_nnnorm]

lemma edist_eq_coe_nnnorm (x : E) : edist x 0 = (∥x∥₊ : ℝ≥0∞) :=
by rw [edist_eq_coe_nnnorm_sub, _root_.sub_zero]

lemma mem_emetric_ball_zero_iff {x : E} {r : ℝ≥0∞} : x ∈ emetric.ball (0 : E) r ↔ ↑∥x∥₊ < r :=
by rw [emetric.mem_ball, edist_eq_coe_nnnorm]

lemma nndist_add_add_le (g₁ g₂ h₁ h₂ : E) :
  nndist (g₁ + g₂) (h₁ + h₂) ≤ nndist g₁ h₁ + nndist g₂ h₂ :=
nnreal.coe_le_coe.1 $ dist_add_add_le g₁ g₂ h₁ h₂

lemma edist_add_add_le (g₁ g₂ h₁ h₂ : E) :
  edist (g₁ + g₂) (h₁ + h₂) ≤ edist g₁ h₁ + edist g₂ h₂ :=
by { simp only [edist_nndist], norm_cast, apply nndist_add_add_le }

lemma nnnorm_sum_le (s : finset ι) (f : ι → E) :
  ∥∑ a in s, f a∥₊ ≤ ∑ a in s, ∥f a∥₊ :=
s.le_sum_of_subadditive nnnorm nnnorm_zero nnnorm_add_le f

lemma add_monoid_hom.lipschitz_of_bound_nnnorm (f : E →+ F) (C : ℝ≥0) (h : ∀ x, ∥f x∥₊ ≤ C * ∥x∥₊) :
  lipschitz_with C f :=
@real.to_nnreal_coe C ▸ f.lipschitz_of_bound C h

end nnnorm

namespace lipschitz_with

variables [pseudo_emetric_space α] {K Kf Kg : ℝ≥0} {f g : α → E}

lemma neg (hf : lipschitz_with K f) : lipschitz_with K (λ x, -f x) :=
λ x y, by simpa only [edist_dist, dist_neg_neg] using hf x y

lemma add (hf : lipschitz_with Kf f) (hg : lipschitz_with Kg g) :
  lipschitz_with (Kf + Kg) (λ x, f x + g x) :=
λ x y,
calc edist (f x + g x) (f y + g y) ≤ edist (f x) (f y) + edist (g x) (g y) :
  edist_add_add_le _ _ _ _
... ≤ Kf * edist x y + Kg * edist x y :
  add_le_add (hf x y) (hg x y)
... = (Kf + Kg) * edist x y :
  (add_mul _ _ _).symm

lemma sub (hf : lipschitz_with Kf f) (hg : lipschitz_with Kg g) :
  lipschitz_with (Kf + Kg) (λ x, f x - g x) :=
by simpa only [sub_eq_add_neg] using hf.add hg.neg

end lipschitz_with

namespace antilipschitz_with

variables [pseudo_emetric_space α] {K Kf Kg : ℝ≥0} {f g : α → E}

lemma add_lipschitz_with (hf : antilipschitz_with Kf f) (hg : lipschitz_with Kg g)
  (hK : Kg < Kf⁻¹) : antilipschitz_with (Kf⁻¹ - Kg)⁻¹ (λ x, f x + g x) :=
begin
  letI : pseudo_metric_space α := pseudo_emetric_space.to_pseudo_metric_space hf.edist_ne_top,
  refine antilipschitz_with.of_le_mul_dist (λ x y, _),
  rw [nnreal.coe_inv, ← div_eq_inv_mul],
  rw le_div_iff (nnreal.coe_pos.2 $ tsub_pos_iff_lt.2 hK),
  rw [mul_comm, nnreal.coe_sub hK.le, sub_mul],
  calc ↑Kf⁻¹ * dist x y - Kg * dist x y ≤ dist (f x) (f y) - dist (g x) (g y) :
    sub_le_sub (hf.mul_le_dist x y) (hg.dist_le_mul x y)
  ... ≤ _ : le_trans (le_abs_self _) (abs_dist_sub_le_dist_add_add _ _ _ _)
end

lemma add_sub_lipschitz_with (hf : antilipschitz_with Kf f) (hg : lipschitz_with Kg (g - f))
  (hK : Kg < Kf⁻¹) : antilipschitz_with (Kf⁻¹ - Kg)⁻¹ g :=
by simpa only [pi.sub_apply, add_sub_cancel'_right] using hf.add_lipschitz_with hg hK

end antilipschitz_with

/-- A group homomorphism from an `add_comm_group` to a `semi_normed_group` induces a
`semi_normed_group` structure on the domain.

See note [reducible non-instances] -/
@[reducible]
def semi_normed_group.induced {E} [add_comm_group E] (f : E →+ F) : semi_normed_group E :=
{ norm    := λ x, ∥f x∥,
  dist_eq := λ x y, by simpa only [add_monoid_hom.map_sub, ← dist_eq_norm],
  .. pseudo_metric_space.induced f semi_normed_group.to_pseudo_metric_space, }

/-- A subgroup of a seminormed group is also a seminormed group,
with the restriction of the norm. -/
instance add_subgroup.semi_normed_group (s : add_subgroup E) : semi_normed_group s :=
semi_normed_group.induced s.subtype

/-- If `x` is an element of a subgroup `s` of a seminormed group `E`, its norm in `s` is equal to
its norm in `E`. -/
@[simp] lemma add_subgroup.coe_norm {E : Type*} [semi_normed_group E]
  {s : add_subgroup E} (x : s) :
  ∥(x : s)∥ = ∥(x:E)∥ :=
rfl

/-- If `x` is an element of a subgroup `s` of a seminormed group `E`, its norm in `s` is equal to
its norm in `E`.

This is a reversed version of the `simp` lemma `add_subgroup.coe_norm` for use by `norm_cast`.
-/

@[norm_cast] lemma add_subgroup.norm_coe {E : Type*} [semi_normed_group E] {s : add_subgroup E}
  (x : s) :
  ∥(x : E)∥ = ∥(x : s)∥ :=
rfl

/-- A submodule of a seminormed group is also a seminormed group, with the restriction of the norm.

See note [implicit instance arguments]. -/
instance submodule.semi_normed_group {𝕜 : Type*} {_ : ring 𝕜}
  {E : Type*} [semi_normed_group E] {_ : module 𝕜 E} (s : submodule 𝕜 E) : semi_normed_group s :=
{ norm := λx, norm (x : E),
  dist_eq := λx y, dist_eq_norm (x : E) (y : E) }

/-- If `x` is an element of a submodule `s` of a normed group `E`, its norm in `s` is equal to its
norm in `E`.

See note [implicit instance arguments]. -/
@[simp] lemma submodule.coe_norm {𝕜 : Type*} {_ : ring 𝕜}
  {E : Type*} [semi_normed_group E] {_ : module 𝕜 E} {s : submodule 𝕜 E} (x : s) :
  ∥(x : s)∥ = ∥(x : E)∥ :=
rfl

/-- If `x` is an element of a submodule `s` of a normed group `E`, its norm in `E` is equal to its
norm in `s`.

This is a reversed version of the `simp` lemma `submodule.coe_norm` for use by `norm_cast`.

See note [implicit instance arguments]. -/
@[norm_cast] lemma submodule.norm_coe {𝕜 : Type*} {_ : ring 𝕜}
  {E : Type*} [semi_normed_group E] {_ : module 𝕜 E} {s : submodule 𝕜 E} (x : s) :
  ∥(x : E)∥ = ∥(x : s)∥ :=
rfl

/-- seminormed group instance on the product of two seminormed groups, using the sup norm. -/
noncomputable instance prod.semi_normed_group : semi_normed_group (E × F) :=
{ norm := λx, max ∥x.1∥ ∥x.2∥,
  dist_eq := assume (x y : E × F),
    show max (dist x.1 y.1) (dist x.2 y.2) = (max ∥(x - y).1∥ ∥(x - y).2∥), by simp [dist_eq_norm] }

lemma prod.norm_def (x : E × F) : ∥x∥ = (max ∥x.1∥ ∥x.2∥) := rfl

lemma prod.nnnorm_def (x : E × F) : ∥x∥₊ = max (∥x.1∥₊) (∥x.2∥₊) :=
by { have := x.norm_def, simp only [← coe_nnnorm] at this, exact_mod_cast this }

lemma norm_fst_le (x : E × F) : ∥x.1∥ ≤ ∥x∥ :=
le_max_left _ _

lemma norm_snd_le (x : E × F) : ∥x.2∥ ≤ ∥x∥ :=
le_max_right _ _

lemma norm_prod_le_iff {x : E × F} {r : ℝ} :
  ∥x∥ ≤ r ↔ ∥x.1∥ ≤ r ∧ ∥x.2∥ ≤ r :=
max_le_iff

/-- seminormed group instance on the product of finitely many seminormed groups,
using the sup norm. -/
noncomputable instance pi.semi_normed_group {π : ι → Type*} [fintype ι]
  [Π i, semi_normed_group (π i)] : semi_normed_group (Π i, π i) :=
{ norm := λf, ((finset.sup finset.univ (λ b, ∥f b∥₊) : ℝ≥0) : ℝ),
  dist_eq := assume x y,
    congr_arg (coe : ℝ≥0 → ℝ) $ congr_arg (finset.sup finset.univ) $ funext $ assume a,
    show nndist (x a) (y a) = ∥x a - y a∥₊, from nndist_eq_nnnorm _ _ }

/-- The seminorm of an element in a product space is `≤ r` if and only if the norm of each
component is. -/
lemma pi_norm_le_iff {π : ι → Type*} [fintype ι] [∀i, semi_normed_group (π i)] {r : ℝ}
  (hr : 0 ≤ r) {x : Πi, π i} : ∥x∥ ≤ r ↔ ∀i, ∥x i∥ ≤ r :=
by simp only [← dist_zero_right, dist_pi_le_iff hr, pi.zero_apply]

/-- The seminorm of an element in a product space is `< r` if and only if the norm of each
component is. -/
lemma pi_norm_lt_iff {π : ι → Type*} [fintype ι] [∀i, semi_normed_group (π i)] {r : ℝ}
  (hr : 0 < r) {x : Πi, π i} : ∥x∥ < r ↔ ∀i, ∥x i∥ < r :=
by simp only [← dist_zero_right, dist_pi_lt_iff hr, pi.zero_apply]

lemma norm_le_pi_norm {π : ι → Type*} [fintype ι] [∀i, semi_normed_group (π i)] (x : Πi, π i)
  (i : ι) : ∥x i∥ ≤ ∥x∥ :=
(pi_norm_le_iff (norm_nonneg x)).1 le_rfl i

@[simp] lemma pi_norm_const [nonempty ι] [fintype ι] (a : E) : ∥(λ i : ι, a)∥ = ∥a∥ :=
by simpa only [← dist_zero_right] using dist_pi_const a 0

@[simp] lemma pi_nnnorm_const [nonempty ι] [fintype ι] (a : E) :
  ∥(λ i : ι, a)∥₊ = ∥a∥₊ :=
nnreal.eq $ pi_norm_const a

lemma tendsto_iff_norm_tendsto_zero {f : α → E} {a : filter α} {b : E} :
  tendsto f a (𝓝 b) ↔ tendsto (λ e, ∥f e - b∥) a (𝓝 0) :=
by { convert tendsto_iff_dist_tendsto_zero, simp [dist_eq_norm] }

lemma is_bounded_under_of_tendsto {l : filter α} {f : α → E} {c : E}
  (h : filter.tendsto f l (𝓝 c)) : is_bounded_under (≤) l (λ x, ∥f x∥) :=
⟨∥c∥ + 1, @tendsto.eventually α E f _ _ (λ k, ∥k∥ ≤ ∥c∥ + 1) h (filter.eventually_iff_exists_mem.mpr
  ⟨metric.closed_ball c 1, metric.closed_ball_mem_nhds c zero_lt_one,
    λ y hy, norm_le_norm_add_const_of_dist_le hy⟩)⟩

lemma tendsto_zero_iff_norm_tendsto_zero {f : α → E} {a : filter α} :
  tendsto f a (𝓝 0) ↔ tendsto (λ e, ∥f e∥) a (𝓝 0) :=
by { rw [tendsto_iff_norm_tendsto_zero], simp only [sub_zero] }

/-- Special case of the sandwich theorem: if the norm of `f` is eventually bounded by a real
function `g` which tends to `0`, then `f` tends to `0`.
In this pair of lemmas (`squeeze_zero_norm'` and `squeeze_zero_norm`), following a convention of
similar lemmas in `topology.metric_space.basic` and `topology.algebra.ordered`, the `'` version is
phrased using "eventually" and the non-`'` version is phrased absolutely. -/
lemma squeeze_zero_norm' {f : α → E} {g : α → ℝ} {t₀ : filter α}
  (h : ∀ᶠ n in t₀, ∥f n∥ ≤ g n)
  (h' : tendsto g t₀ (𝓝 0)) : tendsto f t₀ (𝓝 0) :=
tendsto_zero_iff_norm_tendsto_zero.mpr
  (squeeze_zero' (eventually_of_forall (λ n, norm_nonneg _)) h h')

/-- Special case of the sandwich theorem: if the norm of `f` is bounded by a real function `g` which
tends to `0`, then `f` tends to `0`.  -/
lemma squeeze_zero_norm {f : α → E} {g : α → ℝ} {t₀ : filter α}
  (h : ∀ n, ∥f n∥ ≤ g n) (h' : tendsto g t₀ (𝓝 0)) :
  tendsto f t₀ (𝓝 0) :=
squeeze_zero_norm' (eventually_of_forall h) h'

lemma tendsto_norm_sub_self (x : E) : tendsto (λ g : E, ∥g - x∥) (𝓝 x) (𝓝 0) :=
by simpa [dist_eq_norm] using tendsto_id.dist (tendsto_const_nhds : tendsto (λ g, (x:E)) (𝓝 x) _)

lemma tendsto_norm {x : E} : tendsto (λg : E, ∥g∥) (𝓝 x) (𝓝 ∥x∥) :=
by simpa using tendsto_id.dist (tendsto_const_nhds : tendsto (λ g, (0:E)) _ _)

lemma tendsto_norm_zero : tendsto (λg : E, ∥g∥) (𝓝 0) (𝓝 0) :=
by simpa using tendsto_norm_sub_self (0:E)

@[continuity]
lemma continuous_norm : continuous (λg:E, ∥g∥) :=
by simpa using continuous_id.dist (continuous_const : continuous (λ g, (0:E)))

@[continuity]
lemma continuous_nnnorm : continuous (λ (a : E), ∥a∥₊) :=
continuous_subtype_mk _ continuous_norm

lemma lipschitz_with_one_norm : lipschitz_with 1 (norm : E → ℝ) :=
by simpa only [dist_zero_left] using lipschitz_with.dist_right (0 : E)

lemma uniform_continuous_norm : uniform_continuous (norm : E → ℝ) :=
lipschitz_with_one_norm.uniform_continuous

lemma uniform_continuous_nnnorm : uniform_continuous (λ (a : E), ∥a∥₊) :=
uniform_continuous_subtype_mk uniform_continuous_norm _

section

variables {l : filter α} {f : α → E} {a : E}

lemma filter.tendsto.norm (h : tendsto f l (𝓝 a)) : tendsto (λ x, ∥f x∥) l (𝓝 ∥a∥) :=
tendsto_norm.comp h

lemma filter.tendsto.nnnorm (h : tendsto f l (𝓝 a)) :
  tendsto (λ x, ∥f x∥₊) l (𝓝 (∥a∥₊)) :=
tendsto.comp continuous_nnnorm.continuous_at h

end

section

variables [topological_space α] {f : α → E} {s : set α} {a : α} {b : E}

lemma continuous.norm (h : continuous f) : continuous (λ x, ∥f x∥) := continuous_norm.comp h

lemma continuous.nnnorm (h : continuous f) : continuous (λ x, ∥f x∥₊) :=
continuous_nnnorm.comp h

lemma continuous_at.norm (h : continuous_at f a) : continuous_at (λ x, ∥f x∥) a := h.norm

lemma continuous_at.nnnorm (h : continuous_at f a) : continuous_at (λ x, ∥f x∥₊) a := h.nnnorm

lemma continuous_within_at.norm (h : continuous_within_at f s a) :
  continuous_within_at (λ x, ∥f x∥) s a :=
h.norm

lemma continuous_within_at.nnnorm (h : continuous_within_at f s a) :
  continuous_within_at (λ x, ∥f x∥₊) s a :=
h.nnnorm

lemma continuous_on.norm (h : continuous_on f s) : continuous_on (λ x, ∥f x∥) s :=
λ x hx, (h x hx).norm

lemma continuous_on.nnnorm (h : continuous_on f s) : continuous_on (λ x, ∥f x∥₊) s :=
λ x hx, (h x hx).nnnorm

end

/-- If `∥y∥→∞`, then we can assume `y≠x` for any fixed `x`. -/
lemma eventually_ne_of_tendsto_norm_at_top {l : filter α} {f : α → E}
  (h : tendsto (λ y, ∥f y∥) l at_top) (x : E) :
  ∀ᶠ y in l, f y ≠ x :=
begin
  have : ∀ᶠ y in l, 1 + ∥x∥ ≤ ∥f y∥ := h (mem_at_top (1 + ∥x∥)),
  refine this.mono (λ y hy hxy, _),
  subst x,
  exact not_le_of_lt zero_lt_one (add_le_iff_nonpos_left.1 hy)
end

@[priority 100] -- see Note [lower instance priority]
instance semi_normed_group.has_lipschitz_add : has_lipschitz_add E :=
{ lipschitz_add := ⟨2, lipschitz_with.prod_fst.add lipschitz_with.prod_snd⟩ }

/-- A seminormed group is a uniform additive group, i.e., addition and subtraction are uniformly
continuous. -/
@[priority 100] -- see Note [lower instance priority]
instance normed_uniform_group : uniform_add_group E :=
⟨(lipschitz_with.prod_fst.sub lipschitz_with.prod_snd).uniform_continuous⟩

@[priority 100] -- see Note [lower instance priority]
instance normed_top_group : topological_add_group E :=
by apply_instance -- short-circuit type class inference

lemma nat.norm_cast_le [has_one E] : ∀ n : ℕ, ∥(n : E)∥ ≤ n * ∥(1 : E)∥
| 0 := by simp
| (n + 1) := by { rw [n.cast_succ, n.cast_succ, add_mul, one_mul],
                  exact norm_add_le_of_le (nat.norm_cast_le n) le_rfl }

lemma semi_normed_group.mem_closure_iff {s : set E} {x : E} :
  x ∈ closure s ↔ ∀ ε > 0, ∃ y ∈ s, ∥x - y∥ < ε :=
by simp [metric.mem_closure_iff, dist_eq_norm]

lemma norm_le_zero_iff' [separated_space E] {g : E} :
  ∥g∥ ≤ 0 ↔ g = 0 :=
begin
  letI : normed_group E := { to_metric_space := of_t2_pseudo_metric_space ‹_›,
    .. ‹semi_normed_group E› },
  rw [← dist_zero_right], exact dist_le_zero
end

lemma norm_eq_zero_iff' [separated_space E] {g : E} : ∥g∥ = 0 ↔ g = 0 :=
(norm_nonneg g).le_iff_eq.symm.trans norm_le_zero_iff'

lemma norm_pos_iff' [separated_space E] {g : E} : 0 < ∥g∥ ↔ g ≠ 0 :=
by rw [← not_le, norm_le_zero_iff']

lemma cauchy_seq_sum_of_eventually_eq {u v : ℕ → E} {N : ℕ} (huv : ∀ n ≥ N, u n = v n)
  (hv : cauchy_seq (λ n, ∑ k in range (n+1), v k)) : cauchy_seq (λ n, ∑ k in range (n + 1), u k) :=
begin
  let d : ℕ → E := λ n, ∑ k in range (n + 1), (u k - v k),
  rw show (λ n, ∑ k in range (n + 1), u k) = d + (λ n, ∑ k in range (n + 1), v k),
    by { ext n, simp [d] },
  have : ∀ n ≥ N, d n = d N,
  { intros n hn,
    dsimp [d],
    rw eventually_constant_sum _ hn,
    intros m hm,
    simp [huv m hm] },
  exact (tendsto_at_top_of_eventually_const this).cauchy_seq.add hv
end

end semi_normed_group

section normed_group

/-- Construct a normed group from a translation invariant distance -/
def normed_group.of_add_dist [has_norm E] [add_comm_group E] [metric_space E]
  (H1 : ∀ x : E, ∥x∥ = dist x 0)
  (H2 : ∀ x y z : E, dist x y ≤ dist (x + z) (y + z)) : normed_group E :=
{ dist_eq := λ x y, begin
    rw H1, apply le_antisymm,
    { rw [sub_eq_add_neg, ← add_right_neg y], apply H2 },
    { have := H2 (x-y) 0 y, rwa [sub_add_cancel, zero_add] at this }
  end }

/-- A normed group can be built from a norm that satisfies algebraic properties. This is
formalised in this structure. -/
structure normed_group.core (E : Type*) [add_comm_group E] [has_norm E] : Prop :=
(norm_eq_zero_iff : ∀ x : E, ∥x∥ = 0 ↔ x = 0)
(triangle : ∀ x y : E, ∥x + y∥ ≤ ∥x∥ + ∥y∥)
(norm_neg : ∀ x : E, ∥-x∥ = ∥x∥)

/-- The `semi_normed_group.core` induced by a `normed_group.core`. -/
lemma normed_group.core.to_semi_normed_group.core {E : Type*} [add_comm_group E] [has_norm E]
  (C : normed_group.core E) : semi_normed_group.core E :=
{ norm_zero := (C.norm_eq_zero_iff 0).2 rfl,
  triangle := C.triangle,
  norm_neg := C.norm_neg }

/-- Constructing a normed group from core properties of a norm, i.e., registering the distance and
the metric space structure from the norm properties. -/
def normed_group.of_core (E : Type*) [add_comm_group E] [has_norm E]
  (C : normed_group.core E) : normed_group E :=
{ eq_of_dist_eq_zero := λ x y h,
  begin
    rw [dist_eq_norm] at h,
    exact sub_eq_zero.mp ((C.norm_eq_zero_iff _).1 h)
  end
  ..semi_normed_group.of_core E (normed_group.core.to_semi_normed_group.core C) }

variables [normed_group E] [normed_group F]

@[simp] lemma norm_eq_zero {g : E} : ∥g∥ = 0 ↔ g = 0 := norm_eq_zero_iff'

lemma norm_ne_zero_iff {g : E} : ∥g∥ ≠ 0 ↔ g ≠ 0 := not_congr norm_eq_zero

@[simp] lemma norm_pos_iff {g : E} : 0 < ∥ g ∥ ↔ g ≠ 0 := norm_pos_iff'

@[simp] lemma norm_le_zero_iff {g : E} : ∥g∥ ≤ 0 ↔ g = 0 := norm_le_zero_iff'

lemma norm_sub_eq_zero_iff {u v : E} : ∥u - v∥ = 0 ↔ u = v :=
by rw [norm_eq_zero, sub_eq_zero]

lemma eq_of_norm_sub_le_zero {g h : E} (a : ∥g - h∥ ≤ 0) : g = h :=
by rwa [← sub_eq_zero, ← norm_le_zero_iff]

lemma eq_of_norm_sub_eq_zero {u v : E} (h : ∥u - v∥ = 0) : u = v :=
norm_sub_eq_zero_iff.1 h

@[simp] lemma nnnorm_eq_zero {a : E} : ∥a∥₊ = 0 ↔ a = 0 :=
by rw [← nnreal.coe_eq_zero, coe_nnnorm, norm_eq_zero]

lemma nnnorm_ne_zero_iff {g : E} : ∥g∥₊ ≠ 0 ↔ g ≠ 0 := not_congr nnnorm_eq_zero

/-- An injective group homomorphism from an `add_comm_group` to a `normed_group` induces a
`normed_group` structure on the domain.

See note [reducible non-instances]. -/
@[reducible]
def normed_group.induced {E} [add_comm_group E]
  (f : E →+ F) (h : function.injective f) : normed_group E :=
{ .. semi_normed_group.induced f,
  .. metric_space.induced f h normed_group.to_metric_space, }

/-- A subgroup of a normed group is also a normed group, with the restriction of the norm. -/
instance add_subgroup.normed_group (s : add_subgroup E) : normed_group s :=
normed_group.induced s.subtype subtype.coe_injective

/-- A submodule of a normed group is also a normed group, with the restriction of the norm.

See note [implicit instance arguments]. -/
instance submodule.normed_group {𝕜 : Type*} {_ : ring 𝕜}
  {E : Type*} [normed_group E] {_ : module 𝕜 E} (s : submodule 𝕜 E) : normed_group s :=
{ ..submodule.semi_normed_group s }

/-- normed group instance on the product of two normed groups, using the sup norm. -/
noncomputable instance prod.normed_group : normed_group (E × F) := { ..prod.semi_normed_group }

/-- normed group instance on the product of finitely many normed groups, using the sup norm. -/
noncomputable instance pi.normed_group {π : ι → Type*} [fintype ι] [∀i, normed_group (π i)] :
  normed_group (Πi, π i) := { ..pi.semi_normed_group }

lemma tendsto_norm_sub_self_punctured_nhds (a : E) : tendsto (λ x, ∥x - a∥) (𝓝[≠] a) (𝓝[>] 0) :=
(tendsto_norm_sub_self a).inf $ tendsto_principal_principal.2 $ λ x hx,
  norm_pos_iff.2 $ sub_ne_zero.2 hx

lemma tendsto_norm_nhds_within_zero : tendsto (norm : E → ℝ) (𝓝[≠] 0) (𝓝[>] 0) :=
tendsto_norm_zero.inf $ tendsto_principal_principal.2 $ λ x, norm_pos_iff.2

end normed_group


/-
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
prelude
import init.data.nat.basic init.data.prod

universes u v

inductive acc {α : Sort u} (r : α → α → Prop) : α → Prop
| intro (x : α) (h : ∀ y, r y x → acc y) : acc x

namespace acc
variables {α : Sort u} {r : α → α → Prop}

lemma inv {x y : α} (h₁ : acc r x) (h₂ : r y x) : acc r y :=
acc.rec_on h₁ (λ x₁ ac₁ ih h₂, ac₁ y h₂) h₂

end acc

/-- A relation `r : α → α → Prop` is well-founded when `∀ x, (∀ y, r y x → P y → P x) → P x` for all predicates `P`.
Once you know that a relation is well_founded, you can use it to define fixpoint functions on `α`.-/
structure well_founded {α : Sort u} (r : α → α → Prop) : Prop :=
intro :: (apply : ∀ a, acc r a)

class has_well_founded (α : Sort u) : Type u :=
(r : α → α → Prop) (wf : well_founded r)

namespace well_founded

section
parameters {α : Sort u} {r : α → α → Prop}
local infix `≺`:50    := r

parameter hwf : well_founded r

def recursion {C : α → Sort v} (a : α) (h : Π x, (Π y, y ≺ x → C y) → C x) : C a :=
acc.rec_on (apply hwf a) (λ x₁ ac₁ ih, h x₁ ih)

lemma induction {C : α → Prop} (a : α) (h : ∀ x, (∀ y, y ≺ x → C y) → C x) : C a :=
recursion a h

variable {C : α → Sort v}
variable F : Π x, (Π y, y ≺ x → C y) → C x

def fix_F (x : α) (a : acc r x) : C x :=
acc.rec_on a (λ x₁ ac₁ ih, F x₁ ih)

lemma fix_F_eq (x : α) (acx : acc r x) :
  fix_F F x acx = F x (λ (y : α) (p : y ≺ x), fix_F F y (acc.inv acx p)) :=
acc.drec (λ x r ih, rfl) acx
end

variables {α : Sort u} {C : α → Sort v} {r : α → α → Prop}

/-- Well-founded fixpoint -/
def fix (hwf : well_founded r) (F : Π x, (Π y, r y x → C y) → C x) (x : α) : C x :=
fix_F F x (apply hwf x)

/-- Well-founded fixpoint satisfies fixpoint equation -/

open well_founded

/-- Empty relation is well-founded -/
lemma empty_wf {α : Sort u} : well_founded (@empty_relation α) :=
well_founded.intro (λ (a : α),
  acc.intro a (λ (b : α) (lt : false), false.rec _ lt))

/- Subrelation of a well-founded relation is well-founded -/
namespace subrelation
section
  parameters {α : Sort u} {r Q : α → α → Prop}
  parameters (h₁ : subrelation Q r)
  parameters (h₂ : well_founded r)

  lemma accessible {a : α} (ac : acc r a) : acc Q a :=
  acc.rec_on ac (λ x ax ih,
    acc.intro x (λ (y : α) (lt : Q y x), ih y (h₁ lt)))

  lemma wf : well_founded Q :=
  ⟨λ a, accessible (apply h₂ a)⟩
end
end subrelation

-- The inverse image of a well-founded relation is well-founded
namespace inv_image
section
  parameters {α : Sort u} {β : Sort v} {r : β → β → Prop}
  parameters (f : α → β)
  parameters (h : well_founded r)

  private def acc_aux {b : β} (ac : acc r b) : ∀ (x : α), f x = b → acc (inv_image r f) x :=
  acc.rec_on ac (λ x acx ih z e,
    acc.intro z (λ y lt, eq.rec_on e (λ acx ih, ih (f y) lt y rfl) acx ih))

  lemma accessible {a : α} (ac : acc r (f a)) : acc (inv_image r f) a :=
  acc_aux ac a rfl

  lemma wf : well_founded (inv_image r f) :=
  ⟨λ a, accessible (apply h (f a))⟩
end
end inv_image

-- The transitive closure of a well-founded relation is well-founded
namespace tc
section
  parameters {α : Sort u} {r : α → α → Prop}
  local notation `r⁺` := tc r

  lemma accessible {z : α} (ac : acc r z) : acc (tc r) z :=
  acc.rec_on ac (λ x acx ih,
    acc.intro x (λ y rel,
      tc.rec_on rel
        (λ a b rab acx ih, ih a rab)
        (λ a b c rab rbc ih₁ ih₂ acx ih, acc.inv (ih₂ acx ih) rab)
        acx ih))

  lemma wf (h : well_founded r) : well_founded r⁺ :=
  ⟨λ a, accessible (apply h a)⟩
end
end tc

/-- less-than is well-founded -/
lemma nat.lt_wf : well_founded nat.lt :=
⟨nat.rec
  (acc.intro 0 (λ n h, absurd h (nat.not_lt_zero n)))
  (λ n ih, acc.intro (nat.succ n) (λ m h,
     or.elim (nat.eq_or_lt_of_le (nat.le_of_succ_le_succ h))
        (λ e, eq.substr e ih) (acc.inv ih)))⟩

def measure {α : Sort u} : (α → ℕ) → α → α → Prop :=
inv_image (<)

lemma measure_wf {α : Sort u} (f : α → ℕ) : well_founded (measure f) :=
inv_image.wf f nat.lt_wf

def sizeof_measure (α : Sort u) [has_sizeof α] : α → α → Prop :=
measure sizeof

lemma sizeof_measure_wf (α : Sort u) [has_sizeof α] : well_founded (sizeof_measure α) :=
measure_wf sizeof

instance has_well_founded_of_has_sizeof (α : Sort u) [has_sizeof α] : has_well_founded α :=
{r := sizeof_measure α, wf := sizeof_measure_wf α}

namespace prod
open well_founded

section
  variables {α : Type u} {β : Type v}
  variable  (ra  : α → α → Prop)
  variable  (rb  : β → β → Prop)

  -- Lexicographical order based on ra and rb
  inductive lex : α × β → α × β → Prop
  | left  {a₁} (b₁) {a₂} (b₂) (h : ra a₁ a₂) : lex (a₁, b₁) (a₂, b₂)
  | right (a) {b₁ b₂} (h : rb b₁ b₂)         : lex (a, b₁)  (a, b₂)

  -- relational product based on ra and rb
  inductive rprod : α × β → α × β → Prop
  | intro {a₁ b₁ a₂ b₂} (h₁ : ra a₁ a₂) (h₂ : rb b₁ b₂) : rprod (a₁, b₁) (a₂, b₂)
  end

  section
  parameters {α : Type u} {β : Type v}
  parameters {ra  : α → α → Prop} {rb  : β → β → Prop}
  local infix `≺`:50 := lex ra rb

  lemma lex_accessible {a} (aca : acc ra a) (acb : ∀ b, acc rb b): ∀ b, acc (lex ra rb) (a, b) :=
  acc.rec_on aca (λ xa aca iha b,
    acc.rec_on (acb b) (λ xb acb ihb,
      acc.intro (xa, xb) (λ p lt,
        have aux : xa = xa → xb = xb → acc (lex ra rb) p, from
          @prod.lex.rec_on α β ra rb (λ p₁ p₂, fst p₂ = xa → snd p₂ = xb → acc (lex ra rb) p₁)
                           p (xa, xb) lt
            (λ a₁ b₁ a₂ b₂ h (eq₂ : a₂ = xa) (eq₃ : b₂ = xb), iha a₁ (eq.rec_on eq₂ h) b₁)
            (λ a b₁ b₂ h (eq₂ : a = xa) (eq₃ : b₂ = xb), eq.rec_on eq₂.symm (ihb b₁ (eq.rec_on eq₃ h))),
        aux rfl rfl)))

  -- The lexicographical order of well founded relations is well-founded
  lemma lex_wf (ha : well_founded ra) (hb : well_founded rb) : well_founded (lex ra rb) :=
  ⟨λ p, cases_on p (λ a b, lex_accessible (apply ha a) (well_founded.apply hb) b)⟩

  -- relational product is a subrelation of the lex
  lemma rprod_sub_lex : ∀ a b, rprod ra rb a b → lex ra rb a b :=
  λ a b h, prod.rprod.rec_on h (λ a₁ b₁ a₂ b₂ h₁ h₂, lex.left b₁ b₂ h₁)

  -- The relational product of well founded relations is well-founded
  lemma rprod_wf (ha : well_founded ra) (hb : well_founded rb) : well_founded (rprod ra rb) :=
  subrelation.wf (rprod_sub_lex) (lex_wf ha hb)
end

instance has_well_founded {α : Type u} {β : Type v} [s₁ : has_well_founded α]
  [s₂ : has_well_founded β] : has_well_founded (α × β) :=
{r := lex s₁.r s₂.r, wf := lex_wf s₁.wf s₂.wf}

end prod


-- Las_funciones_de_extraccion_no_estan_acotadas.lean
-- Las funciones de extracción no están acotadas
-- José A. Alonso Jiménez
-- Sevilla, 30 de agosto de 2021
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Para extraer una subsucesión se aplica una función de extracción que
-- conserva el orden; por ejemplo, la subsucesión
--    uₒ, u₂, u₄, u₆, ...
-- se ha obtenido con la función de extracción φ tal que φ(n) = 2*n.
--
-- En Lean, se puede definir que φ es una función de extracción por
--    def extraccion (φ : ℕ → ℕ) :=
--      ∀ n m, n < m → φ n < φ m
--
-- Demostrar que las funciones de extracción no está acotadas; es decir,
-- que si φ es una función de extracción, entonces
--     ∀ N N', ∃ n ≥ N', φ n ≥ N
-- ---------------------------------------------------------------------

import tactic
open nat

variable {φ : ℕ → ℕ}

def extraccion (φ : ℕ → ℕ) :=
  ∀ n m, n < m → φ n < φ m

lemma aux
  (h : extraccion φ)
  : ∀ n, n ≤ φ n :=
begin
  intro n,
  induction n with m HI,
  { exact nat.zero_le (φ 0), },
  { apply nat.succ_le_of_lt,
    calc m ≤ φ m        : HI
       ... < φ (succ m) : h m (m+1) (lt_add_one m), },
end

-- 1ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
begin
  intros N N',
  let n := max N N',
  use n,
  split,
  { exact le_max_right N N', },
  { calc N ≤ n   : le_max_left N N'
       ... ≤ φ n : aux h n, },
end

-- 2ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
begin
  intros N N',
  let n := max N N',
  use n,
  split,
  { exact le_max_right N N', },
  { exact le_trans (le_max_left N N')
                   (aux h n), },
end

-- 3ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
begin
  intros N N',
  use max N N',
  split,
  { exact le_max_right N N', },
  { exact le_trans (le_max_left N N')
                   (aux h (max N N')), },
end

-- 4ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
begin
  intros N N',
  use max N N',
  exact ⟨le_max_right N N',
         le_trans (le_max_left N N')
                  (aux h (max N N'))⟩,
end

-- 5ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
λ N N',
  ⟨max N N', ⟨le_max_right N N',
              le_trans (le_max_left N N')
                       (aux h (max N N'))⟩⟩

-- 6ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
assume N N',
let n := max N N' in
have h1 : n ≥ N',
  from le_max_right N N',
show ∃ n ≥ N', φ n ≥ N, from
exists.intro n
  (exists.intro h1
    (show φ n ≥ N, from
       calc N ≤ n   : le_max_left N N'
          ... ≤ φ n : aux h n))

-- 7ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
assume N N',
let n := max N N' in
have h1 : n ≥ N',
  from le_max_right N N',
show ∃ n ≥ N', φ n ≥ N, from
⟨n, h1, calc N ≤ n   : le_max_left N N'
          ...  ≤ φ n : aux h n⟩

-- 8ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
assume N N',
let n := max N N' in
have h1 : n ≥ N',
  from le_max_right N N',
show ∃ n ≥ N', φ n ≥ N, from
⟨n, h1, le_trans (le_max_left N N')
                 (aux h (max N N'))⟩

-- 9ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
assume N N',
let n := max N N' in
have h1 : n ≥ N',
  from le_max_right N N',
⟨n, h1, le_trans (le_max_left N N')
                 (aux h n)⟩

-- 10ª demostración
example
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
assume N N',
⟨max N N', le_max_right N N',
           le_trans (le_max_left N N')
                    (aux h (max N N'))⟩

-- 11ª demostración
lemma extraccion_mye
  (h : extraccion φ)
  : ∀ N N', ∃ n ≥ N', φ n ≥ N :=
λ N N',
  ⟨max N N', le_max_right N N',
             le_trans (le_max_left N N')
             (aux h (max N N'))⟩


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

! This file was ported from Lean 3 source module category_theory.groupoid.free_groupoid
! leanprover-community/mathlib commit 706d88f2b8fdfeb0b22796433d7a6c1a010af9f2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Category.Basic
import Mathbin.CategoryTheory.Functor.Basic
import Mathbin.CategoryTheory.Groupoid
import Mathbin.Tactic.NthRewrite.Default
import Mathbin.CategoryTheory.PathCategory
import Mathbin.CategoryTheory.Quotient
import Mathbin.Combinatorics.Quiver.Symmetric

/-!
# Free groupoid on a quiver

This file defines the free groupoid on a quiver, the lifting of a prefunctor to its unique
extension as a functor from the free groupoid, and proves uniqueness of this extension.

## Main results

Given the type `V` and a quiver instance on `V`:

- `free_groupoid V`: a type synonym for `V`.
- `free_groupoid_groupoid`: the `groupoid` instance on `free_groupoid V`.
- `lift`: the lifting of a prefunctor from `V` to `V'` where `V'` is a groupoid, to a functor.
  `free_groupoid V ⥤ V'`.
- `lift_spec` and `lift_unique`: the proofs that, respectively, `lift` indeed is a lifting
  and is the unique one.

## Implementation notes

The free groupoid is first defined by symmetrifying the quiver, taking the induced path category
and finally quotienting by the reducibility relation.

-/

open Set Classical Function

attribute [local instance] prop_decidable

namespace CategoryTheory

namespace Groupoid

namespace Free

universe u v u' v' u'' v''

variable {V : Type u} [Quiver.{v + 1} V]

/-- Shorthand for the "forward" arrow corresponding to `f` in `paths $ symmetrify V` -/
abbrev Quiver.Hom.toPosPath {X Y : V} (f : X ⟶ Y) :
    (CategoryTheory.Paths.categoryPaths <| Quiver.Symmetrify V).Hom X Y :=
  f.toPos.toPath
#align category_theory.groupoid.free.quiver.hom.to_pos_path CategoryTheory.Groupoid.Free.Quiver.Hom.toPosPath

/-- Shorthand for the "forward" arrow corresponding to `f` in `paths $ symmetrify V` -/
abbrev Quiver.Hom.toNegPath {X Y : V} (f : X ⟶ Y) :
    (CategoryTheory.Paths.categoryPaths <| Quiver.Symmetrify V).Hom Y X :=
  f.toNeg.toPath
#align category_theory.groupoid.free.quiver.hom.to_neg_path CategoryTheory.Groupoid.Free.Quiver.Hom.toNegPath

/-- The "reduction" relation -/
inductive redStep : HomRel (Paths (Quiver.Symmetrify V))
  |
  step (X Z : Quiver.Symmetrify V) (f : X ⟶ Z) :
    red_step (𝟙 X) (f.toPath ≫ (Quiver.reverse f).toPath)
#align category_theory.groupoid.free.red_step CategoryTheory.Groupoid.Free.redStep

/-- The underlying vertices of the free groupoid -/
def CategoryTheory.FreeGroupoid (V) [Q : Quiver V] :=
  Quotient (@redStep V Q)
#align category_theory.free_groupoid CategoryTheory.FreeGroupoid

instance {V} [Q : Quiver V] [h : Nonempty V] : Nonempty (FreeGroupoid V) :=
  ⟨⟨h.some⟩⟩

theorem congr_reverse {X Y : Paths <| Quiver.Symmetrify V} (p q : X ⟶ Y) :
    Quotient.CompClosure redStep p q → Quotient.CompClosure redStep p.reverse q.reverse :=
  by
  rintro ⟨XW, pp, qq, WY, _, Z, f⟩
  have :
    quotient.comp_closure red_step (WY.reverse ≫ 𝟙 _ ≫ XW.reverse)
      (WY.reverse ≫ (f.to_path ≫ (Quiver.reverse f).toPath) ≫ XW.reverse) :=
    by
    apply quotient.comp_closure.intro
    apply red_step.step
  simpa only [category_struct.comp, category_struct.id, Quiver.Path.reverse, Quiver.Path.nil_comp,
    Quiver.Path.reverse_comp, Quiver.reverse_reverse, Quiver.Path.reverse_toPath,
    Quiver.Path.comp_assoc] using this
#align category_theory.groupoid.free.congr_reverse CategoryTheory.Groupoid.Free.congr_reverse

theorem congr_comp_reverse {X Y : Paths <| Quiver.Symmetrify V} (p : X ⟶ Y) :
    Quot.mk (@Quotient.CompClosure _ _ redStep _ _) (p ≫ p.reverse) =
      Quot.mk (@Quotient.CompClosure _ _ redStep _ _) (𝟙 X) :=
  by
  apply Quot.EqvGen_sound
  induction' p with _ _ q f ih
  · apply EqvGen.refl
  · simp only [Quiver.Path.reverse]
    fapply EqvGen.trans
    · exact q ≫ q.reverse
    · apply EqvGen.symm
      apply EqvGen.rel
      have :
        quotient.comp_closure red_step (q ≫ 𝟙 _ ≫ q.reverse)
          (q ≫ (f.to_path ≫ (Quiver.reverse f).toPath) ≫ q.reverse) :=
        by
        apply quotient.comp_closure.intro
        apply red_step.step
      have that : q.cons f = q.comp f.to_path := by rfl
      rw [that]
      simp only [category.assoc, category.id_comp] at this⊢
      simp only [category_struct.comp, Quiver.Path.comp_assoc] at this⊢
      exact this
    · exact ih
#align category_theory.groupoid.free.congr_comp_reverse CategoryTheory.Groupoid.Free.congr_comp_reverse

theorem congr_reverse_comp {X Y : Paths <| Quiver.Symmetrify V} (p : X ⟶ Y) :
    Quot.mk (@Quotient.CompClosure _ _ redStep _ _) (p.reverse ≫ p) =
      Quot.mk (@Quotient.CompClosure _ _ redStep _ _) (𝟙 Y) :=
  by
  nth_rw 2 [← Quiver.Path.reverse_reverse p]
  apply congr_comp_reverse
#align category_theory.groupoid.free.congr_reverse_comp CategoryTheory.Groupoid.Free.congr_reverse_comp

instance : Category (FreeGroupoid V) :=
  Quotient.category redStep

/-- The inverse of an arrow in the free groupoid -/
def quotInv {X Y : FreeGroupoid V} (f : X ⟶ Y) : Y ⟶ X :=
  Quot.liftOn f (fun pp => Quot.mk _ <| pp.reverse) fun pp qq con =>
    Quot.sound <| congr_reverse pp qq Con
#align category_theory.groupoid.free.quot_inv CategoryTheory.Groupoid.Free.quotInv

instance : Groupoid (FreeGroupoid V)
    where
  inv X Y f := quotInv f
  inv_comp' X Y p := Quot.inductionOn p fun pp => congr_reverse_comp pp
  comp_inv' X Y p := Quot.inductionOn p fun pp => congr_comp_reverse pp

/-- The inclusion of the quiver on `V` to the underlying quiver on `free_groupoid V`-/
def of (V) [Quiver V] : V ⥤q FreeGroupoid V
    where
  obj X := ⟨X⟩
  map X Y f := Quot.mk _ f.toPosPath
#align category_theory.groupoid.free.of CategoryTheory.Groupoid.Free.of

theorem of_eq :
    of V =
      (Quiver.Symmetrify.of ⋙q Paths.of).comp (Quotient.functor <| @redStep V _).toPrefunctor :=
  by
  apply Prefunctor.ext; rotate_left
  · rintro X
    rfl
  · rintro X Y f
    rfl
#align category_theory.groupoid.free.of_eq CategoryTheory.Groupoid.Free.of_eq

section UniversalProperty

variable {V' : Type u'} [Groupoid V'] (φ : V ⥤q V')

/-- The lift of a prefunctor to a groupoid, to a functor from `free_groupoid V` -/
def lift (φ : V ⥤q V') : FreeGroupoid V ⥤ V' :=
  Quotient.lift _ (Paths.lift <| Quiver.Symmetrify.lift φ)
    (by
      rintro _ _ _ _ ⟨X, Y, f⟩
      simp only [Quiver.Symmetrify.lift_reverse, paths.lift_nil, Quiver.Path.comp_nil,
        paths.lift_cons, paths.lift_to_path]
      symm
      apply groupoid.comp_inv)
#align category_theory.groupoid.free.lift CategoryTheory.Groupoid.Free.lift

theorem lift_spec (φ : V ⥤q V') : of V ⋙q (lift φ).toPrefunctor = φ :=
  by
  rw [of_eq, Prefunctor.comp_assoc, Prefunctor.comp_assoc, functor.to_prefunctor_comp]
  dsimp [lift]
  rw [quotient.lift_spec, paths.lift_spec, Quiver.Symmetrify.lift_spec]
#align category_theory.groupoid.free.lift_spec CategoryTheory.Groupoid.Free.lift_spec

theorem lift_unique (φ : V ⥤q V') (Φ : FreeGroupoid V ⥤ V') (hΦ : of V ⋙q Φ.toPrefunctor = φ) :
    Φ = lift φ := by
  apply quotient.lift_unique
  apply paths.lift_unique
  fapply @Quiver.Symmetrify.lift_unique _ _ _ _ _ _ _ _ _
  · rw [← functor.to_prefunctor_comp]
    exact hΦ
  · constructor
    rintro X Y f
    simp only [← functor.to_prefunctor_comp, Prefunctor.comp_map, paths.of_map, inv_eq_inv]
    change
      Φ.map (inv ((quotient.functor red_step).toPrefunctor.map f.to_path)) =
        inv (Φ.map ((quotient.functor red_step).toPrefunctor.map f.to_path))
    have := functor.map_inv Φ ((quotient.functor red_step).toPrefunctor.map f.to_path)
    convert this <;> simp only [inv_eq_inv]
#align category_theory.groupoid.free.lift_unique CategoryTheory.Groupoid.Free.lift_unique

end UniversalProperty

section Functoriality

variable {V' : Type u'} [Quiver.{v' + 1} V'] {V'' : Type u''} [Quiver.{v'' + 1} V'']

/-- The functor of free groupoid induced by a prefunctor of quivers -/
def CategoryTheory.freeGroupoidFunctor (φ : V ⥤q V') : FreeGroupoid V ⥤ FreeGroupoid V' :=
  lift (φ ⋙q of V')
#align category_theory.free_groupoid_functor CategoryTheory.freeGroupoidFunctor

theorem freeGroupoidFunctor_id :
    freeGroupoidFunctor (Prefunctor.id V) = Functor.id (FreeGroupoid V) :=
  by
  dsimp only [free_groupoid_functor]; symm
  apply lift_unique; rfl
#align category_theory.groupoid.free.free_groupoid_functor_id CategoryTheory.Groupoid.Free.freeGroupoidFunctor_id

theorem freeGroupoidFunctor_comp (φ : V ⥤q V') (φ' : V' ⥤q V'') :
    freeGroupoidFunctor (φ ⋙q φ') = freeGroupoidFunctor φ ⋙ freeGroupoidFunctor φ' :=
  by
  dsimp only [free_groupoid_functor]; symm
  apply lift_unique; rfl
#align category_theory.groupoid.free.free_groupoid_functor_comp CategoryTheory.Groupoid.Free.freeGroupoidFunctor_comp

end Functoriality

end Free

end Groupoid

end CategoryTheory


import analysis.complex.basic
import data.complex.basic
import order.bounded_order

/-!
# Extended complex numbers:

We define `ecomplex = ℂ∞ := with_top ℂ` to be the type of extended complex numbers,
We try to define some algebraic operations and a linear order on `ℂ∞`
and prove basic properties of these operations, conversions to/from `ℂ`.

## Main definitions

* `ℂ∞`: the extended complex numbers; defined as `with_top ℂ`; it is
  equipped with the following structures:

  - coercion from `ℂ` defined in the natural way;

* Coercions to/from other types:

  - coercion `ℂ → ℂ∞` is defined as `has_coe`, so one can use `(p : ℂ)` in a context that
    expects `z : ℂ∞`, and Lean will apply `coe` automatically;

  - `ecomplex.to_complex` sends `↑p` to `p` and `∞` to `0`;

  - `ecomplex.ne_top_equiv_complex` is an equivalence between `{z : ℂ∞ // z ≠ 0}` and `ℂ`.

## Implementation notes

We define a `can_lift ℂ∞ ℂ` instance, so one of the ways to prove theorems about an `ℂ∞`
number `z` is to consider the cases `z = ∞` and `z ≠ ∞`, and use the tactic `lift z to ℂ using hz`
in the second case. This instance is even more useful if one already has `hz : z ≠ ∞` in the
context, or if we have `(f : α → ℂ∞) (hf : ∀ z, f z ≠ ∞)`.

## Notations

* `C∞`: the type of the extended complex numbers;
* `ℂ`: the type of complex numbers; defined in `data.complex.basic`;
* `∞`: a localized notation in `ℂ∞` for `⊤ : ℂ∞`.

-/

open classical set
open_locale big_operators
noncomputable theory

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

@[derive [has_zero, nontrivial, add_comm_monoid_with_one]]
def ecomplex := with_top ℂ

localized "notation `ℂ∞` := ecomplex" in ecomplex
localized "notation `∞` := (⊤ : ecomplex)" in ecomplex

namespace ecomplex

instance : inhabited ℂ∞ := ⟨0⟩

instance : has_top ℂ∞ := ⟨option.none⟩

instance : has_coe ℂ ℂ∞ := ⟨ option.some ⟩

instance can_lift : can_lift ℂ∞ ℂ (coe) (λ z, z ≠ ∞) :=
{ prf := λ x hx, ⟨option.get $ option.ne_none_iff_is_some.1 hx, option.some_get _⟩ }

lemma none_eq_top : (none : ecomplex) = ∞ := rfl
lemma some_eq_coe (z : ℂ) : (some z : ecomplex) = (↑z : ecomplex) := rfl

protected def to_complex : ecomplex → ℂ := with_top.untop' 0

end ecomplex


import .homotopy_invariance

open category_theory

noncomputable
def singular_homology_of_base_to_of_pair (R : Type*) [comm_ring R] (n : ℕ)
  : arrow.right_func ⋙ singular_homology R n ⟶ singular_homology_of_pair R n :=
  (functor.associator arrow.right_func (singular_chain_complex R)
                      (homology_functor (Module R) (complex_shape.down ℕ) n)).inv
  ≫ whisker_right
      (whisker_left (singular_chain_complex R).map_arrow
                    (coker_functor_proj (chain_complex (Module R) ℕ)))
      (homology_functor (Module R) (complex_shape.down ℕ) n).

/-
Should move these lemmas
-/

lemma iso_of_four_term_exact_seq_start_zero_end_mono {V : Type*} [category V] [abelian V]
  {A B C D E : V} {f : A ⟶ B} {g : B ⟶ C} {h : C ⟶ D} {i : D ⟶ E}
  (e : exact_seq V [f, g, h, i]) (hf : f = 0) (hi : mono i) : is_iso g :=
begin
  apply_with is_iso_of_mono_of_epi {instances:=ff}, { apply_instance },
  { exact exact.mono_of_eq_zero ((exact_iff_exact_seq _ _).mpr (e.extract 0 2)) hf },
  { refine exact.epi_of_eq_zero ((exact_iff_exact_seq _ _).mpr (e.extract 1 2)) _,
    exact (exact.mono_iff_eq_zero ((exact_iff_exact_seq _ _).mpr (e.extract 2 2))).mp hi }
end

lemma injective_of_basis_to_basis_and_injective_on_basis {R : Type*} [ring R] [nontrivial R]
  {M N : Type*} [add_comm_group M] [add_comm_group N] [module R M] [module R N]
  {ι ι' : Type*} (bM : basis ι R M) (bN : basis ι' R N) (f : M →ₗ[R] N)
  (h1 : ∀ i, f (bM i) ∈ set.range bN) (h2 : ∀ i j, f (bM i) = f (bM j) → i = j)
  : function.injective f :=
begin
  rw [← linear_map.ker_eq_bot, linear_map.ker_eq_bot'],
  intros m hm,
  rw ← basis.total_repr bM m at ⊢ hm,
  dsimp [finsupp.total] at hm,
  simp at hm,
  rw linear_independent_iff.mp _ (bM.repr m) hm, simp,

  let get_mapped_idx := λ i, classical.some (h1 i),
  have get_mapped_idx_spec : ∀ i, bN (get_mapped_idx i) = f (bM i) :=
    λ i, classical.some_spec (h1 i),
  simp_rw ← get_mapped_idx_spec,
  apply linear_independent.comp bN.linear_independent,
  intros i j h,
  apply h2,
  exact eq.trans (get_mapped_idx_spec i).symm (eq.trans (congr_arg bN h) (get_mapped_idx_spec j))
end

-- technically we don't need the nontrivial assumption
lemma contractible_subspace_homology_of_pair_map_is_iso (R : Type*) [comm_ring R] [nontrivial R] (A X : Top)
  (f : A ⟶ X) (hf : function.injective f)
  (hA : contractible_space A) (n : ℕ) (hn : n > 0)
  : is_iso ((singular_homology_of_base_to_of_pair R n).app (arrow.mk f)) :=
begin
  letI := (_ : mono ((singular_chain_complex R).map f)),
  swap,
  { apply_with homological_complex.mono_of_eval {instances:=ff},
    intro, rw Module.mono_iff_injective, apply singular_chain_complex_map_inj, assumption },
  have : exact_seq _ [↑((homology_functor (Module R) (complex_shape.down ℕ) n).map ((singular_chain_complex R).map f)),
                      ↑((homology_functor (Module R) (complex_shape.down ℕ) n).map
                        ((coker_functor_proj (chain_complex (Module R) ℕ)).app (arrow.mk ((singular_chain_complex R).map f)))),
                      ↑(homological_complex.δ ((singular_chain_complex R).map f)
                                              ((coker_functor_proj (chain_complex (Module R) ℕ)).app
                                                (arrow.mk ((singular_chain_complex R).map f))) _ n (n - 1) _),
                      ↑((homology_functor (Module R) (complex_shape.down ℕ) (n - 1)).map ((singular_chain_complex R).map f))]
       := (homological_complex.six_term_exact_seq ((singular_chain_complex R).map f)
                                                  ((coker_functor_proj (chain_complex (Module R) ℕ)).app
                                                    (arrow.mk ((singular_chain_complex R).map f)))
                                                  (coker_functor_degreewise_SES _)
                                                  n (n - 1) _).extract 0 4,
  swap, { rw complex_shape.down_rel, apply nat.sub_add_cancel, exact hn },
  convert iso_of_four_term_exact_seq_start_zero_end_mono this _ _,
  { exact category.id_comp _ },
  { apply limits.is_zero.eq_of_src,
    obtain ⟨zero_iso⟩ := homology_of_contractible_space R A hA n hn,
    apply limits.is_zero_of_iso_of_zero (limits.is_zero_zero _),
    exact zero_iso.symm,
    apply_instance },
  { by_cases n = 1,
    { subst h,
      rw nat.sub_self 1,
      rw Module.mono_iff_injective,
      refine injective_of_basis_to_basis_and_injective_on_basis (singular_homology0_basis R A)
                                                                (singular_homology0_basis R X)
                                                                ((homology_functor (Module R) (complex_shape.down ℕ) 0).map ((singular_chain_complex R).map f))
                                                                _ _,
      { intro a, induction a, existsi quot.mk (path_setoid X).r (f a),
        symmetry,
        convert singular_homology0_map_matrix R f a,
        refl },
      { intros a b hab, induction a, induction b,
        apply quot.sound,
        letI := hA, apply path_connected_space.joined,
        refl, refl } },
    { have h' : n - 1 > 0,
      { cases n, cases hn, cases n, contradiction,
        simp only [tsub_zero, nat.succ_pos', nat.succ_sub_succ_eq_sub, gt_iff_lt] },
      constructor, intros,
      apply limits.is_zero.eq_of_tgt,
       obtain ⟨zero_iso⟩ := homology_of_contractible_space R A hA (n - 1) h',
       apply limits.is_zero_of_iso_of_zero (limits.is_zero_zero _),
       exact zero_iso.symm,
       apply_instance } }
end.

noncomputable
def singular_homology_connecting_homomorphism (R : Type*) [comm_ring R] (n : ℕ)
  {A X : Top} (f : A ⟶ X) (hf : function.injective f)
  : (singular_homology_of_pair R (n + 1)).obj (arrow.mk f) ⟶ (singular_homology R n).obj A :=
  homological_complex.δ ((singular_chain_complex R).map f)
                        ((coker_functor_proj (chain_complex (Module R) ℕ)).app
                          (arrow.mk ((singular_chain_complex R).map f)))
                          (@coker_functor_degreewise_SES _ _ _ _ _ _ _ _
                            (by { apply_with homological_complex.mono_of_eval {instances:=ff},
                                  intro, rw Module.mono_iff_injective,
                                  apply singular_chain_complex_map_inj, assumption }))
                          (n+1) n rfl.

lemma singular_homology_connecting_homomorphism_naturality (R : Type*) [comm_ring R] (n : ℕ)
  {A B X Y : Top} (f : A ⟶ X) (g : B ⟶ Y) (hf : function.injective f) (hg : function.injective g)
  (p : A ⟶ B) (q : X ⟶ Y) (w : p ≫ g = f ≫ q)
  : (singular_homology_of_pair R (n + 1)).map (arrow.hom_mk' w)
  ≫ singular_homology_connecting_homomorphism R n g hg
  = singular_homology_connecting_homomorphism R n f hf
  ≫ (singular_homology R n).map p :=
begin
  dsimp [singular_homology, singular_homology_of_pair, singular_homology_connecting_homomorphism],
  symmetry,
  have w' : (singular_chain_complex R).map f ≫ (singular_chain_complex R).map q
          = (singular_chain_complex R).map p ≫ (singular_chain_complex R).map g,
  { rw [← (singular_chain_complex R).map_comp, ← (singular_chain_complex R).map_comp, w] },
  apply δ_natural' _ _ _ _ _ ((singular_chain_complex R).map q), assumption,
  symmetry,
  exact (coker_functor_proj (chain_complex (Module R) ℕ)).naturality (arrow.hom_mk' w'.symm)
end.

lemma contractible_space_connecting_homomorphism_is_iso (R : Type*) [comm_ring R] [nontrivial R]
  (A X : Top) (f : A ⟶ X) (hf : function.injective f)
  (hX : contractible_space X) (n : ℕ) (hn : n > 0)
  : is_iso (singular_homology_connecting_homomorphism R n f hf) :=
begin
  have H : ∀ k > 0, limits.is_zero ((homology_functor (Module R) _ k).obj
                      ((singular_chain_complex R).obj X)),
  { intros k hk,
    obtain ⟨zero_iso⟩ := homology_of_contractible_space R X hX k hk,
    apply limits.is_zero_of_iso_of_zero (limits.is_zero_zero _),
    exact zero_iso.symm,
    apply_instance },
  letI := (_ : mono ((singular_chain_complex R).map f)),
  swap,
  { apply_with homological_complex.mono_of_eval {instances:=ff},
    intro, rw Module.mono_iff_injective, apply singular_chain_complex_map_inj, assumption },
  have := (homological_complex.six_term_exact_seq ((singular_chain_complex R).map f)
                                                  ((coker_functor_proj (chain_complex (Module R) ℕ)).app
                                                    (arrow.mk ((singular_chain_complex R).map f)))
                                                  (coker_functor_degreewise_SES _)
                                                  (n + 1) n _).extract 1 3,
  swap, { exact eq.refl (n + 1) },
  apply_with is_iso_of_mono_of_epi {instances:=ff}, { apply_instance },
  { refine exact.mono_of_is_zero ((exact_iff_exact_seq _ _).mpr (this.extract 0 2)) _,
    apply H, exact nat.succ_pos' },
  { refine exact.epi_of_is_zero ((exact_iff_exact_seq _ _).mpr (this.extract 1 2)) _,
    apply H, assumption }
end.


import Mathlib.Tactic.Linarith
import Mathlib.Data.Rat.Init
import Mathlib.Data.Rat.Order
import Mathlib.Data.Int.Order.Basic

set_option linter.unusedVariables false

example [LinearOrderedCommRing α] {a b : α} (h : a < b) (w : b < a) : False := by
  linarith

example {α : Type} (_inst : (a : Prop) → Decidable a) [LinearOrderedCommRing α]
    {a b c : α}
    (ha : a < 0)
    (hb : ¬b = 0)
    (hc' : c = 0)
    (h : (1 - a) * (b * b) ≤ 0)
    (hc : 0 ≤ 0)
    (w : -(a * -b * -b + b * -b + 0) = (1 - a) * (b * b))
    (h : (1 - a) * (b * b) ≤ 0) :
    0 < 1 - a := by
  linarith

example (e b c a v0 v1 : Rat) (h1 : v0 = 5*a) (h2 : v1 = 3*b) (h3 : v0 + v1 + c = 10) :
    v0 + 5 + (v1 - 3) + (c - 2) = 10 := by
  linarith

example [LinearOrderedCommRing α] (e b c a v0 v1 : α) (h1 : v0 = 5*a) (h2 : v1 = 3*b)
    (h3 : v0 + v1 + c = 10) : v0 + 5 + (v1 - 3) + (c - 2) = 10 := by
  linarith

example (h : (1 : ℤ) < 0) (g : ¬ (37 : ℤ) < 42) (_k : True) (l : (-7 : ℤ) < 5): (3 : ℤ) < 7 := by
  linarith [(rfl : 0 = 0)]

example (u v r s t : Rat) (h : 0 < u*(t*v + t*r + s)) : 0 < (t*(r + v) + s)*3*u := by
  linarith

example (A B : Rat) (h : 0 < A * B) : 0 < 8*A*B := by
  linarith

example (A B : Rat) (h : 0 < A * B) : 0 < A*8*B := by
  linarith

example [LinearOrderedCommRing α] (x : α) : 0 ≤ x := by
  have h : 0 ≤ x := sorry
  linarith

example [LinearOrderedCommRing α] (x : α) : 0 ≤ x := by
  have h : 0 ≤ x := sorry
  linarith [h]

example [LinearOrderedCommRing α] (u v r s t : α) (h : 0 < u*(t*v + t*r + s)) :
    0 < (t*(r + v) + s)*3*u := by linarith

example [LinearOrderedCommRing α] (A B : α) (h : 0 < A * B) : 0 < 8*A*B := by
  linarith

-- Needs the `cancel_denoms` preprocessor, which in turn needs the `cancel_denoms` tactic ported.
section cancel_denoms
-- example (A B : Rat) (h : 0 < A * B) : 0 < A*B/8 := by
--   linarith

-- example (A B : Rat) (h : 0 < A * B) : 0 < A/8*B := by
--   linarith

-- example (ε : Rat) (h1 : ε > 0) : ε / 2 + ε / 3 + ε / 7 < ε :=
--  by linarith

-- example (x y z : Rat) (h1 : 2*x  < 3*y) (h2 : -4*x + z/2 < 0)
--         (h3 : 12*y - z < 0)  : False :=
-- by linarith

-- example (ε : Rat) (h1 : ε > 0) : ε / 2 < ε :=
-- by linarith

-- example (ε : Rat) (h1 : ε > 0) : ε / 3 + ε / 3 + ε / 3 = ε :=
-- by linarith
end cancel_denoms

example (a b c : Rat) (h2 : b + 2 > 3 + b) : False := by
  linarith (config := {discharger := do Lean.Elab.Tactic.evalTactic (←`(tactic| ring))})

example (a b c : Rat) (h2 : b + 2 > 3 + b) : False := by
  linarith

-- We haven't implemented `restrict_type` yet.
-- example (a b c : ℚ) (x y : ℤ) (h1 : x ≤ 3*y) (h2 : b + 2 > 3 + b) : false :=
-- by linarith (config := {restrict_type := ℚ})

example (g v V c h : Rat) (h1 : h = 0) (h2 : v = V) (h3 : V > 0) (h4 : g > 0)
    (h5 : 0 ≤ c) (h6 : c < 1) : v ≤ V := by
  linarith

example (x y z : ℤ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0) (h3 : 12*y - 4* z < 0) : False := by
  linarith

example (x y z : ℤ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0) (h3 : x*y < 5) (h3 : 12*y - 4* z < 0) :
    False := by
  linarith

example (a b c : Rat) (h1 : a > 0) (h2 : b > 5) (h3 : c < -10) (h4 : a + b - c < 3) : False := by
  linarith

example (a b c : Rat) (h2 : b > 0) (h3 : ¬ b ≥ 0) : False := by
  linarith

example (x y z : Rat) (hx : x ≤ 3*y) (h2 : y ≤ 2*z) (h3 : x ≥ 6*z) : x = 3*y := by
  linarith

example (x y z : ℤ) (h1 : 2*x < 3*y) (h2 : -4*x + 2*z < 0) (h3 : x*y < 5) : ¬ 12*y - 4* z < 0 := by
  linarith

example (x y z : Rat) (hx : ¬ x > 3*y) (h2 : ¬ y > 2*z) (h3 : x ≥ 6*z) : x = 3*y := by
  linarith

example (x y : Rat) (h : 6 + ((x + 4) * x + (6 + 3 * y) * y) = 3) (h' : (x + 4) * x ≥ 0)
    (h'' : (6 + 3 * y) * y ≥ 0) : False := by
  linarith

example (a : Rat) (ha : 0 ≤ a) : 0 * 0 ≤ 2 * a := by
  linarith

example (x y : Rat) (h : x < y) : x ≠ y := by linarith

example (x y : Rat) (h : x < y) : ¬ x = y := by linarith

-- Check `linarith!` works as expected
example (x : Rat) : id x ≥ x := by
  fail_if_success
    linarith
  linarith!

opaque Nat.prime : ℕ → Prop

example (x y z : Rat) (h1 : 2*x + ((-3)*y) < 0) (h2 : (-4)*x + 2*z < 0) (h3 : 12*y + (-4)* z < 0)
    (h4 : Nat.prime 7) : False := by
  linarith

example (x y z : Rat) (h1 : 2*1*x + (3)*(y*(-1)) < 0) (h2 : (-2)*x*2 < -(z + z))
    (h3 : 12*y + (-4)* z < 0) (h4 : Nat.prime 7) : False := by
  linarith

example (w x y z : ℤ) (h1 : 4*x + (-3)*y + 6*w ≤ 0) (h2 : (-1)*x < 0) (h3 : y < 0) (h4 : w ≥ 0)
    (h5 : Nat.prime x.natAbs) : False := by
  linarith

section term_arguments

example (x : Rat) (hx : x > 0) (h : x.num < 0) : False := by
  linarith [Rat.num_pos_iff_pos.mpr hx, h]

example (x : Rat) (hx : x > 0) (h : x.num < 0) : False := by
  fail_if_success
    linarith
  fail_if_success
    linarith only [h]
  fail_if_success
    linarith only [Rat.num_pos_iff_pos.mpr hx]
  linarith only [Rat.num_pos_iff_pos.mpr hx, h]

end term_arguments

-- Check we use `exfalso` on non-comparison goals.
example (a b c : Rat) (h2 : b > 0) (h3 : b < 0) : Nat.prime 10 := by
  linarith

example (a b c : Rat) (h2 : (2 : Rat) > 3)  : a + b - c ≥ 3 :=
by linarith (config := {exfalso := false})

-- Verify that we split conjunctions in hypotheses.
example (x y : Rat)
    (h : 6 + ((x + 4) * x + (6 + 3 * y) * y) = 3 ∧ (x + 4) * x ≥ 0 ∧ (6 + 3 * y) * y ≥ 0) : False :=
by
  fail_if_success
    linarith (config := {split_hypotheses := false})
  linarith

example (h : 1 < 0) (g : ¬ 37 < 42) (k : True) (l : (-7 : ℤ) < 5) : 3 < 7 := by
  linarith [(rfl : 0 = 0)]

example (h : 1 < 0) : 3 = 7 := by
  linarith [Int.zero_lt_one]

example (h1 : (1 : ℕ) < 1) : False := by
  linarith

example (a b c : ℕ) : a + b ≥ a := by
  linarith

example (a b i : ℕ) (h1 :  ¬ a < i) (h2 : b < i) (h3 : a ≤ b) : False := by
  linarith

example (x y : ℕ) (h : x < 3 * y) : True := by
  zify at h
  trivial

example : (Nat.cast 2 : ℤ) = 2 := Nat.cast_ofNat

example (x y z : ℕ) (hx : x ≤ 3*y) (h2 : y ≤ 2*z) (h3 : x ≥ 6*z) : x = 3*y := by
  linarith

example (a b c : ℕ) : ¬ a + b < a := by
  linarith

example (n : ℕ) (h1 : n ≤ 3) (h2 : n > 2) : n = 3 := by
  linarith

example (z : ℕ) (hz : ¬ z ≥ 2) (h2 : ¬ z + 1 ≤ 2) : False := by
  linarith

example (z : ℕ) (hz : ¬ z ≥ 2) : z + 1 ≤ 2 := by
  linarith

example (i : ℤ) (hi : i > 5) : 2 * i + 3 > 11 := by
  linarith

example (m : ℕ) : m * m + m + (2 * m + 2) = m * m + m + (m + 1) + (m + 1) := by
  linarith

example (mess : ℕ → ℕ) (S n : ℕ) :
    mess S + (n * mess S + n * 2 + 1) < n * mess S + mess S + (n * 2 + 2) := by
  linarith

example (p n p' n' : ℕ) (h : p + n' = p' + n) : n + p' = n' + p := by
  linarith

-- example (a b c : ℚ) (h1 : 1 / a < b) (h2 : b < c) : 1 / a < c := by
--   linarith

example (N : ℕ) (n : ℕ) (Hirrelevant : n > N) (A : Rat) (l : Rat) (h : A - l ≤ -(A - l))
    (h_1 : ¬A ≤ -A) (h_2 : ¬l ≤ -l) (h_3 : -(A - l) < 1) :  A < l + 1 := by
  linarith

-- example (d : Rat) (q n : ℕ) (h1 : ((q : Rat) - 1)*n ≥ 0) (h2 : d = 2/3*(((q : Rat) - 1)*n)) :
--     d ≤ ((q : Rat) - 1)*n := by
--   linarith

-- example (d : Rat) (q n : ℕ) (h1 : ((q : Rat) - 1)*n ≥ 0) (h2 : d = 2/3*(((q : Rat) - 1)*n)) :
--     ((q : Rat) - 1)*n - d = 1/3 * (((q : Rat) - 1)*n) := by
--   linarith

-- example (x y z : ℚ) (hx : x < 5) (hx2 : x > 5) (hy : y < 5000000000) (hz : z > 34*y) : false :=
-- by linarith only [hx, hx2]

-- example (x y z : ℚ) (hx : x < 5) (hy : y < 5000000000) (hz : z > 34*y) : x ≤ 5 :=
-- by linarith only [hx]

example (u v x y A B : ℚ)
(a : 0 < A)
(a_1 : 0 <= 1 - A)
(a_2 : 0 <= B - 1)
(a_3 : 0 <= B - x)
(a_4 : 0 <= B - y)
(a_5 : 0 <= u)
(a_6 : 0 <= v)
(a_7 : 0 < A - u)
(a_8 : 0 < A - v) :
 u * y + v * x + u * v < 3 * A * B :=
 by nlinarith

example (u v x y A B : ℚ) : (0 < A) → (A ≤ 1) → (1 ≤ B)
→ (x ≤ B) → ( y ≤ B)
→ (0 ≤ u ) → (0 ≤ v )
→ (u < A) → ( v < A)
→ (u * y + v * x + u * v < 3 * A * B) := by
  intros
  nlinarith

example (u v x y A B : Rat)
(a_7 : 0 < A - u)
(a_8 : 0 < A - v) :
(0 <= A * (1 - A))
-> (0 <= A * (B - 1))
-> (0 < A * (A - u))
-> (0 <= (B - 1) * (A - u))
-> (0 <= (B - 1) * (A - v))
-> (0 <= (B - x) * v)
-> (0 <= (B - y) * u)
-> (0 <= u * (A - v))
->
 u * y + v * x + u * v < 3 * A * B := by
  intros
  linarith

example (u v x y A B : Rat)
(a : 0 < A)
(a_1 : 0 <= 1 - A)
(a_2 : 0 <= B - 1)
(a_3 : 0 <= B - x)
(a_4 : 0 <= B - y)
(a_5 : 0 <= u)
(a_6 : 0 <= v)
(a_7 : 0 < A - u)
(a_8 : 0 < A - v) :
 (0 < A * A)
-> (0 <= A * (1 - A))
-> (0 <= A * (B - 1))
-> (0 <= A * (B - x))
-> (0 <= A * (B - y))
-> (0 <= A * u)
-> (0 <= A * v)
-> (0 < A * (A - u))
-> (0 < A * (A - v))
-> (0 <= (1 - A) * A)
-> (0 <= (1 - A) * (1 - A))
-> (0 <= (1 - A) * (B - 1))
-> (0 <= (1 - A) * (B - x))
-> (0 <= (1 - A) * (B - y))
-> (0 <= (1 - A) * u)
-> (0 <= (1 - A) * v)
-> (0 <= (1 - A) * (A - u))
-> (0 <= (1 - A) * (A - v))
-> (0 <= (B - 1) * A)
-> (0 <= (B - 1) * (1 - A))
-> (0 <= (B - 1) * (B - 1))
-> (0 <= (B - 1) * (B - x))
-> (0 <= (B - 1) * (B - y))
-> (0 <= (B - 1) * u)
-> (0 <= (B - 1) * v)
-> (0 <= (B - 1) * (A - u))
-> (0 <= (B - 1) * (A - v))
-> (0 <= (B - x) * A)
-> (0 <= (B - x) * (1 - A))
-> (0 <= (B - x) * (B - 1))
-> (0 <= (B - x) * (B - x))
-> (0 <= (B - x) * (B - y))
-> (0 <= (B - x) * u)
-> (0 <= (B - x) * v)
-> (0 <= (B - x) * (A - u))
-> (0 <= (B - x) * (A - v))
-> (0 <= (B - y) * A)
-> (0 <= (B - y) * (1 - A))
-> (0 <= (B - y) * (B - 1))
-> (0 <= (B - y) * (B - x))
-> (0 <= (B - y) * (B - y))
-> (0 <= (B - y) * u)
-> (0 <= (B - y) * v)
-> (0 <= (B - y) * (A - u))
-> (0 <= (B - y) * (A - v))
-> (0 <= u * A)
-> (0 <= u * (1 - A))
-> (0 <= u * (B - 1))
-> (0 <= u * (B - x))
-> (0 <= u * (B - y))
-> (0 <= u * u)
-> (0 <= u * v)
-> (0 <= u * (A - u))
-> (0 <= u * (A - v))
-> (0 <= v * A)
-> (0 <= v * (1 - A))
-> (0 <= v * (B - 1))
-> (0 <= v * (B - x))
-> (0 <= v * (B - y))
-> (0 <= v * u)
-> (0 <= v * v)
-> (0 <= v * (A - u))
-> (0 <= v * (A - v))
-> (0 < (A - u) * A)
-> (0 <= (A - u) * (1 - A))
-> (0 <= (A - u) * (B - 1))
-> (0 <= (A - u) * (B - x))
-> (0 <= (A - u) * (B - y))
-> (0 <= (A - u) * u)
-> (0 <= (A - u) * v)
-> (0 < (A - u) * (A - u))
-> (0 < (A - u) * (A - v))
-> (0 < (A - v) * A)
-> (0 <= (A - v) * (1 - A))
-> (0 <= (A - v) * (B - 1))
-> (0 <= (A - v) * (B - x))
-> (0 <= (A - v) * (B - y))
-> (0 <= (A - v) * u)
-> (0 <= (A - v) * v)
-> (0 < (A - v) * (A - u))
-> (0 < (A - v) * (A - v))
->
 u * y + v * x + u * v < 3 * A * B := by
  intros
  linarith

-- TODO this needs the `cancelDenoms` preprocessor.
-- example (A B : ℚ) : (0 < A) → (1 ≤ B) → (0 < A / 8 * B) := by
--   intros
--   nlinarith

example (x y : ℚ) : 0 ≤ x ^2 + y ^2 := by
  nlinarith

example (x y : ℚ) : 0 ≤ x*x + y*y := by
  nlinarith

example (x y : ℚ) : x = 0 → y = 0 → x*x + y*y = 0 := by
  intros
  nlinarith

lemma norm_eq_zero_iff {x y : ℚ} : x * x + y * y = 0 ↔ x = 0 ∧ y = 0 := by
  constructor
  · intro
    constructor <;>
      nlinarith
  · intro; nlinarith

lemma norm_zero_left {x y : ℚ} (h1 : x * x + y * y = 0) : x = 0 := by
  nlinarith

lemma norm_nonpos_right {x y : ℚ} (h1 : x * x + y * y ≤ 0) : y = 0 := by
  nlinarith

lemma norm_nonpos_left (x y : ℚ) (h1 : x * x + y * y ≤ 0) : x = 0 := by
  nlinarith

variable {E : Type _} [AddGroup E]
example (f : ℤ → E) (h : 0 = f 0) : 1 ≤ 2 := by nlinarith
example (a : E) (h : a = a) : 1 ≤ 2  := by nlinarith

-- -- test that the apply bug doesn't affect linarith preprocessing

-- constant α : Type
-- variable [fact false] -- we work in an inconsistent context below
-- def leα : α → α → Prop := λ a b, ∀ c : α, true

-- noncomputable instance : linear_ordered_field α :=
-- by refine_struct { le := leα }; exact (fact.out false).elim

-- example (a : α) (ha : a < 2) : a ≤ a :=
-- by linarith

example (p q r s t u v w : ℕ) (h1 : p + u = q + t) (h2 : r + w = s + v) :
  p * r + q * s + (t * w + u * v) = p * s + q * r + (t * v + u * w) :=
by nlinarith

-- -- Tests involving a norm, including that squares in a type where `sq_nonneg` does not apply
-- -- do not cause an exception
-- variables {R : Type _} [Ring R] (abs : R → ℚ)

-- lemma abs_nonneg' : ∀ r, 0 ≤ abs r := (fact.out false).elim

-- example (t : R) (a b : ℚ) (h : a ≤ b) : abs (t^2) * a ≤ abs (t^2) * b :=
-- by nlinarith [abs_nonneg' abs (t^2)]

-- example (t : R)  (a b : ℚ) (h : a ≤ b) : a ≤ abs (t^2) + b :=
-- by linarith [abs_nonneg' abs (t^2)]

-- example (t : R) (a b : ℚ) (h : a ≤ b) : abs t * a ≤ abs t * b :=
-- by nlinarith [abs_nonneg' abs t]

-- constant T : Type

-- attribute [instance]
-- constant T_zero : ordered_ring T

-- namespace T

-- lemma zero_lt_one : (0 : T) < 1 := (fact.out false).elim

-- lemma works {a b : ℕ} (hab : a ≤ b) (h : b < a) : false :=
-- begin
--   linarith,
-- end

-- end T

-- example (a b c : ℚ) (h : a ≠ b) (h3 : b ≠ c) (h2 : a ≥ b) : b ≠ c :=
-- by linarith {split_ne := tt}

-- example (a b c : ℚ) (h : a ≠ b) (h2 : a ≥ b) (h3 : b ≠ c) : a > b :=
-- by linarith {split_ne := tt}

example (x y : ℚ) (h₁ : 0 ≤ y) (h₂ : y ≤ x) : y * x ≤ x * x := by nlinarith

example (x y : ℚ) (h₁ : 0 ≤ y) (h₂ : y ≤ x) : y * x ≤ x ^ 2 := by nlinarith

axiom foo {x : Int} : 1 ≤ x → 1 ≤ x*x
lemma bar (x y: Int) (h : 0 ≤ y ∧ 1 ≤ x) : 1 ≤ y + x * x := by linarith [foo h.2]

-- -- issue #9822
-- lemma mytest (j : ℕ) (h : 0 < j) : j-1 < j:= by linarith

example [LinearOrderedCommRing α] (h : ∃ x : α, 0 ≤ x) : True := by
  cases' h with x h
  have : 0 ≤ x; · linarith
  trivial

-- At one point, this failed, due to `mdata` interfering with `Expr.isEq`.
example (a : Int) : a = a := by
  have h : True := True.intro
  linarith

example (n : Nat) (h1 : ¬n = 1) (h2 : n ≥ 1) : n ≥ 2 := by
  by_contra h3
  suffices n = 1 by exact h1 this
  linarith

example (n : Nat) (h1 : ¬n = 1) (h2 : n ≥ 1) : n ≥ 2 := by
  have h4 : n ≥ 1 := h2
  by_contra h3
  suffices n = 1 by exact h1 this
  linarith


class HasMulComm (α : Type u) [Mul α] : Prop where
    mulComm : {a b : α} → a * b = b * a

class A (α : Type u) where
    [Mul : Mul α]
attribute [instance] A.mk A.Mul

class B (α : Type u) where
    [Mul : Mul α]
    [HasMulComm : HasMulComm α]
attribute [instance] B.mk B.Mul B.HasMulComm

example [A α] [HasMulComm α] : B α := inferInstance

section
variable [A α] [HasMulComm α]

example : B α := by exact inferInstance

example : B α := inferInstance

end


import pseudo_normed_group.basic
import pseudo_normed_group.category
import breen_deligne.suitable

/-!

# Universal maps and pseudo-normed groups

This file contains the definition of the action of a basic universal map
on powers of a pseudo-normed group and related types.

## Main definitions

- `f.eval_png : (M^m) →+ (M^n)` : the group homomorphism induced by a basic universal map.
- `f.eval_png₀ : M_{c₁}^m → M_{c₂}^n` : the induced map if `f` is (c₁, c₂)-suitable.

-/
noncomputable theory

local attribute [instance] type_pow

open_locale nnreal big_operators matrix

namespace breen_deligne

namespace basic_universal_map

variables {m n : ℕ} (f : basic_universal_map m n)
variables (M : Type*)

section pseudo_normed_group

variables [pseudo_normed_group M]

open add_monoid_hom pseudo_normed_group

end pseudo_normed_group

section profinitely_filtered_pseudo_normed_group

open pseudo_normed_group profinitely_filtered_pseudo_normed_group add_monoid_hom

open comphaus_filtered_pseudo_normed_group_hom comphaus_filtered_pseudo_normed_group

variables [profinitely_filtered_pseudo_normed_group M]

/-- `eval_png M f` is the homomorphism `M^m → M^n` of profinitely filtered pseudo-normed groups
obtained by matrix multiplication with the matrix `f`. -/
def eval_png : basic_universal_map m n →+
  comphaus_filtered_pseudo_normed_group_hom (M ^ m) (M ^ n) :=
add_monoid_hom.mk' (λ f, pi_lift (λ j, ∑ i, f j i • pi_proj i)
  begin
    let C : ℝ≥0 := finset.univ.sup (λ j, ∑ i, (f j i).nat_abs),
    refine ⟨C, λ j, _⟩,
    intros c x hx,
    have : ∑ i, (↑(f j i).nat_abs * (1 * c)) ≤ C * c,
    { rw ← finset.sum_mul, exact mul_le_mul' (finset.le_sup (finset.mem_univ j)) (one_mul c).le },
    refine filtration_mono this _,
    simp only [← coe_to_add_monoid_hom, ← to_add_monoid_hom_hom_apply,
      add_monoid_hom.map_sum, add_monoid_hom.map_zsmul, add_monoid_hom.finset_sum_apply],
    refine sum_mem_filtration _ _ _ _,
    rintro i -,
    refine int_smul_mem_filtration _ _ _ _,
    exact @pi_proj_bound_by _ (λ _, M) _ i _ _ hx,
  end)
  begin
    intros f g,
    ext x j,
    simp only [← coe_to_add_monoid_hom, ← to_add_monoid_hom_hom_apply,
      add_monoid_hom.map_add, add_monoid_hom.add_apply, pi.add_apply],
    simp only [coe_to_add_monoid_hom, to_add_monoid_hom_hom_apply],
    simp only [pi_lift_to_fun, mk_to_pi_apply, ← to_add_monoid_hom_hom_apply,
      add_monoid_hom.map_sum, add_monoid_hom.map_zsmul, add_monoid_hom.finset_sum_apply],
    rw ← finset.sum_add_distrib,
    simp only [pi_proj_to_fun, to_add_monoid_hom_hom_apply, coe_smul,
      coe_to_add_monoid_hom, pi.smul_apply, add_smul],
    refl
  end

lemma eval_png_apply (x : M^m) : eval_png M f x = λ j, ∑ i, f j i • (x i) :=
begin
  ext j,
  simp only [eval_png, mk'_apply, pi_lift_to_fun, mk_to_pi_apply, ← to_add_monoid_hom_hom_apply],
  simp only [add_monoid_hom.map_sum, add_monoid_hom.map_zsmul,
    add_monoid_hom.finset_sum_apply],
  refl
end

lemma eval_png_mem_filtration :
  (eval_png M f).to_add_monoid_hom ∈
    filtration ((M^m) →+ (M^n)) (finset.univ.sup $ λ i, ∑ j, (f i j).nat_abs) :=
begin
  apply mk_to_pi_mem_filtration,
  intro j,
  let C : ℝ≥0 := finset.univ.sup (λ j, ∑ i, (f j i).nat_abs),
  intros c x hx,
  have : ∑ i, (↑(f j i).nat_abs * (1 * c)) ≤ C * c,
  { rw ← finset.sum_mul, exact mul_le_mul' (finset.le_sup (finset.mem_univ j)) (one_mul c).le },
  refine filtration_mono this _,
  simp only [← coe_to_add_monoid_hom, ← to_add_monoid_hom_hom_apply,
    add_monoid_hom.map_sum, add_monoid_hom.map_zsmul, add_monoid_hom.finset_sum_apply],
  refine sum_mem_filtration _ _ _ _,
  rintro i -,
  refine int_smul_mem_filtration _ _ _ _,
  exact @pi_proj_bound_by _ (λ _, M) _ i _ _ hx,
end

lemma eval_png_mem_filtration' (c₁ c₂ : ℝ≥0) [h : f.suitable c₁ c₂]
  (x : M^m) (hx : x ∈ filtration (M^m) c₁) :
  (eval_png M f x) ∈ filtration (M^n) c₂ :=
filtration_mono (f.sup_mul_le c₁ c₂) (f.eval_png_mem_filtration M hx)

/-- `f.eval_png₀ M` is the group homomorphism `(M^m) →+ (M^n)`
obtained by matrix multiplication with the matrix `f`,
but restricted to `(filtration M c₁)^m → (filtration M c₂)^n`. -/
@[simps {fully_applied := ff}]
def eval_png₀ (c₁ c₂ : ℝ≥0) [h : f.suitable c₁ c₂] (x : filtration (M^m) c₁) :
  filtration (M^n) c₂ :=
⟨eval_png M f x, eval_png_mem_filtration' f M c₁ c₂ x x.2⟩

lemma eval_png_comp {l m n} (g : basic_universal_map m n) (f : basic_universal_map l m) :
  eval_png M (basic_universal_map.comp g f) = (eval_png M g).comp (eval_png M f) :=
begin
  ext x j,
  simp only [eval_png_apply, function.comp_app, coe_comp, basic_universal_map.comp, comp_to_fun,
    matrix.mul_apply, finset.smul_sum, finset.sum_smul, mul_smul, add_monoid_hom.mk'_apply],
  rw finset.sum_comm
end

lemma eval_png₀_continuous (c₁ c₂ : ℝ≥0) [f.suitable c₁ c₂] : continuous (f.eval_png₀ M c₁ c₂) :=
(eval_png M f).continuous _ (λ x, rfl)

end profinitely_filtered_pseudo_normed_group

end basic_universal_map

end breen_deligne

#lint- only unused_arguments def_lemma doc_blame


theorem P_implies_P (P : Prop) : P → P :=
begin
  intro HP,
  exact HP
end