import ring_theory.algebraic
-- import data.finset
-- import algebra.field
-- import field_theory.subfield
import field_theory.algebraic_closure
-- import order.zorn
import Rings.ToMathlib
-- import data.mv_polynomial.equiv
-- import data.equiv.fin

open classical
local attribute [instance] prop_decidable

universes u v v0

section nttn

@[simp] def algebra.image
  (K : Type u) (L : Type v) [comm_semiring K] [semiring L] [algebra K L] : set L :=
set.range (algebra_map K L)

@[simp] def subfield_generated
  (K : Type u) {L : Type v} [field K] [field L] [algebra K L] (S : set L)
  : subfield L :=
subfield.closure (S ∪ algebra.image K L)

namespace subfield_generated

variables
  (K : Type u) {L : Type v} [field K] [field L] [algebra K L] (S : set L)

lemma subset_closure_set : S ⊆ subfield_generated K S :=
begin
  intros _ hs,
  simp only [subfield_generated, set_like.mem_coe],
  apply subfield.subset_closure,
  left,
  exact hs,
end

lemma subset_closure_base (a : K) : algebra_map K L a ∈ subfield_generated K S :=
begin
  simp only [subfield_generated, set_like.mem_coe],
  apply subfield.subset_closure,
  right,
  simp only [set.mem_range_self, algebra.image]
end

def ring_hom : K →+* subfield_generated K S :=
⟨
  (λ a, ⟨ algebra_map K L a , subset_closure_base K S a⟩ ),
  begin simp only [ring_hom.map_one], refl end,
  begin intros, simp only [ring_hom.map_mul], refl end,
  begin simp only [ring_hom.map_zero], refl end,
  begin intros, simp only [ring_hom.map_add], refl end
⟩

-- #check algebra

-- instance algebra : algebra K (subfield_generated K S) :=
--  ⟨ ring_hom , _ , _ , _ , _ ⟩

end subfield_generated

end nttn

namespace transcendent

variables
  (K : Type u) {L : Type u} [field K] [field L] [algebra K L]

def indep (S : set L) : Prop :=
Π (n : ℕ) (f : mv_polynomial (fin n) K) (as : fin n → L),
(Π k, as k ∈ S) → mv_polynomial.eval as (f : mv_polynomial (fin n) L) = 0 → f = 0

lemma indep_subset (S T : set L) (hST : S ⊆ T) : indep K T → indep K S :=
begin
  intros hT _ f as has hf,
  have has' : Π k, as k ∈ T := λ k, hST (has k),
  exact hT _ f as has' hf,
end

lemma indep_empty : indep K (∅ : set L)
| nat.zero f as has hf :=
begin
  apply @mv_polynomial.map_injective K L (fin 0) _ _ (algebra_map K L) (ring_hom.injective _),
  simp,
  rw mv_polynomial.is_empty (fin.is_empty) (mv_polynomial.map (algebra_map K L) f) as,
  rw ← mv_polynomial.C_0,
  rw (mv_polynomial.C_inj L),
  rw ← hf,
  simp only [mv_polynomial.eval, mv_polynomial.eval_map],
  unfold_coes,
end
| (nat.succ n) f as has hf :=
begin
  exfalso,
  rw ← set.mem_empty_eq (as 0),
  {apply has},
end

lemma indep_sUnion_chain {c : set (set L)} (hchain : zorn.chain has_subset.subset c)
  (h0 : c.nonempty)
  (hc : Π (S : set L) (hS : S ∈ c), indep K S) :
  indep K ⋃₀ c :=
begin
  intros k f as has hf,
  cases zorn.fin_range_sub_mem_chain_of_sub_union hchain h0 as has with S hS,
  cases hS with hSc hS,
  apply hc S hSc _ _ as _ hf,
  exact hS,
end

def basis (B : set L) : Prop := indep K B ∧ Π (S : set L), indep K S → B ⊆ S → S = B

lemma extend_to_basis_aux (S : set L) (hindS : indep K S) :
  ∃ (B : set L) (H : B ∈ {T : set L | S ⊆ T ∧ indep K T}),
  S ⊆ B ∧ Π (T : set L), T ∈ {T : set L | S ⊆ T ∧ indep K T} → B ⊆ T → T = B :=
(@zorn.zorn_subset_nonempty L { T : set L | S ⊆ T ∧ indep K T }
  (λ c hcsub hchain hc0,
    ⟨
      -- the upper bound by taking union
      ⋃₀ c ,
      ⟨
        let hScup : S ⊆ ⋃₀ c :=
        begin
          cases hc0 with T hT,
          cases hcsub hT with hST hand,
          have hTcup : T ⊆ ⋃₀ c := λ t ht , ⟨ T , hT , ht ⟩,
          exact set.subset.trans hST hTcup,
        end in
        ⟨ -- the upper bound is in the set
          hScup ,
          indep_sUnion_chain K hchain hc0 (λ S hs, (hcsub hs).2)
        ⟩ ,
        (λ S hS s hs, ⟨ S , hS , hs ⟩) -- showing the maximal element is in the set
      ⟩
    ⟩
  )
  S -- give U for the set being non-empty
  ⟨ set.subset.refl _ , hindS ⟩)

lemma subfield_mem (a : L) (K : subfield L) (haK : a ∈ K) :
  a = algebra_map K L (⟨ a , haK ⟩ : K) := rfl

lemma is_algebraic_subfield {a : L} {K : subfield L} (haK : a ∈ K) : is_algebraic K a :=
⟨
  polynomial.X - polynomial.C (⟨ a , haK ⟩ : K) ,
  polynomial.X_sub_C_ne_zero _ ,
  begin
    simp only [polynomial.aeval_X, polynomial.aeval_C, alg_hom.map_sub],
    rw ← subfield_mem a K haK,
    simp,
  end
⟩

-- #check @mv_polynomial.map_injective
-- -- mv_polynomial.eval₂ polynomial.C bs (⇑(mv_polynomial.map (subfield_generated.ring_hom K B)) f)

-- #check @ring_hom.injective

-- #check @mv_polynomial.eval₂

-- #check mv_polynomial.eval₂_zero
--
#check polynomial.as_sum_range

lemma mv_polynomial.zero_iff {K : Type u} {R : Type v} {σ : Type v0} [field K] [comm_semiring R]
  [nontrivial R] (f : K →+* R) (p : mv_polynomial σ K) : p = 0 ↔ mv_polynomial.map f p = 0 :=
begin
  split,
  {intro hp, rw hp, simp,},
  {
    intro hp,
    apply mv_polynomial.map_injective f (ring_hom.injective f),
    simpa using hp,
  },
end

lemma insert_dependent_iff_is_algebraic (S : set L) (a : L) :
  ¬ indep K (S ∪ {a}) → is_algebraic (subfield_generated K S) a :=
begin
  intro hdep,
  simp only [indep, not_forall] at hdep,
    cases hdep with n hdep,
    cases hdep with f hdep,
    cases hdep with as hdep,
    cases hdep with has hdep,
    cases hdep with hf hf0,
    let bs : fin n → polynomial (subfield_generated K S) :=
    λ k, @decidable.by_cases (as k ∈ S) (polynomial (subfield_generated K S)) _
      (λ h, polynomial.C
        (⟨ as k , subfield_generated.subset_closure_set K S h ⟩ : subfield_generated K S))
      (λ _, polynomial.X),
    let p : polynomial (subfield_generated K S) :=
      mv_polynomial.eval₂ polynomial.C bs
      (mv_polynomial.map (subfield_generated.ring_hom K S) f),
    use p,
    split,
    {
      intro hp,
      apply hf0,
      rw mv_polynomial.zero_iff (subfield_generated.ring_hom K S),

      sorry,
    },
    {
      simp only [p],
      rw ← hf,
      simp,
      sorry,
    },
end
#exit
lemma algebraic_over_basis (B : set L) (hB : basis K B) :
  algebra.is_algebraic (subfield_generated K B) L :=
begin
  intro x,
  by_cases hind : indep K (B ∪ {x}),
  {
    -- if B ∪ {x} is independent then x ∈ B since B is a basis
    have hBx := hB.2 (B ∪ {x}) hind (by simp),
    -- hence x ∈ K(B) and is independent over K(B)
    have hxBx : x ∈ subfield_generated K B,
    {apply subfield.subset_closure, left, rw ← hBx, right, exact set.mem_singleton x},
    apply is_algebraic_subfield hxBx,
  },
  {
    --
    simp only [indep, not_forall] at hind,
    cases hind with n hind,
    cases hind with f hind,
    cases hind with as hind,
    cases hind with has hind,
    cases hind with hf hf0,
    let bs : fin n → polynomial (subfield_generated K B) :=
    λ k, @decidable.by_cases (as k ∈ B) (polynomial (subfield_generated K B)) _
      (λ h, polynomial.C
        (⟨ as k , subfield_generated.subset_closure_set K B h ⟩ : subfield_generated K B))
      (λ _, polynomial.X),
    let p : polynomial (subfield_generated K B) :=
    mv_polynomial.eval₂ polynomial.C bs
    (mv_polynomial.map (subfield_generated.ring_hom K B) f),
    use p,
    split,
    {
      intro hp,
      apply hf0,
      rw mv_polynomial.zero_iff (subfield_generated.ring_hom K B),

      sorry,
    },
    {
      simp only [p],
      rw ← hf,
      simp,
      sorry,
    },
  }
end
#exit
variables (K) (L)

lemma extend_to_basis (S : set L) (hindS : indep K S) :
    ∃ (B : set L), S ⊆ B ∧ basis K B :=
begin
  cases extend_to_basis_aux K S hindS with B hB,
  cases hB with hmem hB,
  cases hB with hSB hbasis,
  use B,
  split,
  {exact hSB},
  {
    split,
    {exact hmem.2},
    {
      intros T hindT hBT,
      apply hbasis T _ hBT,
      exact ⟨ set.subset.trans hSB hBT , hindT ⟩,
    }
  }
end

lemma basis_ex : ∃ (B : set L), basis K B :=
begin
  cases extend_to_basis K L ∅ (indep_empty K) with B hB,
  use B,
  exact hB.2,
end

lemma basis_some : set L := @classical.some (set L) (λ S, basis K S) (basis_ex K L)

lemma degree : cardinal.{u} := cardinal.mk (basis_some K L)

end transcendent

namespace field_theory

variables
  {K L0 L1 : Type u}
  [field K] [field L0] [field L1]
  [algebra K L0] [algebra K L1]

open transcendent algebra

lemma iso_of_bij_indep (S0 : set L0) (S1 : set L1) :
  indep K S0 → indep K S1 → equiv S0 S1 →
  subfield_generated K S0 ≃+* subfield_generated K S1 :=
sorry

lemma iso_of_alg_closed_algebraic
  {K0 K1 L0 L1: Type u} [field K0] [field K1] [field L0] [field L1]
  [is_alg_closed L0] [is_alg_closed L1]
  [algebra K0 L0] [algebra K1 L1] : K0 ≃+* K1 →
  is_algebraic K0 L0 → is_algebraic K1 L1 → L0 ≃+* L1 := sorry

lemma iso_of_alg_closed_of_eq_trans_deg [is_alg_closed L0] [is_alg_closed L1]
  (B0 : set L0) (B1 : set L1) : transcendent.basis K B0 → transcendent.basis K B1 →
  equiv B0 B1
  → L0 ≃+* L1 :=
begin
  intros hB0 hB1 htdeg,
  apply iso_of_alg_closed_algebraic _
  (algebraic_over_basis K B0 hB0)
  (algebraic_over_basis K B1 hB1),
  apply iso_of_bij_indep B0 B1 hB0.1 hB1.1 htdeg,
end

end field_theory


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

! This file was ported from Lean 3 source module algebra.group.prod
! leanprover-community/mathlib commit cf9386b56953fb40904843af98b7a80757bbe7f9
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Group.Opposite
import Mathlib.Algebra.GroupWithZero.Units.Basic
import Mathlib.Algebra.Hom.Units

/-!
# Monoid, group etc structures on `M × N`

In this file we define one-binop (`Monoid`, `Group` etc) structures on `M × N`. We also prove
trivial `simp` lemmas, and define the following operations on `MonoidHom`s:

* `fst M N : M × N →* M`, `snd M N : M × N →* N`: projections `prod.fst` and `prod.snd`
  as `MonoidHom`s;
