import linear_algebra.basic
import algebra.module
variables (M : Type )(R :Type) [ring R] [add_comm_group M]  [add_comm_group M] [module R M]
variables (f g : M→ₗ[R]M)
example : f * g = linear_map.id := sorry

#check (by apply_instance : has_mul (M→ₗ[R]M ))

linear_map.has_scalar


/-
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 Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.instances.nnreal
import Mathlib.topology.algebra.module
import Mathlib.topology.metric_space.antilipschitz
import Mathlib.PostPort

universes u_5 l u_1 u_2 u_3 u_4 u_6 u_7

namespace Mathlib

/-!
# Normed spaces
-/

/-- Auxiliary class, endowing a type `α` with a function `norm : α → ℝ`. This class is designed to
be extended in more interesting classes specifying the properties of the norm. -/
class has_norm (α : Type u_5)
where
  norm : α → ℝ

/-- 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 (α : Type u_5)
extends metric_space α, has_norm α, add_comm_group α
where
  dist_eq : ∀ (x y : α), dist x y = norm (x - y)

/-- Construct a normed group from a translation invariant distance -/
def normed_group.of_add_dist {α : Type u_1} [has_norm α] [add_comm_group α] [metric_space α] (H1 : ∀ (x : α), norm x = dist x 0) (H2 : ∀ (x y z : α), dist x y ≤ dist (x + z) (y + z)) : normed_group α :=
  normed_group.mk sorry

/-- Construct a normed group from a translation invariant distance -/
def normed_group.of_add_dist' {α : Type u_1} [has_norm α] [add_comm_group α] [metric_space α] (H1 : ∀ (x : α), norm x = dist x 0) (H2 : ∀ (x y z : α), dist (x + z) (y + z) ≤ dist x y) : normed_group α :=
  normed_group.mk sorry

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

/-- 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 (α : Type u_1) [add_comm_group α] [has_norm α] (C : normed_group.core α) : normed_group α :=
  normed_group.mk sorry

theorem dist_eq_norm {α : Type u_1} [normed_group α] (g : α) (h : α) : dist g h = norm (g - h) :=
  normed_group.dist_eq g h

theorem dist_eq_norm' {α : Type u_1} [normed_group α] (g : α) (h : α) : dist g h = norm (h - g) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (dist g h = norm (h - g))) (dist_comm g h)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (dist h g = norm (h - g))) (dist_eq_norm h g))) (Eq.refl (norm (h - g))))

@[simp] theorem dist_zero_right {α : Type u_1} [normed_group α] (g : α) : dist g 0 = norm g :=
  eq.mpr (id (Eq._oldrec (Eq.refl (dist g 0 = norm g)) (dist_eq_norm g 0)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (norm (g - 0) = norm g)) (sub_zero g))) (Eq.refl (norm g)))

theorem tendsto_norm_cocompact_at_top {α : Type u_1} [normed_group α] [proper_space α] : filter.tendsto norm (filter.cocompact α) filter.at_top := sorry

theorem norm_sub_rev {α : Type u_1} [normed_group α] (g : α) (h : α) : norm (g - h) = norm (h - g) := sorry

@[simp] theorem norm_neg {α : Type u_1} [normed_group α] (g : α) : norm (-g) = norm g := sorry

@[simp] theorem dist_add_left {α : Type u_1} [normed_group α] (g : α) (h₁ : α) (h₂ : α) : dist (g + h₁) (g + h₂) = dist h₁ h₂ := sorry

@[simp] theorem dist_add_right {α : Type u_1} [normed_group α] (g₁ : α) (g₂ : α) (h : α) : dist (g₁ + h) (g₂ + h) = dist g₁ g₂ := sorry

@[simp] theorem dist_neg_neg {α : Type u_1} [normed_group α] (g : α) (h : α) : dist (-g) (-h) = dist g h := sorry

@[simp] theorem dist_sub_left {α : Type u_1} [normed_group α] (g : α) (h₁ : α) (h₂ : α) : dist (g - h₁) (g - h₂) = dist h₁ h₂ := sorry

@[simp] theorem dist_sub_right {α : Type u_1} [normed_group α] (g₁ : α) (g₂ : α) (h : α) : dist (g₁ - h) (g₂ - h) = dist g₁ g₂ := sorry

/-- Triangle inequality for the norm. -/
theorem norm_add_le {α : Type u_1} [normed_group α] (g : α) (h : α) : norm (g + h) ≤ norm g + norm h := sorry

theorem norm_add_le_of_le {α : Type u_1} [normed_group α] {g₁ : α} {g₂ : α} {n₁ : ℝ} {n₂ : ℝ} (H₁ : norm g₁ ≤ n₁) (H₂ : norm g₂ ≤ n₂) : norm (g₁ + g₂) ≤ n₁ + n₂ :=
  le_trans (norm_add_le g₁ g₂) (add_le_add H₁ H₂)

theorem dist_add_add_le {α : Type u_1} [normed_group α] (g₁ : α) (g₂ : α) (h₁ : α) (h₂ : α) : dist (g₁ + g₂) (h₁ + h₂) ≤ dist g₁ h₁ + dist g₂ h₂ := sorry

theorem dist_add_add_le_of_le {α : Type u_1} [normed_group α] {g₁ : α} {g₂ : α} {h₁ : α} {h₂ : α} {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₂)

theorem dist_sub_sub_le {α : Type u_1} [normed_group α] (g₁ : α) (g₂ : α) (h₁ : α) (h₂ : α) : dist (g₁ - g₂) (h₁ - h₂) ≤ dist g₁ h₁ + dist g₂ h₂ := sorry

theorem dist_sub_sub_le_of_le {α : Type u_1} [normed_group α] {g₁ : α} {g₂ : α} {h₁ : α} {h₂ : α} {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₂)

theorem abs_dist_sub_le_dist_add_add {α : Type u_1} [normed_group α] (g₁ : α) (g₂ : α) (h₁ : α) (h₂ : α) : abs (dist g₁ h₁ - dist g₂ h₂) ≤ dist (g₁ + g₂) (h₁ + h₂) := sorry

@[simp] theorem norm_nonneg {α : Type u_1} [normed_group α] (g : α) : 0 ≤ norm g :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ norm g)) (Eq.symm (dist_zero_right g)))) dist_nonneg

@[simp] theorem norm_eq_zero {α : Type u_1} [normed_group α] {g : α} : norm g = 0 ↔ g = 0 :=
  dist_zero_right g ▸ dist_eq_zero

@[simp] theorem norm_zero {α : Type u_1} [normed_group α] : norm 0 = 0 :=
  iff.mpr norm_eq_zero rfl

theorem norm_of_subsingleton {α : Type u_1} [normed_group α] [subsingleton α] (x : α) : norm x = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (norm x = 0)) (subsingleton.elim x 0)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (norm 0 = 0)) norm_zero)) (Eq.refl 0))

theorem norm_sum_le {α : Type u_1} [normed_group α] {β : Type u_2} (s : finset β) (f : β → α) : norm (finset.sum s fun (a : β) => f a) ≤ finset.sum s fun (a : β) => norm (f a) :=
  finset.le_sum_of_subadditive norm norm_zero norm_add_le

theorem norm_sum_le_of_le {α : Type u_1} [normed_group α] {β : Type u_2} (s : finset β) {f : β → α} {n : β → ℝ} (h : ∀ (b : β), b ∈ s → norm (f b) ≤ n b) : norm (finset.sum s fun (b : β) => f b) ≤ finset.sum s fun (b : β) => n b :=
  le_trans (norm_sum_le s f) (finset.sum_le_sum h)

@[simp] theorem norm_pos_iff {α : Type u_1} [normed_group α] {g : α} : 0 < norm g ↔ g ≠ 0 :=
  dist_zero_right g ▸ dist_pos

@[simp] theorem norm_le_zero_iff {α : Type u_1} [normed_group α] {g : α} : norm g ≤ 0 ↔ g = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (norm g ≤ 0 ↔ g = 0)) (Eq.symm (dist_zero_right g)))) dist_le_zero

theorem eq_of_norm_sub_le_zero {α : Type u_1} [normed_group α] {g : α} {h : α} (a : norm (g - h) ≤ 0) : g = h :=
  eq.mpr (id (Eq._oldrec (Eq.refl (g = h)) (Eq.symm (propext sub_eq_zero))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (g - h = 0)) (Eq.symm (propext norm_le_zero_iff)))) a)

theorem norm_sub_le {α : Type u_1} [normed_group α] (g : α) (h : α) : norm (g - h) ≤ norm g + norm h := sorry

theorem norm_sub_le_of_le {α : Type u_1} [normed_group α] {g₁ : α} {g₂ : α} {n₁ : ℝ} {n₂ : ℝ} (H₁ : norm g₁ ≤ n₁) (H₂ : norm g₂ ≤ n₂) : norm (g₁ - g₂) ≤ n₁ + n₂ :=
  le_trans (norm_sub_le g₁ g₂) (add_le_add H₁ H₂)

theorem dist_le_norm_add_norm {α : Type u_1} [normed_group α] (g : α) (h : α) : dist g h ≤ norm g + norm h :=
  eq.mpr (id (Eq._oldrec (Eq.refl (dist g h ≤ norm g + norm h)) (dist_eq_norm g h))) (norm_sub_le g h)

theorem abs_norm_sub_norm_le {α : Type u_1} [normed_group α] (g : α) (h : α) : abs (norm g - norm h) ≤ norm (g - h) := sorry

theorem norm_sub_norm_le {α : Type u_1} [normed_group α] (g : α) (h : α) : norm g - norm h ≤ norm (g - h) :=
  le_trans (le_abs_self (norm g - norm h)) (abs_norm_sub_norm_le g h)

theorem dist_norm_norm_le {α : Type u_1} [normed_group α] (g : α) (h : α) : dist (norm g) (norm h) ≤ norm (g - h) :=
  abs_norm_sub_norm_le g h

theorem eq_of_norm_sub_eq_zero {α : Type u_1} [normed_group α] {u : α} {v : α} (h : norm (u - v) = 0) : u = v :=
  eq_of_dist_eq_zero (eq.mpr (id (Eq._oldrec (Eq.refl (dist u v = 0)) (dist_eq_norm u v))) h)

theorem norm_sub_eq_zero_iff {α : Type u_1} [normed_group α] {u : α} {v : α} : norm (u - v) = 0 ↔ u = v := sorry

theorem norm_le_insert {α : Type u_1} [normed_group α] (u : α) (v : α) : norm v ≤ norm u + norm (u - v) := sorry

theorem ball_0_eq {α : Type u_1} [normed_group α] (ε : ℝ) : metric.ball 0 ε = set_of fun (x : α) => norm x < ε := sorry

theorem mem_ball_iff_norm {α : Type u_1} [normed_group α] {g : α} {h : α} {r : ℝ} : h ∈ metric.ball g r ↔ norm (h - g) < r :=
  eq.mpr (id (Eq._oldrec (Eq.refl (h ∈ metric.ball g r ↔ norm (h - g) < r)) (propext metric.mem_ball)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (dist h g < r ↔ norm (h - g) < r)) (dist_eq_norm h g)))
      (iff.refl (norm (h - g) < r)))

theorem mem_ball_iff_norm' {α : Type u_1} [normed_group α] {g : α} {h : α} {r : ℝ} : h ∈ metric.ball g r ↔ norm (g - h) < r :=
  eq.mpr (id (Eq._oldrec (Eq.refl (h ∈ metric.ball g r ↔ norm (g - h) < r)) (propext metric.mem_ball')))
    (eq.mpr (id (Eq._oldrec (Eq.refl (dist g h < r ↔ norm (g - h) < r)) (dist_eq_norm g h)))
      (iff.refl (norm (g - h) < r)))

theorem mem_closed_ball_iff_norm {α : Type u_1} [normed_group α] {g : α} {h : α} {r : ℝ} : h ∈ metric.closed_ball g r ↔ norm (h - g) ≤ r :=
  eq.mpr (id (Eq._oldrec (Eq.refl (h ∈ metric.closed_ball g r ↔ norm (h - g) ≤ r)) (propext metric.mem_closed_ball)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (dist h g ≤ r ↔ norm (h - g) ≤ r)) (dist_eq_norm h g)))
      (iff.refl (norm (h - g) ≤ r)))

theorem mem_closed_ball_iff_norm' {α : Type u_1} [normed_group α] {g : α} {h : α} {r : ℝ} : h ∈ metric.closed_ball g r ↔ norm (g - h) ≤ r :=
  eq.mpr (id (Eq._oldrec (Eq.refl (h ∈ metric.closed_ball g r ↔ norm (g - h) ≤ r)) (propext metric.mem_closed_ball')))
    (eq.mpr (id (Eq._oldrec (Eq.refl (dist g h ≤ r ↔ norm (g - h) ≤ r)) (dist_eq_norm g h)))
      (iff.refl (norm (g - h) ≤ r)))

theorem norm_le_of_mem_closed_ball {α : Type u_1} [normed_group α] {g : α} {h : α} {r : ℝ} (H : h ∈ metric.closed_ball g r) : norm h ≤ norm g + r := sorry

theorem norm_lt_of_mem_ball {α : Type u_1} [normed_group α] {g : α} {h : α} {r : ℝ} (H : h ∈ metric.ball g r) : norm h < norm g + r := sorry

@[simp] theorem mem_sphere_iff_norm {α : Type u_1} [normed_group α] (v : α) (w : α) (r : ℝ) : w ∈ metric.sphere v r ↔ norm (w - v) = r := sorry

@[simp] theorem mem_sphere_zero_iff_norm {α : Type u_1} [normed_group α] {w : α} {r : ℝ} : w ∈ metric.sphere 0 r ↔ norm w = r := sorry

@[simp] theorem norm_eq_of_mem_sphere {α : Type u_1} [normed_group α] {r : ℝ} (x : ↥(metric.sphere 0 r)) : norm ↑x = r :=
  iff.mp mem_sphere_zero_iff_norm (subtype.property x)

theorem nonzero_of_mem_sphere {α : Type u_1} [normed_group α] {r : ℝ} (hr : 0 < r) (x : ↥(metric.sphere 0 r)) : ↑x ≠ 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑x ≠ 0)) (Eq.symm (propext norm_pos_iff))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (0 < norm ↑x)) (norm_eq_of_mem_sphere x))) hr)

