/-
Copyright (c) 2018 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin

! This file was ported from Lean 3 source module data.real.nnreal
! leanprover-community/mathlib commit b3f4f007a962e3787aa0f3b5c7942a1317f7d88e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Algebra.Basic
import Mathlib.Algebra.Order.Field.Canonical.Basic
import Mathlib.Algebra.Order.Nonneg.Field
import Mathlib.Algebra.Order.Nonneg.Floor
import Mathlib.Data.Real.Pointwise
import Mathlib.Order.ConditionallyCompleteLattice.Group
import Mathlib.Tactic.Positivity

/-!
# Nonnegative real numbers

In this file we define `NNReal` (notation: `ℝ≥0`) to be the type of non-negative real numbers,
a.k.a. the interval `[0, ∞)`. We also define the following operations and structures on `ℝ≥0`:

* the order on `ℝ≥0` is the restriction of the order on `ℝ`; these relations define a conditionally
  complete linear order with a bottom element, `ConditionallyCompleteLinearOrderBot`;

* `a + b` and `a * b` are the restrictions of addition and multiplication of real numbers to `ℝ≥0`;
  these operations together with `0 = ⟨0, _⟩` and `1 = ⟨1, _⟩` turn `ℝ≥0` into a conditionally
  complete linear ordered archimedean commutative semifield; we have no typeclass for this in
  `mathlib` yet, so we define the following instances instead:

  - `LinearOrderedSemiring ℝ≥0`;
  - `OrderedCommSemiring ℝ≥0`;
  - `CanonicallyOrderedCommSemiring ℝ≥0`;
  - `LinearOrderedCommGroupWithZero ℝ≥0`;
  - `CanonicallyLinearOrderedAddMonoid ℝ≥0`;
  - `Archimedean ℝ≥0`;
  - `ConditionallyCompleteLinearOrderBot ℝ≥0`.

  These instances are derived from corresponding instances about the type `{x : α // 0 ≤ x}` in an
  appropriate ordered field/ring/group/monoid `α`, see `Mathlib.Algebra.Order.Nonneg.Ring`.

* `Real.toNNReal x` is defined as `⟨max x 0, _⟩`, i.e. `↑(Real.toNNReal x) = x` when `0 ≤ x` and
  `↑(Real.toNNReal x) = 0` otherwise.

We also define an instance `CanLift ℝ ℝ≥0`. This instance can be used by the `lift` tactic to
replace `x : ℝ` and `hx : 0 ≤ x` in the proof context with `x : ℝ≥0` while replacing all occurences
of `x` with `↑x`. This tactic also works for a function `f : α → ℝ` with a hypothesis
`hf : ∀ x, 0 ≤ f x`.

## Notations

This file defines `ℝ≥0` as a localized notation for `NNReal`.
-/

open BigOperators Function

