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

! This file was ported from Lean 3 source module data.nat.interval
! leanprover-community/mathlib commit e3d9ab8faa9dea8f78155c6c27d62a621f4c152d
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Finset.LocallyFinite

/-!
# Finite intervals of naturals

This file proves that `ℕ` is a `LocallyFiniteOrder` and calculates the cardinality of its
intervals as finsets and fintypes.

## TODO

Some lemmas can be generalized using `OrderedGroup`, `CanonicallyOrderedMonoid` or `SuccOrder`
and subsequently be moved upstream to `Data.Finset.LocallyFinite`.
-/

open Finset Nat

instance : LocallyFiniteOrder ℕ where
  finsetIcc a b := ⟨List.range' a (b + 1 - a), List.nodup_range' _ _⟩
  finsetIco a b := ⟨List.range' a (b - a), List.nodup_range' _ _⟩
  finsetIoc a b := ⟨List.range' (a + 1) (b - a), List.nodup_range' _ _⟩
  finsetIoo a b := ⟨List.range' (a + 1) (b - a - 1), List.nodup_range' _ _⟩
  finset_mem_Icc a b x := by
    rw [Finset.mem_mk, Multiset.mem_coe, List.mem_range']
    cases le_or_lt a b with
    | inl h => rw [add_tsub_cancel_of_le (Nat.lt_succ_of_le h).le, Nat.lt_succ_iff]
    | inr h =>
      rw [tsub_eq_zero_iff_le.2 (succ_le_of_lt h), add_zero]
      exact iff_of_false (fun hx => hx.2.not_le hx.1) fun hx => h.not_le (hx.1.trans hx.2)
  finset_mem_Ico a b x := by
    rw [Finset.mem_mk, Multiset.mem_coe, List.mem_range']
    cases le_or_lt a b with
    | inl h => rw [add_tsub_cancel_of_le h]
    | inr h =>
      rw [tsub_eq_zero_iff_le.2 h.le, add_zero]
      exact iff_of_false (fun hx => hx.2.not_le hx.1) fun hx => h.not_le (hx.1.trans hx.2.le)
  finset_mem_Ioc a b x := by
    rw [Finset.mem_mk, Multiset.mem_coe, List.mem_range']
    cases le_or_lt a b with
    | inl h =>
      rw [← succ_sub_succ, add_tsub_cancel_of_le (succ_le_succ h), Nat.lt_succ_iff, Nat.succ_le_iff]
    | inr h =>
      rw [tsub_eq_zero_iff_le.2 h.le, add_zero]
      exact iff_of_false (fun hx => hx.2.not_le hx.1) fun hx => h.not_le (hx.1.le.trans hx.2)
  finset_mem_Ioo a b x := by
    rw [Finset.mem_mk, Multiset.mem_coe, List.mem_range', ← tsub_add_eq_tsub_tsub]
    cases le_or_lt (a + 1) b with
    | inl h => rw [add_tsub_cancel_of_le h, Nat.succ_le_iff]
    | inr h =>
      rw [tsub_eq_zero_iff_le.2 h.le, add_zero]
      exact iff_of_false (fun hx => hx.2.not_le hx.1) fun hx => h.not_le (hx.1.trans hx.2)

variable (a b c : ℕ)

namespace Nat

theorem Ico_eq_range' : Ico a b = ⟨List.range' a (b - a), List.nodup_range' _ _⟩ :=
  rfl
#align nat.Ico_eq_range' Nat.Ico_eq_range'

theorem Ioc_eq_range' : Ioc a b = ⟨List.range' (a + 1) (b - a), List.nodup_range' _ _⟩ :=
  rfl
#align nat.Ioc_eq_range' Nat.Ioc_eq_range'

theorem Ioo_eq_range' : Ioo a b = ⟨List.range' (a + 1) (b - a - 1), List.nodup_range' _ _⟩ :=
  rfl
#align nat.Ioo_eq_range' Nat.Ioo_eq_range'

theorem Iio_eq_range : Iio = range := by
  ext (b x)
  rw [mem_Iio, mem_range]
#align nat.Iio_eq_range Nat.Iio_eq_range

@[simp]
theorem Ico_zero_eq_range : Ico 0 = range := by rw [← bot_eq_zero, ← Iio_eq_Ico, Iio_eq_range]
#align nat.Ico_zero_eq_range Nat.Ico_zero_eq_range

theorem _root_.Finset.range_eq_Ico : range = Ico 0 :=
  Ico_zero_eq_range.symm
#align finset.range_eq_Ico Finset.range_eq_Ico

@[simp]
theorem card_Icc : (Icc a b).card = b + 1 - a :=
  List.length_range' _ _
#align nat.card_Icc Nat.card_Icc

@[simp]
theorem card_Ico : (Ico a b).card = b - a :=
  List.length_range' _ _
#align nat.card_Ico Nat.card_Ico

@[simp]
theorem card_Ioc : (Ioc a b).card = b - a :=
  List.length_range' _ _
#align nat.card_Ioc Nat.card_Ioc

@[simp]
theorem card_Ioo : (Ioo a b).card = b - a - 1 :=
  List.length_range' _ _
#align nat.card_Ioo Nat.card_Ioo

@[simp]
theorem card_Iic : (Iic b).card = b + 1 := by rw [Iic_eq_Icc, card_Icc, bot_eq_zero, tsub_zero]
#align nat.card_Iic Nat.card_Iic

@[simp]
theorem card_Iio : (Iio b).card = b := by rw [Iio_eq_Ico, card_Ico, bot_eq_zero, tsub_zero]
#align nat.card_Iio Nat.card_Iio

-- Porting note: simp can prove this
-- @[simp]
theorem card_fintypeIcc : Fintype.card (Set.Icc a b) = b + 1 - a := by
  rw [Fintype.card_ofFinset, card_Icc]
#align nat.card_fintype_Icc Nat.card_fintypeIcc

-- Porting note: simp can prove this
-- @[simp]
theorem card_fintypeIco : Fintype.card (Set.Ico a b) = b - a := by
  rw [Fintype.card_ofFinset, card_Ico]
#align nat.card_fintype_Ico Nat.card_fintypeIco

-- Porting note: simp can prove this
-- @[simp]
theorem card_fintypeIoc : Fintype.card (Set.Ioc a b) = b - a := by
  rw [Fintype.card_ofFinset, card_Ioc]
#align nat.card_fintype_Ioc Nat.card_fintypeIoc

-- Porting note: simp can prove this
-- @[simp]
theorem card_fintypeIoo : Fintype.card (Set.Ioo a b) = b - a - 1 := by
  rw [Fintype.card_ofFinset, card_Ioo]
#align nat.card_fintype_Ioo Nat.card_fintypeIoo

-- Porting note: simp can prove this
-- @[simp]
theorem card_fintypeIic : Fintype.card (Set.Iic b) = b + 1 := by
  rw [Fintype.card_ofFinset, card_Iic]
#align nat.card_fintype_Iic Nat.card_fintypeIic

-- Porting note: simp can prove this
-- @[simp]
theorem card_fintypeIio : Fintype.card (Set.Iio b) = b := by rw [Fintype.card_ofFinset, card_Iio]
#align nat.card_fintype_Iio Nat.card_fintypeIio

-- TODO@Yaël: Generalize all the following lemmas to `succ_order`
theorem Icc_succ_left : Icc a.succ b = Ioc a b :=
  by
  ext x
  rw [mem_Icc, mem_Ioc, succ_le_iff]
#align nat.Icc_succ_left Nat.Icc_succ_left

theorem Ico_succ_right : Ico a b.succ = Icc a b :=
  by
  ext x
  rw [mem_Ico, mem_Icc, lt_succ_iff]
#align nat.Ico_succ_right Nat.Ico_succ_right

theorem Ico_succ_left : Ico a.succ b = Ioo a b :=
  by
  ext x
  rw [mem_Ico, mem_Ioo, succ_le_iff]
#align nat.Ico_succ_left Nat.Ico_succ_left

theorem Icc_pred_right {b : ℕ} (h : 0 < b) : Icc a (b - 1) = Ico a b :=
  by
  ext x
  rw [mem_Icc, mem_Ico, lt_iff_le_pred h]
#align nat.Icc_pred_right Nat.Icc_pred_right

theorem Ico_succ_succ : Ico a.succ b.succ = Ioc a b :=
  by
  ext x
  rw [mem_Ico, mem_Ioc, succ_le_iff, lt_succ_iff]
#align nat.Ico_succ_succ Nat.Ico_succ_succ

@[simp]
theorem Ico_succ_singleton : Ico a (a + 1) = {a} := by rw [Ico_succ_right, Icc_self]
#align nat.Ico_succ_singleton Nat.Ico_succ_singleton

@[simp]
theorem Ico_pred_singleton {a : ℕ} (h : 0 < a) : Ico (a - 1) a = {a - 1} := by
  rw [← Icc_pred_right _ h, Icc_self]
#align nat.Ico_pred_singleton Nat.Ico_pred_singleton

@[simp]
theorem Ioc_succ_singleton : Ioc b (b + 1) = {b + 1} := by rw [← Nat.Icc_succ_left, Icc_self]
#align nat.Ioc_succ_singleton Nat.Ioc_succ_singleton

variable {a b c}

theorem Ico_succ_right_eq_insert_Ico (h : a ≤ b) : Ico a (b + 1) = insert b (Ico a b) := by
  rw [Ico_succ_right, ← Ico_insert_right h]
#align nat.Ico_succ_right_eq_insert_Ico Nat.Ico_succ_right_eq_insert_Ico

theorem Ico_insert_succ_left (h : a < b) : insert a (Ico a.succ b) = Ico a b := by
  rw [Ico_succ_left, ← Ioo_insert_left h]
#align nat.Ico_insert_succ_left Nat.Ico_insert_succ_left

theorem image_sub_const_Ico (h : c ≤ a) : ((Ico a b).image fun x => x - c) = Ico (a - c) (b - c) :=
  by
  ext x
  rw [mem_image]
  constructor
  · rintro ⟨x, hx, rfl⟩
    rw [mem_Ico] at hx⊢
    exact ⟨tsub_le_tsub_right hx.1 _, tsub_lt_tsub_right_of_le (h.trans hx.1) hx.2⟩
  · rintro h
    refine' ⟨x + c, _, add_tsub_cancel_right _ _⟩
    rw [mem_Ico] at h⊢
    exact ⟨tsub_le_iff_right.1 h.1, lt_tsub_iff_right.1 h.2⟩
#align nat.image_sub_const_Ico Nat.image_sub_const_Ico

theorem Ico_image_const_sub_eq_Ico (hac : a ≤ c) :
    ((Ico a b).image fun x => c - x) = Ico (c + 1 - b) (c + 1 - a) :=
  by
  ext x
  rw [mem_image, mem_Ico]
  constructor
  · rintro ⟨x, hx, rfl⟩
    rw [mem_Ico] at hx
    refine'
      ⟨_,
        ((tsub_le_tsub_iff_left hac).2 hx.1).trans_lt
          ((tsub_lt_tsub_iff_right hac).2 (Nat.lt_succ_self _))⟩
    cases lt_or_le c b with
    | inl h =>
      rw [tsub_eq_zero_iff_le.mpr (succ_le_of_lt h)]
      exact zero_le _
    | inr h =>
      rw [← succ_sub_succ c]
      exact (tsub_le_tsub_iff_left (succ_le_succ <| hx.2.le.trans h)).2 hx.2
  · rintro ⟨hb, ha⟩
    rw [lt_tsub_iff_left, lt_succ_iff] at ha
    have hx : x ≤ c := (Nat.le_add_left _ _).trans ha
    refine' ⟨c - x, _, tsub_tsub_cancel_of_le hx⟩
    · rw [mem_Ico]
      exact
        ⟨le_tsub_of_add_le_right ha,
          (tsub_lt_iff_left hx).2 <| succ_le_iff.1 <| tsub_le_iff_right.1 hb⟩
#align nat.Ico_image_const_sub_eq_Ico Nat.Ico_image_const_sub_eq_Ico

theorem Ico_succ_left_eq_erase_Ico : Ico a.succ b = erase (Ico a b) a :=
  by
  ext x
  rw [Ico_succ_left, mem_erase, mem_Ico, mem_Ioo, ← and_assoc, ne_comm, @and_comm (a ≠ x),
    lt_iff_le_and_ne]
#align nat.Ico_succ_left_eq_erase_Ico Nat.Ico_succ_left_eq_erase_Ico

theorem mod_injOn_Ico (n a : ℕ) : Set.InjOn (· % a) (Finset.Ico n (n + a)) :=
  by
  induction' n with n ih
  · simp only [zero_add, Nat.zero_eq, Ico_zero_eq_range]
    rintro k hk l hl (hkl : k % a = l % a)
    simp only [Finset.mem_range, Finset.mem_coe] at hk hl
    rwa [mod_eq_of_lt hk, mod_eq_of_lt hl] at hkl
  rw [Ico_succ_left_eq_erase_Ico, succ_add, succ_eq_add_one,
    Ico_succ_right_eq_insert_Ico le_self_add]
  rintro k hk l hl (hkl : k % a = l % a)
  have ha : 0 < a := by
    by_contra ha
    simp only [not_lt, nonpos_iff_eq_zero] at ha
    simp [ha] at hk
  simp only [Finset.mem_coe, Finset.mem_insert, Finset.mem_erase] at hk hl
  rcases hk with ⟨hkn, rfl | hk⟩ <;> rcases hl with ⟨hln, rfl | hl⟩
  · rfl
  · rw [add_mod_right] at hkl
    refine' (hln <| ih hl _ hkl.symm).elim
    simp only [lt_add_iff_pos_right, Set.left_mem_Ico, Finset.coe_Ico, ha]
  · rw [add_mod_right] at hkl
    suffices k = n by contradiction
    refine' ih hk _ hkl
    simp only [lt_add_iff_pos_right, Set.left_mem_Ico, Finset.coe_Ico, ha]
  · refine' ih _ _ hkl <;> simp only [Finset.mem_coe, hk, hl]
#align nat.mod_inj_on_Ico Nat.mod_injOn_Ico

/-- Note that while this lemma cannot be easily generalized to a type class, it holds for ℤ as
well. See `int.image_Ico_mod` for the ℤ version. -/
theorem image_Ico_mod (n a : ℕ) : (Ico n (n + a)).image (· % a) = range a :=
  by
  obtain rfl | ha := eq_or_ne a 0
  · rw [range_zero, add_zero, Ico_self, image_empty]
  ext i
  simp only [mem_image, exists_prop, mem_range, mem_Ico]
  constructor
  · rintro ⟨i, _, rfl⟩
    exact mod_lt i ha.bot_lt
  intro hia
  have hn := Nat.mod_add_div n a
  obtain hi | hi := lt_or_le i (n % a)
  · refine' ⟨i + a * (n / a + 1), ⟨_, _⟩, _⟩
    · rw [add_comm (n / a), mul_add, mul_one, ← add_assoc]
      refine' hn.symm.le.trans (add_le_add_right _ _)
      simpa only [zero_add] using add_le_add (zero_le i) (Nat.mod_lt n ha.bot_lt).le
    · refine' lt_of_lt_of_le (add_lt_add_right hi (a * (n / a + 1))) _
      rw [mul_add, mul_one, ← add_assoc, hn]
    · rw [Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt hia]
  · refine' ⟨i + a * (n / a), ⟨_, _⟩, _⟩
    · exact hn.symm.le.trans (add_le_add_right hi _)
    · rw [add_comm n a]
      refine' add_lt_add_of_lt_of_le hia (le_trans _ hn.le)
      simp only [zero_le, le_add_iff_nonneg_left]
    · rw [Nat.add_mul_mod_self_left, Nat.mod_eq_of_lt hia]
#align nat.image_Ico_mod Nat.image_Ico_mod

section Multiset

open Multiset

theorem multiset_Ico_map_mod (n a : ℕ) :
    (Multiset.Ico n (n + a)).map (· % a) = Multiset.range a := by
  convert congr_arg Finset.val (image_Ico_mod n a)
  refine' ((nodup_map_iff_inj_on (Finset.Ico _ _).nodup).2 <| _).dedup.symm
  exact mod_injOn_Ico _ _
#align nat.multiset_Ico_map_mod Nat.multiset_Ico_map_mod

end Multiset

end Nat

namespace Finset

theorem range_image_pred_top_sub (n : ℕ) :
    ((Finset.range n).image fun j => n - 1 - j) = Finset.range n := by
  cases n
  · rw [range_zero, image_empty]
  · rw [Finset.range_eq_Ico, Nat.Ico_image_const_sub_eq_Ico (Nat.zero_le _)]
    simp_rw [succ_sub_succ, tsub_zero, tsub_self]
#align finset.range_image_pred_top_sub Finset.range_image_pred_top_sub

-- Porting note: had to use `@` and specify `(a + b)` explicitely. mathlib3 managed without.
theorem range_add_eq_union : range (a + b) = range a ∪ (range b).map (addLeftEmbedding a) := by
  rw [Finset.range_eq_Ico, map_eq_image]
  convert (@Ico_union_Ico_eq_Ico _ _ _ _ _ (a + b) a.zero_le le_self_add).symm
  exact image_add_left_Ico _ _ _
#align finset.range_add_eq_union Finset.range_add_eq_union

end Finset

section Induction

variable {P : ℕ → Prop} (h : ∀ n, P (n + 1) → P n)

theorem Nat.decreasing_induction_of_not_bddAbove (hP : ¬BddAbove { x | P x }) (n : ℕ) : P n :=
  let ⟨_, hm, hl⟩ := not_bddAbove_iff.1 hP n
  decreasingInduction h hl.le hm
#align nat.decreasing_induction_of_not_bdd_above Nat.decreasing_induction_of_not_bddAbove

theorem Nat.decreasing_induction_of_infinite (hP : { x | P x }.Infinite) (n : ℕ) : P n :=
  Nat.decreasing_induction_of_not_bddAbove h (mt BddAbove.finite hP) n
#align nat.decreasing_induction_of_infinite Nat.decreasing_induction_of_infinite

theorem Nat.cauchy_induction' (seed : ℕ) (hs : P seed) (hi : ∀ x, seed ≤ x → P x → ∃ y, x < y ∧ P y)
    (n : ℕ) : P n := by
  apply Nat.decreasing_induction_of_infinite h fun hf => _
  intro hf
  obtain ⟨m, hP, hm⟩ := hf.exists_maximal_wrt id _ ⟨seed, hs⟩
  obtain ⟨y, hl, hy⟩ := hi m (le_of_not_lt fun hl => hl.ne <| hm seed hs hl.le) hP
  exact hl.ne (hm y hy hl.le)
#align nat.cauchy_induction' Nat.cauchy_induction'

theorem Nat.cauchy_induction (seed : ℕ) (hs : P seed) (f : ℕ → ℕ)
    (hf : ∀ x, seed ≤ x → P x → x < f x ∧ P (f x)) (n : ℕ) : P n :=
  seed.cauchy_induction' h hs (fun x hl hx => ⟨f x, hf x hl hx⟩) n
#align nat.cauchy_induction Nat.cauchy_induction

theorem Nat.cauchy_induction_mul (k seed : ℕ) (hk : 1 < k) (hs : P seed.succ)
    (hm : ∀ x, seed < x → P x → P (k * x)) (n : ℕ) : P n := by
  apply Nat.cauchy_induction h _ hs ((· * ·) k) fun x hl hP => ⟨_, hm x hl hP⟩
  intro _ hl _
  convert (mul_lt_mul_right <| seed.succ_pos.trans_le hl).2 hk
  rw [one_mul]
#align nat.cauchy_induction_mul Nat.cauchy_induction_mul

theorem Nat.cauchy_induction_two_mul (seed : ℕ) (hs : P seed.succ)
    (hm : ∀ x, seed < x → P x → P (2 * x)) (n : ℕ) : P n :=
  Nat.cauchy_induction_mul h 2 seed one_lt_two hs hm n
#align nat.cauchy_induction_two_mul Nat.cauchy_induction_two_mul

end Induction


/-
Copyright (c) 2018 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Simon Hudon
-/
import data.list.basic

/-!
# The Following Are Equivalent

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

This file allows to state that all propositions in a list are equivalent. It is used by
`tactic.tfae`.
`tfae l` means `∀ x ∈ l, ∀ y ∈ l, x ↔ y`. This is equivalent to `pairwise (↔) l`.
-/

namespace list

/--
tfae: The Following (propositions) Are Equivalent.

The `tfae_have` and `tfae_finish` tactics can be useful in proofs with `tfae` goals.
-/
def tfae (l : list Prop) : Prop := ∀ x ∈ l, ∀ y ∈ l, x ↔ y

theorem tfae_nil : tfae [] := forall_mem_nil _
theorem tfae_singleton (p) : tfae [p] := by simp [tfae, -eq_iff_iff]

theorem tfae_cons_of_mem {a b} {l : list Prop} (h : b ∈ l) :
  tfae (a::l) ↔ (a ↔ b) ∧ tfae l :=
⟨λ H, ⟨H a (by simp) b (or.inr h), λ p hp q hq, H _ (or.inr hp) _ (or.inr hq)⟩,
begin
  rintro ⟨ab, H⟩ p (rfl | hp) q (rfl | hq),
  { refl },
  { exact ab.trans (H _ h _ hq) },
  { exact (ab.trans (H _ h _ hp)).symm },
  { exact H _ hp _ hq }
end⟩

theorem tfae_cons_cons {a b} {l : list Prop} : tfae (a::b::l) ↔ (a ↔ b) ∧ tfae (b::l) :=
tfae_cons_of_mem (or.inl rfl)

theorem tfae_of_forall (b : Prop) (l : list Prop) (h : ∀ a ∈ l, a ↔ b) : tfae l :=
λ a₁ h₁ a₂ h₂, (h _ h₁).trans (h _ h₂).symm

theorem tfae_of_cycle {a b} {l : list Prop} :
  list.chain (→) a (b::l) → (ilast' b l → a) → tfae (a::b::l) :=
begin
  induction l with c l IH generalizing a b;
    simp only [tfae_cons_cons, tfae_singleton, and_true, chain_cons, chain.nil] at *,
  { intros a b, exact iff.intro a b },
  rintros ⟨ab,⟨bc,ch⟩⟩ la,
  have := IH ⟨bc,ch⟩ (ab ∘ la),
  exact ⟨⟨ab, la ∘ (this.2 c (or.inl rfl) _ (ilast'_mem _ _)).1 ∘ bc⟩, this⟩
end

theorem tfae.out {l} (h : tfae l) (n₁ n₂) {a b}
  (h₁ : list.nth l n₁ = some a . tactic.interactive.refl)
  (h₂ : list.nth l n₂ = some b . tactic.interactive.refl) :
  a ↔ b :=
h _ (list.nth_mem h₁) _ (list.nth_mem h₂)

end list


import
  ...semantics
  tactic.induction
  tactic.finish

open exp

lemma big_subst_empty (e : exp) :
  e = big_subst [] e :=
by rw big_subst

lemma subst_absorb {E v x e} :
    big_subst E (subst v x e)
  = big_subst (⟨x, v⟩ :: E) e := by rw big_subst

lemma subst_merge {E v x e} :
    subst v x (big_subst E e)
  = big_subst (E ++ [⟨x, v⟩]) e :=
begin
  induction' E,
  case nil {
    rw list.nil_append,
    repeat { rw big_subst }
  },
  case cons {
    cases' hd with y u,
    rw list.cons_append,
    repeat { rw big_subst },
    exact ih
  }
end

lemma subst_x_idempotent {e x v u} :
  subst u x (subst v x e) = subst v x e :=
begin
  induction' e,
  case EVal {
    repeat { rw [subst] }
  },
  case EVar {
    apply dite (x = s), {
      assume heq,
      rw [subst, if_pos heq, subst]
    }, {
      assume hne,
      rw [subst, if_neg hne,
          subst, if_neg hne]
    }
  },
  case EOp {
    repeat { rw subst },
    rw [ih_e, ih_e_1]
  },
  case EIf {
    repeat { rw subst },
    rw [ih_e, ih_e_1, ih_e_2]
  },
  case ELet {
    apply dite (x = s), {
      assume heq,
      repeat { rw [subst, if_pos heq] },
      rw ih_e
    }, {
      assume hne,
      repeat { rw [subst, if_neg hne] },
      rw [ih_e, ih_e_1]
    }
  }
end

lemma subst_comm {x y v u e} :
    y ≠ x →
    subst u y (subst v x e)
  = subst v x (subst u y e) :=
begin
  assume hne,
  induction' e,
  case EVal {
    repeat { rw subst }
  },
  case EVar {
    apply dite (x = s), {
      assume heqx,
      rw [subst, if_pos heqx],
      apply dite (y = s), {
        assume heqy,
        rw [subst, if_pos heqy, subst, subst],
        finish -- contradiction
      }, {
        assume hney,
        rw [subst, if_neg hney,
            subst, if_pos heqx,
            subst]
      }
    }, {
      assume hnex,
      rw [subst, if_neg hnex],
      apply dite (y = s), {
        assume heqy,
        rw [subst, if_pos heqy, subst]
      }, {
        assume hney,
        rw [subst, if_neg hney, subst, if_neg hnex]
      }
    }
  },
  case EOp {
    repeat { rw [subst] },
    rw [ih_e hne, ih_e_1 hne]
  },
  case EIf {
    repeat { rw [subst] },
    rw [ih_e hne, ih_e_1 hne, ih_e_2 hne]
  },
  case ELet {
    repeat { rw [subst] },
    apply dite (x = s), {
      assume heqx,
      rw [if_pos heqx, subst],
      apply dite (y = s), {
        assume heqy,
        rw [if_pos heqy, if_pos heqy,
            subst, if_pos heqx,
            ih_e hne]
      }, {
        assume hney,
        rw [if_neg hney, if_neg hney,
            subst, if_pos heqx,
            ih_e hne]
      }
    }, {
      assume hnex,
      rw [if_neg hnex, subst],
      apply dite (y = s), {
        assume heqy,
        rw [if_pos heqy, if_pos heqy,
            subst, if_neg hnex,
            ih_e hne]
      }, {
        assume hney,
        rw [if_neg hney, if_neg hney,
            subst, if_neg hnex,
            ih_e hne, ih_e_1 hne]
      }
    }
  }
end

lemma big_subst_remove_append {E v x e} :
    big_subst (remove x E ++ [(x, v)]) e
  = big_subst ((x, v) :: E) e :=
begin
  induction' E,
  case nil {
    rw remove, simp
  },
  case cons {
    cases' hd with y u,
    rw [big_subst, remove],
    apply dite (y = x), {
      assume heq,
      rw [if_pos heq, heq,
        big_subst,
        subst_x_idempotent,
        subst_absorb],
      exact ih
    }, {
      assume hne,
      rw [if_neg hne,
        list.cons_append,
        big_subst, big_subst,
        subst_comm hne],
      exact ih
    }
  }
end

lemma big_subst_val {E v} :
  big_subst E (EVal v) = (EVal v) :=
begin
  induction' E,
  case nil {
    rw big_subst
  },
  case cons {
    cases' hd with x u,
    rw [big_subst, subst],
    exact ih
  }
end

lemma big_subst_spread_op {E op e f} :
    big_subst E (EOp op e f)
  = EOp op (big_subst E e) (big_subst E f) :=
begin
  induction' E,
  case nil {
    repeat { rw big_subst }
  },
  case cons {
    cases' hd with x v,
    repeat { rw big_subst },
    rw [←ih, subst]
  }
end

lemma big_subst_spread_if {E c t f} :
    big_subst E (EIf c t f)
  = EIf (big_subst E c) (big_subst E t) (big_subst E f) :=
begin
  induction' E,
  case nil {
    repeat { rw big_subst }
  },
  case cons {
    cases' hd with x v,
    repeat { rw big_subst },
    rw ←ih,
    rw subst
  }
end

lemma big_subst_spread_let {E x e b} :
    big_subst E (ELet x e b)
  = ELet x (big_subst E e) (big_subst (remove x E) b) :=
begin
  induction' E,
  case nil {
    rw remove,
    repeat { rw big_subst }
  },
  case cons {
    cases' hd with y v,
    repeat { rw big_subst },
    rw [remove, subst],
    apply dite (y = x), {
      assume heq,
      repeat { rw [if_pos heq] },
      rw ih
    }, {
      assume hne,
      repeat { rw [if_neg hne] },
      rw big_subst,
      apply ih
    }
  }
end

open val

lemma big_subst_bound_var {E x v r} :
    bound x v E
  → big_subst E (EVar x) ⟹ r
  → v = r :=
begin
  assume hbound heval,
  induction' E,
  case nil {
    rw big_subst at heval,
    cases' heval
  },
  case cons {
    cases' hd with y u,
    apply dite (x = y), {
      assume heqx,
      rw heqx at heval hbound,
      rw [big_subst, subst,
            if_pos (eq.refl y),
            big_subst_val] at heval,
      cases' heval,
      cases' hbound,
      { refl },
      { contradiction }
    }, {
      assume hnex,
      rw [big_subst, subst,
          if_neg (ne.intro hnex).symm] at heval,
      cases' hbound,
      case bhead { contradiction },
      exact ih hbound heval
    }
  }
end

lemma big_subst_var_implies_bound {E x r} :
  big_subst E (EVar x) ⟹ r → ∃v, bound x v E :=
begin
  assume h,
  induction' E,
  case nil {
    rw big_subst at h,
    cases' h
  },
  case cons {
    cases' hd with y u,
    rw [big_subst, subst] at h,
    apply dite (y = x), {
      assume heq,
      rw heq,
      use u,
      exact bound.bhead
    }, {
      assume hneq,
      rw [if_neg hneq] at h,
      cases ih h with w hbound,
      use w,
      apply bound.btail,
      exact (ne.intro hneq).symm,
      exact hbound
    }
  }
end


/-
Copyright (c) 2021 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser
-/
import data.equiv.fin
import data.equiv.fintype
import group_theory.perm.option
import group_theory.perm.cycle_type

/-!
# Permutations of `fin n`
-/
open equiv

/-- Permutations of `fin (n + 1)` are equivalent to fixing a single
`fin (n + 1)` and permuting the remaining with a `perm (fin n)`.
The fixed `fin (n + 1)` is swapped with `0`. -/
def equiv.perm.decompose_fin {n : ℕ} :
  perm (fin n.succ) ≃ fin n.succ × perm (fin n) :=
((equiv.perm_congr $ fin_succ_equiv n).trans equiv.perm.decompose_option).trans
  (equiv.prod_congr (fin_succ_equiv n).symm (equiv.refl _))

@[simp] lemma equiv.perm.decompose_fin_symm_of_refl {n : ℕ} (p : fin (n + 1)) :
  equiv.perm.decompose_fin.symm (p, equiv.refl _) = swap 0 p :=
by simp [equiv.perm.decompose_fin, equiv.perm_congr_def]

@[simp] lemma equiv.perm.decompose_fin_symm_of_one {n : ℕ} (p : fin (n + 1)) :
  equiv.perm.decompose_fin.symm (p, 1) = swap 0 p :=
equiv.perm.decompose_fin_symm_of_refl p

@[simp] lemma equiv.perm.decompose_fin_symm_apply_zero {n : ℕ}
  (p : fin (n + 1)) (e : perm (fin n)) :
  equiv.perm.decompose_fin.symm (p, e) 0 = p :=
by simp [equiv.perm.decompose_fin]

@[simp] lemma equiv.perm.decompose_fin_symm_apply_succ {n : ℕ}
  (e : perm (fin n)) (p : fin (n + 1)) (x : fin n) :
  equiv.perm.decompose_fin.symm (p, e) x.succ = swap 0 p (e x).succ :=
begin
  refine fin.cases _ _ p,
  { simp [equiv.perm.decompose_fin, equiv_functor.map] },
  { intros i,
    by_cases h : i = e x,
    { simp [h, equiv.perm.decompose_fin, equiv_functor.map] },
    { have h' : some (e x) ≠ some i := λ H, h (option.some_injective _ H).symm,
      have h'' : (e x).succ ≠ i.succ := λ H, h (fin.succ_injective _ H).symm,
      simp [h, h'', fin.succ_ne_zero, equiv.perm.decompose_fin, equiv_functor.map,
            swap_apply_of_ne_of_ne, swap_apply_of_ne_of_ne (option.some_ne_none (e x)) h'] } }
end

@[simp] lemma equiv.perm.decompose_fin_symm_apply_one {n : ℕ}
  (e : perm (fin (n + 1))) (p : fin (n + 2)) :
  equiv.perm.decompose_fin.symm (p, e) 1 = swap 0 p (e 0).succ :=
by rw [← fin.succ_zero_eq_one, equiv.perm.decompose_fin_symm_apply_succ e p 0]

@[simp] lemma equiv.perm.decompose_fin.symm_sign {n : ℕ} (p : fin (n + 1)) (e : perm (fin n)) :
  perm.sign (equiv.perm.decompose_fin.symm (p, e)) = ite (p = 0) 1 (-1) * perm.sign e :=
by { refine fin.cases _ _ p; simp [equiv.perm.decompose_fin, fin.succ_ne_zero] }

/-- The set of all permutations of `fin (n + 1)` can be constructed by augmenting the set of
permutations of `fin n` by each element of `fin (n + 1)` in turn. -/
lemma finset.univ_perm_fin_succ {n : ℕ} :
  @finset.univ (perm $ fin n.succ) _ = (finset.univ : finset $ fin n.succ × perm (fin n)).map
  equiv.perm.decompose_fin.symm.to_embedding :=
(finset.univ_map_equiv_to_embedding _).symm

section cycle_range

/-! ### `cycle_range` section

Define the permutations `fin.cycle_range i`, the cycle `(0 1 2 ... i)`.
-/

open equiv.perm

lemma fin_rotate_succ {n : ℕ} :
  fin_rotate n.succ = decompose_fin.symm (1, fin_rotate n) :=
begin
  ext i,
  cases n, { simp },
  refine fin.cases _ (λ i, _) i,
  { simp },
  rw [coe_fin_rotate, decompose_fin_symm_apply_succ, if_congr (i.succ_eq_last_succ) rfl rfl],
  split_ifs with h,
  { simp [h] },
  { rw [fin.coe_succ, function.injective.map_swap fin.coe_injective, fin.coe_succ, coe_fin_rotate,
    if_neg h, fin.coe_zero, fin.coe_one,
    swap_apply_of_ne_of_ne (nat.succ_ne_zero _) (nat.succ_succ_ne_one _)] }
end

@[simp] lemma sign_fin_rotate (n : ℕ) : perm.sign (fin_rotate (n + 1)) = (-1) ^ n :=
begin
  induction n with n ih,
  { simp },
  { rw fin_rotate_succ, simp [ih, pow_succ] },
end

@[simp] lemma support_fin_rotate {n : ℕ} : support (fin_rotate (n + 2)) = finset.univ :=
by { ext, simp }

lemma support_fin_rotate_of_le {n : ℕ} (h : 2 ≤ n) :
  support (fin_rotate n) = finset.univ :=
begin
  obtain ⟨m, rfl⟩ := exists_add_of_le h,
  rw [add_comm, support_fin_rotate],
end

lemma is_cycle_fin_rotate {n : ℕ} : is_cycle (fin_rotate (n + 2)) :=
begin
  refine ⟨0, dec_trivial, λ x hx', ⟨x, _⟩⟩,
  clear hx',
  cases x with x hx,
  rw [coe_coe, zpow_coe_nat, fin.ext_iff, fin.coe_mk],
  induction x with x ih, { refl },
  rw [pow_succ, perm.mul_apply, coe_fin_rotate_of_ne_last, ih (lt_trans x.lt_succ_self hx)],
  rw [ne.def, fin.ext_iff, ih (lt_trans x.lt_succ_self hx), fin.coe_last],
  exact ne_of_lt (nat.lt_of_succ_lt_succ hx),
end

lemma is_cycle_fin_rotate_of_le {n : ℕ} (h : 2 ≤ n) :
  is_cycle (fin_rotate n) :=
begin
  obtain ⟨m, rfl⟩ := exists_add_of_le h,
  rw [add_comm],
  exact is_cycle_fin_rotate
end

@[simp] lemma cycle_type_fin_rotate {n : ℕ} : cycle_type (fin_rotate (n + 2)) = {n + 2} :=
begin
  rw [is_cycle_fin_rotate.cycle_type, support_fin_rotate, ← fintype.card, fintype.card_fin],
  refl,
end

lemma cycle_type_fin_rotate_of_le {n : ℕ} (h : 2 ≤ n) :
  cycle_type (fin_rotate n) = {n} :=
begin
  obtain ⟨m, rfl⟩ := exists_add_of_le h,
  rw [add_comm, cycle_type_fin_rotate]
end

namespace fin

/-- `fin.cycle_range i` is the cycle `(0 1 2 ... i)` leaving `(i+1 ... (n-1))` unchanged. -/
def cycle_range {n : ℕ} (i : fin n) : perm (fin n) :=
(fin_rotate (i + 1))
  .extend_domain (equiv.of_left_inverse' (fin.cast_le (nat.succ_le_of_lt i.is_lt)).to_embedding
    coe (by { intros x, ext, simp }))

lemma cycle_range_of_gt {n : ℕ} {i j : fin n.succ} (h : i < j) :
  cycle_range i j = j :=
begin
  rw [cycle_range, of_left_inverse'_eq_of_injective,
      ←function.embedding.to_equiv_range_eq_of_injective,
      ←via_fintype_embedding, via_fintype_embedding_apply_not_mem_range],
  simpa
end

lemma cycle_range_of_le {n : ℕ} {i j : fin n.succ} (h : j ≤ i) :
  cycle_range i j = if j = i then 0 else j + 1 :=
begin
  cases n,
  { simp },

  have : j = (fin.cast_le (nat.succ_le_of_lt i.is_lt)).to_embedding
    ⟨j, lt_of_le_of_lt h (nat.lt_succ_self i)⟩,
  { simp },

  ext,
  rw [this, cycle_range, of_left_inverse'_eq_of_injective,
      ←function.embedding.to_equiv_range_eq_of_injective,
      ←via_fintype_embedding, via_fintype_embedding_apply_image, rel_embedding.coe_fn_to_embedding,
      coe_cast_le, coe_fin_rotate],
  simp only [fin.ext_iff, coe_last, coe_mk, coe_zero, fin.eta, apply_ite coe, cast_le_mk],
  split_ifs with heq,
  { refl },
  { rw fin.coe_add_one_of_lt,
    exact lt_of_lt_of_le (lt_of_le_of_ne h (mt (congr_arg coe) heq)) (le_last i) }
end

lemma coe_cycle_range_of_le {n : ℕ} {i j : fin n.succ} (h : j ≤ i) :
  (cycle_range i j : ℕ) = if j = i then 0 else j + 1 :=
by { rw [cycle_range_of_le h],
     split_ifs with h', { refl },
     exact coe_add_one_of_lt (calc (j : ℕ) < i : fin.lt_iff_coe_lt_coe.mp (lt_of_le_of_ne h h')
                                       ... ≤ n : nat.lt_succ_iff.mp i.2) }

lemma cycle_range_of_lt {n : ℕ} {i j : fin n.succ} (h : j < i) :
  cycle_range i j = j + 1 :=
by rw [cycle_range_of_le h.le, if_neg h.ne]

lemma coe_cycle_range_of_lt {n : ℕ} {i j : fin n.succ} (h : j < i) :
  (cycle_range i j : ℕ) = j + 1 :=
by rw [coe_cycle_range_of_le h.le, if_neg h.ne]

@[simp]
lemma cycle_range_self {n : ℕ} (i : fin n.succ) :
  cycle_range i i = 0 :=
cycle_range_of_eq rfl

lemma cycle_range_apply {n : ℕ} (i j : fin n.succ) :
  cycle_range i j = if j < i then j + 1 else if j = i then 0 else j :=
begin
  split_ifs with h₁ h₂,
  { exact cycle_range_of_lt h₁ },
  { exact cycle_range_of_eq h₂ },
  { exact cycle_range_of_gt (lt_of_le_of_ne (le_of_not_gt h₁) (ne.symm h₂)) },
end

@[simp] lemma cycle_range_zero (n : ℕ) : cycle_range (0 : fin n.succ) = 1 :=
begin
  ext j,
  refine fin.cases _ (λ j, _) j,
  { simp },
  { rw [cycle_range_of_gt (fin.succ_pos j), one_apply] },
end

@[simp] lemma cycle_range_last (n : ℕ) : cycle_range (last n) = fin_rotate (n + 1) :=
by { ext i, rw [coe_cycle_range_of_le (le_last _), coe_fin_rotate] }

@[simp] lemma cycle_range_zero' {n : ℕ} (h : 0 < n) : cycle_range ⟨0, h⟩ = 1 :=
begin
  cases n with n,
  { cases h },
  exact cycle_range_zero n
end

@[simp] lemma sign_cycle_range {n : ℕ} (i : fin n) :
  perm.sign (cycle_range i) = (-1) ^ (i : ℕ) :=
by simp [cycle_range]

@[simp] lemma succ_above_cycle_range {n : ℕ} (i j : fin n) :
  i.succ.succ_above (i.cycle_range j) = swap 0 i.succ j.succ :=
begin
  cases n,
  { rcases j with ⟨_, ⟨⟩⟩ },
  rcases lt_trichotomy j i with hlt | heq | hgt,
  { have : (j + 1).cast_succ = j.succ,
    { ext, rw [coe_cast_succ, coe_succ, fin.coe_add_one_of_lt (lt_of_lt_of_le hlt i.le_last)] },
    rw [fin.cycle_range_of_lt hlt, fin.succ_above_below, this, swap_apply_of_ne_of_ne],
    { apply fin.succ_ne_zero },
    { exact (fin.succ_injective _).ne hlt.ne },
    { rw fin.lt_iff_coe_lt_coe,
      simpa [this] using hlt } },
  { rw [heq, fin.cycle_range_self, fin.succ_above_below, swap_apply_right, fin.cast_succ_zero],
    { rw fin.cast_succ_zero, apply fin.succ_pos } },
  { rw [fin.cycle_range_of_gt hgt, fin.succ_above_above, swap_apply_of_ne_of_ne],
    { apply fin.succ_ne_zero },
    { apply (fin.succ_injective _).ne hgt.ne.symm },
    { simpa [fin.le_iff_coe_le_coe] using hgt } },
end

@[simp] lemma cycle_range_succ_above {n : ℕ} (i : fin (n + 1)) (j : fin n) :
  i.cycle_range (i.succ_above j) = j.succ :=
begin
  cases lt_or_ge j.cast_succ i with h h,
  { rw [fin.succ_above_below _ _ h, fin.cycle_range_of_lt h, fin.coe_succ_eq_succ] },
  { rw [fin.succ_above_above _ _ h, fin.cycle_range_of_gt (fin.le_cast_succ_iff.mp h)] }
end

@[simp] lemma cycle_range_symm_zero {n : ℕ} (i : fin (n + 1)) :
  i.cycle_range.symm 0 = i :=
i.cycle_range.injective (by simp)

@[simp] lemma cycle_range_symm_succ {n : ℕ} (i : fin (n + 1)) (j : fin n) :
  i.cycle_range.symm j.succ = i.succ_above j :=
i.cycle_range.injective (by simp)

lemma is_cycle_cycle_range {n : ℕ} {i : fin (n + 1)} (h0 : i ≠ 0) : is_cycle (cycle_range i) :=
begin
  cases i with i hi,
  cases i,
  { exact (h0 rfl).elim },
  exact is_cycle_fin_rotate.extend_domain _,
end

@[simp] lemma cycle_type_cycle_range {n : ℕ} {i : fin (n + 1)} (h0 : i ≠ 0) :
  cycle_type (cycle_range i) = {i + 1} :=
begin
  cases i with i hi,
  cases i,
  { exact (h0 rfl).elim },
  rw [cycle_range, cycle_type_extend_domain],
  exact cycle_type_fin_rotate,
end

lemma is_three_cycle_cycle_range_two {n : ℕ} :
  is_three_cycle (cycle_range 2 : perm (fin (n + 3))) :=
begin
  rw [is_three_cycle, cycle_type_cycle_range];
  dec_trivial
end

end fin

end cycle_range


import tactic data.nat.prime
noncomputable theory
open_locale classical

open nat

theorem dvd_sub_one {p a : ℕ} : (p ∣ a) → (p ∣ a + 1) → (p ∣ 1) :=
begin
  sorry,
end

/-
dvd_sub_one : (p ∣ a) → (p ∣ a + 1) → (p ∣ 1)

m ∣ n := ∃ k : ℕ, m = n * k
m.prime :=  2 ≤ p ∧ (∀ (m : ℕ), m ∣ p → m = 1 ∨ m = p)
nat.prime.not_dvd_one : (prime p) → ¬ p ∣ 1

factorial n is defined to be n!
factorial_pos : ∀ (n : ℕ), 0 < factorial n
dvd_factorial : 0 < m → m ≤ n → m ∣ factorial n

n.min_fac is defined to be the smallest non-trivial factor of n
min_fac_prime : n ≠ 1 → n.min_fac.prime
min_fac_pos : ∀ (n : ℕ), 0 < n.min_fac
min_fac_dvd : ∀ (n : ℕ), n.min_fac ∣ n
-/

theorem exists_infinite_primes (n : ℕ) : ∃ p, nat.prime p ∧ p ≥ n :=
begin
  set p := (n.factorial + 1).min_fac, -- Use `set` instead of `have` when you're just making an abbreviation for a number.
  sorry,
end


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

universes u l u_1 u_2

namespace Mathlib

/-!
# Ordered groups

This file develops the basics of ordered groups.

## Implementation details

Unfortunately, the number of `'` appended to lemmas in this file
may differ between the multiplicative and the additive version of a lemma.
The reason is that we did not want to change existing names in the library.

-/

/-- An ordered additive commutative group is an additive commutative group
with a partial order in which addition is strictly monotone. -/
class ordered_add_comm_group (α : Type u) extends partial_order α, add_comm_group α where
  add_le_add_left : ∀ (a b : α), a ≤ b → ∀ (c : α), c + a ≤ c + b

/-- An ordered commutative group is an commutative group
with a partial order in which multiplication is strictly monotone. -/
class ordered_comm_group (α : Type u) extends comm_group α, partial_order α where
  mul_le_mul_left : ∀ (a b : α), a ≤ b → ∀ (c : α), c * a ≤ c * b

/--The units of an ordered commutative monoid form an ordered commutative group. -/
protected instance add_units.ordered_add_comm_group {α : Type u} [ordered_add_comm_monoid α] :
    ordered_add_comm_group (add_units α) :=
  ordered_add_comm_group.mk add_comm_group.add sorry add_comm_group.zero sorry sorry
    add_comm_group.neg add_comm_group.sub sorry sorry partial_order.le partial_order.lt sorry sorry
    sorry sorry

theorem ordered_comm_group.mul_lt_mul_left' {α : Type u} [ordered_comm_group α] (a : α) (b : α)
    (h : a < b) (c : α) : c * a < c * b :=
  sorry

theorem ordered_comm_group.le_of_mul_le_mul_left {α : Type u} [ordered_comm_group α] {a : α} {b : α}
    {c : α} (h : a * b ≤ a * c) : b ≤ c :=
  sorry

theorem ordered_comm_group.lt_of_mul_lt_mul_left {α : Type u} [ordered_comm_group α] {a : α} {b : α}
    {c : α} (h : a * b < a * c) : b < c :=
  sorry

protected instance ordered_add_comm_group.to_ordered_cancel_add_comm_monoid (α : Type u)
    [s : ordered_add_comm_group α] : ordered_cancel_add_comm_monoid α :=
  ordered_cancel_add_comm_monoid.mk ordered_add_comm_group.add ordered_add_comm_group.add_assoc
    sorry ordered_add_comm_group.zero ordered_add_comm_group.zero_add
    ordered_add_comm_group.add_zero ordered_add_comm_group.add_comm sorry ordered_add_comm_group.le
    ordered_add_comm_group.lt ordered_add_comm_group.le_refl ordered_add_comm_group.le_trans
    ordered_add_comm_group.le_antisymm ordered_add_comm_group.add_le_add_left
    ordered_add_comm_group.le_of_add_le_add_left

theorem neg_le_neg {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} (h : a ≤ b) : -b ≤ -a :=
  sorry

theorem le_of_inv_le_inv {α : Type u} [ordered_comm_group α] {a : α} {b : α} (h : b⁻¹ ≤ (a⁻¹)) :
    a ≤ b :=
  sorry

theorem one_le_of_inv_le_one {α : Type u} [ordered_comm_group α] {a : α} (h : a⁻¹ ≤ 1) : 1 ≤ a :=
  (fun (this : a⁻¹ ≤ (1⁻¹)) => le_of_inv_le_inv this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ ≤ (1⁻¹))) one_inv)) h)

theorem inv_le_one_of_one_le {α : Type u} [ordered_comm_group α] {a : α} (h : 1 ≤ a) : a⁻¹ ≤ 1 :=
  (fun (this : a⁻¹ ≤ (1⁻¹)) => eq.mp (Eq._oldrec (Eq.refl (a⁻¹ ≤ (1⁻¹))) one_inv) this)
    (inv_le_inv' h)

theorem le_one_of_one_le_inv {α : Type u} [ordered_comm_group α] {a : α} (h : 1 ≤ (a⁻¹)) : a ≤ 1 :=
  (fun (this : 1⁻¹ ≤ (a⁻¹)) => le_of_inv_le_inv this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (1⁻¹ ≤ (a⁻¹))) one_inv)) h)

theorem neg_nonneg_of_nonpos {α : Type u} [ordered_add_comm_group α] {a : α} (h : a ≤ 0) : 0 ≤ -a :=
  (fun (this : -0 ≤ -a) => eq.mp (Eq._oldrec (Eq.refl (-0 ≤ -a)) neg_zero) this) (neg_le_neg h)

theorem neg_lt_neg {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} (h : a < b) : -b < -a :=
  sorry

theorem lt_of_neg_lt_neg {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} (h : -b < -a) :
    a < b :=
  neg_neg a ▸ neg_neg b ▸ neg_lt_neg h

theorem pos_of_neg_neg {α : Type u} [ordered_add_comm_group α] {a : α} (h : -a < 0) : 0 < a :=
  (fun (this : -a < -0) => lt_of_neg_lt_neg this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (-a < -0)) neg_zero)) h)

theorem inv_inv_of_one_lt {α : Type u} [ordered_comm_group α] {a : α} (h : 1 < a) : a⁻¹ < 1 :=
  (fun (this : a⁻¹ < (1⁻¹)) => eq.mp (Eq._oldrec (Eq.refl (a⁻¹ < (1⁻¹))) one_inv) this)
    (inv_lt_inv' h)

theorem inv_of_one_lt_inv {α : Type u} [ordered_comm_group α] {a : α} (h : 1 < (a⁻¹)) : a < 1 :=
  (fun (this : 1⁻¹ < (a⁻¹)) => lt_of_inv_lt_inv this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (1⁻¹ < (a⁻¹))) one_inv)) h)