* `inl M N : M →* M × N`, `inr M N : N →* M × N`: inclusions of first/second monoid
  into the product;
* `f.prod g : `M →* N × P`: sends `x` to `(f x, g x)`;
* `f.coprod g : M × N →* P`: sends `(x, y)` to `f x * g y`;
* `f.prodMap g : M × N → M' × N'`: `prod.map f g` as a `MonoidHom`,
  sends `(x, y)` to `(f x, g y)`.

## Main declarations

* `mulMulHom`/`mulMonoidHom`/`mulMonoidWithZeroHom`: Multiplication bundled as a
  multiplicative/monoid/monoid with zero homomorphism.
* `divMonoidHom`/`divMonoidWithZeroHom`: Division bundled as a monoid/monoid with zero
  homomorphism.
-/

variable {A : Type _} {B : Type _} {G : Type _} {H : Type _} {M : Type _} {N : Type _} {P : Type _}

namespace Prod

@[to_additive]
instance [Mul M] [Mul N] : Mul (M × N) :=
  ⟨fun p q => ⟨p.1 * q.1, p.2 * q.2⟩⟩

@[to_additive (attr := simp)]
theorem fst_mul [Mul M] [Mul N] (p q : M × N) : (p * q).1 = p.1 * q.1 :=
  rfl
#align prod.fst_mul Prod.fst_mul
#align prod.fst_add Prod.fst_add

@[to_additive (attr := simp)]
theorem snd_mul [Mul M] [Mul N] (p q : M × N) : (p * q).2 = p.2 * q.2 :=
  rfl
#align prod.snd_mul Prod.snd_mul
#align prod.snd_add Prod.snd_add

@[to_additive (attr := simp)]
theorem mk_mul_mk [Mul M] [Mul N] (a₁ a₂ : M) (b₁ b₂ : N) :
    (a₁, b₁) * (a₂, b₂) = (a₁ * a₂, b₁ * b₂) :=
  rfl
#align prod.mk_mul_mk Prod.mk_mul_mk
#align prod.mk_add_mk Prod.mk_add_mk

@[to_additive (attr := simp)]
theorem swap_mul [Mul M] [Mul N] (p q : M × N) : (p * q).swap = p.swap * q.swap :=
  rfl
#align prod.swap_mul Prod.swap_mul
#align prod.swap_add Prod.swap_add

@[to_additive]
theorem mul_def [Mul M] [Mul N] (p q : M × N) : p * q = (p.1 * q.1, p.2 * q.2) :=
  rfl
#align prod.mul_def Prod.mul_def
#align prod.add_def Prod.add_def

@[to_additive]
theorem one_mk_mul_one_mk [Monoid M] [Mul N] (b₁ b₂ : N) :
    ((1 : M), b₁) * (1, b₂) = (1, b₁ * b₂) :=
  by rw [mk_mul_mk, mul_one]
#align prod.one_mk_mul_one_mk Prod.one_mk_mul_one_mk
#align prod.zero_mk_add_zero_mk Prod.zero_mk_add_zero_mk

@[to_additive]
theorem mk_one_mul_mk_one [Mul M] [Monoid N] (a₁ a₂ : M) :
    (a₁, (1 : N)) * (a₂, 1) = (a₁ * a₂, 1) :=
  by rw [mk_mul_mk, mul_one]
#align prod.mk_one_mul_mk_one Prod.mk_one_mul_mk_one
#align prod.mk_zero_add_mk_zero Prod.mk_zero_add_mk_zero

@[to_additive]
instance [One M] [One N] : One (M × N) :=
  ⟨(1, 1)⟩

@[to_additive (attr := simp)]
theorem fst_one [One M] [One N] : (1 : M × N).1 = 1 :=
  rfl
#align prod.fst_one Prod.fst_one
#align prod.fst_zero Prod.fst_zero

@[to_additive (attr := simp)]
theorem snd_one [One M] [One N] : (1 : M × N).2 = 1 :=
  rfl
#align prod.snd_one Prod.snd_one
#align prod.snd_zero Prod.snd_zero

@[to_additive]
theorem one_eq_mk [One M] [One N] : (1 : M × N) = (1, 1) :=
  rfl
#align prod.one_eq_mk Prod.one_eq_mk
#align prod.zero_eq_mk Prod.zero_eq_mk

@[to_additive (attr := simp)]
theorem mk_eq_one [One M] [One N] {x : M} {y : N} : (x, y) = 1 ↔ x = 1 ∧ y = 1 :=
  mk.inj_iff
#align prod.mk_eq_one Prod.mk_eq_one
#align prod.mk_eq_zero Prod.mk_eq_zero

@[to_additive (attr := simp)]
theorem swap_one [One M] [One N] : (1 : M × N).swap = 1 :=
  rfl
#align prod.swap_one Prod.swap_one
#align prod.swap_zero Prod.swap_zero

@[to_additive]
theorem fst_mul_snd [MulOneClass M] [MulOneClass N] (p : M × N) : (p.fst, 1) * (1, p.snd) = p :=
  ext (mul_one p.1) (one_mul p.2)
#align prod.fst_mul_snd Prod.fst_mul_snd
#align prod.fst_add_snd Prod.fst_add_snd

@[to_additive]
instance [Inv M] [Inv N] : Inv (M × N) :=
  ⟨fun p => (p.1⁻¹, p.2⁻¹)⟩

@[to_additive (attr := simp)]
theorem fst_inv [Inv G] [Inv H] (p : G × H) : p⁻¹.1 = p.1⁻¹ :=
  rfl
#align prod.fst_inv Prod.fst_inv
#align prod.fst_neg Prod.fst_neg

@[to_additive (attr := simp)]
theorem snd_inv [Inv G] [Inv H] (p : G × H) : p⁻¹.2 = p.2⁻¹ :=
  rfl
#align prod.snd_inv Prod.snd_inv
#align prod.snd_neg Prod.snd_neg

@[to_additive (attr := simp)]
theorem inv_mk [Inv G] [Inv H] (a : G) (b : H) : (a, b)⁻¹ = (a⁻¹, b⁻¹) :=
  rfl
#align prod.inv_mk Prod.inv_mk
#align prod.neg_mk Prod.neg_mk

@[to_additive (attr := simp)]
theorem swap_inv [Inv G] [Inv H] (p : G × H) : p⁻¹.swap = p.swap⁻¹ :=
  rfl
#align prod.swap_inv Prod.swap_inv
#align prod.swap_neg Prod.swap_neg

@[to_additive]
instance [InvolutiveInv M] [InvolutiveInv N] : InvolutiveInv (M × N) :=
  { inv_inv := fun _ => ext (inv_inv _) (inv_inv _) }

@[to_additive]
instance [Div M] [Div N] : Div (M × N) :=
  ⟨fun p q => ⟨p.1 / q.1, p.2 / q.2⟩⟩

@[to_additive (attr := simp)]
theorem fst_div [Div G] [Div H] (a b : G × H) : (a / b).1 = a.1 / b.1 :=
  rfl
#align prod.fst_div Prod.fst_div
#align prod.fst_sub Prod.fst_sub

@[to_additive (attr := simp)]
theorem snd_div [Div G] [Div H] (a b : G × H) : (a / b).2 = a.2 / b.2 :=
  rfl
#align prod.snd_div Prod.snd_div
#align prod.snd_sub Prod.snd_sub

@[to_additive (attr := simp)]
theorem mk_div_mk [Div G] [Div H] (x₁ x₂ : G) (y₁ y₂ : H) :
    (x₁, y₁) / (x₂, y₂) = (x₁ / x₂, y₁ / y₂) :=
  rfl
#align prod.mk_div_mk Prod.mk_div_mk
#align prod.mk_sub_mk Prod.mk_sub_mk

@[to_additive (attr := simp)]
theorem swap_div [Div G] [Div H] (a b : G × H) : (a / b).swap = a.swap / b.swap :=
  rfl
#align prod.swap_div Prod.swap_div
#align prod.swap_sub Prod.swap_sub

instance [MulZeroClass M] [MulZeroClass N] : MulZeroClass (M × N) :=
  { zero_mul := fun a => Prod.recOn a fun _ _ => mk.inj_iff.mpr ⟨zero_mul _, zero_mul _⟩,
    mul_zero := fun a => Prod.recOn a fun _ _ => mk.inj_iff.mpr ⟨mul_zero _, mul_zero _⟩ }

@[to_additive]
instance [Semigroup M] [Semigroup N] : Semigroup (M × N) :=
  { mul_assoc := fun _ _ _ => mk.inj_iff.mpr ⟨mul_assoc _ _ _, mul_assoc _ _ _⟩ }

@[to_additive]
instance [CommSemigroup G] [CommSemigroup H] : CommSemigroup (G × H) :=
  { mul_comm := fun _ _ => mk.inj_iff.mpr ⟨mul_comm _ _, mul_comm _ _⟩ }

instance [SemigroupWithZero M] [SemigroupWithZero N] : SemigroupWithZero (M × N) :=
  { zero_mul := by simp,
    mul_zero := by simp }

@[to_additive]
instance [MulOneClass M] [MulOneClass N] : MulOneClass (M × N) :=
  { one_mul := fun a => Prod.recOn a fun _ _ => mk.inj_iff.mpr ⟨one_mul _, one_mul _⟩,
    mul_one := fun a => Prod.recOn a fun _ _ => mk.inj_iff.mpr ⟨mul_one _, mul_one _⟩ }

@[to_additive]
instance [Monoid M] [Monoid N] : Monoid (M × N) :=
  { npow := fun z a => ⟨Monoid.npow z a.1, Monoid.npow z a.2⟩,
    npow_zero := fun z => ext (Monoid.npow_zero _) (Monoid.npow_zero _),
    npow_succ := fun z a => ext (Monoid.npow_succ _ _) (Monoid.npow_succ _ _),
    one_mul := by simp,
    mul_one := by simp }

@[to_additive Prod.subNegMonoid]
instance [DivInvMonoid G] [DivInvMonoid H] : DivInvMonoid (G × H) :=
  { div_eq_mul_inv := fun _ _ => mk.inj_iff.mpr ⟨div_eq_mul_inv _ _, div_eq_mul_inv _ _⟩,
    zpow := fun z a => ⟨DivInvMonoid.zpow z a.1, DivInvMonoid.zpow z a.2⟩,
    zpow_zero' := fun _ => ext (DivInvMonoid.zpow_zero' _) (DivInvMonoid.zpow_zero' _),
    zpow_succ' := fun _ _ => ext (DivInvMonoid.zpow_succ' _ _) (DivInvMonoid.zpow_succ' _ _),
    zpow_neg' := fun _ _ => ext (DivInvMonoid.zpow_neg' _ _) (DivInvMonoid.zpow_neg' _ _) }

@[to_additive]
instance [DivisionMonoid G] [DivisionMonoid H] : DivisionMonoid (G × H) :=
  { mul_inv_rev := fun a b => ext (mul_inv_rev _ _) (mul_inv_rev _ _),
    inv_eq_of_mul := fun a b h =>
      ext (inv_eq_of_mul_eq_one_right <| congr_arg fst h)
        (inv_eq_of_mul_eq_one_right <| congr_arg snd h),
    inv_inv := by simp }

@[to_additive SubtractionCommMonoid]
instance [DivisionCommMonoid G] [DivisionCommMonoid H] : DivisionCommMonoid (G × H) :=
  { mul_comm := fun ⟨g₁ , h₁⟩ ⟨_, _⟩ => by rw [mk_mul_mk, mul_comm g₁, mul_comm h₁]; rfl }

@[to_additive]
instance [Group G] [Group H] : Group (G × H) :=
  { mul_left_inv := fun _ => mk.inj_iff.mpr ⟨mul_left_inv _, mul_left_inv _⟩ }

@[to_additive]
instance [LeftCancelSemigroup G] [LeftCancelSemigroup H] : LeftCancelSemigroup (G × H) :=
  { mul_left_cancel := fun _ _ _ h =>
      Prod.ext (mul_left_cancel (Prod.ext_iff.1 h).1) (mul_left_cancel (Prod.ext_iff.1 h).2) }

@[to_additive]
instance [RightCancelSemigroup G] [RightCancelSemigroup H] : RightCancelSemigroup (G × H) :=
  { mul_right_cancel := fun _ _ _ h =>
      Prod.ext (mul_right_cancel (Prod.ext_iff.1 h).1) (mul_right_cancel (Prod.ext_iff.1 h).2) }

@[to_additive]
instance [LeftCancelMonoid M] [LeftCancelMonoid N] : LeftCancelMonoid (M × N) :=
  { mul_one := by simp,
    one_mul := by simp }

@[to_additive]
instance [RightCancelMonoid M] [RightCancelMonoid N] : RightCancelMonoid (M × N) :=
  { mul_one := by simp,
    one_mul := by simp }

@[to_additive]
instance [CancelMonoid M] [CancelMonoid N] : CancelMonoid (M × N) :=
  { mul_right_cancel := by simp only [mul_left_inj, imp_self, forall_const] }

@[to_additive]
instance [CommMonoid M] [CommMonoid N] : CommMonoid (M × N) :=
  { mul_comm := fun ⟨m₁, n₁⟩ ⟨_, _⟩ => by rw [mk_mul_mk, mk_mul_mk, mul_comm m₁, mul_comm n₁] }

@[to_additive]
instance [CancelCommMonoid M] [CancelCommMonoid N] : CancelCommMonoid (M × N) :=
  { mul_comm := fun ⟨m₁, n₁⟩ ⟨_, _⟩ => by rw [mk_mul_mk, mk_mul_mk, mul_comm m₁, mul_comm n₁] }

instance [MulZeroOneClass M] [MulZeroOneClass N] : MulZeroOneClass (M × N) :=
  { zero_mul := by simp,
    mul_zero := by simp }

instance [MonoidWithZero M] [MonoidWithZero N] : MonoidWithZero (M × N) :=
  { zero_mul := by simp,
    mul_zero := by simp }

instance [CommMonoidWithZero M] [CommMonoidWithZero N] : CommMonoidWithZero (M × N) :=
  { zero_mul := by simp,
    mul_zero := by simp }

@[to_additive]
instance [CommGroup G] [CommGroup H] : CommGroup (G × H) :=
  { mul_comm := fun ⟨g₁, h₁⟩ ⟨_, _⟩ => by rw [mk_mul_mk, mk_mul_mk, mul_comm g₁, mul_comm h₁] }

end Prod

namespace MulHom

section Prod

variable (M N) [Mul M] [Mul N] [Mul P]

/-- Given magmas `M`, `N`, the natural projection homomorphism from `M × N` to `M`.-/
@[to_additive
      "Given additive magmas `A`, `B`, the natural projection homomorphism
      from `A × B` to `A`"]
def fst : M × N →ₙ* M :=
  ⟨Prod.fst, fun _ _ => rfl⟩
#align mul_hom.fst MulHom.fst
#align add_hom.fst AddHom.fst

/-- Given magmas `M`, `N`, the natural projection homomorphism from `M × N` to `N`.-/
@[to_additive
      "Given additive magmas `A`, `B`, the natural projection homomorphism
      from `A × B` to `B`"]
def snd : M × N →ₙ* N :=
  ⟨Prod.snd, fun _ _ => rfl⟩
#align mul_hom.snd MulHom.snd
#align add_hom.snd AddHom.snd

variable {M N}

@[to_additive (attr := simp)]

@[to_additive (attr := simp)]
theorem coe_snd : ⇑(snd M N) = Prod.snd :=
  rfl
#align mul_hom.coe_snd MulHom.coe_snd
#align add_hom.coe_snd AddHom.coe_snd

/-- Combine two `MonoidHom`s `f : M →ₙ* N`, `g : M →ₙ* P` into
`f.prod g : M →ₙ* (N × P)` given by `(f.prod g) x = (f x, g x)`. -/
@[to_additive prod
      "Combine two `AddMonoidHom`s `f : AddHom M N`, `g : AddHom M P` into
      `f.prod g : AddHom M (N × P)` given by `(f.prod g) x = (f x, g x)`"]
protected def prod (f : M →ₙ* N) (g : M →ₙ* P) :
    M →ₙ* N × P where
  toFun := Pi.prod f g
  map_mul' x y := Prod.ext (f.map_mul x y) (g.map_mul x y)
#align mul_hom.prod MulHom.prod
#align add_hom.prod AddHom.prod

@[to_additive coe_prod]
theorem coe_prod (f : M →ₙ* N) (g : M →ₙ* P) : ⇑(f.prod g) = Pi.prod f g :=
  rfl
#align mul_hom.coe_prod MulHom.coe_prod
#align add_hom.coe_prod AddHom.coe_prod

@[to_additive (attr := simp) prod_apply]
theorem prod_apply (f : M →ₙ* N) (g : M →ₙ* P) (x) : f.prod g x = (f x, g x) :=
  rfl
#align mul_hom.prod_apply MulHom.prod_apply
#align add_hom.prod_apply AddHom.prod_apply

@[to_additive (attr := simp) fst_comp_prod]
theorem fst_comp_prod (f : M →ₙ* N) (g : M →ₙ* P) : (fst N P).comp (f.prod g) = f :=
  ext fun _ => rfl
#align mul_hom.fst_comp_prod MulHom.fst_comp_prod
#align add_hom.fst_comp_prod AddHom.fst_comp_prod

@[to_additive (attr := simp) snd_comp_prod]
theorem snd_comp_prod (f : M →ₙ* N) (g : M →ₙ* P) : (snd N P).comp (f.prod g) = g :=
  ext fun _ => rfl
#align mul_hom.snd_comp_prod MulHom.snd_comp_prod
#align add_hom.snd_comp_prod AddHom.snd_comp_prod

@[to_additive (attr := simp) prod_unique]
theorem prod_unique (f : M →ₙ* N × P) : ((fst N P).comp f).prod ((snd N P).comp f) = f :=
  ext fun x => by simp only [prod_apply, coe_fst, coe_snd, comp_apply, Prod.mk.eta]
#align mul_hom.prod_unique MulHom.prod_unique
#align add_hom.prod_unique AddHom.prod_unique

end Prod

section Prod_map

variable {M' : Type _} {N' : Type _} [Mul M] [Mul N] [Mul M'] [Mul N'] [Mul P] (f : M →ₙ* M')
  (g : N →ₙ* N')

/-- `Prod.map` as a `MonoidHom`. -/
@[to_additive prodMap "`prod.map` as an `AddMonoidHom`"]
def prodMap : M × N →ₙ* M' × N' :=
  (f.comp (fst M N)).prod (g.comp (snd M N))
#align mul_hom.prod_map MulHom.prodMap
#align add_hom.prod_map AddHom.prodMap

@[to_additive prodMap_def]
theorem prodMap_def : prodMap f g = (f.comp (fst M N)).prod (g.comp (snd M N)) :=
  rfl
#align mul_hom.prod_map_def MulHom.prodMap_def
#align add_hom.prod_map_def AddHom.prodMap_def

@[to_additive (attr := simp) coe_prodMap]
theorem coe_prodMap : ⇑(prodMap f g) = Prod.map f g :=
  rfl
#align mul_hom.coe_prod_map MulHom.coe_prodMap
#align add_hom.coe_prod_map AddHom.coe_prodMap

@[to_additive prod_comp_prodMap]
theorem prod_comp_prodMap (f : P →ₙ* M) (g : P →ₙ* N) (f' : M →ₙ* M') (g' : N →ₙ* N') :
    (f'.prodMap g').comp (f.prod g) = (f'.comp f).prod (g'.comp g) :=
  rfl
#align mul_hom.prod_comp_prod_map MulHom.prod_comp_prodMap
#align add_hom.prod_comp_prod_map AddHom.prod_comp_prodMap

end Prod_map

section Coprod

variable [Mul M] [Mul N] [CommSemigroup P] (f : M →ₙ* P) (g : N →ₙ* P)

/-- Coproduct of two `MulHom`s with the same codomain:
`f.coprod g (p : M × N) = f p.1 * g p.2`. -/
@[to_additive
      "Coproduct of two `AddHom`s with the same codomain:
      `f.coprod g (p : M × N) = f p.1 + g p.2`."]
def coprod : M × N →ₙ* P :=
  f.comp (fst M N) * g.comp (snd M N)
#align mul_hom.coprod MulHom.coprod
#align add_hom.coprod AddHom.coprod

@[to_additive (attr := simp)]
theorem coprod_apply (p : M × N) : f.coprod g p = f p.1 * g p.2 :=
  rfl
#align mul_hom.coprod_apply MulHom.coprod_apply
#align add_hom.coprod_apply AddHom.coprod_apply

@[to_additive]
theorem comp_coprod {Q : Type _} [CommSemigroup Q] (h : P →ₙ* Q) (f : M →ₙ* P) (g : N →ₙ* P) :
    h.comp (f.coprod g) = (h.comp f).coprod (h.comp g) :=
  ext fun x => by simp
#align mul_hom.comp_coprod MulHom.comp_coprod
#align add_hom.comp_coprod AddHom.comp_coprod

end Coprod

end MulHom

namespace MonoidHom

variable (M N) [MulOneClass M] [MulOneClass N]

/-- Given monoids `M`, `N`, the natural projection homomorphism from `M × N` to `M`.-/
@[to_additive
      "Given additive monoids `A`, `B`, the natural projection homomorphism
      from `A × B` to `A`"]
def fst : M × N →* M :=
  { toFun := Prod.fst,
    map_one' := rfl,
    map_mul' := fun _ _ => rfl }
#align monoid_hom.fst MonoidHom.fst
#align add_monoid_hom.fst AddMonoidHom.fst

/-- Given monoids `M`, `N`, the natural projection homomorphism from `M × N` to `N`.-/
@[to_additive
      "Given additive monoids `A`, `B`, the natural projection homomorphism
      from `A × B` to `B`"]
def snd : M × N →* N :=
  { toFun := Prod.snd,
    map_one' := rfl,
    map_mul' := fun _ _ => rfl }
#align monoid_hom.snd MonoidHom.snd
#align add_monoid_hom.snd AddMonoidHom.snd

/-- Given monoids `M`, `N`, the natural inclusion homomorphism from `M` to `M × N`. -/
@[to_additive
      "Given additive monoids `A`, `B`, the natural inclusion homomorphism
      from `A` to `A × B`."]
def inl : M →* M × N :=
  { toFun := fun x => (x, 1),
    map_one' := rfl,
    map_mul' := fun _ _ => Prod.ext rfl (one_mul 1).symm }
#align monoid_hom.inl MonoidHom.inl
#align add_monoid_hom.inl AddMonoidHom.inl

/-- Given monoids `M`, `N`, the natural inclusion homomorphism from `N` to `M × N`. -/
@[to_additive
      "Given additive monoids `A`, `B`, the natural inclusion homomorphism
      from `B` to `A × B`."]
def inr : N →* M × N :=
  { toFun := fun y => (1, y),
    map_one' := rfl,
    map_mul' := fun _ _ => Prod.ext (one_mul 1).symm rfl }
#align monoid_hom.inr MonoidHom.inr
#align add_monoid_hom.inr AddMonoidHom.inr

variable {M N}

@[to_additive (attr := simp)]
theorem coe_fst : ⇑(fst M N) = Prod.fst :=
  rfl
#align monoid_hom.coe_fst MonoidHom.coe_fst
#align add_monoid_hom.coe_fst AddMonoidHom.coe_fst

@[to_additive (attr := simp)]
theorem coe_snd : ⇑(snd M N) = Prod.snd :=
  rfl
#align monoid_hom.coe_snd MonoidHom.coe_snd
#align add_monoid_hom.coe_snd AddMonoidHom.coe_snd

@[to_additive (attr := simp)]
theorem inl_apply (x) : inl M N x = (x, 1) :=
  rfl
#align monoid_hom.inl_apply MonoidHom.inl_apply
#align add_monoid_hom.inl_apply AddMonoidHom.inl_apply

@[to_additive (attr := simp)]
theorem inr_apply (y) : inr M N y = (1, y) :=
  rfl
#align monoid_hom.inr_apply MonoidHom.inr_apply
#align add_monoid_hom.inr_apply AddMonoidHom.inr_apply

@[to_additive (attr := simp)]
theorem fst_comp_inl : (fst M N).comp (inl M N) = id M :=
  rfl
#align monoid_hom.fst_comp_inl MonoidHom.fst_comp_inl
#align add_monoid_hom.fst_comp_inl AddMonoidHom.fst_comp_inl

@[to_additive (attr := simp)]
theorem snd_comp_inl : (snd M N).comp (inl M N) = 1 :=
  rfl
#align monoid_hom.snd_comp_inl MonoidHom.snd_comp_inl
#align add_monoid_hom.snd_comp_inl AddMonoidHom.snd_comp_inl

@[to_additive (attr := simp)]
theorem fst_comp_inr : (fst M N).comp (inr M N) = 1 :=
  rfl
#align monoid_hom.fst_comp_inr MonoidHom.fst_comp_inr
#align add_monoid_hom.fst_comp_inr AddMonoidHom.fst_comp_inr

@[to_additive (attr := simp)]
theorem snd_comp_inr : (snd M N).comp (inr M N) = id N :=
  rfl
#align monoid_hom.snd_comp_inr MonoidHom.snd_comp_inr
#align add_monoid_hom.snd_comp_inr AddMonoidHom.snd_comp_inr

section Prod

variable [MulOneClass P]

/-- Combine two `MonoidHom`s `f : M →* N`, `g : M →* P` into `f.prod g : M →* N × P`
given by `(f.prod g) x = (f x, g x)`. -/
@[to_additive prod
      "Combine two `AddMonoidHom`s `f : M →+ N`, `g : M →+ P` into
      `f.prod g : M →+ N × P` given by `(f.prod g) x = (f x, g x)`"]
protected def prod (f : M →* N) (g : M →* P) :
    M →* N × P where
  toFun := Pi.prod f g
  map_one' := Prod.ext f.map_one g.map_one
  map_mul' x y := Prod.ext (f.map_mul x y) (g.map_mul x y)
#align monoid_hom.prod MonoidHom.prod
#align add_monoid_hom.prod AddMonoidHom.prod

@[to_additive coe_prod]
theorem coe_prod (f : M →* N) (g : M →* P) : ⇑(f.prod g) = Pi.prod f g :=
  rfl
#align monoid_hom.coe_prod MonoidHom.coe_prod
#align add_monoid_hom.coe_prod AddMonoidHom.coe_prod

@[to_additive (attr := simp) prod_apply]
theorem prod_apply (f : M →* N) (g : M →* P) (x) : f.prod g x = (f x, g x) :=
  rfl
#align monoid_hom.prod_apply MonoidHom.prod_apply
#align add_monoid_hom.prod_apply AddMonoidHom.prod_apply

@[to_additive (attr := simp) fst_comp_prod]
theorem fst_comp_prod (f : M →* N) (g : M →* P) : (fst N P).comp (f.prod g) = f :=
  ext fun _ => rfl
#align monoid_hom.fst_comp_prod MonoidHom.fst_comp_prod
#align add_monoid_hom.fst_comp_prod AddMonoidHom.fst_comp_prod

@[to_additive (attr := simp) snd_comp_prod]
theorem snd_comp_prod (f : M →* N) (g : M →* P) : (snd N P).comp (f.prod g) = g :=
  ext fun _ => rfl
#align monoid_hom.snd_comp_prod MonoidHom.snd_comp_prod
#align add_monoid_hom.snd_comp_prod AddMonoidHom.snd_comp_prod

@[to_additive (attr := simp) prod_unique]
theorem prod_unique (f : M →* N × P) : ((fst N P).comp f).prod ((snd N P).comp f) = f :=
  ext fun x => by simp only [prod_apply, coe_fst, coe_snd, comp_apply, Prod.mk.eta]
#align monoid_hom.prod_unique MonoidHom.prod_unique
#align add_monoid_hom.prod_unique AddMonoidHom.prod_unique

end Prod

section Prod_map

variable {M' : Type _} {N' : Type _} [MulOneClass M'] [MulOneClass N'] [MulOneClass P]
  (f : M →* M') (g : N →* N')

/-- `prod.map` as a `MonoidHom`. -/
@[to_additive prodMap "`prod.map` as an `AddHonoidHom`"]
def prodMap : M × N →* M' × N' :=
  (f.comp (fst M N)).prod (g.comp (snd M N))
#align monoid_hom.prod_map MonoidHom.prodMap
#align add_monoid_hom.prod_map AddMonoidHom.prodMap

@[to_additive prodMap_def]
theorem prodMap_def : prodMap f g = (f.comp (fst M N)).prod (g.comp (snd M N)) :=
  rfl
#align monoid_hom.prod_map_def MonoidHom.prodMap_def
#align add_monoid_hom.prod_map_def AddMonoidHom.prodMap_def

@[to_additive (attr := simp) coe_prodMap]
theorem coe_prodMap : ⇑(prodMap f g) = Prod.map f g :=
  rfl
#align monoid_hom.coe_prod_map MonoidHom.coe_prodMap
#align add_monoid_hom.coe_prod_map AddMonoidHom.coe_prodMap

@[to_additive prod_comp_prodMap]
theorem prod_comp_prodMap (f : P →* M) (g : P →* N) (f' : M →* M') (g' : N →* N') :
    (f'.prodMap g').comp (f.prod g) = (f'.comp f).prod (g'.comp g) :=
  rfl
#align monoid_hom.prod_comp_prod_map MonoidHom.prod_comp_prodMap
#align add_monoid_hom.prod_comp_prod_map AddMonoidHom.prod_comp_prodMap

end Prod_map

section Coprod

variable [CommMonoid P] (f : M →* P) (g : N →* P)

/-- Coproduct of two `MonoidHom`s with the same codomain:
`f.coprod g (p : M × N) = f p.1 * g p.2`. -/
@[to_additive
      "Coproduct of two `AddMonoidHom`s with the same codomain:
      `f.coprod g (p : M × N) = f p.1 + g p.2`."]
def coprod : M × N →* P :=
  f.comp (fst M N) * g.comp (snd M N)
#align monoid_hom.coprod MonoidHom.coprod
#align add_monoid_hom.coprod AddMonoidHom.coprod

@[to_additive (attr := simp)]
theorem coprod_apply (p : M × N) : f.coprod g p = f p.1 * g p.2 :=
  rfl
#align monoid_hom.coprod_apply MonoidHom.coprod_apply
#align add_monoid_hom.coprod_apply AddMonoidHom.coprod_apply

@[to_additive (attr := simp)]
theorem coprod_comp_inl : (f.coprod g).comp (inl M N) = f :=
  ext fun x => by simp [coprod_apply]
#align monoid_hom.coprod_comp_inl MonoidHom.coprod_comp_inl
#align add_monoid_hom.coprod_comp_inl AddMonoidHom.coprod_comp_inl

@[to_additive (attr := simp)]
theorem coprod_comp_inr : (f.coprod g).comp (inr M N) = g :=
  ext fun x => by simp [coprod_apply]
#align monoid_hom.coprod_comp_inr MonoidHom.coprod_comp_inr
#align add_monoid_hom.coprod_comp_inr AddMonoidHom.coprod_comp_inr

@[to_additive (attr := simp)]
theorem coprod_unique (f : M × N →* P) : (f.comp (inl M N)).coprod (f.comp (inr M N)) = f :=
  ext fun x => by simp [coprod_apply, inl_apply, inr_apply, ← map_mul]
#align monoid_hom.coprod_unique MonoidHom.coprod_unique
#align add_monoid_hom.coprod_unique AddMonoidHom.coprod_unique

@[to_additive (attr := simp)]
theorem coprod_inl_inr {M N : Type _} [CommMonoid M] [CommMonoid N] :
    (inl M N).coprod (inr M N) = id (M × N) :=
  coprod_unique (id <| M × N)
#align monoid_hom.coprod_inl_inr MonoidHom.coprod_inl_inr
#align add_monoid_hom.coprod_inl_inr AddMonoidHom.coprod_inl_inr

@[to_additive]
theorem comp_coprod {Q : Type _} [CommMonoid Q] (h : P →* Q) (f : M →* P) (g : N →* P) :
    h.comp (f.coprod g) = (h.comp f).coprod (h.comp g) :=
  ext fun x => by simp
#align monoid_hom.comp_coprod MonoidHom.comp_coprod
#align add_monoid_hom.comp_coprod AddMonoidHom.comp_coprod

end Coprod

end MonoidHom

namespace MulEquiv

section

variable [MulOneClass M] [MulOneClass N]

/-- The equivalence between `M × N` and `N × M` given by swapping the components
is multiplicative. -/
@[to_additive prodComm
      "The equivalence between `M × N` and `N × M` given by swapping the
      components is additive."]
def prodComm : M × N ≃* N × M :=
  { Equiv.prodComm M N with map_mul' := fun ⟨_, _⟩ ⟨_, _⟩ => rfl }
#align mul_equiv.prod_comm MulEquiv.prodComm
#align add_equiv.prod_comm AddEquiv.prodComm

@[to_additive (attr := simp) coe_prodComm]
theorem coe_prodComm : ⇑(prodComm : M × N ≃* N × M) = Prod.swap :=
  rfl
#align mul_equiv.coe_prod_comm MulEquiv.coe_prodComm
#align add_equiv.coe_prod_comm AddEquiv.coe_prodComm

@[to_additive (attr := simp) coe_prodComm_symm]
theorem coe_prodComm_symm : ⇑(prodComm : M × N ≃* N × M).symm = Prod.swap :=
  rfl
#align mul_equiv.coe_prod_comm_symm MulEquiv.coe_prodComm_symm
#align add_equiv.coe_prod_comm_symm AddEquiv.coe_prodComm_symm

variable {M' N' : Type _} [MulOneClass M'] [MulOneClass N']

/-- Product of multiplicative isomorphisms; the maps come from `Equiv.prodCongr`.-/
@[to_additive prodCongr "Product of additive isomorphisms; the maps come from `Equiv.prodCongr`."]
def prodCongr (f : M ≃* M') (g : N ≃* N') : M × N ≃* M' × N' :=
  { f.toEquiv.prodCongr g.toEquiv with
    map_mul' := fun _ _ => Prod.ext (f.map_mul _ _) (g.map_mul _ _) }
#align mul_equiv.prod_congr MulEquiv.prodCongr
#align add_equiv.prod_congr AddEquiv.prodCongr

/-- Multiplying by the trivial monoid doesn't change the structure.-/
@[to_additive uniqueProd "Multiplying by the trivial monoid doesn't change the structure."]
def uniqueProd [Unique N] : N × M ≃* M :=
  { Equiv.uniqueProd M N with map_mul' := fun _ _ => rfl }
#align mul_equiv.unique_prod MulEquiv.uniqueProd
#align add_equiv.unique_prod AddEquiv.uniqueProd

/-- Multiplying by the trivial monoid doesn't change the structure.-/
@[to_additive prodUnique "Multiplying by the trivial monoid doesn't change the structure."]
def prodUnique [Unique N] : M × N ≃* M :=
  { Equiv.prodUnique M N with map_mul' := fun _ _ => rfl }
#align mul_equiv.prod_unique MulEquiv.prodUnique
#align add_equiv.prod_unique AddEquiv.prodUnique

end

section

variable [Monoid M] [Monoid N]

/-- The monoid equivalence between units of a product of two monoids, and the product of the
    units of each monoid. -/
@[to_additive prodAddUnits
      "The additive monoid equivalence between additive units of a product
      of two additive monoids, and the product of the additive units of each additive monoid."]
def prodUnits : (M × N)ˣ ≃* Mˣ × Nˣ where
  toFun := (Units.map (MonoidHom.fst M N)).prod (Units.map (MonoidHom.snd M N))
  invFun u := ⟨(u.1, u.2), (↑u.1⁻¹, ↑u.2⁻¹), by simp, by simp⟩
  left_inv u := by
    simp only [MonoidHom.prod_apply, Units.coe_map, MonoidHom.coe_fst, MonoidHom.coe_snd,
      Prod.mk.eta, Units.coe_map_inv, Units.mk_val]
  right_inv := fun ⟨u₁, u₂⟩ => by
    simp only [Units.map, MonoidHom.coe_fst, Units.inv_eq_val_inv,
      MonoidHom.coe_snd, MonoidHom.prod_apply, Prod.mk.injEq]
    exact ⟨rfl, rfl⟩
  map_mul' := MonoidHom.map_mul _
#align mul_equiv.prod_units MulEquiv.prodUnits
#align add_equiv.prod_add_units AddEquiv.prodAddUnits

end

end MulEquiv

namespace Units

open MulOpposite

/-- Canonical homomorphism of monoids from `αˣ` into `α × αᵐᵒᵖ`.
Used mainly to define the natural topology of `αˣ`. -/
@[to_additive (attr := simps)
      "Canonical homomorphism of additive monoids from `AddUnits α` into `α × αᵃᵒᵖ`.
      Used mainly to define the natural topology of `AddUnits α`."]
def embedProduct (α : Type _) [Monoid α] : αˣ →* α × αᵐᵒᵖ where
  toFun x := ⟨x, op ↑x⁻¹⟩
  map_one' := by
    simp only [inv_one, eq_self_iff_true, Units.val_one, op_one, Prod.mk_eq_one, and_self_iff]
  map_mul' x y := by simp only [mul_inv_rev, op_mul, Units.val_mul, Prod.mk_mul_mk]
#align units.embed_product Units.embedProduct
#align add_units.embed_product AddUnits.embedProduct
#align units.embed_product_apply Units.embedProduct_apply
#align add_units.embed_product_apply AddUnits.embedProduct_apply

@[to_additive]
theorem embedProduct_injective (α : Type _) [Monoid α] : Function.Injective (embedProduct α) :=
  fun _ _ h => Units.ext <| (congr_arg Prod.fst h : _)
#align units.embed_product_injective Units.embedProduct_injective
#align add_units.embed_product_injective AddUnits.embedProduct_injective

end Units

/-! ### Multiplication and division as homomorphisms -/

section BundledMulDiv

variable {α : Type _}

/-- Multiplication as a multiplicative homomorphism. -/
@[to_additive (attr := simps) "Addition as an additive homomorphism."]
def mulMulHom [CommSemigroup α] :
    α × α →ₙ* α where
  toFun a := a.1 * a.2
  map_mul' _ _ := mul_mul_mul_comm _ _ _ _
#align mul_mul_hom mulMulHom
#align add_add_hom addAddHom
#align mul_mul_hom_apply mulMulHom_apply
#align add_add_hom_apply addAddHom_apply

/-- Multiplication as a monoid homomorphism. -/
@[to_additive (attr := simps) "Addition as an additive monoid homomorphism."]
def mulMonoidHom [CommMonoid α] : α × α →* α :=
  { mulMulHom with map_one' := mul_one _ }
#align mul_monoid_hom mulMonoidHom
#align add_add_monoid_hom addAddMonoidHom
#align mul_monoid_hom_apply mulMonoidHom_apply
#align add_add_monoid_hom_apply addAddMonoidHom_apply

/-- Multiplication as a multiplicative homomorphism with zero. -/
@[simps]
def mulMonoidWithZeroHom [CommMonoidWithZero α] : α × α →*₀ α :=
  { mulMonoidHom with map_zero' := mul_zero _ }
#align mul_monoid_with_zero_hom mulMonoidWithZeroHom
#align mul_monoid_with_zero_hom_apply mulMonoidWithZeroHom_apply

/-- Division as a monoid homomorphism. -/
@[to_additive (attr := simps) "Subtraction as an additive monoid homomorphism."]
def divMonoidHom [DivisionCommMonoid α] : α × α →* α where
  toFun a := a.1 / a.2
  map_one' := div_one _
  map_mul' _ _ := mul_div_mul_comm _ _ _ _
#align div_monoid_hom divMonoidHom
#align sub_add_monoid_hom subAddMonoidHom
#align div_monoid_hom_apply divMonoidHom_apply
#align sub_add_monoid_hom_apply subAddMonoidHom_apply

/-- Division as a multiplicative homomorphism with zero. -/
@[simps]
def divMonoidWithZeroHom [CommGroupWithZero α] : α × α →*₀ α where
  toFun a := a.1 / a.2
  map_zero' := zero_div _
  map_one' := div_one _
  map_mul' _ _ := mul_div_mul_comm _ _ _ _
#align div_monoid_with_zero_hom divMonoidWithZeroHom
#align div_monoid_with_zero_hom_apply divMonoidWithZeroHom_apply

end BundledMulDiv


/-
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 8631e2d5ea77f6c13054d9151d82b83069680cb1
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
-- import Mathbin.Order.ConditionallyCompleteLattice.Group
import Mathlib.Algebra.BigOperators.Basic
import Mathlib.Algebra.Ring.Basic
import Mathlib.Algebra.Ring.Defs
import Mathlib.Algebra.Field.Basic
import Mathlib.Algebra.Order.Field.Basic
import Mathlib.Algebra.Order.Ring.Defs
import Mathlib.Algebra.Order.Floor
import Mathlib.Algebra.Module.Basic
import Hata.MathlibPort.Algebra.Algebra.Basic
import Mathlib.Data.Real.Basic
import Hata.MathlibPort.Algebra.Order.Nonneg.Field
import Hata.MathlibPort.Algebra.Order.Nonneg.Ring
import Mathlib.Algebra.Order.Field.Canonical.Basic
-- import Mathlib.Data.Real.Pointwise
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, `conditionally_complete_linear_order_bot`;

* `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:

  - `linear_ordered_semiring ℝ≥0`;
  - `ordered_comm_semiring ℝ≥0`;
  - `canonically_ordered_comm_semiring ℝ≥0`;
  - `linear_ordered_comm_group_with_zero ℝ≥0`;
  - `canonically_linear_ordered_add_monoid ℝ≥0`;
  - `archimedean ℝ≥0`;
  - `conditionally_complete_linear_order_bot ℝ≥0`.

  These instances are derived from corresponding instances about the type `{x : α // 0 ≤ x}` in an
  appropriate ordered field/ring/group/monoid `α`. See `algebra/order/nonneg`.

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

