/-
Copyright (c) 2021 Christopher Hoskin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Christopher Hoskin

! This file was ported from Lean 3 source module analysis.normed.order.lattice
! leanprover-community/mathlib commit 10bf4f825ad729c5653adc039dafa3622e7f93c9
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Order.Lattice
import Mathbin.Analysis.Normed.Group.Basic
import Mathbin.Algebra.Order.LatticeGroup

/-!
# Normed lattice ordered groups

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

Motivated by the theory of Banach Lattices, we then define `normed_lattice_add_comm_group` as a
lattice with a covariant normed group addition satisfying the solid axiom.

## Main statements

We show that a normed lattice ordered group is a topological lattice with respect to the norm
topology.

## References

* [Meyer-Nieberg, Banach lattices][MeyerNieberg1991]

## Tags

normed, lattice, ordered, group
-/

/-!
### Normed lattice orderd groups

Motivated by the theory of Banach Lattices, this section introduces normed lattice ordered groups.
-/

-- mathport name: abs
local notation "|" a "|" => abs a

#print NormedLatticeAddCommGroup /-
/--
Let `α` be a normed commutative group equipped with a partial order covariant with addition, with
respect which `α` forms a lattice. Suppose that `α` is *solid*, that is to say, for `a` and `b` in
`α`, with absolute values `|a|` and `|b|` respectively, `|a| ≤ |b|` implies `‖a‖ ≤ ‖b‖`. Then `α` is
said to be a normed lattice ordered group.
-/
class NormedLatticeAddCommGroup (α : Type _) extends NormedAddCommGroup α, Lattice α where
  add_le_add_left : ∀ a b : α, a ≤ b → ∀ c : α, c + a ≤ c + b
  solid : ∀ a b : α, |a| ≤ |b| → ‖a‖ ≤ ‖b‖
#align normed_lattice_add_comm_group NormedLatticeAddCommGroup
-/

/- warning: solid -> solid is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] {a : α} {b : α}, (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))))) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (SubNegMonoid.toHasNeg.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))) (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) a) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (SubNegMonoid.toHasNeg.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))) (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) b)) -> (LE.le.{0} Real Real.hasLe (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) a) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) b))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] {a : α} {b : α}, (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))))) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (NegZeroClass.toNeg.{u1} α (SubNegZeroMonoid.toNegZeroClass.{u1} α (SubtractionMonoid.toSubNegZeroMonoid.{u1} α (SubtractionCommMonoid.toSubtractionMonoid.{u1} α (AddCommGroup.toDivisionAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))))) (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) a) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (NegZeroClass.toNeg.{u1} α (SubNegZeroMonoid.toNegZeroClass.{u1} α (SubtractionMonoid.toSubNegZeroMonoid.{u1} α (SubtractionCommMonoid.toSubtractionMonoid.{u1} α (AddCommGroup.toDivisionAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))))) (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) b)) -> (LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) a) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) b))
Case conversion may be inaccurate. Consider using '#align solid solidₓ'. -/
theorem solid {α : Type _} [NormedLatticeAddCommGroup α] {a b : α} (h : |a| ≤ |b|) : ‖a‖ ≤ ‖b‖ :=
  NormedLatticeAddCommGroup.solid a b h
#align solid solid

instance : NormedLatticeAddCommGroup ℝ
    where
  add_le_add_left _ _ h _ := add_le_add le_rfl h
  solid _ _ := id

#print NormedLatticeAddCommGroup.toOrderedAddCommGroup /-
-- see Note [lower instance priority]
/-- A normed lattice ordered group is an ordered additive commutative group
-/
instance (priority := 100) NormedLatticeAddCommGroup.toOrderedAddCommGroup {α : Type _}
    [h : NormedLatticeAddCommGroup α] : OrderedAddCommGroup α :=
  { h with }
#align normed_lattice_add_comm_group_to_ordered_add_comm_group NormedLatticeAddCommGroup.toOrderedAddCommGroup
-/

variable {α : Type _} [NormedLatticeAddCommGroup α]

open LatticeOrderedCommGroup

/- warning: dual_solid -> dual_solid is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α) (b : α), (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommGroup.toPartialOrder.{u1} α (NormedLatticeAddCommGroup.toOrderedAddCommGroup.{u1} α _inst_1)))) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) b (Neg.neg.{u1} α (SubNegMonoid.toHasNeg.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))) b)) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) a (Neg.neg.{u1} α (SubNegMonoid.toHasNeg.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))) a))) -> (LE.le.{0} Real Real.hasLe (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) a) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) b))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α) (b : α), (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (OrderedAddCommGroup.toPartialOrder.{u1} α (NormedLatticeAddCommGroup.toOrderedAddCommGroup.{u1} α _inst_1)))) (Inf.inf.{u1} α (Lattice.toInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)) b (Neg.neg.{u1} α (NegZeroClass.toNeg.{u1} α (SubNegZeroMonoid.toNegZeroClass.{u1} α (SubtractionMonoid.toSubNegZeroMonoid.{u1} α (SubtractionCommMonoid.toSubtractionMonoid.{u1} α (AddCommGroup.toDivisionAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))))) b)) (Inf.inf.{u1} α (Lattice.toInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)) a (Neg.neg.{u1} α (NegZeroClass.toNeg.{u1} α (SubNegZeroMonoid.toNegZeroClass.{u1} α (SubtractionMonoid.toSubNegZeroMonoid.{u1} α (SubtractionCommMonoid.toSubtractionMonoid.{u1} α (AddCommGroup.toDivisionAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))))) a))) -> (LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) a) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) b))
Case conversion may be inaccurate. Consider using '#align dual_solid dual_solidₓ'. -/
theorem dual_solid (a b : α) (h : b ⊓ -b ≤ a ⊓ -a) : ‖a‖ ≤ ‖b‖ :=
  by
  apply solid
  rw [abs_eq_sup_neg]
  nth_rw 1 [← neg_neg a]
  rw [← neg_inf_eq_sup_neg]
  rw [abs_eq_sup_neg]
  nth_rw 1 [← neg_neg b]
  rwa [← neg_inf_eq_sup_neg, neg_le_neg_iff, @inf_comm _ _ _ b, @inf_comm _ _ _ a]
#align dual_solid dual_solid

-- see Note [lower instance priority]
/-- Let `α` be a normed lattice ordered group, then the order dual is also a
normed lattice ordered group.
-/
instance (priority := 100) : NormedLatticeAddCommGroup αᵒᵈ :=
  { OrderDual.orderedAddCommGroup, OrderDual.normedAddCommGroup with solid := dual_solid }

/- warning: norm_abs_eq_norm -> norm_abs_eq_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α), Eq.{1} Real (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (SubNegMonoid.toHasNeg.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))) (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) a)) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) a)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α), Eq.{1} Real (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (NegZeroClass.toNeg.{u1} α (SubNegZeroMonoid.toNegZeroClass.{u1} α (SubtractionMonoid.toSubNegZeroMonoid.{u1} α (SubtractionCommMonoid.toSubtractionMonoid.{u1} α (AddCommGroup.toDivisionAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))))) (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) a)) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) a)
Case conversion may be inaccurate. Consider using '#align norm_abs_eq_norm norm_abs_eq_normₓ'. -/
theorem norm_abs_eq_norm (a : α) : ‖|a|‖ = ‖a‖ :=
  (solid (abs_abs a).le).antisymm (solid (abs_abs a).symm.le)
#align norm_abs_eq_norm norm_abs_eq_norm

/- warning: norm_inf_sub_inf_le_add_norm -> norm_inf_sub_inf_le_add_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α) (b : α) (c : α) (d : α), LE.le.{0} Real Real.hasLe (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) a b) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) c d))) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.hasAdd) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) a c)) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) b d)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α) (b : α) (c : α) (d : α), LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Inf.inf.{u1} α (Lattice.toInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)) a b) (Inf.inf.{u1} α (Lattice.toInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)) c d))) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.instAddReal) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) a c)) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) b d)))
Case conversion may be inaccurate. Consider using '#align norm_inf_sub_inf_le_add_norm norm_inf_sub_inf_le_add_normₓ'. -/
theorem norm_inf_sub_inf_le_add_norm (a b c d : α) : ‖a ⊓ b - c ⊓ d‖ ≤ ‖a - c‖ + ‖b - d‖ :=
  by
  rw [← norm_abs_eq_norm (a - c), ← norm_abs_eq_norm (b - d)]
  refine' le_trans (solid _) (norm_add_le |a - c| |b - d|)
  rw [abs_of_nonneg (|a - c| + |b - d|) (add_nonneg (abs_nonneg (a - c)) (abs_nonneg (b - d)))]
  calc
    |a ⊓ b - c ⊓ d| = |a ⊓ b - c ⊓ b + (c ⊓ b - c ⊓ d)| := by rw [sub_add_sub_cancel]
    _ ≤ |a ⊓ b - c ⊓ b| + |c ⊓ b - c ⊓ d| := (abs_add_le _ _)
    _ ≤ |a - c| + |b - d| := by
      apply add_le_add
      · exact abs_inf_sub_inf_le_abs _ _ _
      · rw [@inf_comm _ _ c, @inf_comm _ _ c]
        exact abs_inf_sub_inf_le_abs _ _ _

#align norm_inf_sub_inf_le_add_norm norm_inf_sub_inf_le_add_norm