theorem one_lt_inv_of_inv {α : Type u} [ordered_comm_group α] {a : α} (h : a < 1) : 1 < (a⁻¹) :=
  (fun (this : 1⁻¹ < (a⁻¹)) => eq.mp (Eq._oldrec (Eq.refl (1⁻¹ < (a⁻¹))) one_inv) this)
    (inv_lt_inv' h)

theorem le_inv_of_le_inv {α : Type u} [ordered_comm_group α] {a : α} {b : α} (h : a ≤ (b⁻¹)) :
    b ≤ (a⁻¹) :=
  eq.mp (Eq._oldrec (Eq.refl (b⁻¹⁻¹ ≤ (a⁻¹))) (inv_inv b)) (inv_le_inv' h)

theorem neg_le_of_neg_le {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} (h : -a ≤ b) :
    -b ≤ a :=
  eq.mp (Eq._oldrec (Eq.refl (-b ≤ --a)) (neg_neg a)) (neg_le_neg h)

theorem lt_inv_of_lt_inv {α : Type u} [ordered_comm_group α] {a : α} {b : α} (h : a < (b⁻¹)) :
    b < (a⁻¹) :=
  eq.mp (Eq._oldrec (Eq.refl (b⁻¹⁻¹ < (a⁻¹))) (inv_inv b)) (inv_lt_inv' h)

theorem neg_lt_of_neg_lt {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} (h : -a < b) :
    -b < a :=
  eq.mp (Eq._oldrec (Eq.refl (-b < --a)) (neg_neg a)) (neg_lt_neg h)

theorem add_le_of_le_neg_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (h : b ≤ -a + c) : a + b ≤ c :=
  eq.mp (Eq._oldrec (Eq.refl (a + b ≤ a + (-a + c))) (add_neg_cancel_left a c))
    (add_le_add_left h a)

theorem le_inv_mul_of_mul_le {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α}
    (h : a * b ≤ c) : b ≤ a⁻¹ * c :=
  eq.mp (Eq._oldrec (Eq.refl (a⁻¹ * (a * b) ≤ a⁻¹ * c)) (inv_mul_cancel_left a b))
    (mul_le_mul_left' h (a⁻¹))

theorem le_mul_of_inv_mul_le {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α}
    (h : b⁻¹ * a ≤ c) : a ≤ b * c :=
  eq.mp (Eq._oldrec (Eq.refl (b * (b⁻¹ * a) ≤ b * c)) (mul_inv_cancel_left b a))
    (mul_le_mul_left' h b)

theorem inv_mul_le_of_le_mul {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α}
    (h : a ≤ b * c) : b⁻¹ * a ≤ c :=
  eq.mp (Eq._oldrec (Eq.refl (b⁻¹ * a ≤ b⁻¹ * (b * c))) (inv_mul_cancel_left b c))
    (mul_le_mul_left' h (b⁻¹))

theorem le_mul_of_inv_mul_le_left {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α}
    (h : b⁻¹ * a ≤ c) : a ≤ b * c :=
  le_mul_of_inv_mul_le h

theorem neg_add_le_left_of_le_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (h : a ≤ b + c) : -b + a ≤ c :=
  neg_add_le_of_le_add h

theorem le_add_of_neg_add_le_right {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (h : -c + a ≤ b) : a ≤ b + c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ b + c)) (add_comm b c))) (le_add_of_neg_add_le h)

theorem inv_mul_le_right_of_le_mul {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α}
    (h : a ≤ b * c) : c⁻¹ * a ≤ b :=
  inv_mul_le_left_of_le_mul (eq.mp (Eq._oldrec (Eq.refl (a ≤ b * c)) (mul_comm b c)) h)

theorem mul_lt_of_lt_inv_mul {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α}
    (h : b < a⁻¹ * c) : a * b < c :=
  eq.mp (Eq._oldrec (Eq.refl (a * b < a * (a⁻¹ * c))) (mul_inv_cancel_left a c))
    (mul_lt_mul_left' h a)

theorem lt_neg_add_of_add_lt {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (h : a + b < c) : b < -a + c :=
  eq.mp (Eq._oldrec (Eq.refl (-a + (a + b) < -a + c)) (neg_add_cancel_left a b))
    (add_lt_add_left h (-a))

theorem lt_mul_of_inv_mul_lt {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α}
    (h : b⁻¹ * a < c) : a < b * c :=
  eq.mp (Eq._oldrec (Eq.refl (b * (b⁻¹ * a) < b * c)) (mul_inv_cancel_left b a))
    (mul_lt_mul_left' h b)

theorem neg_add_lt_of_lt_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (h : a < b + c) : -b + a < c :=
  eq.mp (Eq._oldrec (Eq.refl (-b + a < -b + (b + c))) (neg_add_cancel_left b c))
    (add_lt_add_left h (-b))

theorem lt_add_of_neg_add_lt_left {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (h : -b + a < c) : a < b + c :=
  lt_add_of_neg_add_lt h

theorem neg_add_lt_left_of_lt_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (h : a < b + c) : -b + a < c :=
  neg_add_lt_of_lt_add h

theorem lt_add_of_neg_add_lt_right {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (h : -c + a < b) : a < b + c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a < b + c)) (add_comm b c))) (lt_add_of_neg_add_lt h)

theorem neg_add_lt_right_of_lt_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (h : a < b + c) : -c + a < b :=
  neg_add_lt_of_lt_add (eq.mp (Eq._oldrec (Eq.refl (a < b + c)) (add_comm b c)) h)

@[simp] theorem neg_neg_iff_pos {α : Type u} [ordered_add_comm_group α] {a : α} : -a < 0 ↔ 0 < a :=
  { mp := pos_of_neg_neg, mpr := neg_neg_of_pos }

@[simp] theorem neg_le_neg_iff {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    -a ≤ -b ↔ b ≤ a :=
  sorry

theorem inv_le' {α : Type u} [ordered_comm_group α] {a : α} {b : α} : a⁻¹ ≤ b ↔ b⁻¹ ≤ a :=
  (fun (this : a⁻¹ ≤ (b⁻¹⁻¹) ↔ b⁻¹ ≤ a) =>
      eq.mp (Eq._oldrec (Eq.refl (a⁻¹ ≤ (b⁻¹⁻¹) ↔ b⁻¹ ≤ a)) (inv_inv b)) this)
    inv_le_inv_iff

theorem le_inv' {α : Type u} [ordered_comm_group α] {a : α} {b : α} : a ≤ (b⁻¹) ↔ b ≤ (a⁻¹) :=
  (fun (this : a⁻¹⁻¹ ≤ (b⁻¹) ↔ b ≤ (a⁻¹)) =>
      eq.mp (Eq._oldrec (Eq.refl (a⁻¹⁻¹ ≤ (b⁻¹) ↔ b ≤ (a⁻¹))) (inv_inv a)) this)
    inv_le_inv_iff

theorem inv_le_iff_one_le_mul {α : Type u} [ordered_comm_group α] {a : α} {b : α} :
    a⁻¹ ≤ b ↔ 1 ≤ b * a :=
  iff.trans (iff.symm (mul_le_mul_iff_right a))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ * a ≤ b * a ↔ 1 ≤ b * a)) (inv_mul_self a)))
      (iff.refl (1 ≤ b * a)))

