/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov

! This file was ported from Lean 3 source module topology.metric_space.antilipschitz
! leanprover-community/mathlib commit f47581155c818e6361af4e4fda60d27d020c226b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.MetricSpace.Lipschitz
import Mathbin.Topology.UniformSpace.CompleteSeparated

/-!
# Antilipschitz functions

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

We say that a map `f : α → β` between two (extended) metric spaces is
`antilipschitz_with K`, `K ≥ 0`, if for all `x, y` we have `edist x y ≤ K * edist (f x) (f y)`.
For a metric space, the latter inequality is equivalent to `dist x y ≤ K * dist (f x) (f y)`.

## Implementation notes

The parameter `K` has type `ℝ≥0`. This way we avoid conjuction in the definition and have
coercions both to `ℝ` and `ℝ≥0∞`. We do not require `0 < K` in the definition, mostly because
we do not have a `posreal` type.
-/

variable {α : Type _} {β : Type _} {γ : Type _}

open NNReal ENNReal uniformity

open Set Filter Bornology

#print AntilipschitzWith /-
/-- We say that `f : α → β` is `antilipschitz_with K` if for any two points `x`, `y` we have
`edist x y ≤ K * edist (f x) (f y)`. -/
def AntilipschitzWith [PseudoEMetricSpace α] [PseudoEMetricSpace β] (K : ℝ≥0) (f : α → β) :=
  ∀ x y, edist x y ≤ K * edist (f x) (f y)
#align antilipschitz_with AntilipschitzWith
-/

/- warning: antilipschitz_with.edist_lt_top -> AntilipschitzWith.edist_lt_top is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) -> (forall (x : α) (y : α), LT.lt.{0} ENNReal (Preorder.toLT.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder))))) (EDist.edist.{u1} α (PseudoEMetricSpace.toHasEdist.{u1} α _inst_1) x y) (Top.top.{0} ENNReal (CompleteLattice.toHasTop.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) -> (forall (x : α) (y : α), LT.lt.{0} ENNReal (Preorder.toLT.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal))))) (EDist.edist.{u2} α (PseudoEMetricSpace.toEDist.{u2} α _inst_1) x y) (Top.top.{0} ENNReal (CompleteLattice.toTop.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal))))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.edist_lt_top AntilipschitzWith.edist_lt_topₓ'. -/
theorem AntilipschitzWith.edist_lt_top [PseudoEMetricSpace α] [PseudoMetricSpace β] {K : ℝ≥0}
    {f : α → β} (h : AntilipschitzWith K f) (x y : α) : edist x y < ⊤ :=
  (h x y).trans_lt <| ENNReal.mul_lt_top ENNReal.coe_ne_top (edist_ne_top _ _)
#align antilipschitz_with.edist_lt_top AntilipschitzWith.edist_lt_top

/- warning: antilipschitz_with.edist_ne_top -> AntilipschitzWith.edist_ne_top is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) -> (forall (x : α) (y : α), Ne.{1} ENNReal (EDist.edist.{u1} α (PseudoEMetricSpace.toHasEdist.{u1} α _inst_1) x y) (Top.top.{0} ENNReal (CompleteLattice.toHasTop.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) -> (forall (x : α) (y : α), Ne.{1} ENNReal (EDist.edist.{u2} α (PseudoEMetricSpace.toEDist.{u2} α _inst_1) x y) (Top.top.{0} ENNReal (CompleteLattice.toTop.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal))))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.edist_ne_top AntilipschitzWith.edist_ne_topₓ'. -/
theorem AntilipschitzWith.edist_ne_top [PseudoEMetricSpace α] [PseudoMetricSpace β] {K : ℝ≥0}
    {f : α → β} (h : AntilipschitzWith K f) (x y : α) : edist x y ≠ ⊤ :=
  (h.edist_lt_top x y).Ne
#align antilipschitz_with.edist_ne_top AntilipschitzWith.edist_ne_top

section Metric

variable [PseudoMetricSpace α] [PseudoMetricSpace β] {K : ℝ≥0} {f : α → β}

/- warning: antilipschitz_with_iff_le_mul_nndist -> antilipschitzWith_iff_le_mul_nndist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, Iff (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) (forall (x : α) (y : α), LE.le.{0} NNReal (Preorder.toLE.{0} NNReal (PartialOrder.toPreorder.{0} NNReal (OrderedCancelAddCommMonoid.toPartialOrder.{0} NNReal (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} NNReal NNReal.strictOrderedSemiring)))) (NNDist.nndist.{u1} α (PseudoMetricSpace.toNNDist.{u1} α _inst_1) x y) (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (Distrib.toHasMul.{0} NNReal (NonUnitalNonAssocSemiring.toDistrib.{0} NNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} NNReal (Semiring.toNonAssocSemiring.{0} NNReal NNReal.semiring))))) K (NNDist.nndist.{u2} β (PseudoMetricSpace.toNNDist.{u2} β _inst_2) (f x) (f y))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, Iff (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) (forall (x : α) (y : α), LE.le.{0} NNReal (Preorder.toLE.{0} NNReal (PartialOrder.toPreorder.{0} NNReal (StrictOrderedSemiring.toPartialOrder.{0} NNReal instNNRealStrictOrderedSemiring))) (NNDist.nndist.{u2} α (PseudoMetricSpace.toNNDist.{u2} α _inst_1) x y) (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (CanonicallyOrderedCommSemiring.toMul.{0} NNReal instNNRealCanonicallyOrderedCommSemiring)) K (NNDist.nndist.{u1} β (PseudoMetricSpace.toNNDist.{u1} β _inst_2) (f x) (f y))))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with_iff_le_mul_nndist antilipschitzWith_iff_le_mul_nndistₓ'. -/
theorem antilipschitzWith_iff_le_mul_nndist :
    AntilipschitzWith K f ↔ ∀ x y, nndist x y ≤ K * nndist (f x) (f y) :=
  by
  simp only [AntilipschitzWith, edist_nndist]
  norm_cast
#align antilipschitz_with_iff_le_mul_nndist antilipschitzWith_iff_le_mul_nndist

/- warning: antilipschitz_with.le_mul_nndist -> AntilipschitzWith.le_mul_nndist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) -> (forall (x : α) (y : α), LE.le.{0} NNReal (Preorder.toLE.{0} NNReal (PartialOrder.toPreorder.{0} NNReal (OrderedCancelAddCommMonoid.toPartialOrder.{0} NNReal (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} NNReal NNReal.strictOrderedSemiring)))) (NNDist.nndist.{u1} α (PseudoMetricSpace.toNNDist.{u1} α _inst_1) x y) (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (Distrib.toHasMul.{0} NNReal (NonUnitalNonAssocSemiring.toDistrib.{0} NNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} NNReal (Semiring.toNonAssocSemiring.{0} NNReal NNReal.semiring))))) K (NNDist.nndist.{u2} β (PseudoMetricSpace.toNNDist.{u2} β _inst_2) (f x) (f y))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) -> (forall (x : α) (y : α), LE.le.{0} NNReal (Preorder.toLE.{0} NNReal (PartialOrder.toPreorder.{0} NNReal (StrictOrderedSemiring.toPartialOrder.{0} NNReal instNNRealStrictOrderedSemiring))) (NNDist.nndist.{u2} α (PseudoMetricSpace.toNNDist.{u2} α _inst_1) x y) (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (CanonicallyOrderedCommSemiring.toMul.{0} NNReal instNNRealCanonicallyOrderedCommSemiring)) K (NNDist.nndist.{u1} β (PseudoMetricSpace.toNNDist.{u1} β _inst_2) (f x) (f y))))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.le_mul_nndist AntilipschitzWith.le_mul_nndistₓ'. -/
/- warning: antilipschitz_with.of_le_mul_nndist -> AntilipschitzWith.of_le_mul_nndist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (forall (x : α) (y : α), LE.le.{0} NNReal (Preorder.toLE.{0} NNReal (PartialOrder.toPreorder.{0} NNReal (OrderedCancelAddCommMonoid.toPartialOrder.{0} NNReal (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} NNReal NNReal.strictOrderedSemiring)))) (NNDist.nndist.{u1} α (PseudoMetricSpace.toNNDist.{u1} α _inst_1) x y) (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (Distrib.toHasMul.{0} NNReal (NonUnitalNonAssocSemiring.toDistrib.{0} NNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} NNReal (Semiring.toNonAssocSemiring.{0} NNReal NNReal.semiring))))) K (NNDist.nndist.{u2} β (PseudoMetricSpace.toNNDist.{u2} β _inst_2) (f x) (f y)))) -> (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (forall (x : α) (y : α), LE.le.{0} NNReal (Preorder.toLE.{0} NNReal (PartialOrder.toPreorder.{0} NNReal (StrictOrderedSemiring.toPartialOrder.{0} NNReal instNNRealStrictOrderedSemiring))) (NNDist.nndist.{u2} α (PseudoMetricSpace.toNNDist.{u2} α _inst_1) x y) (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (CanonicallyOrderedCommSemiring.toMul.{0} NNReal instNNRealCanonicallyOrderedCommSemiring)) K (NNDist.nndist.{u1} β (PseudoMetricSpace.toNNDist.{u1} β _inst_2) (f x) (f y)))) -> (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f)
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.of_le_mul_nndist AntilipschitzWith.of_le_mul_nndistₓ'. -/
alias antilipschitzWith_iff_le_mul_nndist ↔
  AntilipschitzWith.le_mul_nndist AntilipschitzWith.of_le_mul_nndist
#align antilipschitz_with.le_mul_nndist AntilipschitzWith.le_mul_nndist
#align antilipschitz_with.of_le_mul_nndist AntilipschitzWith.of_le_mul_nndist

/- warning: antilipschitz_with_iff_le_mul_dist -> antilipschitzWith_iff_le_mul_dist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, Iff (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) (forall (x : α) (y : α), LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) x y) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.hasMul) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) NNReal Real (HasLiftT.mk.{1, 1} NNReal Real (CoeTCₓ.coe.{1, 1} NNReal Real (coeBase.{1, 1} NNReal Real NNReal.Real.hasCoe))) K) (Dist.dist.{u2} β (PseudoMetricSpace.toHasDist.{u2} β _inst_2) (f x) (f y))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, Iff (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) (forall (x : α) (y : α), LE.le.{0} Real Real.instLEReal (Dist.dist.{u2} α (PseudoMetricSpace.toDist.{u2} α _inst_1) x y) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.instMulReal) (NNReal.toReal K) (Dist.dist.{u1} β (PseudoMetricSpace.toDist.{u1} β _inst_2) (f x) (f y))))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with_iff_le_mul_dist antilipschitzWith_iff_le_mul_distₓ'. -/
theorem antilipschitzWith_iff_le_mul_dist :
    AntilipschitzWith K f ↔ ∀ x y, dist x y ≤ K * dist (f x) (f y) :=
  by
  simp only [antilipschitzWith_iff_le_mul_nndist, dist_nndist]
  norm_cast
#align antilipschitz_with_iff_le_mul_dist antilipschitzWith_iff_le_mul_dist

/- warning: antilipschitz_with.le_mul_dist -> AntilipschitzWith.le_mul_dist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) -> (forall (x : α) (y : α), LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) x y) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.hasMul) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) NNReal Real (HasLiftT.mk.{1, 1} NNReal Real (CoeTCₓ.coe.{1, 1} NNReal Real (coeBase.{1, 1} NNReal Real NNReal.Real.hasCoe))) K) (Dist.dist.{u2} β (PseudoMetricSpace.toHasDist.{u2} β _inst_2) (f x) (f y))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) -> (forall (x : α) (y : α), LE.le.{0} Real Real.instLEReal (Dist.dist.{u2} α (PseudoMetricSpace.toDist.{u2} α _inst_1) x y) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.instMulReal) (NNReal.toReal K) (Dist.dist.{u1} β (PseudoMetricSpace.toDist.{u1} β _inst_2) (f x) (f y))))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.le_mul_dist AntilipschitzWith.le_mul_distₓ'. -/
/- warning: antilipschitz_with.of_le_mul_dist -> AntilipschitzWith.of_le_mul_dist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (forall (x : α) (y : α), LE.le.{0} Real Real.hasLe (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) x y) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.hasMul) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) NNReal Real (HasLiftT.mk.{1, 1} NNReal Real (CoeTCₓ.coe.{1, 1} NNReal Real (coeBase.{1, 1} NNReal Real NNReal.Real.hasCoe))) K) (Dist.dist.{u2} β (PseudoMetricSpace.toHasDist.{u2} β _inst_2) (f x) (f y)))) -> (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (forall (x : α) (y : α), LE.le.{0} Real Real.instLEReal (Dist.dist.{u2} α (PseudoMetricSpace.toDist.{u2} α _inst_1) x y) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.instMulReal) (NNReal.toReal K) (Dist.dist.{u1} β (PseudoMetricSpace.toDist.{u1} β _inst_2) (f x) (f y)))) -> (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f)
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.of_le_mul_dist AntilipschitzWith.of_le_mul_distₓ'. -/
alias antilipschitzWith_iff_le_mul_dist ↔
  AntilipschitzWith.le_mul_dist AntilipschitzWith.of_le_mul_dist