theorem nonzero_of_mem_unit_sphere {α : Type u_1} [normed_group α] (x : ↥(metric.sphere 0 1)) : ↑x ≠ 0 :=
  nonzero_of_mem_sphere (eq.mpr (id (eq_true_intro zero_lt_one')) trivial) x

/-- We equip the sphere, in a normed group, with a formal operation of negation, namely the
antipodal map. -/
protected instance metric.sphere.has_neg {α : Type u_1} [normed_group α] {r : ℝ} : Neg ↥(metric.sphere 0 r) :=
  { neg := fun (w : ↥(metric.sphere 0 r)) => { val := -↑w, property := sorry } }

@[simp] theorem coe_neg_sphere {α : Type u_1} [normed_group α] {r : ℝ} (v : ↥(metric.sphere 0 r)) : ↑(-v) = -↑v :=
  rfl

theorem normed_group.tendsto_nhds_zero {α : Type u_1} {γ : Type u_3} [normed_group α] {f : γ → α} {l : filter γ} : filter.tendsto f l (nhds 0) ↔ ∀ (ε : ℝ), ε > 0 → filter.eventually (fun (x : γ) => norm (f x) < ε) l := sorry

theorem normed_group.tendsto_nhds_nhds {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] {f : α → β} {x : α} {y : β} : filter.tendsto f (nhds x) (nhds y) ↔
  ∀ (ε : ℝ) (H : ε > 0), ∃ (δ : ℝ), ∃ (H : δ > 0), ∀ (x' : α), norm (x' - x) < δ → norm (f x' - y) < ε := sorry

/-- A homomorphism `f` of normed 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 normed spaces is in `normed_space.operator_norm`. -/
theorem add_monoid_hom.lipschitz_of_bound {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] (f : α →+ β) (C : ℝ) (h : ∀ (x : α), norm (coe_fn f x) ≤ C * norm x) : lipschitz_with (nnreal.of_real C) ⇑f := sorry

theorem lipschitz_on_with_iff_norm_sub_le {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] {f : α → β} {C : nnreal} {s : set α} : lipschitz_on_with C f s ↔ ∀ (x : α), x ∈ s → ∀ (y : α), y ∈ s → norm (f x - f y) ≤ ↑C * norm (x - y) := sorry

theorem lipschitz_on_with.norm_sub_le {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] {f : α → β} {C : nnreal} {s : set α} (h : lipschitz_on_with C f s) {x : α} {y : α} (x_in : x ∈ s) (y_in : y ∈ s) : norm (f x - f y) ≤ ↑C * norm (x - y) :=
  iff.mp lipschitz_on_with_iff_norm_sub_le h x x_in y y_in

/-- A homomorphism `f` of normed 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`. -/
theorem add_monoid_hom.continuous_of_bound {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] (f : α →+ β) (C : ℝ) (h : ∀ (x : α), norm (coe_fn f x) ≤ C * norm x) : continuous ⇑f :=
  lipschitz_with.continuous (add_monoid_hom.lipschitz_of_bound f C h)

/-- Version of the norm taking values in nonnegative reals. -/
def nnnorm {α : Type u_1} [normed_group α] (a : α) : nnreal :=
  { val := norm a, property := norm_nonneg a }

@[simp] theorem coe_nnnorm {α : Type u_1} [normed_group α] (a : α) : ↑(nnnorm a) = norm a :=
  rfl

theorem nndist_eq_nnnorm {α : Type u_1} [normed_group α] (a : α) (b : α) : nndist a b = nnnorm (a - b) :=
  nnreal.eq (dist_eq_norm a b)

@[simp] theorem nnnorm_eq_zero {α : Type u_1} [normed_group α] {a : α} : nnnorm a = 0 ↔ a = 0 := sorry

@[simp] theorem nnnorm_zero {α : Type u_1} [normed_group α] : nnnorm 0 = 0 :=
  nnreal.eq norm_zero

theorem nnnorm_add_le {α : Type u_1} [normed_group α] (g : α) (h : α) : nnnorm (g + h) ≤ nnnorm g + nnnorm h :=
  iff.mpr nnreal.coe_le_coe (norm_add_le g h)

@[simp] theorem nnnorm_neg {α : Type u_1} [normed_group α] (g : α) : nnnorm (-g) = nnnorm g :=
  nnreal.eq (norm_neg g)

theorem nndist_nnnorm_nnnorm_le {α : Type u_1} [normed_group α] (g : α) (h : α) : nndist (nnnorm g) (nnnorm h) ≤ nnnorm (g - h) :=
  iff.mpr nnreal.coe_le_coe (dist_norm_norm_le g h)

theorem of_real_norm_eq_coe_nnnorm {β : Type u_2} [normed_group β] (x : β) : ennreal.of_real (norm x) = ↑(nnnorm x) :=
  ennreal.of_real_eq_coe_nnreal (norm_nonneg x)

theorem edist_eq_coe_nnnorm_sub {β : Type u_2} [normed_group β] (x : β) (y : β) : edist x y = ↑(nnnorm (x - y)) := sorry

theorem edist_eq_coe_nnnorm {β : Type u_2} [normed_group β] (x : β) : edist x 0 = ↑(nnnorm x) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (edist x 0 = ↑(nnnorm x))) (edist_eq_coe_nnnorm_sub x 0)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (↑(nnnorm (x - 0)) = ↑(nnnorm x))) (sub_zero x))) (Eq.refl ↑(nnnorm x)))

theorem mem_emetric_ball_0_iff {β : Type u_2} [normed_group β] {x : β} {r : ennreal} : x ∈ emetric.ball 0 r ↔ ↑(nnnorm x) < r :=
  eq.mpr (id (Eq._oldrec (Eq.refl (x ∈ emetric.ball 0 r ↔ ↑(nnnorm x) < r)) (propext emetric.mem_ball)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (edist x 0 < r ↔ ↑(nnnorm x) < r)) (edist_eq_coe_nnnorm x)))
      (iff.refl (↑(nnnorm x) < r)))

theorem nndist_add_add_le {α : Type u_1} [normed_group α] (g₁ : α) (g₂ : α) (h₁ : α) (h₂ : α) : nndist (g₁ + g₂) (h₁ + h₂) ≤ nndist g₁ h₁ + nndist g₂ h₂ :=
  iff.mpr nnreal.coe_le_coe (dist_add_add_le g₁ g₂ h₁ h₂)

theorem edist_add_add_le {α : Type u_1} [normed_group α] (g₁ : α) (g₂ : α) (h₁ : α) (h₂ : α) : edist (g₁ + g₂) (h₁ + h₂) ≤ edist g₁ h₁ + edist g₂ h₂ := sorry

theorem nnnorm_sum_le {α : Type u_1} [normed_group α] {β : Type u_2} (s : finset β) (f : β → α) : nnnorm (finset.sum s fun (a : β) => f a) ≤ finset.sum s fun (a : β) => nnnorm (f a) :=
  finset.le_sum_of_subadditive nnnorm nnnorm_zero nnnorm_add_le

theorem lipschitz_with.neg {β : Type u_2} [normed_group β] {α : Type u_1} [emetric_space α] {K : nnreal} {f : α → β} (hf : lipschitz_with K f) : lipschitz_with K fun (x : α) => -f x := sorry

theorem lipschitz_with.add {β : Type u_2} [normed_group β] {α : Type u_1} [emetric_space α] {Kf : nnreal} {f : α → β} (hf : lipschitz_with Kf f) {Kg : nnreal} {g : α → β} (hg : lipschitz_with Kg g) : lipschitz_with (Kf + Kg) fun (x : α) => f x + g x :=
  fun (x y : α) =>
    trans_rel_left LessEq (le_trans (edist_add_add_le (f x) (g x) (f y) (g y)) (add_le_add (hf x y) (hg x y)))
      (Eq.symm (add_mul (↑Kf) (↑Kg) (edist x y)))

theorem lipschitz_with.sub {β : Type u_2} [normed_group β] {α : Type u_1} [emetric_space α] {Kf : nnreal} {f : α → β} (hf : lipschitz_with Kf f) {Kg : nnreal} {g : α → β} (hg : lipschitz_with Kg g) : lipschitz_with (Kf + Kg) fun (x : α) => f x - g x := sorry

theorem antilipschitz_with.add_lipschitz_with {β : Type u_2} [normed_group β] {α : Type u_1} [metric_space α] {Kf : nnreal} {f : α → β} (hf : antilipschitz_with Kf f) {Kg : nnreal} {g : α → β} (hg : lipschitz_with Kg g) (hK : Kg < (Kf⁻¹)) : antilipschitz_with (Kf⁻¹ - Kg⁻¹) fun (x : α) => f x + g x := sorry

/-- A submodule of a normed group is also a normed group, with the restriction of the norm.
As all instances can be inferred from the submodule `s`, they are put as implicit instead of
typeclasses. -/
protected instance submodule.normed_group {𝕜 : Type u_1} {_x : ring 𝕜} {E : Type u_2} [normed_group E] : {_x_1 : module 𝕜 E} → (s : submodule 𝕜 E) → normed_group ↥s :=
  fun (s : submodule 𝕜 E) => normed_group.mk sorry

/-- normed group instance on the product of two normed groups, using the sup norm. -/
protected instance prod.normed_group {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] : normed_group (α × β) :=
  normed_group.mk sorry

theorem prod.norm_def {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] (x : α × β) : norm x = max (norm (prod.fst x)) (norm (prod.snd x)) :=
  rfl

theorem prod.nnnorm_def {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] (x : α × β) : nnnorm x = max (nnnorm (prod.fst x)) (nnnorm (prod.snd x)) := sorry

theorem norm_fst_le {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] (x : α × β) : norm (prod.fst x) ≤ norm x :=
  le_max_left (norm (prod.fst x)) (norm (prod.snd x))

theorem norm_snd_le {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] (x : α × β) : norm (prod.snd x) ≤ norm x :=
  le_max_right (norm (prod.fst x)) (norm (prod.snd x))

theorem norm_prod_le_iff {α : Type u_1} {β : Type u_2} [normed_group α] [normed_group β] {x : α × β} {r : ℝ} : norm x ≤ r ↔ norm (prod.fst x) ≤ r ∧ norm (prod.snd x) ≤ r :=
  max_le_iff

/-- normed group instance on the product of finitely many normed groups, using the sup norm. -/
protected instance pi.normed_group {ι : Type u_4} {π : ι → Type u_1} [fintype ι] [(i : ι) → normed_group (π i)] : normed_group ((i : ι) → π i) :=
  normed_group.mk sorry

/-- The norm of an element in a product space is `≤ r` if and only if the norm of each
component is. -/
theorem pi_norm_le_iff {ι : Type u_4} {π : ι → Type u_1} [fintype ι] [(i : ι) → normed_group (π i)] {r : ℝ} (hr : 0 ≤ r) {x : (i : ι) → π i} : norm x ≤ r ↔ ∀ (i : ι), norm (x i) ≤ r := sorry

/-- The norm of an element in a product space is `< r` if and only if the norm of each
component is. -/
theorem pi_norm_lt_iff {ι : Type u_4} {π : ι → Type u_1} [fintype ι] [(i : ι) → normed_group (π i)] {r : ℝ} (hr : 0 < r) {x : (i : ι) → π i} : norm x < r ↔ ∀ (i : ι), norm (x i) < r := sorry

theorem norm_le_pi_norm {ι : Type u_4} {π : ι → Type u_1} [fintype ι] [(i : ι) → normed_group (π i)] (x : (i : ι) → π i) (i : ι) : norm (x i) ≤ norm x :=
  iff.mp (pi_norm_le_iff (norm_nonneg x)) (le_refl (norm x)) i

@[simp] theorem pi_norm_const {α : Type u_1} {ι : Type u_4} [normed_group α] [Nonempty ι] [fintype ι] (a : α) : (norm fun (i : ι) => a) = norm a := sorry

@[simp] theorem pi_nnnorm_const {α : Type u_1} {ι : Type u_4} [normed_group α] [Nonempty ι] [fintype ι] (a : α) : (nnnorm fun (i : ι) => a) = nnnorm a :=
  nnreal.eq (pi_norm_const a)

theorem tendsto_iff_norm_tendsto_zero {β : Type u_2} {ι : Type u_4} [normed_group β] {f : ι → β} {a : filter ι} {b : β} : filter.tendsto f a (nhds b) ↔ filter.tendsto (fun (e : ι) => norm (f e - b)) a (nhds 0) := sorry

theorem tendsto_zero_iff_norm_tendsto_zero {β : Type u_2} {γ : Type u_3} [normed_group β] {f : γ → β} {a : filter γ} : filter.tendsto f a (nhds 0) ↔ filter.tendsto (fun (e : γ) => norm (f e)) a (nhds 0) := sorry

/-- 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. -/
theorem squeeze_zero_norm' {α : Type u_1} {γ : Type u_3} [normed_group α] {f : γ → α} {g : γ → ℝ} {t₀ : filter γ} (h : filter.eventually (fun (n : γ) => norm (f n) ≤ g n) t₀) (h' : filter.tendsto g t₀ (nhds 0)) : filter.tendsto f t₀ (nhds 0) :=
  iff.mpr tendsto_zero_iff_norm_tendsto_zero
    (squeeze_zero' (filter.eventually_of_forall fun (n : γ) => norm_nonneg (f n)) 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`.  -/
theorem squeeze_zero_norm {α : Type u_1} {γ : Type u_3} [normed_group α] {f : γ → α} {g : γ → ℝ} {t₀ : filter γ} (h : ∀ (n : γ), norm (f n) ≤ g n) (h' : filter.tendsto g t₀ (nhds 0)) : filter.tendsto f t₀ (nhds 0) :=
  squeeze_zero_norm' (filter.eventually_of_forall h) h'

theorem tendsto_norm_sub_self {α : Type u_1} [normed_group α] (x : α) : filter.tendsto (fun (g : α) => norm (g - x)) (nhds x) (nhds 0) := sorry

theorem tendsto_norm {α : Type u_1} [normed_group α] {x : α} : filter.tendsto (fun (g : α) => norm g) (nhds x) (nhds (norm x)) := sorry

theorem tendsto_norm_zero {α : Type u_1} [normed_group α] : filter.tendsto (fun (g : α) => norm g) (nhds 0) (nhds 0) := sorry

theorem continuous_norm {α : Type u_1} [normed_group α] : continuous fun (g : α) => norm g := sorry

theorem continuous_nnnorm {α : Type u_1} [normed_group α] : continuous nnnorm :=
  continuous_subtype_mk (fun (a : α) => norm_nonneg a) continuous_norm

theorem tendsto_norm_nhds_within_zero {α : Type u_1} [normed_group α] : filter.tendsto norm (nhds_within 0 (singleton 0ᶜ)) (nhds_within 0 (set.Ioi 0)) :=
  filter.tendsto.inf (continuous.tendsto' continuous_norm 0 0 norm_zero)
    (iff.mpr filter.tendsto_principal_principal fun (x : α) => iff.mpr norm_pos_iff)

theorem filter.tendsto.norm {α : Type u_1} {γ : Type u_3} [normed_group α] {l : filter γ} {f : γ → α} {a : α} (h : filter.tendsto f l (nhds a)) : filter.tendsto (fun (x : γ) => norm (f x)) l (nhds (norm a)) :=
  filter.tendsto.comp tendsto_norm h

theorem filter.tendsto.nnnorm {α : Type u_1} {γ : Type u_3} [normed_group α] {l : filter γ} {f : γ → α} {a : α} (h : filter.tendsto f l (nhds a)) : filter.tendsto (fun (x : γ) => nnnorm (f x)) l (nhds (nnnorm a)) :=
  filter.tendsto.comp (continuous.continuous_at continuous_nnnorm) h

theorem continuous.norm {α : Type u_1} {γ : Type u_3} [normed_group α] [topological_space γ] {f : γ → α} (h : continuous f) : continuous fun (x : γ) => norm (f x) :=
  continuous.comp continuous_norm h

theorem continuous.nnnorm {α : Type u_1} {γ : Type u_3} [normed_group α] [topological_space γ] {f : γ → α} (h : continuous f) : continuous fun (x : γ) => nnnorm (f x) :=
  continuous.comp continuous_nnnorm h

theorem continuous_at.norm {α : Type u_1} {γ : Type u_3} [normed_group α] [topological_space γ] {f : γ → α} {a : γ} (h : continuous_at f a) : continuous_at (fun (x : γ) => norm (f x)) a :=
  filter.tendsto.norm h

theorem continuous_at.nnnorm {α : Type u_1} {γ : Type u_3} [normed_group α] [topological_space γ] {f : γ → α} {a : γ} (h : continuous_at f a) : continuous_at (fun (x : γ) => nnnorm (f x)) a :=
  filter.tendsto.nnnorm h

theorem continuous_within_at.norm {α : Type u_1} {γ : Type u_3} [normed_group α] [topological_space γ] {f : γ → α} {s : set γ} {a : γ} (h : continuous_within_at f s a) : continuous_within_at (fun (x : γ) => norm (f x)) s a :=
  filter.tendsto.norm h

theorem continuous_within_at.nnnorm {α : Type u_1} {γ : Type u_3} [normed_group α] [topological_space γ] {f : γ → α} {s : set γ} {a : γ} (h : continuous_within_at f s a) : continuous_within_at (fun (x : γ) => nnnorm (f x)) s a :=
  filter.tendsto.nnnorm h

theorem continuous_on.norm {α : Type u_1} {γ : Type u_3} [normed_group α] [topological_space γ] {f : γ → α} {s : set γ} (h : continuous_on f s) : continuous_on (fun (x : γ) => norm (f x)) s :=
  fun (x : γ) (hx : x ∈ s) => continuous_within_at.norm (h x hx)

theorem continuous_on.nnnorm {α : Type u_1} {γ : Type u_3} [normed_group α] [topological_space γ] {f : γ → α} {s : set γ} (h : continuous_on f s) : continuous_on (fun (x : γ) => nnnorm (f x)) s :=
  fun (x : γ) (hx : x ∈ s) => continuous_within_at.nnnorm (h x hx)

/-- If `∥y∥→∞`, then we can assume `y≠x` for any fixed `x`. -/
theorem eventually_ne_of_tendsto_norm_at_top {α : Type u_1} {γ : Type u_3} [normed_group α] {l : filter γ} {f : γ → α} (h : filter.tendsto (fun (y : γ) => norm (f y)) l filter.at_top) (x : α) : filter.eventually (fun (y : γ) => f y ≠ x) l := sorry

/-- A normed group is a uniform additive group, i.e., addition and subtraction are uniformly
continuous. -/
protected instance normed_uniform_group {α : Type u_1} [normed_group α] : uniform_add_group α :=
  uniform_add_group.mk
    (lipschitz_with.uniform_continuous (lipschitz_with.sub lipschitz_with.prod_fst lipschitz_with.prod_snd))

protected instance normed_top_monoid {α : Type u_1} [normed_group α] : has_continuous_add α :=
  topological_add_group.to_has_continuous_add

protected instance normed_top_group {α : Type u_1} [normed_group α] : topological_add_group α :=
  uniform_add_group.to_topological_add_group

theorem nat.norm_cast_le {α : Type u_1} [normed_group α] [HasOne α] (n : ℕ) : norm ↑n ≤ ↑n * norm 1 := sorry

/-- A normed ring is a ring endowed with a norm which satisfies the inequality `∥x y∥ ≤ ∥x∥ ∥y∥`. -/
class normed_ring (α : Type u_5)
extends ring α, has_norm α, metric_space α
where
  dist_eq : ∀ (x y : α), dist x y = norm (x - y)
  norm_mul : ∀ (a b : α), norm (a * b) ≤ norm a * norm b

/-- A normed commutative ring is a commutative ring endowed with a norm which satisfies
the inequality `∥x y∥ ≤ ∥x∥ ∥y∥`. -/
class normed_comm_ring (α : Type u_5)
extends normed_ring α
where
  mul_comm : ∀ (x y : α), x * y = y * x

/-- A mixin class with the axiom `∥1∥ = 1`. Many `normed_ring`s and all `normed_field`s satisfy this
axiom. -/
class norm_one_class (α : Type u_5) [has_norm α] [HasOne α]
where
  norm_one : norm 1 = 1

@[simp] theorem nnnorm_one {α : Type u_1} [normed_group α] [HasOne α] [norm_one_class α] : nnnorm 1 = 1 :=
  nnreal.eq norm_one

protected instance normed_comm_ring.to_comm_ring {α : Type u_1} [β : normed_comm_ring α] : comm_ring α :=
  comm_ring.mk ring.add sorry ring.zero sorry sorry ring.neg ring.sub sorry sorry ring.mul sorry ring.one sorry sorry
    sorry sorry normed_comm_ring.mul_comm

protected instance normed_ring.to_normed_group {α : Type u_1} [β : normed_ring α] : normed_group α :=
  normed_group.mk normed_ring.dist_eq

protected instance prod.norm_one_class {α : Type u_1} {β : Type u_2} [normed_group α] [HasOne α] [norm_one_class α] [normed_group β] [HasOne β] [norm_one_class β] : norm_one_class (α × β) :=
  norm_one_class.mk
    (eq.mpr
      (id
        (Eq.trans
          ((fun (a a_1 : ℝ) (e_1 : a = a_1) (ᾰ ᾰ_1 : ℝ) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2) (norm 1) 1
            (Eq.trans
              (Eq.trans (prod.norm_def 1)
                ((fun (a a_1 : ℝ) (e_2 : a = a_1) (b b_1 : ℝ) (e_3 : b = b_1) => congr (congr_arg max e_2) e_3)
                  (norm (prod.fst 1)) 1
                  (Eq.trans ((fun (ᾰ ᾰ_1 : α) (e_2 : ᾰ = ᾰ_1) => congr_arg norm e_2) (prod.fst 1) 1 prod.fst_one)
                    norm_one)
                  (norm (prod.snd 1)) 1
                  (Eq.trans ((fun (ᾰ ᾰ_1 : β) (e_2 : ᾰ = ᾰ_1) => congr_arg norm e_2) (prod.snd 1) 1 prod.snd_one)
                    norm_one)))
              (max_eq_right (le_refl 1)))
            1 1 (Eq.refl 1))
          (propext (eq_self_iff_true 1))))
      trivial)

theorem norm_mul_le {α : Type u_1} [normed_ring α] (a : α) (b : α) : norm (a * b) ≤ norm a * norm b :=
  normed_ring.norm_mul a b

theorem list.norm_prod_le' {α : Type u_1} [normed_ring α] {l : List α} : l ≠ [] → norm (list.prod l) ≤ list.prod (list.map norm l) := sorry

theorem list.norm_prod_le {α : Type u_1} [normed_ring α] [norm_one_class α] (l : List α) : norm (list.prod l) ≤ list.prod (list.map norm l) := sorry

theorem finset.norm_prod_le' {ι : Type u_4} {α : Type u_1} [normed_comm_ring α] (s : finset ι) (hs : finset.nonempty s) (f : ι → α) : norm (finset.prod s fun (i : ι) => f i) ≤ finset.prod s fun (i : ι) => norm (f i) := sorry

theorem finset.norm_prod_le {ι : Type u_4} {α : Type u_1} [normed_comm_ring α] [norm_one_class α] (s : finset ι) (f : ι → α) : norm (finset.prod s fun (i : ι) => f i) ≤ finset.prod s fun (i : ι) => norm (f i) := sorry

/-- If `α` is a normed ring, then `∥a^n∥≤ ∥a∥^n` for `n > 0`. See also `norm_pow_le`. -/
theorem norm_pow_le' {α : Type u_1} [normed_ring α] (a : α) {n : ℕ} : 0 < n → norm (a ^ n) ≤ norm a ^ n := sorry

/-- If `α` is a normed ring with `∥1∥=1`, then `∥a^n∥≤ ∥a∥^n`. See also `norm_pow_le'`. -/
theorem norm_pow_le {α : Type u_1} [normed_ring α] [norm_one_class α] (a : α) (n : ℕ) : norm (a ^ n) ≤ norm a ^ n := sorry

theorem eventually_norm_pow_le {α : Type u_1} [normed_ring α] (a : α) : filter.eventually (fun (n : ℕ) => norm (a ^ n) ≤ norm a ^ n) filter.at_top :=
  iff.mpr filter.eventually_at_top (Exists.intro 1 fun (b : ℕ) (h : b ≥ 1) => norm_pow_le' a (iff.mp nat.succ_le_iff h))

theorem units.norm_pos {α : Type u_1} [normed_ring α] [nontrivial α] (x : units α) : 0 < norm ↑x :=
  iff.mpr norm_pos_iff (units.ne_zero x)

/-- In a normed ring, the left-multiplication `add_monoid_hom` is bounded. -/
theorem mul_left_bound {α : Type u_1} [normed_ring α] (x : α) (y : α) : norm (coe_fn (add_monoid_hom.mul_left x) y) ≤ norm x * norm y :=
  norm_mul_le x

/-- In a normed ring, the right-multiplication `add_monoid_hom` is bounded. -/
theorem mul_right_bound {α : Type u_1} [normed_ring α] (x : α) (y : α) : norm (coe_fn (add_monoid_hom.mul_right x) y) ≤ norm x * norm y := sorry

/-- Normed ring structure on the product of two normed rings, using the sup norm. -/
protected instance prod.normed_ring {α : Type u_1} {β : Type u_2} [normed_ring α] [normed_ring β] : normed_ring (α × β) :=
  normed_ring.mk sorry sorry

protected instance normed_ring_top_monoid {α : Type u_1} [normed_ring α] : has_continuous_mul α := sorry

/-- A normed ring is a topological ring. -/
protected instance normed_top_ring {α : Type u_1} [normed_ring α] : topological_ring α := sorry

/-- A normed field is a field with a norm satisfying ∥x y∥ = ∥x∥ ∥y∥. -/
class normed_field (α : Type u_5)
extends metric_space α, has_norm α, field α
where
  dist_eq : ∀ (x y : α), dist x y = norm (x - y)
  norm_mul' : ∀ (a b : α), norm (a * b) = norm a * norm b

/-- A nondiscrete normed field is a normed field in which there is an element of norm different from
`0` and `1`. This makes it possible to bring any element arbitrarily close to `0` by multiplication
by the powers of any element, and thus to relate algebra and topology. -/
class nondiscrete_normed_field (α : Type u_5)
extends normed_field α
where
  non_trivial : ∃ (x : α), 1 < norm x

namespace normed_field

@[simp] theorem norm_mul {α : Type u_1} [normed_field α] (a : α) (b : α) : norm (a * b) = norm a * norm b :=
  norm_mul' a b

protected instance to_normed_comm_ring {α : Type u_1} [normed_field α] : normed_comm_ring α :=
  normed_comm_ring.mk sorry

protected instance to_norm_one_class {α : Type u_1} [normed_field α] : norm_one_class α :=
  norm_one_class.mk
    (mul_left_cancel' (mt (iff.mp norm_eq_zero) one_ne_zero)
      (eq.mpr (id (Eq._oldrec (Eq.refl (norm 1 * norm 1 = norm 1 * 1)) (Eq.symm (norm_mul 1 1))))
        (eq.mpr (id (Eq._oldrec (Eq.refl (norm (1 * 1) = norm 1 * 1)) (mul_one 1)))
          (eq.mpr (id (Eq._oldrec (Eq.refl (norm 1 = norm 1 * 1)) (mul_one (norm 1)))) (Eq.refl (norm 1))))))

@[simp] theorem nnnorm_mul {α : Type u_1} [normed_field α] (a : α) (b : α) : nnnorm (a * b) = nnnorm a * nnnorm b :=
  nnreal.eq (norm_mul a b)

/-- `norm` as a `monoid_hom`. -/
@[simp] theorem norm_hom_apply {α : Type u_1} [normed_field α] : ∀ (ᾰ : α), coe_fn norm_hom ᾰ = norm ᾰ :=
  fun (ᾰ : α) => Eq.refl (coe_fn norm_hom ᾰ)

/-- `nnnorm` as a `monoid_hom`. -/
def nnnorm_hom {α : Type u_1} [normed_field α] : monoid_with_zero_hom α nnreal :=
  monoid_with_zero_hom.mk nnnorm sorry sorry nnnorm_mul

@[simp] theorem norm_pow {α : Type u_1} [normed_field α] (a : α) (n : ℕ) : norm (a ^ n) = norm a ^ n :=
  monoid_hom.map_pow (monoid_with_zero_hom.to_monoid_hom norm_hom) a

@[simp] theorem nnnorm_pow {α : Type u_1} [normed_field α] (a : α) (n : ℕ) : nnnorm (a ^ n) = nnnorm a ^ n :=
  monoid_hom.map_pow (monoid_with_zero_hom.to_monoid_hom nnnorm_hom) a n

@[simp] theorem norm_prod {α : Type u_1} {β : Type u_2} [normed_field α] (s : finset β) (f : β → α) : norm (finset.prod s fun (b : β) => f b) = finset.prod s fun (b : β) => norm (f b) :=
  monoid_hom.map_prod (monoid_with_zero_hom.to_monoid_hom norm_hom) f s

@[simp] theorem nnnorm_prod {α : Type u_1} {β : Type u_2} [normed_field α] (s : finset β) (f : β → α) : nnnorm (finset.prod s fun (b : β) => f b) = finset.prod s fun (b : β) => nnnorm (f b) :=
  monoid_hom.map_prod (monoid_with_zero_hom.to_monoid_hom nnnorm_hom) f s

@[simp] theorem norm_div {α : Type u_1} [normed_field α] (a : α) (b : α) : norm (a / b) = norm a / norm b :=
  monoid_with_zero_hom.map_div norm_hom a b

@[simp] theorem nnnorm_div {α : Type u_1} [normed_field α] (a : α) (b : α) : nnnorm (a / b) = nnnorm a / nnnorm b :=
  monoid_with_zero_hom.map_div nnnorm_hom a b

@[simp] theorem norm_inv {α : Type u_1} [normed_field α] (a : α) : norm (a⁻¹) = (norm a⁻¹) :=
  monoid_with_zero_hom.map_inv' norm_hom a

@[simp] theorem nnnorm_inv {α : Type u_1} [normed_field α] (a : α) : nnnorm (a⁻¹) = (nnnorm a⁻¹) := sorry

@[simp] theorem norm_fpow {α : Type u_1} [normed_field α] (a : α) (n : ℤ) : norm (a ^ n) = norm a ^ n :=
  monoid_with_zero_hom.map_fpow norm_hom

@[simp] theorem nnnorm_fpow {α : Type u_1} [normed_field α] (a : α) (n : ℤ) : nnnorm (a ^ n) = nnnorm a ^ n :=
  monoid_with_zero_hom.map_fpow nnnorm_hom

protected instance has_continuous_inv' {α : Type u_1} [normed_field α] : has_continuous_inv' α :=
  has_continuous_inv'.mk sorry

end normed_field

theorem normed_field.exists_one_lt_norm (α : Type u_1) [nondiscrete_normed_field α] : ∃ (x : α), 1 < norm x :=
  nondiscrete_normed_field.non_trivial

theorem normed_field.exists_norm_lt_one (α : Type u_1) [nondiscrete_normed_field α] : ∃ (x : α), 0 < norm x ∧ norm x < 1 := sorry

theorem normed_field.exists_lt_norm (α : Type u_1) [nondiscrete_normed_field α] (r : ℝ) : ∃ (x : α), r < norm x := sorry

theorem normed_field.exists_norm_lt (α : Type u_1) [nondiscrete_normed_field α] {r : ℝ} (hr : 0 < r) : ∃ (x : α), 0 < norm x ∧ norm x < r := sorry

instance normed_field.punctured_nhds_ne_bot {α : Type u_1} [nondiscrete_normed_field α] (x : α) : filter.ne_bot (nhds_within x (singleton xᶜ)) := sorry

instance normed_field.nhds_within_is_unit_ne_bot {α : Type u_1} [nondiscrete_normed_field α] : filter.ne_bot (nhds_within 0 (set_of fun (x : α) => is_unit x)) := sorry

protected instance real.normed_field : normed_field ℝ :=
  normed_field.mk sorry abs_mul

protected instance real.nondiscrete_normed_field : nondiscrete_normed_field ℝ :=
  nondiscrete_normed_field.mk sorry

namespace real

theorem norm_eq_abs (r : ℝ) : norm r = abs r :=
  rfl

theorem norm_of_nonneg {x : ℝ} (hx : 0 ≤ x) : norm x = x :=
  abs_of_nonneg hx

@[simp] theorem norm_coe_nat (n : ℕ) : norm ↑n = ↑n :=
  abs_of_nonneg (nat.cast_nonneg n)

@[simp] theorem nnnorm_coe_nat (n : ℕ) : nnnorm ↑n = ↑n := sorry

@[simp] theorem norm_two : norm (bit0 1) = bit0 1 :=
  abs_of_pos zero_lt_two

@[simp] theorem nnnorm_two : nnnorm (bit0 1) = bit0 1 := sorry

@[simp] theorem nnreal.norm_eq (x : nnreal) : norm ↑x = ↑x :=
  eq.mpr (id (Eq._oldrec (Eq.refl (norm ↑x = ↑x)) (norm_eq_abs ↑x)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (abs ↑x = ↑x)) (nnreal.abs_eq x))) (Eq.refl ↑x))

theorem nnnorm_coe_eq_self {x : nnreal} : nnnorm ↑x = x :=
  subtype.ext (norm_of_nonneg (zero_le x))

theorem nnnorm_of_nonneg {x : ℝ} (hx : 0 ≤ x) : nnnorm x = { val := x, property := hx } :=
  nnnorm_coe_eq_self

theorem ennnorm_eq_of_real {x : ℝ} (hx : 0 ≤ x) : ↑(nnnorm x) = ennreal.of_real x :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(nnnorm x) = ennreal.of_real x)) (Eq.symm (of_real_norm_eq_coe_nnnorm x))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (ennreal.of_real (norm x) = ennreal.of_real x)) (norm_of_nonneg hx)))
      (Eq.refl (ennreal.of_real x)))