theorem neg_le_iff_add_nonneg' {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    -a ≤ b ↔ 0 ≤ a + b :=
  iff.trans (iff.symm (add_le_add_iff_left a))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + -a ≤ a + b ↔ 0 ≤ a + b)) (add_neg_self a)))
      (iff.refl (0 ≤ a + b)))

theorem inv_lt_iff_one_lt_mul {α : Type u} [ordered_comm_group α] {a : α} {b : α} :
    a⁻¹ < b ↔ 1 < b * a :=
  iff.trans (iff.symm (mul_lt_mul_iff_right a))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a⁻¹ * a < b * a ↔ 1 < b * a)) (inv_mul_self a)))
      (iff.refl (1 < b * a)))

theorem neg_lt_iff_pos_add' {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    -a < b ↔ 0 < a + b :=
  iff.trans (iff.symm (add_lt_add_iff_left a))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + -a < a + b ↔ 0 < a + b)) (add_neg_self a)))
      (iff.refl (0 < a + b)))

theorem le_neg_iff_add_nonpos {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    a ≤ -b ↔ a + b ≤ 0 :=
  iff.trans (iff.symm (add_le_add_iff_right b))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + b ≤ -b + b ↔ a + b ≤ 0)) (neg_add_self b)))
      (iff.refl (a + b ≤ 0)))