#align antilipschitz_with.le_mul_dist AntilipschitzWith.le_mul_dist
#align antilipschitz_with.of_le_mul_dist AntilipschitzWith.of_le_mul_dist

namespace AntilipschitzWith

/- warning: antilipschitz_with.mul_le_nndist -> AntilipschitzWith.mul_le_nndist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) -> (forall (x : α) (y : α), LE.le.{0} NNReal (Preorder.toLE.{0} NNReal (PartialOrder.toPreorder.{0} NNReal (OrderedCancelAddCommMonoid.toPartialOrder.{0} NNReal (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} NNReal NNReal.strictOrderedSemiring)))) (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (Distrib.toHasMul.{0} NNReal (NonUnitalNonAssocSemiring.toDistrib.{0} NNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} NNReal (Semiring.toNonAssocSemiring.{0} NNReal NNReal.semiring))))) (Inv.inv.{0} NNReal (DivInvMonoid.toHasInv.{0} NNReal (GroupWithZero.toDivInvMonoid.{0} NNReal (DivisionSemiring.toGroupWithZero.{0} NNReal (Semifield.toDivisionSemiring.{0} NNReal (LinearOrderedSemifield.toSemifield.{0} NNReal (CanonicallyLinearOrderedSemifield.toLinearOrderedSemifield.{0} NNReal NNReal.canonicallyLinearOrderedSemifield)))))) K) (NNDist.nndist.{u1} α (PseudoMetricSpace.toNNDist.{u1} α _inst_1) x y)) (NNDist.nndist.{u2} β (PseudoMetricSpace.toNNDist.{u2} β _inst_2) (f x) (f y)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) -> (forall (x : α) (y : α), LE.le.{0} NNReal (Preorder.toLE.{0} NNReal (PartialOrder.toPreorder.{0} NNReal (StrictOrderedSemiring.toPartialOrder.{0} NNReal instNNRealStrictOrderedSemiring))) (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (CanonicallyOrderedCommSemiring.toMul.{0} NNReal instNNRealCanonicallyOrderedCommSemiring)) (Inv.inv.{0} NNReal (CanonicallyLinearOrderedSemifield.toInv.{0} NNReal NNReal.instCanonicallyLinearOrderedSemifieldNNReal) K) (NNDist.nndist.{u2} α (PseudoMetricSpace.toNNDist.{u2} α _inst_1) x y)) (NNDist.nndist.{u1} β (PseudoMetricSpace.toNNDist.{u1} β _inst_2) (f x) (f y)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.mul_le_nndist AntilipschitzWith.mul_le_nndistₓ'. -/
theorem mul_le_nndist (hf : AntilipschitzWith K f) (x y : α) :
    K⁻¹ * nndist x y ≤ nndist (f x) (f y) := by
  simpa only [div_eq_inv_mul] using NNReal.div_le_of_le_mul' (hf.le_mul_nndist x y)
#align antilipschitz_with.mul_le_nndist AntilipschitzWith.mul_le_nndist

/- warning: antilipschitz_with.mul_le_dist -> AntilipschitzWith.mul_le_dist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) -> (forall (x : α) (y : α), LE.le.{0} Real Real.hasLe (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.hasMul) (Inv.inv.{0} Real Real.hasInv ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) NNReal Real (HasLiftT.mk.{1, 1} NNReal Real (CoeTCₓ.coe.{1, 1} NNReal Real (coeBase.{1, 1} NNReal Real NNReal.Real.hasCoe))) K)) (Dist.dist.{u1} α (PseudoMetricSpace.toHasDist.{u1} α _inst_1) x y)) (Dist.dist.{u2} β (PseudoMetricSpace.toHasDist.{u2} β _inst_2) (f x) (f y)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) -> (forall (x : α) (y : α), LE.le.{0} Real Real.instLEReal (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.instMulReal) (NNReal.toReal (Inv.inv.{0} NNReal (CanonicallyLinearOrderedSemifield.toInv.{0} NNReal NNReal.instCanonicallyLinearOrderedSemifieldNNReal) K)) (Dist.dist.{u2} α (PseudoMetricSpace.toDist.{u2} α _inst_1) x y)) (Dist.dist.{u1} β (PseudoMetricSpace.toDist.{u1} β _inst_2) (f x) (f y)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.mul_le_dist AntilipschitzWith.mul_le_distₓ'. -/
theorem mul_le_dist (hf : AntilipschitzWith K f) (x y : α) :
    (K⁻¹ * dist x y : ℝ) ≤ dist (f x) (f y) := by exact_mod_cast hf.mul_le_nndist x y
#align antilipschitz_with.mul_le_dist AntilipschitzWith.mul_le_dist

end AntilipschitzWith

end Metric

namespace AntilipschitzWith

variable [PseudoEMetricSpace α] [PseudoEMetricSpace β] [PseudoEMetricSpace γ]

variable {K : ℝ≥0} {f : α → β}

open Emetric

#print AntilipschitzWith.k /-
-- uses neither `f` nor `hf`
/-- Extract the constant from `hf : antilipschitz_with K f`. This is useful, e.g.,
if `K` is given by a long formula, and we want to reuse this value. -/
@[nolint unused_arguments]
protected def k (hf : AntilipschitzWith K f) : ℝ≥0 :=
  K
#align antilipschitz_with.K AntilipschitzWith.k
-/

/- warning: antilipschitz_with.injective -> AntilipschitzWith.injective is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_4 : EMetricSpace.{u1} α] [_inst_5 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β (EMetricSpace.toPseudoEmetricSpace.{u1} α _inst_4) _inst_5 K f) -> (Function.Injective.{succ u1, succ u2} α β f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_4 : EMetricSpace.{u2} α] [_inst_5 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β (EMetricSpace.toPseudoEMetricSpace.{u2} α _inst_4) _inst_5 K f) -> (Function.Injective.{succ u2, succ u1} α β f)
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.injective AntilipschitzWith.injectiveₓ'. -/
protected theorem injective {α : Type _} {β : Type _} [EMetricSpace α] [PseudoEMetricSpace β]
    {K : ℝ≥0} {f : α → β} (hf : AntilipschitzWith K f) : Function.Injective f := fun x y h => by
  simpa only [h, edist_self, MulZeroClass.mul_zero, edist_le_zero] using hf x y
#align antilipschitz_with.injective AntilipschitzWith.injective

/- warning: antilipschitz_with.mul_le_edist -> AntilipschitzWith.mul_le_edist is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (forall (x : α) (y : α), LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder))))) (HMul.hMul.{0, 0, 0} ENNReal ENNReal ENNReal (instHMul.{0} ENNReal (Distrib.toHasMul.{0} ENNReal (NonUnitalNonAssocSemiring.toDistrib.{0} ENNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENNReal (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.canonicallyOrderedCommSemiring)))))))) (Inv.inv.{0} ENNReal ENNReal.hasInv ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) NNReal ENNReal (HasLiftT.mk.{1, 1} NNReal ENNReal (CoeTCₓ.coe.{1, 1} NNReal ENNReal (coeBase.{1, 1} NNReal ENNReal ENNReal.hasCoe))) K)) (EDist.edist.{u1} α (PseudoEMetricSpace.toHasEdist.{u1} α _inst_1) x y)) (EDist.edist.{u2} β (PseudoEMetricSpace.toHasEdist.{u2} β _inst_2) (f x) (f y)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (forall (x : α) (y : α), LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal))))) (HMul.hMul.{0, 0, 0} ENNReal ENNReal ENNReal (instHMul.{0} ENNReal (CanonicallyOrderedCommSemiring.toMul.{0} ENNReal ENNReal.instCanonicallyOrderedCommSemiringENNReal)) (Inv.inv.{0} ENNReal ENNReal.instInvENNReal (ENNReal.some K)) (EDist.edist.{u2} α (PseudoEMetricSpace.toEDist.{u2} α _inst_1) x y)) (EDist.edist.{u1} β (PseudoEMetricSpace.toEDist.{u1} β _inst_2) (f x) (f y)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.mul_le_edist AntilipschitzWith.mul_le_edistₓ'. -/
theorem mul_le_edist (hf : AntilipschitzWith K f) (x y : α) :
    (K⁻¹ * edist x y : ℝ≥0∞) ≤ edist (f x) (f y) :=
  by
  rw [mul_comm, ← div_eq_mul_inv]
  exact ENNReal.div_le_of_le_mul' (hf x y)
#align antilipschitz_with.mul_le_edist AntilipschitzWith.mul_le_edist

/- warning: antilipschitz_with.ediam_preimage_le -> AntilipschitzWith.ediam_preimage_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (forall (s : Set.{u2} β), LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder))))) (EMetric.diam.{u1} α _inst_1 (Set.preimage.{u1, u2} α β f s)) (HMul.hMul.{0, 0, 0} ENNReal ENNReal ENNReal (instHMul.{0} ENNReal (Distrib.toHasMul.{0} ENNReal (NonUnitalNonAssocSemiring.toDistrib.{0} ENNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENNReal (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.canonicallyOrderedCommSemiring)))))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) NNReal ENNReal (HasLiftT.mk.{1, 1} NNReal ENNReal (CoeTCₓ.coe.{1, 1} NNReal ENNReal (coeBase.{1, 1} NNReal ENNReal ENNReal.hasCoe))) K) (EMetric.diam.{u2} β _inst_2 s)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (forall (s : Set.{u1} β), LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal))))) (EMetric.diam.{u2} α _inst_1 (Set.preimage.{u2, u1} α β f s)) (HMul.hMul.{0, 0, 0} ENNReal ENNReal ENNReal (instHMul.{0} ENNReal (CanonicallyOrderedCommSemiring.toMul.{0} ENNReal ENNReal.instCanonicallyOrderedCommSemiringENNReal)) (ENNReal.some K) (EMetric.diam.{u1} β _inst_2 s)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.ediam_preimage_le AntilipschitzWith.ediam_preimage_leₓ'. -/
theorem ediam_preimage_le (hf : AntilipschitzWith K f) (s : Set β) : diam (f ⁻¹' s) ≤ K * diam s :=
  diam_le fun x hx y hy => (hf x y).trans <| mul_le_mul_left' (edist_le_diam_of_mem hx hy) K
#align antilipschitz_with.ediam_preimage_le AntilipschitzWith.ediam_preimage_le

/- warning: antilipschitz_with.le_mul_ediam_image -> AntilipschitzWith.le_mul_ediam_image is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (forall (s : Set.{u1} α), LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder))))) (EMetric.diam.{u1} α _inst_1 s) (HMul.hMul.{0, 0, 0} ENNReal ENNReal ENNReal (instHMul.{0} ENNReal (Distrib.toHasMul.{0} ENNReal (NonUnitalNonAssocSemiring.toDistrib.{0} ENNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENNReal (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.canonicallyOrderedCommSemiring)))))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) NNReal ENNReal (HasLiftT.mk.{1, 1} NNReal ENNReal (CoeTCₓ.coe.{1, 1} NNReal ENNReal (coeBase.{1, 1} NNReal ENNReal ENNReal.hasCoe))) K) (EMetric.diam.{u2} β _inst_2 (Set.image.{u1, u2} α β f s))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (forall (s : Set.{u2} α), LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal))))) (EMetric.diam.{u2} α _inst_1 s) (HMul.hMul.{0, 0, 0} ENNReal ENNReal ENNReal (instHMul.{0} ENNReal (CanonicallyOrderedCommSemiring.toMul.{0} ENNReal ENNReal.instCanonicallyOrderedCommSemiringENNReal)) (ENNReal.some K) (EMetric.diam.{u1} β _inst_2 (Set.image.{u2, u1} α β f s))))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.le_mul_ediam_image AntilipschitzWith.le_mul_ediam_imageₓ'. -/
theorem le_mul_ediam_image (hf : AntilipschitzWith K f) (s : Set α) : diam s ≤ K * diam (f '' s) :=
  (diam_mono (subset_preimage_image _ _)).trans (hf.ediam_preimage_le (f '' s))
#align antilipschitz_with.le_mul_ediam_image AntilipschitzWith.le_mul_ediam_image

#print AntilipschitzWith.id /-
protected theorem id : AntilipschitzWith 1 (id : α → α) := fun x y => by
  simp only [ENNReal.coe_one, one_mul, id, le_refl]
#align antilipschitz_with.id AntilipschitzWith.id
-/