/- warning: norm_sup_sub_sup_le_add_norm -> norm_sup_sub_sup_le_add_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α) (b : α) (c : α) (d : α), LE.le.{0} Real Real.hasLe (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) a b) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) c d))) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.hasAdd) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) a c)) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) b d)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α) (b : α) (c : α) (d : α), LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) a b) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) c d))) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.instAddReal) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) a c)) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) b d)))
Case conversion may be inaccurate. Consider using '#align norm_sup_sub_sup_le_add_norm norm_sup_sub_sup_le_add_normₓ'. -/
theorem norm_sup_sub_sup_le_add_norm (a b c d : α) : ‖a ⊔ b - c ⊔ d‖ ≤ ‖a - c‖ + ‖b - d‖ :=
  by
  rw [← norm_abs_eq_norm (a - c), ← norm_abs_eq_norm (b - d)]
  refine' le_trans (solid _) (norm_add_le |a - c| |b - d|)
  rw [abs_of_nonneg (|a - c| + |b - d|) (add_nonneg (abs_nonneg (a - c)) (abs_nonneg (b - d)))]
  calc
    |a ⊔ b - c ⊔ d| = |a ⊔ b - c ⊔ b + (c ⊔ b - c ⊔ d)| := by rw [sub_add_sub_cancel]
    _ ≤ |a ⊔ b - c ⊔ b| + |c ⊔ b - c ⊔ d| := (abs_add_le _ _)
    _ ≤ |a - c| + |b - d| := by
      apply add_le_add
      · exact abs_sup_sub_sup_le_abs _ _ _
      · rw [@sup_comm _ _ c, @sup_comm _ _ c]
        exact abs_sup_sub_sup_le_abs _ _ _

#align norm_sup_sub_sup_le_add_norm norm_sup_sub_sup_le_add_norm

/- warning: norm_inf_le_add -> norm_inf_le_add is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (x : α) (y : α), LE.le.{0} Real Real.hasLe (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) x y)) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.hasAdd) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) x) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) y))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (x : α) (y : α), LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (Inf.inf.{u1} α (Lattice.toInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)) x y)) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.instAddReal) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) x) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) y))
Case conversion may be inaccurate. Consider using '#align norm_inf_le_add norm_inf_le_addₓ'. -/
theorem norm_inf_le_add (x y : α) : ‖x ⊓ y‖ ≤ ‖x‖ + ‖y‖ :=
  by
  have h : ‖x ⊓ y - 0 ⊓ 0‖ ≤ ‖x - 0‖ + ‖y - 0‖ := norm_inf_sub_inf_le_add_norm x y 0 0
  simpa only [inf_idem, sub_zero] using h
#align norm_inf_le_add norm_inf_le_add

/- warning: norm_sup_le_add -> norm_sup_le_add is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (x : α) (y : α), LE.le.{0} Real Real.hasLe (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) x y)) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.hasAdd) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) x) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) y))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (x : α) (y : α), LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) x y)) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.instAddReal) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) x) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) y))
Case conversion may be inaccurate. Consider using '#align norm_sup_le_add norm_sup_le_addₓ'. -/
theorem norm_sup_le_add (x y : α) : ‖x ⊔ y‖ ≤ ‖x‖ + ‖y‖ :=
  by
  have h : ‖x ⊔ y - 0 ⊔ 0‖ ≤ ‖x - 0‖ + ‖y - 0‖ := norm_sup_sub_sup_le_add_norm x y 0 0
  simpa only [sup_idem, sub_zero] using h
#align norm_sup_le_add norm_sup_le_add

/- warning: normed_lattice_add_comm_group_has_continuous_inf -> NormedLatticeAddCommGroup.continuousInf is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α], ContinuousInf.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedAddCommGroup.toPseudoMetricSpace.{u1} α (NormedAddCommGroup.toSeminormedAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))) (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α], ContinuousInf.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedAddCommGroup.toPseudoMetricSpace.{u1} α (NormedAddCommGroup.toSeminormedAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))) (Lattice.toInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))
Case conversion may be inaccurate. Consider using '#align normed_lattice_add_comm_group_has_continuous_inf NormedLatticeAddCommGroup.continuousInfₓ'. -/
-- see Note [lower instance priority]
/-- Let `α` be a normed lattice ordered group. Then the infimum is jointly continuous.
-/
instance (priority := 100) NormedLatticeAddCommGroup.continuousInf : ContinuousInf α :=
  by
  refine' ⟨continuous_iff_continuousAt.2 fun q => tendsto_iff_norm_tendsto_zero.2 <| _⟩
  have : ∀ p : α × α, ‖p.1 ⊓ p.2 - q.1 ⊓ q.2‖ ≤ ‖p.1 - q.1‖ + ‖p.2 - q.2‖ := fun _ =>
    norm_inf_sub_inf_le_add_norm _ _ _ _
  refine' squeeze_zero (fun e => norm_nonneg _) this _
  convert((continuous_fst.tendsto q).sub tendsto_const_nhds).norm.add
      ((continuous_snd.tendsto q).sub tendsto_const_nhds).norm
  simp
#align normed_lattice_add_comm_group_has_continuous_inf NormedLatticeAddCommGroup.continuousInf

/- warning: normed_lattice_add_comm_group_has_continuous_sup -> NormedLatticeAddCommGroup.continuousSup is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_2 : NormedLatticeAddCommGroup.{u1} α], ContinuousSup.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedAddCommGroup.toPseudoMetricSpace.{u1} α (NormedAddCommGroup.toSeminormedAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_2))))) (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_2)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_2 : NormedLatticeAddCommGroup.{u1} α], ContinuousSup.{u1} α (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α (SeminormedAddCommGroup.toPseudoMetricSpace.{u1} α (NormedAddCommGroup.toSeminormedAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_2))))) (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_2)))
Case conversion may be inaccurate. Consider using '#align normed_lattice_add_comm_group_has_continuous_sup NormedLatticeAddCommGroup.continuousSupₓ'. -/
-- see Note [lower instance priority]
instance (priority := 100) NormedLatticeAddCommGroup.continuousSup {α : Type _}
    [NormedLatticeAddCommGroup α] : ContinuousSup α :=
  OrderDual.continuousSup αᵒᵈ
#align normed_lattice_add_comm_group_has_continuous_sup NormedLatticeAddCommGroup.continuousSup

#print NormedLatticeAddCommGroup.toTopologicalLattice /-
-- see Note [lower instance priority]
/--
Let `α` be a normed lattice ordered group. Then `α` is a topological lattice in the norm topology.
-/
instance (priority := 100) NormedLatticeAddCommGroup.toTopologicalLattice : TopologicalLattice α :=
  TopologicalLattice.mk
#align normed_lattice_add_comm_group_topological_lattice NormedLatticeAddCommGroup.toTopologicalLattice
-/

/- warning: norm_abs_sub_abs -> norm_abs_sub_abs is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α) (b : α), LE.le.{0} Real Real.hasLe (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (SubNegMonoid.toHasNeg.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))) (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) a) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (SubNegMonoid.toHasNeg.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))) (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) b))) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) a b))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (a : α) (b : α), LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (NegZeroClass.toNeg.{u1} α (SubNegZeroMonoid.toNegZeroClass.{u1} α (SubtractionMonoid.toSubNegZeroMonoid.{u1} α (SubtractionCommMonoid.toSubtractionMonoid.{u1} α (AddCommGroup.toDivisionAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))))) (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) a) (Abs.abs.{u1} α (Neg.toHasAbs.{u1} α (NegZeroClass.toNeg.{u1} α (SubNegZeroMonoid.toNegZeroClass.{u1} α (SubtractionMonoid.toSubNegZeroMonoid.{u1} α (SubtractionCommMonoid.toSubtractionMonoid.{u1} α (AddCommGroup.toDivisionAddCommMonoid.{u1} α (NormedAddCommGroup.toAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1))))))) (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)))) b))) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) a b))
Case conversion may be inaccurate. Consider using '#align norm_abs_sub_abs norm_abs_sub_absₓ'. -/
theorem norm_abs_sub_abs (a b : α) : ‖|a| - |b|‖ ≤ ‖a - b‖ :=
  solid (LatticeOrderedCommGroup.abs_abs_sub_abs_le _ _)
#align norm_abs_sub_abs norm_abs_sub_abs

/- warning: norm_sup_sub_sup_le_norm -> norm_sup_sub_sup_le_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (x : α) (y : α) (z : α), LE.le.{0} Real Real.hasLe (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) x z) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) y z))) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) x y))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (x : α) (y : α) (z : α), LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) x z) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) y z))) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) x y))
Case conversion may be inaccurate. Consider using '#align norm_sup_sub_sup_le_norm norm_sup_sub_sup_le_normₓ'. -/
theorem norm_sup_sub_sup_le_norm (x y z : α) : ‖x ⊔ z - y ⊔ z‖ ≤ ‖x - y‖ :=
  solid (abs_sup_sub_sup_le_abs x y z)
#align norm_sup_sub_sup_le_norm norm_sup_sub_sup_le_norm

/- warning: norm_inf_sub_inf_le_norm -> norm_inf_sub_inf_le_norm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (x : α) (y : α) (z : α), LE.le.{0} Real Real.hasLe (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) x z) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α (Lattice.toSemilatticeInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) y z))) (Norm.norm.{u1} α (NormedAddCommGroup.toHasNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toHasSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) x y))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (x : α) (y : α) (z : α), LE.le.{0} Real Real.instLEReal (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) (Inf.inf.{u1} α (Lattice.toInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)) x z) (Inf.inf.{u1} α (Lattice.toInf.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1)) y z))) (Norm.norm.{u1} α (NormedAddCommGroup.toNorm.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)) (HSub.hSub.{u1, u1, u1} α α α (instHSub.{u1} α (SubNegMonoid.toSub.{u1} α (AddGroup.toSubNegMonoid.{u1} α (NormedAddGroup.toAddGroup.{u1} α (NormedAddCommGroup.toNormedAddGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))))) x y))
Case conversion may be inaccurate. Consider using '#align norm_inf_sub_inf_le_norm norm_inf_sub_inf_le_normₓ'. -/
theorem norm_inf_sub_inf_le_norm (x y z : α) : ‖x ⊓ z - y ⊓ z‖ ≤ ‖x - y‖ :=
  solid (abs_inf_sub_inf_le_abs x y z)
#align norm_inf_sub_inf_le_norm norm_inf_sub_inf_le_norm