end real

@[simp] theorem norm_norm {α : Type u_1} [normed_group α] (x : α) : norm (norm x) = norm x :=
  eq.mpr (id (Eq._oldrec (Eq.refl (norm (norm x) = norm x)) (real.norm_of_nonneg (norm_nonneg x)))) (Eq.refl (norm x))

@[simp] theorem nnnorm_norm {α : Type u_1} [normed_group α] (a : α) : nnnorm (norm a) = nnnorm a := sorry

protected instance int.normed_comm_ring : normed_comm_ring ℤ :=
  normed_comm_ring.mk mul_comm

theorem int.norm_cast_real (m : ℤ) : norm ↑m = norm m :=
  rfl

protected instance int.norm_one_class : norm_one_class ℤ :=
  norm_one_class.mk
    (eq.mpr
      (id
        (Eq.trans
          ((fun (a a_1 : ℝ) (e_1 : a = a_1) (ᾰ ᾰ_1 : ℝ) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2) (norm 1) 1
            (Eq.trans
              (Eq.trans (Eq.symm (int.norm_cast_real 1))
                ((fun (ᾰ ᾰ_1 : ℝ) (e_2 : ᾰ = ᾰ_1) => congr_arg norm e_2) (↑1) 1 int.cast_one))
              norm_one)
            1 1 (Eq.refl 1))
          (propext (eq_self_iff_true 1))))
      trivial)