-- to ensure these instances are computable
/-- Nonnegative real numbers. -/
def NNReal := { r : ℝ // 0 ≤ r } deriving
  Zero, One, Semiring, StrictOrderedSemiring, CommMonoidWithZero, CommSemiring,
  SemilatticeInf, SemilatticeSup, DistribLattice, OrderedCommSemiring,
  CanonicallyOrderedCommSemiring, Inhabited
#align nnreal NNReal

namespace NNReal

-- mathport name: nnreal
scoped notation "ℝ≥0" => NNReal

noncomputable instance : FloorSemiring ℝ≥0 := Nonneg.floorSemiring
instance : DenselyOrdered ℝ≥0 := Nonneg.densely_ordered
instance : OrderBot ℝ≥0 := inferInstance
instance : Archimedean ℝ≥0 := Nonneg.archimedean
noncomputable instance : Sub ℝ≥0 := Nonneg.sub
noncomputable instance : OrderedSub ℝ≥0 := Nonneg.orderedSub

noncomputable instance : CanonicallyLinearOrderedSemifield ℝ≥0 :=
  Nonneg.canonicallyLinearOrderedSemifield

/-- Coercion `ℝ≥0 → ℝ`. -/
@[coe] def toReal : ℝ≥0 → ℝ := Subtype.val

instance : Coe ℝ≥0 ℝ := ⟨toReal⟩

-- Simp lemma to put back `n.val` into the normal form given by the coercion.
@[simp]
theorem val_eq_coe (n : ℝ≥0) : n.val = n :=
  rfl
#align nnreal.val_eq_coe NNReal.val_eq_coe

instance canLift : CanLift ℝ ℝ≥0 toReal fun r => 0 ≤ r :=
  Subtype.canLift _
#align nnreal.can_lift NNReal.canLift

@[ext] protected theorem eq {n m : ℝ≥0} : (n : ℝ) = (m : ℝ) → n = m :=
  Subtype.eq
#align nnreal.eq NNReal.eq

protected theorem eq_iff {n m : ℝ≥0} : (n : ℝ) = (m : ℝ) ↔ n = m :=
  Subtype.ext_iff.symm
#align nnreal.eq_iff NNReal.eq_iff

theorem ne_iff {x y : ℝ≥0} : (x : ℝ) ≠ (y : ℝ) ↔ x ≠ y :=
  not_congr <| NNReal.eq_iff
#align nnreal.ne_iff NNReal.ne_iff

protected theorem «forall» {p : ℝ≥0 → Prop} :
    (∀ x : ℝ≥0, p x) ↔ ∀ (x : ℝ) (hx : 0 ≤ x), p ⟨x, hx⟩ :=
  Subtype.forall
#align nnreal.forall NNReal.forall

protected theorem «exists» {p : ℝ≥0 → Prop} :
    (∃ x : ℝ≥0, p x) ↔ ∃ (x : ℝ) (hx : 0 ≤ x), p ⟨x, hx⟩ :=
  Subtype.exists
#align nnreal.exists NNReal.exists

/-- Reinterpret a real number `r` as a non-negative real number. Returns `0` if `r < 0`. -/
noncomputable def _root_.Real.toNNReal (r : ℝ) : ℝ≥0 :=
  ⟨max r 0, le_max_right _ _⟩
#align real.to_nnreal Real.toNNReal

theorem _root_.Real.coe_toNNReal (r : ℝ) (hr : 0 ≤ r) : (Real.toNNReal r : ℝ) = r :=
  max_eq_left hr
#align real.coe_to_nnreal Real.coe_toNNReal

theorem _root_.Real.toNNReal_of_nonneg {r : ℝ} (hr : 0 ≤ r) : r.toNNReal = ⟨r, hr⟩ := by
  simp_rw [Real.toNNReal, max_eq_left hr]
#align real.to_nnreal_of_nonneg Real.toNNReal_of_nonneg

theorem _root_.Real.le_coe_toNNReal (r : ℝ) : r ≤ Real.toNNReal r :=
  le_max_left r 0
#align real.le_coe_to_nnreal Real.le_coe_toNNReal

theorem coe_nonneg (r : ℝ≥0) : (0 : ℝ) ≤ r := r.2
#align nnreal.coe_nonneg NNReal.coe_nonneg

@[simp, norm_cast] theorem coe_mk (a : ℝ) (ha) : toReal ⟨a, ha⟩ = a := rfl
#align nnreal.coe_mk NNReal.coe_mk

example : Zero ℝ≥0 := by infer_instance

example : One ℝ≥0 := by infer_instance

example : Add ℝ≥0 := by infer_instance

noncomputable example : Sub ℝ≥0 := by infer_instance

example : Mul ℝ≥0 := by infer_instance

noncomputable example : Inv ℝ≥0 := by infer_instance

noncomputable example : Div ℝ≥0 := by infer_instance

example : LE ℝ≥0 := by infer_instance

example : Bot ℝ≥0 := by infer_instance

example : Inhabited ℝ≥0 := by infer_instance

example : Nontrivial ℝ≥0 := by infer_instance

protected theorem coe_injective : Injective ((↑) : ℝ≥0 → ℝ) := Subtype.coe_injective
#align nnreal.coe_injective NNReal.coe_injective

@[simp, norm_cast]
protected theorem coe_eq {r₁ r₂ : ℝ≥0} : (r₁ : ℝ) = r₂ ↔ r₁ = r₂ :=
  NNReal.coe_injective.eq_iff
#align nnreal.coe_eq NNReal.coe_eq

@[simp, norm_cast]
protected theorem coe_zero : ((0 : ℝ≥0) : ℝ) = 0 :=
  rfl
#align nnreal.coe_zero NNReal.coe_zero

@[simp, norm_cast]
protected theorem coe_one : ((1 : ℝ≥0) : ℝ) = 1 :=
  rfl
#align nnreal.coe_one NNReal.coe_one

@[simp, norm_cast]
protected theorem coe_add (r₁ r₂ : ℝ≥0) : ((r₁ + r₂ : ℝ≥0) : ℝ) = r₁ + r₂ :=
  rfl
#align nnreal.coe_add NNReal.coe_add

@[simp, norm_cast]
protected theorem coe_mul (r₁ r₂ : ℝ≥0) : ((r₁ * r₂ : ℝ≥0) : ℝ) = r₁ * r₂ :=
  rfl
#align nnreal.coe_mul NNReal.coe_mul

@[simp, norm_cast]
protected theorem coe_inv (r : ℝ≥0) : ((r⁻¹ : ℝ≥0) : ℝ) = (r : ℝ)⁻¹ :=
  rfl
#align nnreal.coe_inv NNReal.coe_inv

@[simp, norm_cast]
protected theorem coe_div (r₁ r₂ : ℝ≥0) : ((r₁ / r₂ : ℝ≥0) : ℝ) = (r₁ : ℝ) / r₂ :=
  rfl
#align nnreal.coe_div NNReal.coe_div

#noalign nnreal.coe_bit0
#noalign nnreal.coe_bit1

protected theorem coe_two : ((2 : ℝ≥0) : ℝ) = 2 := rfl
#align nnreal.coe_two NNReal.coe_two

@[simp, norm_cast]
protected theorem coe_sub {r₁ r₂ : ℝ≥0} (h : r₂ ≤ r₁) : ((r₁ - r₂ : ℝ≥0) : ℝ) = ↑r₁ - ↑r₂ :=
  max_eq_left <| le_sub_comm.2 <| by simp [show (r₂ : ℝ) ≤ r₁ from h]
#align nnreal.coe_sub NNReal.coe_sub

@[simp, norm_cast]
protected theorem coe_eq_zero (r : ℝ≥0) : ↑r = (0 : ℝ) ↔ r = 0 := by
  rw [← NNReal.coe_zero, NNReal.coe_eq]
#align nnreal.coe_eq_zero NNReal.coe_eq_zero

@[simp, norm_cast]
protected theorem coe_eq_one (r : ℝ≥0) : ↑r = (1 : ℝ) ↔ r = 1 := by
  rw [← NNReal.coe_one, NNReal.coe_eq]
#align nnreal.coe_eq_one NNReal.coe_eq_one

-- porting note: why `norm_cast` fails?
theorem coe_ne_zero {r : ℝ≥0} : (r : ℝ) ≠ 0 ↔ r ≠ 0 := r.coe_eq_zero.not
#align nnreal.coe_ne_zero NNReal.coe_ne_zero

example : CommSemiring ℝ≥0 := by infer_instance

/-- Coercion `ℝ≥0 → ℝ` as a `RingHom`.

Porting note: todo: what if we define `Coe ℝ≥0 ℝ` using this function? -/
def toRealHom : ℝ≥0 →+* ℝ where
  toFun := (↑)
  map_one' := NNReal.coe_one
  map_mul' := NNReal.coe_mul
  map_zero' := NNReal.coe_zero
  map_add' := NNReal.coe_add
#align nnreal.to_real_hom NNReal.toRealHom

@[simp] theorem coe_toRealHom : ⇑toRealHom = toReal := rfl
#align nnreal.coe_to_real_hom NNReal.coe_toRealHom

section Actions

/-- A `MulAction` over `ℝ` restricts to a `MulAction` over `ℝ≥0`. -/
instance {M : Type _} [MulAction ℝ M] : MulAction ℝ≥0 M :=
  MulAction.compHom M toRealHom.toMonoidHom

theorem smul_def {M : Type _} [MulAction ℝ M] (c : ℝ≥0) (x : M) : c • x = (c : ℝ) • x :=
  rfl
#align nnreal.smul_def NNReal.smul_def

instance {M N : Type _} [MulAction ℝ M] [MulAction ℝ N] [SMul M N] [IsScalarTower ℝ M N] :
    IsScalarTower ℝ≥0 M N where smul_assoc r := (smul_assoc (r : ℝ) : _)

instance smulCommClass_left {M N : Type _} [MulAction ℝ N] [SMul M N] [SMulCommClass ℝ M N] :
    SMulCommClass ℝ≥0 M N where smul_comm r := (smul_comm (r : ℝ) : _)
#align nnreal.smul_comm_class_left NNReal.smulCommClass_left

instance smulCommClass_right {M N : Type _} [MulAction ℝ N] [SMul M N] [SMulCommClass M ℝ N] :
    SMulCommClass M ℝ≥0 N where smul_comm m r := (smul_comm m (r : ℝ) : _)
#align nnreal.smul_comm_class_right NNReal.smulCommClass_right

/-- A `DistribMulAction` over `ℝ` restricts to a `DistribMulAction` over `ℝ≥0`. -/
instance {M : Type _} [AddMonoid M] [DistribMulAction ℝ M] : DistribMulAction ℝ≥0 M :=
  DistribMulAction.compHom M toRealHom.toMonoidHom

/-- A `Module` over `ℝ` restricts to a `Module` over `ℝ≥0`. -/
instance {M : Type _} [AddCommMonoid M] [Module ℝ M] : Module ℝ≥0 M :=
  Module.compHom M toRealHom

-- porting note: TODO: after this line, `↑` uses `Algebra.cast` instead of `toReal`
/-- An `Algebra` over `ℝ` restricts to an `Algebra` over `ℝ≥0`. -/
instance {A : Type _} [Semiring A] [Algebra ℝ A] : Algebra ℝ≥0 A where
  smul := (· • ·)
  commutes' r x := by simp [Algebra.commutes]
  smul_def' r x := by simp [← Algebra.smul_def (r : ℝ) x, smul_def]
  toRingHom := (algebraMap ℝ A).comp (toRealHom : ℝ≥0 →+* ℝ)

-- verify that the above produces instances we might care about
example : Algebra ℝ≥0 ℝ := by infer_instance

example : DistribMulAction ℝ≥0ˣ ℝ := by infer_instance

end Actions

example : MonoidWithZero ℝ≥0 := by infer_instance

example : CommMonoidWithZero ℝ≥0 := by infer_instance

noncomputable example : CommGroupWithZero ℝ≥0 := by infer_instance

@[simp, norm_cast]
theorem coe_indicator {α} (s : Set α) (f : α → ℝ≥0) (a : α) :
    ((s.indicator f a : ℝ≥0) : ℝ) = s.indicator (fun x => ↑(f x)) a :=
  (toRealHom : ℝ≥0 →+ ℝ).map_indicator _ _ _
#align nnreal.coe_indicator NNReal.coe_indicator

@[simp, norm_cast]
theorem coe_pow (r : ℝ≥0) (n : ℕ) : ((r ^ n : ℝ≥0) : ℝ) = (r : ℝ) ^ n := rfl
#align nnreal.coe_pow NNReal.coe_pow

@[simp, norm_cast]
theorem coe_zpow (r : ℝ≥0) (n : ℤ) : ((r ^ n : ℝ≥0) : ℝ) = (r : ℝ) ^ n := rfl
#align nnreal.coe_zpow NNReal.coe_zpow

@[norm_cast]
theorem coe_list_sum (l : List ℝ≥0) : ((l.sum : ℝ≥0) : ℝ) = (l.map (↑)).sum :=
  toRealHom.map_list_sum l
#align nnreal.coe_list_sum NNReal.coe_list_sum

@[norm_cast]
theorem coe_list_prod (l : List ℝ≥0) : ((l.prod : ℝ≥0) : ℝ) = (l.map (↑)).prod :=
  toRealHom.map_list_prod l
#align nnreal.coe_list_prod NNReal.coe_list_prod

@[norm_cast]
theorem coe_multiset_sum (s : Multiset ℝ≥0) : ((s.sum : ℝ≥0) : ℝ) = (s.map (↑)).sum :=
  toRealHom.map_multiset_sum s
#align nnreal.coe_multiset_sum NNReal.coe_multiset_sum

@[norm_cast]
theorem coe_multiset_prod (s : Multiset ℝ≥0) : ((s.prod : ℝ≥0) : ℝ) = (s.map (↑)).prod :=
  toRealHom.map_multiset_prod s
#align nnreal.coe_multiset_prod NNReal.coe_multiset_prod

@[norm_cast]
theorem coe_sum {α} {s : Finset α} {f : α → ℝ≥0} : ↑(∑ a in s, f a) = ∑ a in s, (f a : ℝ) :=
  toRealHom.map_sum _ _
#align nnreal.coe_sum NNReal.coe_sum

theorem _root_.Real.toNNReal_sum_of_nonneg {α} {s : Finset α} {f : α → ℝ}
    (hf : ∀ a, a ∈ s → 0 ≤ f a) :
    Real.toNNReal (∑ a in s, f a) = ∑ a in s, Real.toNNReal (f a) := by
  rw [← NNReal.coe_eq, NNReal.coe_sum, Real.coe_toNNReal _ (Finset.sum_nonneg hf)]
  exact Finset.sum_congr rfl fun x hxs => by rw [Real.coe_toNNReal _ (hf x hxs)]
#align real.to_nnreal_sum_of_nonneg Real.toNNReal_sum_of_nonneg

@[norm_cast]
theorem coe_prod {α} {s : Finset α} {f : α → ℝ≥0} : ↑(∏ a in s, f a) = ∏ a in s, (f a : ℝ) :=
  toRealHom.map_prod _ _
#align nnreal.coe_prod NNReal.coe_prod

theorem _root_.Real.toNNReal_prod_of_nonneg {α} {s : Finset α} {f : α → ℝ}
    (hf : ∀ a, a ∈ s → 0 ≤ f a) :
    Real.toNNReal (∏ a in s, f a) = ∏ a in s, Real.toNNReal (f a) := by
  rw [← NNReal.coe_eq, NNReal.coe_prod, Real.coe_toNNReal _ (Finset.prod_nonneg hf)]
  exact Finset.prod_congr rfl fun x hxs => by rw [Real.coe_toNNReal _ (hf x hxs)]
#align real.to_nnreal_prod_of_nonneg Real.toNNReal_prod_of_nonneg

-- porting note: todo: `simp`? `norm_cast`?
theorem coe_nsmul (r : ℝ≥0) (n : ℕ) : ↑(n • r) = n • (r : ℝ) := rfl
#align nnreal.nsmul_coe NNReal.coe_nsmul

@[simp, norm_cast]
protected theorem coe_nat_cast (n : ℕ) : (↑(↑n : ℝ≥0) : ℝ) = n :=
  map_natCast toRealHom n
#align nnreal.coe_nat_cast NNReal.coe_nat_cast

@[simp, norm_cast]
protected theorem coe_ofNat (n : ℕ) [n.AtLeastTwo] : ((OfNat.ofNat n : ℝ≥0) : ℝ) = OfNat.ofNat n :=
  rfl

noncomputable example : LinearOrder ℝ≥0 := by infer_instance

@[simp, norm_cast]
protected theorem coe_le_coe {r₁ r₂ : ℝ≥0} : (r₁ : ℝ) ≤ r₂ ↔ r₁ ≤ r₂ :=
  Iff.rfl
#align nnreal.coe_le_coe NNReal.coe_le_coe

@[simp, norm_cast]
protected theorem coe_lt_coe {r₁ r₂ : ℝ≥0} : (r₁ : ℝ) < r₂ ↔ r₁ < r₂ :=
  Iff.rfl
#align nnreal.coe_lt_coe NNReal.coe_lt_coe

@[simp, norm_cast]
protected theorem coe_pos {r : ℝ≥0} : (0 : ℝ) < r ↔ 0 < r :=
  Iff.rfl
#align nnreal.coe_pos NNReal.coe_pos

protected theorem coe_mono : Monotone ((↑) : ℝ≥0 → ℝ) := fun _ _ => NNReal.coe_le_coe.2
#align nnreal.coe_mono NNReal.coe_mono

protected theorem _root_.Real.toNNReal_mono : Monotone Real.toNNReal := fun _ _ h =>
  max_le_max h (le_refl 0)
#align real.to_nnreal_mono Real.toNNReal_mono

@[simp]
theorem _root_.Real.toNNReal_coe {r : ℝ≥0} : Real.toNNReal r = r :=
  NNReal.eq <| max_eq_left r.2
#align real.to_nnreal_coe Real.toNNReal_coe

@[simp]
theorem mk_coe_nat (n : ℕ) : @Eq ℝ≥0 (⟨(n : ℝ), n.cast_nonneg⟩ : ℝ≥0) n :=
  NNReal.eq (NNReal.coe_nat_cast n).symm
#align nnreal.mk_coe_nat NNReal.mk_coe_nat

@[simp]
theorem toNNReal_coe_nat (n : ℕ) : Real.toNNReal n = n :=
  NNReal.eq <| by simp [Real.coe_toNNReal]
#align nnreal.to_nnreal_coe_nat NNReal.toNNReal_coe_nat

/-- `Real.toNNReal` and `NNReal.toReal : ℝ≥0 → ℝ` form a Galois insertion. -/
noncomputable def gi : GaloisInsertion Real.toNNReal (↑) :=
  GaloisInsertion.monotoneIntro NNReal.coe_mono Real.toNNReal_mono Real.le_coe_toNNReal fun _ =>
    Real.toNNReal_coe
#align nnreal.gi NNReal.gi

-- note that anything involving the (decidability of the) linear order,
-- will be noncomputable, everything else should not be.
example : OrderBot ℝ≥0 := by infer_instance

example : PartialOrder ℝ≥0 := by infer_instance

noncomputable example : CanonicallyLinearOrderedAddMonoid ℝ≥0 := by infer_instance

noncomputable example : LinearOrderedAddCommMonoid ℝ≥0 := by infer_instance

example : DistribLattice ℝ≥0 := by infer_instance

example : SemilatticeInf ℝ≥0 := by infer_instance

example : SemilatticeSup ℝ≥0 := by infer_instance

noncomputable example : LinearOrderedSemiring ℝ≥0 := by infer_instance

example : OrderedCommSemiring ℝ≥0 := by infer_instance

noncomputable example : LinearOrderedCommMonoid ℝ≥0 := by infer_instance

noncomputable example : LinearOrderedCommMonoidWithZero ℝ≥0 := by infer_instance

noncomputable example : LinearOrderedCommGroupWithZero ℝ≥0 := by infer_instance

example : CanonicallyOrderedCommSemiring ℝ≥0 := by infer_instance

example : DenselyOrdered ℝ≥0 := by infer_instance

example : NoMaxOrder ℝ≥0 := by infer_instance

/-- If `a` is a nonnegative real number, then the closed interval `[0, a]` in `ℝ` is order
isomorphic to the interval `Set.Iic a`. -/
-- porting note: todo: restore once `simps` supports `ℝ≥0` @[simps!? apply_coe_coe]
def orderIsoIccZeroCoe (a : ℝ≥0) : Set.Icc (0 : ℝ) a ≃o Set.Iic a where
  toEquiv := Equiv.Set.sep (Set.Ici 0) fun x : ℝ => x ≤ a
  map_rel_iff' := Iff.rfl
#align nnreal.order_iso_Icc_zero_coe NNReal.orderIsoIccZeroCoe

@[simp]
theorem orderIsoIccZeroCoe_apply_coe_coe (a : ℝ≥0) (b : Set.Icc (0 : ℝ) a) :
    (orderIsoIccZeroCoe a b : ℝ) = b :=
  rfl

@[simp]
theorem orderIsoIccZeroCoe_symm_apply_coe (a : ℝ≥0) (b : Set.Iic a) :
    ((orderIsoIccZeroCoe a).symm b : ℝ) = b :=
  rfl
#align nnreal.order_iso_Icc_zero_coe_symm_apply_coe NNReal.orderIsoIccZeroCoe_symm_apply_coe

-- note we need the `@` to make the `Membership.mem` have a sensible type
theorem coe_image {s : Set ℝ≥0} :
    (↑) '' s = { x : ℝ | ∃ h : 0 ≤ x, @Membership.mem ℝ≥0 _ _ ⟨x, h⟩ s } :=
  Subtype.coe_image
#align nnreal.coe_image NNReal.coe_image

theorem bddAbove_coe {s : Set ℝ≥0} : BddAbove (((↑) : ℝ≥0 → ℝ) '' s) ↔ BddAbove s :=
  Iff.intro
    (fun ⟨b, hb⟩ =>
      ⟨Real.toNNReal b, fun ⟨y, _⟩ hys =>
        show y ≤ max b 0 from le_max_of_le_left <| hb <| Set.mem_image_of_mem _ hys⟩)
    fun ⟨b, hb⟩ => ⟨b, fun _ ⟨_, hx, eq⟩ => eq ▸ hb hx⟩
#align nnreal.bdd_above_coe NNReal.bddAbove_coe

theorem bddBelow_coe (s : Set ℝ≥0) : BddBelow (((↑) : ℝ≥0 → ℝ) '' s) :=
  ⟨0, fun _ ⟨q, _, eq⟩ => eq ▸ q.2⟩
#align nnreal.bdd_below_coe NNReal.bddBelow_coe

noncomputable instance : ConditionallyCompleteLinearOrderBot ℝ≥0 :=
  Nonneg.conditionallyCompleteLinearOrderBot Real.supₛ_empty.le

@[norm_cast]
theorem coe_supₛ (s : Set ℝ≥0) : (↑(supₛ s) : ℝ) = supₛ (((↑) : ℝ≥0 → ℝ) '' s) :=
  Eq.symm <|
    @subset_supₛ_of_within ℝ (Set.Ici 0) _ ⟨(0 : ℝ≥0)⟩ s <|
      Real.supₛ_nonneg _ fun _y ⟨x, _, hy⟩ => hy ▸ x.2
#align nnreal.coe_Sup NNReal.coe_supₛ

@[simp, norm_cast] -- porting note: add `simp`
theorem coe_supᵢ {ι : Sort _} (s : ι → ℝ≥0) : (↑(⨆ i, s i) : ℝ) = ⨆ i, ↑(s i) := by
  rw [supᵢ, supᵢ, coe_supₛ, ← Set.range_comp]; rfl
#align nnreal.coe_supr NNReal.coe_supᵢ

@[norm_cast]
theorem coe_infₛ (s : Set ℝ≥0) : (↑(infₛ s) : ℝ) = infₛ (((↑) : ℝ≥0 → ℝ) '' s) :=
  Eq.symm <|
    @subset_infₛ_of_within ℝ (Set.Ici 0) _ ⟨(0 : ℝ≥0)⟩ s <|
      Real.infₛ_nonneg _ fun _y ⟨x, _, hy⟩ => hy ▸ x.2
#align nnreal.coe_Inf NNReal.coe_infₛ

@[simp]
theorem infₛ_empty : infₛ (∅ : Set ℝ≥0) = 0 := by
  rw [← NNReal.coe_eq_zero, coe_infₛ, Set.image_empty, Real.infₛ_empty]
#align nnreal.Inf_empty NNReal.infₛ_empty

@[norm_cast]
theorem coe_infᵢ {ι : Sort _} (s : ι → ℝ≥0) : (↑(⨅ i, s i) : ℝ) = ⨅ i, ↑(s i) := by
  rw [infᵢ, infᵢ, coe_infₛ, ← Set.range_comp]; rfl
#align nnreal.coe_infi NNReal.coe_infᵢ

theorem le_infᵢ_add_infᵢ {ι ι' : Sort _} [Nonempty ι] [Nonempty ι'] {f : ι → ℝ≥0} {g : ι' → ℝ≥0}
    {a : ℝ≥0} (h : ∀ i j, a ≤ f i + g j) : a ≤ (⨅ i, f i) + ⨅ j, g j := by
  rw [← NNReal.coe_le_coe, NNReal.coe_add, coe_infᵢ, coe_infᵢ]
  exact le_cinfᵢ_add_cinfᵢ h
#align nnreal.le_infi_add_infi NNReal.le_infᵢ_add_infᵢ

example : Archimedean ℝ≥0 := by infer_instance

-- porting note: TODO: remove?
instance covariant_add : CovariantClass ℝ≥0 ℝ≥0 (· + ·) (· ≤ ·) := inferInstance
#align nnreal.covariant_add NNReal.covariant_add

instance contravariant_add : ContravariantClass ℝ≥0 ℝ≥0 (· + ·) (· < ·) := inferInstance
#align nnreal.contravariant_add NNReal.contravariant_add

instance covariant_mul : CovariantClass ℝ≥0 ℝ≥0 (· * ·) (· ≤ ·) := inferInstance
#align nnreal.covariant_mul NNReal.covariant_mul

-- porting note: TODO: delete?
nonrec theorem le_of_forall_pos_le_add {a b : ℝ≥0} (h : ∀ ε, 0 < ε → a ≤ b + ε) : a ≤ b :=
  le_of_forall_pos_le_add h
#align nnreal.le_of_forall_pos_le_add NNReal.le_of_forall_pos_le_add

theorem lt_iff_exists_rat_btwn (a b : ℝ≥0) :
    a < b ↔ ∃ q : ℚ, 0 ≤ q ∧ a < Real.toNNReal q ∧ Real.toNNReal q < b :=
  Iff.intro
    (fun h : (↑a : ℝ) < (↑b : ℝ) =>
      let ⟨q, haq, hqb⟩ := exists_rat_btwn h
      have : 0 ≤ (q : ℝ) := le_trans a.2 <| le_of_lt haq
      ⟨q, Rat.cast_nonneg.1 this, by
        simp [Real.coe_toNNReal _ this, NNReal.coe_lt_coe.symm, haq, hqb]⟩)
    fun ⟨q, _, haq, hqb⟩ => lt_trans haq hqb
#align nnreal.lt_iff_exists_rat_btwn NNReal.lt_iff_exists_rat_btwn

theorem bot_eq_zero : (⊥ : ℝ≥0) = 0 := rfl
#align nnreal.bot_eq_zero NNReal.bot_eq_zero

theorem mul_sup (a b c : ℝ≥0) : a * (b ⊔ c) = a * b ⊔ a * c :=
  mul_max_of_nonneg _ _ <| zero_le a
#align nnreal.mul_sup NNReal.mul_sup

theorem sup_mul (a b c : ℝ≥0) : (a ⊔ b) * c = a * c ⊔ b * c :=
  max_mul_of_nonneg _ _ <| zero_le c
#align nnreal.sup_mul NNReal.sup_mul

theorem mul_finset_sup {α} (r : ℝ≥0) (s : Finset α) (f : α → ℝ≥0) :
    r * s.sup f = s.sup fun a => r * f a :=
  Finset.comp_sup_eq_sup_comp _ (NNReal.mul_sup r) (mul_zero r)
#align nnreal.mul_finset_sup NNReal.mul_finset_sup

theorem finset_sup_mul {α} (s : Finset α) (f : α → ℝ≥0) (r : ℝ≥0) :
    s.sup f * r = s.sup fun a => f a * r :=
  Finset.comp_sup_eq_sup_comp (· * r) (fun x y => NNReal.sup_mul x y r) (zero_mul r)
#align nnreal.finset_sup_mul NNReal.finset_sup_mul

theorem finset_sup_div {α} {f : α → ℝ≥0} {s : Finset α} (r : ℝ≥0) :
    s.sup f / r = s.sup fun a => f a / r := by simp only [div_eq_inv_mul, mul_finset_sup]
#align nnreal.finset_sup_div NNReal.finset_sup_div

@[simp, norm_cast]
theorem coe_max (x y : ℝ≥0) : ((max x y : ℝ≥0) : ℝ) = max (x : ℝ) (y : ℝ) :=
  NNReal.coe_mono.map_max
#align nnreal.coe_max NNReal.coe_max

@[simp, norm_cast]
theorem coe_min (x y : ℝ≥0) : ((min x y : ℝ≥0) : ℝ) = min (x : ℝ) (y : ℝ) :=
  NNReal.coe_mono.map_min
#align nnreal.coe_min NNReal.coe_min

@[simp]
theorem zero_le_coe {q : ℝ≥0} : 0 ≤ (q : ℝ) :=
  q.2
#align nnreal.zero_le_coe NNReal.zero_le_coe

end NNReal

open NNReal

namespace Real

section ToNNReal

@[simp]
theorem coe_toNNReal' (r : ℝ) : (Real.toNNReal r : ℝ) = max r 0 :=
  rfl
#align real.coe_to_nnreal' Real.coe_toNNReal'

@[simp]
theorem toNNReal_zero : Real.toNNReal 0 = 0 := NNReal.eq <| coe_toNNReal _ le_rfl
#align real.to_nnreal_zero Real.toNNReal_zero

@[simp]
theorem toNNReal_one : Real.toNNReal 1 = 1 := NNReal.eq <| coe_toNNReal _ zero_le_one
#align real.to_nnreal_one Real.toNNReal_one

@[simp]
theorem toNNReal_pos {r : ℝ} : 0 < Real.toNNReal r ↔ 0 < r := by
  simp [← NNReal.coe_lt_coe, lt_irrefl]
#align real.to_nnreal_pos Real.toNNReal_pos

@[simp]
theorem toNNReal_eq_zero {r : ℝ} : Real.toNNReal r = 0 ↔ r ≤ 0 := by
  simpa [-toNNReal_pos] using not_iff_not.2 (@toNNReal_pos r)
#align real.to_nnreal_eq_zero Real.toNNReal_eq_zero

theorem toNNReal_of_nonpos {r : ℝ} : r ≤ 0 → Real.toNNReal r = 0 :=
  toNNReal_eq_zero.2
#align real.to_nnreal_of_nonpos Real.toNNReal_of_nonpos

@[simp]
theorem toNNReal_le_toNNReal_iff {r p : ℝ} (hp : 0 ≤ p) :
    toNNReal r ≤ toNNReal p ↔ r ≤ p := by simp [← NNReal.coe_le_coe, hp]
#align real.to_nnreal_le_to_nnreal_iff Real.toNNReal_le_toNNReal_iff

@[simp]
theorem toNNReal_eq_toNNReal_iff {r p : ℝ} (hr : 0 ≤ r) (hp : 0 ≤ p) :
    toNNReal r = toNNReal p ↔ r = p := by simp [← NNReal.coe_eq, coe_toNNReal, hr, hp]
#align real.to_nnreal_eq_to_nnreal_iff Real.toNNReal_eq_toNNReal_iff

@[simp]
theorem toNNReal_lt_toNNReal_iff' {r p : ℝ} : Real.toNNReal r < Real.toNNReal p ↔ r < p ∧ 0 < p :=
  NNReal.coe_lt_coe.symm.trans max_lt_max_left_iff
#align real.to_nnreal_lt_to_nnreal_iff' Real.toNNReal_lt_toNNReal_iff'

theorem toNNReal_lt_toNNReal_iff {r p : ℝ} (h : 0 < p) :
    Real.toNNReal r < Real.toNNReal p ↔ r < p :=
  toNNReal_lt_toNNReal_iff'.trans (and_iff_left h)
#align real.to_nnreal_lt_to_nnreal_iff Real.toNNReal_lt_toNNReal_iff

theorem toNNReal_lt_toNNReal_iff_of_nonneg {r p : ℝ} (hr : 0 ≤ r) :
    Real.toNNReal r < Real.toNNReal p ↔ r < p :=
  toNNReal_lt_toNNReal_iff'.trans ⟨And.left, fun h => ⟨h, lt_of_le_of_lt hr h⟩⟩
#align real.to_nnreal_lt_to_nnreal_iff_of_nonneg Real.toNNReal_lt_toNNReal_iff_of_nonneg

@[simp]
theorem toNNReal_add {r p : ℝ} (hr : 0 ≤ r) (hp : 0 ≤ p) :
    Real.toNNReal (r + p) = Real.toNNReal r + Real.toNNReal p :=
  NNReal.eq <| by simp [hr, hp, add_nonneg]
#align real.to_nnreal_add Real.toNNReal_add

theorem toNNReal_add_toNNReal {r p : ℝ} (hr : 0 ≤ r) (hp : 0 ≤ p) :
    Real.toNNReal r + Real.toNNReal p = Real.toNNReal (r + p) :=
  (Real.toNNReal_add hr hp).symm
#align real.to_nnreal_add_to_nnreal Real.toNNReal_add_toNNReal

theorem toNNReal_le_toNNReal {r p : ℝ} (h : r ≤ p) : Real.toNNReal r ≤ Real.toNNReal p :=
  Real.toNNReal_mono h
#align real.to_nnreal_le_to_nnreal Real.toNNReal_le_toNNReal

theorem toNNReal_add_le {r p : ℝ} : Real.toNNReal (r + p) ≤ Real.toNNReal r + Real.toNNReal p :=
  NNReal.coe_le_coe.1 <| max_le (add_le_add (le_max_left _ _) (le_max_left _ _)) NNReal.zero_le_coe
#align real.to_nnreal_add_le Real.toNNReal_add_le

theorem toNNReal_le_iff_le_coe {r : ℝ} {p : ℝ≥0} : toNNReal r ≤ p ↔ r ≤ ↑p :=
  NNReal.gi.gc r p
#align real.to_nnreal_le_iff_le_coe Real.toNNReal_le_iff_le_coe

theorem le_toNNReal_iff_coe_le {r : ℝ≥0} {p : ℝ} (hp : 0 ≤ p) : r ≤ Real.toNNReal p ↔ ↑r ≤ p := by
  rw [← NNReal.coe_le_coe, Real.coe_toNNReal p hp]
#align real.le_to_nnreal_iff_coe_le Real.le_toNNReal_iff_coe_le

theorem le_toNNReal_iff_coe_le' {r : ℝ≥0} {p : ℝ} (hr : 0 < r) : r ≤ Real.toNNReal p ↔ ↑r ≤ p :=
  (le_or_lt 0 p).elim le_toNNReal_iff_coe_le fun hp => by
    simp only [(hp.trans_le r.coe_nonneg).not_le, toNNReal_eq_zero.2 hp.le, hr.not_le]
#align real.le_to_nnreal_iff_coe_le' Real.le_toNNReal_iff_coe_le'

theorem toNNReal_lt_iff_lt_coe {r : ℝ} {p : ℝ≥0} (ha : 0 ≤ r) : Real.toNNReal r < p ↔ r < ↑p := by
  rw [← NNReal.coe_lt_coe, Real.coe_toNNReal r ha]
#align real.to_nnreal_lt_iff_lt_coe Real.toNNReal_lt_iff_lt_coe

theorem lt_toNNReal_iff_coe_lt {r : ℝ≥0} {p : ℝ} : r < Real.toNNReal p ↔ ↑r < p :=
  lt_iff_lt_of_le_iff_le toNNReal_le_iff_le_coe
#align real.lt_to_nnreal_iff_coe_lt Real.lt_toNNReal_iff_coe_lt

#noalign real.to_nnreal_bit0
#noalign real.to_nnreal_bit1

theorem toNNReal_pow {x : ℝ} (hx : 0 ≤ x) (n : ℕ) : (x ^ n).toNNReal = x.toNNReal ^ n := by
  rw [← NNReal.coe_eq, NNReal.coe_pow, Real.coe_toNNReal _ (pow_nonneg hx _),
    Real.coe_toNNReal x hx]
#align real.to_nnreal_pow Real.toNNReal_pow

theorem toNNReal_mul {p q : ℝ} (hp : 0 ≤ p) :
    Real.toNNReal (p * q) = Real.toNNReal p * Real.toNNReal q :=
  NNReal.eq <| by simp [mul_max_of_nonneg, hp]

#align real.to_nnreal_mul Real.toNNReal_mul

end ToNNReal

end Real

open Real

namespace NNReal

section Mul

theorem mul_eq_mul_left {a b c : ℝ≥0} (h : a ≠ 0) : a * b = a * c ↔ b = c := by
  rw [mul_eq_mul_left_iff, or_iff_left h]
#align nnreal.mul_eq_mul_left NNReal.mul_eq_mul_left

end Mul

section Pow

theorem pow_antitone_exp {a : ℝ≥0} (m n : ℕ) (mn : m ≤ n) (a1 : a ≤ 1) : a ^ n ≤ a ^ m :=
  pow_le_pow_of_le_one (zero_le a) a1 mn
#align nnreal.pow_antitone_exp NNReal.pow_antitone_exp

nonrec theorem exists_pow_lt_of_lt_one {a b : ℝ≥0} (ha : 0 < a) (hb : b < 1) :
    ∃ n : ℕ, b ^ n < a := by
  simpa only [← coe_pow, NNReal.coe_lt_coe] using
    exists_pow_lt_of_lt_one (NNReal.coe_pos.2 ha) (NNReal.coe_lt_coe.2 hb)
#align nnreal.exists_pow_lt_of_lt_one NNReal.exists_pow_lt_of_lt_one

nonrec theorem exists_mem_Ico_zpow {x : ℝ≥0} {y : ℝ≥0} (hx : x ≠ 0) (hy : 1 < y) :
    ∃ n : ℤ, x ∈ Set.Ico (y ^ n) (y ^ (n + 1)) :=
  exists_mem_Ico_zpow (α := ℝ) hx.bot_lt hy
#align nnreal.exists_mem_Ico_zpow NNReal.exists_mem_Ico_zpow

nonrec theorem exists_mem_Ioc_zpow {x : ℝ≥0} {y : ℝ≥0} (hx : x ≠ 0) (hy : 1 < y) :
    ∃ n : ℤ, x ∈ Set.Ioc (y ^ n) (y ^ (n + 1)) :=
  exists_mem_Ioc_zpow (α := ℝ) hx.bot_lt hy
#align nnreal.exists_mem_Ioc_zpow NNReal.exists_mem_Ioc_zpow

end Pow

section Sub

/-!
### Lemmas about subtraction

In this section we provide a few lemmas about subtraction that do not fit well into any other
typeclass. For lemmas about subtraction and addition see lemmas about `OrderedSub` in the file
`Mathlib.Algebra.Order.Sub.Bsic`. See also `mul_tsub` and `tsub_mul`.
-/

theorem sub_def {r p : ℝ≥0} : r - p = Real.toNNReal (r - p) :=
  rfl
#align nnreal.sub_def NNReal.sub_def

theorem coe_sub_def {r p : ℝ≥0} : ↑(r - p) = max (r - p : ℝ) 0 :=
  rfl
#align nnreal.coe_sub_def NNReal.coe_sub_def

noncomputable example : OrderedSub ℝ≥0 := by infer_instance

theorem sub_div (a b c : ℝ≥0) : (a - b) / c = a / c - b / c :=
  tsub_div _ _ _
#align nnreal.sub_div NNReal.sub_div

end Sub

section Inv

theorem sum_div {ι} (s : Finset ι) (f : ι → ℝ≥0) (b : ℝ≥0) :
    (∑ i in s, f i) / b = ∑ i in s, f i / b :=
  Finset.sum_div
#align nnreal.sum_div NNReal.sum_div

@[simp]
theorem inv_le {r p : ℝ≥0} (h : r ≠ 0) : r⁻¹ ≤ p ↔ 1 ≤ r * p := by
  rw [← mul_le_mul_left (pos_iff_ne_zero.2 h), mul_inv_cancel h]
#align nnreal.inv_le NNReal.inv_le

theorem inv_le_of_le_mul {r p : ℝ≥0} (h : 1 ≤ r * p) : r⁻¹ ≤ p := by
  by_cases r = 0 <;> simp [*, inv_le]
#align nnreal.inv_le_of_le_mul NNReal.inv_le_of_le_mul

@[simp]
theorem le_inv_iff_mul_le {r p : ℝ≥0} (h : p ≠ 0) : r ≤ p⁻¹ ↔ r * p ≤ 1 := by
  rw [← mul_le_mul_left (pos_iff_ne_zero.2 h), mul_inv_cancel h, mul_comm]
#align nnreal.le_inv_iff_mul_le NNReal.le_inv_iff_mul_le

@[simp]
theorem lt_inv_iff_mul_lt {r p : ℝ≥0} (h : p ≠ 0) : r < p⁻¹ ↔ r * p < 1 := by
  rw [← mul_lt_mul_left (pos_iff_ne_zero.2 h), mul_inv_cancel h, mul_comm]
#align nnreal.lt_inv_iff_mul_lt NNReal.lt_inv_iff_mul_lt

theorem mul_le_iff_le_inv {a b r : ℝ≥0} (hr : r ≠ 0) : r * a ≤ b ↔ a ≤ r⁻¹ * b := by
  have : 0 < r := lt_of_le_of_ne (zero_le r) hr.symm
  rw [← mul_le_mul_left (inv_pos.mpr this), ← mul_assoc, inv_mul_cancel hr, one_mul]
#align nnreal.mul_le_iff_le_inv NNReal.mul_le_iff_le_inv

theorem le_div_iff_mul_le {a b r : ℝ≥0} (hr : r ≠ 0) : a ≤ b / r ↔ a * r ≤ b :=
  le_div_iff₀ hr
#align nnreal.le_div_iff_mul_le NNReal.le_div_iff_mul_le

theorem div_le_iff {a b r : ℝ≥0} (hr : r ≠ 0) : a / r ≤ b ↔ a ≤ b * r :=
  div_le_iff₀ hr
#align nnreal.div_le_iff NNReal.div_le_iff

nonrec theorem div_le_iff' {a b r : ℝ≥0} (hr : r ≠ 0) : a / r ≤ b ↔ a ≤ r * b :=
  @div_le_iff' ℝ _ a r b <| pos_iff_ne_zero.2 hr
#align nnreal.div_le_iff' NNReal.div_le_iff'

theorem div_le_of_le_mul {a b c : ℝ≥0} (h : a ≤ b * c) : a / c ≤ b :=
  if h0 : c = 0 then by simp [h0] else (div_le_iff h0).2 h
#align nnreal.div_le_of_le_mul NNReal.div_le_of_le_mul

theorem div_le_of_le_mul' {a b c : ℝ≥0} (h : a ≤ b * c) : a / b ≤ c :=
  div_le_of_le_mul <| mul_comm b c ▸ h
#align nnreal.div_le_of_le_mul' NNReal.div_le_of_le_mul'

nonrec theorem le_div_iff {a b r : ℝ≥0} (hr : r ≠ 0) : a ≤ b / r ↔ a * r ≤ b :=
  @le_div_iff ℝ _ a b r <| pos_iff_ne_zero.2 hr
#align nnreal.le_div_iff NNReal.le_div_iff

nonrec theorem le_div_iff' {a b r : ℝ≥0} (hr : r ≠ 0) : a ≤ b / r ↔ r * a ≤ b :=
  @le_div_iff' ℝ _ a b r <| pos_iff_ne_zero.2 hr
#align nnreal.le_div_iff' NNReal.le_div_iff'

theorem div_lt_iff {a b r : ℝ≥0} (hr : r ≠ 0) : a / r < b ↔ a < b * r :=
  lt_iff_lt_of_le_iff_le (le_div_iff hr)
#align nnreal.div_lt_iff NNReal.div_lt_iff

theorem div_lt_iff' {a b r : ℝ≥0} (hr : r ≠ 0) : a / r < b ↔ a < r * b :=
  lt_iff_lt_of_le_iff_le (le_div_iff' hr)
#align nnreal.div_lt_iff' NNReal.div_lt_iff'

theorem lt_div_iff {a b r : ℝ≥0} (hr : r ≠ 0) : a < b / r ↔ a * r < b :=
  lt_iff_lt_of_le_iff_le (div_le_iff hr)
#align nnreal.lt_div_iff NNReal.lt_div_iff

theorem lt_div_iff' {a b r : ℝ≥0} (hr : r ≠ 0) : a < b / r ↔ r * a < b :=
  lt_iff_lt_of_le_iff_le (div_le_iff' hr)
#align nnreal.lt_div_iff' NNReal.lt_div_iff'

theorem mul_lt_of_lt_div {a b r : ℝ≥0} (h : a < b / r) : a * r < b :=
  (lt_div_iff fun hr => False.elim <| by simp [hr] at h).1 h
#align nnreal.mul_lt_of_lt_div NNReal.mul_lt_of_lt_div

-- porting note: drop an unneeded assumption, assume `≠ 0`
theorem div_le_div_left_of_le {a b c : ℝ≥0} (c0 : c ≠ 0) (cb : c ≤ b) :
    a / b ≤ a / c :=
  div_le_div_of_le_left (zero_le _) c0.bot_lt cb
#align nnreal.div_le_div_left_of_le NNReal.div_le_div_left_of_leₓ

nonrec theorem div_le_div_left {a b c : ℝ≥0} (a0 : 0 < a) (b0 : 0 < b) (c0 : 0 < c) :
    a / b ≤ a / c ↔ c ≤ b :=
  div_le_div_left a0 b0 c0
#align nnreal.div_le_div_left NNReal.div_le_div_left

theorem le_of_forall_lt_one_mul_le {x y : ℝ≥0} (h : ∀ a < 1, a * x ≤ y) : x ≤ y :=
  le_of_forall_ge_of_dense fun a ha => by
    have hx : x ≠ 0 := pos_iff_ne_zero.1 (lt_of_le_of_lt (zero_le _) ha)
    have hx' : x⁻¹ ≠ 0 := by rwa [Ne.def, inv_eq_zero]
    have : a * x⁻¹ < 1 := by rwa [← lt_inv_iff_mul_lt hx', inv_inv]
    have : a * x⁻¹ * x ≤ y := h _ this
    rwa [mul_assoc, inv_mul_cancel hx, mul_one] at this
#align nnreal.le_of_forall_lt_one_mul_le NNReal.le_of_forall_lt_one_mul_le

nonrec theorem half_le_self (a : ℝ≥0) : a / 2 ≤ a :=
  half_le_self bot_le
#align nnreal.half_le_self NNReal.half_le_self

nonrec theorem half_lt_self {a : ℝ≥0} (h : a ≠ 0) : a / 2 < a :=
  half_lt_self h.bot_lt
#align nnreal.half_lt_self NNReal.half_lt_self

theorem div_lt_one_of_lt {a b : ℝ≥0} (h : a < b) : a / b < 1 := by
  rwa [div_lt_iff, one_mul]
  exact ne_of_gt (lt_of_le_of_lt (zero_le _) h)
#align nnreal.div_lt_one_of_lt NNReal.div_lt_one_of_lt

theorem _root_.Real.toNNReal_inv {x : ℝ} : Real.toNNReal x⁻¹ = (Real.toNNReal x)⁻¹ := by
  cases' le_total 0 x with hx hx
  · nth_rw 1 [← Real.coe_toNNReal x hx]
    rw [← NNReal.coe_inv, Real.toNNReal_coe]
  · rw [toNNReal_eq_zero.mpr hx, inv_zero, toNNReal_eq_zero.mpr (inv_nonpos.mpr hx)]
#align real.to_nnreal_inv Real.toNNReal_inv

theorem _root_.Real.toNNReal_div {x y : ℝ} (hx : 0 ≤ x) :
    Real.toNNReal (x / y) = Real.toNNReal x / Real.toNNReal y := by
  rw [div_eq_mul_inv, div_eq_mul_inv, ← Real.toNNReal_inv, ← Real.toNNReal_mul hx]
#align real.to_nnreal_div Real.toNNReal_div

theorem _root_.Real.toNNReal_div' {x y : ℝ} (hy : 0 ≤ y) :
    Real.toNNReal (x / y) = Real.toNNReal x / Real.toNNReal y := by
  rw [div_eq_inv_mul, div_eq_inv_mul, Real.toNNReal_mul (inv_nonneg.2 hy), Real.toNNReal_inv]
#align real.to_nnreal_div' Real.toNNReal_div'

theorem inv_lt_one_iff {x : ℝ≥0} (hx : x ≠ 0) : x⁻¹ < 1 ↔ 1 < x := by
  rw [← one_div, div_lt_iff hx, one_mul]
#align nnreal.inv_lt_one_iff NNReal.inv_lt_one_iff

theorem zpow_pos {x : ℝ≥0} (hx : x ≠ 0) (n : ℤ) : 0 < x ^ n :=
  zpow_pos_of_pos hx.bot_lt _
#align nnreal.zpow_pos NNReal.zpow_pos

theorem inv_lt_inv {x y : ℝ≥0} (hx : x ≠ 0) (h : x < y) : y⁻¹ < x⁻¹ :=
  inv_lt_inv_of_lt hx.bot_lt h
#align nnreal.inv_lt_inv NNReal.inv_lt_inv

end Inv

@[simp]
theorem abs_eq (x : ℝ≥0) : |(x : ℝ)| = x :=
  abs_of_nonneg x.property
#align nnreal.abs_eq NNReal.abs_eq

section Csupr

open Set

variable {ι : Sort _} {f : ι → ℝ≥0}

theorem le_toNNReal_of_coe_le {x : ℝ≥0} {y : ℝ} (h : ↑x ≤ y) : x ≤ y.toNNReal :=
  (le_toNNReal_iff_coe_le <| x.2.trans h).2 h
#align nnreal.le_to_nnreal_of_coe_le NNReal.le_toNNReal_of_coe_le

nonrec theorem supₛ_of_not_bddAbove {s : Set ℝ≥0} (hs : ¬BddAbove s) : SupSet.supₛ s = 0 := by
  rw [← bddAbove_coe] at hs
  rw [← NNReal.coe_eq, coe_supₛ, NNReal.coe_zero]
  exact supₛ_of_not_bddAbove hs
#align nnreal.Sup_of_not_bdd_above NNReal.supₛ_of_not_bddAbove

theorem supᵢ_of_not_bddAbove (hf : ¬BddAbove (range f)) : (⨆ i, f i) = 0 :=
  supₛ_of_not_bddAbove hf
#align nnreal.supr_of_not_bdd_above NNReal.supᵢ_of_not_bddAbove

theorem supᵢ_empty [IsEmpty ι] (f : ι → ℝ≥0) : (⨆ i, f i) = 0 := csupᵢ_of_empty f

theorem infᵢ_empty [IsEmpty ι] (f : ι → ℝ≥0) : (⨅ i, f i) = 0 := by
  rw [infᵢ_of_empty', infₛ_empty]
#align nnreal.infi_empty NNReal.infᵢ_empty

@[simp]
theorem infᵢ_const_zero {α : Sort _} : (⨅ _i : α, (0 : ℝ≥0)) = 0 := by
  rw [← NNReal.coe_eq, coe_infᵢ]
  exact Real.cinfᵢ_const_zero
#align nnreal.infi_const_zero NNReal.infᵢ_const_zero

theorem infᵢ_mul (f : ι → ℝ≥0) (a : ℝ≥0) : infᵢ f * a = ⨅ i, f i * a := by
  rw [← NNReal.coe_eq, NNReal.coe_mul, coe_infᵢ, coe_infᵢ]
  exact Real.infᵢ_mul_of_nonneg (NNReal.coe_nonneg _) _
#align nnreal.infi_mul NNReal.infᵢ_mul

theorem mul_infᵢ (f : ι → ℝ≥0) (a : ℝ≥0) : a * infᵢ f = ⨅ i, a * f i := by
  simpa only [mul_comm] using infᵢ_mul f a
#align nnreal.mul_infi NNReal.mul_infᵢ

theorem mul_supᵢ (f : ι → ℝ≥0) (a : ℝ≥0) : (a * ⨆ i, f i) = ⨆ i, a * f i := by
  rw [← NNReal.coe_eq, NNReal.coe_mul, NNReal.coe_supᵢ, NNReal.coe_supᵢ]
  exact Real.mul_supᵢ_of_nonneg (NNReal.coe_nonneg _) _
#align nnreal.mul_supr NNReal.mul_supᵢ

theorem supᵢ_mul (f : ι → ℝ≥0) (a : ℝ≥0) : (⨆ i, f i) * a = ⨆ i, f i * a := by
  rw [mul_comm, mul_supᵢ]
  simp_rw [mul_comm]
#align nnreal.supr_mul NNReal.supᵢ_mul

theorem supᵢ_div (f : ι → ℝ≥0) (a : ℝ≥0) : (⨆ i, f i) / a = ⨆ i, f i / a := by
  simp only [div_eq_mul_inv, supᵢ_mul]
#align nnreal.supr_div NNReal.supᵢ_div

-- porting note: generalized to allow empty `ι`
theorem mul_supᵢ_le {a : ℝ≥0} {g : ℝ≥0} {h : ι → ℝ≥0} (H : ∀ j, g * h j ≤ a) : g * supᵢ h ≤ a := by
  rw [mul_supᵢ]
  exact csupᵢ_le' H
#align nnreal.mul_supr_le NNReal.mul_supᵢ_le

-- porting note: generalized to allow empty `ι`
theorem supᵢ_mul_le {a : ℝ≥0} {g : ι → ℝ≥0} {h : ℝ≥0} (H : ∀ i, g i * h ≤ a) : supᵢ g * h ≤ a := by
  rw [supᵢ_mul]
  exact csupᵢ_le' H
#align nnreal.supr_mul_le NNReal.supᵢ_mul_le

-- porting note: generalized to allow empty `ι`
theorem supᵢ_mul_supᵢ_le {a : ℝ≥0} {g h : ι → ℝ≥0} (H : ∀ i j, g i * h j ≤ a) :
    supᵢ g * supᵢ h ≤ a :=
  supᵢ_mul_le fun _ => mul_supᵢ_le <| H _
#align nnreal.supr_mul_supr_le NNReal.supᵢ_mul_supᵢ_le

variable [Nonempty ι]

theorem le_mul_infᵢ {a : ℝ≥0} {g : ℝ≥0} {h : ι → ℝ≥0} (H : ∀ j, a ≤ g * h j) : a ≤ g * infᵢ h := by
  rw [mul_infᵢ]
  exact le_cinfᵢ H
#align nnreal.le_mul_infi NNReal.le_mul_infᵢ

theorem le_infᵢ_mul {a : ℝ≥0} {g : ι → ℝ≥0} {h : ℝ≥0} (H : ∀ i, a ≤ g i * h) : a ≤ infᵢ g * h := by
  rw [infᵢ_mul]
  exact le_cinfᵢ H
#align nnreal.le_infi_mul NNReal.le_infᵢ_mul

theorem le_infᵢ_mul_infᵢ {a : ℝ≥0} {g h : ι → ℝ≥0} (H : ∀ i j, a ≤ g i * h j) :
    a ≤ infᵢ g * infᵢ h :=
  le_infᵢ_mul fun i => le_mul_infᵢ <| H i
#align nnreal.le_infi_mul_infi NNReal.le_infᵢ_mul_infᵢ

end Csupr

end NNReal

namespace Set

namespace OrdConnected

variable {s : Set ℝ} {t : Set ℝ≥0}

theorem preimage_coe_nnreal_real (h : s.OrdConnected) : ((↑) ⁻¹' s : Set ℝ≥0).OrdConnected :=
  h.preimage_mono NNReal.coe_mono
#align set.ord_connected.preimage_coe_nnreal_real Set.OrdConnected.preimage_coe_nnreal_real

theorem image_coe_nnreal_real (h : t.OrdConnected) : ((↑) '' t : Set ℝ).OrdConnected :=
  ⟨ball_image_iff.2 fun x hx =>
      ball_image_iff.2 fun _y hy z hz => ⟨⟨z, x.2.trans hz.1⟩, h.out hx hy hz, rfl⟩⟩
#align set.ord_connected.image_coe_nnreal_real Set.OrdConnected.image_coe_nnreal_real

-- porting note: todo: does it generalize to a `GaloisInsertion`?
theorem image_real_toNNReal (h : s.OrdConnected) : (Real.toNNReal '' s).OrdConnected := by
  refine' ⟨ball_image_iff.2 fun x hx => ball_image_iff.2 fun y hy z hz => _⟩
  cases' le_total y 0 with hy₀ hy₀
  · rw [mem_Icc, Real.toNNReal_of_nonpos hy₀, nonpos_iff_eq_zero] at hz
    exact ⟨y, hy, (toNNReal_of_nonpos hy₀).trans hz.2.symm⟩
  · lift y to ℝ≥0 using hy₀
    rw [toNNReal_coe] at hz
    exact ⟨z, h.out hx hy ⟨toNNReal_le_iff_le_coe.1 hz.1, hz.2⟩, toNNReal_coe⟩
#align set.ord_connected.image_real_to_nnreal Set.OrdConnected.image_real_toNNReal

theorem preimage_real_toNNReal (h : t.OrdConnected) : (Real.toNNReal ⁻¹' t).OrdConnected :=
  h.preimage_mono Real.toNNReal_mono
#align set.ord_connected.preimage_real_to_nnreal Set.OrdConnected.preimage_real_toNNReal

end OrdConnected

end Set

namespace Real

/-- The absolute value on `ℝ` as a map to `ℝ≥0`. -/
-- porting note: removed @[pp_nodot]
def nnabs : ℝ →*₀ ℝ≥0 where
  toFun x := ⟨|x|, abs_nonneg x⟩
  map_zero' := by ext; simp
  map_one' := by ext; simp
  map_mul' x y := by ext; simp [abs_mul]
#align real.nnabs Real.nnabs

@[norm_cast, simp]
theorem coe_nnabs (x : ℝ) : (nnabs x : ℝ) = |x| :=
  rfl
#align real.coe_nnabs Real.coe_nnabs

@[simp]
theorem nnabs_of_nonneg {x : ℝ} (h : 0 ≤ x) : nnabs x = toNNReal x := by
  ext
  rw [coe_toNNReal x h, coe_nnabs, abs_of_nonneg h]
#align real.nnabs_of_nonneg Real.nnabs_of_nonneg

theorem nnabs_coe (x : ℝ≥0) : nnabs x = x := by simp
#align real.nnabs_coe Real.nnabs_coe

theorem coe_toNNReal_le (x : ℝ) : (toNNReal x : ℝ) ≤ |x| :=
  max_le (le_abs_self _) (abs_nonneg _)
#align real.coe_to_nnreal_le Real.coe_toNNReal_le

theorem cast_natAbs_eq_nnabs_cast (n : ℤ) : (n.natAbs : ℝ≥0) = nnabs n := by
  ext
  rw [NNReal.coe_nat_cast, Int.cast_natAbs, Real.coe_nnabs, Int.cast_abs]
#align real.cast_nat_abs_eq_nnabs_cast Real.cast_natAbs_eq_nnabs_cast

end Real

/-
namespace Tactic

open Positivity

private theorem nnreal_coe_pos {r : ℝ≥0} : 0 < r → 0 < (r : ℝ) :=
  NNReal.coe_pos.2
#align tactic.nnreal_coe_pos tactic.nnreal_coe_pos

/-- Extension for the `positivity` tactic: cast from `ℝ≥0` to `ℝ`. -/
@[positivity]
unsafe def positivity_coe_nnreal_real : expr → tactic strictness
  | q(@coe _ _ $(inst) $(a)) => do
    unify inst q(@coeToLift _ _ <| @coeBase _ _ NNReal.Real.hasCoe)
    let strictness_a ← core a
    match strictness_a with
      | positive p => positive <$> mk_app `` nnreal_coe_pos [p]
      | _ => nonnegative <$> mk_app `` NNReal.coe_nonneg [a]
  | e =>
    pp e >>= fail ∘ format.bracket "The expression " " is not of the form `(r : ℝ)` for `r : ℝ≥0`"
#align tactic.positivity_coe_nnreal_real tactic.positivity_coe_nnreal_real

end Tactic
-/


import data.set
import data.set.basic
import set_theory.zfc
-- import data.nat.basic
-- import set_theory.cardinal

-- open Set

open set
--open cardinal

universes u v
variables {α β : Type u}
variables {a b c: Type u}
variables {A C: set α}
variable B: set β

#check prod α β
#reduce prod A B
#check α
#check (set α)

#eval 1 + 2
--#check set({a b})
#check (a,b)
#reduce (a, b).1
#reduce (a, b).2
#check [1, 2] -- list ℕ
#check [a,b] -- List (Type u)
--instance i: (x: nat) (y: nat) := ⟨1, 2⟩
def v : set nat := {1,2}
#check v

def v2 : set nat := {2, 3}

def v3: set (ℕ × ℕ)  := {(1,2), (2,4)}
def v4: set(ℕ × ℕ) := {(1,2),(3,4)}

def powerset' (A : set α) : set (set α) := {B : set α | B ⊆ A}

#reduce ∃⦃x:v3⦄, x.val.1 = 1 ∧ x.val.2 = 4
#print v3
#reduce (1,2) ∈ v3
#print powerset
#reduce powerset A
#reduce v × v2
-- #reduce (1,2) ∈ set(v × v2)
#reduce 1 ∈ v2
#reduce 2 ∈ v
#reduce 1 ∈ v
#reduce A × B
#reduce powerset A × B  -- this is parsed with: powerset (A) × B
#reduce powerset v2 × v2 -- parswed with: powerset (v2) × v2
#reduce powerset' {v2 × v2} --set(v2 × v2)
#check v2 × v2
#print ×
#print prod
#check prod v2 v2
#check set (prod v2 v2)
#reduce (2,2) ∈  {v2 × v2}
--#check (2,2) ∈ set (v2 × v2)
#check A × B
#check set (A × B)
#check powerset {A × B}
#check powerset A
#reduce powerset A
#reduce powerset {A × B}
#check powerset {A × B}
#check (1,2) ∈ powerset {A × B}
#check powerset {v2 × v2}
#check (2,2) ∈ powerset {v2 × v2}
#reduce (2,2) ∈ powerset {v2 × v2}
#check set (powerset {v2 × v2})
#check ℕ × ℕ
#check set (ℕ × ℕ)
-- are these same?
def p : ℕ × ℤ := ⟨ 1, 2 ⟩
def p2 : ℕ × ℤ := (1, 2)
#check p
#check p2

#check Π x : α, β
#check λ x: α, β

-- these are same
#check fun x: nat, x + 5
#check λ x: nat, x+ 5

#print ∣
def evens : set ℕ := {n | even n}
#check nat × nat
#check evens
variable n':nat
def n₂ : ℕ := 10
#check even n₂
#reduce even n₂

--variable e:nat
variable s': set ℕ
#check 1 ∈ s'
-- caution!! `∣`(\mid) and `|` is different!! we can not use \mid in
-- set comprehension.
def test_false : set ℕ := {e | even e }
#check test_false
variables {A' B': set ℕ}
#check A' × B'
#check powerset {A' × B'}
#check {(a,b)} ∈ powerset {A × B}
#print A
#print powerset
-- This does not raises error but `set (set Type u)` does not seem what we want
def test_function_from_set : set (set(Type u)) := {f | f ⊆ {A × B}}
--def test_function_from_set : set (set(A × B)) :=
-- {sf | sf ∈ powerset (A.prod B)}
def test_function_from_set2 {X' X'': set ℕ}: set (set(ℕ × ℕ)) :=
 {sf | sf ∈ powerset (X'.prod X'')}

namespace test
variables X Y: set ℕ
#check X × Y
variables x: ℕ  × ℕ
#check x
-- !caution! `prod X Y` and `set.prod X Y` are different!!
#check prod X Y -- ↥X × ↥Y : Type
#check set.prod X Y -- set (ℕ × ℕ) : Type ;; is equivalent to `X.prod Y`
#check set (prod X Y)
#print set.prod -- {p : α × β | p.fst ∈ s ∧ p.snd ∈ t}
#print prod
#print × -- prod

def t {X Y: set ℕ}: set(ℕ × ℕ) := X.prod Y
def t' : set(ℕ × ℕ) := X.prod Y

#check t -- t : set (ℕ × ℕ)
#check t' -- t' : set ℕ → set ℕ → set (ℕ × ℕ)
#check t' 3 5 -- t' 3 5 : set (ℕ × ℕ)  ; t' requires two parameters!
#check x ∈ t

variable f₂: ℕ → ℕ
#check f₂

variable f₃: α → β
#check f₃
#check ℕ

namespace function
def graph {α β: Type u} (f: α → β) : set (α × β) := {p | p.2 = f p.1}
end function
#check function.graph(f₃)

def test_function_from_set2 {X' X'': set ℕ}: set (set(ℕ × ℕ)) :=
 {sf | sf ∈ powerset (X'.prod X'')}

variables {D E: set (Type u)}
#check {p | p ∈ 𝒫(D.prod E)}
-- not yet defined

def setA {a: α} : set α:= {a | a ∈ A}
def setA2 {A2: set α}: set α := {a | a ∈ A2}
def setN {N: set ℕ}: set ℕ := {n | n ∈ N}
variable θ: Type
def setC {C2: set θ} :set θ := {c | c ∈ C2}

-- Type*, Type u, Type _ is used for dependant type
variable γ : Type* -- to avoid arbitaly universe name we can use `Type*`
#check α -- Type u
#check γ -- Type u_1
#check setN -- set ℕ
#check setA -- set ?M_1 (this can not have set `α` because `α` is `Type u`)
#check setA2 -- set ?M_1
#check setC -- set θ (this have type `set θ` is because `θ` is `Type`)
#check ℕ -- Type
#print ℕ -- `ℕ`:1024 := nat
#check A -- set α
#check {l | l ∈ setA} -- set ?M_1
#check {l2 | l2 ∈ A} -- set α
-- We can not remove the hint `{setA: set α}` from `setA_from_setA` definition.
-- Is this because `def` has its own scope that `setA` is missing the type info?
def setA_from_setA {setA: set α}: set α := {x | x ∈ setA}
-- Why OK? because the scope inside the {} is global?
#check {l: α | l ∈ setA}
end test
--#reduce {f:nat × nat ∣ f ∈ powerset {v2 × v2}}don't know how to synthesize placeholder
-- #check {n sf ∣ n ∈ ℕ}
--#reduce {f ∣ f ∈ powerset {v2 × v2}}
-- v = {1,2}, v2 = {2,3}
-- v × v2 = {(1,2),(1,3),(2,2),(2,3)}
-- powerset v × v2 = {∅,
--                    {(1,2)}, {(1,2),(1,3)},{(1,2),(2,2)},{(1,2),(2,3)},{}
--                    {(1,3),(2,2)}, {(1,3),(2,3)},
--                    {(2,2),(2,3)},
--                    {(1,2),(1,3),(2,2)},{(1,2),(2,2),(2,3)},
--                    {(1,3),(2,2),(2,3)},
--                    {(1,2),(1,3),(2,2),(2,3)}}

namespace test2
#check a
#check α
#check 'a'
-- We can not define the following
-- def onetwo : α×β := ⟨α, β⟩
def ab : char × char := ⟨ 'a', 'b'⟩
#print char
#print nat
#print α
#check (1,2)
def sf₁ : set (set (ℕ × ℕ)) := {{(1,2)},{(1,2),(1,3)}}
-- def sf₂ : set (set (α × β)) := {{⟨a,b⟩ },{(a,b)}}
#check sf₁

def npair : ℕ × ℕ := (1,2)
#check npair -- npair : ℕ × ℕ

def npair₂ : ℕ × ℕ := ⟨1,2⟩  -- whats the difference on `()` and `⟨ ⟩`?
#check npair₂ -- npair₂ : ℕ × ℕ

def nset₁ : set ℕ := {1,2}
def nset₂ : set ℕ := {1,2}

-- we can not call this. why?
--#check nset₁.prod nset₂
#check nset₁ -- set: ℕ
-- we can not infer the type of {1,2}
--def setnpair : set (ℕ × ℕ) := {1,2}.prod {2,3}
-- the type is `set (ℕ × ℕ)` (not `(set ℕ) × (set ℕ)`)
def npairset : set (ℕ × ℕ) := ({1,2}:set ℕ).prod ({2,3}:set ℕ)
#check npairset -- set (ℕ × ℕ)
#reduce npairset

def npairset₂ : set (ℕ × ℕ) := {(1,2),(3,4),(5,6)}

#check npairset₂ -- npaierset₂ : set (ℕ × ℕ )
#reduce npairset₂ -- λ (b : ℕ × ℕ), b = (1, 2) ∨ b = (3, 4) ∨ b = (5, 6)

def setnpair : set ℕ × set ℕ := ⟨ {1,2,3} , {2,3,4,5} ⟩
#check setnpair -- set ℕ × set ℕ
#check A -- set α
#check A.prod B -- set (α × β)

-- When the type is ℕ
variables {n₁ n₂ : ℕ}
variable {N₁: set ℕ}
variable {N₂: set ℕ}
#check N₁.prod N₂ -- set (ℕ × ℕ)
#check (1,2) ∈ N₁.prod N₂ --(X.prod Y)
#check (n₁,n₂) ∈ N₁.prod N₂

#print ℕ
#print nat

-- When it is generic Type
variables {x₁ y₁: Type}
variables {X₁: set Type}
variables {Y₁: set Type}
#check X₁.prod Y₁ -- X₁.prod Y₁ : set (Type × Type)
#check (x₁,y₁) ∈ X₁.prod Y₁

variable f₁ : set (Type × Type)
#check f₁ -- f₁ : set (Type × Type)
#check set(X₁ × Y₁)  -- set (↥X₁ × ↥Y₁) : Type 1
#check ↥X₁
#reduce ↥X₁ -- {x // X₁ x}
#check ↥X₁ × ↥Y₁ -- ↥X₁ × ↥Y₁ : Type 1
#reduce coe_sort X₁
#check X₁.prod Y₁ -- X₁.prod Y₁ : set (Type × Type)
#check (x₁,y₁) -- (x₁, y₁) : Type × Type
#check (x₁,y₁) ∈ f₁ -- (x₁, y₁) ∈ f₁ : Prop
#check coe_sort X₁ × coe_sort Y₁ -- ↥X₁ × ↥Y₁ : Type 1
#check coe_sort X₁
--#check (x₁, y₁) ∈ (X₁ × Y₁)
#check ↑X₁ --↑X₁ : ?M_1
#check subtype X₁ -- subtype X₁ : Type 1

variable s : set α
#check subtype s

/-- invalid definition -/
-- Pitfalls
-- ========
-- 1. The type of f is not `set (X × Y)` because the type of `X` is `set Type*`,
--    This it means it is a term not a type, so `X × Y` does not work as
--    expected.
-- 2. `x:X` is expected to express `x ∈ X`, but again
--    `X` does not express the expected type. (it is coerced type ↥X in reality)
--    Thus `x:X` does not mean that `x` is a member of the set `X`.
--    It also does not mean `x` is a type of `Type*`.
--    Instead, it means `x` is a type of `↥X`, therefore `(x, y)` has
--    the type `(↥X, ↥Y)`.
def is_function_invalid (X Y: set Type*) (f: set (X × Y)): Prop :=
 ∀x:X,∃!y:Y, (x,y) ∈ f

/-- invalid definition -/
def funs_invalid {X Y: set Type*} : set (set (X × Y)) :=
{f | f ∈ 𝒫 (X.prod Y) ∧ (is_function_invalid X Y f)}

/--
The function x is a triple: (T, T, (T, T)) (T can be any type)
-/
-- Note that:
-- `∀x ∈ X` means `∀x: α, x ∈ X → ...`
-- `∃y ∈ Y` means `∃y: β, y ∈ Y ∧ ... `
def is_function (X:set α) (Y: set β) (f: set (α × β)): Prop :=
f ⊆ X.prod Y ∧ ∀x ∈ X,∃!y ∈ Y, (x,y) ∈ f

-- not yet defined
-- `funs X Y` is `Y ^ X`
def funs (X : set α) (Y: set β): set (set (α × β)) :=
{f | f ∈ 𝒫 (X.prod Y) ∧ (is_function X Y f)}

theorem mem_funs_equiv_isfunction {X: set α} {Y: set β } {f: set (α × β)}:
f ∈ funs X Y ↔ is_function X Y f :=
by simp [funs, is_function]

--variable a_number₂ :
--def setN: set ℕ := {n | n: ℕ}
def example2 {n: ℕ} : Prop := n = 1
--lemma example3: ∀n:ℕ, n = 1 := sorry
#print set

-- universes u v
-- this definition is from library/init/data/set.lean
def set₁ (α : Type u) := α → Prop

end test2

namespace ZFCSet

-- These definitions are copied from set_theorey/zfc.lean

/-- The type of pre-sets in universe `u`. A pre-set
  is a family of pre-sets indexed by a type in `Type u`.
  The ZFC universe is defined as a quotient of this
  to ensure extensionality. -/
inductive pSet : Type (u+1)
| mk (α : Type u) (A : α → pSet) : pSet

#check pSet

/-- The ZFC universe of sets consists of the type of pre-sets,
  quotiented by extensional equivalence. -/
def Set : Type (u+1) := quotient pSet.setoid.{u}

#check Set
#reduce Set.{u}
#check Set.{v}
#print pSet.setoid

--variable (x : Set.{u})

notation `⋃` := Set.Union

/-- Kuratowski ordered pair -/
def pair (x y : Set.{u}) : Set.{u} := {{x}, {x, y}}

/-- A subset of pairs `{(a, b) ∈ x × y | p a b}` -/
def pair_sep (p : Set.{u} → Set.{u} → Prop) (x y : Set.{u}) : Set.{u} :=
{z ∈ powerset (powerset (x ∪ y)) | ∃a ∈ x, ∃b ∈ y, z = pair a b ∧ p a b}

/-- The cartesian product, `{(a, b) | a ∈ x, b ∈ y}` -/
def prod : Set.{u} → Set.{u} → Set.{u} := pair_sep (λa b, true)

/-- `is_func x y f` is the assertion `f : x → y` where `f` is a ZFC function
  (a set of ordered pairs) -/
def is_func (x y f : Set.{u}) : Prop :=
f ⊆ prod x y ∧ ∀z:Set.{u}, z ∈ x → ∃! w, pair z w ∈ f

/-- `funs x y` is `y ^ x`, the set of all set functions `x → y` -/
def funs (x y : Set.{u}) : Set.{u} :=
{f ∈ powerset (prod x y) | is_func x y f}

end ZFCSet


/-
Copyright (c) 2020 Zhouhang Zhou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou
-/
import order.bounds.basic
import data.set.intervals.basic

/-!
# Intervals without endpoints ordering

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

In any lattice `α`, we define `uIcc a b` to be `Icc (a ⊓ b) (a ⊔ b)`, which in a linear order is the
set of elements lying between `a` and `b`.

`Icc a b` requires the assumption `a ≤ b` to be meaningful, which is sometimes inconvenient. The
interval as defined in this file is always the set of things lying between `a` and `b`, regardless
of the relative order of `a` and `b`.

For real numbers, `uIcc a b` is the same as `segment ℝ a b`.

In a product or pi type, `uIcc a b` is the smallest box containing `a` and `b`. For example,
`uIcc (1, -1) (-1, 1) = Icc (-1, -1) (1, 1)` is the square of vertices `(1, -1)`, `(-1, -1)`,
`(-1, 1)`, `(1, 1)`.

In `finset α` (seen as a hypercube of dimension `fintype.card α`), `uIcc a b` is the smallest
subcube containing both `a` and `b`.

## Notation

We use the localized notation `[a, b]` for `uIcc a b`. One can open the locale `interval` to
make the notation available.

-/

open function order_dual (to_dual of_dual)

variables {α β : Type*}

namespace set
section lattice
variables [lattice α] {a a₁ a₂ b b₁ b₂ c x : α}

/-- `uIcc a b` is the set of elements lying between `a` and `b`, with `a` and `b` included.
Note that we define it more generally in a lattice as `set.Icc (a ⊓ b) (a ⊔ b)`. In a product type,
`uIcc` corresponds to the bounding box of the two elements. -/
def uIcc (a b : α) : set α := Icc (a ⊓ b) (a ⊔ b)

localized "notation (name := set.uIcc) `[`a `, ` b `]` := set.uIcc a b" in interval

@[simp] lemma dual_uIcc (a b : α) : [to_dual a, to_dual b] = of_dual ⁻¹' [a, b] := dual_Icc

@[simp] lemma uIcc_of_le (h : a ≤ b) : [a, b] = Icc a b :=
by rw [uIcc, inf_eq_left.2 h, sup_eq_right.2 h]

@[simp] lemma uIcc_of_ge (h : b ≤ a) : [a, b] = Icc b a :=
by rw [uIcc, inf_eq_right.2 h, sup_eq_left.2 h]

lemma uIcc_comm (a b : α) : [a, b] = [b, a] := by simp_rw [uIcc, inf_comm, sup_comm]

lemma uIcc_of_lt (h : a < b) : [a, b] = Icc a b := uIcc_of_le h.le
lemma uIcc_of_gt (h : b < a) : [a, b] = Icc b a := uIcc_of_ge h.le

@[simp] lemma uIcc_self : [a, a] = {a} := by simp [uIcc]

@[simp] lemma nonempty_uIcc : [a, b].nonempty := nonempty_Icc.2 inf_le_sup

lemma Icc_subset_uIcc : Icc a b ⊆ [a, b] := Icc_subset_Icc inf_le_left le_sup_right
lemma Icc_subset_uIcc' : Icc b a ⊆ [a, b] := Icc_subset_Icc inf_le_right le_sup_left

@[simp] lemma left_mem_uIcc : a ∈ [a, b] := ⟨inf_le_left, le_sup_left⟩
@[simp] lemma right_mem_uIcc : b ∈ [a, b] := ⟨inf_le_right, le_sup_right⟩

lemma mem_uIcc_of_le (ha : a ≤ x) (hb : x ≤ b) : x ∈ [a, b] := Icc_subset_uIcc ⟨ha, hb⟩
lemma mem_uIcc_of_ge (hb : b ≤ x) (ha : x ≤ a) : x ∈ [a, b] := Icc_subset_uIcc' ⟨hb, ha⟩

lemma uIcc_subset_uIcc (h₁ : a₁ ∈ [a₂, b₂]) (h₂ : b₁ ∈ [a₂, b₂]) : [a₁, b₁] ⊆ [a₂, b₂] :=
Icc_subset_Icc (le_inf h₁.1 h₂.1) (sup_le h₁.2 h₂.2)

lemma uIcc_subset_uIcc_iff_mem : [a₁, b₁] ⊆ [a₂, b₂] ↔ a₁ ∈ [a₂, b₂] ∧ b₁ ∈ [a₂, b₂] :=
iff.intro (λh, ⟨h left_mem_uIcc, h right_mem_uIcc⟩) (λ h, uIcc_subset_uIcc h.1 h.2)

lemma uIcc_subset_uIcc_iff_le' : [a₁, b₁] ⊆ [a₂, b₂] ↔ a₂ ⊓ b₂ ≤ a₁ ⊓ b₁ ∧ a₁ ⊔ b₁ ≤ a₂ ⊔ b₂ :=
Icc_subset_Icc_iff inf_le_sup

lemma uIcc_subset_uIcc_right (h : x ∈ [a, b]) : [x, b] ⊆ [a, b] := uIcc_subset_uIcc h right_mem_uIcc
lemma uIcc_subset_uIcc_left (h : x ∈ [a, b]) : [a, x] ⊆ [a, b] := uIcc_subset_uIcc left_mem_uIcc h

lemma bdd_below_bdd_above_iff_subset_uIcc (s : set α) :
  bdd_below s ∧ bdd_above s ↔ ∃ a b, s ⊆ [a, b] :=
bdd_below_bdd_above_iff_subset_Icc.trans
  ⟨λ ⟨a, b, h⟩, ⟨a, b, λ x hx, Icc_subset_uIcc (h hx)⟩, λ ⟨a, b, h⟩, ⟨_, _, h⟩⟩

end lattice

open_locale interval

section distrib_lattice
variables [distrib_lattice α] {a a₁ a₂ b b₁ b₂ c x : α}

lemma eq_of_mem_uIcc_of_mem_uIcc (ha : a ∈ [b, c]) (hb : b ∈ [a, c]) : a = b :=
eq_of_inf_eq_sup_eq (inf_congr_right ha.1 hb.1) $ sup_congr_right ha.2 hb.2

lemma eq_of_mem_uIcc_of_mem_uIcc' : b ∈ [a, c] → c ∈ [a, b] → b = c :=
by simpa only [uIcc_comm a] using eq_of_mem_uIcc_of_mem_uIcc

lemma uIcc_injective_right (a : α) : injective (λ b, uIcc b a) :=
λ b c h, by { rw ext_iff at h,
  exact eq_of_mem_uIcc_of_mem_uIcc ((h _).1 left_mem_uIcc) ((h _).2 left_mem_uIcc) }

lemma uIcc_injective_left (a : α) : injective (uIcc a) :=
by simpa only [uIcc_comm] using uIcc_injective_right a

end distrib_lattice

section linear_order
variables [linear_order α] [linear_order β] {f : α → β} {s : set α} {a a₁ a₂ b b₁ b₂ c d x : α}

lemma Icc_min_max : Icc (min a b) (max a b) = [a, b] := rfl

lemma uIcc_of_not_le (h : ¬ a ≤ b) : [a, b] = Icc b a := uIcc_of_gt $ lt_of_not_ge h
lemma uIcc_of_not_ge (h : ¬ b ≤ a) : [a, b] = Icc a b := uIcc_of_lt $ lt_of_not_ge h

lemma uIcc_eq_union : [a, b] = Icc a b ∪ Icc b a := by rw [Icc_union_Icc', max_comm]; refl

lemma mem_uIcc : a ∈ [b, c] ↔ b ≤ a ∧ a ≤ c ∨ c ≤ a ∧ a ≤ b := by simp [uIcc_eq_union]

lemma not_mem_uIcc_of_lt (ha : c < a) (hb : c < b) : c ∉ [a, b] :=
not_mem_Icc_of_lt $ lt_min_iff.mpr ⟨ha, hb⟩

lemma not_mem_uIcc_of_gt (ha : a < c) (hb : b < c) : c ∉ [a, b] :=
not_mem_Icc_of_gt $ max_lt_iff.mpr ⟨ha, hb⟩

lemma uIcc_subset_uIcc_iff_le :
  [a₁, b₁] ⊆ [a₂, b₂] ↔ min a₂ b₂ ≤ min a₁ b₁ ∧ max a₁ b₁ ≤ max a₂ b₂ :=
uIcc_subset_uIcc_iff_le'

/-- A sort of triangle inequality. -/
lemma uIcc_subset_uIcc_union_uIcc : [a, c] ⊆ [a, b] ∪ [b, c] :=
λ x, by simp only [mem_uIcc, mem_union]; cases le_total a c; cases le_total x b; tauto

lemma monotone_or_antitone_iff_uIcc :
  monotone f ∨ antitone f ↔ ∀ a b c, c ∈ [a, b] → f c ∈ [f a, f b] :=
begin
  split,
  { rintro (hf | hf) a b c; simp_rw [←Icc_min_max, ←hf.map_min, ←hf.map_max],
    exacts [λ hc, ⟨hf hc.1, hf hc.2⟩, λ hc, ⟨hf hc.2, hf hc.1⟩] },
  contrapose!,
  rw not_monotone_not_antitone_iff_exists_le_le,
  rintro ⟨a, b, c, hab, hbc, ⟨hfab, hfcb⟩ | ⟨hfba, hfbc⟩⟩,
  { exact ⟨a, c, b, Icc_subset_uIcc ⟨hab, hbc⟩, λ h, h.2.not_lt $ max_lt hfab hfcb⟩ },
  { exact ⟨a, c, b, Icc_subset_uIcc ⟨hab, hbc⟩, λ h, h.1.not_lt $ lt_min hfba hfbc⟩ }
end

lemma monotone_on_or_antitone_on_iff_uIcc :
  monotone_on f s ∨ antitone_on f s ↔ ∀ a b c ∈ s, c ∈ [a, b] → f c ∈ [f a, f b] :=
by simp [monotone_on_iff_monotone, antitone_on_iff_antitone, monotone_or_antitone_iff_uIcc,
  mem_uIcc]

/-- The open-closed interval with unordered bounds. -/
def uIoc : α → α → set α := λ a b, Ioc (min a b) (max a b)

-- Below is a capital iota
localized "notation `Ι` := set.uIoc" in interval

@[simp] lemma uIoc_of_le (h : a ≤ b) : Ι a b = Ioc a b := by simp [uIoc, h]
@[simp] lemma uIoc_of_lt (h : b < a) : Ι a b = Ioc b a := by simp [uIoc, h.le]

lemma uIoc_eq_union : Ι a b = Ioc a b ∪ Ioc b a := by cases le_total a b; simp [uIoc, *]

lemma mem_uIoc : a ∈ Ι b c ↔ b < a ∧ a ≤ c ∨ c < a ∧ a ≤ b :=
by simp only [uIoc_eq_union, mem_union, mem_Ioc]

lemma not_mem_uIoc : a ∉ Ι b c ↔ a ≤ b ∧ a ≤ c ∨ c < a ∧ b < a :=
by { simp only [uIoc_eq_union, mem_union, mem_Ioc, not_lt, ←not_le], tauto }

@[simp] lemma left_mem_uIoc : a ∈ Ι a b ↔ b < a := by simp [mem_uIoc]
@[simp] lemma right_mem_uIoc : b ∈ Ι a b ↔ a < b := by simp [mem_uIoc]

lemma forall_uIoc_iff  {P : α → Prop} :
  (∀ x ∈ Ι a b, P x) ↔ (∀ x ∈ Ioc a b, P x) ∧ (∀ x ∈ Ioc b a, P x) :=
by simp only [uIoc_eq_union, mem_union, or_imp_distrib, forall_and_distrib]

lemma uIoc_subset_uIoc_of_uIcc_subset_uIcc (h : [a, b] ⊆ [c, d]) : Ι a b ⊆ Ι c d :=
Ioc_subset_Ioc (uIcc_subset_uIcc_iff_le.1 h).1 (uIcc_subset_uIcc_iff_le.1 h).2

lemma uIoc_swap (a b : α) : Ι a b = Ι b a := by simp only [uIoc, min_comm a b, max_comm a b]

lemma Ioc_subset_uIoc : Ioc a b ⊆ Ι a b := Ioc_subset_Ioc (min_le_left _ _) (le_max_right _ _)
lemma Ioc_subset_uIoc' : Ioc a b ⊆ Ι b a := Ioc_subset_Ioc (min_le_right _ _) (le_max_left _ _)

lemma eq_of_mem_uIoc_of_mem_uIoc : a ∈ Ι b c → b ∈ Ι a c → a = b :=
by simp_rw mem_uIoc; rintro (⟨_, _⟩ | ⟨_, _⟩) (⟨_, _⟩ | ⟨_, _⟩); apply le_antisymm;
  assumption <|> exact le_of_lt ‹_› <|> exact le_trans ‹_› (le_of_lt ‹_›)

lemma eq_of_mem_uIoc_of_mem_uIoc' : b ∈ Ι a c → c ∈ Ι a b → b = c :=
by simpa only [uIoc_swap a] using eq_of_mem_uIoc_of_mem_uIoc

lemma eq_of_not_mem_uIoc_of_not_mem_uIoc (ha : a ≤ c) (hb : b ≤ c) :
  a ∉ Ι b c → b ∉ Ι a c → a = b :=
by simp_rw not_mem_uIoc; rintro (⟨_, _⟩ | ⟨_, _⟩) (⟨_, _⟩ | ⟨_, _⟩); apply le_antisymm;
    assumption <|> exact le_of_lt ‹_› <|> cases not_le_of_lt ‹_› ‹_›

lemma uIoc_injective_right (a : α) : injective (λ b, Ι b a) :=
begin
  rintro b c h,
  rw ext_iff at h,
  obtain ha | ha := le_or_lt b a,
  { have hb := (h b).not,
    simp only [ha, left_mem_uIoc, not_lt, true_iff, not_mem_uIoc, ←not_le, and_true,
      not_true, false_and, not_false_iff, true_iff, or_false] at hb,
    refine hb.eq_of_not_lt (λ hc, _),
    simpa [ha, and_iff_right hc, ←@not_le _ _ _ a, -not_le] using h c },
  { refine eq_of_mem_uIoc_of_mem_uIoc ((h _).1 $ left_mem_uIoc.2 ha)
      ((h _).2 $ left_mem_uIoc.2 $ ha.trans_le _),
    simpa [ha, ha.not_le, mem_uIoc] using h b }
end

lemma uIoc_injective_left (a : α) : injective (Ι a) :=
by simpa only [uIoc_swap] using uIoc_injective_right a

end linear_order
end set


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

! This file was ported from Lean 3 source module logic.encodable.lattice
! leanprover-community/mathlib commit 9003f28797c0664a49e4179487267c494477d853
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Logic.Encodable.Basic
import Mathlib.Logic.Pairwise

/-!
# Lattice operations on encodable types

Lemmas about lattice and set operations on encodable types

## Implementation Notes

This is a separate file, to avoid unnecessary imports in basic files.

Previously some of these results were in the `MeasureTheory` folder.
-/

open Set

namespace Encodable

variable {α : Type _} {β : Type _} [Encodable β]

theorem supᵢ_decode₂ [CompleteLattice α] (f : β → α) :
    (⨆ (i : ℕ) (b ∈ decode₂ β i), f b) = (⨆ b, f b) := by
  rw [supᵢ_comm]
  simp only [mem_decode₂, supᵢ_supᵢ_eq_right]
#align encodable.supr_decode₂ Encodable.supᵢ_decode₂

theorem unionᵢ_decode₂ (f : β → Set α) : (⋃ (i : ℕ) (b ∈ decode₂ β i), f b) = ⋃ b, f b :=
  supᵢ_decode₂ f
#align encodable.Union_decode₂ Encodable.unionᵢ_decode₂

/- Porting note: `@[elab_as_elim]` gives `unexpected eliminator resulting type`. -/
--@[elab_as_elim]
theorem unionᵢ_decode₂_cases {f : β → Set α} {C : Set α → Prop} (H0 : C ∅) (H1 : ∀ b, C (f b)) {n} :
    C (⋃ b ∈ decode₂ β n, f b) :=
  match decode₂ β n with
  | none => by
    simp
    apply H0
  | some b => by
    convert H1 b
    simp [ext_iff]
#align encodable.Union_decode₂_cases Encodable.unionᵢ_decode₂_cases

theorem unionᵢ_decode₂_disjoint_on {f : β → Set α} (hd : Pairwise (Disjoint on f)) :
    Pairwise (Disjoint on fun i => ⋃ b ∈ decode₂ β i, f b) :=
  by
  rintro i j ij
  refine' disjoint_left.mpr fun x => _
  suffices ∀ a, encode a = i → x ∈ f a → ∀ b, encode b = j → x ∉ f b by simpa [decode₂_eq_some]
  rintro a rfl ha b rfl hb
  exact (hd (mt (congr_arg encode) ij)).le_bot ⟨ha, hb⟩
#align encodable.Union_decode₂_disjoint_on Encodable.unionᵢ_decode₂_disjoint_on

end Encodable


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

 This file sets up some definitions for working with parametrised families
 of types, and morphisms between them.  In other words, we essentially
 work with the category of arrows in the category of types.

 A key issue is as follows.  Suppose we have a family X : B → Type*,
 and two terms b0 and b1 of B, and terms x0 : (X b0) and x1 : (X b1) of
 the corresponding fibres.  Suppose that b0 and b1 are provably
 equal, but not definitionally equal.  Lean will then regard the
 expression x0 = x1 as not being meaningful.  There are two ways
 around this.  One way is to use the heterogenous equality relation
 x0 == x1 instead of x0 = x1.  The other is to find an explicit term
 e of type b0 = b1, then (eq.mp e x0) will have type (X b1) and so
 (eq.mp e x0) = x1 will be meaningful.  A large part of this file
 deals with the properties of these two approaches and the relationship
 between them.

 It is far from clear that the approach taken here is optimal.
-/

import logic.equiv.basic
import data.heq_extra

/-
 This defines the notion of a morphism between two parametrised families.
-/
structure family_fun {B : Type*} (X : B → Type*) {C : Type*} (Y : C → Type*) :=
(base : B → C)
(fiber : ∀ {b : B}, (X b) → (Y (base b)))

/-
 The following lemma says roughly the following: if some property P makes
 sense for heterogenously equal pairs, and is true for definitionally
 equal pairs, then it is true for all heterogenously equal pairs.
-/
lemma family_eq_rec {B : Type*} {X : B → Type*}
 (P : ∀ {b0 b1 : B} {x0 : X b0} {x1 : X b1} (eb : b0 = b1) (ex : x0 == x1), Prop)
 (p : ∀ (b : B) (x : X b), @P b b x x (@rfl B b) (heq.refl x)) :
 ∀ b0 b1 x0 x1 eb ex, @P b0 b1 x0 x1 eb ex :=
begin
 intros b0 b1 x0 x1 eb ex,
 rcases eb,
 rcases (eq_of_heq ex),
 exact p b0 x0
end

namespace family_fun

def total : ∀ {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f : family_fun X Y) (x : Σ (b : B), X b), (Σ (c : C), Y c) :=
begin
 intros,exact ⟨f.base x.1, f.fiber x.2⟩
end

/- This just makes b explicit as an argument of f.fiber
-/
def fiber_alt  {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f : family_fun X Y) : ∀ (b : B), (X b) → (Y (f.base b)) := f.fiber

/- The identity morphism of a parametrised family -/
def id {B : Type*} (X : B → Type*) : family_fun X X :=
{ base := @id B, fiber := λ b, (@id (X b))}

/- Composition of morphisms of parametrised families -/
def compose
 {B : Type*} {X : B → Type*}
 {C : Type*} {Y : C → Type*}
 {D : Type*} {Z : D → Type*}
 (g : family_fun Y Z) (f : family_fun X Y) : (family_fun X Z) :=
 { base := g.base ∘ f.base,
   fiber := λ (b : B) (x : X b), g.fiber (f.fiber x)
 }

/- Morphisms preserve heterogenous equality -/
def congr_args  {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f : family_fun X Y)
 {b0 b1 : B} (eb : b0 = b1)
 {x0 : X b0} {x1 : X b1} (ex : x0 == x1) :
  f.fiber x0 == f.fiber x1 :=
   heq_subst f.base f.fiber eb ex

/-
 We now define a notion of equivalence for morphisms:
 morphisms f0 and f1 are equivalent if they are pointwise equal on
 the base and pointwise heterogenously equal on fibres.

 Later we prove a kind of function extensionality result, that
 equivalent morphisms are equal.  However, I think that this result
 is not "computationally pure" and so inhibits virtual machine
 compilation; so it is preferable to avoid it and work with
 equivalence.  But I do not have a good understanding of all the
 relevant issues.
-/
def hequiv  {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f0 : family_fun X Y) (f1 : family_fun X Y) : Prop :=
  (∀ (b : B), f0.base b = f1.base b) ∧
  (forall (b : B) (x : X b), f0.fiber x == f1.fiber x)
lemma heq_rec (B : Type*) (X : B → Type*) (b0 b1 : B) (eb : b0 = b1) (x : X b0) :
 x == (@eq.rec_on B b0 X b1 eb x) :=
begin
 exact eq.dcases_on eb (heq.refl x)
end

/-
 The next three lemmas show that we have indeed defined an
 equivalence relation on morphisms.
-/
lemma hequiv.refl {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f : family_fun X Y) : hequiv f f := begin
 split,
 exact λ (b : B), rfl,
 exact λ (b : B) (x : X b), heq.refl (f.fiber x)
end

lemma hequiv.symm {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f0 f1 : family_fun X Y) : (hequiv f0 f1) → (hequiv f1 f0) := begin
 intro e,
 let eb := λ (b : B), (e.left b).symm,
 let ex := λ (b : B) (x : X b), (e.right b x).symm,
 exact and.intro eb ex
end

lemma hequiv.trans {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f0 f1 f2 : family_fun X Y) :
  (hequiv f0 f1) → (hequiv f1 f2) → (hequiv f0 f2) := begin
 intros e01 e12,split,
 {intro b,
  exact (e01.left b).trans(e12.left b),
 },{
  intros b x,
  exact (e01.right b x).trans(e12.right b x),
 }
end

/-
 We now have three different results, all of which prove that two
 different morphisms are equal.  The hypotheses involve several
 different combinations of pointwise/global (heterogenous)
 equality on the base or fibres.
-/

lemma ext0 {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f0 f1 : family_fun X Y)
  (eb : f0.base = f1.base)
   (et : f0.fiber == f1.fiber) :
  f0 = f1 :=
begin
 cases f0 with f0b f0t,
 cases f1 with f1b f1t,
 exact @heq_sigma (B → C) (λ fb, Π b,(X b) → (Y (fb b))) (family_fun X Y)
  family_fun.mk f0b f1b @f0t @f1t eb et
end

lemma ext1 {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f0 f1 : family_fun X Y)
  (eb : f0.base = f1.base)
   (et : ∀ b, f0.fiber_alt b == f1.fiber_alt b) :
  f0 = f1 :=
begin
 let M : (B → C) → (B → C) → Prop :=
  λ g0 g1,
   (∀ h0 : (∀ b, (X b) → (Y (g0 b))),
    ∀ h1 : (∀ b, (X b) → (Y (g1 b))),
    ∀ eh : (∀ b, (h0 b) == (h1 b)),
    h0 == h1),
 have m : ∀ g : B → C, M g g :=
 begin
  intros g h0 h1 eh,
  have eh1 : ∀ b, h0 b = h1 b := λ b,eq_of_heq (eh b),
  exact heq_of_eq (@funext B (λ b, (X b) → (Y (g b))) h0 h1 eh1),
 end,
 let N : ∀ (g0 g1 : (B → C)), g0 = g1 → M g0 g1 :=
  begin
   intros g0 g1 e,cases e, exact m g0,
  end,
 have et1 : f0.fiber == f1.fiber :=
 begin
  exact N f0.base f1.base eb f0.fiber f1.fiber et,
 end,
 exact ext0 f0 f1 eb et1
end

lemma ext {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f0 f1 : family_fun X Y) (e : hequiv f0 f1) : f0 = f1 :=
begin
 have eb : f0.base = f1.base := funext e.left,
 let M : (B → C) → (B → C) → Prop :=
  λ g0 g1,
   (∀ h0 : (∀ b, (X b) → (Y (g0 b))),
    ∀ h1 : (∀ b, (X b) → (Y (g1 b))),
    ∀ eh : (∀ b x, (h0 b x) == (h1 b x)),
    ∀ b, h0 b == h1 b),
 have m : ∀ (g : B → C), M g g :=
 begin
  intros g h0 h1 e b,
  have e1 : ∀ x, h0 b x = h1 b x := λ x,eq_of_heq (e b x),
  exact heq_of_eq (@funext (X b) (λ _,Y (g b)) (h0 b) (h1 b) e1),
 end,
 let N : ∀ (g0 g1 : (B → C)), g0 = g1 → M g0 g1 :=
 begin
  intros g0 g1 e,cases e, exact m g0,
 end,
 have et : ∀ b, f0.fiber_alt b == f1.fiber_alt b :=
 begin
  exact N f0.base f1.base eb f0.fiber f1.fiber e.right,
 end,
 exact ext1 f0 f1 eb et
end

end family_fun

/-
 We now have some definitions and results about equivalences between
 parametrised families.  Such an equivalence is encoded as a structure
 involving two morphisms of families, together with proofs that the
 relevant composites are equivalent to identities with respect to the
 equivalence relation defined above.
-/
structure family_equiv {B : Type*} (X : B → Type*) {C : Type*} (Y : C → Type*) :=
(to_fun  : family_fun X Y)
(inv_fun : family_fun Y X)
(left_inv  : family_fun.hequiv (family_fun.compose inv_fun to_fun) (family_fun.id X))
(right_inv : family_fun.hequiv (family_fun.compose to_fun inv_fun) (family_fun.id Y))

namespace family_equiv

/-
 In the standard library there is an existing notion of an equivalence
 between two types.  In the next two results, we prove that an equivalence
 of families gives an equivalence between base types, and also equivalences
 between fiber types.
-/
def base {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f : family_equiv X Y) :
 equiv B C := {
   to_fun := f.to_fun.base,
   inv_fun := f.inv_fun.base,
   left_inv := f.left_inv.left,
   right_inv := f.right_inv.left
 }

def fiber {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f : family_equiv X Y) (b : B) :
 equiv (X b) (Y (f.to_fun.base b)) := begin
  let c : C := f.to_fun.base b,
  let b0 : B := f.inv_fun.base c,
  let eb : b0 = b := f.left_inv.left b,
  let tfn := f.to_fun.fiber_alt b,
  let ifn : (Y c) → (X b) := λ y, @eq.rec_on B (f.inv_fun.base c) X b eb (f.inv_fun.fiber y),
  let e0 : ∀ (y : Y c), ifn y == f.inv_fun.fiber y :=
    λ y, (heq_rec eb (f.inv_fun.fiber y)).symm,
  let li : ∀ x : (X b), ifn (tfn x) = x :=
   λ x, eq_of_heq ((e0 (tfn x)).trans (f.left_inv.right b x)),
  let ri : ∀ y : (Y c), tfn (ifn y) = y :=
  begin
   intro y,
   let x0 := ifn y,
   let x1 := f.inv_fun.fiber y,
   let y0 := f.to_fun.fiber x0,
   let y1 := f.to_fun.fiber x1,
   let e3 : y1 == y0 := heq_subst f.to_fun.base f.to_fun.fiber eb (e0 y).symm,
   exact eq_of_heq (e3.symm.trans (f.right_inv.right c y)),
  end,
  exact {
    to_fun := tfn,
    inv_fun := ifn,
    left_inv := li,
    right_inv := ri
  }
 end

/-
 We next show that there are identity equivalences of families, and that
 equivalences of families can be reversed or composed.  The corresponding
 results for equivalences of types are proved in the standard library.  We
 follow the notational conventions established there by writing refl,
 symm and trans rather than referring to identities, reversal and
 composition.
-/
def refl {B : Type*} (X : B → Type*) : family_equiv X X := {
  to_fun := family_fun.id X,
  inv_fun := family_fun.id X,
  left_inv  := and.intro (λ (b : B),@rfl B b) (λ (b : B) (x : X b),heq.refl x),
  right_inv := and.intro (λ (b : B),@rfl B b) (λ (b : B) (x : X b),heq.refl x),
 }

def symm {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*}
 (f : family_equiv X Y) : family_equiv Y X := {
   to_fun := f.inv_fun,
   inv_fun := f.to_fun,
   left_inv := f.right_inv,
   right_inv := f.left_inv
 }

def trans {B : Type*} {X : B → Type*} {C : Type*} {Y : C → Type*} {D : Type*} {Z : D → Type*}
 (f : family_equiv X Y) (g : family_equiv Y Z) : (family_equiv X Z) := {
   to_fun := family_fun.compose g.to_fun f.to_fun,
   inv_fun := family_fun.compose f.inv_fun g.inv_fun,
   left_inv := begin
    split,
    {intro b,
    let e0 := f.left_inv.left b,
    let e1 := g.left_inv.left (f.to_fun.base b),
    dsimp[family_fun.compose,family_fun.id] at e0 e1 ⊢,
    exact (congr_arg f.inv_fun.base e1).trans e0
    },{
     intros b x,
     let c := f.to_fun.base b,
     let y := f.to_fun.fiber x,
     let eb0 := f.left_inv.left b,
     let eb1 := g.left_inv.left c,
     let et0 := f.left_inv.right b x,
     let et1 := g.left_inv.right c y,
     dsimp[family_fun.compose,family_fun.id] at eb0 eb1 et0 et1 ⊢,
     exact (family_fun.congr_args f.inv_fun eb1 et1).trans et0,
    }
   end,
   right_inv := begin
    split,
    {intro d,
    let e0 := g.right_inv.left d,
    let e1 := f.right_inv.left (g.inv_fun.base d),
    dsimp[family_fun.compose,family_fun.id] at e0 e1 ⊢,
    exact (congr_arg g.to_fun.base e1).trans e0
    },{
     intros d z,
     let c := g.inv_fun.base d,
     let y := g.inv_fun.fiber z,
     let eb0 := g.right_inv.left d,
     let eb1 := f.right_inv.left c,
     let et0 := g.right_inv.right d z,
     let et1 := f.right_inv.right c y,
     dsimp[family_fun.compose,family_fun.id] at eb0 eb1 et0 et1 ⊢,
     exact (family_fun.congr_args g.to_fun eb1 et1).trans et0,
    }
   end,
 }

end family_equiv


/-
Copyright (c) 2019 Amelia Livingston. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Amelia Livingston, Bryan Gin-ge Chen

! This file was ported from Lean 3 source module data.setoid.basic
! leanprover-community/mathlib commit bbeb185db4ccee8ed07dc48449414ebfa39cb821
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Logic.Relation
import Mathlib.Order.GaloisConnection

/-!
# Equivalence relations

This file defines the complete lattice of equivalence relations on a type, results about the
inductively defined equivalence closure of a binary relation, and the analogues of some isomorphism
theorems for quotients of arbitrary types.

## Implementation notes

The function `Rel` and lemmas ending in ' make it easier to talk about different
equivalence relations on the same type.

The complete lattice instance for equivalence relations could have been defined by lifting
the Galois insertion of equivalence relations on α into binary relations on α, and then using
`CompleteLattice.copy` to define a complete lattice instance with more appropriate
definitional equalities (a similar example is `Filter.CompleteLattice` in
`Order/Filter/Basic.lean`). This does not save space, however, and is less clear.

Partitions are not defined as a separate structure here; users are encouraged to
reason about them using the existing `Setoid` and its infrastructure.

## Tags

setoid, equivalence, iseqv, relation, equivalence relation
-/

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

/-- A version of `Setoid.r` that takes the equivalence relation as an explicit argument. -/
def Setoid.Rel (r : Setoid α) : α → α → Prop :=
  @Setoid.r _ r
#align setoid.rel Setoid.Rel

instance Setoid.decidableRel (r : Setoid α) [h : DecidableRel r.r] : DecidableRel r.Rel :=
  h
#align setoid.decidable_rel Setoid.decidableRel

/-- A version of `Quotient.eq'` compatible with `Setoid.Rel`, to make rewriting possible. -/
theorem Quotient.eq_rel {r : Setoid α} {x y} :
    (Quotient.mk' x : Quotient r) = Quotient.mk' y ↔ r.Rel x y :=
  Quotient.eq
#align quotient.eq_rel Quotient.eq_rel

namespace Setoid

@[ext]
theorem ext' {r s : Setoid α} (H : ∀ a b, r.Rel a b ↔ s.Rel a b) : r = s :=
  ext H
#align setoid.ext' Setoid.ext'

theorem ext_iff {r s : Setoid α} : r = s ↔ ∀ a b, r.Rel a b ↔ s.Rel a b :=
  ⟨fun h _ _ => h ▸ Iff.rfl, ext'⟩
#align setoid.ext_iff Setoid.ext_iff

/-- Two equivalence relations are equal iff their underlying binary operations are equal. -/
theorem eq_iff_rel_eq {r₁ r₂ : Setoid α} : r₁ = r₂ ↔ r₁.Rel = r₂.Rel :=
  ⟨fun h => h ▸ rfl, fun h => Setoid.ext' fun _ _ => h ▸ Iff.rfl⟩
#align setoid.eq_iff_rel_eq Setoid.eq_iff_rel_eq

/-- Defining `≤` for equivalence relations. -/
instance : LE (Setoid α) :=
  ⟨fun r s => ∀ ⦃x y⦄, r.Rel x y → s.Rel x y⟩

theorem le_def {r s : Setoid α} : r ≤ s ↔ ∀ {x y}, r.Rel x y → s.Rel x y :=
  Iff.rfl
#align setoid.le_def Setoid.le_def

@[refl]
theorem refl' (r : Setoid α) (x) : r.Rel x x := r.iseqv.refl x
#align setoid.refl' Setoid.refl'

@[symm]
theorem symm' (r : Setoid α) : ∀ {x y}, r.Rel x y → r.Rel y x := r.iseqv.symm
#align setoid.symm' Setoid.symm'

@[trans]
theorem trans' (r : Setoid α) : ∀ {x y z}, r.Rel x y → r.Rel y z → r.Rel x z := r.iseqv.trans
#align setoid.trans' Setoid.trans'

theorem comm' (s : Setoid α) {x y} : s.Rel x y ↔ s.Rel y x :=
  ⟨s.symm', s.symm'⟩
#align setoid.comm' Setoid.comm'

/-- The kernel of a function is an equivalence relation. -/
def ker (f : α → β) : Setoid α :=
  ⟨(· = ·) on f, eq_equivalence.comap f⟩
#align setoid.ker Setoid.ker

/-- The kernel of the quotient map induced by an equivalence relation r equals r. -/
@[simp]
theorem ker_mk_eq (r : Setoid α) : ker (@Quotient.mk'' _ r) = r :=
  ext' fun _ _ => Quotient.eq
#align setoid.ker_mk_eq Setoid.ker_mk_eq

theorem ker_apply_mk_out {f : α → β} (a : α) : f (haveI := Setoid.ker f; ⟦a⟧.out) = f a :=
  @Quotient.mk_out _ (Setoid.ker f) a
#align setoid.ker_apply_mk_out Setoid.ker_apply_mk_out

theorem ker_apply_mk_out' {f : α → β} (a : α) :
    f (Quotient.mk _ a : Quotient <| Setoid.ker f).out' = f a :=
  @Quotient.mk_out' _ (Setoid.ker f) a
#align setoid.ker_apply_mk_out' Setoid.ker_apply_mk_out'

theorem ker_def {f : α → β} {x y : α} : (ker f).Rel x y ↔ f x = f y :=
  Iff.rfl
#align setoid.ker_def Setoid.ker_def

/-- Given types `α`, `β`, the product of two equivalence relations `r` on `α` and `s` on `β`:
    `(x₁, x₂), (y₁, y₂) ∈ α × β` are related by `r.prod s` iff `x₁` is related to `y₁`
    by `r` and `x₂` is related to `y₂` by `s`. -/
protected def prod (r : Setoid α) (s : Setoid β) :
    Setoid (α × β) where
  r x y := r.Rel x.1 y.1 ∧ s.Rel x.2 y.2
  iseqv :=
    ⟨fun x => ⟨r.refl' x.1, s.refl' x.2⟩, fun h => ⟨r.symm' h.1, s.symm' h.2⟩,
      fun h₁ h₂ => ⟨r.trans' h₁.1 h₂.1, s.trans' h₁.2 h₂.2⟩⟩
#align setoid.prod Setoid.prod

/-- The infimum of two equivalence relations. -/
instance : Inf (Setoid α) :=
  ⟨fun r s =>
    ⟨fun x y => r.Rel x y ∧ s.Rel x y,
      ⟨fun x => ⟨r.refl' x, s.refl' x⟩, fun h => ⟨r.symm' h.1, s.symm' h.2⟩, fun h1 h2 =>
        ⟨r.trans' h1.1 h2.1, s.trans' h1.2 h2.2⟩⟩⟩⟩

/-- The infimum of 2 equivalence relations r and s is the same relation as the infimum
    of the underlying binary operations. -/
theorem inf_def {r s : Setoid α} : (r ⊓ s).Rel = r.Rel ⊓ s.Rel :=
  rfl
#align setoid.inf_def Setoid.inf_def

theorem inf_iff_and {r s : Setoid α} {x y} : (r ⊓ s).Rel x y ↔ r.Rel x y ∧ s.Rel x y :=
  Iff.rfl
#align setoid.inf_iff_and Setoid.inf_iff_and

/-- The infimum of a set of equivalence relations. -/
instance : InfSet (Setoid α) :=
  ⟨fun S =>
    { r := fun x y => ∀ r ∈ S, r.Rel x y
      iseqv := ⟨fun x r _ => r.refl' x, fun h r hr => r.symm' <| h r hr, fun h1 h2 r hr =>
        r.trans' (h1 r hr) <| h2 r hr⟩ }⟩

/-- The underlying binary operation of the infimum of a set of equivalence relations
    is the infimum of the set's image under the map to the underlying binary operation. -/
theorem infₛ_def {s : Set (Setoid α)} : (infₛ s).Rel = infₛ (Rel '' s) := by
  ext
  simp only [infₛ_image, infᵢ_apply, infᵢ_Prop_eq]
  rfl
#align setoid.Inf_def Setoid.infₛ_def

instance : PartialOrder (Setoid α) where
  le := (· ≤ ·)
  lt r s := r ≤ s ∧ ¬s ≤ r
  le_refl _ _ _ := id
  le_trans _ _ _ hr hs _ _ h := hs <| hr h
  lt_iff_le_not_le _ _ := Iff.rfl
  le_antisymm _ _ h1 h2 := Setoid.ext' fun _ _ => ⟨fun h => h1 h, fun h => h2 h⟩

/-- The complete lattice of equivalence relations on a type, with bottom element `=`
    and top element the trivial equivalence relation. -/
instance completeLattice : CompleteLattice (Setoid α) :=
  { (completeLatticeOfInf (Setoid α)) fun _ =>
      ⟨fun _ hr _ _ h => h _ hr, fun _ hr _ _ h _ hr' => hr hr' h⟩ with
    inf := Inf.inf
    inf_le_left := fun _ _ _ _ h => h.1
    inf_le_right := fun _ _ _ _ h => h.2
    le_inf := fun _ _ _ h1 h2 _ _ h => ⟨h1 h, h2 h⟩
    top := ⟨fun _ _ => True, ⟨fun _ => trivial, fun h => h, fun h1 _ => h1⟩⟩
    le_top := fun _ _ _ _ => trivial
    bot := ⟨(· = ·), ⟨fun _ => rfl, fun h => h.symm, fun h1 h2 => h1.trans h2⟩⟩
    bot_le := fun r x _ h => h ▸ r.2.1 x }
#align setoid.complete_lattice Setoid.completeLattice

@[simp]
theorem top_def : (⊤ : Setoid α).Rel = ⊤ :=
  rfl
#align setoid.top_def Setoid.top_def

@[simp]

theorem eq_top_iff {s : Setoid α} : s = (⊤ : Setoid α) ↔ ∀ x y : α, s.Rel x y := by
  rw [_root_.eq_top_iff, Setoid.le_def, Setoid.top_def]
  simp only [Pi.top_apply, Prop.top_eq_true, forall_true_left]
#align setoid.eq_top_iff Setoid.eq_top_iff

/-- The inductively defined equivalence closure of a binary relation r is the infimum
    of the set of all equivalence relations containing r. -/
theorem eqvGen_eq (r : α → α → Prop) :
    EqvGen.Setoid r = infₛ { s : Setoid α | ∀ ⦃x y⦄, r x y → s.Rel x y } :=
  le_antisymm
    (fun _ _ H =>
      EqvGen.rec (fun _ _ h _ hs => hs h) (refl' _) (fun _ _ _ => symm' _)
        (fun _ _ _ _ _ => trans' _) H)
    (infₛ_le fun _ _ h => EqvGen.rel _ _ h)
#align setoid.eqv_gen_eq Setoid.eqvGen_eq

/-- The supremum of two equivalence relations r and s is the equivalence closure of the binary
    relation `x is related to y by r or s`. -/
theorem sup_eq_eqvGen (r s : Setoid α) :
    r ⊔ s = EqvGen.Setoid fun x y => r.Rel x y ∨ s.Rel x y := by
  rw [eqvGen_eq]
  apply congr_arg infₛ
  simp only [le_def, or_imp, ← forall_and]
#align setoid.sup_eq_eqv_gen Setoid.sup_eq_eqvGen

/-- The supremum of 2 equivalence relations r and s is the equivalence closure of the
    supremum of the underlying binary operations. -/
theorem sup_def {r s : Setoid α} : r ⊔ s = EqvGen.Setoid (r.Rel ⊔ s.Rel) := by
  rw [sup_eq_eqvGen]; rfl
#align setoid.sup_def Setoid.sup_def

/-- The supremum of a set S of equivalence relations is the equivalence closure of the binary
    relation `there exists r ∈ S relating x and y`. -/
theorem supₛ_eq_eqvGen (S : Set (Setoid α)) :
    supₛ S = EqvGen.Setoid fun x y => ∃ r : Setoid α, r ∈ S ∧ r.Rel x y := by
  rw [eqvGen_eq]
  apply congr_arg infₛ
  simp only [upperBounds, le_def, and_imp, exists_imp]
  ext
  exact ⟨fun H x y r hr => H hr, fun H r hr x y => H r hr⟩
#align setoid.Sup_eq_eqv_gen Setoid.supₛ_eq_eqvGen

/-- The supremum of a set of equivalence relations is the equivalence closure of the
    supremum of the set's image under the map to the underlying binary operation. -/
theorem supₛ_def {s : Set (Setoid α)} : supₛ s = EqvGen.Setoid (supₛ (Rel '' s)) := by
  rw [supₛ_eq_eqvGen, supₛ_image]
  congr with (x y)
  simp only [supᵢ_apply, supᵢ_Prop_eq, exists_prop]
#align setoid.Sup_def Setoid.supₛ_def

/-- The equivalence closure of an equivalence relation r is r. -/
@[simp]
theorem eqvGen_of_setoid (r : Setoid α) : EqvGen.Setoid r.r = r :=
  le_antisymm (by rw [eqvGen_eq]; exact infₛ_le fun _ _ => id) EqvGen.rel
#align setoid.eqv_gen_of_setoid Setoid.eqvGen_of_setoid

/-- Equivalence closure is idempotent. -/
@[simp]
theorem eqvGen_idem (r : α → α → Prop) : EqvGen.Setoid (EqvGen.Setoid r).Rel = EqvGen.Setoid r :=
  eqvGen_of_setoid _
#align setoid.eqv_gen_idem Setoid.eqvGen_idem

/-- The equivalence closure of a binary relation r is contained in any equivalence
    relation containing r. -/
theorem eqvGen_le {r : α → α → Prop} {s : Setoid α} (h : ∀ x y, r x y → s.Rel x y) :
    EqvGen.Setoid r ≤ s := by rw [eqvGen_eq]; exact infₛ_le h
#align setoid.eqv_gen_le Setoid.eqvGen_le

/-- Equivalence closure of binary relations is monotone. -/
theorem eqvGen_mono {r s : α → α → Prop} (h : ∀ x y, r x y → s x y) :
    EqvGen.Setoid r ≤ EqvGen.Setoid s :=
  eqvGen_le fun _ _ hr => EqvGen.rel _ _ <| h _ _ hr
#align setoid.eqv_gen_mono Setoid.eqvGen_mono

/-- There is a Galois insertion of equivalence relations on α into binary relations
    on α, with equivalence closure the lower adjoint. -/
def gi : @GaloisInsertion (α → α → Prop) (Setoid α) _ _ EqvGen.Setoid Rel where
  choice r _ := EqvGen.Setoid r
  gc _ s := ⟨fun H _ _ h => H <| EqvGen.rel _ _ h, fun H => eqvGen_of_setoid s ▸ eqvGen_mono H⟩
  le_l_u x := (eqvGen_of_setoid x).symm ▸ le_refl x
  choice_eq _ _ := rfl
#align setoid.gi Setoid.gi

open Function

/-- A function from α to β is injective iff its kernel is the bottom element of the complete lattice
    of equivalence relations on α. -/
theorem injective_iff_ker_bot (f : α → β) : Injective f ↔ ker f = ⊥ :=
  (@eq_bot_iff (Setoid α) _ _ (ker f)).symm
#align setoid.injective_iff_ker_bot Setoid.injective_iff_ker_bot

/-- The elements related to x ∈ α by the kernel of f are those in the preimage of f(x) under f. -/
theorem ker_iff_mem_preimage {f : α → β} {x y} : (ker f).Rel x y ↔ x ∈ f ⁻¹' {f y} :=
  Iff.rfl
#align setoid.ker_iff_mem_preimage Setoid.ker_iff_mem_preimage

/-- Equivalence between functions `α → β` such that `r x y → f x = f y` and functions
`quotient r → β`. -/
def liftEquiv (r : Setoid α) : { f : α → β // r ≤ ker f } ≃ (Quotient r → β) where
  toFun f := Quotient.lift (f : α → β) f.2
  invFun f := ⟨f ∘ Quotient.mk'', fun x y h => by simp [ker_def, Quotient.sound' h]⟩
  left_inv := fun ⟨f, hf⟩ => Subtype.eq <| funext fun x => rfl
  right_inv f := funext fun x => Quotient.inductionOn' x fun x => rfl
#align setoid.lift_equiv Setoid.liftEquiv

/-- The uniqueness part of the universal property for quotients of an arbitrary type. -/
theorem lift_unique {r : Setoid α} {f : α → β} (H : r ≤ ker f) (g : Quotient r → β)
    (Hg : f = g ∘ Quotient.mk'') : Quotient.lift f H = g := by
  ext ⟨x⟩
  erw [Quotient.lift_mk f H, Hg]
  rfl
#align setoid.lift_unique Setoid.lift_unique

/-- Given a map f from α to β, the natural map from the quotient of α by the kernel of f is
    injective. -/
theorem ker_lift_injective (f : α → β) : Injective (@Quotient.lift _ _ (ker f) f fun _ _ h => h) :=
  fun x y => Quotient.inductionOn₂' x y fun _ _ h => Quotient.sound' h
#align setoid.ker_lift_injective Setoid.ker_lift_injective

/-- Given a map f from α to β, the kernel of f is the unique equivalence relation on α whose
    induced map from the quotient of α to β is injective. -/
theorem ker_eq_lift_of_injective {r : Setoid α} (f : α → β) (H : ∀ x y, r.Rel x y → f x = f y)
    (h : Injective (Quotient.lift f H)) : ker f = r :=
  le_antisymm
    (fun x y hk =>
      Quotient.exact <| h <| show Quotient.lift f H ⟦x⟧ = Quotient.lift f H ⟦y⟧ from hk)
    H
#align setoid.ker_eq_lift_of_injective Setoid.ker_eq_lift_of_injective

variable (r : Setoid α) (f : α → β)

/-- The first isomorphism theorem for sets: the quotient of α by the kernel of a function f
    bijects with f's image. -/
noncomputable def quotientKerEquivRange : Quotient (ker f) ≃ Set.range f :=
  Equiv.ofBijective
    ((@Quotient.lift _ (Set.range f) (ker f) fun x => ⟨f x, Set.mem_range_self x⟩) fun _ _ h =>
      Subtype.ext_val h)
    ⟨fun x y h => ker_lift_injective f <| by rcases x with ⟨⟩; rcases y with ⟨⟩; injections,
      fun ⟨w, z, hz⟩ =>
      ⟨@Quotient.mk'' _ (ker f) z, Subtype.ext_iff_val.2 hz⟩⟩
#align setoid.quotient_ker_equiv_range Setoid.quotientKerEquivRange

/-- If `f` has a computable right-inverse, then the quotient by its kernel is equivalent to its
domain. -/
@[simps]
def quotientKerEquivOfRightInverse (g : β → α) (hf : Function.RightInverse g f) :
    Quotient (ker f) ≃ β where
  toFun a := (Quotient.liftOn' a f) fun _ _ => id
  invFun b := Quotient.mk'' (g b)
  left_inv a := Quotient.inductionOn' a fun a => Quotient.sound' <| hf (f a)
  right_inv := hf
#align setoid.quotient_ker_equiv_of_right_inverse Setoid.quotientKerEquivOfRightInverse
#align setoid.quotient_ker_equiv_of_right_inverse_symm_apply Setoid.quotientKerEquivOfRightInverse_symm_apply
#align setoid.quotient_ker_equiv_of_right_inverse_apply Setoid.quotientKerEquivOfRightInverse_apply

/-- The quotient of α by the kernel of a surjective function f bijects with f's codomain.

If a specific right-inverse of `f` is known, `Setoid.quotientKerEquivOfRightInverse` can be
definitionally more useful. -/
noncomputable def quotientKerEquivOfSurjective (hf : Surjective f) : Quotient (ker f) ≃ β :=
  quotientKerEquivOfRightInverse _ (Function.surjInv hf) (rightInverse_surjInv hf)
#align setoid.quotient_ker_equiv_of_surjective Setoid.quotientKerEquivOfSurjective

variable {r f}

/-- Given a function `f : α → β` and equivalence relation `r` on `α`, the equivalence
    closure of the relation on `f`'s image defined by '`x ≈ y` iff the elements of `f⁻¹(x)` are
    related to the elements of `f⁻¹(y)` by `r`.' -/
def map (r : Setoid α) (f : α → β) : Setoid β :=
  EqvGen.Setoid fun x y => ∃ a b, f a = x ∧ f b = y ∧ r.Rel a b
#align setoid.map Setoid.map

/-- Given a surjective function f whose kernel is contained in an equivalence relation r, the
    equivalence relation on f's codomain defined by x ≈ y ↔ the elements of f⁻¹(x) are related to
    the elements of f⁻¹(y) by r. -/
def mapOfSurjective (r) (f : α → β) (h : ker f ≤ r) (hf : Surjective f) : Setoid β :=
  ⟨fun x y => ∃ a b, f a = x ∧ f b = y ∧ r.Rel a b,
    ⟨fun x =>
      let ⟨y, hy⟩ := hf x
      ⟨y, y, hy, hy, r.refl' y⟩,
      fun ⟨x, y, hx, hy, h⟩ => ⟨y, x, hy, hx, r.symm' h⟩,
      fun ⟨x, y, hx, hy, h₁⟩ ⟨y', z, hy', hz, h₂⟩ =>
      ⟨x, z, hx, hz, r.trans' h₁ <| r.trans' (h <| by rwa [← hy'] at hy) h₂⟩⟩⟩
#align setoid.map_of_surjective Setoid.mapOfSurjective

/-- A special case of the equivalence closure of an equivalence relation r equalling r. -/
theorem mapOfSurjective_eq_map (h : ker f ≤ r) (hf : Surjective f) :
    map r f = mapOfSurjective r f h hf := by
  rw [← eqvGen_of_setoid (mapOfSurjective r f h hf)]; rfl
#align setoid.map_of_surjective_eq_map Setoid.mapOfSurjective_eq_map

/-- Given a function `f : α → β`, an equivalence relation `r` on `β` induces an equivalence
relation on `α` defined by '`x ≈ y` iff `f(x)` is related to `f(y)` by `r`'.

See note [reducible non-instances]. -/
@[reducible]
def comap (f : α → β) (r : Setoid β) : Setoid α :=
  ⟨r.Rel on f, r.iseqv.comap _⟩
#align setoid.comap Setoid.comap

theorem comap_rel (f : α → β) (r : Setoid β) (x y : α) : (comap f r).Rel x y ↔ r.Rel (f x) (f y) :=
  Iff.rfl
#align setoid.comap_rel Setoid.comap_rel

/-- Given a map `f : N → M` and an equivalence relation `r` on `β`, the equivalence relation
    induced on `α` by `f` equals the kernel of `r`'s quotient map composed with `f`. -/
theorem comap_eq {f : α → β} {r : Setoid β} : comap f r = ker (@Quotient.mk'' _ r ∘ f) :=
  ext fun x y => show _ ↔ ⟦_⟧ = ⟦_⟧ by rw [Quotient.eq]; rfl
#align setoid.comap_eq Setoid.comap_eq

/-- The second isomorphism theorem for sets. -/
noncomputable def comapQuotientEquiv (f : α → β) (r : Setoid β) :
    Quotient (comap f r) ≃ Set.range (@Quotient.mk'' _ r ∘ f) :=
  (Quotient.congrRight <| ext_iff.1 comap_eq).trans <| quotientKerEquivRange <| Quotient.mk'' ∘ f
#align setoid.comap_quotient_equiv Setoid.comapQuotientEquiv

variable (r f)

/-- The third isomorphism theorem for sets. -/
def quotientQuotientEquivQuotient (s : Setoid α) (h : r ≤ s) :
    Quotient (ker (Quot.mapRight h)) ≃ Quotient s where
  toFun x :=
    (Quotient.liftOn' x fun w =>
        (Quotient.liftOn' w (@Quotient.mk'' _ s)) fun x y H => Quotient.sound <| h H)
      fun x y => Quotient.inductionOn₂' x y fun w z H => show @Quot.mk _ _ _ = @Quot.mk _ _ _ from H
  invFun x :=
    (Quotient.liftOn' x fun w => @Quotient.mk'' _ (ker <| Quot.mapRight h) <| @Quotient.mk'' _ r w)
      fun x y H => Quotient.sound' <| show @Quot.mk _ _ _ = @Quot.mk _ _ _ from Quotient.sound H
  left_inv x :=
    Quotient.inductionOn' x fun y => Quotient.inductionOn' y fun w => by show ⟦_⟧ = _; rfl
  right_inv x := Quotient.inductionOn' x fun y => by show ⟦_⟧ = _; rfl
#align setoid.quotient_quotient_equiv_quotient Setoid.quotientQuotientEquivQuotient

variable {r f}

open Quotient

/-- Given an equivalence relation `r` on `α`, the order-preserving bijection between the set of
equivalence relations containing `r` and the equivalence relations on the quotient of `α` by `r`. -/
def correspondence (r : Setoid α) : { s // r ≤ s } ≃o Setoid (Quotient r) where
  toFun s := mapOfSurjective s.1 Quotient.mk'' ((ker_mk_eq r).symm ▸ s.2) exists_rep
  invFun s := ⟨comap Quotient.mk' s, fun x y h => by rw [comap_rel, eq_rel.2 h]⟩
  left_inv s := by
    ext
    refine ⟨?_, fun h => ⟨_, _, rfl, rfl, h⟩⟩
    intro ⟨a, b, hx, hy, H⟩
    refine s.1.trans' (s.1.symm' <| s.2 <| eq_rel.1 hx) (s.1.trans' H <| s.2 <| (eq_rel.1 hy))
  right_inv s :=
    ext' fun x y =>
      ⟨fun h => let ⟨_, _, hx, hy, H⟩ := h; hx ▸ hy ▸ H,
        Quotient.inductionOn₂ x y fun w z h => ⟨w, z, rfl, rfl, h⟩⟩
  map_rel_iff' := by
    intro s t
    refine ⟨?_, ?_⟩
    · intro h x y hs
      let ⟨a, b, hx, hy, ht⟩ := h ⟨x, y, rfl, rfl, hs⟩
      exact t.1.trans' (t.1.symm' <| t.2 <| eq_rel.1 hx) <| t.1.trans' ht <| t.2 <| eq_rel.1 hy
    · intro h x y hs
      let ⟨a, b, hx, hy, Hs⟩ := hs
      exact ⟨a, b, hx, hy, h Hs⟩
#align setoid.correspondence Setoid.correspondence

end Setoid

@[simp]
theorem Quotient.subsingleton_iff {s : Setoid α} : Subsingleton (Quotient s) ↔ s = ⊤ := by
  simp only [_root_.subsingleton_iff, eq_top_iff, Setoid.le_def, Setoid.top_def, Pi.top_apply,
    forall_const]
  refine' (surjective_quotient_mk _).forall.trans (forall_congr' fun a => _)
  refine' (surjective_quotient_mk _).forall.trans (forall_congr' fun b => _)
  simp_rw [←Quotient.mk''_eq_mk, Prop.top_eq_true, true_implies, Quotient.eq'']
  rfl
#align quotient.subsingleton_iff Quotient.subsingleton_iff

theorem Quot.subsingleton_iff (r : α → α → Prop) : Subsingleton (Quot r) ↔ EqvGen r = ⊤ := by
  simp only [_root_.subsingleton_iff, _root_.eq_top_iff, Pi.le_def, Pi.top_apply, forall_const]
  refine' (surjective_quot_mk _).forall.trans (forall_congr' fun a => _)
  refine' (surjective_quot_mk _).forall.trans (forall_congr' fun b => _)
  rw [Quot.eq]
  simp only [forall_const, le_Prop_eq, OrderTop.toTop, Pi.orderTop, Pi.top_apply,
             Prop.top_eq_true, true_implies]
#align quot.subsingleton_iff Quot.subsingleton_iff


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura, Mario Carneiro, Johannes Hölzl

! This file was ported from Lean 3 source module algebra.order.group.densely_ordered
! leanprover-community/mathlib commit 4e87c8477c6c38b753f050bc9664b94ee859896c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Order.Monoid.Canonical.Defs
import Mathlib.Algebra.Order.Group.Defs
import Mathlib.Algebra.Order.Monoid.OrderDual

/-!
# Lemmas about densely linearly ordered groups.
-/

variable {α : Type _}

section DenselyOrdered

variable [Group α] [LinearOrder α]

variable [CovariantClass α α (· * ·) (· ≤ ·)]

variable [DenselyOrdered α] {a b c : α}

@[to_additive]
theorem le_of_forall_lt_one_mul_le (h : ∀ ε < 1, a * ε ≤ b) : a ≤ b :=
  @le_of_forall_one_lt_le_mul αᵒᵈ _ _ _ _ _ _ _ _ h
#align le_of_forall_lt_one_mul_le le_of_forall_lt_one_mul_le
#align le_of_forall_neg_add_le le_of_forall_neg_add_le

@[to_additive]
theorem le_of_forall_one_lt_div_le (h : ∀ ε : α, 1 < ε → a / ε ≤ b) : a ≤ b :=
  le_of_forall_lt_one_mul_le fun ε ε1 => by
    simpa only [div_eq_mul_inv, inv_inv] using h ε⁻¹ (Left.one_lt_inv_iff.2 ε1)
#align le_of_forall_one_lt_div_le le_of_forall_one_lt_div_le
#align le_of_forall_pos_sub_le le_of_forall_pos_sub_le

@[to_additive]
theorem le_iff_forall_one_lt_le_mul : a ≤ b ↔ ∀ ε, 1 < ε → a ≤ b * ε :=
  ⟨fun h _ ε_pos => le_mul_of_le_of_one_le h ε_pos.le, le_of_forall_one_lt_le_mul⟩
#align le_iff_forall_one_lt_le_mul le_iff_forall_one_lt_le_mul
#align le_iff_forall_pos_le_add le_iff_forall_pos_le_add

@[to_additive]
theorem le_iff_forall_lt_one_mul_le : a ≤ b ↔ ∀ ε < 1, a * ε ≤ b :=
  @le_iff_forall_one_lt_le_mul αᵒᵈ _ _ _ _ _ _
#align le_iff_forall_lt_one_mul_le le_iff_forall_lt_one_mul_le
#align le_iff_forall_neg_add_le le_iff_forall_neg_add_le

end DenselyOrdered


theorem le_trans (a b c : mynat) (hab : a ≤ b) (hbc : b ≤ c) : a ≤ c :=
begin
cases hab with d hd,
cases hbc with e he,
use (d + e),
rw he,
rw hd,
rw add_assoc,
refl,
end