We also define an instance `can_lift ℝ ℝ≥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`.
-/

section

open Classical Real Nonneg -- BigOperators

-- instance : DenselyOrdered ℝ := inferInstance
-- instance : DenselyOrdered { r : ℝ // 0 ≤ r } := inferInstance
-- example : LinearOrderedRing ℝ := inferInstance

-- to ensure these instances are computable
/-- Nonnegative real numbers. -/
def Nnreal :=
  { r : ℝ // 0 ≤ r }deriving StrictOrderedSemiring, CommMonoidWithZero, -- FloorSemiring,
   CommSemiring,
  Semiring, SemilatticeInf, SemilatticeSup, DistribLattice --, DenselyOrdered,
  --  OrderBot,
  -- CanonicallyLinearOrderedSemifield, LinearOrderedCommGroupWithZero, Archimedean,
  -- LinearOrderedSemiring, OrderedCommSemiring, CanonicallyOrderedCommSemiring, Sub, OrderedSub, Div,
  -- Inhabited
#align nnreal Nnreal

-- porting note (MxmUrw): somehow the deriving statement doesn't work, so we use
-- simple instance statements
instance : DenselyOrdered Nnreal := denselyOrdered
noncomputable instance : FloorSemiring Nnreal := floorSemiring
instance : OrderBot Nnreal := orderBot
noncomputable instance : CanonicallyLinearOrderedSemifield Nnreal := canonicallyLinearOrderedSemifield
noncomputable instance : LinearOrderedCommGroupWithZero Nnreal := linearOrderedCommGroupWithZero
instance : Archimedean Nnreal := archimedean
noncomputable instance : LinearOrderedSemiring Nnreal := linearOrderedSemiring
instance : OrderedCommSemiring Nnreal := orderedCommSemiring
instance : CanonicallyOrderedCommSemiring Nnreal := canonicallyOrderedCommSemiring
noncomputable instance : Sub Nnreal := Nonneg.hasSub
noncomputable instance : OrderedSub Nnreal := Nonneg.hasOrderedSub
noncomputable instance : Div Nnreal := Nonneg.hasDiv
instance : Inhabited Nnreal := Nonneg.inhabited

-- mathport name: nnreal
scoped[Nnreal] notation "ℝ≥0" => Nnreal

namespace Nnreal

instance : Coe ℝ≥0 ℝ :=
  ⟨Subtype.val⟩

open Coe

-- example (a : ℝ) : (coe a : ℝ≥0) =

-- 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 coe fun r => 0 ≤ r :=
  Subtype.canLift _
#align nnreal.can_lift Nnreal.canLift

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 :=
  Iff.intro Nnreal.eq (congr_arg coe)
#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

end Nnreal

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

theorem Real.coe_toNnreal (r : ℝ) (hr : 0 ≤ r) : (Real.toNnreal r : ℝ) = r :=
  max_eq_left hr
#align real.coe_to_nnreal Real.coe_toNnreal

theorem 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 Real.le_coe_toNnreal (r : ℝ) : r ≤ Real.toNnreal r :=
  le_max_left r 0
#align real.le_coe_to_nnreal Real.le_coe_toNnreal

namespace Nnreal
open Coe

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

@[norm_cast]
theorem coe_mk (a : ℝ) (ha) : ((⟨a, ha⟩ : ℝ≥0) : ℝ) = a :=
  rfl
#align nnreal.coe_mk Nnreal.coe_mk

noncomputable 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 : Function.Injective (Coe.coe : ℝ≥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

protected theorem coe_zero : ((0 : ℝ≥0) : ℝ) = 0 :=
  rfl
#align nnreal.coe_zero Nnreal.coe_zero

protected theorem coe_one : ((1 : ℝ≥0) : ℝ) = 1 :=
  rfl
#align nnreal.coe_one Nnreal.coe_one

protected theorem coe_add (r₁ r₂ : ℝ≥0) : ((r₁ + r₂ : ℝ≥0) : ℝ) = r₁ + r₂ :=
  rfl
#align nnreal.coe_add Nnreal.coe_add

protected theorem coe_mul (r₁ r₂ : ℝ≥0) : ((r₁ * r₂ : ℝ≥0) : ℝ) = r₁ * r₂ :=
  rfl
#align nnreal.coe_mul Nnreal.coe_mul

protected theorem coe_inv (r : ℝ≥0) : ((r⁻¹ : ℝ≥0) : ℝ) = r⁻¹ :=
  rfl
#align nnreal.coe_inv Nnreal.coe_inv

protected theorem coe_div (r₁ r₂ : ℝ≥0) : ((r₁ / r₂ : ℝ≥0) : ℝ) = r₁ / r₂ :=
  rfl
#align nnreal.coe_div Nnreal.coe_div

@[simp, norm_cast]
protected theorem coe_bit0 (r : ℝ≥0) : ((bit0 r : ℝ≥0) : ℝ) = bit0 r.val :=
  rfl
#align nnreal.coe_bit0 Nnreal.coe_bit0

@[simp, norm_cast]
protected theorem coe_bit1 (r : ℝ≥0) : ((bit1 r : ℝ≥0) : ℝ) = bit1 r.val :=
  rfl
#align nnreal.coe_bit1 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 <| sorry -- 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
  sorry
  -- 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

theorem coe_ne_zero {r : ℝ≥0} : (r : ℝ) ≠ 0 ↔ r ≠ 0 := sorry -- by norm_cast
#align nnreal.coe_ne_zero Nnreal.coe_ne_zero

example : CommSemiring ℝ≥0 := by infer_instance

/-- Coercion `ℝ≥0 → ℝ` as a `ring_hom`. -/
def toRealHom : ℝ≥0 →+* ℝ :=
  ⟨⟨⟨Coe.coe, Nnreal.coe_one⟩, Nnreal.coe_mul⟩, Nnreal.coe_zero, Nnreal.coe_add⟩
#align nnreal.to_real_hom Nnreal.toRealHom

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

section Actions

/-- A `mul_action` over `ℝ` restricts to a `mul_action` 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 `distrib_mul_action` over `ℝ` restricts to a `distrib_mul_action` 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

/-
/-- 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 :=
  toRealHom.map_pow r n
#align nnreal.coe_pow Nnreal.coe_pow

@[simp, norm_cast]
theorem coe_zpow (r : ℝ≥0) (n : ℤ) : ((r ^ n : ℝ≥0) : ℝ) = r ^ n := by cases n <;> simp
#align nnreal.coe_zpow Nnreal.coe_zpow

@[norm_cast]
theorem coe_list_sum (l : List ℝ≥0) : ((l.Sum : ℝ≥0) : ℝ) = (l.map coe).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 coe).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 coe).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 coe).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 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 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

