/-
Copyright (c) 2022 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Myers

! This file was ported from Lean 3 source module algebra.order.to_interval_mod
! leanprover-community/mathlib commit 2196ab363eb097c008d4497125e0dde23fb36db2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Module.Basic
import Mathbin.Algebra.Order.Archimedean
import Mathbin.Algebra.Periodic
import Mathbin.Data.Int.SuccPred
import Mathbin.GroupTheory.QuotientGroup

/-!
# Reducing to an interval modulo its length

This file defines operations that reduce a number (in an `archimedean`
`linear_ordered_add_comm_group`) to a number in a given interval, modulo the length of that
interval.

## Main definitions

* `to_Ico_div a hb x` (where `hb : 0 < b`): The unique integer such that this multiple of `b`,
  subtracted from `x`, is in `Ico a (a + b)`.
* `to_Ico_mod a hb x` (where `hb : 0 < b`): Reduce `x` to the interval `Ico a (a + b)`.
* `to_Ioc_div a hb x` (where `hb : 0 < b`): The unique integer such that this multiple of `b`,
  subtracted from `x`, is in `Ioc a (a + b)`.
* `to_Ioc_mod a hb x` (where `hb : 0 < b`): Reduce `x` to the interval `Ioc a (a + b)`.

-/

noncomputable section

section LinearOrderedAddCommGroup

variable {α : Type _} [LinearOrderedAddCommGroup α] [hα : Archimedean α]

include hα

/--
The unique integer such that this multiple of `b`, subtracted from `x`, is in `Ico a (a + b)`. -/
def toIcoDiv (a : α) {b : α} (hb : 0 < b) (x : α) : ℤ :=
  (existsUnique_sub_zsmul_mem_Ico hb x a).some
#align to_Ico_div toIcoDiv

theorem sub_toIcoDiv_zsmul_mem_Ico (a : α) {b : α} (hb : 0 < b) (x : α) :
    x - toIcoDiv a hb x • b ∈ Set.Ico a (a + b) :=
  (existsUnique_sub_zsmul_mem_Ico hb x a).choose_spec.1
#align sub_to_Ico_div_zsmul_mem_Ico sub_toIcoDiv_zsmul_mem_Ico

theorem eq_toIcoDiv_of_sub_zsmul_mem_Ico {a b x : α} (hb : 0 < b) {y : ℤ}
    (hy : x - y • b ∈ Set.Ico a (a + b)) : y = toIcoDiv a hb x :=
  (existsUnique_sub_zsmul_mem_Ico hb x a).choose_spec.2 y hy
#align eq_to_Ico_div_of_sub_zsmul_mem_Ico eq_toIcoDiv_of_sub_zsmul_mem_Ico

/--
The unique integer such that this multiple of `b`, subtracted from `x`, is in `Ioc a (a + b)`. -/
def toIocDiv (a : α) {b : α} (hb : 0 < b) (x : α) : ℤ :=
  (existsUnique_sub_zsmul_mem_Ioc hb x a).some
#align to_Ioc_div toIocDiv

theorem sub_toIocDiv_zsmul_mem_Ioc (a : α) {b : α} (hb : 0 < b) (x : α) :
    x - toIocDiv a hb x • b ∈ Set.Ioc a (a + b) :=
  (existsUnique_sub_zsmul_mem_Ioc hb x a).choose_spec.1
#align sub_to_Ioc_div_zsmul_mem_Ioc sub_toIocDiv_zsmul_mem_Ioc

theorem eq_toIocDiv_of_sub_zsmul_mem_Ioc {a b x : α} (hb : 0 < b) {y : ℤ}
    (hy : x - y • b ∈ Set.Ioc a (a + b)) : y = toIocDiv a hb x :=
  (existsUnique_sub_zsmul_mem_Ioc hb x a).choose_spec.2 y hy
#align eq_to_Ioc_div_of_sub_zsmul_mem_Ioc eq_toIocDiv_of_sub_zsmul_mem_Ioc

/-- Reduce `x` to the interval `Ico a (a + b)`. -/
def toIcoMod (a : α) {b : α} (hb : 0 < b) (x : α) : α :=
  x - toIcoDiv a hb x • b
#align to_Ico_mod toIcoMod

/-- Reduce `x` to the interval `Ioc a (a + b)`. -/
def toIocMod (a : α) {b : α} (hb : 0 < b) (x : α) : α :=
  x - toIocDiv a hb x • b
#align to_Ioc_mod toIocMod

theorem toIcoMod_mem_Ico (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a hb x ∈ Set.Ico a (a + b) :=
  sub_toIcoDiv_zsmul_mem_Ico a hb x
#align to_Ico_mod_mem_Ico toIcoMod_mem_Ico

theorem toIcoMod_mem_Ico' {b : α} (hb : 0 < b) (x : α) : toIcoMod 0 hb x ∈ Set.Ico 0 b :=
  by
  convert toIcoMod_mem_Ico 0 hb x
  exact (zero_add b).symm
#align to_Ico_mod_mem_Ico' toIcoMod_mem_Ico'

theorem toIocMod_mem_Ioc (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a hb x ∈ Set.Ioc a (a + b) :=
  sub_toIocDiv_zsmul_mem_Ioc a hb x
#align to_Ioc_mod_mem_Ioc toIocMod_mem_Ioc

theorem left_le_toIcoMod (a : α) {b : α} (hb : 0 < b) (x : α) : a ≤ toIcoMod a hb x :=
  (Set.mem_Ico.1 (toIcoMod_mem_Ico a hb x)).1
#align left_le_to_Ico_mod left_le_toIcoMod

theorem left_lt_toIocMod (a : α) {b : α} (hb : 0 < b) (x : α) : a < toIocMod a hb x :=
  (Set.mem_Ioc.1 (toIocMod_mem_Ioc a hb x)).1
#align left_lt_to_Ioc_mod left_lt_toIocMod

theorem toIcoMod_lt_right (a : α) {b : α} (hb : 0 < b) (x : α) : toIcoMod a hb x < a + b :=
  (Set.mem_Ico.1 (toIcoMod_mem_Ico a hb x)).2
#align to_Ico_mod_lt_right toIcoMod_lt_right

theorem toIocMod_le_right (a : α) {b : α} (hb : 0 < b) (x : α) : toIocMod a hb x ≤ a + b :=
  (Set.mem_Ioc.1 (toIocMod_mem_Ioc a hb x)).2
#align to_Ioc_mod_le_right toIocMod_le_right

@[simp]
theorem self_sub_toIcoDiv_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) :
    x - toIcoDiv a hb x • b = toIcoMod a hb x :=
  rfl
#align self_sub_to_Ico_div_zsmul self_sub_toIcoDiv_zsmul

@[simp]
theorem self_sub_toIocDiv_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) :
    x - toIocDiv a hb x • b = toIocMod a hb x :=
  rfl
#align self_sub_to_Ioc_div_zsmul self_sub_toIocDiv_zsmul