protected instance rat.normed_field : normed_field ℚ :=
  normed_field.mk sorry sorry

protected instance rat.nondiscrete_normed_field : nondiscrete_normed_field ℚ :=
  nondiscrete_normed_field.mk sorry

@[simp] theorem rat.norm_cast_real (r : ℚ) : norm ↑r = norm r :=
  rfl

@[simp] theorem int.norm_cast_rat (m : ℤ) : norm ↑m = norm m := sorry

-- Here, we set a rather high priority for the instance `[normed_space α β] : semimodule α β`

-- to take precedence over `semiring.to_semimodule` as this leads to instance paths with better

-- unification properties.

-- see Note[vector space definition] for why we extend `semimodule`.

/-- A normed space over a normed field is a vector space endowed with a norm which satisfies the
equality `∥c • x∥ = ∥c∥ ∥x∥`. We require only `∥c • x∥ ≤ ∥c∥ ∥x∥` in the definition, then prove
`∥c • x∥ = ∥c∥ ∥x∥` in `norm_smul`. -/
class normed_space (α : Type u_5) (β : Type u_6) [normed_field α] [normed_group β]
extends semimodule α β
where
  norm_smul_le : ∀ (a : α) (b : β), norm (a • b) ≤ norm a * norm b

protected instance normed_field.to_normed_space {α : Type u_1} [normed_field α] : normed_space α α :=
  normed_space.mk sorry

theorem norm_smul {α : Type u_1} {β : Type u_2} [normed_field α] [normed_group β] [normed_space α β] (s : α) (x : β) : norm (s • x) = norm s * norm x := sorry

@[simp] theorem abs_norm_eq_norm {β : Type u_2} [normed_group β] (z : β) : abs (norm z) = norm z :=
  iff.mpr (abs_eq (norm_nonneg z)) (Or.inl rfl)

theorem dist_smul {α : Type u_1} {β : Type u_2} [normed_field α] [normed_group β] [normed_space α β] (s : α) (x : β) (y : β) : dist (s • x) (s • y) = norm s * dist x y := sorry

theorem nnnorm_smul {α : Type u_1} {β : Type u_2} [normed_field α] [normed_group β] [normed_space α β] (s : α) (x : β) : nnnorm (s • x) = nnnorm s * nnnorm x :=
  nnreal.eq (norm_smul s x)

theorem nndist_smul {α : Type u_1} {β : Type u_2} [normed_field α] [normed_group β] [normed_space α β] (s : α) (x : β) (y : β) : nndist (s • x) (s • y) = nnnorm s * nndist x y :=
  nnreal.eq (dist_smul s x y)

theorem norm_smul_of_nonneg {β : Type u_2} [normed_group β] [normed_space ℝ β] {t : ℝ} (ht : 0 ≤ t) (x : β) : norm (t • x) = t * norm x :=
  eq.mpr (id (Eq._oldrec (Eq.refl (norm (t • x) = t * norm x)) (norm_smul t x)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (norm t * norm x = t * norm x)) (real.norm_eq_abs t)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (abs t * norm x = t * norm x)) (abs_of_nonneg ht))) (Eq.refl (t * norm x))))

protected instance normed_space.topological_vector_space {α : Type u_1} [normed_field α] {E : Type u_5} [normed_group E] [normed_space α E] : topological_vector_space α E := sorry

theorem closure_ball {E : Type u_5} [normed_group E] [normed_space ℝ E] (x : E) {r : ℝ} (hr : 0 < r) : closure (metric.ball x r) = metric.closed_ball x r := sorry

theorem frontier_ball {E : Type u_5} [normed_group E] [normed_space ℝ E] (x : E) {r : ℝ} (hr : 0 < r) : frontier (metric.ball x r) = metric.sphere x r := sorry

theorem interior_closed_ball {E : Type u_5} [normed_group E] [normed_space ℝ E] (x : E) {r : ℝ} (hr : 0 < r) : interior (metric.closed_ball x r) = metric.ball x r := sorry

theorem interior_closed_ball' {E : Type u_5} [normed_group E] [normed_space ℝ E] [nontrivial E] (x : E) (r : ℝ) : interior (metric.closed_ball x r) = metric.ball x r := sorry

theorem frontier_closed_ball {E : Type u_5} [normed_group E] [normed_space ℝ E] (x : E) {r : ℝ} (hr : 0 < r) : frontier (metric.closed_ball x r) = metric.sphere x r := sorry

theorem frontier_closed_ball' {E : Type u_5} [normed_group E] [normed_space ℝ E] [nontrivial E] (x : E) (r : ℝ) : frontier (metric.closed_ball x r) = metric.sphere x r := sorry

theorem ne_neg_of_mem_sphere (α : Type u_1) [normed_field α] {E : Type u_5} [normed_group E] [normed_space α E] [char_zero α] {r : ℝ} (hr : 0 < r) (x : ↥(metric.sphere 0 r)) : x ≠ -x := sorry