theorem le_inv_iff_mul_le_one' {α : Type u} [ordered_comm_group α] {a : α} {b : α} :
    a ≤ (b⁻¹) ↔ b * a ≤ 1 :=
  iff.trans (iff.symm (mul_le_mul_iff_left b))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a ≤ b * (b⁻¹) ↔ b * a ≤ 1)) (mul_inv_self b)))
      (iff.refl (b * a ≤ 1)))

theorem lt_neg_iff_add_neg {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    a < -b ↔ a + b < 0 :=
  iff.trans (iff.symm (add_lt_add_iff_right b))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + b < -b + b ↔ a + b < 0)) (neg_add_self b)))
      (iff.refl (a + b < 0)))

theorem lt_inv_iff_mul_lt_one' {α : Type u} [ordered_comm_group α] {a : α} {b : α} :
    a < (b⁻¹) ↔ b * a < 1 :=
  iff.trans (iff.symm (mul_lt_mul_iff_left b))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a < b * (b⁻¹) ↔ b * a < 1)) (mul_inv_self b)))
      (iff.refl (b * a < 1)))

@[simp] theorem inv_le_one' {α : Type u} [ordered_comm_group α] {a : α} : a⁻¹ ≤ 1 ↔ 1 ≤ a :=
  (fun (this : a⁻¹ ≤ (1⁻¹) ↔ 1 ≤ a) =>
      eq.mp (Eq._oldrec (Eq.refl (a⁻¹ ≤ (1⁻¹) ↔ 1 ≤ a)) one_inv) this)
    inv_le_inv_iff

@[simp] theorem one_le_inv' {α : Type u} [ordered_comm_group α] {a : α} : 1 ≤ (a⁻¹) ↔ a ≤ 1 :=
  (fun (this : 1⁻¹ ≤ (a⁻¹) ↔ a ≤ 1) =>
      eq.mp (Eq._oldrec (Eq.refl (1⁻¹ ≤ (a⁻¹) ↔ a ≤ 1)) one_inv) this)
    inv_le_inv_iff