/- warning: antilipschitz_with.comp -> AntilipschitzWith.comp is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] [_inst_3 : PseudoEMetricSpace.{u3} γ] {Kg : NNReal} {g : β -> γ}, (AntilipschitzWith.{u2, u3} β γ _inst_2 _inst_3 Kg g) -> (forall {Kf : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 Kf f) -> (AntilipschitzWith.{u1, u3} α γ _inst_1 _inst_3 (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (Distrib.toHasMul.{0} NNReal (NonUnitalNonAssocSemiring.toDistrib.{0} NNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} NNReal (Semiring.toNonAssocSemiring.{0} NNReal NNReal.semiring))))) Kf Kg) (Function.comp.{succ u1, succ u2, succ u3} α β γ g f)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u3} β] [_inst_3 : PseudoEMetricSpace.{u2} γ] {Kg : NNReal} {g : β -> γ}, (AntilipschitzWith.{u3, u2} β γ _inst_2 _inst_3 Kg g) -> (forall {Kf : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u3} α β _inst_1 _inst_2 Kf f) -> (AntilipschitzWith.{u1, u2} α γ _inst_1 _inst_3 (HMul.hMul.{0, 0, 0} NNReal NNReal NNReal (instHMul.{0} NNReal (CanonicallyOrderedCommSemiring.toMul.{0} NNReal instNNRealCanonicallyOrderedCommSemiring)) Kf Kg) (Function.comp.{succ u1, succ u3, succ u2} α β γ g f)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.comp AntilipschitzWith.compₓ'. -/
theorem comp {Kg : ℝ≥0} {g : β → γ} (hg : AntilipschitzWith Kg g) {Kf : ℝ≥0} {f : α → β}
    (hf : AntilipschitzWith Kf f) : AntilipschitzWith (Kf * Kg) (g ∘ f) := fun x y =>
  calc
    edist x y ≤ Kf * edist (f x) (f y) := hf x y
    _ ≤ Kf * (Kg * edist (g (f x)) (g (f y))) := (ENNReal.mul_left_mono (hg _ _))
    _ = _ := by rw [ENNReal.coe_mul, mul_assoc]

#align antilipschitz_with.comp AntilipschitzWith.comp

/- warning: antilipschitz_with.restrict -> AntilipschitzWith.restrict is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (forall (s : Set.{u1} α), AntilipschitzWith.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β (Subtype.pseudoEmetricSpace.{u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x s) _inst_1) _inst_2 K (Set.restrict.{u1, u2} α (fun (ᾰ : α) => β) s f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (forall (s : Set.{u2} α), AntilipschitzWith.{u2, u1} (Set.Elem.{u2} α s) β (instPseudoEMetricSpaceSubtype.{u2} α (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x s) _inst_1) _inst_2 K (Set.restrict.{u2, u1} α (fun (ᾰ : α) => β) s f))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.restrict AntilipschitzWith.restrictₓ'. -/
theorem restrict (hf : AntilipschitzWith K f) (s : Set α) : AntilipschitzWith K (s.restrict f) :=
  fun x y => hf x y
#align antilipschitz_with.restrict AntilipschitzWith.restrict

/- warning: antilipschitz_with.cod_restrict -> AntilipschitzWith.codRestrict is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (forall {s : Set.{u2} β} (hs : forall (x : α), Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) (f x) s), AntilipschitzWith.{u1, u2} α (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) s) _inst_1 (Subtype.pseudoEmetricSpace.{u2} β (fun (x : β) => Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x s) _inst_2) K (Set.codRestrict.{u2, succ u1} β α f s hs))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (forall {s : Set.{u1} β} (hs : forall (x : α), Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) (f x) s), AntilipschitzWith.{u2, u1} α (Set.Elem.{u1} β s) _inst_1 (instPseudoEMetricSpaceSubtype.{u1} β (fun (x : β) => Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) x s) _inst_2) K (Set.codRestrict.{u1, succ u2} β α f s hs))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.cod_restrict AntilipschitzWith.codRestrictₓ'. -/
theorem codRestrict (hf : AntilipschitzWith K f) {s : Set β} (hs : ∀ x, f x ∈ s) :
    AntilipschitzWith K (s.codRestrict f hs) := fun x y => hf x y
#align antilipschitz_with.cod_restrict AntilipschitzWith.codRestrict