theorem ne_neg_of_mem_unit_sphere (α : Type u_1) [normed_field α] {E : Type u_5} [normed_group E] [normed_space α E] [char_zero α] (x : ↥(metric.sphere 0 1)) : x ≠ -x :=
  ne_neg_of_mem_sphere α (eq.mpr (id (eq_true_intro zero_lt_one')) trivial) x

/-- If there is a scalar `c` with `∥c∥>1`, then any element can be moved by scalar multiplication to
any shell of width `∥c∥`. Also recap information on the norm of the rescaling element that shows
up in applications. -/
theorem rescale_to_shell {α : Type u_1} [normed_field α] {E : Type u_5} [normed_group E] [normed_space α E] {c : α} (hc : 1 < norm c) {ε : ℝ} (εpos : 0 < ε) {x : E} (hx : x ≠ 0) : ∃ (d : α), d ≠ 0 ∧ norm (d • x) < ε ∧ ε / norm c ≤ norm (d • x) ∧ norm d⁻¹ ≤ ε⁻¹ * norm c * norm x := sorry

/-- The product of two normed spaces is a normed space, with the sup norm. -/
protected instance prod.normed_space {α : Type u_1} [normed_field α] {E : Type u_5} {F : Type u_6} [normed_group E] [normed_space α E] [normed_group F] [normed_space α F] : normed_space α (E × F) :=
  normed_space.mk sorry

/-- The product of finitely many normed spaces is a normed space, with the sup norm. -/
protected instance pi.normed_space {α : Type u_1} {ι : Type u_4} [normed_field α] {E : ι → Type u_2} [fintype ι] [(i : ι) → normed_group (E i)] [(i : ι) → normed_space α (E i)] : normed_space α ((i : ι) → E i) :=
  normed_space.mk sorry

/-- A subspace of a normed space is also a normed space, with the restriction of the norm. -/
protected instance submodule.normed_space {𝕜 : Type u_1} [normed_field 𝕜] {E : Type u_2} [normed_group E] [normed_space 𝕜 E] (s : submodule 𝕜 E) : normed_space 𝕜 ↥s :=
  normed_space.mk sorry

/-- A normed algebra `𝕜'` over `𝕜` is an algebra endowed with a norm for which the embedding of
`𝕜` in `𝕜'` is an isometry. -/
class normed_algebra (𝕜 : Type u_5) (𝕜' : Type u_6) [normed_field 𝕜] [normed_ring 𝕜']
extends algebra 𝕜 𝕜'
where
  norm_algebra_map_eq : ∀ (x : 𝕜), norm (coe_fn (algebra_map 𝕜 𝕜') x) = norm x

@[simp] theorem norm_algebra_map_eq {𝕜 : Type u_1} (𝕜' : Type u_2) [normed_field 𝕜] [normed_ring 𝕜'] [h : normed_algebra 𝕜 𝕜'] (x : 𝕜) : norm (coe_fn (algebra_map 𝕜 𝕜') x) = norm x :=
  normed_algebra.norm_algebra_map_eq x

protected instance normed_algebra.to_normed_space (𝕜 : Type u_5) [normed_field 𝕜] (𝕜' : Type u_6) [normed_ring 𝕜'] [h : normed_algebra 𝕜 𝕜'] : normed_space 𝕜 𝕜' :=
  normed_space.mk sorry

protected instance normed_algebra.id (𝕜 : Type u_5) [normed_field 𝕜] : normed_algebra 𝕜 𝕜 :=
  normed_algebra.mk sorry

@[simp] theorem normed_algebra.norm_one (𝕜 : Type u_5) [normed_field 𝕜] {𝕜' : Type u_6} [normed_ring 𝕜'] [normed_algebra 𝕜 𝕜'] : norm 1 = 1 := sorry

theorem normed_algebra.norm_one_class (𝕜 : Type u_5) [normed_field 𝕜] {𝕜' : Type u_6} [normed_ring 𝕜'] [normed_algebra 𝕜 𝕜'] : norm_one_class 𝕜' :=
  norm_one_class.mk (normed_algebra.norm_one 𝕜)

theorem normed_algebra.zero_ne_one (𝕜 : Type u_5) [normed_field 𝕜] {𝕜' : Type u_6} [normed_ring 𝕜'] [normed_algebra 𝕜 𝕜'] : 0 ≠ 1 := sorry

theorem normed_algebra.nontrivial (𝕜 : Type u_5) [normed_field 𝕜] {𝕜' : Type u_6} [normed_ring 𝕜'] [normed_algebra 𝕜 𝕜'] : nontrivial 𝕜' :=
  nontrivial.mk (Exists.intro 0 (Exists.intro 1 (normed_algebra.zero_ne_one 𝕜)))

/-- Warning: This declaration should be used judiciously.
Please consider using `is_scalar_tower` instead.

`𝕜`-normed space structure induced by a `𝕜'`-normed space structure when `𝕜'` is a
normed algebra over `𝕜`. Not registered as an instance as `𝕜'` can not be inferred.

The type synonym `semimodule.restrict_scalars 𝕜 𝕜' E` will be endowed with this instance by default.
-/
def normed_space.restrict_scalars (𝕜 : Type u_5) (𝕜' : Type u_6) [normed_field 𝕜] [normed_field 𝕜'] [normed_algebra 𝕜 𝕜'] (E : Type u_7) [normed_group E] [normed_space 𝕜' E] : normed_space 𝕜 E :=
  normed_space.mk sorry

protected instance restrict_scalars.normed_group {𝕜 : Type u_1} {𝕜' : Type u_2} {E : Type u_3} [I : normed_group E] : normed_group (restrict_scalars 𝕜 𝕜' E) :=
  I

protected instance semimodule.restrict_scalars.normed_space_orig {𝕜 : Type u_1} {𝕜' : Type u_2} {E : Type u_3} [normed_field 𝕜'] [normed_group E] [I : normed_space 𝕜' E] : normed_space 𝕜' (restrict_scalars 𝕜 𝕜' E) :=
  I

protected instance restrict_scalars.normed_space (𝕜 : Type u_5) (𝕜' : Type u_6) [normed_field 𝕜] [normed_field 𝕜'] [normed_algebra 𝕜 𝕜'] (E : Type u_7) [normed_group E] [normed_space 𝕜' E] : normed_space 𝕜 (restrict_scalars 𝕜 𝕜' E) :=
  normed_space.restrict_scalars 𝕜 𝕜' E

theorem cauchy_seq_finset_iff_vanishing_norm {α : Type u_1} {ι : Type u_4} [normed_group α] {f : ι → α} : (cauchy_seq fun (s : finset ι) => finset.sum s fun (i : ι) => f i) ↔
  ∀ (ε : ℝ), ε > 0 → ∃ (s : finset ι), ∀ (t : finset ι), disjoint t s → norm (finset.sum t fun (i : ι) => f i) < ε := sorry

theorem summable_iff_vanishing_norm {α : Type u_1} {ι : Type u_4} [normed_group α] [complete_space α] {f : ι → α} : summable f ↔
  ∀ (ε : ℝ), ε > 0 → ∃ (s : finset ι), ∀ (t : finset ι), disjoint t s → norm (finset.sum t fun (i : ι) => f i) < ε := sorry

theorem cauchy_seq_finset_of_norm_bounded {α : Type u_1} {ι : Type u_4} [normed_group α] {f : ι → α} (g : ι → ℝ) (hg : summable g) (h : ∀ (i : ι), norm (f i) ≤ g i) : cauchy_seq fun (s : finset ι) => finset.sum s fun (i : ι) => f i := sorry

theorem cauchy_seq_finset_of_summable_norm {α : Type u_1} {ι : Type u_4} [normed_group α] {f : ι → α} (hf : summable fun (a : ι) => norm (f a)) : cauchy_seq fun (s : finset ι) => finset.sum s fun (a : ι) => f a :=
  cauchy_seq_finset_of_norm_bounded (fun (a : ι) => norm (f a)) hf fun (i : ι) => le_refl (norm (f i))

/-- If a function `f` is summable in norm, and along some sequence of finsets exhausting the space
its sum is converging to a limit `a`, then this holds along all finsets, i.e., `f` is summable
with sum `a`. -/
theorem has_sum_of_subseq_of_summable {α : Type u_1} {γ : Type u_3} {ι : Type u_4} [normed_group α] {f : ι → α} (hf : summable fun (a : ι) => norm (f a)) {s : γ → finset ι} {p : filter γ} [filter.ne_bot p] (hs : filter.tendsto s p filter.at_top) {a : α} (ha : filter.tendsto (fun (b : γ) => finset.sum (s b) fun (i : ι) => f i) p (nhds a)) : has_sum f a :=
  tendsto_nhds_of_cauchy_seq_of_subseq (cauchy_seq_finset_of_summable_norm hf) hs ha

theorem has_sum_iff_tendsto_nat_of_summable_norm {α : Type u_1} [normed_group α] {f : ℕ → α} {a : α} (hf : summable fun (i : ℕ) => norm (f i)) : has_sum f a ↔ filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top (nhds a) := sorry

/-- The direct comparison test for series:  if the norm of `f` is bounded by a real function `g`
which is summable, then `f` is summable. -/
theorem summable_of_norm_bounded {α : Type u_1} {ι : Type u_4} [normed_group α] [complete_space α] {f : ι → α} (g : ι → ℝ) (hg : summable g) (h : ∀ (i : ι), norm (f i) ≤ g i) : summable f :=
  eq.mpr (id (Eq._oldrec (Eq.refl (summable f)) (propext summable_iff_cauchy_seq_finset)))
    (cauchy_seq_finset_of_norm_bounded g hg h)

theorem has_sum.norm_le_of_bounded {α : Type u_1} {ι : Type u_4} [normed_group α] {f : ι → α} {g : ι → ℝ} {a : α} {b : ℝ} (hf : has_sum f a) (hg : has_sum g b) (h : ∀ (i : ι), norm (f i) ≤ g i) : norm a ≤ b :=
  le_of_tendsto_of_tendsto' (filter.tendsto.norm hf) hg
    fun (s : finset ι) => norm_sum_le_of_le s fun (i : ι) (hi : i ∈ s) => h i

/-- Quantitative result associated to the direct comparison test for series:  If `∑' i, g i` is
summable, and for all `i`, `∥f i∥ ≤ g i`, then `∥∑' i, f i∥ ≤ ∑' i, g i`. Note that we do not
assume that `∑' i, f i` is summable, and it might not be the case if `α` is not a complete space. -/
theorem tsum_of_norm_bounded {α : Type u_1} {ι : Type u_4} [normed_group α] {f : ι → α} {g : ι → ℝ} {a : ℝ} (hg : has_sum g a) (h : ∀ (i : ι), norm (f i) ≤ g i) : norm (tsum fun (i : ι) => f i) ≤ a := sorry

/-- If `∑' i, ∥f i∥` is summable, then `∥∑' i, f i∥ ≤ (∑' i, ∥f i∥)`. Note that we do not assume
that `∑' i, f i` is summable, and it might not be the case if `α` is not a complete space. -/
theorem norm_tsum_le_tsum_norm {α : Type u_1} {ι : Type u_4} [normed_group α] {f : ι → α} (hf : summable fun (i : ι) => norm (f i)) : norm (tsum fun (i : ι) => f i) ≤ tsum fun (i : ι) => norm (f i) :=
  tsum_of_norm_bounded (summable.has_sum hf) fun (i : ι) => le_rfl

/-- Variant of the direct comparison test for series:  if the norm of `f` is eventually bounded by a
real function `g` which is summable, then `f` is summable. -/
theorem summable_of_norm_bounded_eventually {α : Type u_1} {ι : Type u_4} [normed_group α] [complete_space α] {f : ι → α} (g : ι → ℝ) (hg : summable g) (h : filter.eventually (fun (i : ι) => norm (f i) ≤ g i) filter.cofinite) : summable f := sorry

theorem summable_of_nnnorm_bounded {α : Type u_1} {ι : Type u_4} [normed_group α] [complete_space α] {f : ι → α} (g : ι → nnreal) (hg : summable g) (h : ∀ (i : ι), nnnorm (f i) ≤ g i) : summable f :=
  summable_of_norm_bounded (fun (i : ι) => ↑(g i)) (iff.mpr nnreal.summable_coe hg) fun (i : ι) => h i

theorem summable_of_summable_norm {α : Type u_1} {ι : Type u_4} [normed_group α] [complete_space α] {f : ι → α} (hf : summable fun (a : ι) => norm (f a)) : summable f :=
  summable_of_norm_bounded (fun (a : ι) => norm (f a)) hf fun (i : ι) => le_refl (norm (f i))

theorem summable_of_summable_nnnorm {α : Type u_1} {ι : Type u_4} [normed_group α] [complete_space α] {f : ι → α} (hf : summable fun (a : ι) => nnnorm (f a)) : summable f :=
  summable_of_nnnorm_bounded (fun (a : ι) => nnnorm (f a)) hf fun (i : ι) => le_refl (nnnorm (f i))


/-
Copyright (c) 2019 Seul Baek. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Seul Baek

! This file was ported from Lean 3 source module tactic.omega.int.dnf
! leanprover-community/mathlib commit 402f8982dddc1864bd703da2d6e2ee304a866973
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.List.ProdSigma
import Mathbin.Tactic.Omega.Clause
import Mathbin.Tactic.Omega.Int.Form

/-!
# DNF transformation
-/

namespace Omega

namespace Int

open Omega.Int

/-- push_neg p returns the result of normalizing ¬ p by
    pushing the outermost negation all the way down,
    until it reaches either a negation or an atom -/
@[simp]
def pushNeg : Preform → Preform
  | p ∨* q => push_neg p ∧* push_neg q
  | p ∧* q => push_neg p ∨* push_neg q
  | ¬* p => p
  | p => ¬* p
#align omega.int.push_neg Omega.Int.pushNeg

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic omega.int.preform.induce -/
/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
theorem pushNeg_equiv : ∀ {p : Preform}, Preform.Equiv (pushNeg p) (¬* p) :=
  by
  run_tac
    preform.induce sorry
  · simp only [Classical.not_not, push_neg, preform.holds]
  · simp only [preform.holds, push_neg, not_or, ihp v, ihq v]
  · simp only [preform.holds, push_neg, not_and_or, ihp v, ihq v]
#align omega.int.push_neg_equiv Omega.Int.pushNeg_equiv

/-- NNF transformation -/
def nnf : Preform → Preform
  | ¬* p => pushNeg (nnf p)
  | p ∨* q => nnf p ∨* nnf q
  | p ∧* q => nnf p ∧* nnf q
  | a => a
#align omega.int.nnf Omega.Int.nnf

def IsNnf : Preform → Prop
  | t =* s => True
  | t ≤* s => True
  | ¬* t =* s => True
  | ¬* t ≤* s => True
  | p ∨* q => is_nnf p ∧ is_nnf q
  | p ∧* q => is_nnf p ∧ is_nnf q
  | _ => False
#align omega.int.is_nnf Omega.Int.IsNnf

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic omega.int.preform.induce -/
/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
theorem isNnf_pushNeg : ∀ p : Preform, IsNnf p → IsNnf (pushNeg p) :=
  by
  run_tac
    preform.induce sorry
  · cases p <;> try cases h1 <;> trivial
  · cases h1
    constructor <;> [· apply ihp, · apply ihq] <;> assumption
  · cases h1
    constructor <;> [· apply ihp, · apply ihq] <;> assumption
#align omega.int.is_nnf_push_neg Omega.Int.isNnf_pushNeg

/-- Argument is free of negations -/
def NegFree : Preform → Prop
  | t =* s => True
  | t ≤* s => True
  | p ∨* q => neg_free p ∧ neg_free q
  | p ∧* q => neg_free p ∧ neg_free q
  | _ => False
#align omega.int.neg_free Omega.Int.NegFree

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic omega.int.preform.induce -/
/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
theorem isNnf_nnf : ∀ p : Preform, IsNnf (nnf p) :=
  by
  run_tac
    preform.induce sorry
  · apply is_nnf_push_neg _ ih
  · constructor <;> assumption
  · constructor <;> assumption
#align omega.int.is_nnf_nnf Omega.Int.isNnf_nnf

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic omega.int.preform.induce -/
/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
theorem nnf_equiv : ∀ {p : Preform}, Preform.Equiv (nnf p) p :=
  by
  run_tac
    preform.induce sorry
  · rw [push_neg_equiv]
    apply not_congr
    apply ih
  · apply pred_mono_2' (ihp v) (ihq v)
  · apply pred_mono_2' (ihp v) (ihq v)
#align omega.int.nnf_equiv Omega.Int.nnf_equiv

/-- Eliminate all negations from preform -/
@[simp]
def negElim : Preform → Preform
  | ¬* t =* s => (t.add_one ≤* s) ∨* s.add_one ≤* t
  | ¬* t ≤* s => s.add_one ≤* t
  | p ∨* q => neg_elim p ∨* neg_elim q
  | p ∧* q => neg_elim p ∧* neg_elim q
  | p => p
#align omega.int.neg_elim Omega.Int.negElim

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic omega.int.preform.induce -/
/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
theorem negFree_negElim : ∀ p : Preform, IsNnf p → NegFree (negElim p) :=
  by
  run_tac
    preform.induce sorry
  · cases p <;> try cases h1 <;> try trivial
    constructor <;> trivial
  · cases h1
    constructor <;> [· apply ihp, · apply ihq] <;> assumption
  · cases h1
    constructor <;> [· apply ihp, · apply ihq] <;> assumption
#align omega.int.neg_free_neg_elim Omega.Int.negFree_negElim

theorem le_and_le_iff_eq {α : Type} [PartialOrder α] {a b : α} : a ≤ b ∧ b ≤ a ↔ a = b :=
  by
  constructor <;> intro h1
  · cases h1
    apply le_antisymm <;> assumption
  · constructor <;> apply le_of_eq <;> rw [h1]
#align omega.int.le_and_le_iff_eq Omega.Int.le_and_le_iff_eq

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic omega.int.preform.induce -/
/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
theorem implies_negElim : ∀ {p : Preform}, Preform.Implies p (negElim p) :=
  by
  run_tac
    preform.induce sorry
  · cases' p with t s t s <;> try apply h
    · simp only [le_and_le_iff_eq.symm, not_and_or, not_le, preterm.val, preform.holds] at h
      simp only [Int.add_one_le_iff, preterm.add_one, preterm.val, preform.holds, neg_elim]
      rw [or_comm']
      assumption
    · simp only [not_le, Int.add_one_le_iff, preterm.add_one, not_le, preterm.val, preform.holds,
        neg_elim] at *
      assumption
  · simp only [neg_elim]
    cases h <;>
        [·
          left
          apply ihp,
        · right
          apply ihq] <;>
      assumption
  · apply And.imp (ihp _) (ihq _) h
#align omega.int.implies_neg_elim Omega.Int.implies_negElim

@[simp]
def dnfCore : Preform → List Clause
  | p ∨* q => dnf_core p ++ dnf_core q
  | p ∧* q => (List.product (dnf_core p) (dnf_core q)).map fun pq => Clause.append pq.fst pq.snd
  | t =* s => [([Term.sub (canonize s) (canonize t)], [])]
  | t ≤* s => [([], [Term.sub (canonize s) (canonize t)])]
  | ¬* _ => []
#align omega.int.dnf_core Omega.Int.dnfCore

/-- DNF transformation -/
def dnf (p : Preform) : List Clause :=
  dnfCore <| negElim <| nnf p
#align omega.int.dnf Omega.Int.dnf

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:69:18: unsupported non-interactive tactic omega.int.preform.induce -/
/- ./././Mathport/Syntax/Translate/Expr.lean:330:4: warning: unsupported (TODO): `[tacs] -/
theorem exists_clause_holds {v : Nat → Int} :
    ∀ {p : Preform}, NegFree p → p.Holds v → ∃ c ∈ dnfCore p, Clause.Holds v c :=
  by
  run_tac
    preform.induce sorry
  · apply List.exists_mem_cons_of
    constructor
    · simp only [preterm.val, preform.holds] at h2
      rw [List.forall_mem_singleton]
      simp only [h2, Omega.Int.val_canonize, Omega.Term.val_sub, sub_self]
    · apply List.forall_mem_nil
  · apply List.exists_mem_cons_of
    constructor
    · apply List.forall_mem_nil
    · simp only [preterm.val, preform.holds] at h2
      rw [List.forall_mem_singleton]
      simp only [val_canonize, preterm.val, term.val_sub]
      rw [le_sub_comm, sub_zero]
      assumption
  · cases h1
  ·
    cases' h2 with h2 h2 <;> [· cases' ihp h1.left h2 with c h3,
              · cases' ihq h1.right h2 with c h3] <;>
            cases' h3 with h3 h4 <;>
          refine' ⟨c, list.mem_append.elim_right _, h4⟩ <;>
        [left, right] <;>
      assumption
  · rcases ihp h1.left h2.left with ⟨cp, hp1, hp2⟩
    rcases ihq h1.right h2.right with ⟨cq, hq1, hq2⟩
    refine' ⟨clause.append cp cq, ⟨_, clause.holds_append hp2 hq2⟩⟩
    simp only [dnf_core, List.mem_map]
    refine' ⟨(cp, cq), ⟨_, rfl⟩⟩
    rw [List.mem_product]
    constructor <;> assumption
#align omega.int.exists_clause_holds Omega.Int.exists_clause_holds

theorem clauses_sat_dnfCore {p : Preform} : NegFree p → p.Sat → Clauses.Sat (dnfCore p) :=
  by
  intro h1 h2; cases' h2 with v h2
  rcases exists_clause_holds h1 h2 with ⟨c, h3, h4⟩
  refine' ⟨c, h3, v, h4⟩
#align omega.int.clauses_sat_dnf_core Omega.Int.clauses_sat_dnfCore

theorem unsat_of_clauses_unsat {p : Preform} : Clauses.Unsat (dnf p) → p.Unsat :=
  by
  intro h1 h2; apply h1
  apply clauses_sat_dnf_core
  apply neg_free_neg_elim _ (is_nnf_nnf _)
  apply preform.sat_of_implies_of_sat implies_neg_elim
  have hrw := exists_congr (@nnf_equiv p)
  apply hrw.elim_right h2
#align omega.int.unsat_of_clauses_unsat Omega.Int.unsat_of_clauses_unsat

end Int

end Omega


/-
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad, Floris van Doorn
-/

import Mathlib.Tactic.Basic
import Mathlib.Tactic.Ext

-- Workaround for not being able to add ext lemmas from other modules.
@[ext] private def funext' := @funext
@[ext] private def propext' := @propext

@[ext] protected lemma Unit.ext (x y : Unit) : x = y := rfl
@[ext] protected lemma PUnit.ext (x y : Unit) : x = y := rfl

instance {f : α → β} [DecidablePred p] : DecidablePred (p ∘ f) :=
  inferInstanceAs <| DecidablePred fun x => p (f x)

theorem opt_param_eq (α : Sort u) (default : α) : optParam α default = α := optParam_eq α default

theorem Not.intro {a : Prop} (h : a → False) : ¬ a := h

/- not -/

def non_contradictory (a : Prop) : Prop := ¬¬a

theorem non_contradictory_intro {a : Prop} (ha : a) : ¬¬a :=
λ hna : ¬a => absurd ha hna

/-- Ex falso for negation. From `¬ a` and `a` anything follows. This is the same as `absurd` with
the arguments flipped, but it is in the `not` namespace so that projection notation can be used. -/
def Not.elim {α : Sort _} (H1 : ¬a) (H2 : a) : α := absurd H2 H1

@[reducible] theorem Not.imp {a b : Prop} (H2 : ¬b) (H1 : a → b) : ¬a := mt H1 H2

/- eq -/

-- proof irrelevance is built in
def proof_irrel := @proofIrrel

def congr_fun := @congrFun
def congr_arg := @congrArg

lemma trans_rel_left {α : Sort u} {a b c : α} (r : α → α → Prop) (h₁ : r a b) (h₂ : b = c) : r a c :=
h₂ ▸ h₁

lemma trans_rel_right {α : Sort u} {a b c : α} (r : α → α → Prop) (h₁ : a = b) (h₂ : r b c) : r a c :=
h₁.symm ▸ h₂

lemma not_of_eq_false {p : Prop} (h : p = False) : ¬p := fun hp => h ▸ hp

lemma cast_proof_irrel (h₁ h₂ : α = β) (a : α) : cast h₁ a = cast h₂ a := rfl

/- ne -/

lemma Ne.def {α : Sort u} (a b : α) : (a ≠ b) = ¬ (a = b) := rfl

def eq_rec_heq := @eqRec_heq

lemma heq_of_eq_rec_left {φ : α → Sort v} {a a' : α} {p₁ : φ a} {p₂ : φ a'} :
  (e : a = a') → (h₂ : Eq.rec (motive := fun a _ => φ a) p₁ e = p₂) → HEq p₁ p₂
| rfl, rfl => HEq.rfl

lemma heq_of_eq_rec_right {φ : α → Sort v} {a a' : α} {p₁ : φ a} {p₂ : φ a'} :
  (e : a' = a) → (h₂ : p₁ = Eq.rec (motive := fun a _ => φ a) p₂ e) → HEq p₁ p₂
| rfl, rfl => HEq.rfl

lemma of_heq_true (h : HEq a True) : a := of_eq_true (eq_of_heq h)

-- TODO eq_rec_compose

/- and -/

variable {a b c d : Prop}

def And.elim (f : a → b → α) (h : a ∧ b) : α := f h.1 h.2

lemma and.swap : a ∧ b → b ∧ a :=
λ ⟨ha, hb⟩ => ⟨hb, ha⟩

lemma And.symm : a ∧ b → b ∧ a | ⟨ha, hb⟩ => ⟨hb, ha⟩

/- or -/

lemma non_contradictory_em (a : Prop) : ¬¬(a ∨ ¬a) :=
λ not_em : ¬(a ∨ ¬a) =>
  have neg_a : ¬a := λ pos_a : a => absurd (Or.inl pos_a) not_em
  absurd (Or.inr neg_a) not_em

lemma Or.swap : a ∨ b → b ∨ a := Or.rec Or.inr Or.inl

lemma Or.symm : a ∨ b → b ∨ a
| Or.inl h => Or.inr h
| Or.inr h => Or.inl h

/- xor -/

def xor (a b : Prop) := (a ∧ ¬ b) ∨ (b ∧ ¬ a)

/- iff -/

def Iff.elim (f : (a → b) → (b → a) → c) (h : a ↔ b) : c := f h.1 h.2

def Iff.elim_left : (a ↔ b) → a → b := Iff.mp

def Iff.elim_right : (a ↔ b) → b → a := Iff.mpr

lemma Eq.to_iff : a = b → (a ↔ b) | rfl => Iff.rfl

lemma neq_of_not_iff : ¬(a ↔ b) → a ≠ b := mt Eq.to_iff

lemma not_iff_not_of_iff (h₁ : a ↔ b) : ¬ a ↔ ¬ b :=
Iff.intro
  (λ (hna : ¬ a) (hb : b) => hna (Iff.elim_right h₁ hb))
  (λ (hnb : ¬ b) (ha : a) => hnb (Iff.elim_left h₁ ha))

lemma of_iff_true (h : a ↔ True) : a := h.2 ⟨⟩

lemma not_of_iff_false : (a ↔ False) → ¬a := Iff.mp

lemma iff_true_intro (h : a) : a ↔ True := ⟨fun _ => ⟨⟩, fun _ => h⟩

lemma iff_false_intro (h : ¬a) : a ↔ False := ⟨h, fun h => h.elim⟩

lemma not_iff_false_intro (h : a) : ¬a ↔ False := iff_false_intro (not_not_intro h)

lemma not_non_contradictory_iff_absurd (a : Prop) : ¬¬¬a ↔ ¬a := ⟨mt not_not_intro, not_not_intro⟩

lemma imp_congr_left (h : a ↔ b) : (a → c) ↔ (b → c) :=
⟨fun hac ha => hac (h.2 ha), fun hbc ha => hbc (h.1 ha)⟩

lemma imp_congr_right (h : a → (b ↔ c)) : (a → b) ↔ (a → c) :=
⟨fun hab ha => (h ha).1 (hab ha), fun hcd ha => (h ha).2 (hcd ha)⟩

lemma imp_congr_ctx (h₁ : a ↔ c) (h₂ : c → (b ↔ d)) : (a → b) ↔ (c → d) :=
(imp_congr_left h₁).trans (imp_congr_right h₂)

lemma imp_congr (h₁ : a ↔ c) (h₂ : b ↔ d) : (a → b) ↔ (c → d) := imp_congr_ctx h₁ fun _ => h₂

lemma not_of_not_not_not (h : ¬¬¬a) : ¬a :=
λ ha => absurd (not_not_intro ha) h

-- @[simp] -- Lean 4 has this built-in because it simplifies using decidable instances
lemma not_true : (¬ True) ↔ False :=
iff_false_intro (not_not_intro trivial)

-- @[simp] -- Lean 4 has this built-in because it simplifies using decidable instances
lemma not_false_iff : (¬ False) ↔ True :=
iff_true_intro not_false

lemma not_congr (h : a ↔ b) : ¬a ↔ ¬b := ⟨mt h.2, mt h.1⟩

lemma ne_self_iff_false (a : α) : a ≠ a ↔ False := not_iff_false_intro rfl

lemma eq_self_iff_true (a : α) : a = a ↔ True := iff_true_intro rfl

lemma heq_self_iff_true (a : α) : HEq a a ↔ True := iff_true_intro HEq.rfl

lemma iff_not_self : ¬(a ↔ ¬a) | H => let f h := H.1 h h; f (H.2 f)

@[simp] lemma not_iff_self : ¬(¬a ↔ a) | H => iff_not_self H.symm

lemma true_iff_false : (True ↔ False) ↔ False :=
iff_false_intro (λ h => Iff.mp h trivial)

lemma false_iff_true : (False ↔ True) ↔ False :=
iff_false_intro (λ h => Iff.mpr h trivial)

lemma false_of_true_iff_false : (True ↔ False) → False :=
λ h => Iff.mp h trivial

lemma false_of_true_eq_false : (True = False) → False :=
λ h => h ▸ trivial

lemma true_eq_false_of_false : False → (True = False) :=
False.elim

lemma eq_comm {a b : α} : a = b ↔ b = a := ⟨Eq.symm, Eq.symm⟩

/- and simp rules -/
lemma And.imp (f : a → c) (g : b → d) (h : a ∧ b) : c ∧ d := ⟨f h.1, g h.2⟩

lemma and_implies (hac : a → c) (hbd : b → d) : a ∧ b → c ∧ d := And.imp hac hbd

lemma and_congr (h₁ : a ↔ c) (h₂ : b ↔ d) : a ∧ b ↔ c ∧ d := ⟨And.imp h₁.1 h₂.1, And.imp h₁.2 h₂.2⟩

lemma and_congr_right (h : a → (b ↔ c)) : (a ∧ b) ↔ (a ∧ c) :=
⟨fun ⟨ha, hb⟩ => ⟨ha, (h ha).1 hb⟩, fun ⟨ha, hb⟩ => ⟨ha, (h ha).2 hb⟩⟩

lemma And.comm : a ∧ b ↔ b ∧ a := ⟨And.symm, And.symm⟩

lemma and_comm (a b : Prop) : a ∧ b ↔ b ∧ a := And.comm

lemma And.assoc : (a ∧ b) ∧ c ↔ a ∧ (b ∧ c) :=
⟨fun ⟨⟨ha, hb⟩, hc⟩ => ⟨ha, hb, hc⟩, fun ⟨ha, hb, hc⟩ => ⟨⟨ha, hb⟩, hc⟩⟩

lemma and_assoc (a b : Prop) : (a ∧ b) ∧ c ↔ a ∧ (b ∧ c) := And.assoc

lemma And.left_comm : a ∧ (b ∧ c) ↔ b ∧ (a ∧ c) :=
by rw [← and_assoc, ← and_assoc, @And.comm a b]

lemma and_iff_left (hb : b) : a ∧ b ↔ a := ⟨And.left, fun ha => ⟨ha, hb⟩⟩

lemma and_iff_right (ha : a) : a ∧ b ↔ b := ⟨And.right, fun hb => ⟨ha, hb⟩⟩

@[simp] lemma and_not_self : ¬(a ∧ ¬a) | ⟨ha, hn⟩ => hn ha

@[simp] lemma not_and_self : ¬(¬a ∧ a) | ⟨hn, ha⟩ => hn ha

/- or simp rules -/

lemma Or.imp (f : a → c) (g : b → d) (h : a ∨ b) : c ∨ d := h.elim (inl ∘ f) (inr ∘ g)

lemma Or.imp_left (f : a → b) : a ∨ c → b ∨ c := Or.imp f id

lemma Or.imp_right (f : b → c) : a ∨ b → a ∨ c := Or.imp id f

lemma or_congr (h₁ : a ↔ c) (h₂ : b ↔ d) : (a ∨ b) ↔ (c ∨ d) :=
⟨Or.imp h₁.1 h₂.1, Or.imp h₁.2 h₂.2⟩

lemma Or.comm : a ∨ b ↔ b ∨ a := ⟨Or.symm, Or.symm⟩

lemma or_comm (a b : Prop) : a ∨ b ↔ b ∨ a := Or.comm

lemma Or.assoc : (a ∨ b) ∨ c ↔ a ∨ (b ∨ c) :=
Iff.intro
  (Or.rec (Or.imp_right Or.inl) (λ h => Or.inr (Or.inr h)))
  (Or.rec (λ h => Or.inl (Or.inl h)) (Or.imp_left Or.inr))

lemma or_assoc (a b : Prop) : (a ∨ b) ∨ c ↔ a ∨ (b ∨ c) :=
Or.assoc

lemma Or.left_comm : a ∨ (b ∨ c) ↔ b ∨ (a ∨ c) :=
Iff.trans (Iff.symm Or.assoc) (Iff.trans (or_congr Or.comm (Iff.refl c)) Or.assoc)

theorem or_iff_right_of_imp (ha : a → b) : (a ∨ b) ↔ b :=
Iff.intro (Or.rec ha id) Or.inr

theorem or_iff_left_of_imp (hb : b → a) : (a ∨ b) ↔ a :=
Iff.intro (Or.rec id hb) Or.inl

-- Port note: in mathlib3, this is not_or
lemma not_or_intro {a b : Prop} : ¬ a → ¬ b → ¬ (a ∨ b)
| hna, hnb, (Or.inl ha) => absurd ha hna
| hna, hnb, (Or.inr hb) => absurd hb hnb

lemma not_or (p q) : ¬ (p ∨ q) ↔ ¬ p ∧ ¬ q :=
⟨fun H => ⟨mt Or.inl H, mt Or.inr H⟩, fun ⟨hp, hq⟩ pq => pq.elim hp hq⟩

/- or resolution rules -/

lemma Or.resolve_left {a b : Prop} (h: a ∨ b) (na : ¬ a) : b :=
Or.elim h (λ ha => absurd ha na) id

lemma Or.neg_resolve_left (h : ¬a ∨ b) (ha : a) : b := h.elim (absurd ha) id

lemma Or.resolve_right {a b : Prop} (h: a ∨ b) (nb : ¬ b) : a :=
Or.elim h id (λ hb => absurd hb nb)

lemma Or.neg_resolve_right (h : a ∨ ¬b) (nb : b) : a := h.elim id (absurd nb)

lemma iff_congr (h₁ : a ↔ c) (h₂ : b ↔ d) : (a ↔ b) ↔ (c ↔ d) :=
⟨fun h => h₁.symm.trans $ h.trans h₂, fun h => h₁.trans $ h.trans h₂.symm⟩

/- implies simp rule -/
-- This is not marked `@[simp]` because we have `implies_true : (α → True) = True` in core.
lemma implies_true_iff (α : Sort u) : (α → True) ↔ True :=
Iff.intro (λ h => trivial) (λ ha h => trivial)

lemma false_implies_iff (a : Prop) : (False → a) ↔ True :=
Iff.intro (λ h => trivial) (λ ha h => False.elim h)

theorem true_implies_iff (α : Prop) : (True → α) ↔ α :=
Iff.intro (λ h => h trivial) (λ h h' => h)

/- exists unique -/

def ExistsUnique (p : α → Prop) := ∃ x, p x ∧ ∀ y, p y → y = x

open Lean in
macro "∃! " xs:explicitBinders ", " b:term : term => expandExplicitBinders ``ExistsUnique xs b

lemma ExistsUnique.intro {p : α → Prop} (w : α)
  (h₁ : p w) (h₂ : ∀ y, p y → y = w) : ∃! x, p x := ⟨w, h₁, h₂⟩

lemma ExistsUnique.elim {α : Sort u} {p : α → Prop} {b : Prop}
    (h₂ : ∃! x, p x) (h₁ : ∀ x, p x → (∀ y, p y → y = x) → b) : b :=
Exists.elim h₂ (λ w hw => h₁ w (And.left hw) (And.right hw))

lemma exists_unique_of_exists_of_unique {α : Sort u} {p : α → Prop}
    (hex : ∃ x, p x) (hunique : ∀ y₁ y₂, p y₁ → p y₂ → y₁ = y₂) : ∃! x, p x :=
Exists.elim hex (λ x px => ExistsUnique.intro x px (λ y (h : p y) => hunique y x h px))

lemma exists_of_exists_unique {α : Sort u} {p : α → Prop} (h : ∃! x, p x) : ∃ x, p x :=
Exists.elim h (λ x hx => ⟨x, And.left hx⟩)

lemma unique_of_exists_unique {α : Sort u} {p : α → Prop}
    (h : ∃! x, p x) {y₁ y₂ : α} (py₁ : p y₁) (py₂ : p y₂) : y₁ = y₂ :=
let ⟨x, hx, hy⟩ := h; (hy _ py₁).trans (hy _ py₂).symm

/- exists, forall, exists unique congruences -/

-- Port note: this is `forall_congr` from Lean 3. In Lean 4, there is already something
-- with that name and a slightly different type.
lemma forall_congr' {p q : α → Prop} (h : ∀ a, p a ↔ q a) : (∀ a, p a) ↔ ∀ a, q a :=
⟨fun H a => (h a).1 (H a), fun H a => (h a).2 (H a)⟩

lemma exists_imp_exists {α : Sort u} {p q : α → Prop}
  (h : ∀ a, (p a → q a)) (p : ∃ a, p a) : ∃ a, q a :=
Exists.elim p (λ a hp => ⟨a, h a hp⟩)

lemma Exists.imp {α : Sort u} {p q : α → Prop}
  (h : ∀ a, (p a → q a)) (p : ∃ a, p a) : ∃ a, q a := exists_imp_exists h p

lemma exists_congr {p q : α → Prop} (h : ∀ a, p a ↔ q a) : (∃ a, p a) ↔ ∃ a, q a :=
⟨exists_imp_exists fun x => (h x).1, exists_imp_exists fun x => (h x).2⟩

lemma exists_unique_congr {p q : α → Prop} (h : ∀ a, p a ↔ q a) : (∃! a, p a) ↔ ∃! a, q a :=
exists_congr fun x => and_congr (h _) $ forall_congr' fun y => imp_congr_left (h _)

lemma forall_not_of_not_exists {p : α → Prop} (hne : ¬∃ x, p x) (x) : ¬p x | hp => hne ⟨x, hp⟩

/- decidable -/

namespace Decidable
  variable {p q : Prop}

  -- TODO: rec_on_true and rec_on_false

  def by_cases {q : Sort u} [φ : Decidable p] : (p → q) → (¬p → q) → q := byCases

  lemma by_contradiction [φ : Decidable p] (h : ¬ p → False) : p := @byContradiction p φ h

  lemma not_not_iff (p) [Decidable p] : (¬ ¬ p) ↔ p :=
  Iff.intro of_not_not not_not_intro

  lemma not_or_iff_and_not (p q) [d₁ : Decidable p] [d₂ : Decidable q] : ¬ (p ∨ q) ↔ ¬ p ∧ ¬ q :=
  Iff.intro
    (λ h => match d₁ with
          | isTrue h₁  => False.elim $ h (Or.inl h₁)
          | isFalse h₁ =>
            match d₂ with
            | isTrue h₂  => False.elim $ h (Or.inr h₂)
            | isFalse h₂ => ⟨h₁, h₂⟩)
    (λ ⟨np, nq⟩ h => Or.elim h np nq)

end Decidable
section
  variable {p q : Prop}
  protected def Or.by_cases [Decidable p] [Decidable q] {α : Sort u}
                                   (h : p ∨ q) (h₁ : p → α) (h₂ : q → α) : α :=
  if hp : p then h₁ hp else
    if hq : q then h₂ hq else
      False.elim (Or.elim h hp hq)
end

section
  variable {p q : Prop}

  instance [Decidable p] [Decidable q] : Decidable (xor p q) :=
  if hp : p then
    if hq : q then isFalse (Or.rec (λ ⟨_, h⟩ => h hq : ¬(p ∧ ¬ q)) (λ ⟨_, h⟩ => h hp : ¬(q ∧ ¬ p)))
    else isTrue $ Or.inl ⟨hp, hq⟩
  else
    if hq : q then isTrue $ Or.inr ⟨hq, hp⟩
    else isFalse (Or.rec (λ ⟨h, _⟩ => hp h : ¬(p ∧ ¬ q)) (λ ⟨h, _⟩ => hq h : ¬(q ∧ ¬ p)))

  instance exists_prop_decidable {p} (P : p → Prop)
    [Dp : Decidable p] [DP : ∀ h, Decidable (P h)] : Decidable (∃ h, P h) :=
  if h : p then decidable_of_decidable_of_iff
    ⟨λ h2 => ⟨h, h2⟩, λ⟨h', h2⟩ => h2⟩ else isFalse (mt (λ⟨h, _⟩ => h) h)

  instance forall_prop_decidable {p} (P : p → Prop)
    [Dp : Decidable p] [DP : ∀ h, Decidable (P h)] : Decidable (∀ h, P h) :=
  if h : p
  then decidable_of_decidable_of_iff ⟨λ h2 _ => h2, λ al => al h⟩
  else isTrue (λ h2 => absurd h2 h)
end

lemma Bool.ff_ne_tt : false = true → False := Bool.noConfusion

def is_dec_eq {α : Sort u} (p : α → α → Bool) : Prop   := ∀ ⦃x y : α⦄, p x y = true → x = y
def is_dec_refl {α : Sort u} (p : α → α → Bool) : Prop := ∀ x, p x x = true

def decidable_eq_of_bool_pred {α : Sort u} {p : α → α → Bool} (h₁ : is_dec_eq p) (h₂ : is_dec_refl p) :
  DecidableEq α :=
λ (x y : α) =>
 if hp : p x y = true then isTrue (h₁ hp)
 else isFalse (λ hxy : x = y => absurd (h₂ y) (by rwa [hxy] at hp))

lemma decidable_eq_inl_refl {α : Sort u} [h : DecidableEq α] (a : α) : h a a = isTrue (Eq.refl a) :=
match (h a a) with
| (isTrue e)  => rfl
| (isFalse n) => absurd rfl n

lemma decidable_eq_inr_neg {α : Sort u} [h : DecidableEq α] {a b : α} : ∀ n : a ≠ b, h a b = isFalse n :=
λ n =>
match (h a b) with
| (isTrue e)   => absurd e n
| (isFalse n₁) => proof_irrel n n₁ ▸ Eq.refl (isFalse n)

/- subsingleton -/

-- TODO: rec_subsingleton

lemma implies_of_if_pos {c t e : Prop} [Decidable c] (h : ite c t e) : c → t :=
by intro hc
   have hp : ite c t e = t := if_pos hc
   rwa [hp] at h

lemma implies_of_if_neg {c t e : Prop} [Decidable c] (h : ite c t e) : ¬c → e :=
by intro hnc
   have hn : ite c t e = e := if_neg hnc
   rwa [hn] at h

lemma if_ctx_congr {α : Sort u} {b c : Prop} [dec_b : Decidable b] [dec_c : Decidable c]
                   {x y u v : α}
                   (h_c : b ↔ c) (h_t : c → x = u) (h_e : ¬c → y = v) :
        ite b x y = ite c u v :=
match dec_b, dec_c with
| (isFalse h₁), (isFalse h₂) => h_e h₂
| (isTrue h₁),  (isTrue h₂)  => h_t h₂
| (isFalse h₁), (isTrue h₂)  => absurd h₂ (Iff.mp (not_iff_not_of_iff h_c) h₁)
| (isTrue h₁),  (isFalse h₂) => absurd h₁ (Iff.mpr (not_iff_not_of_iff h_c) h₂)

lemma if_congr {α : Sort u} {b c : Prop} [dec_b : Decidable b] [dec_c : Decidable c]
               {x y u v : α}
               (h_c : b ↔ c) (h_t : x = u) (h_e : y = v) :
        ite b x y = ite c u v :=
@if_ctx_congr α b c dec_b dec_c x y u v h_c (λ h => h_t) (λ h => h_e)

@[simp] lemma if_true {h : Decidable True} (t e : α) : (@ite α True h t e) = t :=
if_pos trivial

@[simp] lemma if_false {h : Decidable False} (t e : α) : (@ite α False h t e) = e :=
if_neg not_false

lemma if_ctx_congr_prop {b c x y u v : Prop} [dec_b : Decidable b] [dec_c : Decidable c]
                      (h_c : b ↔ c) (h_t : c → (x ↔ u)) (h_e : ¬c → (y ↔ v)) :
        ite b x y ↔ ite c u v :=
match dec_b, dec_c with
| (isFalse h₁), (isFalse h₂) => h_e h₂
| (isTrue h₁),  (isTrue h₂)  => h_t h₂
| (isFalse h₁), (isTrue h₂)  => absurd h₂ (Iff.mp (not_iff_not_of_iff h_c) h₁)
| (isTrue h₁),  (isFalse h₂) => absurd h₁ (Iff.mpr (not_iff_not_of_iff h_c) h₂)

lemma if_congr_prop {b c x y u v : Prop} [dec_b : Decidable b] [dec_c : Decidable c]
                    (h_c : b ↔ c) (h_t : x ↔ u) (h_e : y ↔ v) :
        ite b x y ↔ ite c u v :=
if_ctx_congr_prop h_c (λ h => h_t) (λ h => h_e)

lemma if_ctx_simp_congr_prop {b c x y u v : Prop} [dec_b : Decidable b]
                               (h_c : b ↔ c) (h_t : c → (x ↔ u)) (h_e : ¬c → (y ↔ v)) :
        ite b x y ↔ (@ite Prop c (decidable_of_decidable_of_iff h_c) u v) :=
@if_ctx_congr_prop b c x y u v dec_b (decidable_of_decidable_of_iff h_c) h_c h_t h_e

lemma if_simp_congr_prop {b c x y u v : Prop} [dec_b : Decidable b]
                           (h_c : b ↔ c) (h_t : x ↔ u) (h_e : y ↔ v) :
        ite b x y ↔ (@ite Prop c (decidable_of_decidable_of_iff h_c) u v) :=
@if_ctx_simp_congr_prop b c x y u v dec_b h_c (λ h => h_t) (λ h => h_e)

lemma dif_ctx_congr {α : Sort u} {b c : Prop} [dec_b : Decidable b] [dec_c : Decidable c]
                    {x : b → α} {u : c → α} {y : ¬b → α} {v : ¬c → α}
                    (h_c : b ↔ c)
                    (h_t : ∀ (h : c),    x (Iff.mpr h_c h)                      = u h)
                    (h_e : ∀ (h : ¬c),   y (Iff.mpr (not_iff_not_of_iff h_c) h) = v h) :
        (@dite α b dec_b x y) = (@dite α c dec_c u v) :=
match dec_b, dec_c with
| (isFalse h₁), (isFalse h₂) => h_e h₂
| (isTrue h₁),  (isTrue h₂)  => h_t h₂
| (isFalse h₁), (isTrue h₂)  => absurd h₂ (Iff.mp (not_iff_not_of_iff h_c) h₁)
| (isTrue h₁),  (isFalse h₂) => absurd h₁ (Iff.mpr (not_iff_not_of_iff h_c) h₂)

lemma dif_ctx_simp_congr {α : Sort u} {b c : Prop} [dec_b : Decidable b]
                         {x : b → α} {u : c → α} {y : ¬b → α} {v : ¬c → α}
                         (h_c : b ↔ c)
                         (h_t : ∀ (h : c),    x (Iff.mpr h_c h)                      = u h)
                         (h_e : ∀ (h : ¬c),   y (Iff.mpr (not_iff_not_of_iff h_c) h) = v h) :
        (@dite α b dec_b x y) = (@dite α c (decidable_of_decidable_of_iff h_c) u v) :=
@dif_ctx_congr α b c dec_b (decidable_of_decidable_of_iff h_c) x u y v h_c h_t h_e

def as_true (c : Prop) [Decidable c] : Prop :=
if c then True else False

def as_false (c : Prop) [Decidable c] : Prop :=
if c then False else True

lemma of_as_true {c : Prop} [h₁ : Decidable c] (h₂ : as_true c) : c :=
match h₁, h₂ with
| (isTrue h_c),  h₂ => h_c
| (isFalse h_c), h₂ => False.elim h₂

/-- Universe lifting operation -/
structure ulift.{r, s} (α : Type s) : Type (max s r) :=
up :: (down : α)

namespace ulift

/- Bijection between α and ulift.{v} α -/
lemma up_down {α : Type u} : ∀ (b : ulift.{v} α), up (down b) = b
| up a => rfl

lemma down_up {α : Type u} (a : α) : down (up.{v} a) = a := rfl

end ulift

/-- Universe lifting operation from Sort to Type -/
structure plift (α : Sort u) : Type u :=
up :: (down : α)

namespace plift
/- Bijection between α and plift α -/
lemma up_down : ∀ (b : plift α), up (down b) = b
| (up a) => rfl

lemma down_up (a : α) : down (up a) = a := rfl
end plift

/- Equalities for rewriting let-expressions -/
lemma let_value_eq {α : Sort u} {β : Sort v} {a₁ a₂ : α} (b : α → β) :
                   a₁ = a₂ → (let x : α := a₁; b x) = (let x : α := a₂; b x) :=
λ h => Eq.recOn (motive := (λ a _ => (let x : α := a₁; b x) = (let x : α := a; b x))) h rfl

lemma let_value_heq {α : Sort v} {β : α → Sort u} {a₁ a₂ : α} (b : ∀ x : α, β x) :
                    a₁ = a₂ → HEq (let x : α := a₁; b x) (let x : α := a₂; b x) :=
by intro h; rw [h]

lemma let_body_eq {α : Sort v} {β : α → Sort u} (a : α) {b₁ b₂ : ∀ x : α, β x} :
                  (∀ x, b₁ x = b₂ x) → (let x : α := a; b₁ x) = (let x : α := a; b₂ x) :=
by intro h; rw [h]

lemma let_eq {α : Sort v} {β : Sort u} {a₁ a₂ : α} {b₁ b₂ : α → β} :
             a₁ = a₂ → (∀ x, b₁ x = b₂ x) → (let x : α := a₁; b₁ x) = (let x : α := a₂; b₂ x) :=
λ h₁ h₂ => Eq.recOn (motive := λ a _ => (let x := a₁; b₁ x) = (let x := a; b₂ x)) h₁ (h₂ a₁)

-- TODO: `section relation`

section binary
variable {α : Type u} {β : Type v}
variable (f : α → α → α)
variable (inv : α → α)
variable (one : α)
variable (g : α → α → α)

def commutative        := ∀ a b, f a b = f b a
def associative        := ∀ a b c, f (f a b) c = f a (f b c)
def left_identity      := ∀ a, f one a = a
def right_identity     := ∀ a, f a one = a
def RightInverse      := ∀ a, f a (inv a) = one
def left_cancelative   := ∀ a b c, f a b = f a c → b = c
def right_cancelative  := ∀ a b c, f a b = f c b → a = c
def left_distributive  := ∀ a b c, f a (g b c) = g (f a b) (f a c)
def right_distributive := ∀ a b c, f (g a b) c = g (f a c) (f b c)
def right_commutative (h : β → α → β) := ∀ b a₁ a₂, h (h b a₁) a₂ = h (h b a₂) a₁
def left_commutative  (h : α → β → β) := ∀ a₁ a₂ b, h a₁ (h a₂ b) = h a₂ (h a₁ b)

lemma left_comm : commutative f → associative f → left_commutative f :=
by intros hcomm hassoc a b c
   have h1 : f a (f b c) = f (f a b) c := Eq.symm (hassoc a b c)
   have h2 : f (f a b) c = f (f b a) c := hcomm a b ▸ rfl
   have h3 : f (f b a) c = f b (f a c) := hassoc b a c
   rw [←h3, ←h2, ←h1]

lemma right_comm : commutative f → associative f → right_commutative f :=
by intros hcomm hassoc a b c
   have h1 : f (f a b) c = f a (f b c) := hassoc a b c
   have h2 : f a (f b c) = f a (f c b) := hcomm b c ▸ rfl
   have h3 : f a (f c b) = f (f a c) b := Eq.symm (hassoc a c b)
   rw [←h3, ←h2, ←h1]

end binary

-- We define a fix' function here because the fix function in the Lean 4 prelude has
-- `set_option codegen false`.

namespace WellFounded

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

unsafe def fix'.impl (hwf : WellFounded r) (F : ∀ x, (∀ y, r y x → C y) → C x) (x : α) : C x :=
  F x fun y _ => impl hwf F y

set_option codegen false in
@[implementedBy fix'.impl]
def fix' (hwf : WellFounded r) (F : ∀ x, (∀ y, r y x → C y) → C x) (x : α) : C x := hwf.fix F x

end WellFounded


/-
Copyright (c) 2019 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Patrick Massot, Casper Putz, Anne Baanen

! This file was ported from Lean 3 source module linear_algebra.matrix.dual
! leanprover-community/mathlib commit 738c19f572805cff525a93aa4ffbdf232df05aa8
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.LinearAlgebra.Dual
import Mathbin.LinearAlgebra.Matrix.ToLin

/-!
# Dual space, linear maps and matrices.

This file contains some results on the matrix corresponding to the
transpose of a linear map (in the dual space).

## Tags

matrix, linear_map, transpose, dual
-/

open Matrix

section Transpose

variable {K V₁ V₂ ι₁ ι₂ : Type _} [Field K] [AddCommGroup V₁] [Module K V₁] [AddCommGroup V₂]
  [Module K V₂] [Fintype ι₁] [Fintype ι₂] [DecidableEq ι₁] [DecidableEq ι₂] {B₁ : Basis ι₁ K V₁}
  {B₂ : Basis ι₂ K V₂}

@[simp]
theorem LinearMap.toMatrix_transpose (u : V₁ →ₗ[K] V₂) :
    LinearMap.toMatrix B₂.dualBasis B₁.dualBasis (Module.Dual.transpose u) =
      (LinearMap.toMatrix B₁ B₂ u)ᵀ :=
  by
  ext (i j)
  simp only [LinearMap.toMatrix_apply, Module.Dual.transpose_apply, B₁.dual_basis_repr,
    B₂.dual_basis_apply, Matrix.transpose_apply, LinearMap.comp_apply]
#align linear_map.to_matrix_transpose LinearMap.toMatrix_transpose

@[simp]
theorem Matrix.toLin_transpose (M : Matrix ι₁ ι₂ K) :
    Matrix.toLin B₁.dualBasis B₂.dualBasis Mᵀ = Module.Dual.transpose (Matrix.toLin B₂ B₁ M) :=
  by
  apply (LinearMap.toMatrix B₁.dual_basis B₂.dual_basis).Injective
  rw [LinearMap.toMatrix_toLin, LinearMap.toMatrix_transpose, LinearMap.toMatrix_toLin]
#align matrix.to_lin_transpose Matrix.toLin_transpose

end Transpose


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

example : (∀ x : α, p x ∧ q x) -> ∀ y : α, p y :=
  assume h : ∀ x : α, p x ∧ q x,
  assume y : α,
  show p y, from (h y).left


import CvxLean.Syntax.Minimization
import CvxLean.Syntax.Prod
import CvxLean.Tactic.DCP.AtomLibrary
import CvxLean.Tactic.Basic.Rename
import CvxLean.Tactic.Basic.RenameConstr
import CvxLean.Tactic.Basic.RemoveConstr

open CvxLean
open Minimization
open Real

-- noncomputable def test004 : Solution $
--   optimization (v w : ℝ)
--     minimize exp v
--     subject to
--       cv : 0 ≤ v
--       cw : 0 ≤ w
--       c0 :kl_div v w ≤ 1
-- := by
--   dcp
--   sorry

-- #print test004

set_option trace.Meta.debug true
noncomputable def test000 : Solution $
  optimization (x : Finₓ 3 → ℝ) (y : ℝ)
    minimize y
    subject to
      c0 : exp (Vec.sum (Vec.exp x)) ≤ y
:= by
  dcp
  sorry

noncomputable def test001' (h : 0 ≤ (2 : ℝ)) (h : 0 ≤ (3 : ℝ)) : Solution $
  optimization (x y : ℝ)
    minimize y * (2 : ℝ)
    subject to
      c1 : exp (exp x) ≤ 3 * x
:= by
  dcp
  sorry

noncomputable def test001 : Solution $
  optimization (x y : ℝ)
    minimize exp $ exp (abs x)
    subject to
      c0 : exp (abs x) ≤ 0
      c' : 0 < y
      c1 : exp (exp (exp (exp (exp x)))) ≤ log y
:= by
  dcp
  sorry

noncomputable def test002 : Solution $
  optimization (x y : ℝ)
    minimize exp (huber y)
    subject to
      c0 : exp (exp (huber x)) ≤ y
:= by
  dcp
  sorry

noncomputable def test003 (h : (0 : ℝ) ≤ 2): Solution $
  optimization (x y : ℝ)
    minimize (2 : ℝ) * (huber (y + x))
    subject to
      c0 : x ≤ y
:= by
  dcp
  sorry

-- noncomputable def testVec0 [Fintype m] : Solution $
--   optimization (x y : m → ℝ)
--     minimize (0 : ℝ)
--     subject to
--       c0 : Vec.exp y ≤ x
-- := by
--   dcp
--   sorry

-- noncomputable def testVec [Fintype m] : Solution $
--   optimization (x y : m → ℝ)
--     minimize (0 : ℝ)
--     subject to
--       c0 : Vec.exp (Vec.exp x) ≤ x
-- := by
--   dcp
  -- sorry

-- noncomputable def test_Vec_huber [Fintype m] : Solution $
-- optimization (x y : m → ℝ)
--   minimize (0 : ℝ)
--   subject to
--     c0 : Vec.huber x ≤ x
-- := by
--   dcp
--   sorry

set_option trace.Meta.debug true

-- noncomputable def test_Vec_kl_div [Fintype m] : Solution $
-- optimization (x y : m → ℝ)
--   minimize (0 : ℝ)
--   subject to
--     cx : 0 < x
--     cy : 0 < y
--     c0 : Vec.kl_div x y ≤ x
-- := by
--   dcp
--   sorry

noncomputable def test2 : Solution $
  optimization (x y : ℝ)
    minimize x
    subject to
      c0 : 0 < x
      c1 : 0 < log x
      c2 : 0 ≤ log (log x)
:= by
  dcp
  sorry

noncomputable def test_log_det : Solution $
  optimization (M : Matrix (Finₓ n) (Finₓ n) ℝ)
    minimize (0 : ℝ)
    subject to
      c0 : 0 ≤ Real.log M.det
      c_pos_def : M.PosDef
:= by
  dcp
  sorry

-- Example from Grant's thesis

-- noncomputable example (a : ℝ) (h : 0 ≤ a) : Solution $
--   minimization! (x y : ℝ) :
--     objective (c * x)
--     constraints
--       (cmain : exp y ≤ log (a * sqrt x + b))
--       (clin  : a * x + b * y = d)
--       (csqrt : 0 ≤ x)
--       (clog  : 0 < a * sqrt x + b)
-- := by
--   dcp
--   sorry

noncomputable def a1 : ℝ := 3
noncomputable def b1 : ℝ := 4
noncomputable def c1 : ℝ := 5
noncomputable def d1 : ℝ := 6

set_option trace.Elab.debug true

-- TODO:
-- noncomputable example : Solution $
--   minimization! (x y : ℝ) :
--     objective (x)
--     constraints
--       (hlog : 0 < exp a1)
--       (e : exp y ≤ log (exp a1))
--       (hsqrt : 0 ≤ x) := by
--   dcp


import data.real.basic
import data.polynomial.derivative
import linear_algebra.basic
import linear_algebra.finite_dimensional

-- On se place dans le R -espace vectoriel E = R[X] .
-- (a) Soit H un sous-espace vectoriel de dimension finie et f un endomorphisme de H. Montrer qu'il existe p ∈ N tel que
--    ∀k ≥ p, Ker f k+1 = Ker f k.
--
-- Soit F un sous-espace vectoriel de E stable par l'opérateur D de dérivation.
-- (b) On suppose que F est de dimension finie non nulle. Montrer que l'endomorphisme induit par D sur R n [X] est nilpotent pour tout n ∈ N .
-- Montrer qu'il existe m ∈ N tel que F = R m [X] .
-- (c) Montrer que si F est de dimension innie alors F = R[X] .
-- (d) Soit g ∈ L(E) tel que g^2 = kId + D avec k ∈ R . Quel est le signe de k ?

theorem a
  (H: submodule real (polynomial real))
  (f: H →ₗ[real] H):
  exists p,
  forall k > p,
  (linear_map.ker (f ^ (k + 1))).carrier = (linear_map.ker (f ^ k)).carrier
:=
  sorry


/-
Copyright (c) 2018 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import topology.algebra.infinite_sum.order
import topology.algebra.infinite_sum.ring
import topology.instances.real

/-!
# Topology on `ℝ≥0`

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

The natural topology on `ℝ≥0` (the one induced from `ℝ`), and a basic API.

## Main definitions

Instances for the following typeclasses are defined:

* `topological_space ℝ≥0`
* `topological_semiring ℝ≥0`
* `second_countable_topology ℝ≥0`
* `order_topology ℝ≥0`
* `has_continuous_sub ℝ≥0`
* `has_continuous_inv₀ ℝ≥0` (continuity of `x⁻¹` away from `0`)
* `has_continuous_smul ℝ≥0 α` (whenever `α` has a continuous `mul_action ℝ α`)

Everything is inherited from the corresponding structures on the reals.

## Main statements

Various mathematically trivial lemmas are proved about the compatibility
of limits and sums in `ℝ≥0` and `ℝ`. For example

* `tendsto_coe {f : filter α} {m : α → ℝ≥0} {x : ℝ≥0} :
  tendsto (λa, (m a : ℝ)) f (𝓝 (x : ℝ)) ↔ tendsto m f (𝓝 x)`

says that the limit of a filter along a map to `ℝ≥0` is the same in `ℝ` and `ℝ≥0`, and

* `coe_tsum {f : α → ℝ≥0} : ((∑'a, f a) : ℝ) = (∑'a, (f a : ℝ))`

says that says that a sum of elements in `ℝ≥0` is the same in `ℝ` and `ℝ≥0`.

Similarly, some mathematically trivial lemmas about infinite sums are proved,
a few of which rely on the fact that subtraction is continuous.

-/
noncomputable theory
open set topological_space metric filter
open_locale topology

namespace nnreal
open_locale nnreal big_operators filter

instance : topological_space ℝ≥0 := infer_instance -- short-circuit type class inference

instance : topological_semiring ℝ≥0 :=
{ continuous_mul :=
    (continuous_subtype_val.fst'.mul continuous_subtype_val.snd').subtype_mk _,
  continuous_add :=
    (continuous_subtype_val.fst'.add continuous_subtype_val.snd').subtype_mk _ }

instance : second_countable_topology ℝ≥0 :=
topological_space.subtype.second_countable_topology _ _

instance : order_topology ℝ≥0 := @order_topology_of_ord_connected _ _ _ _ (Ici 0) _

section coe
variable {α : Type*}
open filter finset

lemma _root_.continuous_real_to_nnreal : continuous real.to_nnreal :=
(continuous_id.max continuous_const).subtype_mk _

lemma continuous_coe : continuous (coe : ℝ≥0 → ℝ) :=
continuous_subtype_val

/-- Embedding of `ℝ≥0` to `ℝ` as a bundled continuous map. -/
@[simps { fully_applied := ff }] def _root_.continuous_map.coe_nnreal_real : C(ℝ≥0, ℝ) :=
⟨coe, continuous_coe⟩

instance continuous_map.can_lift {X : Type*} [topological_space X] :
  can_lift C(X, ℝ) C(X, ℝ≥0) continuous_map.coe_nnreal_real.comp (λ f, ∀ x, 0 ≤ f x) :=
{ prf := λ f hf, ⟨⟨λ x, ⟨f x, hf x⟩, f.2.subtype_mk _⟩, fun_like.ext' rfl⟩ }

@[simp, norm_cast] lemma tendsto_coe {f : filter α} {m : α → ℝ≥0} {x : ℝ≥0} :
  tendsto (λa, (m a : ℝ)) f (𝓝 (x : ℝ)) ↔ tendsto m f (𝓝 x) :=
tendsto_subtype_rng.symm

lemma tendsto_coe' {f : filter α} [ne_bot f] {m : α → ℝ≥0} {x : ℝ} :
  tendsto (λ a, m a : α → ℝ) f (𝓝 x) ↔ ∃ hx : 0 ≤ x, tendsto m f (𝓝 ⟨x, hx⟩) :=
⟨λ h, ⟨ge_of_tendsto' h (λ c, (m c).2), tendsto_coe.1 h⟩, λ ⟨hx, hm⟩, tendsto_coe.2 hm⟩

@[simp] lemma map_coe_at_top : map (coe : ℝ≥0 → ℝ) at_top = at_top :=
map_coe_Ici_at_top 0

lemma comap_coe_at_top : comap (coe : ℝ≥0 → ℝ) at_top = at_top :=
(at_top_Ici_eq 0).symm

@[simp, norm_cast] lemma tendsto_coe_at_top {f : filter α} {m : α → ℝ≥0} :
  tendsto (λ a, (m a : ℝ)) f at_top ↔ tendsto m f at_top :=
tendsto_Ici_at_top.symm

lemma _root_.tendsto_real_to_nnreal {f : filter α} {m : α → ℝ} {x : ℝ} (h : tendsto m f (𝓝 x)) :
  tendsto (λa, real.to_nnreal (m a)) f (𝓝 (real.to_nnreal x)) :=
(continuous_real_to_nnreal.tendsto _).comp h

lemma _root_.tendsto_real_to_nnreal_at_top : tendsto real.to_nnreal at_top at_top :=
begin
  rw ← tendsto_coe_at_top,
  apply tendsto_id.congr' _,
  filter_upwards [Ici_mem_at_top (0 : ℝ)] with x hx,
  simp only [max_eq_left (set.mem_Ici.1 hx), id.def, real.coe_to_nnreal'],
end

lemma nhds_zero : 𝓝 (0 : ℝ≥0) = ⨅a ≠ 0, 𝓟 (Iio a) :=
nhds_bot_order.trans $ by simp [bot_lt_iff_ne_bot]

lemma nhds_zero_basis : (𝓝 (0 : ℝ≥0)).has_basis (λ a : ℝ≥0, 0 < a) (λ a, Iio a) :=
nhds_bot_basis

instance : has_continuous_sub ℝ≥0 :=
⟨((continuous_coe.fst'.sub continuous_coe.snd').max continuous_const).subtype_mk _⟩

instance : has_continuous_inv₀ ℝ≥0 :=
⟨λ x hx, tendsto_coe.1 $ (real.tendsto_inv $ nnreal.coe_ne_zero.2 hx).comp
  continuous_coe.continuous_at⟩

instance [topological_space α] [mul_action ℝ α] [has_continuous_smul ℝ α] :
  has_continuous_smul ℝ≥0 α :=
{ continuous_smul := (continuous_induced_dom.comp continuous_fst).smul continuous_snd }

@[norm_cast] lemma has_sum_coe {f : α → ℝ≥0} {r : ℝ≥0} :
  has_sum (λa, (f a : ℝ)) (r : ℝ) ↔ has_sum f r :=
by simp only [has_sum, coe_sum.symm, tendsto_coe]

lemma has_sum_real_to_nnreal_of_nonneg {f : α → ℝ} (hf_nonneg : ∀ n, 0 ≤ f n) (hf : summable f) :
  has_sum (λ n, real.to_nnreal (f n)) (real.to_nnreal (∑' n, f n)) :=
begin
  have h_sum : (λ s, ∑ b in s, real.to_nnreal (f b)) = λ s, real.to_nnreal (∑ b in s, f b),
    from funext (λ _, (real.to_nnreal_sum_of_nonneg (λ n _, hf_nonneg n)).symm),
  simp_rw [has_sum, h_sum],
  exact tendsto_real_to_nnreal hf.has_sum,
end

@[norm_cast] lemma summable_coe {f : α → ℝ≥0} : summable (λa, (f a : ℝ)) ↔ summable f :=
begin
  split,
  exact assume ⟨a, ha⟩, ⟨⟨a, has_sum_le (λa, (f a).2) has_sum_zero ha⟩, has_sum_coe.1 ha⟩,
  exact assume ⟨a, ha⟩, ⟨a.1, has_sum_coe.2 ha⟩
end

lemma summable_coe_of_nonneg {f : α → ℝ} (hf₁ : ∀ n, 0 ≤ f n) :
  @summable (ℝ≥0) _ _ _ (λ n, ⟨f n, hf₁ n⟩) ↔ summable f :=
begin
  lift f to α → ℝ≥0 using hf₁ with f rfl hf₁,
  simp only [summable_coe, subtype.coe_eta]
end

open_locale classical

@[norm_cast] lemma coe_tsum {f : α → ℝ≥0} : ↑∑'a, f a = ∑'a, (f a : ℝ) :=
if hf : summable f
then (eq.symm $ (has_sum_coe.2 $ hf.has_sum).tsum_eq)
else by simp [tsum, hf, mt summable_coe.1 hf]

lemma coe_tsum_of_nonneg {f : α → ℝ} (hf₁ : ∀ n, 0 ≤ f n) :
  (⟨∑' n, f n, tsum_nonneg hf₁⟩ : ℝ≥0) = (∑' n, ⟨f n, hf₁ n⟩ : ℝ≥0) :=
begin
  lift f to α → ℝ≥0 using hf₁ with f rfl hf₁,
  simp_rw [← nnreal.coe_tsum, subtype.coe_eta]
end

lemma tsum_mul_left (a : ℝ≥0) (f : α → ℝ≥0) : ∑' x, a * f x = a * ∑' x, f x :=
nnreal.eq $ by simp only [coe_tsum, nnreal.coe_mul, tsum_mul_left]

lemma tsum_mul_right (f : α → ℝ≥0) (a : ℝ≥0) : (∑' x, f x * a) = (∑' x, f x) * a :=
nnreal.eq $ by simp only [coe_tsum, nnreal.coe_mul, tsum_mul_right]

lemma summable_comp_injective {β : Type*} {f : α → ℝ≥0} (hf : summable f)
  {i : β → α} (hi : function.injective i) :
  summable (f ∘ i) :=
nnreal.summable_coe.1 $
show summable ((coe ∘ f) ∘ i), from (nnreal.summable_coe.2 hf).comp_injective hi

lemma summable_nat_add (f : ℕ → ℝ≥0) (hf : summable f) (k : ℕ) : summable (λ i, f (i + k)) :=
summable_comp_injective hf $ add_left_injective k

lemma summable_nat_add_iff {f : ℕ → ℝ≥0} (k : ℕ) : summable (λ i, f (i + k)) ↔ summable f :=
begin
  rw [← summable_coe, ← summable_coe],
  exact @summable_nat_add_iff ℝ _ _ _ (λ i, (f i : ℝ)) k,
end

lemma has_sum_nat_add_iff {f : ℕ → ℝ≥0} (k : ℕ) {a : ℝ≥0} :
  has_sum (λ n, f (n + k)) a ↔ has_sum f (a + ∑ i in range k, f i) :=
by simp [← has_sum_coe, coe_sum, nnreal.coe_add, ← has_sum_nat_add_iff k]

lemma sum_add_tsum_nat_add {f : ℕ → ℝ≥0} (k : ℕ) (hf : summable f) :
  ∑' i, f i = (∑ i in range k, f i) + ∑' i, f (i + k) :=
by rw [←nnreal.coe_eq, coe_tsum, nnreal.coe_add, coe_sum, coe_tsum,
  sum_add_tsum_nat_add k (nnreal.summable_coe.2 hf)]

lemma infi_real_pos_eq_infi_nnreal_pos [complete_lattice α] {f : ℝ → α} :
  (⨅ (n : ℝ) (h : 0 < n), f n) = (⨅ (n : ℝ≥0) (h : 0 < n), f n) :=
le_antisymm (infi_mono' $ λ r, ⟨r, le_rfl⟩) (infi₂_mono' $ λ r hr, ⟨⟨r, hr.le⟩, hr, le_rfl⟩)

end coe

lemma tendsto_cofinite_zero_of_summable {α} {f : α → ℝ≥0} (hf : summable f) :
  tendsto f cofinite (𝓝 0) :=
begin
  have h_f_coe : f = λ n, real.to_nnreal (f n : ℝ), from funext (λ n, real.to_nnreal_coe.symm),
  rw [h_f_coe, ← @real.to_nnreal_coe 0],
  exact tendsto_real_to_nnreal ((summable_coe.mpr hf).tendsto_cofinite_zero),
end

lemma tendsto_at_top_zero_of_summable {f : ℕ → ℝ≥0} (hf : summable f) :
  tendsto f at_top (𝓝 0) :=
by { rw ←nat.cofinite_eq_at_top, exact tendsto_cofinite_zero_of_summable hf }

/-- The sum over the complement of a finset tends to `0` when the finset grows to cover the whole
space. This does not need a summability assumption, as otherwise all sums are zero. -/
lemma tendsto_tsum_compl_at_top_zero {α : Type*} (f : α → ℝ≥0) :
  tendsto (λ (s : finset α), ∑' b : {x // x ∉ s}, f b) at_top (𝓝 0) :=
begin
  simp_rw [← tendsto_coe, coe_tsum, nnreal.coe_zero],
  exact tendsto_tsum_compl_at_top_zero (λ (a : α), (f a : ℝ))
end

/-- `x ↦ x ^ n` as an order isomorphism of `ℝ≥0`. -/
def pow_order_iso (n : ℕ) (hn : n ≠ 0) : ℝ≥0 ≃o ℝ≥0 :=
strict_mono.order_iso_of_surjective (λ x, x ^ n)
  (λ x y h, strict_mono_on_pow hn.bot_lt (zero_le x) (zero_le y) h) $
  (continuous_id.pow _).surjective (tendsto_pow_at_top hn) $
    by simpa [order_bot.at_bot_eq, pos_iff_ne_zero]

end nnreal