theorem inv_le_self {α : Type u} [ordered_comm_group α] {a : α} (h : 1 ≤ a) : a⁻¹ ≤ a :=
  le_trans (iff.mpr inv_le_one' h) h

theorem self_le_inv {α : Type u} [ordered_comm_group α] {a : α} (h : a ≤ 1) : a ≤ (a⁻¹) :=
  le_trans h (iff.mpr one_le_inv' h)

@[simp] theorem neg_lt_neg_iff {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    -a < -b ↔ b < a :=
  sorry

theorem neg_lt_zero {α : Type u} [ordered_add_comm_group α] {a : α} : -a < 0 ↔ 0 < a :=
  (fun (this : -a < -0 ↔ 0 < a) => eq.mp (Eq._oldrec (Eq.refl (-a < -0 ↔ 0 < a)) neg_zero) this)
    neg_lt_neg_iff

theorem one_lt_inv' {α : Type u} [ordered_comm_group α] {a : α} : 1 < (a⁻¹) ↔ a < 1 :=
  (fun (this : 1⁻¹ < (a⁻¹) ↔ a < 1) =>
      eq.mp (Eq._oldrec (Eq.refl (1⁻¹ < (a⁻¹) ↔ a < 1)) one_inv) this)
    inv_lt_inv_iff

theorem neg_lt {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} : -a < b ↔ -b < a :=
  (fun (this : -a < --b ↔ -b < a) =>
      eq.mp (Eq._oldrec (Eq.refl (-a < --b ↔ -b < a)) (neg_neg b)) this)
    neg_lt_neg_iff

theorem lt_neg {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} : a < -b ↔ b < -a :=
  (fun (this : --a < -b ↔ b < -a) =>
      eq.mp (Eq._oldrec (Eq.refl ( --a < -b ↔ b < -a)) (neg_neg a)) this)
    neg_lt_neg_iff

theorem neg_lt_self {α : Type u} [ordered_add_comm_group α] {a : α} (h : 0 < a) : -a < a :=
  has_lt.lt.trans (iff.mpr neg_lt_zero h) h

theorem le_inv_mul_iff_mul_le {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α} :
    b ≤ a⁻¹ * c ↔ a * b ≤ c :=
  (fun (this : a⁻¹ * (a * b) ≤ a⁻¹ * c ↔ a * b ≤ c) =>
      eq.mp (Eq._oldrec (Eq.refl (a⁻¹ * (a * b) ≤ a⁻¹ * c ↔ a * b ≤ c)) (inv_mul_cancel_left a b))
        this)
    (mul_le_mul_iff_left (a⁻¹))

@[simp] theorem inv_mul_le_iff_le_mul {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α} :
    b⁻¹ * a ≤ c ↔ a ≤ b * c :=
  (fun (this : b⁻¹ * a ≤ b⁻¹ * (b * c) ↔ a ≤ b * c) =>
      eq.mp (Eq._oldrec (Eq.refl (b⁻¹ * a ≤ b⁻¹ * (b * c) ↔ a ≤ b * c)) (inv_mul_cancel_left b c))
        this)
    (mul_le_mul_iff_left (b⁻¹))

theorem mul_inv_le_iff_le_mul {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α} :
    a * (c⁻¹) ≤ b ↔ a ≤ b * c :=
  sorry

@[simp] theorem mul_inv_le_iff_le_mul' {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α} :
    a * (b⁻¹) ≤ c ↔ a ≤ b * c :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (a * (b⁻¹) ≤ c ↔ a ≤ b * c)) (Eq.symm (propext inv_mul_le_iff_le_mul))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a * (b⁻¹) ≤ c ↔ b⁻¹ * a ≤ c)) (mul_comm a (b⁻¹))))
      (iff.refl (b⁻¹ * a ≤ c)))

theorem neg_add_le_iff_le_add' {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    -c + a ≤ b ↔ a ≤ b + c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (-c + a ≤ b ↔ a ≤ b + c)) (propext neg_add_le_iff_le_add)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ c + b ↔ a ≤ b + c)) (add_comm c b)))
      (iff.refl (a ≤ b + c)))

@[simp] theorem lt_neg_add_iff_add_lt {α : Type u} [ordered_add_comm_group α] {a : α} {b : α}
    {c : α} : b < -a + c ↔ a + b < c :=
  (fun (this : -a + (a + b) < -a + c ↔ a + b < c) =>
      eq.mp (Eq._oldrec (Eq.refl (-a + (a + b) < -a + c ↔ a + b < c)) (neg_add_cancel_left a b))
        this)
    (add_lt_add_iff_left (-a))

@[simp] theorem inv_mul_lt_iff_lt_mul {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α} :
    b⁻¹ * a < c ↔ a < b * c :=
  (fun (this : b⁻¹ * a < b⁻¹ * (b * c) ↔ a < b * c) =>
      eq.mp (Eq._oldrec (Eq.refl (b⁻¹ * a < b⁻¹ * (b * c) ↔ a < b * c)) (inv_mul_cancel_left b c))
        this)
    (mul_lt_mul_iff_left (b⁻¹))

theorem inv_mul_lt_iff_lt_mul_right {α : Type u} [ordered_comm_group α] {a : α} {b : α} {c : α} :
    c⁻¹ * a < b ↔ a < b * c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (c⁻¹ * a < b ↔ a < b * c)) (propext inv_mul_lt_iff_lt_mul)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a < c * b ↔ a < b * c)) (mul_comm c b)))
      (iff.refl (a < b * c)))

theorem add_neg_le_add_neg_iff {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α}
    {d : α} : a + -b ≤ c + -d ↔ a + d ≤ c + b :=
  sorry

theorem sub_le_sub {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} {d : α}
    (hab : a ≤ b) (hcd : c ≤ d) : a - d ≤ b - c :=
  sorry

theorem sub_lt_sub {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} {d : α}
    (hab : a < b) (hcd : c < d) : a - d < b - c :=
  sorry

@[simp] theorem sub_le_self_iff {α : Type u} [ordered_add_comm_group α] (a : α) {b : α} :
    a - b ≤ a ↔ 0 ≤ b :=
  sorry

theorem sub_le_self {α : Type u} [ordered_add_comm_group α] (a : α) {b : α} : 0 ≤ b → a - b ≤ a :=
  iff.mpr (sub_le_self_iff a)

@[simp] theorem sub_lt_self_iff {α : Type u} [ordered_add_comm_group α] (a : α) {b : α} :
    a - b < a ↔ 0 < b :=
  sorry

theorem sub_lt_self {α : Type u} [ordered_add_comm_group α] (a : α) {b : α} : 0 < b → a - b < a :=
  iff.mpr (sub_lt_self_iff a)

theorem sub_le_sub_iff {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} {d : α} :
    a - b ≤ c - d ↔ a + d ≤ c + b :=
  sorry

@[simp] theorem sub_le_sub_iff_left {α : Type u} [ordered_add_comm_group α] (a : α) {b : α}
    {c : α} : a - b ≤ a - c ↔ c ≤ b :=
  sorry

theorem sub_le_sub_left {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} (h : a ≤ b)
    (c : α) : c - b ≤ c - a :=
  iff.mpr (sub_le_sub_iff_left c) h

@[simp] theorem sub_le_sub_iff_right {α : Type u} [ordered_add_comm_group α] {a : α} {b : α}
    (c : α) : a - c ≤ b - c ↔ a ≤ b :=
  sorry

theorem sub_le_sub_right {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} (h : a ≤ b)
    (c : α) : a - c ≤ b - c :=
  iff.mpr (sub_le_sub_iff_right c) h

@[simp] theorem sub_lt_sub_iff_left {α : Type u} [ordered_add_comm_group α] (a : α) {b : α}
    {c : α} : a - b < a - c ↔ c < b :=
  sorry

theorem sub_lt_sub_left {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} (h : a < b)
    (c : α) : c - b < c - a :=
  iff.mpr (sub_lt_sub_iff_left c) h

@[simp] theorem sub_lt_sub_iff_right {α : Type u} [ordered_add_comm_group α] {a : α} {b : α}
    (c : α) : a - c < b - c ↔ a < b :=
  sorry

theorem sub_lt_sub_right {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} (h : a < b)
    (c : α) : a - c < b - c :=
  iff.mpr (sub_lt_sub_iff_right c) h

@[simp] theorem sub_nonneg {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    0 ≤ a - b ↔ b ≤ a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ a - b ↔ b ≤ a)) (Eq.symm (sub_self a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a - a ≤ a - b ↔ b ≤ a)) (propext (sub_le_sub_iff_left a))))
      (iff.refl (b ≤ a)))

theorem sub_nonneg_of_le {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    b ≤ a → 0 ≤ a - b :=
  iff.mpr sub_nonneg

@[simp] theorem sub_nonpos {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    a - b ≤ 0 ↔ a ≤ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a - b ≤ 0 ↔ a ≤ b)) (Eq.symm (sub_self b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a - b ≤ b - b ↔ a ≤ b)) (propext (sub_le_sub_iff_right b))))
      (iff.refl (a ≤ b)))

theorem le_of_sub_nonpos {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    a - b ≤ 0 → a ≤ b :=
  iff.mp sub_nonpos

@[simp] theorem sub_pos {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    0 < a - b ↔ b < a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 < a - b ↔ b < a)) (Eq.symm (sub_self a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a - a < a - b ↔ b < a)) (propext (sub_lt_sub_iff_left a))))
      (iff.refl (b < a)))

theorem lt_of_sub_pos {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} : 0 < a - b → b < a :=
  iff.mp sub_pos

@[simp] theorem sub_lt_zero {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} :
    a - b < 0 ↔ a < b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a - b < 0 ↔ a < b)) (Eq.symm (sub_self b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a - b < b - b ↔ a < b)) (propext (sub_lt_sub_iff_right b))))
      (iff.refl (a < b)))

theorem lt_of_sub_neg {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} : a - b < 0 → a < b :=
  iff.mp sub_lt_zero

theorem le_sub_iff_add_le' {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    b ≤ c - a ↔ a + b ≤ c :=
  sorry

theorem le_sub_iff_add_le {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a ≤ c - b ↔ a + b ≤ c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ c - b ↔ a + b ≤ c)) (propext le_sub_iff_add_le')))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b + a ≤ c ↔ a + b ≤ c)) (add_comm b a)))
      (iff.refl (a + b ≤ c)))

theorem add_le_of_le_sub_right {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a ≤ c - b → a + b ≤ c :=
  iff.mp le_sub_iff_add_le

theorem sub_le_iff_le_add' {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a - b ≤ c ↔ a ≤ b + c :=
  sorry

theorem le_sub_left_of_add_le {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a + b ≤ c → b ≤ c - a :=
  iff.mpr le_sub_iff_add_le'

theorem sub_le_iff_le_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a - c ≤ b ↔ a ≤ b + c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a - c ≤ b ↔ a ≤ b + c)) (propext sub_le_iff_le_add')))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ c + b ↔ a ≤ b + c)) (add_comm c b)))
      (iff.refl (a ≤ b + c)))

@[simp] theorem neg_le_sub_iff_le_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α}
    {c : α} : -b ≤ a - c ↔ c ≤ a + b :=
  iff.trans le_sub_iff_add_le neg_add_le_iff_le_add'

theorem neg_le_sub_iff_le_add' {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    -a ≤ b - c ↔ c ≤ a + b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (-a ≤ b - c ↔ c ≤ a + b)) (propext neg_le_sub_iff_le_add)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (c ≤ b + a ↔ c ≤ a + b)) (add_comm b a)))
      (iff.refl (c ≤ a + b)))