/- warning: antilipschitz_with.to_right_inv_on' -> AntilipschitzWith.to_right_inv_on' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β} {s : Set.{u1} α}, (AntilipschitzWith.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β (Subtype.pseudoEmetricSpace.{u1} α (fun (x : α) => Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x s) _inst_1) _inst_2 K (Set.restrict.{u1, u2} α (fun (ᾰ : α) => β) s f)) -> (forall {g : β -> α} {t : Set.{u2} β}, (Set.MapsTo.{u2, u1} β α g t s) -> (Set.RightInvOn.{u1, u2} α β g f t) -> (LipschitzWith.{u2, u1} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) t) α (Subtype.pseudoEmetricSpace.{u2} β (fun (x : β) => Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x t) _inst_2) _inst_1 K (Set.restrict.{u2, u1} β (fun (ᾰ : β) => α) t g)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β} {s : Set.{u2} α}, (AntilipschitzWith.{u2, u1} (Set.Elem.{u2} α s) β (instPseudoEMetricSpaceSubtype.{u2} α (fun (x : α) => Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x s) _inst_1) _inst_2 K (Set.restrict.{u2, u1} α (fun (ᾰ : α) => β) s f)) -> (forall {g : β -> α} {t : Set.{u1} β}, (Set.MapsTo.{u1, u2} β α g t s) -> (Set.RightInvOn.{u2, u1} α β g f t) -> (LipschitzWith.{u1, u2} (Set.Elem.{u1} β t) α (instPseudoEMetricSpaceSubtype.{u1} β (fun (x : β) => Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) x t) _inst_2) _inst_1 K (Set.restrict.{u1, u2} β (fun (ᾰ : β) => α) t g)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.to_right_inv_on' AntilipschitzWith.to_right_inv_on'ₓ'. -/
theorem to_right_inv_on' {s : Set α} (hf : AntilipschitzWith K (s.restrict f)) {g : β → α}
    {t : Set β} (g_maps : MapsTo g t s) (g_inv : RightInvOn g f t) :
    LipschitzWith K (t.restrict g) := fun x y => by
  simpa only [restrict_apply, g_inv x.mem, g_inv y.mem, Subtype.edist_eq, Subtype.coe_mk] using
    hf ⟨g x, g_maps x.mem⟩ ⟨g y, g_maps y.mem⟩
#align antilipschitz_with.to_right_inv_on' AntilipschitzWith.to_right_inv_on'

/- warning: antilipschitz_with.to_right_inv_on -> AntilipschitzWith.to_rightInvOn is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (forall {g : β -> α} {t : Set.{u2} β}, (Set.RightInvOn.{u1, u2} α β g f t) -> (LipschitzWith.{u2, u1} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) t) α (Subtype.pseudoEmetricSpace.{u2} β (fun (x : β) => Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x t) _inst_2) _inst_1 K (Set.restrict.{u2, u1} β (fun (ᾰ : β) => α) t g)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (forall {g : β -> α} {t : Set.{u1} β}, (Set.RightInvOn.{u2, u1} α β g f t) -> (LipschitzWith.{u1, u2} (Set.Elem.{u1} β t) α (instPseudoEMetricSpaceSubtype.{u1} β (fun (x : β) => Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) x t) _inst_2) _inst_1 K (Set.restrict.{u1, u2} β (fun (ᾰ : β) => α) t g)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.to_right_inv_on AntilipschitzWith.to_rightInvOnₓ'. -/
theorem to_rightInvOn (hf : AntilipschitzWith K f) {g : β → α} {t : Set β} (h : RightInvOn g f t) :
    LipschitzWith K (t.restrict g) :=
  (hf.restrict univ).to_right_inv_on' (mapsTo_univ g t) h
#align antilipschitz_with.to_right_inv_on AntilipschitzWith.to_rightInvOn

/- warning: antilipschitz_with.to_right_inverse -> AntilipschitzWith.to_rightInverse is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (forall {g : β -> α}, (Function.RightInverse.{succ u1, succ u2} α β g f) -> (LipschitzWith.{u2, u1} β α _inst_2 _inst_1 K g))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (forall {g : β -> α}, (Function.RightInverse.{succ u2, succ u1} α β g f) -> (LipschitzWith.{u1, u2} β α _inst_2 _inst_1 K g))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.to_right_inverse AntilipschitzWith.to_rightInverseₓ'. -/
theorem to_rightInverse (hf : AntilipschitzWith K f) {g : β → α} (hg : Function.RightInverse g f) :
    LipschitzWith K g := by
  intro x y
  have := hf (g x) (g y)
  rwa [hg x, hg y] at this
#align antilipschitz_with.to_right_inverse AntilipschitzWith.to_rightInverse

/- warning: antilipschitz_with.comap_uniformity_le -> AntilipschitzWith.comap_uniformity_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (LE.le.{u1} (Filter.{u1} (Prod.{u1, u1} α α)) (Preorder.toLE.{u1} (Filter.{u1} (Prod.{u1, u1} α α)) (PartialOrder.toPreorder.{u1} (Filter.{u1} (Prod.{u1, u1} α α)) (Filter.partialOrder.{u1} (Prod.{u1, u1} α α)))) (Filter.comap.{u1, u2} (Prod.{u1, u1} α α) (Prod.{u2, u2} β β) (Prod.map.{u1, u2, u1, u2} α β α β f f) (uniformity.{u2} β (PseudoEMetricSpace.toUniformSpace.{u2} β _inst_2))) (uniformity.{u1} α (PseudoEMetricSpace.toUniformSpace.{u1} α _inst_1)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (LE.le.{u2} (Filter.{u2} (Prod.{u2, u2} α α)) (Preorder.toLE.{u2} (Filter.{u2} (Prod.{u2, u2} α α)) (PartialOrder.toPreorder.{u2} (Filter.{u2} (Prod.{u2, u2} α α)) (Filter.instPartialOrderFilter.{u2} (Prod.{u2, u2} α α)))) (Filter.comap.{u2, u1} (Prod.{u2, u2} α α) (Prod.{u1, u1} β β) (Prod.map.{u2, u1, u2, u1} α β α β f f) (uniformity.{u1} β (PseudoEMetricSpace.toUniformSpace.{u1} β _inst_2))) (uniformity.{u2} α (PseudoEMetricSpace.toUniformSpace.{u2} α _inst_1)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.comap_uniformity_le AntilipschitzWith.comap_uniformity_leₓ'. -/
theorem comap_uniformity_le (hf : AntilipschitzWith K f) : (𝓤 β).comap (Prod.map f f) ≤ 𝓤 α :=
  by
  refine' ((uniformity_basis_edist.comap _).le_basis_iffₓ uniformity_basis_edist).2 fun ε h₀ => _
  refine' ⟨K⁻¹ * ε, ENNReal.mul_pos (ENNReal.inv_ne_zero.2 ENNReal.coe_ne_top) h₀.ne', _⟩
  refine' fun x hx => (hf x.1 x.2).trans_lt _
  rw [mul_comm, ← div_eq_mul_inv] at hx
  rw [mul_comm]
  exact ENNReal.mul_lt_of_lt_div hx
#align antilipschitz_with.comap_uniformity_le AntilipschitzWith.comap_uniformity_le

/- warning: antilipschitz_with.uniform_inducing -> AntilipschitzWith.uniformInducing is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (UniformContinuous.{u1, u2} α β (PseudoEMetricSpace.toUniformSpace.{u1} α _inst_1) (PseudoEMetricSpace.toUniformSpace.{u2} β _inst_2) f) -> (UniformInducing.{u1, u2} α β (PseudoEMetricSpace.toUniformSpace.{u1} α _inst_1) (PseudoEMetricSpace.toUniformSpace.{u2} β _inst_2) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (UniformContinuous.{u2, u1} α β (PseudoEMetricSpace.toUniformSpace.{u2} α _inst_1) (PseudoEMetricSpace.toUniformSpace.{u1} β _inst_2) f) -> (UniformInducing.{u2, u1} α β (PseudoEMetricSpace.toUniformSpace.{u2} α _inst_1) (PseudoEMetricSpace.toUniformSpace.{u1} β _inst_2) f)
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.uniform_inducing AntilipschitzWith.uniformInducingₓ'. -/
protected theorem uniformInducing (hf : AntilipschitzWith K f) (hfc : UniformContinuous f) :
    UniformInducing f :=
  ⟨le_antisymm hf.comap_uniformity_le hfc.le_comap⟩
#align antilipschitz_with.uniform_inducing AntilipschitzWith.uniformInducing

/- warning: antilipschitz_with.uniform_embedding -> AntilipschitzWith.uniformEmbedding is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_4 : EMetricSpace.{u1} α] [_inst_5 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β (EMetricSpace.toPseudoEmetricSpace.{u1} α _inst_4) _inst_5 K f) -> (UniformContinuous.{u1, u2} α β (PseudoEMetricSpace.toUniformSpace.{u1} α (EMetricSpace.toPseudoEmetricSpace.{u1} α _inst_4)) (PseudoEMetricSpace.toUniformSpace.{u2} β _inst_5) f) -> (UniformEmbedding.{u1, u2} α β (PseudoEMetricSpace.toUniformSpace.{u1} α (EMetricSpace.toPseudoEmetricSpace.{u1} α _inst_4)) (PseudoEMetricSpace.toUniformSpace.{u2} β _inst_5) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_4 : EMetricSpace.{u2} α] [_inst_5 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β (EMetricSpace.toPseudoEMetricSpace.{u2} α _inst_4) _inst_5 K f) -> (UniformContinuous.{u2, u1} α β (PseudoEMetricSpace.toUniformSpace.{u2} α (EMetricSpace.toPseudoEMetricSpace.{u2} α _inst_4)) (PseudoEMetricSpace.toUniformSpace.{u1} β _inst_5) f) -> (UniformEmbedding.{u2, u1} α β (PseudoEMetricSpace.toUniformSpace.{u2} α (EMetricSpace.toPseudoEMetricSpace.{u2} α _inst_4)) (PseudoEMetricSpace.toUniformSpace.{u1} β _inst_5) f)
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.uniform_embedding AntilipschitzWith.uniformEmbeddingₓ'. -/
protected theorem uniformEmbedding {α : Type _} {β : Type _} [EMetricSpace α] [PseudoEMetricSpace β]
    {K : ℝ≥0} {f : α → β} (hf : AntilipschitzWith K f) (hfc : UniformContinuous f) :
    UniformEmbedding f :=
  ⟨hf.UniformInducing hfc, hf.Injective⟩
#align antilipschitz_with.uniform_embedding AntilipschitzWith.uniformEmbedding

/- warning: antilipschitz_with.is_complete_range -> AntilipschitzWith.isComplete_range is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β} [_inst_4 : CompleteSpace.{u1} α (PseudoEMetricSpace.toUniformSpace.{u1} α _inst_1)], (AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (UniformContinuous.{u1, u2} α β (PseudoEMetricSpace.toUniformSpace.{u1} α _inst_1) (PseudoEMetricSpace.toUniformSpace.{u2} β _inst_2) f) -> (IsComplete.{u2} β (PseudoEMetricSpace.toUniformSpace.{u2} β _inst_2) (Set.range.{u2, succ u1} β α f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β} [_inst_4 : CompleteSpace.{u2} α (PseudoEMetricSpace.toUniformSpace.{u2} α _inst_1)], (AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (UniformContinuous.{u2, u1} α β (PseudoEMetricSpace.toUniformSpace.{u2} α _inst_1) (PseudoEMetricSpace.toUniformSpace.{u1} β _inst_2) f) -> (IsComplete.{u1} β (PseudoEMetricSpace.toUniformSpace.{u1} β _inst_2) (Set.range.{u1, succ u2} β α f))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.is_complete_range AntilipschitzWith.isComplete_rangeₓ'. -/
theorem isComplete_range [CompleteSpace α] (hf : AntilipschitzWith K f)
    (hfc : UniformContinuous f) : IsComplete (range f) :=
  (hf.UniformInducing hfc).isComplete_range
#align antilipschitz_with.is_complete_range AntilipschitzWith.isComplete_range

/- warning: antilipschitz_with.is_closed_range -> AntilipschitzWith.isClosed_range is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_4 : PseudoEMetricSpace.{u1} α] [_inst_5 : EMetricSpace.{u2} β] [_inst_6 : CompleteSpace.{u1} α (PseudoEMetricSpace.toUniformSpace.{u1} α _inst_4)] {f : α -> β} {K : NNReal}, (AntilipschitzWith.{u1, u2} α β _inst_4 (EMetricSpace.toPseudoEmetricSpace.{u2} β _inst_5) K f) -> (UniformContinuous.{u1, u2} α β (PseudoEMetricSpace.toUniformSpace.{u1} α _inst_4) (PseudoEMetricSpace.toUniformSpace.{u2} β (EMetricSpace.toPseudoEmetricSpace.{u2} β _inst_5)) f) -> (IsClosed.{u2} β (UniformSpace.toTopologicalSpace.{u2} β (PseudoEMetricSpace.toUniformSpace.{u2} β (EMetricSpace.toPseudoEmetricSpace.{u2} β _inst_5))) (Set.range.{u2, succ u1} β α f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_4 : PseudoEMetricSpace.{u2} α] [_inst_5 : EMetricSpace.{u1} β] [_inst_6 : CompleteSpace.{u2} α (PseudoEMetricSpace.toUniformSpace.{u2} α _inst_4)] {f : α -> β} {K : NNReal}, (AntilipschitzWith.{u2, u1} α β _inst_4 (EMetricSpace.toPseudoEMetricSpace.{u1} β _inst_5) K f) -> (UniformContinuous.{u2, u1} α β (PseudoEMetricSpace.toUniformSpace.{u2} α _inst_4) (PseudoEMetricSpace.toUniformSpace.{u1} β (EMetricSpace.toPseudoEMetricSpace.{u1} β _inst_5)) f) -> (IsClosed.{u1} β (UniformSpace.toTopologicalSpace.{u1} β (PseudoEMetricSpace.toUniformSpace.{u1} β (EMetricSpace.toPseudoEMetricSpace.{u1} β _inst_5))) (Set.range.{u1, succ u2} β α f))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.is_closed_range AntilipschitzWith.isClosed_rangeₓ'. -/
theorem isClosed_range {α β : Type _} [PseudoEMetricSpace α] [EMetricSpace β] [CompleteSpace α]
    {f : α → β} {K : ℝ≥0} (hf : AntilipschitzWith K f) (hfc : UniformContinuous f) :
    IsClosed (range f) :=
  (hf.isComplete_range hfc).IsClosed
#align antilipschitz_with.is_closed_range AntilipschitzWith.isClosed_range

/- warning: antilipschitz_with.closed_embedding -> AntilipschitzWith.closedEmbedding is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_4 : EMetricSpace.{u1} α] [_inst_5 : EMetricSpace.{u2} β] {K : NNReal} {f : α -> β} [_inst_6 : CompleteSpace.{u1} α (PseudoEMetricSpace.toUniformSpace.{u1} α (EMetricSpace.toPseudoEmetricSpace.{u1} α _inst_4))], (AntilipschitzWith.{u1, u2} α β (EMetricSpace.toPseudoEmetricSpace.{u1} α _inst_4) (EMetricSpace.toPseudoEmetricSpace.{u2} β _inst_5) K f) -> (UniformContinuous.{u1, u2} α β (PseudoEMetricSpace.toUniformSpace.{u1} α (EMetricSpace.toPseudoEmetricSpace.{u1} α _inst_4)) (PseudoEMetricSpace.toUniformSpace.{u2} β (EMetricSpace.toPseudoEmetricSpace.{u2} β _inst_5)) f) -> (ClosedEmbedding.{u1, u2} α β (UniformSpace.toTopologicalSpace.{u1} α (PseudoEMetricSpace.toUniformSpace.{u1} α (EMetricSpace.toPseudoEmetricSpace.{u1} α _inst_4))) (UniformSpace.toTopologicalSpace.{u2} β (PseudoEMetricSpace.toUniformSpace.{u2} β (EMetricSpace.toPseudoEmetricSpace.{u2} β _inst_5))) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_4 : EMetricSpace.{u2} α] [_inst_5 : EMetricSpace.{u1} β] {K : NNReal} {f : α -> β} [_inst_6 : CompleteSpace.{u2} α (PseudoEMetricSpace.toUniformSpace.{u2} α (EMetricSpace.toPseudoEMetricSpace.{u2} α _inst_4))], (AntilipschitzWith.{u2, u1} α β (EMetricSpace.toPseudoEMetricSpace.{u2} α _inst_4) (EMetricSpace.toPseudoEMetricSpace.{u1} β _inst_5) K f) -> (UniformContinuous.{u2, u1} α β (PseudoEMetricSpace.toUniformSpace.{u2} α (EMetricSpace.toPseudoEMetricSpace.{u2} α _inst_4)) (PseudoEMetricSpace.toUniformSpace.{u1} β (EMetricSpace.toPseudoEMetricSpace.{u1} β _inst_5)) f) -> (ClosedEmbedding.{u2, u1} α β (UniformSpace.toTopologicalSpace.{u2} α (PseudoEMetricSpace.toUniformSpace.{u2} α (EMetricSpace.toPseudoEMetricSpace.{u2} α _inst_4))) (UniformSpace.toTopologicalSpace.{u1} β (PseudoEMetricSpace.toUniformSpace.{u1} β (EMetricSpace.toPseudoEMetricSpace.{u1} β _inst_5))) f)
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.closed_embedding AntilipschitzWith.closedEmbeddingₓ'. -/
theorem closedEmbedding {α : Type _} {β : Type _} [EMetricSpace α] [EMetricSpace β] {K : ℝ≥0}
    {f : α → β} [CompleteSpace α] (hf : AntilipschitzWith K f) (hfc : UniformContinuous f) :
    ClosedEmbedding f :=
  { (hf.UniformEmbedding hfc).Embedding with closed_range := hf.isClosed_range hfc }
#align antilipschitz_with.closed_embedding AntilipschitzWith.closedEmbedding

#print AntilipschitzWith.subtype_coe /-
theorem subtype_coe (s : Set α) : AntilipschitzWith 1 (coe : s → α) :=
  AntilipschitzWith.id.restrict s
#align antilipschitz_with.subtype_coe AntilipschitzWith.subtype_coe
-/

/- warning: antilipschitz_with.of_subsingleton -> AntilipschitzWith.of_subsingleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {f : α -> β} [_inst_4 : Subsingleton.{succ u1} α] {K : NNReal}, AntilipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {f : α -> β} [_inst_4 : Subsingleton.{succ u2} α] {K : NNReal}, AntilipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.of_subsingleton AntilipschitzWith.of_subsingletonₓ'. -/
theorem of_subsingleton [Subsingleton α] {K : ℝ≥0} : AntilipschitzWith K f := fun x y => by
  simp only [Subsingleton.elim x y, edist_self, zero_le]
#align antilipschitz_with.of_subsingleton AntilipschitzWith.of_subsingleton

/- warning: antilipschitz_with.subsingleton -> AntilipschitzWith.subsingleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_4 : EMetricSpace.{u1} α] [_inst_5 : PseudoEMetricSpace.{u2} β] {f : α -> β}, (AntilipschitzWith.{u1, u2} α β (EMetricSpace.toPseudoEmetricSpace.{u1} α _inst_4) _inst_5 (OfNat.ofNat.{0} NNReal 0 (OfNat.mk.{0} NNReal 0 (Zero.zero.{0} NNReal (MulZeroClass.toHasZero.{0} NNReal (NonUnitalNonAssocSemiring.toMulZeroClass.{0} NNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} NNReal (Semiring.toNonAssocSemiring.{0} NNReal NNReal.semiring))))))) f) -> (Subsingleton.{succ u1} α)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_4 : EMetricSpace.{u2} α] [_inst_5 : PseudoEMetricSpace.{u1} β] {f : α -> β}, (AntilipschitzWith.{u2, u1} α β (EMetricSpace.toPseudoEMetricSpace.{u2} α _inst_4) _inst_5 (OfNat.ofNat.{0} NNReal 0 (Zero.toOfNat0.{0} NNReal instNNRealZero)) f) -> (Subsingleton.{succ u2} α)
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.subsingleton AntilipschitzWith.subsingletonₓ'. -/
/-- If `f : α → β` is `0`-antilipschitz, then `α` is a `subsingleton`. -/
protected theorem subsingleton {α β} [EMetricSpace α] [PseudoEMetricSpace β] {f : α → β}
    (h : AntilipschitzWith 0 f) : Subsingleton α :=
  ⟨fun x y => edist_le_zero.1 <| (h x y).trans_eq <| MulZeroClass.zero_mul _⟩
#align antilipschitz_with.subsingleton AntilipschitzWith.subsingleton

end AntilipschitzWith

namespace AntilipschitzWith

open Metric

variable [PseudoMetricSpace α] [PseudoMetricSpace β] {K : ℝ≥0} {f : α → β}

/- warning: antilipschitz_with.bounded_preimage -> AntilipschitzWith.bounded_preimage is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) -> (forall {s : Set.{u2} β}, (Metric.Bounded.{u2} β _inst_2 s) -> (Metric.Bounded.{u1} α _inst_1 (Set.preimage.{u1, u2} α β f s)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) -> (forall {s : Set.{u1} β}, (Metric.Bounded.{u1} β _inst_2 s) -> (Metric.Bounded.{u2} α _inst_1 (Set.preimage.{u2, u1} α β f s)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.bounded_preimage AntilipschitzWith.bounded_preimageₓ'. -/
theorem bounded_preimage (hf : AntilipschitzWith K f) {s : Set β} (hs : Bounded s) :
    Bounded (f ⁻¹' s) :=
  Exists.intro (K * diam s) fun x hx y hy =>
    calc
      dist x y ≤ K * dist (f x) (f y) := hf.le_mul_dist x y
      _ ≤ K * diam s := mul_le_mul_of_nonneg_left (dist_le_diam_of_mem hs hx hy) K.2

#align antilipschitz_with.bounded_preimage AntilipschitzWith.bounded_preimage

/- warning: antilipschitz_with.tendsto_cobounded -> AntilipschitzWith.tendsto_cobounded is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : PseudoMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β _inst_2) K f) -> (Filter.Tendsto.{u1, u2} α β f (Bornology.cobounded.{u1} α (PseudoMetricSpace.toBornology.{u1} α _inst_1)) (Bornology.cobounded.{u2} β (PseudoMetricSpace.toBornology.{u2} β _inst_2)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : PseudoMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (AntilipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u1} β _inst_2) K f) -> (Filter.Tendsto.{u2, u1} α β f (Bornology.cobounded.{u2} α (PseudoMetricSpace.toBornology.{u2} α _inst_1)) (Bornology.cobounded.{u1} β (PseudoMetricSpace.toBornology.{u1} β _inst_2)))
Case conversion may be inaccurate. Consider using '#align antilipschitz_with.tendsto_cobounded AntilipschitzWith.tendsto_coboundedₓ'. -/
theorem tendsto_cobounded (hf : AntilipschitzWith K f) : Tendsto f (cobounded α) (cobounded β) :=
  compl_surjective.forall.2 fun s (hs : IsBounded s) =>
    Metric.isBounded_iff.2 <| hf.bounded_preimage <| Metric.isBounded_iff.1 hs
#align antilipschitz_with.tendsto_cobounded AntilipschitzWith.tendsto_cobounded

#print AntilipschitzWith.properSpace /-
/-- The image of a proper space under an expanding onto map is proper. -/
protected theorem properSpace {α : Type _} [MetricSpace α] {K : ℝ≥0} {f : α → β} [ProperSpace α]
    (hK : AntilipschitzWith K f) (f_cont : Continuous f) (hf : Function.Surjective f) :
    ProperSpace β :=
  by
  apply properSpace_of_compact_closedBall_of_le 0 fun x₀ r hr => _
  let K := f ⁻¹' closed_ball x₀ r
  have A : IsClosed K := is_closed_ball.preimage f_cont
  have B : bounded K := hK.bounded_preimage bounded_closed_ball
  have : IsCompact K := is_compact_iff_is_closed_bounded.2 ⟨A, B⟩
  convert this.image f_cont
  exact (hf.image_preimage _).symm
#align antilipschitz_with.proper_space AntilipschitzWith.properSpace
-/

end AntilipschitzWith

/- warning: lipschitz_with.to_right_inverse -> LipschitzWith.to_rightInverse is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoEMetricSpace.{u1} α] [_inst_2 : PseudoEMetricSpace.{u2} β] {K : NNReal} {f : α -> β}, (LipschitzWith.{u1, u2} α β _inst_1 _inst_2 K f) -> (forall {g : β -> α}, (Function.RightInverse.{succ u1, succ u2} α β g f) -> (AntilipschitzWith.{u2, u1} β α _inst_2 _inst_1 K g))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoEMetricSpace.{u2} α] [_inst_2 : PseudoEMetricSpace.{u1} β] {K : NNReal} {f : α -> β}, (LipschitzWith.{u2, u1} α β _inst_1 _inst_2 K f) -> (forall {g : β -> α}, (Function.RightInverse.{succ u2, succ u1} α β g f) -> (AntilipschitzWith.{u1, u2} β α _inst_2 _inst_1 K g))
Case conversion may be inaccurate. Consider using '#align lipschitz_with.to_right_inverse LipschitzWith.to_rightInverseₓ'. -/
theorem LipschitzWith.to_rightInverse [PseudoEMetricSpace α] [PseudoEMetricSpace β] {K : ℝ≥0}
    {f : α → β} (hf : LipschitzWith K f) {g : β → α} (hg : Function.RightInverse g f) :
    AntilipschitzWith K g := fun x y => by simpa only [hg _] using hf (g x) (g y)
#align lipschitz_with.to_right_inverse LipschitzWith.to_rightInverse

/- warning: lipschitz_with.proper_space -> LipschitzWith.properSpace is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : PseudoMetricSpace.{u1} α] [_inst_2 : MetricSpace.{u2} β] [_inst_3 : ProperSpace.{u2} β (MetricSpace.toPseudoMetricSpace.{u2} β _inst_2)] {K : NNReal} {f : Homeomorph.{u1, u2} α β (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.toTopologicalSpace.{u2} β (PseudoMetricSpace.toUniformSpace.{u2} β (MetricSpace.toPseudoMetricSpace.{u2} β _inst_2)))}, (LipschitzWith.{u1, u2} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u1} α _inst_1) (PseudoMetricSpace.toPseudoEMetricSpace.{u2} β (MetricSpace.toPseudoMetricSpace.{u2} β _inst_2)) K (coeFn.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (Homeomorph.{u1, u2} α β (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.toTopologicalSpace.{u2} β (PseudoMetricSpace.toUniformSpace.{u2} β (MetricSpace.toPseudoMetricSpace.{u2} β _inst_2)))) (fun (_x : Homeomorph.{u1, u2} α β (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.toTopologicalSpace.{u2} β (PseudoMetricSpace.toUniformSpace.{u2} β (MetricSpace.toPseudoMetricSpace.{u2} β _inst_2)))) => α -> β) (Homeomorph.hasCoeToFun.{u1, u2} α β (UniformSpace.toTopologicalSpace.{u1} α (PseudoMetricSpace.toUniformSpace.{u1} α _inst_1)) (UniformSpace.toTopologicalSpace.{u2} β (PseudoMetricSpace.toUniformSpace.{u2} β (MetricSpace.toPseudoMetricSpace.{u2} β _inst_2)))) f)) -> (ProperSpace.{u1} α _inst_1)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : PseudoMetricSpace.{u2} α] [_inst_2 : MetricSpace.{u1} β] [_inst_3 : ProperSpace.{u1} β (MetricSpace.toPseudoMetricSpace.{u1} β _inst_2)] {K : NNReal} {f : Homeomorph.{u2, u1} α β (UniformSpace.toTopologicalSpace.{u2} α (PseudoMetricSpace.toUniformSpace.{u2} α _inst_1)) (UniformSpace.toTopologicalSpace.{u1} β (PseudoMetricSpace.toUniformSpace.{u1} β (MetricSpace.toPseudoMetricSpace.{u1} β _inst_2)))}, (LipschitzWith.{u2, u1} α β (PseudoMetricSpace.toPseudoEMetricSpace.{u2} α _inst_1) (EMetricSpace.toPseudoEMetricSpace.{u1} β (MetricSpace.toEMetricSpace.{u1} β _inst_2)) K (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (Homeomorph.{u2, u1} α β (UniformSpace.toTopologicalSpace.{u2} α (PseudoMetricSpace.toUniformSpace.{u2} α _inst_1)) (UniformSpace.toTopologicalSpace.{u1} β (PseudoMetricSpace.toUniformSpace.{u1} β (MetricSpace.toPseudoMetricSpace.{u1} β _inst_2)))) α (fun (_x : α) => β) (EmbeddingLike.toFunLike.{max (succ u2) (succ u1), succ u2, succ u1} (Homeomorph.{u2, u1} α β (UniformSpace.toTopologicalSpace.{u2} α (PseudoMetricSpace.toUniformSpace.{u2} α _inst_1)) (UniformSpace.toTopologicalSpace.{u1} β (PseudoMetricSpace.toUniformSpace.{u1} β (MetricSpace.toPseudoMetricSpace.{u1} β _inst_2)))) α β (EquivLike.toEmbeddingLike.{max (succ u2) (succ u1), succ u2, succ u1} (Homeomorph.{u2, u1} α β (UniformSpace.toTopologicalSpace.{u2} α (PseudoMetricSpace.toUniformSpace.{u2} α _inst_1)) (UniformSpace.toTopologicalSpace.{u1} β (PseudoMetricSpace.toUniformSpace.{u1} β (MetricSpace.toPseudoMetricSpace.{u1} β _inst_2)))) α β (Homeomorph.instEquivLikeHomeomorph.{u2, u1} α β (UniformSpace.toTopologicalSpace.{u2} α (PseudoMetricSpace.toUniformSpace.{u2} α _inst_1)) (UniformSpace.toTopologicalSpace.{u1} β (PseudoMetricSpace.toUniformSpace.{u1} β (MetricSpace.toPseudoMetricSpace.{u1} β _inst_2)))))) f)) -> (ProperSpace.{u2} α _inst_1)
Case conversion may be inaccurate. Consider using '#align lipschitz_with.proper_space LipschitzWith.properSpaceₓ'. -/
/-- The preimage of a proper space under a Lipschitz homeomorphism is proper. -/
@[protected]
theorem LipschitzWith.properSpace [PseudoMetricSpace α] [MetricSpace β] [ProperSpace β] {K : ℝ≥0}
    {f : α ≃ₜ β} (hK : LipschitzWith K f) : ProperSpace α :=
  (hK.to_rightInverse f.right_inv).ProperSpace f.symm.Continuous f.symm.Surjective