/- warning: lipschitz_with_sup_right -> lipschitzWith_sup_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (z : α), LipschitzWith.{u1, u1} α α (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α (SeminormedAddCommGroup.toPseudoMetricSpace.{u1} α (NormedAddCommGroup.toSeminormedAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α (SeminormedAddCommGroup.toPseudoMetricSpace.{u1} α (NormedAddCommGroup.toSeminormedAddCommGroup.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))) (OfNat.ofNat.{0} NNReal 1 (OfNat.mk.{0} NNReal 1 (One.one.{0} NNReal (AddMonoidWithOne.toOne.{0} NNReal (AddCommMonoidWithOne.toAddMonoidWithOne.{0} NNReal (NonAssocSemiring.toAddCommMonoidWithOne.{0} NNReal (Semiring.toNonAssocSemiring.{0} NNReal NNReal.semiring))))))) (fun (x : α) => Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) x z)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : NormedLatticeAddCommGroup.{u1} α] (z : α), LipschitzWith.{u1, u1} α α (EMetricSpace.toPseudoEMetricSpace.{u1} α (MetricSpace.toEMetricSpace.{u1} α (NormedAddCommGroup.toMetricSpace.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))) (EMetricSpace.toPseudoEMetricSpace.{u1} α (MetricSpace.toEMetricSpace.{u1} α (NormedAddCommGroup.toMetricSpace.{u1} α (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} α _inst_1)))) (OfNat.ofNat.{0} NNReal 1 (One.toOfNat1.{0} NNReal instNNRealOne)) (fun (x : α) => Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α (Lattice.toSemilatticeSup.{u1} α (NormedLatticeAddCommGroup.toLattice.{u1} α _inst_1))) x z)
Case conversion may be inaccurate. Consider using '#align lipschitz_with_sup_right lipschitzWith_sup_rightₓ'. -/
theorem lipschitzWith_sup_right (z : α) : LipschitzWith 1 fun x => x ⊔ z :=
  LipschitzWith.of_dist_le_mul fun x y =>
    by
    rw [Nonneg.coe_one, one_mul, dist_eq_norm, dist_eq_norm]
    exact norm_sup_sub_sup_le_norm x y z
#align lipschitz_with_sup_right lipschitzWith_sup_right

#print lipschitzWith_pos /-
theorem lipschitzWith_pos : LipschitzWith 1 (PosPart.pos : α → α) :=
  lipschitzWith_sup_right 0
#align lipschitz_with_pos lipschitzWith_pos
-/

#print continuous_pos /-
theorem continuous_pos : Continuous (PosPart.pos : α → α) :=
  LipschitzWith.continuous lipschitzWith_pos
#align continuous_pos continuous_pos
-/

#print continuous_neg' /-
theorem continuous_neg' : Continuous (NegPart.neg : α → α) :=
  continuous_pos.comp continuous_neg
#align continuous_neg' continuous_neg'
-/

/- warning: is_closed_nonneg -> isClosed_nonneg is a dubious translation:
lean 3 declaration is
  forall {E : Type.{u1}} [_inst_2 : NormedLatticeAddCommGroup.{u1} E], IsClosed.{u1} E (UniformSpace.toTopologicalSpace.{u1} E (PseudoMetricSpace.toUniformSpace.{u1} E (SeminormedAddCommGroup.toPseudoMetricSpace.{u1} E (NormedAddCommGroup.toSeminormedAddCommGroup.{u1} E (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} E _inst_2))))) (setOf.{u1} E (fun (x : E) => LE.le.{u1} E (Preorder.toLE.{u1} E (PartialOrder.toPreorder.{u1} E (OrderedAddCommGroup.toPartialOrder.{u1} E (NormedLatticeAddCommGroup.toOrderedAddCommGroup.{u1} E _inst_2)))) (OfNat.ofNat.{u1} E 0 (OfNat.mk.{u1} E 0 (Zero.zero.{u1} E (AddZeroClass.toHasZero.{u1} E (AddMonoid.toAddZeroClass.{u1} E (SubNegMonoid.toAddMonoid.{u1} E (AddGroup.toSubNegMonoid.{u1} E (NormedAddGroup.toAddGroup.{u1} E (NormedAddCommGroup.toNormedAddGroup.{u1} E (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} E _inst_2)))))))))) x))
but is expected to have type
  forall {E : Type.{u1}} [_inst_2 : NormedLatticeAddCommGroup.{u1} E], IsClosed.{u1} E (UniformSpace.toTopologicalSpace.{u1} E (PseudoMetricSpace.toUniformSpace.{u1} E (SeminormedAddCommGroup.toPseudoMetricSpace.{u1} E (NormedAddCommGroup.toSeminormedAddCommGroup.{u1} E (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} E _inst_2))))) (setOf.{u1} E (fun (x : E) => LE.le.{u1} E (Preorder.toLE.{u1} E (PartialOrder.toPreorder.{u1} E (OrderedAddCommGroup.toPartialOrder.{u1} E (NormedLatticeAddCommGroup.toOrderedAddCommGroup.{u1} E _inst_2)))) (OfNat.ofNat.{u1} E 0 (Zero.toOfNat0.{u1} E (NegZeroClass.toZero.{u1} E (SubNegZeroMonoid.toNegZeroClass.{u1} E (SubtractionMonoid.toSubNegZeroMonoid.{u1} E (SubtractionCommMonoid.toSubtractionMonoid.{u1} E (AddCommGroup.toDivisionAddCommMonoid.{u1} E (NormedAddCommGroup.toAddCommGroup.{u1} E (NormedLatticeAddCommGroup.toNormedAddCommGroup.{u1} E _inst_2))))))))) x))
Case conversion may be inaccurate. Consider using '#align is_closed_nonneg isClosed_nonnegₓ'. -/
theorem isClosed_nonneg {E} [NormedLatticeAddCommGroup E] : IsClosed { x : E | 0 ≤ x } :=
  by
  suffices { x : E | 0 ≤ x } = NegPart.neg ⁻¹' {(0 : E)}
    by
    rw [this]
    exact IsClosed.preimage continuous_neg' isClosed_singleton
  ext1 x
  simp only [Set.mem_preimage, Set.mem_singleton_iff, Set.mem_setOf_eq, neg_eq_zero_iff]
#align is_closed_nonneg isClosed_nonneg

/- warning: is_closed_le_of_is_closed_nonneg -> isClosed_le_of_isClosed_nonneg is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_2 : OrderedAddCommGroup.{u1} G] [_inst_3 : TopologicalSpace.{u1} G] [_inst_4 : ContinuousSub.{u1} G _inst_3 (SubNegMonoid.toHasSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G _inst_2))))], (IsClosed.{u1} G _inst_3 (setOf.{u1} G (fun (x : G) => LE.le.{u1} G (Preorder.toLE.{u1} G (PartialOrder.toPreorder.{u1} G (OrderedAddCommGroup.toPartialOrder.{u1} G _inst_2))) (OfNat.ofNat.{u1} G 0 (OfNat.mk.{u1} G 0 (Zero.zero.{u1} G (AddZeroClass.toHasZero.{u1} G (AddMonoid.toAddZeroClass.{u1} G (SubNegMonoid.toAddMonoid.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G _inst_2))))))))) x))) -> (IsClosed.{u1} (Prod.{u1, u1} G G) (Prod.topologicalSpace.{u1, u1} G G _inst_3 _inst_3) (setOf.{u1} (Prod.{u1, u1} G G) (fun (p : Prod.{u1, u1} G G) => LE.le.{u1} G (Preorder.toLE.{u1} G (PartialOrder.toPreorder.{u1} G (OrderedAddCommGroup.toPartialOrder.{u1} G _inst_2))) (Prod.fst.{u1, u1} G G p) (Prod.snd.{u1, u1} G G p))))
but is expected to have type
  forall {G : Type.{u1}} [_inst_2 : OrderedAddCommGroup.{u1} G] [_inst_3 : TopologicalSpace.{u1} G] [_inst_4 : ContinuousSub.{u1} G _inst_3 (SubNegMonoid.toSub.{u1} G (AddGroup.toSubNegMonoid.{u1} G (AddCommGroup.toAddGroup.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G _inst_2))))], (IsClosed.{u1} G _inst_3 (setOf.{u1} G (fun (x : G) => LE.le.{u1} G (Preorder.toLE.{u1} G (PartialOrder.toPreorder.{u1} G (OrderedAddCommGroup.toPartialOrder.{u1} G _inst_2))) (OfNat.ofNat.{u1} G 0 (Zero.toOfNat0.{u1} G (NegZeroClass.toZero.{u1} G (SubNegZeroMonoid.toNegZeroClass.{u1} G (SubtractionMonoid.toSubNegZeroMonoid.{u1} G (SubtractionCommMonoid.toSubtractionMonoid.{u1} G (AddCommGroup.toDivisionAddCommMonoid.{u1} G (OrderedAddCommGroup.toAddCommGroup.{u1} G _inst_2)))))))) x))) -> (IsClosed.{u1} (Prod.{u1, u1} G G) (instTopologicalSpaceProd.{u1, u1} G G _inst_3 _inst_3) (setOf.{u1} (Prod.{u1, u1} G G) (fun (p : Prod.{u1, u1} G G) => LE.le.{u1} G (Preorder.toLE.{u1} G (PartialOrder.toPreorder.{u1} G (OrderedAddCommGroup.toPartialOrder.{u1} G _inst_2))) (Prod.fst.{u1, u1} G G p) (Prod.snd.{u1, u1} G G p))))
Case conversion may be inaccurate. Consider using '#align is_closed_le_of_is_closed_nonneg isClosed_le_of_isClosed_nonnegₓ'. -/
theorem isClosed_le_of_isClosed_nonneg {G} [OrderedAddCommGroup G] [TopologicalSpace G]
    [ContinuousSub G] (h : IsClosed { x : G | 0 ≤ x }) : IsClosed { p : G × G | p.fst ≤ p.snd } :=
  by
  have : { p : G × G | p.fst ≤ p.snd } = (fun p : G × G => p.snd - p.fst) ⁻¹' { x : G | 0 ≤ x } :=
    by
    ext1 p
    simp only [sub_nonneg, Set.preimage_setOf_eq]
  rw [this]
  exact IsClosed.preimage (continuous_snd.sub continuous_fst) h