theorem sub_le {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a - b ≤ c ↔ a - c ≤ b :=
  iff.trans sub_le_iff_le_add' (iff.symm sub_le_iff_le_add)

theorem le_sub {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a ≤ b - c ↔ c ≤ b - a :=
  iff.trans le_sub_iff_add_le' (iff.symm le_sub_iff_add_le)

theorem lt_sub_iff_add_lt' {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    b < c - a ↔ a + b < c :=
  sorry

theorem add_lt_of_lt_sub_left {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    b < c - a → a + b < c :=
  iff.mp lt_sub_iff_add_lt'

theorem lt_sub_iff_add_lt {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a < c - b ↔ a + b < c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a < c - b ↔ a + b < c)) (propext lt_sub_iff_add_lt')))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b + a < c ↔ a + b < c)) (add_comm b a)))
      (iff.refl (a + b < c)))

theorem lt_sub_right_of_add_lt {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a + b < c → a < c - b :=
  iff.mpr lt_sub_iff_add_lt

theorem sub_lt_iff_lt_add' {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a - b < c ↔ a < b + c :=
  sorry

theorem lt_add_of_sub_left_lt {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a - b < c → a < b + c :=
  iff.mp sub_lt_iff_lt_add'

theorem sub_lt_iff_lt_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a - c < b ↔ a < b + c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a - c < b ↔ a < b + c)) (propext sub_lt_iff_lt_add')))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a < c + b ↔ a < b + c)) (add_comm c b)))
      (iff.refl (a < b + c)))

theorem sub_right_lt_of_lt_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a < b + c → a - c < b :=
  iff.mpr sub_lt_iff_lt_add

@[simp] theorem neg_lt_sub_iff_lt_add {α : Type u} [ordered_add_comm_group α] {a : α} {b : α}
    {c : α} : -b < a - c ↔ c < a + b :=
  iff.trans lt_sub_iff_add_lt neg_add_lt_iff_lt_add_right

theorem neg_lt_sub_iff_lt_add' {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    -a < b - c ↔ c < a + b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (-a < b - c ↔ c < a + b)) (propext neg_lt_sub_iff_lt_add)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (c < b + a ↔ c < a + b)) (add_comm b a)))
      (iff.refl (c < a + b)))

theorem sub_lt {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a - b < c ↔ a - c < b :=
  iff.trans sub_lt_iff_lt_add' (iff.symm sub_lt_iff_lt_add)

theorem lt_sub {α : Type u} [ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    a < b - c ↔ c < b - a :=
  iff.trans lt_sub_iff_add_lt' (iff.symm lt_sub_iff_add_lt)

/-!

### Linearly ordered commutative groups

-/

/-- A linearly ordered additive commutative group is an
additive commutative group with a linear order in which
addition is monotone. -/
class linear_ordered_add_comm_group (α : Type u) extends add_comm_group α, linear_order α where
  add_le_add_left : ∀ (a b : α), a ≤ b → ∀ (c : α), c + a ≤ c + b

/-- A linearly ordered commutative group is a
commutative group with a linear order in which
multiplication is monotone. -/
class linear_ordered_comm_group (α : Type u) extends linear_order α, comm_group α where
  mul_le_mul_left : ∀ (a b : α), a ≤ b → ∀ (c : α), c * a ≤ c * b

protected instance linear_ordered_comm_group.to_ordered_comm_group (α : Type u)
    [s : linear_ordered_comm_group α] : ordered_comm_group α :=
  ordered_comm_group.mk linear_ordered_comm_group.mul linear_ordered_comm_group.mul_assoc
    linear_ordered_comm_group.one linear_ordered_comm_group.one_mul
    linear_ordered_comm_group.mul_one linear_ordered_comm_group.inv linear_ordered_comm_group.div
    linear_ordered_comm_group.mul_left_inv linear_ordered_comm_group.mul_comm
    linear_ordered_comm_group.le linear_ordered_comm_group.lt linear_ordered_comm_group.le_refl
    linear_ordered_comm_group.le_trans linear_ordered_comm_group.le_antisymm
    linear_ordered_comm_group.mul_le_mul_left

protected instance linear_ordered_add_comm_group.to_linear_ordered_cancel_add_comm_monoid
    {α : Type u} [linear_ordered_add_comm_group α] : linear_ordered_cancel_add_comm_monoid α :=
  linear_ordered_cancel_add_comm_monoid.mk linear_ordered_add_comm_group.add
    linear_ordered_add_comm_group.add_assoc sorry linear_ordered_add_comm_group.zero
    linear_ordered_add_comm_group.zero_add linear_ordered_add_comm_group.add_zero
    linear_ordered_add_comm_group.add_comm sorry linear_ordered_add_comm_group.le
    linear_ordered_add_comm_group.lt linear_ordered_add_comm_group.le_refl
    linear_ordered_add_comm_group.le_trans linear_ordered_add_comm_group.le_antisymm
    linear_ordered_add_comm_group.add_le_add_left sorry linear_ordered_add_comm_group.le_total
    linear_ordered_add_comm_group.decidable_le linear_ordered_add_comm_group.decidable_eq
    linear_ordered_add_comm_group.decidable_lt

theorem linear_ordered_add_comm_group.add_lt_add_left {α : Type u} [linear_ordered_add_comm_group α]
    (a : α) (b : α) (h : a < b) (c : α) : c + a < c + b :=
  ordered_add_comm_group.add_lt_add_left a b h c

theorem le_of_forall_pos_le_add {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α}
    [densely_ordered α] (h : ∀ (ε : α), 0 < ε → a ≤ b + ε) : a ≤ b :=
  le_of_forall_le_of_dense
    fun (c : α) (hc : c > b) =>
      trans_rel_left LessEq (h (c - b) (sub_pos_of_lt hc)) (add_sub_cancel'_right b c)

theorem min_neg_neg {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) :
    min (-a) (-b) = -max a b :=
  Eq.symm (monotone.map_max fun (a b : α) => neg_le_neg)

theorem max_neg_neg {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) :
    max (-a) (-b) = -min a b :=
  Eq.symm (monotone.map_min fun (a b : α) => neg_le_neg)

theorem min_sub_sub_right {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) :
    min (a - c) (b - c) = min a b - c :=
  sorry

theorem max_sub_sub_right {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) :
    max (a - c) (b - c) = max a b - c :=
  sorry

theorem min_sub_sub_left {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) :
    min (a - b) (a - c) = a - max b c :=
  sorry

theorem max_sub_sub_left {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) :
    max (a - b) (a - c) = a - min b c :=
  sorry

theorem max_zero_sub_eq_self {α : Type u} [linear_ordered_add_comm_group α] (a : α) :
    max a 0 - max (-a) 0 = a :=
  sorry

/-- `abs a` is the absolute value of `a`. -/
def abs {α : Type u} [linear_ordered_add_comm_group α] (a : α) : α := max a (-a)

theorem abs_of_nonneg {α : Type u} [linear_ordered_add_comm_group α] {a : α} (h : 0 ≤ a) :
    abs a = a :=
  max_eq_left (has_le.le.trans (iff.mpr neg_nonpos h) h)

theorem abs_of_pos {α : Type u} [linear_ordered_add_comm_group α] {a : α} (h : 0 < a) : abs a = a :=
  abs_of_nonneg (has_lt.lt.le h)

theorem abs_of_nonpos {α : Type u} [linear_ordered_add_comm_group α] {a : α} (h : a ≤ 0) :
    abs a = -a :=
  max_eq_right (has_le.le.trans h (iff.mpr neg_nonneg h))

theorem abs_of_neg {α : Type u} [linear_ordered_add_comm_group α] {a : α} (h : a < 0) :
    abs a = -a :=
  abs_of_nonpos (has_lt.lt.le h)

@[simp] theorem abs_zero {α : Type u} [linear_ordered_add_comm_group α] : abs 0 = 0 :=
  abs_of_nonneg le_rfl

@[simp] theorem abs_neg {α : Type u} [linear_ordered_add_comm_group α] (a : α) : abs (-a) = abs a :=
  sorry

@[simp] theorem abs_pos {α : Type u} [linear_ordered_add_comm_group α] {a : α} :
    0 < abs a ↔ a ≠ 0 :=
  sorry

theorem abs_pos_of_pos {α : Type u} [linear_ordered_add_comm_group α] {a : α} (h : 0 < a) :
    0 < abs a :=
  iff.mpr abs_pos (ne.symm (has_lt.lt.ne h))

theorem abs_pos_of_neg {α : Type u} [linear_ordered_add_comm_group α] {a : α} (h : a < 0) :
    0 < abs a :=
  iff.mpr abs_pos (has_lt.lt.ne h)

theorem abs_sub {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) :
    abs (a - b) = abs (b - a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (abs (a - b) = abs (b - a))) (Eq.symm (neg_sub b a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (abs (-(b - a)) = abs (b - a))) (abs_neg (b - a))))
      (Eq.refl (abs (b - a))))

theorem abs_le' {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} :
    abs a ≤ b ↔ a ≤ b ∧ -a ≤ b :=
  max_le_iff

theorem abs_le {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} :
    abs a ≤ b ↔ -b ≤ a ∧ a ≤ b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (abs a ≤ b ↔ -b ≤ a ∧ a ≤ b)) (propext abs_le')))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ b ∧ -a ≤ b ↔ -b ≤ a ∧ a ≤ b)) (propext and.comm)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (-a ≤ b ∧ a ≤ b ↔ -b ≤ a ∧ a ≤ b)) (propext neg_le)))
        (iff.refl (-b ≤ a ∧ a ≤ b))))

theorem le_abs_self {α : Type u} [linear_ordered_add_comm_group α] (a : α) : a ≤ abs a :=
  le_max_left a (-a)

theorem neg_le_abs_self {α : Type u} [linear_ordered_add_comm_group α] (a : α) : -a ≤ abs a :=
  le_max_right a (-a)

theorem abs_nonneg {α : Type u} [linear_ordered_add_comm_group α] (a : α) : 0 ≤ abs a :=
  or.elim (le_total 0 a) (fun (h : 0 ≤ a) => has_le.le.trans h (le_abs_self a))
    fun (h : a ≤ 0) => has_le.le.trans (iff.mpr neg_nonneg h) (neg_le_abs_self a)

@[simp] theorem abs_abs {α : Type u} [linear_ordered_add_comm_group α] (a : α) :
    abs (abs a) = abs a :=
  abs_of_nonneg (abs_nonneg a)

@[simp] theorem abs_eq_zero {α : Type u} [linear_ordered_add_comm_group α] {a : α} :
    abs a = 0 ↔ a = 0 :=
  iff.mp not_iff_not
    (iff.trans ne_comm (iff.trans (iff.symm (has_le.le.lt_iff_ne (abs_nonneg a))) abs_pos))

@[simp] theorem abs_nonpos_iff {α : Type u} [linear_ordered_add_comm_group α] {a : α} :
    abs a ≤ 0 ↔ a = 0 :=
  iff.trans (has_le.le.le_iff_eq (abs_nonneg a)) abs_eq_zero

theorem abs_lt {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} :
    abs a < b ↔ -b < a ∧ a < b :=
  iff.trans max_lt_iff
    (iff.trans and.comm
      (eq.mpr (id (Eq._oldrec (Eq.refl (-a < b ∧ a < b ↔ -b < a ∧ a < b)) (propext neg_lt)))
        (iff.refl (-b < a ∧ a < b))))

theorem lt_abs {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} :
    a < abs b ↔ a < b ∨ a < -b :=
  lt_max_iff

theorem max_sub_min_eq_abs' {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) :
    max a b - min a b = abs (a - b) :=
  sorry