#align lipschitz_with.proper_space LipschitzWith.properSpace


/- Various utilities for proving properties of orderings. -/

namespace order

/-- Show le_total for a basic lexiographic-like order. -/
protected lemma lex_like.le_total
    {α β : Type*} [linear_order α] [linear_order β]
    (a a' : α) (b b' : β)
  : (a < a' ∨ (a = a' ∧ b ≤ b')) ∨ (a' < a ∨ (a' = a ∧ b' ≤ b))
  := begin
    cases le_total a a',
    case or.inl : a_le {
      cases lt_or_eq_of_le a_le,
      case or.inl : lt { from or.inl (or.inl lt) },
      case or.inr : eq {
        cases eq,
        from or.imp (λ h, or.inr ⟨ eq, h ⟩) (λ h, or.inr ⟨ symm eq, h ⟩)
          (le_total b b')
      }
    },
    case or.inr : b_le_a {
      cases lt_or_eq_of_le b_le_a,
      case or.inl : lt { from or.inr (or.inl lt) },
      case or.inr : eq {
        cases eq,
        from or.imp (λ h, or.inr ⟨ eq, h ⟩) (λ h, or.inr ⟨ symm eq, h ⟩)
          (le_total b b')
      }
    }
  end

end order


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

! This file was ported from Lean 3 source module order.filter.modeq
! leanprover-community/mathlib commit 13a5329a8625701af92e9a96ffc90fa787fff24d
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Nat.Parity
import Mathbin.Order.Filter.AtTopBot

/-!
# Numbers are frequently modeq to fixed numbers

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

In this file we prove that `m ≡ d [MOD n]` frequently as `m → ∞`.
-/

open Filter

namespace Nat

#print Nat.frequently_modEq /-
/-- Infinitely many natural numbers are equal to `d` mod `n`. -/
theorem frequently_modEq {n : ℕ} (h : n ≠ 0) (d : ℕ) : ∃ᶠ m in atTop, m ≡ d [MOD n] :=
  ((tendsto_add_atTop_nat d).comp (tendsto_id.nsmul_atTop h.bot_lt)).Frequently <|
    frequently_of_forall fun m => by simp [Nat.modEq_iff_dvd, ← sub_sub]
#align nat.frequently_modeq Nat.frequently_modEq
-/

#print Nat.frequently_mod_eq /-
theorem frequently_mod_eq {d n : ℕ} (h : d < n) : ∃ᶠ m in atTop, m % n = d := by
  simpa only [Nat.ModEq, mod_eq_of_lt h] using frequently_modeq h.ne_bot d
#align nat.frequently_mod_eq Nat.frequently_mod_eq
-/

#print Nat.frequently_even /-
theorem frequently_even : ∃ᶠ m : ℕ in atTop, Even m := by
  simpa only [even_iff] using frequently_mod_eq zero_lt_two
#align nat.frequently_even Nat.frequently_even
-/

#print Nat.frequently_odd /-
theorem frequently_odd : ∃ᶠ m : ℕ in atTop, Odd m := by
  simpa only [odd_iff] using frequently_mod_eq one_lt_two
#align nat.frequently_odd Nat.frequently_odd
-/

end Nat


import linear_algebra.basic algebra.field data.complex.basic data.real.basic analysis.metric_space analysis.topology.uniform_space

open vector_space field set complex real
universes u v w

class semi_norm_space (V : Type u) extends module ℂ V :=
(N : V → ℝ)
(semi_norm_nonneg : ∀ (x : V), N(x) ≥ 0)
(semi_norm_sub_add : ∀ (x y : V), N(x + y) ≤ N(x) + N(y))
(semi_norm_abs_hom : ∀ (x : V), ∀ (a : ℂ), N(a • x) = abs(a)*N(x))

class norm_space (V : Type u) extends module ℂ V :=
(N : V → ℝ)
(norm_nonneg : ∀ (x : V), N(x) ≥ 0)
(norm_sub_add : ∀ (x y : V), N(x + y) ≤ N(x) + N(y))
(norm_abs_hom : ∀ (x : V), ∀ (a : ℂ), N(a • x) = abs(a)*N(x))
(norm_pos_def : ∀ (x : V), N(x) = (0 : ℝ) ↔ x = (0 : V))

open norm_space

variables {V : Type u} [norm_space V]

@[simp] lemma norm_zero : N(0 : V) = 0 := (norm_pos_def 0).mpr (refl 0)

@[simp] lemma norm_neg (x : V) : N(-x) = N(x) :=
begin
rw ←neg_one_smul,
rw norm_abs_hom,
simp,
end

lemma norm_ne_zero_iff_ne_zero (x : V) :
N(x) ≠ 0 ↔ x ≠ 0 := --⟨λ H, (iff_false_left H).mp (norm_pos_def x), λ H, (iff_false_right H).mp (norm_pos_def x)⟩
begin
split,
    intros H,
    exact (iff_false_left H).mp (norm_pos_def x),

    intros H,
    exact (iff_false_right H).mp (norm_pos_def x),
end

theorem norm_sub_le_sub_norm (x y : V) : complex.abs(N(x) - N(y)) ≤ N(x - y) :=
begin
rw ←of_real_sub,
rw abs_of_real,
rw abs_le,
split,
    ring,
    have Hy : N((y - x) + x) = N(y),
        simp,
    have H1 : N((y - x) + x) ≤ N(y - x) + N(x),
        exact norm_sub_add (y - x) (x),
    rw Hy at H1,
    rw ←(add_le_add_iff_right (-N(x))) at H1,
    ring at H1,
    rw [←neg_sub, norm_neg],
    rw neg_le,
    ring,
    exact H1,

    have Hx : N((x - y) + y) = N(x),
        simp,
    have H2 : N((x - y) + y) ≤ N(x - y) + N(y),
        exact norm_sub_add (x - y) (y),
    rw Hx at H2,
    rw ←(add_le_add_iff_right (-N(y))) at H2,
    ring at H2,
    exact H2,
end

noncomputable def norm_dist (x y : V) := N(x - y)

noncomputable instance to_metric_space : has_coe (norm_space V) (metric_space V) :=
⟨λh, {
dist := norm_dist,
dist_self :=
    begin
    intros,
    dunfold norm_dist,
    simp,
    end,
eq_of_dist_eq_zero :=
    begin
    dunfold norm_dist,
    intros x y H,
    exact sub_eq_zero.mp ((norm_pos_def (x - y)).mp H),
    end,
dist_comm :=
    begin
    intros,
    dunfold norm_dist,
    rw ←neg_sub,
    rw norm_neg,
    end,
dist_triangle :=
    begin
    dunfold norm_dist,
    intros,
    have H : x - z = (x - y) + (y - z),
        simp,
    rw H,
    exact norm_sub_add (x - y) (y - z),
    end,
} ⟩

def is_normalised (x : V) := N(x) = 1

noncomputable def normalise (x : V) := ↑(N(x))⁻¹ • x

def normalise_set :
set V → set V := image(normalise)

lemma normalised_linear_indep (s : set V) :
linear_independent s → linear_independent (normalise_set s) :=
begin
dunfold linear_independent,
intros H1 l H3 H4,
have H5 : ∀ (x : V), x ∉ s → x ∈ normalise_set s,
    intros x hx,
end

#print finsupp.sum
#print finsupp
#print coe_fn

lemma normalised_span_spans (s : set V) :
span s = span (normalise_set s) :=
begin
rw set_eq_def,
intros,
dunfold span,
split,
    intros H,
    rw mem_set_of_eq at H,
    apply exists.elim H,
    intros v Hv,

    admit,

    admit,
end

theorem exists_normalised_basis :
∃ (b : set V), is_basis b ∧ ∀ (x : V), x ∈ b → is_normalised x :=
begin
have H1 : ∃ (b : set V), is_basis b,
    exact exists_is_basis V,
apply exists.elim H1,
intros b Hb,
exact exists.intro (normalise_set b) (and.intro (normalised_basis_is_basis b Hb) (normalise_set_normalises b (zero_not_mem_of_linear_independent (zero_ne_one ℂ) Hb.left))),
end

noncomputable instance complex_is_norm_space : norm_space ℂ :=
{
N := abs,
norm_nonneg := abs_nonneg,
norm_sub_add := by exact abs_add,
norm_abs_hom := by simp,
norm_pos_def := by simp; exact abs_eq_zero,
}


import tactic
import data.set.finite

import topological_spaces
import neighbourhoods

open set

open topological_space

-- Convergence d'une suite :
def seq_lim {X : Type} [topological_space X] (u : ℕ → X) (l : X) : Prop :=
∀ (V : set X), V ∈ neighbourhoods l → ∃ (N : ℕ), ∀ n ≥ N, u n ∈ V

-- Fonction continue :
def continuous {X Y : Type} [topological_space X] [topological_space Y] (f : X → Y) : Prop :=
∀ (U : set Y), is_open U → is_open (f ⁻¹' U)

-- Une fonction est continue si et seulement si l'image réciproque de tout fermé est un fermé :
lemma continuous_closed {X Y : Type} [topological_space X] [topological_space Y] (f : X → Y) :
continuous f ↔ ∀ (F : set Y), is_closed F → is_closed (f ⁻¹' F) :=
begin
  split,
  { intro hyp,
    intros F hF,
    exact hyp (compl F) hF, },
  { intro hyp,
    intros U hU,
    rw ← compl_compl U at hU,
    rw ← compl_compl (f ⁻¹' U),
    exact hyp (compl U) hU, },
end

-- Intérieur d'une partie :
def interior {X : Type} [topological_space X] (A : set X) : set X := ⋃₀ {U : set X | is_open U ∧ U ⊆ A}

-- Point intérieur :
lemma interior_point {X : Type} [topological_space X] (A : set X) :
∀ a ∈ A, a ∈ interior A ↔ A ∈ neighbourhoods a :=
begin
  intros a ha,
  rw is_neighbourhood_iff,
  split,
  rintro ⟨U, ⟨hU, hUA⟩, haU⟩,
  use [U, hU, haU, hUA],
  rintro ⟨U, hU, hUA, haU⟩,
  use [U, hU, haU, hUA],
end

-- Adhérence d'une partie :
def closure {X : Type} [topological_space X] (A : set X) : set X := ⋂₀ {F : set X | is_closed F ∧ A ⊆ F}

-- Partie dense :
def dense {X : Type} [topological_space X] (A : set X) : Prop := closure A = univ

-- L'adhérence d'une partie est fermée :
lemma closure_closed {X : Type} [topological_space X] (A : set X) :
is_closed (closure A) :=
begin
  unfold is_closed, unfold closure,
  rw compl_sInter,
  apply union,
  rintros U ⟨F, ⟨hF, hAF⟩, hU⟩,
  rw ← hU,
  exact hF,
end

-- Une partie est fermée si et seulement si elle est égale à son adhérence :
lemma is_closed_iff {X : Type} [topological_space X] (F : set X) :
is_closed F ↔ F = closure F :=
begin
  split,
  { intro hyp,
    apply le_antisymm,
    { apply subset_sInter, simp, },
    { apply sInter_subset_of_mem,
      split,
      exact hyp,
      exact le_refl F, }, },
  { intro hyp,
    rw hyp,
    exact closure_closed F, },
end

-- Croissance de l'adhérence :
lemma closure_subset {X : Type} [topological_space X] {A B : set X} :
A ⊆ B → closure A ⊆ closure B :=
begin
  intro hyp,
  intros x hx,
  rintros F ⟨hF, hBF⟩,
  apply hx F, split,
  exact hF,
  rw ← le_eq_subset,
  exact le_trans hyp hBF,
end

-- Point adhérent :
lemma point_of_closure {X : Type} [topological_space X] (A : set X) :
∀ (x : X), x ∈ closure A ↔ ∀ (V : set X), V ∈ neighbourhoods x → (V ∩ A).nonempty :=
begin
  intro x,
  split,
  { intro hyp,
    intros V hV,
    rcases (is_neighbourhood_iff x).1 hV with ⟨U, hU, hxU, hUV⟩,
    by_contradiction hVA,
    have H1 : is_closed (compl U),
    { rw ← (compl_compl U) at hU,
      exact hU, },
    have H2 : A ⊆ compl U,
    { intros a haA,
      simp, by_contradiction haU,
      apply hVA,
      use a,
      exact ⟨hUV haU, haA⟩, },
    exact hyp (compl U) ⟨H1, H2⟩ hxU, },
  { intro hyp,
    rintros F ⟨hF, hAF⟩,
    by_contradiction,
    have clef : compl F ∈ neighbourhoods x,
      { apply generated_filter.generator,
        exact ⟨hF, h⟩, },
    cases hyp (compl F) clef with x hx,
    exact hx.1 (hAF hx.2), },
end

-- La limite d'une suite d'éléments de A est un point adhérent de A :
lemma seq_lim_closure {X : Type} [topological_space X] {A : set X} (u : ℕ → X) {l : X} :
(∀ n, u n ∈ A) ∧ (seq_lim u l) → l ∈ closure A :=
begin
  rintros ⟨h1, h2⟩,
  rw point_of_closure A l,
  intros V hV,
  cases h2 V hV with N hN,
  use u N,
  exact ⟨hN N (by linarith), h1 N⟩,
end

-- Une caractérisation de la continuité :
example {X Y : Type} [topological_space X] [topological_space Y] (f : X → Y) :
continuous f ↔ ∀ (A : set X), f '' (closure A) ⊆ closure (f '' A) :=
begin
  split,
  { intro hyp,
    intro A,
    rintros y ⟨x, hx, hy⟩,
    rw point_of_closure (f '' A) y,
    intros V hV,
    rcases (is_neighbourhood_iff y).1 hV with ⟨U, hU, hyU, hUV⟩,
    have clef : (f ⁻¹' U) ∈ neighbourhoods x,
    { apply generated_filter.generator,
      rw ← hy at hyU,
      exact ⟨hyp U hU, hyU⟩, },
    cases (point_of_closure A x).1 hx (f ⁻¹' U) clef with x' hx',
    use f x',
    split,
    exact hUV hx'.1,
    use x', simp [hx'.2], },
  { intro hyp,
    rw continuous_closed,
    intros F hF,
    rw is_closed_iff,
    apply le_antisymm,
    { apply subset_sInter, simp, },
    { specialize hyp (f ⁻¹' F),
      rw ← le_eq_subset at hyp,
      have hyp' := le_trans hyp (closure_subset (image_preimage_subset f F)),
      rw ← (is_closed_iff F).1 hF at hyp',
      simp at hyp', exact hyp', }, },
end


/-
Copyright (c) 2019 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import order.filter.basic

/-!
# Minimum and maximum w.r.t. a filter and on a aet

## Main Definitions

This file defines six predicates of the form `is_A_B`, where `A` is `min`, `max`, or `extr`,
and `B` is `filter` or `on`.

* `is_min_filter f l a` means that `f a ≤ f x` in some `l`-neighborhood of `a`;
* `is_max_filter f l a` means that `f x ≤ f a` in some `l`-neighborhood of `a`;
* `is_extr_filter f l a` means `is_min_filter f l a` or `is_max_filter f l a`.

Similar predicates with `_on` suffix are particular cases for `l = 𝓟 s`.

## Main statements

### Change of the filter (set) argument

* `is_*_filter.filter_mono` : replace the filter with a smaller one;
* `is_*_filter.filter_inf` : replace a filter `l` with `l ⊓ l'`;
* `is_*_on.on_subset` : restrict to a smaller set;
* `is_*_on.inter` : replace a set `s` wtih `s ∩ t`.

### Composition

* `is_*_*.comp_mono` : if `x` is an extremum for `f` and `g` is a monotone function,
  then `x` is an extremum for `g ∘ f`;
* `is_*_*.comp_antimono` : similarly for the case of monotonically decreasing `g`;
* `is_*_*.bicomp_mono` : if `x` is an extremum of the same type for `f` and `g`
  and a binary operation `op` is monotone in both arguments, then `x` is an extremum
  of the same type for `λ x, op (f x) (g x)`.
* `is_*_filter.comp_tendsto` : if `g x` is an extremum for `f` w.r.t. `l'` and `tendsto g l l'`,
  then `x` is an extremum for `f ∘ g` w.r.t. `l`.
* `is_*_on.on_preimage` : if `g x` is an extremum for `f` on `s`, then `x` is an extremum
  for `f ∘ g` on `g ⁻¹' s`.

### Algebraic operations

* `is_*_*.add` : if `x` is an extremum of the same type for two functions,
  then it is an extremum of the same type for their sum;
* `is_*_*.neg` : if `x` is an extremum for `f`, then it is an extremum
  of the opposite type for `-f`;
* `is_*_*.sub` : if `x` is an a minimum for `f` and a maximum for `g`,
  then it is a minimum for `f - g` and a maximum for `g - f`;
* `is_*_*.max`, `is_*_*.min`, `is_*_*.sup`, `is_*_*.inf` : similarly for `is_*_*.add`
  for pointwise `max`, `min`, `sup`, `inf`, respectively.

### Miscellaneous definitions

* `is_*_*_const` : any point is both a minimum and maximum for a constant function;
* `is_min/max_*.is_ext` : any minimum/maximum point is an extremum;
* `is_*_*.dual`, `is_*_*.undual`: conversion between codomains `α` and `dual α`;

## Missing features (TODO)

* Multiplication and division;
* `is_*_*.bicompl` : if `x` is a minimum for `f`, `y` is a minimum for `g`, and `op` is a monotone
  binary operation, then `(x, y)` is a minimum for `uncurry (bicompl op f g)`. From this point
  of view, `is_*_*.bicomp` is a composition
* It would be nice to have a tactic that specializes `comp_(anti)mono` or `bicomp_mono`
  based on a proof of monotonicity of a given (binary) function. The tactic should maintain a `meta`
  list of known (anti)monotone (binary) functions with their names, as well as a list of special
  types of filters, and define the missing lemmas once one of these two lists grows.
-/

universes u v w x

variables {α : Type u} {β : Type v} {γ : Type w} {δ : Type x}

open set filter
open_locale filter

section preorder

variables [preorder β] [preorder γ]

variables (f : α → β) (s : set α) (l : filter α) (a : α)

/-! ### Definitions -/

/-- `is_min_filter f l a` means that `f a ≤ f x` in some `l`-neighborhood of `a` -/
def is_min_filter : Prop := ∀ᶠ x in l, f a ≤ f x

/-- `is_max_filter f l a` means that `f x ≤ f a` in some `l`-neighborhood of `a` -/
def is_max_filter : Prop := ∀ᶠ x in l, f x ≤ f a

/-- `is_extr_filter f l a` means `is_min_filter f l a` or `is_max_filter f l a` -/
def is_extr_filter : Prop := is_min_filter f l a ∨ is_max_filter f l a

/-- `is_min_on f s a` means that `f a ≤ f x` for all `x ∈ a`. Note that we do not assume `a ∈ s`. -/
def is_min_on := is_min_filter f (𝓟 s) a

/-- `is_max_on f s a` means that `f x ≤ f a` for all `x ∈ a`. Note that we do not assume `a ∈ s`. -/
def is_max_on := is_max_filter f (𝓟 s) a

/-- `is_extr_on f s a` means `is_min_on f s a` or `is_max_on f s a` -/
def is_extr_on : Prop := is_extr_filter f (𝓟 s) a

variables {f s a l} {t : set α} {l' : filter α}

lemma is_extr_on.elim {p : Prop} :
  is_extr_on f s a → (is_min_on f s a → p) → (is_max_on f s a → p) → p :=
or.elim

lemma is_min_on_iff : is_min_on f s a ↔ ∀ x ∈ s, f a ≤ f x := iff.rfl

lemma is_max_on_iff : is_max_on f s a ↔ ∀ x ∈ s, f x ≤ f a := iff.rfl

lemma is_min_on_univ_iff : is_min_on f univ a ↔ ∀ x, f a ≤ f x :=
univ_subset_iff.trans eq_univ_iff_forall

lemma is_max_on_univ_iff : is_max_on f univ a ↔ ∀ x, f x ≤ f a :=
univ_subset_iff.trans eq_univ_iff_forall

lemma is_min_filter.tendsto_principal_Ici (h : is_min_filter f l a) :
  tendsto f l (𝓟 $ Ici (f a)) :=
tendsto_principal.2 h

lemma is_max_filter.tendsto_principal_Iic (h : is_max_filter f l a) :
  tendsto f l (𝓟 $ Iic (f a)) :=
tendsto_principal.2 h

/-! ### Conversion to `is_extr_*` -/

lemma is_min_filter.is_extr : is_min_filter f l a → is_extr_filter f l a := or.inl

lemma is_max_filter.is_extr : is_max_filter f l a → is_extr_filter f l a := or.inr

lemma is_min_on.is_extr (h : is_min_on f s a) : is_extr_on f s a := h.is_extr

lemma is_max_on.is_extr (h : is_max_on f s a) : is_extr_on f s a := h.is_extr

/-! ### Constant function -/

lemma is_min_filter_const {b : β} : is_min_filter (λ _, b) l a :=
univ_mem_sets' $ λ _, le_refl _

lemma is_max_filter_const {b : β} : is_max_filter (λ _, b) l a :=
univ_mem_sets' $ λ _, le_refl _

lemma is_extr_filter_const {b : β} : is_extr_filter (λ _, b) l a := is_min_filter_const.is_extr

lemma is_min_on_const {b : β} : is_min_on (λ _, b) s a := is_min_filter_const

lemma is_max_on_const {b : β} : is_max_on (λ _, b) s a := is_max_filter_const

lemma is_extr_on_const {b : β} : is_extr_on (λ _, b) s a := is_extr_filter_const

/-! ### Order dual -/

lemma is_min_filter_dual_iff : @is_min_filter α (order_dual β) _ f l a ↔ is_max_filter f l a :=
iff.rfl

lemma is_max_filter_dual_iff : @is_max_filter α (order_dual β) _ f l a ↔ is_min_filter f l a :=
iff.rfl

lemma is_extr_filter_dual_iff : @is_extr_filter α (order_dual β) _ f l a ↔ is_extr_filter f l a :=
or_comm _ _

alias is_min_filter_dual_iff ↔ is_min_filter.undual is_max_filter.dual
alias is_max_filter_dual_iff ↔ is_max_filter.undual is_min_filter.dual
alias is_extr_filter_dual_iff ↔ is_extr_filter.undual is_extr_filter.dual

lemma is_min_on_dual_iff : @is_min_on α (order_dual β) _ f s a ↔ is_max_on f s a := iff.rfl
lemma is_max_on_dual_iff : @is_max_on α (order_dual β) _ f s a ↔ is_min_on f s a := iff.rfl
lemma is_extr_on_dual_iff : @is_extr_on α (order_dual β) _ f s a ↔ is_extr_on f s a := or_comm _ _

alias is_min_on_dual_iff ↔ is_min_on.undual is_max_on.dual
alias is_max_on_dual_iff ↔ is_max_on.undual is_min_on.dual
alias is_extr_on_dual_iff ↔ is_extr_on.undual is_extr_on.dual

/-! ### Operations on the filter/set -/

lemma is_min_filter.filter_mono (h : is_min_filter f l a) (hl : l' ≤ l) :
  is_min_filter f l' a := hl h

lemma is_max_filter.filter_mono (h : is_max_filter f l a) (hl : l' ≤ l) :
  is_max_filter f l' a := hl h

lemma is_extr_filter.filter_mono (h : is_extr_filter f l a) (hl : l' ≤ l) :
  is_extr_filter f l' a :=
h.elim (λ h, (h.filter_mono hl).is_extr) (λ h, (h.filter_mono hl).is_extr)

lemma is_min_filter.filter_inf (h : is_min_filter f l a) (l') : is_min_filter f (l ⊓ l') a :=
h.filter_mono inf_le_left

lemma is_max_filter.filter_inf (h : is_max_filter f l a) (l') : is_max_filter f (l ⊓ l') a :=
h.filter_mono inf_le_left

lemma is_extr_filter.filter_inf (h : is_extr_filter f l a) (l') : is_extr_filter f (l ⊓ l') a :=
h.filter_mono inf_le_left

lemma is_min_on.on_subset (hf : is_min_on f t a) (h : s ⊆ t) : is_min_on f s a :=
hf.filter_mono $ principal_mono.2 h

lemma is_max_on.on_subset (hf : is_max_on f t a) (h : s ⊆ t) : is_max_on f s a :=
hf.filter_mono $ principal_mono.2 h

lemma is_extr_on.on_subset (hf : is_extr_on f t a) (h : s ⊆ t) : is_extr_on f s a :=
hf.filter_mono $ principal_mono.2 h

lemma is_min_on.inter (hf : is_min_on f s a) (t) : is_min_on f (s ∩ t) a :=
hf.on_subset (inter_subset_left s t)

lemma is_max_on.inter (hf : is_max_on f s a) (t) : is_max_on f (s ∩ t) a :=
hf.on_subset (inter_subset_left s t)

lemma is_extr_on.inter (hf : is_extr_on f s a) (t) : is_extr_on f (s ∩ t) a :=
hf.on_subset (inter_subset_left s t)

/-! ### Composition with (anti)monotone functions -/

lemma is_min_filter.comp_mono (hf : is_min_filter f l a) {g : β → γ} (hg : monotone g) :
  is_min_filter (g ∘ f) l a :=
mem_sets_of_superset hf $ λ x hx, hg hx

lemma is_max_filter.comp_mono (hf : is_max_filter f l a) {g : β → γ} (hg : monotone g) :
  is_max_filter (g ∘ f) l a :=
mem_sets_of_superset hf $ λ x hx, hg hx

lemma is_extr_filter.comp_mono (hf : is_extr_filter f l a) {g : β → γ} (hg : monotone g) :
  is_extr_filter (g ∘ f) l a :=
hf.elim (λ hf, (hf.comp_mono hg).is_extr)  (λ hf, (hf.comp_mono hg).is_extr)

lemma is_min_filter.comp_antimono (hf : is_min_filter f l a) {g : β → γ}
  (hg : ∀ ⦃x y⦄, x ≤ y → g y ≤ g x) :
  is_max_filter (g ∘ f) l a :=
hf.dual.comp_mono (λ x y h, hg h)

lemma is_max_filter.comp_antimono (hf : is_max_filter f l a) {g : β → γ}
  (hg : ∀ ⦃x y⦄, x ≤ y → g y ≤ g x) :
  is_min_filter (g ∘ f) l a :=
hf.dual.comp_mono (λ x y h, hg h)

lemma is_extr_filter.comp_antimono (hf : is_extr_filter f l a) {g : β → γ}
  (hg : ∀ ⦃x y⦄, x ≤ y → g y ≤ g x) :
  is_extr_filter (g ∘ f) l a :=
hf.dual.comp_mono (λ x y h, hg h)

lemma is_min_on.comp_mono (hf : is_min_on f s a) {g : β → γ} (hg : monotone g) :
  is_min_on (g ∘ f) s a :=
hf.comp_mono hg

lemma is_max_on.comp_mono (hf : is_max_on f s a) {g : β → γ} (hg : monotone g) :
  is_max_on (g ∘ f) s a :=
hf.comp_mono hg

lemma is_extr_on.comp_mono (hf : is_extr_on f s a) {g : β → γ} (hg : monotone g) :
  is_extr_on (g ∘ f) s a :=
hf.comp_mono hg

lemma is_min_on.comp_antimono (hf : is_min_on f s a) {g : β → γ}
  (hg : ∀ ⦃x y⦄, x ≤ y → g y ≤ g x) :
  is_max_on (g ∘ f) s a :=
hf.comp_antimono hg

lemma is_max_on.comp_antimono (hf : is_max_on f s a) {g : β → γ}
  (hg : ∀ ⦃x y⦄, x ≤ y → g y ≤ g x) :
  is_min_on (g ∘ f) s a :=
hf.comp_antimono hg

lemma is_extr_on.comp_antimono (hf : is_extr_on f s a) {g : β → γ}
  (hg : ∀ ⦃x y⦄, x ≤ y → g y ≤ g x) :
  is_extr_on (g ∘ f) s a :=
hf.comp_antimono hg

lemma is_min_filter.bicomp_mono [preorder δ] {op : β → γ → δ} (hop : ((≤) ⇒ (≤) ⇒ (≤)) op op)
  (hf : is_min_filter f l a) {g : α → γ} (hg : is_min_filter g l a) :
  is_min_filter (λ x, op (f x) (g x)) l a :=
mem_sets_of_superset (inter_mem_sets hf hg) $ λ x ⟨hfx, hgx⟩, hop hfx hgx

lemma is_max_filter.bicomp_mono [preorder δ] {op : β → γ → δ} (hop : ((≤) ⇒ (≤) ⇒ (≤)) op op)
  (hf : is_max_filter f l a) {g : α → γ} (hg : is_max_filter g l a) :
  is_max_filter (λ x, op (f x) (g x)) l a :=
mem_sets_of_superset (inter_mem_sets hf hg) $ λ x ⟨hfx, hgx⟩, hop hfx hgx

-- No `extr` version because we need `hf` and `hg` to be of the same kind

lemma is_min_on.bicomp_mono [preorder δ] {op : β → γ → δ} (hop : ((≤) ⇒ (≤) ⇒ (≤)) op op)
  (hf : is_min_on f s a) {g : α → γ} (hg : is_min_on g s a) :
  is_min_on (λ x, op (f x) (g x)) s a :=
hf.bicomp_mono hop hg

lemma is_max_on.bicomp_mono [preorder δ] {op : β → γ → δ} (hop : ((≤) ⇒ (≤) ⇒ (≤)) op op)
  (hf : is_max_on f s a) {g : α → γ} (hg : is_max_on g s a) :
  is_max_on (λ x, op (f x) (g x)) s a :=
hf.bicomp_mono hop hg

/-! ### Composition with `tendsto` -/

lemma is_min_filter.comp_tendsto {g : δ → α} {l' : filter δ} {b : δ} (hf : is_min_filter f l (g b))
  (hg : tendsto g l' l) :
  is_min_filter (f ∘ g) l' b :=
hg hf

lemma is_max_filter.comp_tendsto {g : δ → α} {l' : filter δ} {b : δ} (hf : is_max_filter f l (g b))
  (hg : tendsto g l' l) :
  is_max_filter (f ∘ g) l' b :=
hg hf

lemma is_extr_filter.comp_tendsto {g : δ → α} {l' : filter δ} {b : δ}
  (hf : is_extr_filter f l (g b)) (hg : tendsto g l' l) :
  is_extr_filter (f ∘ g) l' b :=
hf.elim (λ hf, (hf.comp_tendsto hg).is_extr) (λ hf, (hf.comp_tendsto hg).is_extr)

lemma is_min_on.on_preimage (g : δ → α) {b : δ} (hf : is_min_on f s (g b)) :
  is_min_on (f ∘ g) (g ⁻¹' s) b :=
hf.comp_tendsto (tendsto_principal_principal.mpr $ subset.refl _)

lemma is_max_on.on_preimage (g : δ → α) {b : δ} (hf : is_max_on f s (g b)) :
  is_max_on (f ∘ g) (g ⁻¹' s) b :=
hf.comp_tendsto (tendsto_principal_principal.mpr $ subset.refl _)

lemma is_extr_on.on_preimage (g : δ → α) {b : δ} (hf : is_extr_on f s (g b)) :
  is_extr_on (f ∘ g) (g ⁻¹' s) b :=
hf.elim (λ hf, (hf.on_preimage g).is_extr) (λ hf, (hf.on_preimage g).is_extr)

end preorder

/-! ### Pointwise addition -/
section ordered_add_comm_monoid

variables [ordered_add_comm_monoid β] {f g : α → β} {a : α} {s : set α} {l : filter α}

lemma is_min_filter.add (hf : is_min_filter f l a) (hg : is_min_filter g l a) :
  is_min_filter (λ x, f x + g x) l a :=
show is_min_filter (λ x, f x + g x) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, add_le_add hx hy) hg

lemma is_max_filter.add (hf : is_max_filter f l a) (hg : is_max_filter g l a) :
  is_max_filter (λ x, f x + g x) l a :=
show is_max_filter (λ x, f x + g x) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, add_le_add hx hy) hg

lemma is_min_on.add (hf : is_min_on f s a) (hg : is_min_on g s a) :
  is_min_on (λ x, f x + g x) s a :=
hf.add hg

lemma is_max_on.add (hf : is_max_on f s a) (hg : is_max_on g s a) :
  is_max_on (λ x, f x + g x) s a :=
hf.add hg

end ordered_add_comm_monoid

/-! ### Pointwise negation and subtraction -/

section ordered_add_comm_group

variables [ordered_add_comm_group β] {f g : α → β} {a : α} {s : set α} {l : filter α}

lemma is_min_filter.neg (hf : is_min_filter f l a) : is_max_filter (λ x, -f x) l a :=
hf.comp_antimono (λ x y hx, neg_le_neg hx)

lemma is_max_filter.neg (hf : is_max_filter f l a) : is_min_filter (λ x, -f x) l a :=
hf.comp_antimono (λ x y hx, neg_le_neg hx)

lemma is_extr_filter.neg (hf : is_extr_filter f l a) : is_extr_filter (λ x, -f x) l a :=
hf.elim (λ hf, hf.neg.is_extr) (λ hf, hf.neg.is_extr)

lemma is_min_on.neg (hf : is_min_on f s a) : is_max_on (λ x, -f x) s a :=
hf.comp_antimono (λ x y hx, neg_le_neg hx)

lemma is_max_on.neg (hf : is_max_on f s a) : is_min_on (λ x, -f x) s a :=
hf.comp_antimono (λ x y hx, neg_le_neg hx)

lemma is_extr_on.neg (hf : is_extr_on f s a) : is_extr_on (λ x, -f x) s a :=
hf.elim (λ hf, hf.neg.is_extr) (λ hf, hf.neg.is_extr)

lemma is_min_filter.sub (hf : is_min_filter f l a) (hg : is_max_filter g l a) :
  is_min_filter (λ x, f x - g x) l a :=
by simpa only [sub_eq_add_neg] using hf.add hg.neg

lemma is_max_filter.sub (hf : is_max_filter f l a) (hg : is_min_filter g l a) :
  is_max_filter (λ x, f x - g x) l a :=
by simpa only [sub_eq_add_neg] using hf.add hg.neg

lemma is_min_on.sub (hf : is_min_on f s a) (hg : is_max_on g s a) :
  is_min_on (λ x, f x - g x) s a :=
by simpa only [sub_eq_add_neg] using hf.add hg.neg

lemma is_max_on.sub (hf : is_max_on f s a) (hg : is_min_on g s a) :
  is_max_on (λ x, f x - g x) s a :=
by simpa only [sub_eq_add_neg] using hf.add hg.neg

end ordered_add_comm_group

/-! ### Pointwise `sup`/`inf` -/

section semilattice_sup

variables [semilattice_sup β] {f g : α → β} {a : α} {s : set α} {l : filter α}

lemma is_min_filter.sup (hf : is_min_filter f l a) (hg : is_min_filter g l a) :
  is_min_filter (λ x, f x ⊔ g x) l a :=
show is_min_filter (λ x, f x ⊔ g x) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, sup_le_sup hx hy) hg

lemma is_max_filter.sup (hf : is_max_filter f l a) (hg : is_max_filter g l a) :
  is_max_filter (λ x, f x ⊔ g x) l a :=
show is_max_filter (λ x, f x ⊔ g x) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, sup_le_sup hx hy) hg

lemma is_min_on.sup (hf : is_min_on f s a) (hg : is_min_on g s a) :
  is_min_on (λ x, f x ⊔ g x) s a :=
hf.sup hg

lemma is_max_on.sup (hf : is_max_on f s a) (hg : is_max_on g s a) :
  is_max_on (λ x, f x ⊔ g x) s a :=
hf.sup hg

end semilattice_sup

section semilattice_inf

variables [semilattice_inf β] {f g : α → β} {a : α} {s : set α} {l : filter α}

lemma is_min_filter.inf (hf : is_min_filter f l a) (hg : is_min_filter g l a) :
  is_min_filter (λ x, f x ⊓ g x) l a :=
show is_min_filter (λ x, f x ⊓ g x) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, inf_le_inf hx hy) hg

lemma is_max_filter.inf (hf : is_max_filter f l a) (hg : is_max_filter g l a) :
  is_max_filter (λ x, f x ⊓ g x) l a :=
show is_max_filter (λ x, f x ⊓ g x) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, inf_le_inf hx hy) hg

lemma is_min_on.inf (hf : is_min_on f s a) (hg : is_min_on g s a) :
  is_min_on (λ x, f x ⊓ g x) s a :=
hf.inf hg

lemma is_max_on.inf (hf : is_max_on f s a) (hg : is_max_on g s a) :
  is_max_on (λ x, f x ⊓ g x) s a :=
hf.inf hg

end semilattice_inf

/-! ### Pointwise `min`/`max` -/

section linear_order

variables [linear_order β] {f g : α → β} {a : α} {s : set α} {l : filter α}

lemma is_min_filter.min (hf : is_min_filter f l a) (hg : is_min_filter g l a) :
  is_min_filter (λ x, min (f x) (g x)) l a :=
show is_min_filter (λ x, min (f x) (g x)) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, min_le_min hx hy) hg

lemma is_max_filter.min (hf : is_max_filter f l a) (hg : is_max_filter g l a) :
  is_max_filter (λ x, min (f x) (g x)) l a :=
show is_max_filter (λ x, min (f x) (g x)) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, min_le_min hx hy) hg

lemma is_min_on.min (hf : is_min_on f s a) (hg : is_min_on g s a) :
  is_min_on (λ x, min (f x) (g x)) s a :=
hf.min hg

lemma is_max_on.min (hf : is_max_on f s a) (hg : is_max_on g s a) :
  is_max_on (λ x, min (f x) (g x)) s a :=
hf.min hg

lemma is_min_filter.max (hf : is_min_filter f l a) (hg : is_min_filter g l a) :
  is_min_filter (λ x, max (f x) (g x)) l a :=
show is_min_filter (λ x, max (f x) (g x)) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, max_le_max hx hy) hg

lemma is_max_filter.max (hf : is_max_filter f l a) (hg : is_max_filter g l a) :
  is_max_filter (λ x, max (f x) (g x)) l a :=
show is_max_filter (λ x, max (f x) (g x)) l a,
from hf.bicomp_mono (λ x x' hx y y' hy, max_le_max hx hy) hg

lemma is_min_on.max (hf : is_min_on f s a) (hg : is_min_on g s a) :
  is_min_on (λ x, max (f x) (g x)) s a :=
hf.max hg

lemma is_max_on.max (hf : is_max_on f s a) (hg : is_max_on g s a) :
  is_max_on (λ x, max (f x) (g x)) s a :=
hf.max hg

end linear_order

section eventually

/-! ### Relation with `eventually` comparisons of two functions -/

lemma filter.eventually_le.is_max_filter {α β : Type*} [preorder β] {f g : α → β} {a : α}
  {l : filter α} (hle : g ≤ᶠ[l] f) (hfga : f a = g a) (h : is_max_filter f l a) :
  is_max_filter g l a :=
begin
  refine hle.mp (h.mono $ λ x hf hgf, _),
  rw ← hfga,
  exact le_trans hgf hf
end

lemma is_max_filter.congr {α β : Type*} [preorder β] {f g : α → β} {a : α} {l : filter α}
  (h : is_max_filter f l a) (heq : f =ᶠ[l] g) (hfga : f a = g a) :
  is_max_filter g l a :=
heq.symm.le.is_max_filter hfga h

lemma filter.eventually_eq.is_max_filter_iff {α β : Type*} [preorder β] {f g : α → β} {a : α}
  {l : filter α} (heq : f =ᶠ[l] g) (hfga : f a = g a) :
  is_max_filter f l a ↔ is_max_filter g l a :=
⟨λ h, h.congr heq hfga, λ h, h.congr heq.symm hfga.symm⟩

lemma filter.eventually_le.is_min_filter {α β : Type*} [preorder β] {f g : α → β} {a : α}
  {l : filter α} (hle : f ≤ᶠ[l] g) (hfga : f a = g a) (h : is_min_filter f l a) :
  is_min_filter g l a :=
@filter.eventually_le.is_max_filter _ (order_dual β) _ _ _ _ _ hle hfga h

lemma is_min_filter.congr {α β : Type*} [preorder β] {f g : α → β} {a : α} {l : filter α}
  (h : is_min_filter f l a) (heq : f =ᶠ[l] g) (hfga : f a = g a) :
  is_min_filter g l a :=
heq.le.is_min_filter hfga h

lemma filter.eventually_eq.is_min_filter_iff {α β : Type*} [preorder β] {f g : α → β} {a : α}
  {l : filter α} (heq : f =ᶠ[l] g) (hfga : f a = g a) :
  is_min_filter f l a ↔ is_min_filter g l a :=
⟨λ h, h.congr heq hfga, λ h, h.congr heq.symm hfga.symm⟩

lemma is_extr_filter.congr {α β : Type*} [preorder β] {f g : α → β} {a : α} {l : filter α}
  (h : is_extr_filter f l a) (heq : f =ᶠ[l] g) (hfga : f a = g a) :
  is_extr_filter g l a :=
begin
  rw is_extr_filter at *,
  rwa [← heq.is_max_filter_iff hfga, ← heq.is_min_filter_iff hfga],
end

lemma filter.eventually_eq.is_extr_filter_iff {α β : Type*} [preorder β] {f g : α → β} {a : α}
  {l : filter α} (heq : f =ᶠ[l] g) (hfga : f a = g a) :
  is_extr_filter f l a ↔ is_extr_filter g l a :=
⟨λ h, h.congr heq hfga, λ h, h.congr heq.symm hfga.symm⟩

end eventually