#align is_closed_le_of_is_closed_nonneg isClosed_le_of_isClosed_nonneg

#print NormedLatticeAddCommGroup.orderClosedTopology /-
-- See note [lower instance priority]
instance (priority := 100) NormedLatticeAddCommGroup.orderClosedTopology {E}
    [NormedLatticeAddCommGroup E] : OrderClosedTopology E :=
  ⟨isClosed_le_of_isClosed_nonneg isClosed_nonneg⟩
#align normed_lattice_add_comm_group.order_closed_topology NormedLatticeAddCommGroup.orderClosedTopology
-/


/-
Copyright (c) 2020 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.topology.uniform_space.basic
import Mathlib.PostPort

universes u v u_1 w

namespace Mathlib

/-!
# Uniform convergence

A sequence of functions `Fₙ` (with values in a metric space) converges uniformly on a set `s` to a
function `f` if, for all `ε > 0`, for all large enough `n`, one has for all `y ∈ s` the inequality
`dist (f y, Fₙ y) < ε`. Under uniform convergence, many properties of the `Fₙ` pass to the limit,
most notably continuity. We prove this in the file, defining the notion of uniform convergence
in the more general setting of uniform spaces, and with respect to an arbitrary indexing set
endowed with a filter (instead of just `ℕ` with `at_top`).

## Main results

Let `α` be a topological space, `β` a uniform space, `Fₙ` and `f` be functions from `α`to `β`
(where the index `n` belongs to an indexing type `ι` endowed with a filter `p`).

* `tendsto_uniformly_on F f p s`: the fact that `Fₙ` converges uniformly to `f` on `s`. This means
  that, for any entourage `u` of the diagonal, for large enough `n` (with respect to `p`), one has
  `(f y, Fₙ y) ∈ u` for all `y ∈ s`.
* `tendsto_uniformly F f p`: same notion with `s = univ`.
* `tendsto_uniformly_on.continuous_on`: a uniform limit on a set of functions which are continuous
  on this set is itself continuous on this set.
* `tendsto_uniformly.continuous`: a uniform limit of continuous functions is continuous.
* `tendsto_uniformly_on.tendsto_comp`: If `Fₙ` tends uniformly to `f` on a set `s`, and `gₙ` tends
  to `x` within `s`, then `Fₙ gₙ` tends to `f x` if `f` is continuous at `x` within `s`.
* `tendsto_uniformly.tendsto_comp`: If `Fₙ` tends uniformly to `f`, and `gₙ` tends to `x`, then
  `Fₙ gₙ` tends to `f x`.

We also define notions where the convergence is locally uniform, called
`tendsto_locally_uniformly_on F f p s` and `tendsto_locally_uniformly F f p`. The previous theorems
all have corresponding versions under locally uniform convergence.

## Implementation notes

Most results hold under weaker assumptions of locally uniform approximation. In a first section,
we prove the results under these weaker assumptions. Then, we derive the results on uniform
convergence from them.

## Tags

Uniform limit, uniform convergence, tends uniformly to
 -/

/-!
### Different notions of uniform convergence

We define uniform convergence and locally uniform convergence, on a set or in the whole space.
-/

/-- A sequence of functions `Fₙ` converges uniformly on a set `s` to a limiting function `f` with
respect to the filter `p` if, for any entourage of the diagonal `u`, one has `p`-eventually
`(f x, Fₙ x) ∈ u` for all `x ∈ s`. -/
def tendsto_uniformly_on {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β] (F : ι → α → β)
    (f : α → β) (p : filter ι) (s : set α) :=
  ∀ (u : set (β × β)),
    u ∈ uniformity β → filter.eventually (fun (n : ι) => ∀ (x : α), x ∈ s → (f x, F n x) ∈ u) p

/-- A sequence of functions `Fₙ` converges uniformly to a limiting function `f` with respect to a
filter `p` if, for any entourage of the diagonal `u`, one has `p`-eventually
`(f x, Fₙ x) ∈ u` for all `x`. -/
def tendsto_uniformly {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β] (F : ι → α → β)
    (f : α → β) (p : filter ι) :=
  ∀ (u : set (β × β)),
    u ∈ uniformity β → filter.eventually (fun (n : ι) => ∀ (x : α), (f x, F n x) ∈ u) p

theorem tendsto_uniformly_on_univ {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β]
    {F : ι → α → β} {f : α → β} {p : filter ι} :
    tendsto_uniformly_on F f p set.univ ↔ tendsto_uniformly F f p :=
  sorry