theorem max_sub_min_eq_abs {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) :
    max a b - min a b = abs (b - a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (max a b - min a b = abs (b - a))) (abs_sub b a)))
    (max_sub_min_eq_abs' a b)

theorem abs_add {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) :
    abs (a + b) ≤ abs a + abs b :=
  sorry

theorem abs_sub_le_iff {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    abs (a - b) ≤ c ↔ a - b ≤ c ∧ b - a ≤ c :=
  sorry

theorem abs_sub_lt_iff {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} {c : α} :
    abs (a - b) < c ↔ a - b < c ∧ b - a < c :=
  sorry

theorem sub_le_of_abs_sub_le_left {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α}
    {c : α} (h : abs (a - b) ≤ c) : b - c ≤ a :=
  iff.mp sub_le (and.right (iff.mp abs_sub_le_iff h))

theorem sub_le_of_abs_sub_le_right {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α}
    {c : α} (h : abs (a - b) ≤ c) : a - c ≤ b :=
  sub_le_of_abs_sub_le_left (abs_sub a b ▸ h)

theorem sub_lt_of_abs_sub_lt_left {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α}
    {c : α} (h : abs (a - b) < c) : b - c < a :=
  iff.mp sub_lt (and.right (iff.mp abs_sub_lt_iff h))

theorem sub_lt_of_abs_sub_lt_right {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α}
    {c : α} (h : abs (a - b) < c) : a - c < b :=
  sub_lt_of_abs_sub_lt_left (abs_sub a b ▸ h)

theorem abs_sub_abs_le_abs_sub {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) :
    abs a - abs b ≤ abs (a - b) :=
  sorry

theorem abs_abs_sub_abs_le_abs_sub {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) :
    abs (abs a - abs b) ≤ abs (a - b) :=
  sorry

theorem abs_eq {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} (hb : 0 ≤ b) :
    abs a = b ↔ a = b ∨ a = -b :=
  sorry

theorem abs_le_max_abs_abs {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} {c : α}
    (hab : a ≤ b) (hbc : b ≤ c) : abs b ≤ max (abs a) (abs c) :=
  sorry

theorem abs_le_abs {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α} (h₀ : a ≤ b)
    (h₁ : -a ≤ b) : abs a ≤ abs b :=
  has_le.le.trans (iff.mpr abs_le' { left := h₀, right := h₁ }) (le_abs_self b)

theorem abs_max_sub_max_le_abs {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α)
    (c : α) : abs (max a c - max b c) ≤ abs (a - b) :=
  sorry

theorem eq_zero_of_neg_eq {α : Type u} [linear_ordered_add_comm_group α] {a : α} (h : -a = a) :
    a = 0 :=
  sorry

theorem eq_of_abs_sub_eq_zero {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α}
    (h : abs (a - b) = 0) : a = b :=
  iff.mp sub_eq_zero (iff.mp abs_eq_zero h)

theorem abs_by_cases {α : Type u} [linear_ordered_add_comm_group α] (P : α → Prop) {a : α}
    (h1 : P a) (h2 : P (-a)) : P (abs a) :=
  sup_ind a (-a) h1 h2

theorem abs_sub_le {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) :
    abs (a - c) ≤ abs (a - b) + abs (b - c) :=
  sorry

theorem abs_add_three {α : Type u} [linear_ordered_add_comm_group α] (a : α) (b : α) (c : α) :
    abs (a + b + c) ≤ abs a + abs b + abs c :=
  has_le.le.trans (abs_add (a + b) c) (add_le_add_right (abs_add a b) (abs c))

theorem dist_bdd_within_interval {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α}
    {lb : α} {ub : α} (hal : lb ≤ a) (hau : a ≤ ub) (hbl : lb ≤ b) (hbu : b ≤ ub) :
    abs (a - b) ≤ ub - lb :=
  iff.mpr abs_sub_le_iff { left := sub_le_sub hau hbl, right := sub_le_sub hbu hal }

theorem eq_of_abs_sub_nonpos {α : Type u} [linear_ordered_add_comm_group α] {a : α} {b : α}
    (h : abs (a - b) ≤ 0) : a = b :=
  eq_of_abs_sub_eq_zero (le_antisymm h (abs_nonneg (a - b)))

theorem exists_zero_lt {α : Type u} [linear_ordered_add_comm_group α] [nontrivial α] :
    ∃ (a : α), 0 < a :=
  sorry

protected instance linear_ordered_add_comm_group.to_no_top_order {α : Type u}
    [linear_ordered_add_comm_group α] [nontrivial α] : no_top_order α :=
  no_top_order.mk
    (Exists.dcases_on exists_zero_lt
      fun (y : α) (hy : 0 < y) (a : α) => Exists.intro (a + y) (lt_add_of_pos_right a hy))

protected instance linear_ordered_add_comm_group.to_no_bot_order {α : Type u}
    [linear_ordered_add_comm_group α] [nontrivial α] : no_bot_order α :=
  no_bot_order.mk
    (Exists.dcases_on exists_zero_lt
      fun (y : α) (hy : 0 < y) (a : α) => Exists.intro (a - y) (sub_lt_self a hy))

/-- This is not so much a new structure as a construction mechanism
  for ordered groups, by specifying only the "positive cone" of the group. -/
class nonneg_add_comm_group (α : Type u_1) extends add_comm_group α where
  nonneg : α → Prop
  pos : α → Prop
  pos_iff :
    autoParam (∀ (a : α), pos a ↔ nonneg a ∧ ¬nonneg (-a))
      (Lean.Syntax.ident Lean.SourceInfo.none (String.toSubstring "Mathlib.order_laws_tac")
        (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "order_laws_tac") [])
  zero_nonneg : nonneg 0
  add_nonneg : ∀ {a b : α}, nonneg a → nonneg b → nonneg (a + b)
  nonneg_antisymm : ∀ {a : α}, nonneg a → nonneg (-a) → a = 0

namespace nonneg_add_comm_group

protected instance to_ordered_add_comm_group {α : Type u} [s : nonneg_add_comm_group α] :
    ordered_add_comm_group α :=
  ordered_add_comm_group.mk add add_assoc zero zero_add add_zero neg sub add_left_neg add_comm
    (fun (a b : α) => nonneg (b - a)) (fun (a b : α) => pos (b - a)) sorry sorry sorry sorry

theorem nonneg_def {α : Type u} [s : nonneg_add_comm_group α] {a : α} : nonneg a ↔ 0 ≤ a := sorry

theorem pos_def {α : Type u} [s : nonneg_add_comm_group α] {a : α} : pos a ↔ 0 < a := sorry

theorem not_zero_pos {α : Type u} [s : nonneg_add_comm_group α] : ¬pos 0 :=
  mt (iff.mp pos_def) (lt_irrefl 0)

theorem zero_lt_iff_nonneg_nonneg {α : Type u} [s : nonneg_add_comm_group α] {a : α} :
    0 < a ↔ nonneg a ∧ ¬nonneg (-a) :=
  iff.trans (iff.symm pos_def) (pos_iff a)

theorem nonneg_total_iff {α : Type u} [s : nonneg_add_comm_group α] :
    (∀ (a : α), nonneg a ∨ nonneg (-a)) ↔ ∀ (a b : α), a ≤ b ∨ b ≤ a :=
  sorry

/--
A `nonneg_add_comm_group` is a `linear_ordered_add_comm_group`
if `nonneg` is total and decidable.
-/
def to_linear_ordered_add_comm_group {α : Type u} [s : nonneg_add_comm_group α]
    [decidable_pred nonneg] (nonneg_total : ∀ (a : α), nonneg a ∨ nonneg (-a)) :
    linear_ordered_add_comm_group α :=
  linear_ordered_add_comm_group.mk ordered_add_comm_group.add sorry ordered_add_comm_group.zero
    sorry sorry ordered_add_comm_group.neg ordered_add_comm_group.sub sorry sorry LessEq Less sorry
    sorry sorry sorry (fun (a b : α) => _inst_1 (b - a))
    (linear_order.decidable_eq._default LessEq Less sorry sorry sorry
      fun (a b : α) => _inst_1 (b - a))
    (fun (a b : α) => Mathlib.decidable_lt_of_decidable_le a b) sorry

end nonneg_add_comm_group

namespace order_dual

protected instance ordered_add_comm_group {α : Type u} [ordered_add_comm_group α] :
    ordered_add_comm_group (order_dual α) :=
  ordered_add_comm_group.mk ordered_add_comm_monoid.add sorry ordered_add_comm_monoid.zero sorry
    sorry add_comm_group.neg (fun (a b : order_dual α) => a - b) sorry sorry
    ordered_add_comm_monoid.le ordered_add_comm_monoid.lt sorry sorry sorry sorry

protected instance linear_ordered_add_comm_group {α : Type u} [linear_ordered_add_comm_group α] :
    linear_ordered_add_comm_group (order_dual α) :=
  linear_ordered_add_comm_group.mk add_comm_group.add sorry add_comm_group.zero sorry sorry
    add_comm_group.neg add_comm_group.sub sorry sorry linear_order.le linear_order.lt sorry sorry
    sorry sorry linear_order.decidable_le linear_order.decidable_eq linear_order.decidable_lt sorry

end order_dual

namespace prod

protected instance ordered_add_comm_group {G : Type u_1} {H : Type u_2} [ordered_add_comm_group G]
    [ordered_add_comm_group H] : ordered_add_comm_group (G × H) :=
  ordered_add_comm_group.mk add_comm_group.add sorry add_comm_group.zero sorry sorry
    add_comm_group.neg add_comm_group.sub sorry sorry partial_order.le partial_order.lt sorry sorry
    sorry sorry

end prod

protected instance multiplicative.ordered_comm_group {α : Type u} [ordered_add_comm_group α] :
    ordered_comm_group (multiplicative α) :=
  ordered_comm_group.mk comm_group.mul sorry comm_group.one sorry sorry comm_group.inv
    comm_group.div sorry sorry ordered_comm_monoid.le ordered_comm_monoid.lt sorry sorry sorry sorry

protected instance additive.ordered_add_comm_group {α : Type u} [ordered_comm_group α] :
    ordered_add_comm_group (additive α) :=
  ordered_add_comm_group.mk add_comm_group.add sorry add_comm_group.zero sorry sorry
    add_comm_group.neg add_comm_group.sub sorry sorry ordered_add_comm_monoid.le
    ordered_add_comm_monoid.lt sorry sorry sorry sorry

end Mathlib


/-
Copyright (c) 2022 Rémy Degenne. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Rémy Degenne, Kexing Ying

! This file was ported from Lean 3 source module measure_theory.function.conditional_expectation.real
! leanprover-community/mathlib commit b2ff9a3d7a15fd5b0f060b135421d6a89a999c2f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.MeasureTheory.Function.ConditionalExpectation.Indicator
import Mathbin.MeasureTheory.Function.UniformIntegrable
import Mathbin.MeasureTheory.Decomposition.RadonNikodym

/-!

# Conditional expectation of real-valued functions

This file proves some results regarding the conditional expectation of real-valued functions.

## Main results

* `measure_theory.rn_deriv_ae_eq_condexp`: the conditional expectation `μ[f | m]` is equal to the
  Radon-Nikodym derivative of `fμ` restricted on `m` with respect to `μ` restricted on `m`.
* `measure_theory.integrable.uniform_integrable_condexp`: the conditional expectation of a function
  form a uniformly integrable class.
* `measure_theory.condexp_strongly_measurable_mul`: the pull-out property of the conditional
  expectation.

-/

noncomputable section

open TopologicalSpace MeasureTheory.lp Filter ContinuousLinearMap

open NNReal ENNReal Topology BigOperators MeasureTheory

namespace MeasureTheory

variable {α : Type _} {m m0 : MeasurableSpace α} {μ : Measure α}

theorem rnDeriv_ae_eq_condexp {hm : m ≤ m0} [hμm : SigmaFinite (μ.trim hm)] {f : α → ℝ}
    (hf : Integrable f μ) :
    SignedMeasure.rnDeriv ((μ.withDensityᵥ f).trim hm) (μ.trim hm) =ᵐ[μ] μ[f|m] :=
  by
  refine' ae_eq_condexp_of_forall_set_integral_eq hm hf _ _ _
  ·
    exact fun _ _ _ =>
      (integrable_of_integrable_trim hm
          (signed_measure.integrable_rn_deriv ((μ.with_densityᵥ f).trim hm)
            (μ.trim hm))).IntegrableOn
  · intro s hs hlt
    conv_rhs =>
      rw [← hf.with_densityᵥ_trim_eq_integral hm hs, ←
        signed_measure.with_densityᵥ_rn_deriv_eq ((μ.with_densityᵥ f).trim hm) (μ.trim hm)
          (hf.with_densityᵥ_trim_absolutely_continuous hm)]
    rw [with_densityᵥ_apply
        (signed_measure.integrable_rn_deriv ((μ.with_densityᵥ f).trim hm) (μ.trim hm)) hs,
      ← set_integral_trim hm _ hs]
    exact (signed_measure.measurable_rn_deriv _ _).StronglyMeasurable
  ·
    exact
      strongly_measurable.ae_strongly_measurable'
        (signed_measure.measurable_rn_deriv _ _).StronglyMeasurable
#align measure_theory.rn_deriv_ae_eq_condexp MeasureTheory.rnDeriv_ae_eq_condexp

-- TODO: the following couple of lemmas should be generalized and proved using Jensen's inequality
-- for the conditional expectation (not in mathlib yet) .
theorem snorm_one_condexp_le_snorm (f : α → ℝ) : snorm (μ[f|m]) 1 μ ≤ snorm f 1 μ :=
  by
  by_cases hf : integrable f μ
  swap;
  · rw [condexp_undef hf, snorm_zero]
    exact zero_le _
  by_cases hm : m ≤ m0
  swap;
  · rw [condexp_of_not_le hm, snorm_zero]
    exact zero_le _
  by_cases hsig : sigma_finite (μ.trim hm)
  swap;
  · rw [condexp_of_not_sigma_finite hm hsig, snorm_zero]
    exact zero_le _
  calc
    snorm (μ[f|m]) 1 μ ≤ snorm (μ[|f||m]) 1 μ :=
      by
      refine' snorm_mono_ae _
      filter_upwards [@condexp_mono _ m m0 _ _ _ _ _ _ _ _ hf hf.abs
          (@ae_of_all _ m0 _ μ (fun x => le_abs_self (f x) : ∀ x, f x ≤ |f x|)),
        eventually_le.trans (condexp_neg f).symm.le
          (@condexp_mono _ m m0 _ _ _ _ _ _ _ _ hf.neg hf.abs
            (@ae_of_all _ m0 _ μ
              (fun x => neg_le_abs_self (f x) : ∀ x, -f x ≤ |f x|)))]with x hx₁ hx₂
      exact abs_le_abs hx₁ hx₂
    _ = snorm f 1 μ :=
      by
      rw [snorm_one_eq_lintegral_nnnorm, snorm_one_eq_lintegral_nnnorm, ←
        ENNReal.toReal_eq_toReal (ne_of_lt integrable_condexp.2) (ne_of_lt hf.2), ←
        integral_norm_eq_lintegral_nnnorm
          (strongly_measurable_condexp.mono hm).AeStronglyMeasurable,
        ← integral_norm_eq_lintegral_nnnorm hf.1]
      simp_rw [Real.norm_eq_abs]
      rw [← @integral_condexp _ _ _ _ _ m m0 μ _ hm hsig hf.abs]
      refine' integral_congr_ae _
      have : 0 ≤ᵐ[μ] μ[|f||m] := by
        rw [← @condexp_zero α ℝ _ _ _ m m0 μ]
        exact
          condexp_mono (integrable_zero _ _ _) hf.abs
            (@ae_of_all _ m0 _ μ (fun x => abs_nonneg (f x) : ∀ x, 0 ≤ |f x|))
      filter_upwards [this]with x hx
      exact abs_eq_self.2 hx

#align measure_theory.snorm_one_condexp_le_snorm MeasureTheory.snorm_one_condexp_le_snorm

theorem integral_abs_condexp_le (f : α → ℝ) : (∫ x, |(μ[f|m]) x| ∂μ) ≤ ∫ x, |f x| ∂μ :=
  by
  by_cases hm : m ≤ m0
  swap
  · simp_rw [condexp_of_not_le hm, Pi.zero_apply, abs_zero, integral_zero]
    exact integral_nonneg fun x => abs_nonneg _
  by_cases hfint : integrable f μ
  swap
  · simp only [condexp_undef hfint, Pi.zero_apply, abs_zero, integral_const, Algebra.id.smul_eq_mul,
      MulZeroClass.mul_zero]
    exact integral_nonneg fun x => abs_nonneg _
  rw [integral_eq_lintegral_of_nonneg_ae, integral_eq_lintegral_of_nonneg_ae]
  · rw [ENNReal.toReal_le_toReal] <;> simp_rw [← Real.norm_eq_abs, ofReal_norm_eq_coe_nnnorm]
    · rw [← snorm_one_eq_lintegral_nnnorm, ← snorm_one_eq_lintegral_nnnorm]
      exact snorm_one_condexp_le_snorm _
    · exact ne_of_lt integrable_condexp.2
    · exact ne_of_lt hfint.2
  · exact eventually_of_forall fun x => abs_nonneg _
  · simp_rw [← Real.norm_eq_abs]
    exact hfint.1.norm
  · exact eventually_of_forall fun x => abs_nonneg _
  · simp_rw [← Real.norm_eq_abs]
    exact (strongly_measurable_condexp.mono hm).AeStronglyMeasurable.norm
#align measure_theory.integral_abs_condexp_le MeasureTheory.integral_abs_condexp_le

theorem set_integral_abs_condexp_le {s : Set α} (hs : measurable_set[m] s) (f : α → ℝ) :
    (∫ x in s, |(μ[f|m]) x| ∂μ) ≤ ∫ x in s, |f x| ∂μ :=
  by
  by_cases hnm : m ≤ m0
  swap
  · simp_rw [condexp_of_not_le hnm, Pi.zero_apply, abs_zero, integral_zero]
    exact integral_nonneg fun x => abs_nonneg _
  by_cases hfint : integrable f μ
  swap
  · simp only [condexp_undef hfint, Pi.zero_apply, abs_zero, integral_const, Algebra.id.smul_eq_mul,
      MulZeroClass.mul_zero]
    exact integral_nonneg fun x => abs_nonneg _
  have : (∫ x in s, |(μ[f|m]) x| ∂μ) = ∫ x, |(μ[s.indicator f|m]) x| ∂μ :=
    by
    rw [← integral_indicator]
    swap
    · exact hnm _ hs
    refine' integral_congr_ae _
    have : (fun x => |(μ[s.indicator f|m]) x|) =ᵐ[μ] fun x => |s.indicator (μ[f|m]) x| :=
      eventually_eq.fun_comp (condexp_indicator hfint hs) _
    refine' eventually_eq.trans (eventually_of_forall fun x => _) this.symm
    rw [← Real.norm_eq_abs, norm_indicator_eq_indicator_norm]
    rfl
  rw [this, ← integral_indicator]
  swap
  · exact hnm _ hs
  refine'
    (integral_abs_condexp_le _).trans
      (le_of_eq <| integral_congr_ae <| eventually_of_forall fun x => _)
  rw [← Real.norm_eq_abs, norm_indicator_eq_indicator_norm]
  rfl
#align measure_theory.set_integral_abs_condexp_le MeasureTheory.set_integral_abs_condexp_le

/-- If the real valued function `f` is bounded almost everywhere by `R`, then so is its conditional
expectation. -/
theorem ae_bdd_condexp_of_ae_bdd {R : ℝ≥0} {f : α → ℝ} (hbdd : ∀ᵐ x ∂μ, |f x| ≤ R) :
    ∀ᵐ x ∂μ, |(μ[f|m]) x| ≤ R := by
  by_cases hnm : m ≤ m0
  swap
  · simp_rw [condexp_of_not_le hnm, Pi.zero_apply, abs_zero]
    refine' eventually_of_forall fun x => R.coe_nonneg
  by_cases hfint : integrable f μ
  swap
  · simp_rw [condexp_undef hfint]
    filter_upwards [hbdd]with x hx
    rw [Pi.zero_apply, abs_zero]
    exact (abs_nonneg _).trans hx
  by_contra h
  change μ _ ≠ 0 at h
  simp only [← zero_lt_iff, Set.compl_def, Set.mem_setOf_eq, not_le] at h
  suffices (μ { x | ↑R < |(μ[f|m]) x| }).toReal * ↑R < (μ { x | ↑R < |(μ[f|m]) x| }).toReal * ↑R by
    exact this.ne rfl
  refine' lt_of_lt_of_le (set_integral_gt_gt R.coe_nonneg _ _ h.ne.symm) _
  · simp_rw [← Real.norm_eq_abs]
    exact (strongly_measurable_condexp.mono hnm).Measurable.norm
  · exact integrable_condexp.abs.integrable_on
  refine' (set_integral_abs_condexp_le _ _).trans _
  · simp_rw [← Real.norm_eq_abs]
    exact
      @measurableSet_lt _ _ _ _ _ m _ _ _ _ _ measurable_const
        strongly_measurable_condexp.norm.measurable
  simp only [← smul_eq_mul, ← set_integral_const, NNReal.val_eq_coe, IsROrC.coe_real_eq_id, id.def]
  refine' set_integral_mono_ae hfint.abs.integrable_on _ _
  · refine'
      ⟨ae_strongly_measurable_const,
        lt_of_le_of_lt _
          (integrable_condexp.integrable_on : integrable_on (μ[f|m]) { x | ↑R < |(μ[f|m]) x| } μ).2⟩
    refine'
      set_lintegral_mono (Measurable.nnnorm _).coe_nNReal_eNNReal
        (strongly_measurable_condexp.mono hnm).Measurable.nnnorm.coe_nNReal_eNNReal fun x hx => _
    · exact measurable_const
    · rw [ENNReal.coe_le_coe, Real.nnnorm_of_nonneg R.coe_nonneg]
      exact Subtype.mk_le_mk.2 (le_of_lt hx)
  · exact hbdd
#align measure_theory.ae_bdd_condexp_of_ae_bdd MeasureTheory.ae_bdd_condexp_of_ae_bdd

/-- Given a integrable function `g`, the conditional expectations of `g` with respect to
a sequence of sub-σ-algebras is uniformly integrable. -/
theorem Integrable.uniformIntegrableCondexp {ι : Type _} [IsFiniteMeasure μ] {g : α → ℝ}
    (hint : Integrable g μ) {ℱ : ι → MeasurableSpace α} (hℱ : ∀ i, ℱ i ≤ m0) :
    UniformIntegrable (fun i => μ[g|ℱ i]) 1 μ :=
  by
  have hmeas : ∀ n, ∀ C, MeasurableSet { x | C ≤ ‖(μ[g|ℱ n]) x‖₊ } := fun n C =>
    measurableSet_le measurable_const (strongly_measurable_condexp.mono (hℱ n)).Measurable.nnnorm
  have hg : mem_ℒp g 1 μ := mem_ℒp_one_iff_integrable.2 hint
  refine'
    uniform_integrable_of le_rfl ENNReal.one_ne_top
      (fun n => (strongly_measurable_condexp.mono (hℱ n)).AeStronglyMeasurable) fun ε hε => _
  by_cases hne : snorm g 1 μ = 0
  · rw [snorm_eq_zero_iff hg.1 one_ne_zero] at hne
    refine'
      ⟨0, fun n =>
        (le_of_eq <|
              (snorm_eq_zero_iff
                    ((strongly_measurable_condexp.mono (hℱ n)).AeStronglyMeasurable.indicator
                      (hmeas n 0))
                    one_ne_zero).2
                _).trans
          (zero_le _)⟩
    filter_upwards [@condexp_congr_ae _ _ _ _ _ (ℱ n) m0 μ _ _ hne]with x hx
    simp only [zero_le', Set.setOf_true, Set.indicator_univ, Pi.zero_apply, hx, condexp_zero]
  obtain ⟨δ, hδ, h⟩ := hg.snorm_indicator_le μ le_rfl ENNReal.one_ne_top hε
  set C : ℝ≥0 := ⟨δ, hδ.le⟩⁻¹ * (snorm g 1 μ).toNNReal with hC
  have hCpos : 0 < C := mul_pos (inv_pos.2 hδ) (ENNReal.toNNReal_pos hne hg.snorm_lt_top.ne)
  have : ∀ n, μ { x : α | C ≤ ‖(μ[g|ℱ n]) x‖₊ } ≤ ENNReal.ofReal δ :=
    by
    intro n
    have :=
      mul_meas_ge_le_pow_snorm' μ one_ne_zero ENNReal.one_ne_top
        ((@strongly_measurable_condexp _ _ _ _ _ (ℱ n) _ μ g).mono (hℱ n)).AeStronglyMeasurable C
    rw [ENNReal.one_toReal, ENNReal.rpow_one, ENNReal.rpow_one, mul_comm, ←
      ENNReal.le_div_iff_mul_le (Or.inl (ENNReal.coe_ne_zero.2 hCpos.ne.symm))
        (Or.inl ennreal.coe_lt_top.ne)] at
      this
    simp_rw [ENNReal.coe_le_coe] at this
    refine' this.trans _
    rw [ENNReal.div_le_iff_le_mul (Or.inl (ENNReal.coe_ne_zero.2 hCpos.ne.symm))
        (Or.inl ennreal.coe_lt_top.ne),
      hC, Nonneg.inv_mk, ENNReal.coe_mul, ENNReal.coe_toNNReal hg.snorm_lt_top.ne, ← mul_assoc, ←
      ENNReal.ofReal_eq_coe_nnreal, ← ENNReal.ofReal_mul hδ.le, mul_inv_cancel hδ.ne.symm,
      ENNReal.ofReal_one, one_mul]
    exact snorm_one_condexp_le_snorm _
  refine' ⟨C, fun n => le_trans _ (h { x : α | C ≤ ‖(μ[g|ℱ n]) x‖₊ } (hmeas n C) (this n))⟩
  have hmeasℱ : measurable_set[ℱ n] { x : α | C ≤ ‖(μ[g|ℱ n]) x‖₊ } :=
    @measurableSet_le _ _ _ _ _ (ℱ n) _ _ _ _ _ measurable_const
      (@Measurable.nnnorm _ _ _ _ _ (ℱ n) _ strongly_measurable_condexp.measurable)
  rw [← snorm_congr_ae (condexp_indicator hint hmeasℱ)]
  exact snorm_one_condexp_le_snorm _
#align measure_theory.integrable.uniform_integrable_condexp MeasureTheory.Integrable.uniformIntegrableCondexp

section PullOut

-- TODO: this section could be generalized beyond multiplication, to any bounded bilinear map.
/-- Auxiliary lemma for `condexp_measurable_mul`. -/
theorem condexp_strongly_measurable_simpleFunc_mul (hm : m ≤ m0) (f : @SimpleFunc α m ℝ) {g : α → ℝ}
    (hg : Integrable g μ) : μ[f * g|m] =ᵐ[μ] f * μ[g|m] :=
  by
  have : ∀ (s c) (f : α → ℝ), Set.indicator s (Function.const α c) * f = s.indicator (c • f) :=
    by
    intro s c f
    ext1 x
    by_cases hx : x ∈ s
    · simp only [hx, Pi.mul_apply, Set.indicator_of_mem, Pi.smul_apply, Algebra.id.smul_eq_mul]
    · simp only [hx, Pi.mul_apply, Set.indicator_of_not_mem, not_false_iff, MulZeroClass.zero_mul]
  refine' @simple_func.induction _ _ m _ _ (fun c s hs => _) (fun g₁ g₂ h_disj h_eq₁ h_eq₂ => _) f
  · simp only [simple_func.const_zero, simple_func.coe_piecewise, simple_func.coe_const,
      simple_func.coe_zero, Set.piecewise_eq_indicator]
    rw [this, this]
    refine' (condexp_indicator (hg.smul c) hs).trans _
    filter_upwards [@condexp_smul α ℝ ℝ _ _ _ _ _ m m0 μ c g]with x hx
    classical simp_rw [Set.indicator_apply, hx]
  · have h_add := @simple_func.coe_add _ _ m _ g₁ g₂
    calc
      μ[⇑(g₁ + g₂) * g|m] =ᵐ[μ] μ[(⇑g₁ + ⇑g₂) * g|m] :=
        by
        refine' condexp_congr_ae (eventually_eq.mul _ eventually_eq.rfl)
        rw [h_add]
      _ =ᵐ[μ] μ[⇑g₁ * g|m] + μ[⇑g₂ * g|m] := by
        rw [add_mul]
        exact condexp_add (hg.simple_func_mul' hm _) (hg.simple_func_mul' hm _)
      _ =ᵐ[μ] ⇑g₁ * μ[g|m] + ⇑g₂ * μ[g|m] := (eventually_eq.add h_eq₁ h_eq₂)
      _ =ᵐ[μ] ⇑(g₁ + g₂) * μ[g|m] := by rw [h_add, add_mul]

#align measure_theory.condexp_strongly_measurable_simple_func_mul MeasureTheory.condexp_strongly_measurable_simpleFunc_mul

theorem condexp_stronglyMeasurable_mul_of_bound (hm : m ≤ m0) [IsFiniteMeasure μ] {f g : α → ℝ}
    (hf : strongly_measurable[m] f) (hg : Integrable g μ) (c : ℝ) (hf_bound : ∀ᵐ x ∂μ, ‖f x‖ ≤ c) :
    μ[f * g|m] =ᵐ[μ] f * μ[g|m] := by
  let fs := hf.approx_bounded c
  have hfs_tendsto : ∀ᵐ x ∂μ, tendsto (fun n => fs n x) at_top (𝓝 (f x)) :=
    hf.tendsto_approx_bounded_ae hf_bound
  by_cases hμ : μ = 0
  · simp only [hμ, ae_zero]
  have : μ.ae.ne_bot := by simp only [hμ, ae_ne_bot, Ne.def, not_false_iff]
  have hc : 0 ≤ c :=
    haveI h_exists : ∃ x, ‖f x‖ ≤ c := eventually.exists hf_bound
    (norm_nonneg _).trans h_exists.some_spec
  have hfs_bound : ∀ n x, ‖fs n x‖ ≤ c := hf.norm_approx_bounded_le hc
  have hn_eq : ∀ n, μ[fs n * g|m] =ᵐ[μ] fs n * μ[g|m] := fun n =>
    condexp_strongly_measurable_simple_func_mul hm _ hg
  have : μ[f * μ[g|m]|m] = f * μ[g|m] :=
    by
    refine' condexp_of_strongly_measurable hm (hf.mul strongly_measurable_condexp) _
    exact integrable_condexp.bdd_mul' (hf.mono hm).AeStronglyMeasurable hf_bound
  rw [← this]
  refine'
    tendsto_condexp_unique (fun n x => fs n x * g x) (fun n x => fs n x * (μ[g|m]) x) (f * g)
      (f * μ[g|m]) _ _ _ _ (fun x => c * ‖g x‖) _ (fun x => c * ‖(μ[g|m]) x‖) _ _ _ _
  ·
    exact fun n =>
      hg.bdd_mul' ((simple_func.strongly_measurable (fs n)).mono hm).AeStronglyMeasurable
        (eventually_of_forall (hfs_bound n))
  ·
    exact fun n =>
      integrable_condexp.bdd_mul'
        ((simple_func.strongly_measurable (fs n)).mono hm).AeStronglyMeasurable
        (eventually_of_forall (hfs_bound n))
  · filter_upwards [hfs_tendsto]with x hx
    rw [Pi.mul_apply]
    exact tendsto.mul hx tendsto_const_nhds
  · filter_upwards [hfs_tendsto]with x hx
    rw [Pi.mul_apply]
    exact tendsto.mul hx tendsto_const_nhds
  · exact hg.norm.const_mul c
  · exact integrable_condexp.norm.const_mul c
  · refine' fun n => eventually_of_forall fun x => _
    exact (norm_mul_le _ _).trans (mul_le_mul_of_nonneg_right (hfs_bound n x) (norm_nonneg _))
  · refine' fun n => eventually_of_forall fun x => _
    exact (norm_mul_le _ _).trans (mul_le_mul_of_nonneg_right (hfs_bound n x) (norm_nonneg _))
  · intro n
    simp_rw [← Pi.mul_apply]
    refine' (condexp_strongly_measurable_simple_func_mul hm _ hg).trans _
    rw [condexp_of_strongly_measurable hm
        ((simple_func.strongly_measurable _).mul strongly_measurable_condexp) _]
    · infer_instance
    · infer_instance
    exact
      integrable_condexp.bdd_mul'
        ((simple_func.strongly_measurable (fs n)).mono hm).AeStronglyMeasurable
        (eventually_of_forall (hfs_bound n))
#align measure_theory.condexp_strongly_measurable_mul_of_bound MeasureTheory.condexp_stronglyMeasurable_mul_of_bound

theorem condexp_strongly_measurable_mul_of_bound₀ (hm : m ≤ m0) [IsFiniteMeasure μ] {f g : α → ℝ}
    (hf : AeStronglyMeasurable' m f μ) (hg : Integrable g μ) (c : ℝ)
    (hf_bound : ∀ᵐ x ∂μ, ‖f x‖ ≤ c) : μ[f * g|m] =ᵐ[μ] f * μ[g|m] :=
  by
  have : μ[f * g|m] =ᵐ[μ] μ[hf.mk f * g|m] :=
    condexp_congr_ae (eventually_eq.mul hf.ae_eq_mk eventually_eq.rfl)
  refine' this.trans _
  have : f * μ[g|m] =ᵐ[μ] hf.mk f * μ[g|m] := eventually_eq.mul hf.ae_eq_mk eventually_eq.rfl
  refine' eventually_eq.trans _ this.symm
  refine' condexp_strongly_measurable_mul_of_bound hm hf.strongly_measurable_mk hg c _
  filter_upwards [hf_bound, hf.ae_eq_mk]with x hxc hx_eq
  rw [← hx_eq]
  exact hxc
#align measure_theory.condexp_strongly_measurable_mul_of_bound₀ MeasureTheory.condexp_strongly_measurable_mul_of_bound₀

/-- Pull-out property of the conditional expectation. -/
theorem condexp_stronglyMeasurable_mul {f g : α → ℝ} (hf : strongly_measurable[m] f)
    (hfg : Integrable (f * g) μ) (hg : Integrable g μ) : μ[f * g|m] =ᵐ[μ] f * μ[g|m] :=
  by
  by_cases hm : m ≤ m0
  swap
  · simp_rw [condexp_of_not_le hm]
    rw [MulZeroClass.mul_zero]
  by_cases hμm : sigma_finite (μ.trim hm)
  swap
  · simp_rw [condexp_of_not_sigma_finite hm hμm]
    rw [MulZeroClass.mul_zero]
  haveI : sigma_finite (μ.trim hm) := hμm
  obtain ⟨sets, sets_prop, h_univ⟩ := hf.exists_spanning_measurable_set_norm_le hm μ
  simp_rw [forall_and] at sets_prop
  obtain ⟨h_meas, h_finite, h_norm⟩ := sets_prop
  suffices ∀ n, ∀ᵐ x ∂μ, x ∈ sets n → (μ[f * g|m]) x = f x * (μ[g|m]) x
    by
    rw [← ae_all_iff] at this
    filter_upwards [this]with x hx
    rw [Pi.mul_apply]
    obtain ⟨i, hi⟩ : ∃ i, x ∈ sets i :=
      by
      have h_mem : x ∈ ⋃ i, sets i := by
        rw [h_univ]
        exact Set.mem_univ _
      simpa using h_mem
    exact hx i hi
  refine' fun n => ae_imp_of_ae_restrict _
  suffices μ.restrict (sets n)[f * g|m] =ᵐ[μ.restrict (sets n)] f * μ.restrict (sets n)[g|m]
    by
    simp_rw [← Pi.mul_apply]
    refine' (condexp_restrict_ae_eq_restrict hm (h_meas n) hfg).symm.trans _
    exact this.trans (eventually_eq.rfl.mul (condexp_restrict_ae_eq_restrict hm (h_meas n) hg))
  suffices
    μ.restrict (sets n)[(sets n).indicator f * g|m] =ᵐ[μ.restrict (sets n)]
      (sets n).indicator f * μ.restrict (sets n)[g|m]
    by
    refine' eventually_eq.trans _ (this.trans _)
    ·
      exact
        condexp_congr_ae ((indicator_ae_eq_restrict (hm _ (h_meas n))).symm.mul eventually_eq.rfl)
    · exact (indicator_ae_eq_restrict (hm _ (h_meas n))).mul eventually_eq.rfl
  have : is_finite_measure (μ.restrict (sets n)) :=
    by
    constructor
    rw [measure.restrict_apply_univ]
    exact h_finite n
  refine' condexp_strongly_measurable_mul_of_bound hm (hf.indicator (h_meas n)) hg.integrable_on n _
  refine' eventually_of_forall fun x => _
  by_cases hxs : x ∈ sets n
  · simp only [hxs, Set.indicator_of_mem]
    exact h_norm n x hxs
  · simp only [hxs, Set.indicator_of_not_mem, not_false_iff, _root_.norm_zero, Nat.cast_nonneg]
#align measure_theory.condexp_strongly_measurable_mul MeasureTheory.condexp_stronglyMeasurable_mul

/-- Pull-out property of the conditional expectation. -/
theorem condexp_strongly_measurable_mul₀ {f g : α → ℝ} (hf : AeStronglyMeasurable' m f μ)
    (hfg : Integrable (f * g) μ) (hg : Integrable g μ) : μ[f * g|m] =ᵐ[μ] f * μ[g|m] :=
  by
  have : μ[f * g|m] =ᵐ[μ] μ[hf.mk f * g|m] :=
    condexp_congr_ae (eventually_eq.mul hf.ae_eq_mk eventually_eq.rfl)
  refine' this.trans _
  have : f * μ[g|m] =ᵐ[μ] hf.mk f * μ[g|m] := eventually_eq.mul hf.ae_eq_mk eventually_eq.rfl
  refine' eventually_eq.trans _ this.symm
  refine' condexp_strongly_measurable_mul hf.strongly_measurable_mk _ hg
  refine' (integrable_congr _).mp hfg
  exact eventually_eq.mul hf.ae_eq_mk eventually_eq.rfl
#align measure_theory.condexp_strongly_measurable_mul₀ MeasureTheory.condexp_strongly_measurable_mul₀

end PullOut

end MeasureTheory