@[simp]
theorem toIcoDiv_zsmul_sub_self (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoDiv a hb x • b - x = -toIcoMod a hb x := by rw [toIcoMod, neg_sub]
#align to_Ico_div_zsmul_sub_self toIcoDiv_zsmul_sub_self

@[simp]
theorem toIocDiv_zsmul_sub_self (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocDiv a hb x • b - x = -toIocMod a hb x := by rw [toIocMod, neg_sub]
#align to_Ioc_div_zsmul_sub_self toIocDiv_zsmul_sub_self

@[simp]
theorem toIcoMod_sub_self (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a hb x - x = -toIcoDiv a hb x • b := by rw [toIcoMod, sub_sub_cancel_left, neg_smul]
#align to_Ico_mod_sub_self toIcoMod_sub_self

@[simp]
theorem toIocMod_sub_self (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a hb x - x = -toIocDiv a hb x • b := by rw [toIocMod, sub_sub_cancel_left, neg_smul]
#align to_Ioc_mod_sub_self toIocMod_sub_self

@[simp]
theorem self_sub_toIcoMod (a : α) {b : α} (hb : 0 < b) (x : α) :
    x - toIcoMod a hb x = toIcoDiv a hb x • b := by rw [toIcoMod, sub_sub_cancel]
#align self_sub_to_Ico_mod self_sub_toIcoMod

@[simp]
theorem self_sub_toIocMod (a : α) {b : α} (hb : 0 < b) (x : α) :
    x - toIocMod a hb x = toIocDiv a hb x • b := by rw [toIocMod, sub_sub_cancel]
#align self_sub_to_Ioc_mod self_sub_toIocMod

@[simp]
theorem toIcoMod_add_toIcoDiv_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a hb x + toIcoDiv a hb x • b = x := by rw [toIcoMod, sub_add_cancel]
#align to_Ico_mod_add_to_Ico_div_zsmul toIcoMod_add_toIcoDiv_zsmul

@[simp]
theorem toIocMod_add_toIocDiv_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a hb x + toIocDiv a hb x • b = x := by rw [toIocMod, sub_add_cancel]
#align to_Ioc_mod_add_to_Ioc_div_zsmul toIocMod_add_toIocDiv_zsmul

@[simp]
theorem toIcoDiv_zsmul_sub_toIcoMod (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoDiv a hb x • b + toIcoMod a hb x = x := by rw [add_comm, toIcoMod_add_toIcoDiv_zsmul]
#align to_Ico_div_zsmul_sub_to_Ico_mod toIcoDiv_zsmul_sub_toIcoMod

@[simp]
theorem toIocDiv_zsmul_sub_toIocMod (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocDiv a hb x • b + toIocMod a hb x = x := by rw [add_comm, toIocMod_add_toIocDiv_zsmul]
#align to_Ioc_div_zsmul_sub_to_Ioc_mod toIocDiv_zsmul_sub_toIocMod

theorem toIcoMod_eq_iff {a b x y : α} (hb : 0 < b) :
    toIcoMod a hb x = y ↔ y ∈ Set.Ico a (a + b) ∧ ∃ z : ℤ, x = y + z • b :=
  by
  refine'
    ⟨fun h =>
      ⟨h ▸ toIcoMod_mem_Ico a hb x, toIcoDiv a hb x, h ▸ (toIcoMod_add_toIcoDiv_zsmul _ _ _).symm⟩,
      fun h => _⟩
  rcases h with ⟨hy, z, hz⟩
  rw [← sub_eq_iff_eq_add] at hz
  subst hz
  rw [eq_toIcoDiv_of_sub_zsmul_mem_Ico hb hy]
  rfl
#align to_Ico_mod_eq_iff toIcoMod_eq_iff

theorem toIocMod_eq_iff {a b x y : α} (hb : 0 < b) :
    toIocMod a hb x = y ↔ y ∈ Set.Ioc a (a + b) ∧ ∃ z : ℤ, x = y + z • b :=
  by
  refine'
    ⟨fun h =>
      ⟨h ▸ toIocMod_mem_Ioc a hb x, toIocDiv a hb x, h ▸ (toIocMod_add_toIocDiv_zsmul _ hb _).symm⟩,
      fun h => _⟩
  rcases h with ⟨hy, z, hz⟩
  rw [← sub_eq_iff_eq_add] at hz
  subst hz
  rw [eq_toIocDiv_of_sub_zsmul_mem_Ioc hb hy]
  rfl
#align to_Ioc_mod_eq_iff toIocMod_eq_iff

@[simp]
theorem toIcoDiv_apply_left (a : α) {b : α} (hb : 0 < b) : toIcoDiv a hb a = 0 :=
  by
  refine' (eq_toIcoDiv_of_sub_zsmul_mem_Ico hb _).symm
  simp [hb]
#align to_Ico_div_apply_left toIcoDiv_apply_left

@[simp]
theorem toIocDiv_apply_left (a : α) {b : α} (hb : 0 < b) : toIocDiv a hb a = -1 :=
  by
  refine' (eq_toIocDiv_of_sub_zsmul_mem_Ioc hb _).symm
  simp [hb]
#align to_Ioc_div_apply_left toIocDiv_apply_left

@[simp]
theorem toIcoMod_apply_left (a : α) {b : α} (hb : 0 < b) : toIcoMod a hb a = a :=
  by
  rw [toIcoMod_eq_iff hb, Set.left_mem_Ico]
  refine' ⟨lt_add_of_pos_right _ hb, 0, _⟩
  simp
#align to_Ico_mod_apply_left toIcoMod_apply_left

@[simp]
theorem toIocMod_apply_left (a : α) {b : α} (hb : 0 < b) : toIocMod a hb a = a + b :=
  by
  rw [toIocMod_eq_iff hb, Set.right_mem_Ioc]
  refine' ⟨lt_add_of_pos_right _ hb, -1, _⟩
  simp
#align to_Ioc_mod_apply_left toIocMod_apply_left

theorem toIcoDiv_apply_right (a : α) {b : α} (hb : 0 < b) : toIcoDiv a hb (a + b) = 1 :=
  by
  refine' (eq_toIcoDiv_of_sub_zsmul_mem_Ico hb _).symm
  simp [hb]
#align to_Ico_div_apply_right toIcoDiv_apply_right

theorem toIocDiv_apply_right (a : α) {b : α} (hb : 0 < b) : toIocDiv a hb (a + b) = 0 :=
  by
  refine' (eq_toIocDiv_of_sub_zsmul_mem_Ioc hb _).symm
  simp [hb]
#align to_Ioc_div_apply_right toIocDiv_apply_right

theorem toIcoMod_apply_right (a : α) {b : α} (hb : 0 < b) : toIcoMod a hb (a + b) = a :=
  by
  rw [toIcoMod_eq_iff hb, Set.left_mem_Ico]
  refine' ⟨lt_add_of_pos_right _ hb, 1, _⟩
  simp
#align to_Ico_mod_apply_right toIcoMod_apply_right

theorem toIocMod_apply_right (a : α) {b : α} (hb : 0 < b) : toIocMod a hb (a + b) = a + b :=
  by
  rw [toIocMod_eq_iff hb, Set.right_mem_Ioc]
  refine' ⟨lt_add_of_pos_right _ hb, 0, _⟩
  simp
#align to_Ioc_mod_apply_right toIocMod_apply_right

@[simp]
theorem toIcoDiv_add_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIcoDiv a hb (x + m • b) = toIcoDiv a hb x + m :=
  by
  refine' (eq_toIcoDiv_of_sub_zsmul_mem_Ico hb _).symm
  convert sub_toIcoDiv_zsmul_mem_Ico a hb x using 1
  simp [add_smul]
#align to_Ico_div_add_zsmul toIcoDiv_add_zsmul

@[simp]
theorem toIocDiv_add_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIocDiv a hb (x + m • b) = toIocDiv a hb x + m :=
  by
  refine' (eq_toIocDiv_of_sub_zsmul_mem_Ioc hb _).symm
  convert sub_toIocDiv_zsmul_mem_Ioc a hb x using 1
  simp [add_smul]
#align to_Ioc_div_add_zsmul toIocDiv_add_zsmul

@[simp]
theorem toIcoDiv_zsmul_add (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIcoDiv a hb (m • b + x) = m + toIcoDiv a hb x := by
  rw [add_comm, toIcoDiv_add_zsmul, add_comm]
#align to_Ico_div_zsmul_add toIcoDiv_zsmul_add

@[simp]
theorem toIocDiv_zsmul_add (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIocDiv a hb (m • b + x) = toIocDiv a hb x + m := by
  rw [add_comm, toIocDiv_add_zsmul, add_comm]
#align to_Ioc_div_zsmul_add toIocDiv_zsmul_add

@[simp]
theorem toIcoDiv_sub_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIcoDiv a hb (x - m • b) = toIcoDiv a hb x - m := by
  rw [sub_eq_add_neg, ← neg_smul, toIcoDiv_add_zsmul, sub_eq_add_neg]
#align to_Ico_div_sub_zsmul toIcoDiv_sub_zsmul

@[simp]
theorem toIocDiv_sub_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIocDiv a hb (x - m • b) = toIocDiv a hb x - m := by
  rw [sub_eq_add_neg, ← neg_smul, toIocDiv_add_zsmul, sub_eq_add_neg]
#align to_Ioc_div_sub_zsmul toIocDiv_sub_zsmul

@[simp]
theorem toIcoDiv_add_right (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoDiv a hb (x + b) = toIcoDiv a hb x + 1 :=
  by
  convert toIcoDiv_add_zsmul a hb x 1
  exact (one_zsmul _).symm
#align to_Ico_div_add_right toIcoDiv_add_right

@[simp]
theorem toIocDiv_add_right (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocDiv a hb (x + b) = toIocDiv a hb x + 1 :=
  by
  convert toIocDiv_add_zsmul a hb x 1
  exact (one_zsmul _).symm
#align to_Ioc_div_add_right toIocDiv_add_right

@[simp]
theorem toIcoDiv_add_left (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoDiv a hb (b + x) = toIcoDiv a hb x + 1 := by rw [add_comm, toIcoDiv_add_right]
#align to_Ico_div_add_left toIcoDiv_add_left

@[simp]
theorem toIocDiv_add_left (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocDiv a hb (b + x) = toIocDiv a hb x + 1 := by rw [add_comm, toIocDiv_add_right]
#align to_Ioc_div_add_left toIocDiv_add_left

@[simp]
theorem toIcoDiv_sub (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoDiv a hb (x - b) = toIcoDiv a hb x - 1 :=
  by
  convert toIcoDiv_sub_zsmul a hb x 1
  exact (one_zsmul _).symm
#align to_Ico_div_sub toIcoDiv_sub

@[simp]
theorem toIocDiv_sub (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocDiv a hb (x - b) = toIocDiv a hb x - 1 :=
  by
  convert toIocDiv_sub_zsmul a hb x 1
  exact (one_zsmul _).symm
#align to_Ioc_div_sub toIocDiv_sub

theorem toIcoDiv_sub' (a : α) {b : α} (hb : 0 < b) (x y : α) :
    toIcoDiv a hb (x - y) = toIcoDiv (a + y) hb x :=
  by
  rw [eq_comm]
  apply eq_toIcoDiv_of_sub_zsmul_mem_Ico
  rw [← sub_right_comm, Set.sub_mem_Ico_iff_left, add_right_comm]
  exact sub_toIcoDiv_zsmul_mem_Ico (a + y) hb x
#align to_Ico_div_sub' toIcoDiv_sub'

theorem toIocDiv_sub' (a : α) {b : α} (hb : 0 < b) (x y : α) :
    toIocDiv a hb (x - y) = toIocDiv (a + y) hb x :=
  by
  rw [eq_comm]
  apply eq_toIocDiv_of_sub_zsmul_mem_Ioc
  rw [← sub_right_comm, Set.sub_mem_Ioc_iff_left, add_right_comm]
  exact sub_toIocDiv_zsmul_mem_Ioc (a + y) hb x
#align to_Ioc_div_sub' toIocDiv_sub'

theorem toIcoDiv_add_right' (a : α) {b : α} (hb : 0 < b) (x y : α) :
    toIcoDiv a hb (x + y) = toIcoDiv (a - y) hb x := by
  rw [← sub_neg_eq_add, toIcoDiv_sub', sub_eq_add_neg]
#align to_Ico_div_add_right' toIcoDiv_add_right'

theorem toIocDiv_add_right' (a : α) {b : α} (hb : 0 < b) (x y : α) :
    toIocDiv a hb (x + y) = toIocDiv (a - y) hb x := by
  rw [← sub_neg_eq_add, toIocDiv_sub', sub_eq_add_neg]
#align to_Ioc_div_add_right' toIocDiv_add_right'

theorem toIcoDiv_neg (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoDiv a hb (-x) = -(toIocDiv (-a) hb x + 1) :=
  by
  suffices toIcoDiv a hb (-x) = -toIocDiv (-(a + b)) hb x by
    rwa [neg_add, ← sub_eq_add_neg, ← toIocDiv_add_right', toIocDiv_add_right] at this
  rw [← neg_eq_iff_eq_neg]
  apply eq_toIocDiv_of_sub_zsmul_mem_Ioc
  obtain ⟨hc, ho⟩ := sub_toIcoDiv_zsmul_mem_Ico a hb (-x)
  rw [← neg_lt_neg_iff, neg_sub' (-x), neg_neg, ← neg_smul] at ho
  rw [← neg_le_neg_iff, neg_sub' (-x), neg_neg, ← neg_smul] at hc
  refine' ⟨ho, hc.trans_eq _⟩
  rw [neg_add, neg_add_cancel_right]
#align to_Ico_div_neg toIcoDiv_neg

theorem toIocDiv_neg (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocDiv a hb (-x) = -(toIcoDiv (-a) hb x + 1) := by
  rw [← neg_neg x, toIcoDiv_neg, neg_neg, neg_neg, neg_add', neg_neg, add_sub_cancel]
#align to_Ioc_div_neg toIocDiv_neg

@[simp]
theorem toIcoMod_add_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIcoMod a hb (x + m • b) = toIcoMod a hb x :=
  by
  rw [toIcoMod, toIcoDiv_add_zsmul, toIcoMod, add_smul]
  abel
#align to_Ico_mod_add_zsmul toIcoMod_add_zsmul

@[simp]
theorem toIocMod_add_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIocMod a hb (x + m • b) = toIocMod a hb x :=
  by
  rw [toIocMod, toIocDiv_add_zsmul, toIocMod, add_smul]
  abel
#align to_Ioc_mod_add_zsmul toIocMod_add_zsmul

@[simp]
theorem toIcoMod_zsmul_add (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIcoMod a hb (m • b + x) = toIcoMod a hb x := by rw [add_comm, toIcoMod_add_zsmul]
#align to_Ico_mod_zsmul_add toIcoMod_zsmul_add

@[simp]
theorem toIocMod_zsmul_add (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIocMod a hb (m • b + x) = toIocMod a hb x := by rw [add_comm, toIocMod_add_zsmul]
#align to_Ioc_mod_zsmul_add toIocMod_zsmul_add

@[simp]
theorem toIcoMod_sub_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIcoMod a hb (x - m • b) = toIcoMod a hb x := by
  rw [sub_eq_add_neg, ← neg_smul, toIcoMod_add_zsmul]
#align to_Ico_mod_sub_zsmul toIcoMod_sub_zsmul

@[simp]
theorem toIocMod_sub_zsmul (a : α) {b : α} (hb : 0 < b) (x : α) (m : ℤ) :
    toIocMod a hb (x - m • b) = toIocMod a hb x := by
  rw [sub_eq_add_neg, ← neg_smul, toIocMod_add_zsmul]
#align to_Ioc_mod_sub_zsmul toIocMod_sub_zsmul

@[simp]
theorem toIcoMod_add_right (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a hb (x + b) = toIcoMod a hb x :=
  by
  convert toIcoMod_add_zsmul a hb x 1
  exact (one_zsmul _).symm
#align to_Ico_mod_add_right toIcoMod_add_right

@[simp]
theorem toIocMod_add_right (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a hb (x + b) = toIocMod a hb x :=
  by
  convert toIocMod_add_zsmul a hb x 1
  exact (one_zsmul _).symm
#align to_Ioc_mod_add_right toIocMod_add_right

@[simp]
theorem toIcoMod_add_left (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a hb (b + x) = toIcoMod a hb x := by rw [add_comm, toIcoMod_add_right]
#align to_Ico_mod_add_left toIcoMod_add_left

@[simp]
theorem toIocMod_add_left (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a hb (b + x) = toIocMod a hb x := by rw [add_comm, toIocMod_add_right]
#align to_Ioc_mod_add_left toIocMod_add_left

@[simp]
theorem toIcoMod_sub (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a hb (x - b) = toIcoMod a hb x :=
  by
  convert toIcoMod_sub_zsmul a hb x 1
  exact (one_zsmul _).symm
#align to_Ico_mod_sub toIcoMod_sub

@[simp]
theorem toIocMod_sub (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a hb (x - b) = toIocMod a hb x :=
  by
  convert toIocMod_sub_zsmul a hb x 1
  exact (one_zsmul _).symm
#align to_Ioc_mod_sub toIocMod_sub

theorem toIcoMod_sub' (a : α) {b : α} (hb : 0 < b) (x y : α) :
    toIcoMod a hb (x - y) = toIcoMod (a + y) hb x - y := by
  simp_rw [toIcoMod, toIcoDiv_sub', sub_right_comm]
#align to_Ico_mod_sub' toIcoMod_sub'

theorem toIocMod_sub' (a : α) {b : α} (hb : 0 < b) (x y : α) :
    toIocMod a hb (x - y) = toIocMod (a + y) hb x - y := by
  simp_rw [toIocMod, toIocDiv_sub', sub_right_comm]
#align to_Ioc_mod_sub' toIocMod_sub'

theorem toIcoMod_add_right' (a : α) {b : α} (hb : 0 < b) (x y : α) :
    toIcoMod a hb (x + y) = toIcoMod (a - y) hb x + y := by
  simp_rw [toIcoMod, toIcoDiv_add_right', sub_add_eq_add_sub]
#align to_Ico_mod_add_right' toIcoMod_add_right'

theorem toIocMod_add_right' (a : α) {b : α} (hb : 0 < b) (x y : α) :
    toIocMod a hb (x + y) = toIocMod (a - y) hb x + y := by
  simp_rw [toIocMod, toIocDiv_add_right', sub_add_eq_add_sub]
#align to_Ioc_mod_add_right' toIocMod_add_right'

theorem toIcoMod_neg (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a hb (-x) = b - toIocMod (-a) hb x :=
  by
  simp_rw [toIcoMod, toIocMod, toIcoDiv_neg, neg_smul, add_smul]
  abel
#align to_Ico_mod_neg toIcoMod_neg

theorem toIocMod_neg (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a hb (-x) = b - toIcoMod (-a) hb x :=
  by
  simp_rw [toIocMod, toIcoMod, toIocDiv_neg, neg_smul, add_smul]
  abel
#align to_Ioc_mod_neg toIocMod_neg

theorem toIcoMod_eq_toIcoMod (a : α) {b x y : α} (hb : 0 < b) :
    toIcoMod a hb x = toIcoMod a hb y ↔ ∃ z : ℤ, y - x = z • b :=
  by
  refine' ⟨fun h => ⟨toIcoDiv a hb y - toIcoDiv a hb x, _⟩, fun h => _⟩
  · conv_lhs => rw [← toIcoMod_add_toIcoDiv_zsmul a hb x, ← toIcoMod_add_toIcoDiv_zsmul a hb y]
    rw [h, sub_smul]
    abel
  · rcases h with ⟨z, hz⟩
    rw [sub_eq_iff_eq_add] at hz
    rw [hz, toIcoMod_zsmul_add]
#align to_Ico_mod_eq_to_Ico_mod toIcoMod_eq_toIcoMod

theorem toIocMod_eq_toIocMod (a : α) {b x y : α} (hb : 0 < b) :
    toIocMod a hb x = toIocMod a hb y ↔ ∃ z : ℤ, y - x = z • b :=
  by
  refine' ⟨fun h => ⟨toIocDiv a hb y - toIocDiv a hb x, _⟩, fun h => _⟩
  · conv_lhs => rw [← toIocMod_add_toIocDiv_zsmul a hb x, ← toIocMod_add_toIocDiv_zsmul a hb y]
    rw [h, sub_smul]
    abel
  · rcases h with ⟨z, hz⟩
    rw [sub_eq_iff_eq_add] at hz
    rw [hz, toIocMod_zsmul_add]
#align to_Ioc_mod_eq_to_Ioc_mod toIocMod_eq_toIocMod

/-! ### Links between the `Ico` and `Ioc` variants applied to the same element -/

section IcoIoc

variable (a : α) {b : α} (hb : 0 < b) (x : α)

omit hα

/-- `mem_Ioo_mod a b x` means that `x` lies in the open interval `(a, a + b)` modulo `b`.
Equivalently (as shown below), `x` is not congruent to `a` modulo `b`, or `to_Ico_mod a hb` agrees
with `to_Ioc_mod a hb` at `x`, or `to_Ico_div a hb` agrees with `to_Ioc_div a hb` at `x`. -/
def MemIooMod (b x : α) : Prop :=
  ∃ z : ℤ, x - z • b ∈ Set.Ioo a (a + b)
#align mem_Ioo_mod MemIooMod

include hα

theorem tFAE_memIooMod :
    TFAE
      [MemIooMod a b x, toIcoMod a hb x = toIocMod a hb x, toIcoMod a hb x + b ≠ toIocMod a hb x,
        toIcoMod a hb x ≠ a] :=
  by
  tfae_have 1 → 2
  ·
    exact fun ⟨i, hi⟩ =>
      ((toIcoMod_eq_iff hb).2 ⟨Set.Ioo_subset_Ico_self hi, i, (sub_add_cancel x _).symm⟩).trans
        ((toIocMod_eq_iff hb).2 ⟨Set.Ioo_subset_Ioc_self hi, i, (sub_add_cancel x _).symm⟩).symm
  tfae_have 2 → 3
  · intro h
    rw [h, Ne, add_right_eq_self]
    exact hb.ne'
  tfae_have 3 → 4
  · refine' mt fun h => _
    rw [h, eq_comm, toIocMod_eq_iff, Set.right_mem_Ioc]
    refine' ⟨lt_add_of_pos_right a hb, toIcoDiv a hb x - 1, _⟩
    rw [sub_one_zsmul, add_add_add_comm, add_right_neg, add_zero]
    conv_lhs => rw [← toIcoMod_add_toIcoDiv_zsmul a hb x, h]
  tfae_have 4 → 1
  · have h' := toIcoMod_mem_Ico a hb x
    exact fun h => ⟨_, h'.1.lt_of_ne' h, h'.2⟩
  tfae_finish
#align tfae_mem_Ioo_mod tFAE_memIooMod

variable {a x}

theorem memIooMod_iff_toIcoMod_eq_toIocMod : MemIooMod a b x ↔ toIcoMod a hb x = toIocMod a hb x :=
  (tFAE_memIooMod a hb x).out 0 1
#align mem_Ioo_mod_iff_to_Ico_mod_eq_to_Ioc_mod memIooMod_iff_toIcoMod_eq_toIocMod

theorem memIooMod_iff_toIcoMod_add_period_ne_toIocMod :
    MemIooMod a b x ↔ toIcoMod a hb x + b ≠ toIocMod a hb x :=
  (tFAE_memIooMod a hb x).out 0 2
#align mem_Ioo_mod_iff_to_Ico_mod_add_period_ne_to_Ioc_mod memIooMod_iff_toIcoMod_add_period_ne_toIocMod

theorem memIooMod_iff_toIcoMod_ne_left : MemIooMod a b x ↔ toIcoMod a hb x ≠ a :=
  (tFAE_memIooMod a hb x).out 0 3
#align mem_Ioo_mod_iff_to_Ico_mod_ne_left memIooMod_iff_toIcoMod_ne_left

theorem not_memIooMod_iff_toIcoMod_add_period_eq_toIocMod :
    ¬MemIooMod a b x ↔ toIcoMod a hb x + b = toIocMod a hb x :=
  (memIooMod_iff_toIcoMod_add_period_ne_toIocMod hb).not_left
#align not_mem_Ioo_mod_iff_to_Ico_mod_add_period_eq_to_Ioc_mod not_memIooMod_iff_toIcoMod_add_period_eq_toIocMod

theorem not_memIooMod_iff_toIcoMod_eq_left : ¬MemIooMod a b x ↔ toIcoMod a hb x = a :=
  (memIooMod_iff_toIcoMod_ne_left hb).not_left
#align not_mem_Ioo_mod_iff_to_Ico_mod_eq_left not_memIooMod_iff_toIcoMod_eq_left

theorem memIooMod_iff_toIocMod_ne_right : MemIooMod a b x ↔ toIocMod a hb x ≠ a + b :=
  by
  rw [memIooMod_iff_toIcoMod_eq_toIocMod, toIcoMod_eq_iff hb]
  obtain ⟨h₁, h₂⟩ := toIocMod_mem_Ioc a hb x
  exact
    ⟨fun h => h.1.2.Ne, fun h =>
      ⟨⟨h₁.le, h₂.lt_of_ne h⟩, _, (toIocMod_add_toIocDiv_zsmul _ _ _).symm⟩⟩
#align mem_Ioo_mod_iff_to_Ioc_mod_ne_right memIooMod_iff_toIocMod_ne_right

theorem not_memIooMod_iff_to_Ioc_eq_right : ¬MemIooMod a b x ↔ toIocMod a hb x = a + b :=
  (memIooMod_iff_toIocMod_ne_right hb).not_left
#align not_mem_Ioo_mod_iff_to_Ioc_eq_right not_memIooMod_iff_to_Ioc_eq_right

theorem memIooMod_iff_toIcoDiv_eq_toIocDiv : MemIooMod a b x ↔ toIcoDiv a hb x = toIocDiv a hb x :=
  by
  rw [memIooMod_iff_toIcoMod_eq_toIocMod hb, toIcoMod, toIocMod, sub_right_inj,
    (zsmul_strictMono_left hb).Injective.eq_iff]
#align mem_Ioo_mod_iff_to_Ico_div_eq_to_Ioc_div memIooMod_iff_toIcoDiv_eq_toIocDiv

theorem memIooMod_iff_toIcoDiv_ne_toIocDiv_add_one :
    MemIooMod a b x ↔ toIcoDiv a hb x ≠ toIocDiv a hb x + 1 := by
  rw [memIooMod_iff_toIcoMod_add_period_ne_toIocMod hb, Ne, Ne, toIcoMod, toIocMod, ←
    eq_sub_iff_add_eq, sub_sub, sub_right_inj, ← add_one_zsmul,
    (zsmul_strictMono_left hb).Injective.eq_iff]
#align mem_Ioo_mod_iff_to_Ico_div_ne_to_Ioc_div_add_one memIooMod_iff_toIcoDiv_ne_toIocDiv_add_one

theorem not_memIooMod_iff_toIcoDiv_eq_toIocDiv_add_one :
    ¬MemIooMod a b x ↔ toIcoDiv a hb x = toIocDiv a hb x + 1 :=
  (memIooMod_iff_toIcoDiv_ne_toIocDiv_add_one hb).not_left
#align not_mem_Ioo_mod_iff_to_Ico_div_eq_to_Ioc_div_add_one not_memIooMod_iff_toIcoDiv_eq_toIocDiv_add_one

include hb

theorem memIooMod_iff_ne_add_zsmul : MemIooMod a b x ↔ ∀ z : ℤ, x ≠ a + z • b :=
  by
  rw [memIooMod_iff_toIcoMod_ne_left hb, ← not_iff_not]
  push_neg; constructor <;> intro h
  · rw [← h]
    exact ⟨_, (toIcoMod_add_toIcoDiv_zsmul _ _ _).symm⟩
  · rw [toIcoMod_eq_iff, Set.left_mem_Ico]
    exact ⟨lt_add_of_pos_right a hb, h⟩
#align mem_Ioo_mod_iff_ne_add_zsmul memIooMod_iff_ne_add_zsmul

theorem not_memIooMod_iff_eq_add_zsmul : ¬MemIooMod a b x ↔ ∃ z : ℤ, x = a + z • b := by
  simpa only [not_forall, not_ne_iff] using (memIooMod_iff_ne_add_zsmul hb).Not
#align not_mem_Ioo_mod_iff_eq_add_zsmul not_memIooMod_iff_eq_add_zsmul

theorem not_memIooMod_iff_eq_mod_zmultiples :
    ¬MemIooMod a b x ↔ (x : α ⧸ AddSubgroup.zmultiples b) = a := by
  simp_rw [not_memIooMod_iff_eq_add_zsmul hb, QuotientAddGroup.eq_iff_sub_mem,
    AddSubgroup.mem_zmultiples_iff, eq_sub_iff_add_eq', eq_comm]
#align not_mem_Ioo_mod_iff_eq_mod_zmultiples not_memIooMod_iff_eq_mod_zmultiples

theorem memIooMod_iff_ne_mod_zmultiples :
    MemIooMod a b x ↔ (x : α ⧸ AddSubgroup.zmultiples b) ≠ a :=
  (not_memIooMod_iff_eq_mod_zmultiples hb).not_right
#align mem_Ioo_mod_iff_ne_mod_zmultiples memIooMod_iff_ne_mod_zmultiples

theorem Ico_eq_locus_Ioc_eq_unionᵢ_Ioo :
    { x | toIcoMod a hb x = toIocMod a hb x } = ⋃ z : ℤ, Set.Ioo (a + z • b) (a + b + z • b) :=
  by
  ext1; simp_rw [Set.mem_setOf, Set.mem_unionᵢ, ← Set.sub_mem_Ioo_iff_left]
  exact (memIooMod_iff_toIcoMod_eq_toIocMod hb).symm
#align Ico_eq_locus_Ioc_eq_Union_Ioo Ico_eq_locus_Ioc_eq_unionᵢ_Ioo

theorem toIocDiv_wcovby_toIcoDiv (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocDiv a hb x ⩿ toIcoDiv a hb x :=
  by
  suffices toIocDiv a hb x = toIcoDiv a hb x ∨ toIocDiv a hb x + 1 = toIcoDiv a hb x by
    rwa [wcovby_iff_eq_or_covby, ← Order.succ_eq_iff_covby]
  rw [eq_comm, ← memIooMod_iff_toIcoDiv_eq_toIocDiv, eq_comm, ←
    not_memIooMod_iff_toIcoDiv_eq_toIocDiv_add_one]
  exact em _
#align to_Ioc_div_wcovby_to_Ico_div toIocDiv_wcovby_toIcoDiv

theorem toIcoMod_le_toIocMod (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a hb x ≤ toIocMod a hb x :=
  by
  rw [toIcoMod, toIocMod, sub_le_sub_iff_left]
  exact zsmul_mono_left hb.le (toIocDiv_wcovby_toIcoDiv _ _ _).le
#align to_Ico_mod_le_to_Ioc_mod toIcoMod_le_toIocMod

theorem toIocMod_le_toIcoMod_add (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a hb x ≤ toIcoMod a hb x + b :=
  by
  rw [toIcoMod, toIocMod, sub_add, sub_le_sub_iff_left, sub_le_iff_le_add, ← add_one_zsmul,
    (zsmul_strictMono_left hb).le_iff_le]
  apply (toIocDiv_wcovby_toIcoDiv _ _ _).le_succ
#align to_Ioc_mod_le_to_Ico_mod_add toIocMod_le_toIcoMod_add

end IcoIoc

theorem toIcoMod_eq_self {a b x : α} (hb : 0 < b) : toIcoMod a hb x = x ↔ x ∈ Set.Ico a (a + b) :=
  by
  rw [toIcoMod_eq_iff, and_iff_left]
  refine' ⟨0, _⟩
  simp
#align to_Ico_mod_eq_self toIcoMod_eq_self

theorem toIocMod_eq_self {a b x : α} (hb : 0 < b) : toIocMod a hb x = x ↔ x ∈ Set.Ioc a (a + b) :=
  by
  rw [toIocMod_eq_iff, and_iff_left]
  refine' ⟨0, _⟩
  simp
#align to_Ioc_mod_eq_self toIocMod_eq_self

@[simp]
theorem toIcoMod_toIcoMod (a₁ a₂ : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a₁ hb (toIcoMod a₂ hb x) = toIcoMod a₁ hb x :=
  by
  rw [toIcoMod_eq_toIcoMod]
  exact ⟨toIcoDiv a₂ hb x, self_sub_toIcoMod a₂ hb x⟩
#align to_Ico_mod_to_Ico_mod toIcoMod_toIcoMod

@[simp]
theorem toIcoMod_toIocMod (a₁ a₂ : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a₁ hb (toIocMod a₂ hb x) = toIcoMod a₁ hb x :=
  by
  rw [toIcoMod_eq_toIcoMod]
  exact ⟨toIocDiv a₂ hb x, self_sub_toIocMod a₂ hb x⟩
#align to_Ico_mod_to_Ioc_mod toIcoMod_toIocMod

@[simp]
theorem toIocMod_toIocMod (a₁ a₂ : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a₁ hb (toIocMod a₂ hb x) = toIocMod a₁ hb x :=
  by
  rw [toIocMod_eq_toIocMod]
  exact ⟨toIocDiv a₂ hb x, self_sub_toIocMod a₂ hb x⟩
#align to_Ioc_mod_to_Ioc_mod toIocMod_toIocMod

@[simp]
theorem toIocMod_toIcoMod (a₁ a₂ : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a₁ hb (toIcoMod a₂ hb x) = toIocMod a₁ hb x :=
  by
  rw [toIocMod_eq_toIocMod]
  exact ⟨toIcoDiv a₂ hb x, self_sub_toIcoMod a₂ hb x⟩
#align to_Ioc_mod_to_Ico_mod toIocMod_toIcoMod

theorem toIcoMod_periodic (a : α) {b : α} (hb : 0 < b) : Function.Periodic (toIcoMod a hb) b :=
  toIcoMod_add_right a hb
#align to_Ico_mod_periodic toIcoMod_periodic

theorem toIocMod_periodic (a : α) {b : α} (hb : 0 < b) : Function.Periodic (toIocMod a hb) b :=
  toIocMod_add_right a hb
#align to_Ioc_mod_periodic toIocMod_periodic

/-- `to_Ico_mod` as an equiv from the quotient. -/
@[simps symm_apply]
def quotientAddGroup.equivIcoMod (a : α) {b : α} (hb : 0 < b) :
    α ⧸ AddSubgroup.zmultiples b ≃ Set.Ico a (a + b)
    where
  toFun x :=
    ⟨(toIcoMod_periodic a hb).lift x, QuotientAddGroup.induction_on' x <| toIcoMod_mem_Ico a hb⟩
  invFun := coe
  right_inv x := Subtype.ext <| (toIcoMod_eq_self hb).mpr x.Prop
  left_inv x := by
    induction x using QuotientAddGroup.induction_on'
    dsimp
    rw [QuotientAddGroup.eq_iff_sub_mem, toIcoMod_sub_self]
    apply AddSubgroup.zsmul_mem_zmultiples
#align quotient_add_group.equiv_Ico_mod quotientAddGroup.equivIcoMod

@[simp]
theorem quotientAddGroup.equivIcoMod_coe (a : α) {b : α} (hb : 0 < b) (x : α) :
    quotientAddGroup.equivIcoMod a hb ↑x = ⟨toIcoMod a hb x, toIcoMod_mem_Ico a hb _⟩ :=
  rfl
#align quotient_add_group.equiv_Ico_mod_coe quotientAddGroup.equivIcoMod_coe

/-- `to_Ioc_mod` as an equiv  from the quotient. -/
@[simps symm_apply]
def quotientAddGroup.equivIocMod (a : α) {b : α} (hb : 0 < b) :
    α ⧸ AddSubgroup.zmultiples b ≃ Set.Ioc a (a + b)
    where
  toFun x :=
    ⟨(toIocMod_periodic a hb).lift x, QuotientAddGroup.induction_on' x <| toIocMod_mem_Ioc a hb⟩
  invFun := coe
  right_inv x := Subtype.ext <| (toIocMod_eq_self hb).mpr x.Prop
  left_inv x := by
    induction x using QuotientAddGroup.induction_on'
    dsimp
    rw [QuotientAddGroup.eq_iff_sub_mem, toIocMod_sub_self]
    apply AddSubgroup.zsmul_mem_zmultiples
#align quotient_add_group.equiv_Ioc_mod quotientAddGroup.equivIocMod

@[simp]
theorem quotientAddGroup.equivIocMod_coe (a : α) {b : α} (hb : 0 < b) (x : α) :
    quotientAddGroup.equivIocMod a hb ↑x = ⟨toIocMod a hb x, toIocMod_mem_Ioc a hb _⟩ :=
  rfl
#align quotient_add_group.equiv_Ioc_mod_coe quotientAddGroup.equivIocMod_coe

end LinearOrderedAddCommGroup

section LinearOrderedField

variable {α : Type _} [LinearOrderedField α] [FloorRing α]

theorem toIcoDiv_eq_floor (a : α) {b : α} (hb : 0 < b) (x : α) : toIcoDiv a hb x = ⌊(x - a) / b⌋ :=
  by
  refine' (eq_toIcoDiv_of_sub_zsmul_mem_Ico hb _).symm
  rw [Set.mem_Ico, zsmul_eq_mul, ← sub_nonneg, add_comm, sub_right_comm, ← sub_lt_iff_lt_add,
    sub_right_comm _ _ a]
  exact ⟨Int.sub_floor_div_mul_nonneg _ hb, Int.sub_floor_div_mul_lt _ hb⟩
#align to_Ico_div_eq_floor toIcoDiv_eq_floor

theorem toIocDiv_eq_neg_floor (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocDiv a hb x = -⌊(a + b - x) / b⌋ :=
  by
  refine' (eq_toIocDiv_of_sub_zsmul_mem_Ioc hb _).symm
  rw [Set.mem_Ioc, zsmul_eq_mul, Int.cast_neg, neg_mul, sub_neg_eq_add, ← sub_nonneg,
    sub_add_eq_sub_sub]
  refine' ⟨_, Int.sub_floor_div_mul_nonneg _ hb⟩
  rw [← add_lt_add_iff_right b, add_assoc, add_comm x, ← sub_lt_iff_lt_add, add_comm (_ * _), ←
    sub_lt_iff_lt_add]
  exact Int.sub_floor_div_mul_lt _ hb
#align to_Ioc_div_eq_neg_floor toIocDiv_eq_neg_floor

theorem toIcoDiv_zero_one (x : α) : toIcoDiv (0 : α) zero_lt_one x = ⌊x⌋ := by
  simp [toIcoDiv_eq_floor]
#align to_Ico_div_zero_one toIcoDiv_zero_one

theorem toIcoMod_eq_add_fract_mul (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIcoMod a hb x = a + Int.fract ((x - a) / b) * b :=
  by
  rw [toIcoMod, toIcoDiv_eq_floor, Int.fract]
  field_simp [hb.ne.symm]
  ring
#align to_Ico_mod_eq_add_fract_mul toIcoMod_eq_add_fract_mul

theorem toIcoMod_eq_fract_mul {b : α} (hb : 0 < b) (x : α) :
    toIcoMod 0 hb x = Int.fract (x / b) * b := by simp [toIcoMod_eq_add_fract_mul]
#align to_Ico_mod_eq_fract_mul toIcoMod_eq_fract_mul

theorem toIocMod_eq_sub_fract_mul (a : α) {b : α} (hb : 0 < b) (x : α) :
    toIocMod a hb x = a + b - Int.fract ((a + b - x) / b) * b :=
  by
  rw [toIocMod, toIocDiv_eq_neg_floor, Int.fract]
  field_simp [hb.ne.symm]
  ring
#align to_Ioc_mod_eq_sub_fract_mul toIocMod_eq_sub_fract_mul

theorem toIcoMod_zero_one (x : α) : toIcoMod (0 : α) zero_lt_one x = Int.fract x := by
  simp [toIcoMod_eq_add_fract_mul]
#align to_Ico_mod_zero_one toIcoMod_zero_one

end LinearOrderedField

/-! ### Lemmas about unions of translates of intervals -/

section Union

open Set Int

section LinearOrderedAddCommGroup

variable {α : Type _} [LinearOrderedAddCommGroup α] [Archimedean α] (a : α) {b : α} (hb : 0 < b)

include hb

theorem unionᵢ_Ioc_add_zsmul : (⋃ n : ℤ, Ioc (a + n • b) (a + (n + 1) • b)) = univ :=
  by
  refine' eq_univ_iff_forall.mpr fun x => mem_Union.mpr _
  rcases sub_toIocDiv_zsmul_mem_Ioc a hb x with ⟨hl, hr⟩
  refine' ⟨toIocDiv a hb x, ⟨lt_sub_iff_add_lt.mp hl, _⟩⟩
  rw [add_smul, one_smul, ← add_assoc]
  convert sub_le_iff_le_add.mp hr using 1; abel
#align Union_Ioc_add_zsmul unionᵢ_Ioc_add_zsmul

theorem unionᵢ_Ico_add_zsmul : (⋃ n : ℤ, Ico (a + n • b) (a + (n + 1) • b)) = univ :=
  by
  refine' eq_univ_iff_forall.mpr fun x => mem_Union.mpr _
  rcases sub_toIcoDiv_zsmul_mem_Ico a hb x with ⟨hl, hr⟩
  refine' ⟨toIcoDiv a hb x, ⟨le_sub_iff_add_le.mp hl, _⟩⟩
  rw [add_smul, one_smul, ← add_assoc]
  convert sub_lt_iff_lt_add.mp hr using 1; abel
#align Union_Ico_add_zsmul unionᵢ_Ico_add_zsmul

theorem unionᵢ_Icc_add_zsmul : (⋃ n : ℤ, Icc (a + n • b) (a + (n + 1) • b)) = univ := by
  simpa only [unionᵢ_Ioc_add_zsmul a hb, univ_subset_iff] using
    Union_mono fun n : ℤ => (Ioc_subset_Icc_self : Ioc (a + n • b) (a + (n + 1) • b) ⊆ Icc _ _)
#align Union_Icc_add_zsmul unionᵢ_Icc_add_zsmul

theorem unionᵢ_Ioc_zsmul : (⋃ n : ℤ, Ioc (n • b) ((n + 1) • b)) = univ := by
  simpa only [zero_add] using unionᵢ_Ioc_add_zsmul 0 hb
#align Union_Ioc_zsmul unionᵢ_Ioc_zsmul

theorem unionᵢ_Ico_zsmul : (⋃ n : ℤ, Ico (n • b) ((n + 1) • b)) = univ := by
  simpa only [zero_add] using unionᵢ_Ico_add_zsmul 0 hb
#align Union_Ico_zsmul unionᵢ_Ico_zsmul

theorem unionᵢ_Icc_zsmul : (⋃ n : ℤ, Icc (n • b) ((n + 1) • b)) = univ := by
  simpa only [zero_add] using unionᵢ_Icc_add_zsmul 0 hb
#align Union_Icc_zsmul unionᵢ_Icc_zsmul

end LinearOrderedAddCommGroup

section LinearOrderedRing

variable {α : Type _} [LinearOrderedRing α] [Archimedean α] (a : α)

theorem unionᵢ_Ioc_add_int_cast : (⋃ n : ℤ, Ioc (a + n) (a + n + 1)) = Set.univ := by
  simpa only [zsmul_one, Int.cast_add, Int.cast_one, ← add_assoc] using
    unionᵢ_Ioc_add_zsmul a zero_lt_one
#align Union_Ioc_add_int_cast unionᵢ_Ioc_add_int_cast

theorem unionᵢ_Ico_add_int_cast : (⋃ n : ℤ, Ico (a + n) (a + n + 1)) = Set.univ := by
  simpa only [zsmul_one, Int.cast_add, Int.cast_one, ← add_assoc] using
    unionᵢ_Ico_add_zsmul a zero_lt_one
#align Union_Ico_add_int_cast unionᵢ_Ico_add_int_cast

theorem unionᵢ_Icc_add_int_cast : (⋃ n : ℤ, Icc (a + n) (a + n + 1)) = Set.univ := by
  simpa only [zsmul_one, Int.cast_add, Int.cast_one, ← add_assoc] using
    unionᵢ_Icc_add_zsmul a (zero_lt_one' α)
#align Union_Icc_add_int_cast unionᵢ_Icc_add_int_cast

variable (α)

theorem unionᵢ_Ioc_int_cast : (⋃ n : ℤ, Ioc (n : α) (n + 1)) = Set.univ := by
  simpa only [zero_add] using unionᵢ_Ioc_add_int_cast (0 : α)
#align Union_Ioc_int_cast unionᵢ_Ioc_int_cast

theorem unionᵢ_Ico_int_cast : (⋃ n : ℤ, Ico (n : α) (n + 1)) = Set.univ := by
  simpa only [zero_add] using unionᵢ_Ico_add_int_cast (0 : α)
#align Union_Ico_int_cast unionᵢ_Ico_int_cast

theorem unionᵢ_Icc_int_cast : (⋃ n : ℤ, Icc (n : α) (n + 1)) = Set.univ := by
  simpa only [zero_add] using unionᵢ_Icc_add_int_cast (0 : α)
#align Union_Icc_int_cast unionᵢ_Icc_int_cast

end LinearOrderedRing

end Union


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Scott Morrison, Johan Commelin
-/
import data.finset.card

/-!
# Finsets in `fin n`

A few constructions for finsets in `fin n`.

## Main declarations

* `finset.fin_range`: `{0, 1, ..., n - 1}` as a `finset (fin n)`.
* `finset.attach_fin`: Turns a finset of naturals strictly less than `n` into a `finset (fin n)`.
-/

variables {n : ℕ}

namespace finset

/-- `finset.fin_range n` is the finset `{0, 1, ..., n - 1}`, as a `finset (fin n)`. -/
def fin_range (n : ℕ) : finset (fin n) := ⟨list.fin_range n, list.nodup_fin_range n⟩

@[simp]
lemma fin_range_card : (fin_range n).card = n := by simp [fin_range]

@[simp]
lemma mem_fin_range (m : fin n) : m ∈ fin_range n := list.mem_fin_range m

@[simp] lemma coe_fin_range (n : ℕ) : (fin_range n : set (fin n)) = set.univ :=
set.eq_univ_of_forall mem_fin_range

/-- Given a finset `s` of `ℕ` contained in `{0,..., n-1}`, the corresponding finset in `fin n`
is `s.attach_fin h` where `h` is a proof that all elements of `s` are less than `n`. -/
def attach_fin (s : finset ℕ) {n : ℕ} (h : ∀ m ∈ s, m < n) : finset (fin n) :=
⟨s.1.pmap (λ a ha, ⟨a, ha⟩) h, s.nodup.pmap $ λ _ _ _ _, fin.veq_of_eq⟩

@[simp] lemma mem_attach_fin {n : ℕ} {s : finset ℕ} (h : ∀ m ∈ s, m < n) {a : fin n} :
  a ∈ s.attach_fin h ↔ (a : ℕ) ∈ s :=
⟨λ h, let ⟨b, hb₁, hb₂⟩ := multiset.mem_pmap.1 h in hb₂ ▸ hb₁,
λ h, multiset.mem_pmap.2 ⟨a, h, fin.eta _ _⟩⟩

@[simp] lemma card_attach_fin {n : ℕ} (s : finset ℕ) (h : ∀ m ∈ s, m < n) :
  (s.attach_fin h).card = s.card :=
multiset.card_pmap _ _ _

end finset


def Set α := α → Prop

class HasMem (α : outParam $ Type u) (β : Type v) where
    mem : α → β → Prop

infix:50 " ∈ " => HasMem.mem

instance {α : Type u} : HasMem α (Set α) := ⟨λ a s => s a⟩

instance {α : Type u} : LE (Set α) := ⟨λ s t => ∀ {x : α}, x ∈ s → x ∈ t⟩

class HasInf (P : Type u) where
  inf : P → P → P

infix:70 " ⊓ " => HasInf.inf

instance {α : Type u} : HasInf (Set α) := ⟨λ s t x => x ∈ s ∧ x ∈ t⟩

theorem infLeLeft {s t : Set α} : s ⊓ t ≤ s := And.left
theorem infLeRight {s t : Set α} : s ⊓ t ≤ t := And.right

theorem inter_mem_sets_iff {α : Type u} (f : Set (Set α)) (hf : ∀ {s t}, s ∈ f → s ≤ t → t ∈ f) {x y : Set α}
        : x ⊓ y ∈ f → x ∈ f ∧ y ∈ f := by
  intro h
  refine ⟨hf h infLeLeft, hf h ?_⟩
  apply infLeRight


/-
Copyright (c) 2018 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.deprecated.subgroup
import Mathlib.deprecated.group
import Mathlib.PostPort

universes u l v u_1

namespace Mathlib

/-- `S` is a subring: a set containing 1 and closed under multiplication, addition and and additive inverse. -/
class is_subring {R : Type u} [ring R] (S : set R) extends is_add_subgroup S, is_submonoid S where

/-- The ring structure on a subring coerced to a type. -/
def subset.ring {R : Type u} [ring R] {S : set R} [is_subring S] : ring ↥S :=
  ring.mk add_comm_group.add sorry add_comm_group.zero sorry sorry add_comm_group.neg
    add_comm_group.sub sorry sorry monoid.mul sorry monoid.one sorry sorry sorry sorry

/-- The ring structure on a subring coerced to a type. -/
def subtype.ring {R : Type u} [ring R] {S : set R} [is_subring S] : ring (Subtype S) := subset.ring

namespace ring_hom

protected instance is_subring_preimage {R : Type u} {S : Type v} [ring R] [ring S] (f : R →+* S)
    (s : set S) [is_subring s] : is_subring (⇑f ⁻¹' s) :=
  is_subring.mk

protected instance is_subring_image {R : Type u} {S : Type v} [ring R] [ring S] (f : R →+* S)
    (s : set R) [is_subring s] : is_subring (⇑f '' s) :=
  is_subring.mk

protected instance is_subring_set_range {R : Type u} {S : Type v} [ring R] [ring S] (f : R →+* S) :
    is_subring (set.range ⇑f) :=
  is_subring.mk

end ring_hom

/-- Restrict the codomain of a ring homomorphism to a subring that includes the range. -/
def ring_hom.cod_restrict {R : Type u} {S : Type v} [ring R] [ring S] (f : R →+* S) (s : set S)
    [is_subring s] (h : ∀ (x : R), coe_fn f x ∈ s) : R →+* ↥s :=
  ring_hom.mk (fun (x : R) => { val := coe_fn f x, property := h x }) sorry sorry sorry sorry

/-- Coersion `S → R` as a ring homormorphism-/
def is_subring.subtype {R : Type u} [ring R] (S : set R) [is_subring S] : ↥S →+* R :=
  ring_hom.mk coe sorry sorry sorry sorry

@[simp] theorem is_subring.coe_subtype {R : Type u} [ring R] {S : set R} [is_subring S] :
    ⇑(is_subring.subtype S) = coe :=
  rfl

/-- The commutative ring structure on a subring coerced to a type. -/
def subset.comm_ring {cR : Type u} [comm_ring cR] {S : set cR} [is_subring S] : comm_ring ↥S :=
  comm_ring.mk ring.add sorry ring.zero sorry sorry ring.neg ring.sub sorry sorry ring.mul sorry
    ring.one sorry sorry sorry sorry sorry

/-- The commutative ring structure on a subring coerced to a type. -/
def subtype.comm_ring {cR : Type u} [comm_ring cR] {S : set cR} [is_subring S] :
    comm_ring (Subtype S) :=
  subset.comm_ring

/-- The integral domain structure on a subring of an integral domain coerced to a type. -/
def subring.domain {D : Type u_1} [integral_domain D] (S : set D) [is_subring S] :
    integral_domain ↥S :=
  integral_domain.mk comm_ring.add sorry comm_ring.zero sorry sorry comm_ring.neg comm_ring.sub
    sorry sorry comm_ring.mul sorry comm_ring.one sorry sorry sorry sorry sorry sorry sorry

protected instance is_subring.inter {R : Type u} [ring R] (S₁ : set R) (S₂ : set R) [is_subring S₁]
    [is_subring S₂] : is_subring (S₁ ∩ S₂) :=
  is_subring.mk

protected instance is_subring.Inter {R : Type u} [ring R] {ι : Sort u_1} (S : ι → set R)
    [h : ∀ (y : ι), is_subring (S y)] : is_subring (set.Inter S) :=
  is_subring.mk

theorem is_subring_Union_of_directed {R : Type u} [ring R] {ι : Type u_1} [hι : Nonempty ι]
    (s : ι → set R) [∀ (i : ι), is_subring (s i)]
    (directed : ∀ (i j : ι), ∃ (k : ι), s i ⊆ s k ∧ s j ⊆ s k) :
    is_subring (set.Union fun (i : ι) => s i) :=
  is_subring.mk

namespace ring

def closure {R : Type u} [ring R] (s : set R) : set R := add_group.closure (monoid.closure s)

theorem exists_list_of_mem_closure {R : Type u} [ring R] {s : set R} {a : R} (h : a ∈ closure s) :
    ∃ (L : List (List R)),
        (∀ (l : List R), l ∈ L → ∀ (x : R), x ∈ l → x ∈ s ∨ x = -1) ∧
          list.sum (list.map list.prod L) = a :=
  sorry

protected theorem in_closure.rec_on {R : Type u} [ring R] {s : set R} {C : R → Prop} {x : R}
    (hx : x ∈ closure s) (h1 : C 1) (hneg1 : C (-1))
    (hs : ∀ (z : R), z ∈ s → ∀ (n : R), C n → C (z * n)) (ha : ∀ {x y : R}, C x → C y → C (x + y)) :
    C x :=
  sorry

protected instance closure.is_subring {R : Type u} [ring R] {s : set R} : is_subring (closure s) :=
  is_subring.mk

theorem mem_closure {R : Type u} [ring R] {s : set R} {a : R} : a ∈ s → a ∈ closure s :=
  add_group.mem_closure ∘ monoid.subset_closure

theorem subset_closure {R : Type u} [ring R] {s : set R} : s ⊆ closure s :=
  fun (_x : R) => mem_closure

theorem closure_subset {R : Type u} [ring R] {s : set R} {t : set R} [is_subring t] :
    s ⊆ t → closure s ⊆ t :=
  add_group.closure_subset ∘ monoid.closure_subset

theorem closure_subset_iff {R : Type u} [ring R] (s : set R) (t : set R) [is_subring t] :
    closure s ⊆ t ↔ s ⊆ t :=
  iff.trans (add_group.closure_subset_iff (monoid.closure s) t)
    { mp := set.subset.trans monoid.subset_closure, mpr := monoid.closure_subset }

theorem closure_mono {R : Type u} [ring R] {s : set R} {t : set R} (H : s ⊆ t) :
    closure s ⊆ closure t :=
  closure_subset (set.subset.trans H subset_closure)

theorem image_closure {R : Type u} [ring R] {S : Type u_1} [ring S] (f : R →+* S) (s : set R) :
    ⇑f '' closure s = closure (⇑f '' s) :=
  sorry

end Mathlib


-- import tactic

-- lemma eq_zero_of_five_mul_seven_mul_eq_zero {G : Type*} [add_comm_group G] (a : G)
--   (h_five : 5 • a = 0) (h_seven : 7 • a = 0) : a = 0 :=
-- begin
-- end


/-
Copyright © 2020 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nicolò Cavalleri

! This file was ported from Lean 3 source module geometry.manifold.algebra.smooth_functions
! leanprover-community/mathlib commit d1bd9c5df2867c1cb463bc6364446d57bdd9f7f1
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Geometry.Manifold.Algebra.Structures

/-!
# Algebraic structures over smooth functions

In this file, we define instances of algebraic structures over smooth functions.
-/

noncomputable section

open Manifold

variable {𝕜 : Type _} [NontriviallyNormedField 𝕜] {E : Type _} [NormedAddCommGroup E]
  [NormedSpace 𝕜 E] {E' : Type _} [NormedAddCommGroup E'] [NormedSpace 𝕜 E'] {H : Type _}
  [TopologicalSpace H] {I : ModelWithCorners 𝕜 E H} {H' : Type _} [TopologicalSpace H']
  {I' : ModelWithCorners 𝕜 E' H'} {N : Type _} [TopologicalSpace N] [ChartedSpace H N]
  {E'' : Type _} [NormedAddCommGroup E''] [NormedSpace 𝕜 E''] {H'' : Type _} [TopologicalSpace H'']
  {I'' : ModelWithCorners 𝕜 E'' H''} {N' : Type _} [TopologicalSpace N'] [ChartedSpace H'' N']

namespace SmoothMap

@[to_additive]
instance hasMul {G : Type _} [Mul G] [TopologicalSpace G] [ChartedSpace H' G] [HasSmoothMul I' G] :
    Mul C^∞⟮I, N; I', G⟯ :=
  ⟨fun f g => ⟨f * g, f.Smooth.mul g.Smooth⟩⟩
#align smooth_map.has_mul SmoothMap.hasMul
#align smooth_map.has_add SmoothMap.hasAdd

@[simp, to_additive]
theorem coe_mul {G : Type _} [Mul G] [TopologicalSpace G] [ChartedSpace H' G] [HasSmoothMul I' G]
    (f g : C^∞⟮I, N; I', G⟯) : ⇑(f * g) = f * g :=
  rfl
#align smooth_map.coe_mul SmoothMap.coe_mul
#align smooth_map.coe_add SmoothMap.coe_add

@[simp, to_additive]
theorem mul_comp {G : Type _} [Mul G] [TopologicalSpace G] [ChartedSpace H' G] [HasSmoothMul I' G]
    (f g : C^∞⟮I'', N'; I', G⟯) (h : C^∞⟮I, N; I'', N'⟯) : (f * g).comp h = f.comp h * g.comp h :=
  by ext <;> simp only [ContMdiffMap.comp_apply, coe_mul, Pi.mul_apply]
#align smooth_map.mul_comp SmoothMap.mul_comp
#align smooth_map.add_comp SmoothMap.add_comp

@[to_additive]
instance hasOne {G : Type _} [Monoid G] [TopologicalSpace G] [ChartedSpace H' G] :
    One C^∞⟮I, N; I', G⟯ :=
  ⟨ContMdiffMap.const (1 : G)⟩
#align smooth_map.has_one SmoothMap.hasOne
#align smooth_map.has_zero SmoothMap.hasZero

@[simp, to_additive]
theorem coe_one {G : Type _} [Monoid G] [TopologicalSpace G] [ChartedSpace H' G] :
    ⇑(1 : C^∞⟮I, N; I', G⟯) = 1 :=
  rfl
#align smooth_map.coe_one SmoothMap.coe_one
#align smooth_map.coe_zero SmoothMap.coe_zero

section GroupStructure

/-!
### Group structure

In this section we show that smooth functions valued in a Lie group inherit a group structure
under pointwise multiplication.
-/

@[to_additive]
instance semigroup {G : Type _} [Semigroup G] [TopologicalSpace G] [ChartedSpace H' G]
    [HasSmoothMul I' G] : Semigroup C^∞⟮I, N; I', G⟯ :=
  { SmoothMap.hasMul with mul_assoc := fun a b c => by ext <;> exact mul_assoc _ _ _ }
#align smooth_map.semigroup SmoothMap.semigroup
#align smooth_map.add_semigroup SmoothMap.addSemigroup

@[to_additive]
instance monoid {G : Type _} [Monoid G] [TopologicalSpace G] [ChartedSpace H' G]
    [HasSmoothMul I' G] : Monoid C^∞⟮I, N; I', G⟯ :=
  { SmoothMap.semigroup,
    SmoothMap.hasOne with
    one_mul := fun a => by ext <;> exact one_mul _
    mul_one := fun a => by ext <;> exact mul_one _ }
#align smooth_map.monoid SmoothMap.monoid
#align smooth_map.add_monoid SmoothMap.addMonoid

/-- Coercion to a function as an `monoid_hom`. Similar to `monoid_hom.coe_fn`. -/
@[to_additive "Coercion to a function as an `add_monoid_hom`. Similar to `add_monoid_hom.coe_fn`.",
  simps]
def coeFnMonoidHom {G : Type _} [Monoid G] [TopologicalSpace G] [ChartedSpace H' G]
    [HasSmoothMul I' G] : C^∞⟮I, N; I', G⟯ →* N → G
    where
  toFun := coeFn
  map_one' := coe_one
  map_mul' := coe_mul
#align smooth_map.coe_fn_monoid_hom SmoothMap.coeFnMonoidHom
#align smooth_map.coe_fn_add_monoid_hom SmoothMap.coeFnAddMonoidHom

@[to_additive]
instance commMonoid {G : Type _} [CommMonoid G] [TopologicalSpace G] [ChartedSpace H' G]
    [HasSmoothMul I' G] : CommMonoid C^∞⟮I, N; I', G⟯ :=
  { SmoothMap.monoid, SmoothMap.hasOne with mul_comm := fun a b => by ext <;> exact mul_comm _ _ }
#align smooth_map.comm_monoid SmoothMap.commMonoid
#align smooth_map.add_comm_monoid SmoothMap.addCommMonoid

@[to_additive]
instance group {G : Type _} [Group G] [TopologicalSpace G] [ChartedSpace H' G] [LieGroup I' G] :
    Group C^∞⟮I, N; I', G⟯ :=
  { SmoothMap.monoid with
    inv := fun f => ⟨fun x => (f x)⁻¹, f.Smooth.inv⟩
    mul_left_inv := fun a => by ext <;> exact mul_left_inv _
    div := fun f g => ⟨f / g, f.Smooth.div g.Smooth⟩
    div_eq_mul_inv := fun f g => by ext <;> exact div_eq_mul_inv _ _ }
#align smooth_map.group SmoothMap.group
#align smooth_map.add_group SmoothMap.addGroup

@[simp, to_additive]
theorem coe_inv {G : Type _} [Group G] [TopologicalSpace G] [ChartedSpace H' G] [LieGroup I' G]
    (f : C^∞⟮I, N; I', G⟯) : ⇑f⁻¹ = f⁻¹ :=
  rfl
#align smooth_map.coe_inv SmoothMap.coe_inv
#align smooth_map.coe_neg SmoothMap.coe_neg

@[simp, to_additive]
theorem coe_div {G : Type _} [Group G] [TopologicalSpace G] [ChartedSpace H' G] [LieGroup I' G]
    (f g : C^∞⟮I, N; I', G⟯) : ⇑(f / g) = f / g :=
  rfl
#align smooth_map.coe_div SmoothMap.coe_div
#align smooth_map.coe_sub SmoothMap.coe_sub

@[to_additive]
instance commGroup {G : Type _} [CommGroup G] [TopologicalSpace G] [ChartedSpace H' G]
    [LieGroup I' G] : CommGroup C^∞⟮I, N; I', G⟯ :=
  { SmoothMap.group, SmoothMap.commMonoid with }
#align smooth_map.comm_group SmoothMap.commGroup
#align smooth_map.add_comm_group SmoothMap.addCommGroup

end GroupStructure

section RingStructure

/-!
### Ring stucture

In this section we show that smooth functions valued in a smooth ring `R` inherit a ring structure
under pointwise multiplication.
-/

instance semiring {R : Type _} [Semiring R] [TopologicalSpace R] [ChartedSpace H' R]
    [SmoothRing I' R] : Semiring C^∞⟮I, N; I', R⟯ :=
  { SmoothMap.addCommMonoid,
    SmoothMap.monoid with
    left_distrib := fun a b c => by ext <;> exact left_distrib _ _ _
    right_distrib := fun a b c => by ext <;> exact right_distrib _ _ _
    zero_mul := fun a => by ext <;> exact MulZeroClass.zero_mul _
    mul_zero := fun a => by ext <;> exact MulZeroClass.mul_zero _ }
#align smooth_map.semiring SmoothMap.semiring

instance ring {R : Type _} [Ring R] [TopologicalSpace R] [ChartedSpace H' R] [SmoothRing I' R] :
    Ring C^∞⟮I, N; I', R⟯ :=
  { SmoothMap.semiring, SmoothMap.addCommGroup with }
#align smooth_map.ring SmoothMap.ring

instance commRing {R : Type _} [CommRing R] [TopologicalSpace R] [ChartedSpace H' R]
    [SmoothRing I' R] : CommRing C^∞⟮I, N; I', R⟯ :=
  { SmoothMap.semiring, SmoothMap.addCommGroup, SmoothMap.commMonoid with }
#align smooth_map.comm_ring SmoothMap.commRing

/-- Coercion to a function as a `ring_hom`. -/
@[simps]
def coeFnRingHom {R : Type _} [CommRing R] [TopologicalSpace R] [ChartedSpace H' R]
    [SmoothRing I' R] : C^∞⟮I, N; I', R⟯ →+* N → R :=
  { (coeFnMonoidHom : C^∞⟮I, N; I', R⟯ →* _), (coeFnAddMonoidHom : C^∞⟮I, N; I', R⟯ →+ _) with
    toFun := coeFn }
#align smooth_map.coe_fn_ring_hom SmoothMap.coeFnRingHom

/-- `function.eval` as a `ring_hom` on the ring of smooth functions. -/
def evalRingHom {R : Type _} [CommRing R] [TopologicalSpace R] [ChartedSpace H' R] [SmoothRing I' R]
    (n : N) : C^∞⟮I, N; I', R⟯ →+* R :=
  (Pi.evalRingHom _ n : (N → R) →+* R).comp SmoothMap.coeFnRingHom
#align smooth_map.eval_ring_hom SmoothMap.evalRingHom

end RingStructure

section ModuleStructure

/-!
### Semiodule stucture

In this section we show that smooth functions valued in a vector space `M` over a normed
field `𝕜` inherit a vector space structure.
-/

instance hasSmul {V : Type _} [NormedAddCommGroup V] [NormedSpace 𝕜 V] :
    SMul 𝕜 C^∞⟮I, N; 𝓘(𝕜, V), V⟯ :=
  ⟨fun r f => ⟨r • f, smooth_const.smul f.Smooth⟩⟩
#align smooth_map.has_smul SmoothMap.hasSmul

@[simp]
theorem coe_smul {V : Type _} [NormedAddCommGroup V] [NormedSpace 𝕜 V] (r : 𝕜)
    (f : C^∞⟮I, N; 𝓘(𝕜, V), V⟯) : ⇑(r • f) = r • f :=
  rfl
#align smooth_map.coe_smul SmoothMap.coe_smul

@[simp]
theorem smul_comp {V : Type _} [NormedAddCommGroup V] [NormedSpace 𝕜 V] (r : 𝕜)
    (g : C^∞⟮I'', N'; 𝓘(𝕜, V), V⟯) (h : C^∞⟮I, N; I'', N'⟯) : (r • g).comp h = r • g.comp h :=
  rfl
#align smooth_map.smul_comp SmoothMap.smul_comp

instance module {V : Type _} [NormedAddCommGroup V] [NormedSpace 𝕜 V] :
    Module 𝕜 C^∞⟮I, N; 𝓘(𝕜, V), V⟯ :=
  Function.Injective.module 𝕜 coeFnAddMonoidHom ContMdiffMap.coe_inj coe_smul
#align smooth_map.module SmoothMap.module

/-- Coercion to a function as a `linear_map`. -/
@[simps]
def coeFnLinearMap {V : Type _} [NormedAddCommGroup V] [NormedSpace 𝕜 V] :
    C^∞⟮I, N; 𝓘(𝕜, V), V⟯ →ₗ[𝕜] N → V :=
  {
    (coeFnAddMonoidHom : C^∞⟮I, N; 𝓘(𝕜, V), V⟯ →+
        _) with
    toFun := coeFn
    map_smul' := coe_smul }
#align smooth_map.coe_fn_linear_map SmoothMap.coeFnLinearMap

end ModuleStructure

section AlgebraStructure

/-!
### Algebra structure

In this section we show that smooth functions valued in a normed algebra `A` over a normed field `𝕜`
inherit an algebra structure.
-/

variable {A : Type _} [NormedRing A] [NormedAlgebra 𝕜 A] [SmoothRing 𝓘(𝕜, A) A]

/-- Smooth constant functions as a `ring_hom`. -/
def c : 𝕜 →+* C^∞⟮I, N; 𝓘(𝕜, A), A⟯
    where
  toFun := fun c : 𝕜 => ⟨fun x => (algebraMap 𝕜 A) c, smooth_const⟩
  map_one' := by ext x <;> exact (algebraMap 𝕜 A).map_one
  map_mul' c₁ c₂ := by ext x <;> exact (algebraMap 𝕜 A).map_mul _ _
  map_zero' := by ext x <;> exact (algebraMap 𝕜 A).map_zero
  map_add' c₁ c₂ := by ext x <;> exact (algebraMap 𝕜 A).map_add _ _
#align smooth_map.C SmoothMap.c

instance algebra : Algebra 𝕜 C^∞⟮I, N; 𝓘(𝕜, A), A⟯ :=
  {
    SmoothMap.semiring with
    smul := fun r f => ⟨r • f, smooth_const.smul f.Smooth⟩
    toRingHom := SmoothMap.c
    commutes' := fun c f => by ext x <;> exact Algebra.commutes' _ _
    smul_def' := fun c f => by ext x <;> exact Algebra.smul_def' _ _ }
#align smooth_map.algebra SmoothMap.algebra

/-- Coercion to a function as an `alg_hom`. -/
@[simps]
def coeFnAlgHom : C^∞⟮I, N; 𝓘(𝕜, A), A⟯ →ₐ[𝕜] N → A
    where
  toFun := coeFn
  commutes' r := rfl
  -- `..(smooth_map.coe_fn_ring_hom : C^∞⟮I, N; 𝓘(𝕜, A), A⟯ →+* _)` times out for some reason
  map_zero' := SmoothMap.coe_zero
  map_one' := SmoothMap.coe_one
  map_add' := SmoothMap.coe_add
  map_mul' := SmoothMap.coe_mul
#align smooth_map.coe_fn_alg_hom SmoothMap.coeFnAlgHom

end AlgebraStructure

section ModuleOverContinuousFunctions

/-!
### Structure as module over scalar functions

If `V` is a module over `𝕜`, then we show that the space of smooth functions from `N` to `V`
is naturally a vector space over the ring of smooth functions from `N` to `𝕜`. -/

instance hasSmul' {V : Type _} [NormedAddCommGroup V] [NormedSpace 𝕜 V] :
    SMul C^∞⟮I, N; 𝕜⟯ C^∞⟮I, N; 𝓘(𝕜, V), V⟯ :=
  ⟨fun f g => ⟨fun x => f x • g x, Smooth.smul f.2 g.2⟩⟩
#align smooth_map.has_smul' SmoothMap.hasSmul'

@[simp]
theorem smul_comp' {V : Type _} [NormedAddCommGroup V] [NormedSpace 𝕜 V] (f : C^∞⟮I'', N'; 𝕜⟯)
    (g : C^∞⟮I'', N'; 𝓘(𝕜, V), V⟯) (h : C^∞⟮I, N; I'', N'⟯) :
    (f • g).comp h = f.comp h • g.comp h :=
  rfl
#align smooth_map.smul_comp' SmoothMap.smul_comp'

instance module' {V : Type _} [NormedAddCommGroup V] [NormedSpace 𝕜 V] :
    Module C^∞⟮I, N; 𝓘(𝕜), 𝕜⟯ C^∞⟮I, N; 𝓘(𝕜, V), V⟯
    where
  smul := (· • ·)
  smul_add c f g := by ext x <;> exact smul_add (c x) (f x) (g x)
  add_smul c₁ c₂ f := by ext x <;> exact add_smul (c₁ x) (c₂ x) (f x)
  mul_smul c₁ c₂ f := by ext x <;> exact mul_smul (c₁ x) (c₂ x) (f x)
  one_smul f := by ext x <;> exact one_smul 𝕜 (f x)
  zero_smul f := by ext x <;> exact zero_smul _ _
  smul_zero r := by ext x <;> exact smul_zero _
#align smooth_map.module' SmoothMap.module'

end ModuleOverContinuousFunctions

end SmoothMap


/-
Copyright (c) 2022 Alex Kontorovich and Heather Macbeth. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alex Kontorovich, Heather Macbeth
-/

import measure_theory.measure.haar
import measure_theory.group.fundamental_domain
import algebra.group.opposite

/-!
# Haar quotient measure

In this file, we consider properties of fundamental domains and measures for the action of a
subgroup of a group `G` on `G` itself.

## Main results

* `measure_theory.is_fundamental_domain.smul_invariant_measure_map `: given a subgroup `Γ` of a
  topological group `G`, the pushforward to the coset space `G ⧸ Γ` of the restriction of a both
  left- and right-invariant measure on `G` to a fundamental domain `𝓕` is a `G`-invariant measure
  on `G ⧸ Γ`.

* `measure_theory.is_fundamental_domain.is_mul_left_invariant_map `: given a normal subgroup `Γ` of
  a topological group `G`, the pushforward to the quotient group `G ⧸ Γ` of the restriction of
  a both left- and right-invariant measure on `G` to a fundamental domain `𝓕` is a left-invariant
  measure on `G ⧸ Γ`.

Note that a group `G` with Haar measure that is both left and right invariant is called
**unimodular**.
-/

open set measure_theory topological_space measure_theory.measure
open_locale pointwise nnreal

variables {G : Type*} [group G] [measurable_space G] [topological_space G]
  [topological_group G] [borel_space G]
  {μ : measure G}
  {Γ : subgroup G}

/-- Measurability of the action of the topological group `G` on the left-coset space `G/Γ`. -/
@[to_additive "Measurability of the action of the additive topological group `G` on the left-coset
  space `G/Γ`."]
instance quotient_group.has_measurable_smul [measurable_space (G ⧸ Γ)] [borel_space (G ⧸ Γ)] :
  has_measurable_smul G (G ⧸ Γ) :=
{ measurable_const_smul := λ g, (continuous_const_smul g).measurable,
  measurable_smul_const := λ x, (quotient_group.continuous_smul₁ x).measurable }

variables {𝓕 : set G} (h𝓕 : is_fundamental_domain Γ.opposite 𝓕 μ)
include h𝓕

variables [countable Γ] [measurable_space (G ⧸ Γ)] [borel_space (G ⧸ Γ)]

/-- The pushforward to the coset space `G ⧸ Γ` of the restriction of a both left- and right-
  invariant measure on `G` to a fundamental domain `𝓕` is a `G`-invariant measure on `G ⧸ Γ`. -/
@[to_additive "The pushforward to the coset space `G ⧸ Γ` of the restriction of a both left- and
  right-invariant measure on an additive topological group `G` to a fundamental domain `𝓕` is a
  `G`-invariant measure on `G ⧸ Γ`."]
lemma measure_theory.is_fundamental_domain.smul_invariant_measure_map
  [μ.is_mul_left_invariant] [μ.is_mul_right_invariant] :
  smul_invariant_measure G (G ⧸ Γ) (measure.map quotient_group.mk (μ.restrict 𝓕)) :=
{ measure_preimage_smul :=
  begin
    let π : G → G ⧸ Γ := quotient_group.mk,
    have meas_π : measurable π :=
      continuous_quotient_mk.measurable,
    have 𝓕meas : null_measurable_set 𝓕 μ := h𝓕.null_measurable_set,
    intros g A hA,
    have meas_πA : measurable_set (π ⁻¹' A) := measurable_set_preimage meas_π hA,
    rw [measure.map_apply meas_π hA,
      measure.map_apply meas_π (measurable_set_preimage (measurable_const_smul g) hA),
      measure.restrict_apply₀' 𝓕meas, measure.restrict_apply₀' 𝓕meas],
    set π_preA := π ⁻¹' A,
    have : (quotient_group.mk ⁻¹' ((λ (x : G ⧸ Γ), g • x) ⁻¹' A)) = has_mul.mul g ⁻¹' π_preA,
    { ext1, simp },
    rw this,
    have : μ (has_mul.mul g ⁻¹' π_preA ∩ 𝓕) = μ (π_preA ∩ has_mul.mul (g⁻¹) ⁻¹' 𝓕),
    { transitivity μ (has_mul.mul g ⁻¹' (π_preA ∩ has_mul.mul g⁻¹ ⁻¹' 𝓕)),
      { rw preimage_inter,
        congr,
        rw [← preimage_comp, comp_mul_left, mul_left_inv],
        ext,
        simp, },
      rw measure_preimage_mul, },
    rw this,
    have h𝓕_translate_fundom : is_fundamental_domain Γ.opposite (g • 𝓕) μ := h𝓕.smul_of_comm g,
    rw [h𝓕.measure_set_eq h𝓕_translate_fundom meas_πA, ← preimage_smul_inv], refl,
    rintros ⟨γ, γ_in_Γ⟩,
    ext,
    have : π (x * (mul_opposite.unop γ)) = π (x) := by simpa [quotient_group.eq'] using γ_in_Γ,
    simp [(•), this],
  end }

/-- Assuming `Γ` is a normal subgroup of a topological group `G`, the pushforward to the quotient
  group `G ⧸ Γ` of the restriction of a both left- and right-invariant measure on `G` to a
  fundamental domain `𝓕` is a left-invariant measure on `G ⧸ Γ`. -/
@[to_additive "Assuming `Γ` is a normal subgroup of an additive topological group `G`, the
  pushforward to the quotient group `G ⧸ Γ` of the restriction of a both left- and right-invariant
  measure on `G` to a fundamental domain `𝓕` is a left-invariant measure on `G ⧸ Γ`."]
lemma measure_theory.is_fundamental_domain.is_mul_left_invariant_map [subgroup.normal Γ]
  [μ.is_mul_left_invariant] [μ.is_mul_right_invariant] :
  (measure.map (quotient_group.mk' Γ) (μ.restrict 𝓕)).is_mul_left_invariant :=
{ map_mul_left_eq_self := begin
    intros x,
    apply measure.ext,
    intros A hA,
    obtain ⟨x₁, _⟩ := @quotient.exists_rep _ (quotient_group.left_rel Γ) x,
    haveI := h𝓕.smul_invariant_measure_map,
    convert measure_preimage_smul x₁ ((measure.map quotient_group.mk) (μ.restrict 𝓕)) A using 1,
    rw [← h, measure.map_apply],
    { refl, },
    { exact measurable_const_mul _, },
    { exact hA, },
  end }

variables [t2_space (G ⧸ Γ)] [second_countable_topology (G ⧸ Γ)] (K : positive_compacts (G ⧸ Γ))

/-- Given a normal subgroup `Γ` of a topological group `G` with Haar measure `μ`, which is also
  right-invariant, and a finite volume fundamental domain `𝓕`, the pushforward to the quotient
  group `G ⧸ Γ` of the restriction of `μ` to `𝓕` is a multiple of Haar measure on `G ⧸ Γ`. -/
@[to_additive "Given a normal subgroup `Γ` of an additive topological group `G` with Haar measure
  `μ`, which is also right-invariant, and a finite volume fundamental domain `𝓕`, the pushforward
  to the quotient group `G ⧸ Γ` of the restriction of `μ` to `𝓕` is a multiple of Haar measure on
  `G ⧸ Γ`."]
lemma measure_theory.is_fundamental_domain.map_restrict_quotient [subgroup.normal Γ]
  [measure_theory.measure.is_haar_measure μ] [μ.is_mul_right_invariant]
  (h𝓕_finite : μ 𝓕 < ⊤) : measure.map (quotient_group.mk' Γ) (μ.restrict 𝓕)
  = (μ (𝓕 ∩ (quotient_group.mk' Γ) ⁻¹' K)) • (measure_theory.measure.haar_measure K) :=
begin
  let π : G →* G ⧸ Γ := quotient_group.mk' Γ,
  have meas_π : measurable π := continuous_quotient_mk.measurable,
  have 𝓕meas : null_measurable_set 𝓕 μ := h𝓕.null_measurable_set,
  haveI : is_finite_measure (μ.restrict 𝓕) :=
    ⟨by { rw [measure.restrict_apply₀' 𝓕meas, univ_inter], exact h𝓕_finite }⟩,
  -- the measure is left-invariant, so by the uniqueness of Haar measure it's enough to show that
  -- it has the stated size on the reference compact set `K`.
  haveI : (measure.map (quotient_group.mk' Γ) (μ.restrict 𝓕)).is_mul_left_invariant :=
    h𝓕.is_mul_left_invariant_map,
  rw [measure.haar_measure_unique (measure.map (quotient_group.mk' Γ) (μ.restrict 𝓕)) K,
    measure.map_apply meas_π, measure.restrict_apply₀' 𝓕meas, inter_comm],
  exact K.is_compact.measurable_set,
end

/-- Given a normal subgroup `Γ` of a topological group `G` with Haar measure `μ`, which is also
  right-invariant, and a finite volume fundamental domain `𝓕`, the quotient map to `G ⧸ Γ` is
  measure-preserving between appropriate multiples of Haar measure on `G` and `G ⧸ Γ`. -/
@[to_additive measure_preserving_quotient_add_group.mk' "Given a normal subgroup `Γ` of an additive
  topological group `G` with Haar measure `μ`, which is also right-invariant, and a finite volume
  fundamental domain `𝓕`, the quotient map to `G ⧸ Γ` is measure-preserving between appropriate
  multiples of Haar measure on `G` and `G ⧸ Γ`."]
lemma measure_preserving_quotient_group.mk' [subgroup.normal Γ]
  [measure_theory.measure.is_haar_measure μ] [μ.is_mul_right_invariant]
  (h𝓕_finite : μ 𝓕 < ⊤) (c : ℝ≥0) (h : μ (𝓕 ∩ (quotient_group.mk' Γ) ⁻¹' K) = c) :
  measure_preserving
    (quotient_group.mk' Γ)
    (μ.restrict 𝓕)
    (c • (measure_theory.measure.haar_measure K)) :=
{ measurable := continuous_quotient_mk.measurable,
  map_eq := by rw [h𝓕.map_restrict_quotient K h𝓕_finite, h]; refl }


import tactic.trunc_cases
import tactic.interactive
import tactic.congr
import data.quot

example (t : trunc ℕ) : punit :=
begin
  trunc_cases t,
  exact (),
  -- no more goals, because `trunc_cases` used the correct `trunc.rec_on_subsingleton` recursor
end

example (t : trunc ℕ) : ℕ :=
begin
  trunc_cases t,
  guard_hyp t : ℕ, -- verify that the new hypothesis is still called `t`.
  exact 0,
  -- verify that we don't even need to use `simp`,
  -- because `trunc_cases` has already removed the `eq.rec`.
  refl,
end

example {α : Type} [subsingleton α] (I : trunc (has_zero α)) : α :=
begin
  trunc_cases I,
  exact 0,
end

/-- A mock typeclass, set up so that it's possible to extract data from `trunc (has_unit α)`. -/
class has_unit (α : Type) [has_one α] :=
(unit : α)
(unit_eq_one : unit = 1)

def u {α : Type} [has_one α] [has_unit α] : α := has_unit.unit
attribute [simp] has_unit.unit_eq_one

example {α : Type} [has_one α] (I : trunc (has_unit α)) : α :=
begin
  trunc_cases I,
  exact u, -- Verify that the typeclass is immediately available
  -- Verify that there's no `eq.rec` in the goal.
  (do tgt ← tactic.target, eq_rec ← tactic.mk_const `eq.rec, guard $ ¬ eq_rec.occurs tgt),
  simp [u],
end

universes v w z

/-- Transport through a product is given by individually transporting each component. -/
-- It's a pity that this is no good as a `simp` lemma.
-- (It seems the unification problem with `λ a, W a × Z a` is too hard.)
-- (One could write a tactic to syntactically analyse `eq.rec` expressions
-- and simplify more of them!)
lemma eq_rec_prod {α : Sort v} (W : α → Type w) (Z : α → Type z) {a b : α} (p : W a × Z a) (h : a = b) :
  @eq.rec α a (λ a, W a × Z a) p b h = (@eq.rec α a W p.1 b h, @eq.rec α a Z p.2 b h) :=
begin
  cases h,
  simp only [prod.mk.eta],
end

-- This time, we make a goal that (quite artificially) depends on the `trunc`.
example {α : Type} [has_one α] (I : trunc (has_unit α)) : α × plift (I = I) :=
begin
  -- This time `trunc_cases` has no choice but to use `trunc.rec_on`.
  trunc_cases I,
  { exact ⟨u, plift.up rfl⟩, },
  { -- And so we get an `eq.rec` in the invariance goal.
    -- Since `simp` can't handle it because of the unification problem,
    -- for now we have to handle it by hand.
    convert eq_rec_prod (λ I, α) (λ I, plift (I = I)) _ _,
    { simp [u], },
    { ext, } }
end