theorem tendsto_uniformly_on.mono {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β]
    {F : ι → α → β} {f : α → β} {s : set α} {p : filter ι} {s' : set α}
    (h : tendsto_uniformly_on F f p s) (h' : s' ⊆ s) : tendsto_uniformly_on F f p s' :=
  fun (u : set (β × β)) (hu : u ∈ uniformity β) =>
    filter.eventually.mono (h u hu)
      fun (n : ι) (hn : ∀ (x : α), x ∈ s → (f x, F n x) ∈ u) (x : α) (hx : x ∈ s') => hn x (h' hx)

theorem tendsto_uniformly.tendsto_uniformly_on {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} {p : filter ι}
    (h : tendsto_uniformly F f p) : tendsto_uniformly_on F f p s :=
  tendsto_uniformly_on.mono (iff.mpr tendsto_uniformly_on_univ h) (set.subset_univ s)

/-- Composing on the right by a function preserves uniform convergence on a set -/
theorem tendsto_uniformly_on.comp {α : Type u} {β : Type v} {γ : Type w} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} {p : filter ι}
    (h : tendsto_uniformly_on F f p s) (g : γ → α) :
    tendsto_uniformly_on (fun (n : ι) => F n ∘ g) (f ∘ g) p (g ⁻¹' s) :=
  sorry

/-- Composing on the right by a function preserves uniform convergence -/
theorem tendsto_uniformly.comp {α : Type u} {β : Type v} {γ : Type w} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {p : filter ι} (h : tendsto_uniformly F f p)
    (g : γ → α) : tendsto_uniformly (fun (n : ι) => F n ∘ g) (f ∘ g) p :=
  id
    fun (u : set (β × β)) (hu : u ∈ uniformity β) =>
      filter.eventually.mono (h u hu)
        fun (n : ι) (hn : ∀ (x : α), (f x, F n x) ∈ u) (x : γ) => hn (g x)

/-- A sequence of functions `Fₙ` converges locally uniformly on a set `s` to a limiting function
`f` with respect to a filter `p` if, for any entourage of the diagonal `u`, for any `x ∈ s`, one
has `p`-eventually `(f x, Fₙ x) ∈ u` for all `y` in a neighborhood of `x` in `s`. -/
def tendsto_locally_uniformly_on {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β]
    [topological_space α] (F : ι → α → β) (f : α → β) (p : filter ι) (s : set α) :=
  ∀ (u : set (β × β)) (H : u ∈ uniformity β) (x : α) (H : x ∈ s),
    ∃ (t : set α),
      ∃ (H : t ∈ nhds_within x s),
        filter.eventually (fun (n : ι) => ∀ (y : α), y ∈ t → (f y, F n y) ∈ u) p

/-- A sequence of functions `Fₙ` converges locally uniformly to a limiting function `f` with respect
to a filter `p` if, for any entourage of the diagonal `u`, for any `x`, one has `p`-eventually
`(f x, Fₙ x) ∈ u` for all `y` in a neighborhood of `x`. -/
def tendsto_locally_uniformly {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β]
    [topological_space α] (F : ι → α → β) (f : α → β) (p : filter ι) :=
  ∀ (u : set (β × β)) (H : u ∈ uniformity β) (x : α),
    ∃ (t : set α),
      ∃ (H : t ∈ nhds x), filter.eventually (fun (n : ι) => ∀ (y : α), y ∈ t → (f y, F n y) ∈ u) p

theorem tendsto_uniformly_on.tendsto_locally_uniformly_on {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} {p : filter ι} [topological_space α]
    (h : tendsto_uniformly_on F f p s) : tendsto_locally_uniformly_on F f p s :=
  fun (u : set (β × β)) (hu : u ∈ uniformity β) (x : α) (hx : x ∈ s) =>
    Exists.intro s (Exists.intro self_mem_nhds_within (h u hu))

theorem tendsto_uniformly.tendsto_locally_uniformly {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {p : filter ι} [topological_space α]
    (h : tendsto_uniformly F f p) : tendsto_locally_uniformly F f p :=
  sorry

theorem tendsto_locally_uniformly_on.mono {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β]
    {F : ι → α → β} {f : α → β} {s : set α} {s' : set α} {p : filter ι} [topological_space α]
    (h : tendsto_locally_uniformly_on F f p s) (h' : s' ⊆ s) :
    tendsto_locally_uniformly_on F f p s' :=
  sorry

theorem tendsto_locally_uniformly_on_univ {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β]
    {F : ι → α → β} {f : α → β} {p : filter ι} [topological_space α] :
    tendsto_locally_uniformly_on F f p set.univ ↔ tendsto_locally_uniformly F f p :=
  sorry

theorem tendsto_locally_uniformly_on.comp {α : Type u} {β : Type v} {γ : Type w} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} {p : filter ι} [topological_space α]
    [topological_space γ] {t : set γ} (h : tendsto_locally_uniformly_on F f p s) (g : γ → α)
    (hg : set.maps_to g t s) (cg : continuous_on g t) :
    tendsto_locally_uniformly_on (fun (n : ι) => F n ∘ g) (f ∘ g) p t :=
  sorry

theorem tendsto_locally_uniformly.comp {α : Type u} {β : Type v} {γ : Type w} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {p : filter ι} [topological_space α]
    [topological_space γ] (h : tendsto_locally_uniformly F f p) (g : γ → α) (cg : continuous g) :
    tendsto_locally_uniformly (fun (n : ι) => F n ∘ g) (f ∘ g) p :=
  sorry

/-!
### Uniform approximation

In this section, we give lemmas ensuring that a function is continuous if it can be approximated
uniformly by continuous functions. We give various versions, within a set or the whole space, at
a single point or at all points, with locally uniform approximation or uniform approximation. All
the statements are derived from a statement about locally uniform approximation within a set at
a point, called `continuous_within_at_of_locally_uniform_approx_of_continuous_within_at`. -/

/-- A function which can be locally uniformly approximated by functions which are continuous
within a set at a point is continuous within this set at this point. -/
theorem continuous_within_at_of_locally_uniform_approx_of_continuous_within_at {α : Type u}
    {β : Type v} {ι : Type u_1} [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} {x : α}
    [topological_space α] (hx : x ∈ s)
    (L :
      ∀ (u : set (β × β)) (H : u ∈ uniformity β),
        ∃ (t : set α), ∃ (H : t ∈ nhds_within x s), ∃ (n : ι), ∀ (y : α), y ∈ t → (f y, F n y) ∈ u)
    (C : ∀ (n : ι), continuous_within_at (F n) s x) : continuous_within_at f s x :=
  sorry

/-- A function which can be locally uniformly approximated by functions which are continuous at
a point is continuous at this point. -/
theorem continuous_at_of_locally_uniform_approx_of_continuous_at {α : Type u} {β : Type v}
    {ι : Type u_1} [uniform_space β] {F : ι → α → β} {f : α → β} {x : α} [topological_space α]
    (L :
      ∀ (u : set (β × β)) (H : u ∈ uniformity β),
        ∃ (t : set α), ∃ (H : t ∈ nhds x), ∃ (n : ι), ∀ (y : α), y ∈ t → (f y, F n y) ∈ u)
    (C : ∀ (n : ι), continuous_at (F n) x) : continuous_at f x :=
  sorry

/-- A function which can be locally uniformly approximated by functions which are continuous
on a set is continuous on this set. -/
theorem continuous_on_of_locally_uniform_approx_of_continuous_on {α : Type u} {β : Type v}
    {ι : Type u_1} [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} [topological_space α]
    (L :
      ∀ (x : α) (H : x ∈ s) (u : set (β × β)) (H : u ∈ uniformity β),
        ∃ (t : set α), ∃ (H : t ∈ nhds_within x s), ∃ (n : ι), ∀ (y : α), y ∈ t → (f y, F n y) ∈ u)
    (C : ∀ (n : ι), continuous_on (F n) s) : continuous_on f s :=
  fun (x : α) (hx : x ∈ s) =>
    continuous_within_at_of_locally_uniform_approx_of_continuous_within_at hx (L x hx)
      fun (n : ι) => C n x hx

/-- A function which can be uniformly approximated by functions which are continuous on a set
is continuous on this set. -/
theorem continuous_on_of_uniform_approx_of_continuous_on {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} [topological_space α]
    (L : ∀ (u : set (β × β)), u ∈ uniformity β → ∃ (n : ι), ∀ (y : α), y ∈ s → (f y, F n y) ∈ u) :
    (∀ (n : ι), continuous_on (F n) s) → continuous_on f s :=
  continuous_on_of_locally_uniform_approx_of_continuous_on
    fun (x : α) (hx : x ∈ s) (u : set (β × β)) (hu : u ∈ uniformity β) =>
      Exists.intro s (Exists.intro self_mem_nhds_within (L u hu))

/-- A function which can be locally uniformly approximated by continuous functions is continuous. -/
theorem continuous_of_locally_uniform_approx_of_continuous {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} [topological_space α]
    (L :
      ∀ (x : α) (u : set (β × β)) (H : u ∈ uniformity β),
        ∃ (t : set α), ∃ (H : t ∈ nhds x), ∃ (n : ι), ∀ (y : α), y ∈ t → (f y, F n y) ∈ u)
    (C : ∀ (n : ι), continuous (F n)) : continuous f :=
  sorry

/-- A function which can be uniformly approximated by continuous functions is continuous. -/
theorem continuous_of_uniform_approx_of_continuous {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} [topological_space α]
    (L : ∀ (u : set (β × β)), u ∈ uniformity β → ∃ (N : ι), ∀ (y : α), (f y, F N y) ∈ u) :
    (∀ (n : ι), continuous (F n)) → continuous f :=
  sorry

/-!
### Uniform limits

From the previous statements on uniform approximation, we deduce continuity results for uniform
limits.
-/

/-- A locally uniform limit on a set of functions which are continuous on this set is itself
continuous on this set. -/
theorem tendsto_locally_uniformly_on.continuous_on {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} {p : filter ι} [topological_space α]
    (h : tendsto_locally_uniformly_on F f p s) (hc : ∀ (n : ι), continuous_on (F n) s)
    [filter.ne_bot p] : continuous_on f s :=
  sorry

/-- A uniform limit on a set of functions which are continuous on this set is itself continuous
on this set. -/
theorem tendsto_uniformly_on.continuous_on {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} {p : filter ι} [topological_space α]
    (h : tendsto_uniformly_on F f p s) (hc : ∀ (n : ι), continuous_on (F n) s) [filter.ne_bot p] :
    continuous_on f s :=
  tendsto_locally_uniformly_on.continuous_on (tendsto_uniformly_on.tendsto_locally_uniformly_on h)
    hc

/-- A locally uniform limit of continuous functions is continuous. -/
theorem tendsto_locally_uniformly.continuous {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {p : filter ι} [topological_space α]
    (h : tendsto_locally_uniformly F f p) (hc : ∀ (n : ι), continuous (F n)) [filter.ne_bot p] :
    continuous f :=
  sorry

/-- A uniform limit of continuous functions is continuous. -/
theorem tendsto_uniformly.continuous {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β]
    {F : ι → α → β} {f : α → β} {p : filter ι} [topological_space α] (h : tendsto_uniformly F f p)
    (hc : ∀ (n : ι), continuous (F n)) [filter.ne_bot p] : continuous f :=
  tendsto_locally_uniformly.continuous (tendsto_uniformly.tendsto_locally_uniformly h) hc

/-!
### Composing limits under uniform convergence

In general, if `Fₙ` converges pointwise to a function `f`, and `gₙ` tends to `x`, it is not true
that `Fₙ gₙ` tends to `f x`. It is true however if the convergence of `Fₙ` to `f` is uniform. In
this paragraph, we prove variations around this statement.
-/

/-- If `Fₙ` converges locally uniformly on a neighborhood of `x` within a set `s` to a function `f`
which is continuous at `x` within `s `, and `gₙ` tends to `x` within `s`, then `Fₙ (gₙ)` tends
to `f x`. -/
theorem tendsto_comp_of_locally_uniform_limit_within {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} {x : α} {p : filter ι} {g : ι → α}
    [topological_space α] (h : continuous_within_at f s x)
    (hg : filter.tendsto g p (nhds_within x s))
    (hunif :
      ∀ (u : set (β × β)) (H : u ∈ uniformity β),
        ∃ (t : set α),
          ∃ (H : t ∈ nhds_within x s),
            filter.eventually (fun (n : ι) => ∀ (y : α), y ∈ t → (f y, F n y) ∈ u) p) :
    filter.tendsto (fun (n : ι) => F n (g n)) p (nhds (f x)) :=
  sorry

/-- If `Fₙ` converges locally uniformly on a neighborhood of `x` to a function `f` which is
continuous at `x`, and `gₙ` tends to `x`, then `Fₙ (gₙ)` tends to `f x`. -/
theorem tendsto_comp_of_locally_uniform_limit {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {x : α} {p : filter ι} {g : ι → α}
    [topological_space α] (h : continuous_at f x) (hg : filter.tendsto g p (nhds x))
    (hunif :
      ∀ (u : set (β × β)) (H : u ∈ uniformity β),
        ∃ (t : set α),
          ∃ (H : t ∈ nhds x),
            filter.eventually (fun (n : ι) => ∀ (y : α), y ∈ t → (f y, F n y) ∈ u) p) :
    filter.tendsto (fun (n : ι) => F n (g n)) p (nhds (f x)) :=
  sorry

/-- If `Fₙ` tends locally uniformly to `f` on a set `s`, and `gₙ` tends to `x` within `s`, then
`Fₙ gₙ` tends to `f x` if `f` is continuous at `x` within `s` and `x ∈ s`. -/
theorem tendsto_locally_uniformly_on.tendsto_comp {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {s : set α} {x : α} {p : filter ι} {g : ι → α}
    [topological_space α] (h : tendsto_locally_uniformly_on F f p s)
    (hf : continuous_within_at f s x) (hx : x ∈ s) (hg : filter.tendsto g p (nhds_within x s)) :
    filter.tendsto (fun (n : ι) => F n (g n)) p (nhds (f x)) :=
  tendsto_comp_of_locally_uniform_limit_within hf hg
    fun (u : set (β × β)) (hu : u ∈ uniformity β) => h u hu x hx

/-- If `Fₙ` tends uniformly to `f` on a set `s`, and `gₙ` tends to `x` within `s`, then `Fₙ gₙ` tends
to `f x` if `f` is continuous at `x` within `s`. -/
theorem tendsto_uniformly_on.tendsto_comp {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β]
    {F : ι → α → β} {f : α → β} {s : set α} {x : α} {p : filter ι} {g : ι → α} [topological_space α]
    (h : tendsto_uniformly_on F f p s) (hf : continuous_within_at f s x)
    (hg : filter.tendsto g p (nhds_within x s)) :
    filter.tendsto (fun (n : ι) => F n (g n)) p (nhds (f x)) :=
  tendsto_comp_of_locally_uniform_limit_within hf hg
    fun (u : set (β × β)) (hu : u ∈ uniformity β) =>
      Exists.intro s (Exists.intro self_mem_nhds_within (h u hu))

/-- If `Fₙ` tends locally uniformly to `f`, and `gₙ` tends to `x`, then `Fₙ gₙ` tends to `f x`. -/
theorem tendsto_locally_uniformly.tendsto_comp {α : Type u} {β : Type v} {ι : Type u_1}
    [uniform_space β] {F : ι → α → β} {f : α → β} {x : α} {p : filter ι} {g : ι → α}
    [topological_space α] (h : tendsto_locally_uniformly F f p) (hf : continuous_at f x)
    (hg : filter.tendsto g p (nhds x)) : filter.tendsto (fun (n : ι) => F n (g n)) p (nhds (f x)) :=
  tendsto_comp_of_locally_uniform_limit hf hg
    fun (u : set (β × β)) (hu : u ∈ uniformity β) => h u hu x

/-- If `Fₙ` tends uniformly to `f`, and `gₙ` tends to `x`, then `Fₙ gₙ` tends to `f x`. -/
theorem tendsto_uniformly.tendsto_comp {α : Type u} {β : Type v} {ι : Type u_1} [uniform_space β]
    {F : ι → α → β} {f : α → β} {x : α} {p : filter ι} {g : ι → α} [topological_space α]
    (h : tendsto_uniformly F f p) (hf : continuous_at f x) (hg : filter.tendsto g p (nhds x)) :
    filter.tendsto (fun (n : ι) => F n (g n)) p (nhds (f x)) :=
  tendsto_locally_uniformly.tendsto_comp (tendsto_uniformly.tendsto_locally_uniformly h) hf hg

end Mathlib


import linear_algebra.eigenspace

import to_mathlib_maybe.Hilbert_space

open_locale tensor_product big_operators

variables {ℋ : Type} [complex_hilbert_space ℋ]

/--
A quantum state is a linear map from a Hilbert space to itself --
a.k.a., a linear operator, a.k.a, a module endomorphism -- with trace one and
only non-negative eigenvalues.

Definition 4.1.3 here (https://arxiv.org/pdf/1106.1445.pdf).
-/
class quantum_state (ρ : module.End ℂ ℋ) :=
(trace_one : Tr ρ = 1)
(pos_semidef : ∀ e, ρ.has_eigenvalue e → (e.im = 0) ∧ (e.re ≥ 0))
-- Alternatively, we could define this more abstractly
-- (hermitian : ∀ x y : ℋ, inner x (ρ y) = inner (ρ x) y)
-- (pos_semidef : ∀ x : ℋ, inner x (ρ x) ≥ 0)

/--
The list of eigenvalues of a quantum state.
-/
def spectrum (ρ : module.End ℂ ℋ) [quantum_state ρ] : list ℝ := sorry

/--
Trace distance
-/
def trace_distance (ρ : module.End ℂ ℋ) (σ : module.End ℂ ℋ) : ℝ :=  1/2 * ∑ e ∈ spectrum (ρ-σ), abs e

notation `D(`ρ`,`σ`)` := trace_distance ρ σ

universe x

variables
{ι : Type x} -- indexing type
[fintype ι] -- tell Lean that the set of all elements ι is finite.
(ρ : module.End ℂ ℋ)
{e : ι → ℂ} [∀ i, ρ.has_eigenvalue (e i)]

/--
If a linear operator has trace one then its eigenvalues sum to 1.
-/
lemma sum_eigenvalues_eq_one_of_trace_one :
Tr ρ = 1 → ∑ i, e i = 1 :=
begin
    sorry
end


-- Written by Patrick Johnson and released into the public domain at:
-- https://github.com/user7230724/lean-projects/blob/master/src/list_take_join/main.lean

import tactic

namespace list

variables {α : Type*}

private noncomputable def get_max [inhabited α] [linear_order α] (P : α → Prop) :=
classical.epsilon (λ (x : α), P x ∧ ∀ (y : α), x < y → ¬P y)

private lemma epsilon_eq [inhabited α] {P : α → Prop} {x : α}
  (h₁ : P x) (h₂ : ∀ (y : α), P y → y = x) : classical.epsilon P = x :=
begin
  have h₃ : P = (λ (y : α), y = x),
  { ext y, split; intro h₃,
    { exact h₂ y h₃ },
    { rwa h₃ }},
  subst h₃, apply classical.epsilon_singleton,
end

private lemma nat_get_max_spec {P : ℕ → Prop}
  (h₁ : ∃ (x : ℕ), P x) (h₂ : ∃ (x : ℕ), ∀ (y : ℕ), x ≤ y → ¬P y) :
  P (get_max P) ∧ ∀ (x : ℕ), get_max P < x → ¬P x :=
begin
  cases h₁ with m h₁, cases h₂ with n h₂, induction n with n ih,
  { cases h₂ m (zero_le m) h₁ },
  { simp_rw nat.succ_le_iff at h₂, by_cases h₃ : P n,
    { have : get_max P = n,
      { rw get_max, apply epsilon_eq,
        { use h₃, rintro k hk, exact h₂ k hk, },
        { rintro k ⟨h₄, h₅⟩, by_contra h₆,
          rw [←ne, ne_iff_lt_or_gt] at h₆, cases h₆,
          { cases h₅ n h₆ h₃ }, { cases h₂ k h₆ h₄ }}},
      rw this at *, clear this, exact ⟨h₃, h₂⟩ },
    { apply ih, rintro k hk, rw le_iff_eq_or_lt at hk, rcases hk with rfl | hk,
      { exact h₃ },
      { exact h₂ k hk }}},
end

private lemma list.take_eq_take {l : list α} {m n : ℕ} :
  l.take m = l.take n ↔ min m l.length = min n l.length :=
begin
  induction l with x l ih generalizing m n,
  { simp },
  { simp_rw list.length_cons, split; intro h,
    { cases n, { simp at h, subst h },
      cases m, { simp at h, cases h },
      simp only [list.take, eq_self_iff_true, true_and] at h,
      simpa [nat.min_succ_succ] using ih.mp h },
    { cases n, { simp at h, subst h },
      cases m, { simp [nat.min_succ_succ] at h, cases h },
      simp_rw nat.min_succ_succ at h, simpa using ih.mpr h }},
end

private lemma list.take_add {l : list α} {m n : ℕ} :
  l.take (m + n) = l.take m ++ (l.drop m).take n :=
begin
  convert_to
    list.take (m + n) (list.take m l ++ list.drop m l) =
    list.take m l ++ list.take n (list.drop m l),
  { rw list.take_append_drop },
  rw [list.take_append_eq_append_take, list.take_all_of_le,
    list.append_right_inj], swap,
  { transitivity m,
    { apply list.length_take_le },
    { simp }},
  simp only [list.take_eq_take, list.length_take, list.length_drop],
  generalize : l.length = k, by_cases h : m ≤ k,
  { simp [min_eq_left_iff.mpr h] },
  { push_neg at h, simp [nat.sub_eq_zero_of_le (le_of_lt h)] },
end

private lemma list.take_one_drop_eq_of_lt_length {l : list α} {n : ℕ}
  (h : n < l.length) : (l.drop n).take 1 = [l.nth_le n h] :=
begin
  induction l with x l ih generalizing n,
  { cases h },
  { by_cases h₁ : l = [],
    { subst h₁, rw list.nth_le_singleton, simp at h, subst h, simp },
    have h₂ := h, rw [list.length_cons, nat.lt_succ_iff, le_iff_eq_or_lt] at h₂,
    cases n, { simp }, rw [list.drop, list.nth_le], apply ih },
end

lemma take_join_of_lt {L : list (list α)} {n : ℕ} (notall : n < L.join.length) :
  ∃ m k : ℕ, ∃ mlt : m < L.length, k < (L.nth_le m mlt).length ∧
    L.join.take n = (L.take m).join ++ (L.nth_le m mlt).take k :=
begin
  generalize hX : L.length = X, symmetry' at hX,
  generalize hN : L.join.length = N, symmetry' at hN,
  rw ←hN at notall, have hl : L ≠ [],
  { rintro rfl, rw hN at notall, cases notall },
  generalize hP : (λ (m : ℕ), (L.take m).join.length ≤ n) = P, symmetry' at hP,
  generalize hm : get_max P = m, symmetry' at hm,
  generalize hk : n - (L.take m).join.length = k, symmetry' at hk,
  have hP0 : P 0, { rw hP, simp },
  have hPX : ∀ (r : ℕ), X ≤ r → ¬P r,
  { rintro r hr, rw hP, push_neg, convert notall, rw hX at hr,
    rw [hN, list.take_all_of_le hr] },
  obtain ⟨hm₁, hm₂⟩ := nat_get_max_spec ⟨0, hP0⟩ ⟨X, hPX⟩, rw ←hm at hm₁ hm₂,
  have hm₃ : ¬P m.succ := hm₂ _ (nat.lt_succ_self m),
  refine ⟨m, k, _, _, _⟩,
  { rw ←hX, by_contra' hx, cases hPX m hx hm₁ },
  { generalize_proofs h₁, rw hP at hm₁ hm₃, push_neg at hm₃,
    by_contra' hk₁, obtain ⟨k, rfl⟩ := nat.exists_eq_add_of_le hk₁,
    replace hk := congr_arg (λ (x : ℕ), x + (L.take m).join.length) hk,
    dsimp at hk, rw nat.sub_add_cancel hm₁ at hk, rw ←hk at hm₃,
    contrapose! hm₃, rw [add_comm, ←add_assoc], convert le_self_add,
    simp [nat.succ_eq_add_one, list.take_add, list.join_append, list.length_append],
    rw [list.take_one_drop_eq_of_lt_length, map_singleton, sum_singleton] },
  conv { to_lhs, rw [←list.take_append_drop m.succ L, list.join_append] },
  have hn₁ : (L.take m).join.length ≤ n, { rwa hP at hm₁ },
  have hn₂ : n < (L.take m.succ).join.length,
  { rw hP at hm₃, push_neg at hm₃, exact hm₃ },
  rw list.take_append_of_le_length (le_of_lt hn₂),
  change m.succ with m + 1, have hmX : m < X,
  { by_contra' hx, exact hPX m hx hm₁ },
  rw [list.take_add, list.join_append, list.take_one_drop_eq_of_lt_length,
    list.join, list.join, list.append_nil], swap, { rwa ←hX },
  have : n = (list.take m L).join.length + k,
  { rw hk, exact (nat.add_sub_of_le hn₁).symm },
  subst this, rw list.take_append,
end

end list


-- Integration over the complex closed disk

import measure_theory.function.jacobian

import measure
import prod
import simple
import tactics

open complex (abs arg exp I)
open linear_map (to_matrix_apply)
open measure_theory
open metric (ball closed_ball sphere)
open real (cos sin)
open set (Icc Ioc)
open_locale real
noncomputable theory

section real_circle_map

-- circle_map as a map from ℝ² → ℝ²
def real_circle_map (c : ℂ) (x : ℝ × ℝ) : ℝ × ℝ := ⟨c.re + x.1 * cos x.2, c.im + x.1 * sin x.2⟩
def real_circle_map_eq_circle_map (c : ℂ) (x : ℝ × ℝ)
    : real_circle_map c x = complex.equiv_real_prod (circle_map c x.1 x.2) :=
  by simp [real_circle_map, circle_map]

-- The derivative of real_circle_map
def d1 := continuous_linear_map.fst ℝ ℝ ℝ
def d2 := continuous_linear_map.snd ℝ ℝ ℝ
def rcm_deriv (x : ℝ × ℝ) : ℝ × ℝ →L[ℝ] ℝ × ℝ :=
  (0 + (x.1 • (-sin x.2) • d2 + cos x.2 • d1)).prod (0 + (x.1 • cos x.2 • d2 + sin x.2 • d1))
lemma real_circle_map.fderiv {c : ℂ} {x : ℝ × ℝ} : has_fderiv_at (λ x, real_circle_map c x) (rcm_deriv x) x := begin
  simp_rw [real_circle_map],
  apply_rules [has_fderiv_at_const, has_fderiv_at_fst, has_fderiv_at_snd, has_fderiv_at.cos, has_fderiv_at.sin,
               has_fderiv_at.add, has_fderiv_at.mul, has_fderiv_at.prod],
end

-- det rcm_deriv
def rcm_matrix (x : ℝ × ℝ) := linear_map.to_matrix (basis.fin_two_prod ℝ) (basis.fin_two_prod ℝ) (rcm_deriv x)
lemma rcm00 (x : ℝ × ℝ) : rcm_matrix x 0 0 = cos x.2 := by simp [rcm_matrix, rcm_deriv, to_matrix_apply, d1, d2]
lemma rcm01 (x : ℝ × ℝ) : rcm_matrix x 0 1 = -x.1 * sin x.2 := by simp [rcm_matrix, rcm_deriv, to_matrix_apply, d1, d2]
lemma rcm10 (x : ℝ × ℝ) : rcm_matrix x 1 0 = sin x.2 := by simp [rcm_matrix, rcm_deriv, to_matrix_apply, d1, d2]
lemma rcm11 (x : ℝ × ℝ) : rcm_matrix x 1 1 = x.1 * cos x.2 := by simp [rcm_matrix, rcm_deriv, to_matrix_apply, d1, d2]
lemma rcm_deriv.det (x : ℝ × ℝ) : (rcm_deriv x).det = x.1 := begin
  rw [continuous_linear_map.det, ←linear_map.det_to_matrix (basis.fin_two_prod ℝ), ←rcm_matrix],
  rw [matrix.det_fin_two, rcm00, rcm01, rcm10, rcm11], ring_nf,
  calc x.1 * cos x.2^2 + sin x.2^2 * x.1 = x.1 * (cos x.2^2 + sin x.2^2) : by ring
  ... = x.1 : by simp,
end

end real_circle_map

@[simp] lemma metric.sphere_eq_empty {S : Type} [is_R_or_C S] {c : S} {r : ℝ}
    : sphere c r = ∅ ↔ r < 0 := begin
  constructor, {
    intros rp, contrapose rp, simp at rp,
    refine set.nonempty.ne_empty ⟨c+r, _⟩,
    simpa [is_R_or_C.norm_of_real],
  }, {
    intros n, contrapose n,
    rw ←set.not_nonempty_iff_eq_empty at n, simp at n ⊢, assumption,
  },
end

-- range (circle_map c r _) = sphere c r even when restricted to set.Ioc
lemma circle_map_Ioc {c z : ℂ} {r : ℝ} (zs : z ∈ sphere c r) : ∃ t, t ∈ Ioc 0 (2*π) ∧ z = circle_map c r t := begin
  by_cases rp : r < 0, { simp [metric.sphere_eq_empty.mpr rp] at zs, finish },
  simp at rp,
  rw [←abs_of_nonneg rp, ←range_circle_map, set.mem_range] at zs,
  rcases zs with ⟨t,ht⟩,
  generalize ha : 2*π = a,
  have ap : a > 0, { rw ←ha, exact real.two_pi_pos },
  set s := t + a - a*⌈t/a⌉,
  existsi s, constructor, {
    simp, constructor, {
      calc a*⌈t/a⌉ < a*(t/a+1) : by bound [(mul_lt_mul_left ap).mpr, int.ceil_lt_add_one]
      ... = a/a*t + a : by ring
      ... = t + a : by field_simp [ne_of_gt ap]
    }, {
      flip_ineq,
      calc a + a*⌈t/a⌉ ≥ a + a*(t/a) : by bound [int.le_ceil]
      ... = a/a*t + a : by ring
      ... = t + a : by field_simp [ne_of_gt ap]
    }
  }, {
    rw [←ht, circle_map], simp,
    rw [mul_sub_right_distrib, right_distrib, complex.exp_sub, complex.exp_add],
    rw [mul_comm _ ↑⌈_⌉, mul_assoc, complex.exp_int_mul, ←ha], simp,
  },
end

section fubini_helper

-- The square that we'll map onto the ball
def square (r : ℝ) : set (ℝ × ℝ) := set.Ioc 0 r ×ˢ (Ioc 0 (2*π))

lemma square.rp {r : ℝ} {x : ℝ × ℝ} : x ∈ square r → x.1 > 0 := begin simp [square], finish end
lemma measurable.square {r : ℝ} : measurable_set (square r) := by apply_rules [measurable_set.prod, measurable_set_Ioc]

lemma square_eq {c : ℂ} {r : ℝ}
    : complex.measurable_equiv_real_prod.symm ⁻¹' (closed_ball c r \ {c}) = real_circle_map c '' square r := begin
  rw ←measurable_equiv.image_eq_preimage,
  have e : real_circle_map c = (λ x : ℝ × ℝ, complex.measurable_equiv_real_prod (circle_map c x.1 x.2)), {
    funext, rw real_circle_map_eq_circle_map, simp [complex.measurable_equiv_real_prod],
  },
  have i : (λ x : ℝ × ℝ, circle_map c x.1 x.2) '' square r = closed_ball c r \ {c}, {
    apply set.ext, intro z, rw set.mem_image, constructor, {
      intro gp, rcases gp with ⟨⟨s,t⟩,ss,tz⟩, simp at tz, simp [square] at ss, rw ←tz, simp,
      exact ⟨by simp [circle_map, abs_of_pos ss.1.1, ss.1.2], ne_of_gt ss.1.1⟩,
    }, {
      intro zr, simp at zr, rw dist_comm at zr,
      have zz : z ∈ sphere c (dist c z), { simp [complex.dist_eq], rw ←complex.abs_neg, simp },
      rcases circle_map_Ioc zz with ⟨t,ts,tz⟩,
      existsi (⟨dist c z, t⟩ : ℝ × ℝ), simp [dist_nonneg, zr, ne.symm zr.2, square, ts, tz.symm],
    },
  },
  rw [e, set.image_comp _ _ (square r), i],
end

-- exp it = cos t + i sin t
lemma exp_of_im (t : ℝ) : exp (t * I) = cos t + sin t*I :=
  by simp [complex.ext_iff, complex.cos_of_real_re, complex.sin_of_real_re]
lemma complex.cos_eq_cos (t : ℝ) : complex.cos t = ↑(real.cos t) := by simp
lemma complex.sin_eq_sin (t : ℝ) : complex.sin t = ↑(real.sin t) := by simp

-- arg e^(it)
lemma arg_exp_of_im (t : ℝ) : ∃ n : ℤ, arg (exp (t * I)) = t - 2*π*n := begin
  generalize hn : ⌈t/(2*π) - 1/2⌉ = n, existsi n,
  have en : exp (2*π*n*I) = 1, { rw [mul_comm _ ↑n, mul_assoc, complex.exp_int_mul], simp [complex.exp_neg] },
  have e : exp (t*I) = exp (↑(t - 2*π*n)*I) := by simp [mul_sub_right_distrib, complex.exp_sub, en],
  have ts : t - 2*π*n ∈ Ioc (-π) π, {
    simp, constructor, {
      have h : ↑n < t*(2*π)⁻¹ - 1/2 + 1, { rw ←hn, exact int.ceil_lt_add_one _ },
      calc 2*π*↑n < 2*π*(t*(2*π)⁻¹ - 1/2 + 1) : by bound [(mul_lt_mul_left real.two_pi_pos).mpr]
      ... = π + (2*π)*(2*π)⁻¹*t : by ring
      ... = π + t : by field_simp [ne_of_gt real.two_pi_pos]
    }, {
      flip_ineq,
      have h : ↑n ≥ t*(2*π)⁻¹ - 1/2, { rw ←hn, exact int.le_ceil _ },
      calc π + 2*π*↑n ≥ π + 2*π*(t*(2*π)⁻¹ - 1/2) : by bound [real.two_pi_pos]
      ...  = (2*π)*(2*π)⁻¹*t : by ring
      ... = t : by field_simp [ne_of_gt real.two_pi_pos]
    }
  },
  rw [e, exp_of_im, ←complex.cos_eq_cos, ←complex.sin_eq_sin, complex.arg_cos_add_sin_mul_I ts],
end

-- real_circle_map is injective on the square
lemma rcm_inj {c : ℂ} {r : ℝ} : set.inj_on (real_circle_map c) (square r) := begin
  intros x xs y ys e, simp [square] at xs ys,
  simp_rw [real_circle_map_eq_circle_map, equiv.apply_eq_iff_eq] at e,
  simp_rw [circle_map] at e, simp at e,
  have re : abs (↑(x.1) * exp (x.2*I)) = abs (↑(y.1) * exp (y.2*I)) := by rw e,
  simp [abs_of_pos xs.1.1, abs_of_pos ys.1.1] at re,
  have ae : arg (↑(x.1) * exp (x.2*I)) = arg (↑(y.1) * exp (y.2*I)) := by rw e,
  simp [complex.arg_real_mul _ xs.1.1, complex.arg_real_mul _ ys.1.1] at ae,
  rcases arg_exp_of_im x.2 with ⟨nx,hx⟩,
  rcases arg_exp_of_im y.2 with ⟨ny,h⟩,
  rw [←ae, hx] at h, clear e ae hx,
  have n0 : 2*π*(nx - ny) < (2*π)*1 := by linarith,
  have n1 : (2*π)*-1 < 2*π*(nx - ny) := by linarith,
  have hn : (nx : ℝ) - ny = ↑(nx - ny) := by simp,
  have hn1 : (-1 : ℝ) = ↑(-1 : ℤ) := by simp,
  have h1 : (1 : ℝ) = ↑(1 : ℤ) := by simp,
  rw [mul_lt_mul_left real.two_pi_pos, hn] at n0 n1,
  rw hn1 at n1, rw h1 at n0, rw int.cast_lt at n0 n1,
  have n : nx = ny := by linarith,
  rw n at h,
  have i : x.2 = y.2 := by linarith,
  have g : (x.1,x.2) = (y.1,y.2) := by rw [re, i],
  simp at g, exact g,
end

end fubini_helper

-- Inverse lemma for fubini_ball
lemma measurable_symm_equiv_inverse {z : ℂ} : complex.measurable_equiv_real_prod.symm (complex.equiv_real_prod z) = z := begin
  simp, rw [complex.measurable_equiv_real_prod, homeomorph.to_measurable_equiv_symm_coe], simp, apply complex.ext, simp, simp,
end

-- circle_map is continuous on ℝ × ℝ
lemma continuous_circle_map_full {c : ℂ} : continuous (λ x : ℝ × ℝ, circle_map c x.1 x.2) := by continuity

-- If x.to_real = y is positive, then x = of_real y
lemma invert_to_real {x : ennreal} {y : ℝ} (yp : y > 0) : x.to_real = y → x = ennreal.of_real y := begin
  intro h, rw ←h, refine (ennreal.of_real_to_real _).symm,
  contrapose yp, simp at yp, simp [yp] at h, simp [←h],
end

-- Integration over a complex ball using polar coordinates
lemma fubini_ball {E : Type} [normed_add_comm_group E] [normed_space ℝ E] [complete_space E]
    {f : ℂ → E} {c : ℂ} {r : ℝ} (fc : continuous_on f (closed_ball c r))
    : ∫ z in closed_ball c r, f z = ∫ s in set.Ioc 0 r, s • ∫ t in Ioc 0 (2*π), f (circle_map c s t) := begin
  have center : closed_ball c r =ᵐ[volume] (closed_ball c r \ {c} : set ℂ) := ae_minus_point,
  rw measure_theory.set_integral_congr_set_ae center, clear center,
  have im := measure_preserving.symm _ complex.volume_preserving_equiv_real_prod,
  rw ←measure_preserving.set_integral_preimage_emb im complex.measurable_equiv_real_prod.symm.measurable_embedding f _,
  clear im,
  rw square_eq,
  have dc : ∀ x, x ∈ square r → has_fderiv_within_at (real_circle_map c) (rcm_deriv x) (square r) x :=
    λ _ _, real_circle_map.fderiv.has_fderiv_within_at,
  rw integral_image_eq_integral_abs_det_fderiv_smul volume measurable.square dc rcm_inj, clear dc,
  simp_rw rcm_deriv.det,
  simp_rw real_circle_map_eq_circle_map,
  simp_rw measurable_symm_equiv_inverse,
  have e : ∀ x : ℝ × ℝ, x ∈ square r → |x.1| • f (circle_map c x.1 x.2) = x.1 • f (circle_map c x.1 x.2), {
    intros x xs, rw abs_of_pos (square.rp xs),
  },
  rw measure_theory.set_integral_congr measurable.square e, clear e, simp,
  rw [square, measure.volume_eq_prod, measure_theory.set_integral_prod],
  simp [integral_smul],
  have fi : integrable_on (λ x : ℝ × ℝ, x.1 • f (circle_map c x.1 x.2)) (Icc 0 r ×ˢ Icc 0 (2*π)), {
    apply continuous_on.integrable_on_compact,
    exact is_compact.prod is_compact_Icc is_compact_Icc,
    apply continuous_on.smul,
    exact continuous_fst.continuous_on,
    apply fc.comp (continuous_circle_map_full.continuous_on),
    intros x xs, simp only, simp at xs,
    apply metric.closed_ball_subset_closed_ball xs.2.1,
    apply circle_map_mem_closed_ball _ xs.1.1,
  },
  exact fi.mono_set (set.prod_mono set.Ioc_subset_Icc_self set.Ioc_subset_Icc_self),
end

-- The volume of complex closed balls
lemma complex.volume_closed_ball' {c : ℂ} {r : ℝ} (rp : r ≥ 0) : (volume (closed_ball c r)).to_real = π * r^2 := begin
  have c : continuous_on (λ _ : ℂ, (1 : ℝ)) (closed_ball c r) := continuous_on_const,
  have f := fubini_ball c, clear c,
  simp [ennreal.to_real_of_real (le_of_lt real.two_pi_pos), ←interval_integral.integral_of_le rp] at f,
  have e : r ^ 2 / 2 * (2 * π) = π * r^2 := by ring, rwa e at f,
end

lemma complex.volume_closed_ball {c : ℂ} {r : ℝ} (rp : r ≥ 0) : volume (closed_ball c r) = ennreal.of_real (π * r^2) := begin
  by_cases rp' : r > 0, {
    exact invert_to_real (by bound [real.pi_pos]) (complex.volume_closed_ball' rp),
  }, {
    simp at rp', simp [le_antisymm rp' rp],
  },
end

-- The volume of complex open balls
lemma complex.volume_ball' {c : ℂ} {r : ℝ} (rp : r ≥ 0) : (volume (ball c r)).to_real = π * r^2 := begin
  by_cases r0 : r = 0, simp [r0],
  have rs := lt_of_le_of_ne rp (ne.symm r0),
  have hi' : volume (ball c r) ≤ volume (closed_ball c r) := measure_mono metric.ball_subset_closed_ball,
  have hi := ennreal.to_real_mono (by simp [complex.volume_closed_ball rp]) hi',
  have lo : (volume (ball c r)).to_real ≥ (volume (closed_ball c r)).to_real, {
    simp [complex.volume_closed_ball' rp],
    apply @le_of_forall_ge_of_dense _ _ _ (π * r^2) (volume (ball c r)).to_real,
    intros a ar, by_cases an : a < 0, exact trans (le_of_lt an) (by simp), simp at an,
    set s := real.sqrt (a / π),
    have πp := real.pi_pos,
    have sp : s ≥ 0 := real.sqrt_nonneg _,
    have sr : s < r, {
      calc s = real.sqrt (a / π) : rfl
      ... < real.sqrt (π * r^2 / π) : real.sqrt_lt_sqrt (by bound) ((div_lt_div_right (by bound)).mpr (by bound))
      ... = real.sqrt (π / π * r^2) : by ring_nf
      ... = real.sqrt (r^2) : by field_simp [ne_of_gt real.pi_pos]
      ... = r : real.sqrt_sq (by bound)
    },
    have e : a = (volume (closed_ball c s)).to_real, {
      rw complex.volume_closed_ball' sp, symmetry,
      have app : a / π ≥ 0 := by bound,
      calc π * s^2 = π * real.sqrt (a / π)^2 : rfl
      ... = π * (a / π) : by rw real.sq_sqrt app
      ... = π / π * a : by ring
      ... = a : by field_simp [ne_of_gt real.pi_pos]
    },
    rw e, apply ennreal.to_real_mono, {
      rw ←lt_top_iff_ne_top, refine lt_of_le_of_lt hi' _, simp [complex.volume_closed_ball rp],
    }, {
      apply measure_mono (metric.closed_ball_subset_ball sr),
    }
  },
  have e := le_antisymm hi lo, rw e,
  exact complex.volume_closed_ball' rp,
end

lemma complex.volume_ball {c : ℂ} {r : ℝ} (rp : r ≥ 0) : volume (ball c r) = ennreal.of_real (π * r^2) := begin
  by_cases rp' : r > 0, {
    exact invert_to_real (by bound [real.pi_pos]) (complex.volume_ball' rp),
  }, {
    simp at rp', simp [le_antisymm rp' rp],
  },
end

-- closed_balls are nice
lemma nice_volume.closed_ball (c : ℂ) {r : ℝ} (rp : r > 0) : nice_volume (closed_ball c r) := {
  measurable := measurable_set_closed_ball,
  finite := by simp [complex.volume_closed_ball (le_of_lt rp)],
  pos := begin simp [complex.volume_closed_ball (le_of_lt rp)], bound [real.pi_pos], end,
}

-- closed_balls have local volume
lemma local_volume.closed_ball {c : ℂ} {r : ℝ} (rp : r > 0) : local_volume_set (closed_ball c r) := begin
  apply local_volume.closure_interior,
  intros x r rp, rw complex.volume_ball (le_of_lt rp), simp, bound [real.pi_pos],
  have rz := ne_of_gt rp,
  simp [interior_closed_ball c rz, closure_ball c rz],
end