theorem nsmul_coe (r : ℝ≥0) (n : ℕ) : ↑(n • r) = n • (r : ℝ) := by norm_cast
#align nnreal.nsmul_coe Nnreal.nsmul_coe

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

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 (coe : ℝ≥0 → ℝ) := fun _ _ => Nnreal.coe_le_coe.2
#align nnreal.coe_mono Nnreal.coe_mono

protected theorem Real.toNnreal_mono : Monotone Real.toNnreal := fun x y h =>
  max_le_max h (le_refl 0)
#align real.to_nnreal_mono Real.toNnreal_mono

@[simp]
theorem 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.to_nnreal` and `coe : ℝ≥0 → ℝ` form a Galois insertion. -/
noncomputable def gi : GaloisInsertion Real.toNnreal coe :=
  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`. -/
@[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' x y := Iff.rfl
#align nnreal.order_iso_Icc_zero_coe Nnreal.orderIsoIccZeroCoe

@[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 `has_mem.mem` have a sensible type
theorem coe_image {s : Set ℝ≥0} :
    coe '' 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 ((coe : ℝ≥0 → ℝ) '' s) ↔ BddAbove s :=
  Iff.intro
    (fun ⟨b, hb⟩ =>
      ⟨Real.toNnreal b, fun ⟨y, hy⟩ hys =>
        show y ≤ max b 0 from le_max_of_le_left <| hb <| Set.mem_image_of_mem _ hys⟩)
    fun ⟨b, hb⟩ => ⟨b, fun y ⟨x, hx, Eq⟩ => Eq ▸ hb hx⟩
#align nnreal.bdd_above_coe Nnreal.bddAbove_coe

theorem bddBelow_coe (s : Set ℝ≥0) : BddBelow ((coe : ℝ≥0 → ℝ) '' s) :=
  ⟨0, fun r ⟨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ₛ ((coe : ℝ≥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ₛ

@[norm_cast]
theorem coe_supᵢ {ι : Sort _} (s : ι → ℝ≥0) : (↑(⨆ i, s i) : ℝ) = ⨆ i, s i := by
  rw [supᵢ, supᵢ, coe_Sup, Set.range_comp]
#align nnreal.coe_supr Nnreal.coe_supᵢ

@[norm_cast]
theorem coe_infₛ (s : Set ℝ≥0) : (↑(infₛ s) : ℝ) = infₛ ((coe : ℝ≥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]
#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_infi, coe_infi]
  exact le_cinfi_add_cinfi h
#align nnreal.le_infi_add_infi Nnreal.le_infᵢ_add_infᵢ

example : Archimedean ℝ≥0 := by infer_instance

-- TODO: why are these three instances necessary? why aren't they inferred?
instance covariant_add : CovariantClass ℝ≥0 ℝ≥0 (· + ·) (· ≤ ·) :=
  OrderedAddCommMonoid.to_covariantClass_left ℝ≥0
#align nnreal.covariant_add Nnreal.covariant_add

instance contravariant_add : ContravariantClass ℝ≥0 ℝ≥0 (· + ·) (· < ·) :=
  OrderedCancelAddCommMonoid.to_contravariantClass_left ℝ≥0
#align nnreal.contravariant_add Nnreal.contravariant_add

instance covariant_mul : CovariantClass ℝ≥0 ℝ≥0 (· * ·) (· ≤ ·) :=
  OrderedCommMonoid.to_covariantClass_left ℝ≥0
#align nnreal.covariant_mul Nnreal.covariant_mul

-- Why isn't `nnreal.contravariant_add` inferred?
theorem le_of_forall_pos_le_add {a b : ℝ≥0} (h : ∀ ε, 0 < ε → a ≤ b + ε) : a ≤ b :=
  @le_of_forall_pos_le_add _ _ _ _ _ _ Nnreal.contravariant_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

namespace Real

section ToNnreal

@[simp]
theorem toNnreal_zero : Real.toNnreal 0 = 0 := by simp [Real.toNnreal] <;> rfl
#align real.to_nnreal_zero Real.toNnreal_zero

@[simp]
theorem toNnreal_one : Real.toNnreal 1 = 1 := by
  simp [Real.toNnreal, max_eq_left (zero_le_one : (0 : ℝ) ≤ 1)] <;> rfl
#align real.to_nnreal_one Real.toNnreal_one

@[simp]
theorem toNnreal_pos {r : ℝ} : 0 < Real.toNnreal r ↔ 0 < r := by
  simp [Real.toNnreal, nnreal.coe_lt_coe.symm, lt_irrefl]
#align real.to_nnreal_pos Real.toNnreal_pos

@[simp]
theorem toNnreal_eq_zero {r : ℝ} : Real.toNnreal r = 0 ↔ r ≤ 0 := by
  simpa [-to_nnreal_pos] using not_iff_not.2 (@to_nnreal_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 coe_to_nnreal' (r : ℝ) : (Real.toNnreal r : ℝ) = max r 0 :=
  rfl
#align real.coe_to_nnreal' Real.coe_to_nnreal'

@[simp]
theorem toNnreal_le_toNnreal_iff {r p : ℝ} (hp : 0 ≤ p) :
    Real.toNnreal r ≤ Real.toNnreal p ↔ r ≤ p := by simp [nnreal.coe_le_coe.symm, Real.toNnreal, 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) :
    Real.toNnreal r = Real.toNnreal p ↔ r = p := by simp [← Nnreal.coe_eq, coe_to_nnreal, 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 [Real.toNnreal, 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} : Real.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, to_nnreal_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 :=
  by
  cases le_total 0 p
  · rw [← Nnreal.coe_lt_coe, Real.coe_toNnreal p h]
  · rw [to_nnreal_eq_zero.2 h]
    constructor
    · intro
      have := not_lt_of_le (zero_le r)
      contradiction
    · intro rp
      have : ¬p ≤ 0 := not_le_of_lt (lt_of_le_of_lt (Nnreal.coe_nonneg _) rp)
      contradiction
#align real.lt_to_nnreal_iff_coe_lt Real.lt_toNnreal_iff_coe_lt

@[simp]
theorem toNnreal_bit0 (r : ℝ) : Real.toNnreal (bit0 r) = bit0 (Real.toNnreal r) :=
  by
  cases' le_total r 0 with hr hr
  · rw [to_nnreal_of_nonpos hr, to_nnreal_of_nonpos, bit0_zero]
    exact add_nonpos hr hr
  · exact to_nnreal_add hr hr
#align real.to_nnreal_bit0 Real.toNnreal_bit0

@[simp]
theorem toNnreal_bit1 {r : ℝ} (hr : 0 ≤ r) : Real.toNnreal (bit1 r) = bit1 (Real.toNnreal r) :=
  (Real.toNnreal_add (by simp [hr]) zero_le_one).trans (by simp [bit1])
#align real.to_nnreal_bit1 Real.toNnreal_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

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

theorem Real.toNnreal_mul {p q : ℝ} (hp : 0 ≤ p) :
    Real.toNnreal (p * q) = Real.toNnreal p * Real.toNnreal q :=
  by
  cases' le_total 0 q with hq hq
  · apply Nnreal.eq
    simp [Real.toNnreal, hp, hq, max_eq_left, mul_nonneg]
  · have hpq := mul_nonpos_of_nonneg_of_nonpos hp hq
    rw [to_nnreal_eq_zero.2 hq, to_nnreal_eq_zero.2 hpq, mul_zero]
#align real.to_nnreal_mul Real.toNnreal_mul

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

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

theorem exists_mem_ico_zpow {x : ℝ≥0} {y : ℝ≥0} (hx : x ≠ 0) (hy : 1 < y) :
    ∃ n : ℤ, x ∈ Set.Ico (y ^ n) (y ^ (n + 1)) :=
  by
  obtain ⟨n, hn, h'n⟩ : ∃ n : ℤ, (y : ℝ) ^ n ≤ x ∧ (x : ℝ) < y ^ (n + 1) :=
    exists_mem_Ico_zpow (bot_lt_iff_ne_bot.mpr hx) hy
  rw [← Nnreal.coe_zpow] at hn h'n
  exact ⟨n, hn, h'n⟩
#align nnreal.exists_mem_Ico_zpow Nnreal.exists_mem_ico_zpow

theorem exists_mem_ioc_zpow {x : ℝ≥0} {y : ℝ≥0} (hx : x ≠ 0) (hy : 1 < y) :
    ∃ n : ℤ, x ∈ Set.Ioc (y ^ n) (y ^ (n + 1)) :=
  by
  obtain ⟨n, hn, h'n⟩ : ∃ n : ℤ, (y : ℝ) ^ n < x ∧ (x : ℝ) ≤ y ^ (n + 1) :=
    exists_mem_Ioc_zpow (bot_lt_iff_ne_bot.mpr hx) hy
  rw [← Nnreal.coe_zpow] at hn h'n
  exact ⟨n, hn, h'n⟩
#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 `has_ordered_sub` in the file `algebra.order.sub`. 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_pos {r : ℝ≥0} : 0 < r⁻¹ ↔ 0 < r :=
  inv_pos
#align nnreal.inv_pos Nnreal.inv_pos

theorem div_pos {r p : ℝ≥0} (hr : 0 < r) (hp : 0 < p) : 0 < r / p :=
  div_pos hr hp
#align nnreal.div_pos Nnreal.div_pos

theorem div_self_le (r : ℝ≥0) : r / r ≤ 1 :=
  div_self_le_one r
#align nnreal.div_self_le Nnreal.div_self_le

@[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

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'

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

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 :=
  by
  refine' (lt_div_iff fun hr => False.elim _).1 h
  subst r
  simpa using h
#align nnreal.mul_lt_of_lt_div Nnreal.mul_lt_of_lt_div

theorem div_le_div_left_of_le {a b c : ℝ≥0} (b0 : 0 < b) (c0 : 0 < c) (cb : c ≤ b) :
    a / b ≤ a / c := by
  by_cases a0 : a = 0
  · rw [a0, zero_div, zero_div]
  · cases' a with a ha
    replace a0 : 0 < a := lt_of_le_of_ne ha (ne_of_lt (zero_lt_iff.mpr a0))
    exact (div_le_div_left a0 b0 c0).mpr cb
#align nnreal.div_le_div_left_of_le Nnreal.div_le_div_left_of_le

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 [(· ≠ ·), 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

theorem div_add_div_same (a b c : ℝ≥0) : a / c + b / c = (a + b) / c :=
  div_add_div_same _ _ _
#align nnreal.div_add_div_same Nnreal.div_add_div_same

theorem half_pos {a : ℝ≥0} (h : 0 < a) : 0 < a / 2 :=
  half_pos h
#align nnreal.half_pos Nnreal.half_pos

theorem add_halves (a : ℝ≥0) : a / 2 + a / 2 = a :=
  add_halves _
#align nnreal.add_halves Nnreal.add_halves

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

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 two_inv_lt_one : (2⁻¹ : ℝ≥0) < 1 :=
  two_inv_lt_one
#align nnreal.two_inv_lt_one Nnreal.two_inv_lt_one

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

@[field_simps]
theorem div_add_div (a : ℝ≥0) {b : ℝ≥0} (c : ℝ≥0) {d : ℝ≥0} (hb : b ≠ 0) (hd : d ≠ 0) :
    a / b + c / d = (a * d + b * c) / (b * d) :=
  div_add_div _ _ hb hd
#align nnreal.div_add_div Nnreal.div_add_div

@[field_simps]
theorem add_div' (a b c : ℝ≥0) (hc : c ≠ 0) : b + a / c = (b * c + a) / c :=
  add_div' _ _ _ hc
#align nnreal.add_div' Nnreal.add_div'

@[field_simps]
theorem div_add' (a b c : ℝ≥0) (hc : c ≠ 0) : a / c + b = (a + b * c) / c :=
  div_add' _ _ _ hc
#align nnreal.div_add' Nnreal.div_add'

theorem Real.toNnreal_inv {x : ℝ} : Real.toNnreal x⁻¹ = (Real.toNnreal x)⁻¹ :=
  by
  by_cases hx : 0 ≤ x
  · nth_rw 1 [← Real.coe_toNnreal x hx]
    rw [← Nnreal.coe_inv, Real.toNnreal_coe]
  · have hx' := le_of_not_ge hx
    rw [to_nnreal_eq_zero.mpr hx', inv_zero, to_nnreal_eq_zero.mpr (inv_nonpos.mpr hx')]
#align real.to_nnreal_inv Real.toNnreal_inv

theorem 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 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
  rwa [← one_div, div_lt_iff hx, one_mul]
#align nnreal.inv_lt_one_iff Nnreal.inv_lt_one_iff

theorem inv_lt_one {x : ℝ≥0} (hx : 1 < x) : x⁻¹ < 1 :=
  inv_lt_one hx
#align nnreal.inv_lt_one Nnreal.inv_lt_one

theorem zpow_pos {x : ℝ≥0} (hx : x ≠ 0) (n : ℤ) : 0 < x ^ n :=
  by
  cases n
  · simp [pow_pos hx.bot_lt _]
  · simp [pow_pos hx.bot_lt _]
#align nnreal.zpow_pos Nnreal.zpow_pos

theorem inv_lt_inv_iff {x y : ℝ≥0} (hx : x ≠ 0) (hy : y ≠ 0) : y⁻¹ < x⁻¹ ↔ x < y :=
  inv_lt_inv₀ hy hx
#align nnreal.inv_lt_inv_iff Nnreal.inv_lt_inv_iff

theorem inv_lt_inv {x y : ℝ≥0} (hx : x ≠ 0) (h : x < y) : y⁻¹ < x⁻¹ :=
  (inv_lt_inv_iff hx (bot_le.trans_lt h).ne').2 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_to_nnreal_iff_coe_le <| x.2.trans h).2 h
#align nnreal.le_to_nnreal_of_coe_le Nnreal.le_toNnreal_of_coe_le

theorem supₛ_of_not_bddAbove {s : Set ℝ≥0} (hs : ¬BddAbove s) : SupSet.supₛ s = 0 :=
  by
  rw [← bdd_above_coe] at hs
  rw [← Nnreal.coe_eq, coe_Sup]
  exact Sup_of_not_bdd_above 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 infᵢ_empty [IsEmpty ι] (f : ι → ℝ≥0) : (⨅ i, f i) = 0 :=
  by
  rw [← Nnreal.coe_eq, coe_infi]
  exact Real.cinfᵢ_empty _
#align nnreal.infi_empty Nnreal.infᵢ_empty

@[simp]
theorem infᵢ_const_zero {α : Sort _} : (⨅ i : α, (0 : ℝ≥0)) = 0 :=
  by
  rw [← Nnreal.coe_eq, coe_infi]
  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_infi, coe_infi]
  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 infi_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_supr]
  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, supr_mul]
#align nnreal.supr_div Nnreal.supᵢ_div

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_infi]
  exact le_cinfᵢ H
#align nnreal.le_mul_infi Nnreal.le_mul_infᵢ

theorem mul_supᵢ_le {a : ℝ≥0} {g : ℝ≥0} {h : ι → ℝ≥0} (H : ∀ j, g * h j ≤ a) : g * supᵢ h ≤ a :=
  by
  rw [mul_supr]
  exact csupᵢ_le H
#align nnreal.mul_supr_le Nnreal.mul_supᵢ_le

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

theorem supᵢ_mul_le {a : ℝ≥0} {g : ι → ℝ≥0} {h : ℝ≥0} (H : ∀ i, g i * h ≤ a) : supᵢ g * h ≤ a :=
  by
  rw [supr_mul]
  exact csupᵢ_le H
#align nnreal.supr_mul_le Nnreal.supᵢ_mul_le

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

theorem supᵢ_mul_supᵢ_le {a : ℝ≥0} {g h : ι → ℝ≥0} (H : ∀ i j, g i * h j ≤ a) :
    supᵢ g * supᵢ h ≤ a :=
  supr_mul_le fun i => mul_supr_le <| H _
#align nnreal.supr_mul_supr_le Nnreal.supᵢ_mul_supᵢ_le

end Csupr

end Nnreal

namespace Set

namespace OrdConnected

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

theorem preimage_coe_nnreal_real (h : s.OrdConnected) : (coe ⁻¹' 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) : (coe '' 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

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, (to_nnreal_of_nonpos hy₀).trans hz.2.symm⟩
  · lift y to ℝ≥0 using hy₀
    rw [to_nnreal_coe] at hz
    exact ⟨z, h.out hx hy ⟨to_nnreal_le_iff_le_coe.1 hz.1, hz.2⟩, to_nnreal_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`. -/
@[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
  simp [coe_to_nnreal x h, 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]
#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

-/

end Nnreal


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Abhimanyu Pallavi Sudhir, Jean Lo, Calle Sönne, Benjamin Davidson
-/
import analysis.special_functions.trigonometric.arctan
import analysis.special_functions.trigonometric.complex_deriv

/-!
# Derivatives of the `tan` and `arctan` functions.

Continuity and derivatives of the tangent and arctangent functions.
-/

noncomputable theory

namespace real

open set filter
open_locale topology real

lemma has_strict_deriv_at_tan {x : ℝ} (h : cos x ≠ 0) :
  has_strict_deriv_at tan (1 / (cos x)^2) x :=
by exact_mod_cast (complex.has_strict_deriv_at_tan (by exact_mod_cast h)).real_of_complex

lemma has_deriv_at_tan {x : ℝ} (h : cos x ≠ 0) :
  has_deriv_at tan (1 / (cos x)^2) x :=
by exact_mod_cast (complex.has_deriv_at_tan (by exact_mod_cast h)).real_of_complex

lemma tendsto_abs_tan_of_cos_eq_zero {x : ℝ} (hx : cos x = 0) :
  tendsto (λ x, abs (tan x)) (𝓝[≠] x) at_top :=
begin
  have hx : complex.cos x = 0, by exact_mod_cast hx,
  simp only [← complex.abs_of_real, complex.of_real_tan],
  refine (complex.tendsto_abs_tan_of_cos_eq_zero hx).comp _,
  refine tendsto.inf complex.continuous_of_real.continuous_at _,
  exact tendsto_principal_principal.2 (λ y, mt complex.of_real_inj.1)
end

lemma tendsto_abs_tan_at_top (k : ℤ) :
  tendsto (λ x, abs (tan x)) (𝓝[≠] ((2 * k + 1) * π / 2)) at_top :=
tendsto_abs_tan_of_cos_eq_zero $ cos_eq_zero_iff.2 ⟨k, rfl⟩

lemma differentiable_at_tan {x : ℝ} : differentiable_at ℝ tan x ↔ cos x ≠ 0 :=
⟨λ h, continuous_at_tan.1 h.continuous_at, λ h, (has_deriv_at_tan h).differentiable_at⟩

@[simp] lemma deriv_tan (x : ℝ) : deriv tan x = 1 / (cos x)^2 :=
if h : cos x = 0 then
  have ¬differentiable_at ℝ tan x := mt differentiable_at_tan.1 (not_not.2 h),
  by simp [deriv_zero_of_not_differentiable_at this, h, sq]
else (has_deriv_at_tan h).deriv

@[simp] lemma cont_diff_at_tan {n x} : cont_diff_at ℝ n tan x ↔ cos x ≠ 0 :=
⟨λ h, continuous_at_tan.1 h.continuous_at,
  λ h, (complex.cont_diff_at_tan.2 $ by exact_mod_cast h).real_of_complex⟩

lemma has_deriv_at_tan_of_mem_Ioo {x : ℝ} (h : x ∈ Ioo (-(π/2):ℝ) (π/2)) :
  has_deriv_at tan (1 / (cos x)^2) x :=
has_deriv_at_tan (cos_pos_of_mem_Ioo h).ne'

lemma differentiable_at_tan_of_mem_Ioo {x : ℝ} (h : x ∈ Ioo (-(π/2):ℝ) (π/2)) :
  differentiable_at ℝ tan x :=
(has_deriv_at_tan_of_mem_Ioo h).differentiable_at

lemma has_strict_deriv_at_arctan (x : ℝ) : has_strict_deriv_at arctan (1 / (1 + x^2)) x :=
have A : cos (arctan x) ≠ 0 := (cos_arctan_pos x).ne',
by simpa [cos_sq_arctan]
  using tan_local_homeomorph.has_strict_deriv_at_symm trivial (by simpa) (has_strict_deriv_at_tan A)

lemma has_deriv_at_arctan (x : ℝ) : has_deriv_at arctan (1 / (1 + x^2)) x :=
(has_strict_deriv_at_arctan x).has_deriv_at

lemma differentiable_at_arctan (x : ℝ) : differentiable_at ℝ arctan x :=
(has_deriv_at_arctan x).differentiable_at

lemma differentiable_arctan : differentiable ℝ arctan := differentiable_at_arctan

@[simp] lemma deriv_arctan : deriv arctan = (λ x, 1 / (1 + x^2)) :=
funext $ λ x, (has_deriv_at_arctan x).deriv

lemma cont_diff_arctan {n : ℕ∞} : cont_diff ℝ n arctan :=
cont_diff_iff_cont_diff_at.2 $ λ x,
have cos (arctan x) ≠ 0 := (cos_arctan_pos x).ne',
tan_local_homeomorph.cont_diff_at_symm_deriv (by simpa) trivial (has_deriv_at_tan this)
  (cont_diff_at_tan.2 this)

end real

section
/-!
### Lemmas for derivatives of the composition of `real.arctan` with a differentiable function

In this section we register lemmas for the derivatives of the composition of `real.arctan` with a
differentiable function, for standalone use and use with `simp`. -/

open real

section deriv

variables {f : ℝ → ℝ} {f' x : ℝ} {s : set ℝ}

lemma has_strict_deriv_at.arctan (hf : has_strict_deriv_at f f' x) :
  has_strict_deriv_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) * f') x :=
(real.has_strict_deriv_at_arctan (f x)).comp x hf

lemma has_deriv_at.arctan (hf : has_deriv_at f f' x) :
  has_deriv_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) * f') x :=
(real.has_deriv_at_arctan (f x)).comp x hf

lemma has_deriv_within_at.arctan (hf : has_deriv_within_at f f' s x) :
  has_deriv_within_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) * f') s x :=
(real.has_deriv_at_arctan (f x)).comp_has_deriv_within_at x hf

lemma deriv_within_arctan (hf : differentiable_within_at ℝ f s x)
  (hxs : unique_diff_within_at ℝ s x) :
  deriv_within (λ x, arctan (f x)) s x = (1 / (1 + (f x)^2)) * (deriv_within f s x) :=
hf.has_deriv_within_at.arctan.deriv_within hxs

@[simp] lemma deriv_arctan (hc : differentiable_at ℝ f x) :
  deriv (λ x, arctan (f x)) x = (1 / (1 + (f x)^2)) * (deriv f x) :=
hc.has_deriv_at.arctan.deriv

end deriv

section fderiv

variables {E : Type*} [normed_add_comm_group E] [normed_space ℝ E] {f : E → ℝ} {f' : E →L[ℝ] ℝ}
  {x : E} {s : set E} {n : ℕ∞}

lemma has_strict_fderiv_at.arctan (hf : has_strict_fderiv_at f f' x) :
  has_strict_fderiv_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) • f') x :=
(has_strict_deriv_at_arctan (f x)).comp_has_strict_fderiv_at x hf

lemma has_fderiv_at.arctan (hf : has_fderiv_at f f' x) :
  has_fderiv_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) • f') x :=
(has_deriv_at_arctan (f x)).comp_has_fderiv_at x hf

lemma has_fderiv_within_at.arctan (hf : has_fderiv_within_at f f' s x) :
  has_fderiv_within_at (λ x, arctan (f x)) ((1 / (1 + (f x)^2)) • f') s x :=
(has_deriv_at_arctan (f x)).comp_has_fderiv_within_at x hf

lemma fderiv_within_arctan (hf : differentiable_within_at ℝ f s x)
  (hxs : unique_diff_within_at ℝ s x) :
  fderiv_within ℝ (λ x, arctan (f x)) s x = (1 / (1 + (f x)^2)) • (fderiv_within ℝ f s x) :=
hf.has_fderiv_within_at.arctan.fderiv_within hxs

@[simp] lemma fderiv_arctan (hc : differentiable_at ℝ f x) :
  fderiv ℝ (λ x, arctan (f x)) x = (1 / (1 + (f x)^2)) • (fderiv ℝ f x) :=
hc.has_fderiv_at.arctan.fderiv

lemma differentiable_within_at.arctan (hf : differentiable_within_at ℝ f s x) :
  differentiable_within_at ℝ (λ x, real.arctan (f x)) s x :=
hf.has_fderiv_within_at.arctan.differentiable_within_at

@[simp] lemma differentiable_at.arctan (hc : differentiable_at ℝ f x) :
  differentiable_at ℝ (λ x, arctan (f x)) x :=
hc.has_fderiv_at.arctan.differentiable_at

lemma differentiable_on.arctan (hc : differentiable_on ℝ f s) :
  differentiable_on ℝ (λ x, arctan (f x)) s :=
λ x h, (hc x h).arctan

@[simp] lemma differentiable.arctan (hc : differentiable ℝ f) :
  differentiable ℝ (λ x, arctan (f x)) :=
λ x, (hc x).arctan

lemma cont_diff_at.arctan (h : cont_diff_at ℝ n f x) :
  cont_diff_at ℝ n (λ x, arctan (f x)) x :=
cont_diff_arctan.cont_diff_at.comp x h

lemma cont_diff.arctan (h : cont_diff ℝ n f) :
  cont_diff ℝ n (λ x, arctan (f x)) :=
cont_diff_arctan.comp h

lemma cont_diff_within_at.arctan (h : cont_diff_within_at ℝ n f s x) :
  cont_diff_within_at ℝ n (λ x, arctan (f x)) s x :=
cont_diff_arctan.comp_cont_diff_within_at h

lemma cont_diff_on.arctan (h : cont_diff_on ℝ n f s) :
  cont_diff_on ℝ n (λ x, arctan (f x)) s :=
cont_diff_arctan.comp_cont_diff_on h

end fderiv
end


import ALC

open Concept Role ALCStatement

--KB statement 1: Tom is a tall student
def ex1KB1 : ALCStatement :=
  (ConceptAssertion (conjunction (atomicConcept "Tall") (atomicConcept "Student")) "tom")

--KB statement 2: Tim is friends with Tom
def ex1KB2 : ALCStatement :=
  (RoleAssertion (atomicRole "Friend") ("tim", "tom"))

--Define the KB as the set of both of the above statements
def ex1KB: set ALCStatement := {ex1KB1, ex1KB2}

def ex1Statement: ALCStatement :=
(ConceptAssertion (existentialRoleQuant (atomicRole "Friend") (atomicConcept "Tall")) "tim")

--Example 1 Theorem: It is entailed that Tim has a friend who is tall
example : entails ex1KB ex1Statement := begin
  --Expand out and get all of our hypothesis
  rewrite entails,
  intros Δi, intros Iac, intros Iar, intros Io,
  rewrite ex1KB,
  intros IModelsKB,

  -- Simplify down our goal into its base set theoretic interpretation
  rewrite ex1Statement,
  rewrite eval,
  rewrite Ic,
  rewrite Ic,
  rewrite Ir,
  simp,

  --Its obvious to us that tom will take the place here
  split,
  {
    apply individualsInDomain,
  },
  {
    existsi (Io "tom"),
    split,{
      apply individualsInDomain,
    },{
      split,
      {
        --Prove that Tom and Tim are friends
        have IModelsExKB2 := IModelsKB,
        specialize IModelsExKB2 ex1KB2,
        simp at IModelsExKB2,
        rewrite ex1KB2 at IModelsExKB2,
        rewrite eval at IModelsExKB2,
        rewrite Ir at IModelsExKB2,
        exact IModelsExKB2,
      },
      {
        --Prove that Tom is Tall
        have IModelsExKB1 := IModelsKB,
        specialize IModelsExKB1 ex1KB1,
        simp at IModelsExKB1,
        rewrite ex1KB1 at IModelsExKB1,
        rewrite eval at IModelsExKB1,
        rewrite Ic at IModelsExKB1,
        rewrite Ic at IModelsExKB1,
        rewrite Ic at IModelsExKB1,
        simp at IModelsExKB1,
        cases IModelsExKB1,
        exact IModelsExKB1_left,
      }
    }
  }
end


import M4R.Algebra.Group.Group

namespace M4R
  open Monoid CommMonoid Group AbelianGroup

  namespace SubMonoid

    def Self (α : Type _) [Monoid α] : SubMonoid α where
      subset   := Set.Universal
      has_zero := trivial
      add_closed := by intros; trivial

    def Trivial (α : Type _) [Monoid α] : SubMonoid α where
      subset     := {x | x = 0}
      has_zero   := by trivial
      add_closed := fun _ a0 _ b0 => by rw [a0, b0, add_zero]; trivial

    protected theorem ext [Monoid α] (s₁ s₂ : SubMonoid α) : s₁.subset = s₂.subset ↔ s₁ = s₂ :=
      ⟨match s₁, s₂ with | ⟨_, _, _⟩, ⟨_, _, _⟩ => by rw [SubMonoid.mk.injEq]; exact id,
      fun h => by rw [h]⟩

    protected instance toMonoid [Monoid α] (s : SubMonoid α) : Monoid ↑s.subset where
      zero := ⟨0, s.has_zero⟩
      add := fun ⟨x, hx⟩ ⟨y, hy⟩ => ⟨x + y, s.add_closed x hx y hy⟩
      add_zero := fun ⟨a, _⟩ => Set.elementExt (add_zero a)
      zero_add := fun ⟨a, _⟩ => Set.elementExt (zero_add a)
      add_assoc := fun ⟨a, _⟩ ⟨b, _⟩ ⟨c, _⟩ => Set.elementExt (add_assoc a b c)

    protected instance toCommMonoid [CommMonoid α] (s : SubMonoid α) : CommMonoid ↑s.subset where
      add_comm := fun ⟨a, _⟩ ⟨b, _⟩ => Set.elementExt (add_comm a b)

  end SubMonoid

  namespace SubGroup

    def Self (α : Type _) [Group α] : SubGroup α where
      toSubMonoid := SubMonoid.Self α
      neg_closed := by intros; trivial

    def Trivial (α : Type _) [Group α] : SubGroup α where
      toSubMonoid := SubMonoid.Trivial α
      neg_closed := fun a a0 => by rw[a0, neg_zero]; trivial

    protected theorem ext [Group α] (s₁ s₂ : SubGroup α) : s₁.subset = s₂.subset ↔ s₁ = s₂ :=
      ⟨match s₁, s₂ with | ⟨_, _⟩, ⟨_, _⟩ => by rw [SubGroup.mk.injEq]; exact (SubMonoid.ext _ _).mp,
      fun h => by rw [h]⟩

    theorem trivialExt [Group α] (s : SubGroup α) : s = Trivial α ↔ (∀ x ∈ s, x = 0) := by
      rw [←(SubGroup.ext s (Trivial α))]; exact ⟨fun hs => by
        simp only [Mem.mem]; rw [hs]; exact (fun _ => id),
        fun hx => by rw [←Set.ext]; exact fun x => ⟨hx x,
          fun x0 => by rw [x0]; exact s.has_zero⟩⟩

    protected instance toGroup [Group α] (s : SubGroup α) : Group ↑s.subset where
      neg := fun ⟨x, hx⟩ => ⟨-x, s.neg_closed x hx⟩
      add_neg := fun ⟨a, _⟩ => Set.elementExt (add_neg a)

    protected instance toAbelianGroup [AbelianGroup α] (s : SubGroup α) : AbelianGroup ↑s.subset where
      add_comm := s.toCommMonoid.add_comm

    def is_normal [Group α] (s : SubGroup α) : Prop := ∀ a ∈ s, ∀ g, -g + a + g ∈ s

    theorem abelian_is_normal [AbelianGroup α] (s : SubGroup α) : s.is_normal :=
      fun a ha g => CommMonoid.add_comm _ _ ▸ add_assoc _ _ _ ▸ add_neg _ ▸ (zero_add _).symm ▸ ha

  end SubGroup

  namespace QuotientGroup
    variable [Group α] (s : SubGroup α)

    -- i.e. left coset equivalence
    def QuotientRelation (a b : α) : Prop := -a + b ∈ s
    theorem QuotientRelation.refl [Group α] (s : SubGroup α) (a : α) : QuotientRelation s a a := by
      simp only [QuotientRelation]; rw [neg_add]; exact s.has_zero

    def QuotClass : Type _ := Quot (QuotientRelation s)

    def QuotAdd (N : s.is_normal) : QuotClass s → QuotClass s → QuotClass s :=
      Function.Quotient.map₂ (QuotientRelation s) (QuotientRelation s) (QuotientRelation s)
        (QuotientRelation.refl s) (QuotientRelation.refl s) (fun x y => x + y) fun a₁ a₂ b₁ b₂ ha hb => by
          simp only [QuotientRelation]; rw [neg_add_distrib, ←add_zero (-b₁), ←add_neg b₂, ←add_assoc (-b₁),
          add_assoc, add_assoc]; apply s.add_closed _ hb; rw [←add_assoc, ←add_assoc, add_assoc (-b₂)]
          exact N (-a₁ + a₂) ha b₂

    def QuotNeg (N : s.is_normal) : QuotClass s → QuotClass s :=
      Function.Quotient.map (QuotientRelation s) (QuotientRelation s) (fun x => -x)
        fun x y hxy => by
          simp only [QuotientRelation]; rw [←neg_add_distrib]; apply s.neg_closed
          rw[←zero_add y, ←neg_add (-x), add_assoc (- -x)]; exact N (-x + y) hxy (-x)

    protected instance toGroup (α : Type _) (N : s.is_normal) : Group (QuotClass s) := Group.construct
    {
      zero := Quot.mk (QuotientRelation s) 0
      add := QuotAdd s N
      neg := QuotNeg s N
      add_zero := by
        apply Quot.ind; intro a; apply Quot.sound; simp only [QuotientRelation]
        rw [neg_add_distrib, neg_zero, zero_add, neg_add]; exact s.has_zero
      add_assoc := by
        apply Function.Quotient.ind₃; intro a b c; apply Quot.sound; simp only [QuotientRelation]
        rw [neg_add_distrib, neg_add_distrib, ←add_assoc, add_assoc (-c), add_assoc (-b), neg_add,
        add_zero, ←add_assoc, add_assoc (-c), neg_add, add_zero, neg_add]; exact s.has_zero
      add_neg := by
        apply Quot.ind; intro a; apply Quot.sound; simp only [QuotientRelation]
        rw [add_neg, neg_add]; exact s.has_zero
    }
    def LeftCoset (a : α) (s : Set α) : Set α := {x | -a + x ∈ s}
    def RightCoset (s : Set α) (a : α) : Set α := {x | x + -a ∈ s}

    namespace LeftCoset
      instance LeftCosetHAddInt : HAdd α (Set α) (Set α) where hAdd := LeftCoset
      theorem comp (a b : α) (s : Set α) : a + (b + s) = (a + b) + s := by
        simp only [HAdd.hAdd, LeftCoset]
        apply funext; intro x; apply propext; apply Iff.intro
        { intro h; have : Add.add (-Add.add a b) x = -(a + b) + x := rfl
        rw [this, neg_add_distrib, add_assoc]; exact h }
        intro h; simp only [Mem.mem, Set.mem]; have : Add.add (-b) (Add.add (-a) x) = -b + (-a + x) := rfl
        rw [this, ←add_assoc, ←neg_add_distrib]; exact h
    end LeftCoset

    namespace RightCoset
      instance RightCosetHAddInt : HAdd (Set α) α (Set α) where hAdd := RightCoset
      theorem comp (s : Set α) (a b : α) : (s + a) + b = s + (a + b) := by
        simp only [HAdd.hAdd, RightCoset]
        apply funext; intro x; apply propext; apply Iff.intro
        { intro h; have : Add.add x (-Add.add a b) = x + -(a + b) := rfl
        rw [this, neg_add_distrib, ←add_assoc]; exact h }
        intro h; simp only [Mem.mem, Set.mem]; have : Add.add (Add.add x (-b)) (-a) = (x + -b) + -a := rfl
        rw [this, add_assoc, ←neg_add_distrib]; exact h
    end RightCoset
  end QuotientGroup
end M4R