/-
Copyright (c) 2021 Benjamin Davidson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Benjamin Davidson
-/
import algebra.big_operators.basic
import algebra.field.opposite
import algebra.module.basic
import algebra.order.archimedean
import data.int.parity
import group_theory.coset
import group_theory.subgroup.zpowers
import group_theory.submonoid.membership

/-!
# Periodicity

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

In this file we define and then prove facts about periodic and antiperiodic functions.

## Main definitions

* `function.periodic`: A function `f` is *periodic* if `∀ x, f (x + c) = f x`.
  `f` is referred to as periodic with period `c` or `c`-periodic.

* `function.antiperiodic`: A function `f` is *antiperiodic* if `∀ x, f (x + c) = -f x`.
  `f` is referred to as antiperiodic with antiperiod `c` or `c`-antiperiodic.

Note that any `c`-antiperiodic function will necessarily also be `2*c`-periodic.

## Tags

period, periodic, periodicity, antiperiodic
-/

variables {α β γ : Type*} {f g : α → β} {c c₁ c₂ x : α}

open_locale big_operators

namespace function

/-! ### Periodicity -/

/-- A function `f` is said to be `periodic` with period `c` if for all `x`, `f (x + c) = f x`. -/
@[simp] def periodic [has_add α] (f : α → β) (c : α) : Prop :=
∀ x : α, f (x + c) = f x

protected lemma periodic.funext [has_add α] (h : periodic f c) :
  (λ x, f (x + c)) = f :=
funext h

protected lemma periodic.comp [has_add α] (h : periodic f c) (g : β → γ) :
  periodic (g ∘ f) c :=
by simp * at *

lemma periodic.comp_add_hom [has_add α] [has_add γ]
  (h : periodic f c) (g : add_hom γ α) (g_inv : α → γ) (hg : right_inverse g_inv g) :
  periodic (f ∘ g) (g_inv c) :=
λ x, by simp only [hg c, h (g x), add_hom.map_add, comp_app]

@[to_additive]
protected lemma periodic.mul [has_add α] [has_mul β] (hf : periodic f c) (hg : periodic g c) :
  periodic (f * g) c :=
by simp * at *

@[to_additive]
protected lemma periodic.div [has_add α] [has_div β] (hf : periodic f c) (hg : periodic g c) :
  periodic (f / g) c :=
by simp * at *

@[to_additive]
lemma _root_.list.periodic_prod [has_add α] [monoid β]
  (l : list (α → β)) (hl : ∀ f ∈ l, periodic f c) :
  periodic l.prod c :=
begin
  induction l with g l ih hl,
  { simp, },
  { rw [list.forall_mem_cons] at hl,
    simpa only [list.prod_cons] using hl.1.mul (ih hl.2) }
end

@[to_additive]
lemma _root_.multiset.periodic_prod [has_add α] [comm_monoid β]
  (s : multiset (α → β)) (hs : ∀ f ∈ s, periodic f c) :
  periodic s.prod c :=
s.prod_to_list ▸ s.to_list.periodic_prod $ λ f hf, hs f $ multiset.mem_to_list.mp hf

@[to_additive]
lemma _root_.finset.periodic_prod [has_add α] [comm_monoid β]
  {ι : Type*} {f : ι → α → β} (s : finset ι) (hs : ∀ i ∈ s, periodic (f i) c) :
  periodic (∏ i in s, f i) c :=
s.prod_to_list f ▸ (s.to_list.map f).periodic_prod (by simpa [-periodic])

@[to_additive]
protected lemma periodic.smul [has_add α] [has_smul γ β] (h : periodic f c) (a : γ) :
  periodic (a • f) c :=
by simp * at *

protected lemma periodic.const_smul [add_monoid α] [group γ] [distrib_mul_action γ α]
  (h : periodic f c) (a : γ) :
  periodic (λ x, f (a • x)) (a⁻¹ • c) :=
λ x, by simpa only [smul_add, smul_inv_smul] using h (a • x)

lemma periodic.const_smul₀ [add_comm_monoid α] [division_semiring γ] [module γ α]
  (h : periodic f c) (a : γ) :
  periodic (λ x, f (a • x)) (a⁻¹ • c) :=
begin
  intro x,
  by_cases ha : a = 0, { simp only [ha, zero_smul] },
  simpa only [smul_add, smul_inv_smul₀ ha] using h (a • x),
end

protected lemma periodic.const_mul [division_semiring α] (h : periodic f c) (a : α) :
  periodic (λ x, f (a * x)) (a⁻¹ * c) :=
h.const_smul₀ a

lemma periodic.const_inv_smul [add_monoid α] [group γ] [distrib_mul_action γ α]
  (h : periodic f c) (a : γ) :
  periodic (λ x, f (a⁻¹ • x)) (a • c) :=
by simpa only [inv_inv] using h.const_smul a⁻¹

lemma periodic.const_inv_smul₀ [add_comm_monoid α] [division_semiring γ] [module γ α]
  (h : periodic f c) (a : γ) :
  periodic (λ x, f (a⁻¹ • x)) (a • c) :=
by simpa only [inv_inv] using h.const_smul₀ a⁻¹

lemma periodic.const_inv_mul [division_semiring α] (h : periodic f c) (a : α) :
  periodic (λ x, f (a⁻¹ * x)) (a * c) :=
h.const_inv_smul₀ a

lemma periodic.mul_const [division_semiring α] (h : periodic f c) (a : α) :
  periodic (λ x, f (x * a)) (c * a⁻¹) :=
h.const_smul₀ $ mul_opposite.op a

lemma periodic.mul_const' [division_semiring α]
  (h : periodic f c) (a : α) :
  periodic (λ x, f (x * a)) (c / a) :=
by simpa only [div_eq_mul_inv] using h.mul_const a

lemma periodic.mul_const_inv [division_semiring α] (h : periodic f c) (a : α) :
  periodic (λ x, f (x * a⁻¹)) (c * a) :=
h.const_inv_smul₀ $ mul_opposite.op a

lemma periodic.div_const [division_semiring α] (h : periodic f c) (a : α) :
  periodic (λ x, f (x / a)) (c * a) :=
by simpa only [div_eq_mul_inv] using h.mul_const_inv a

lemma periodic.add_period [add_semigroup α] (h1 : periodic f c₁) (h2 : periodic f c₂) :
  periodic f (c₁ + c₂) :=
by simp [*, ← add_assoc] at *

lemma periodic.sub_eq [add_group α] (h : periodic f c) (x : α) :
  f (x - c) = f x :=
by simpa only [sub_add_cancel] using (h (x - c)).symm

lemma periodic.sub_eq' [add_comm_group α] (h : periodic f c) :
  f (c - x) = f (-x) :=
by simpa only [sub_eq_neg_add] using h (-x)

protected lemma periodic.neg [add_group α] (h : periodic f c) :
  periodic f (-c) :=
by simpa only [sub_eq_add_neg, periodic] using h.sub_eq

lemma periodic.sub_period [add_group α] (h1 : periodic f c₁) (h2 : periodic f c₂) :
  periodic f (c₁ - c₂) :=
by simpa only [sub_eq_add_neg] using h1.add_period h2.neg

lemma periodic.const_add [add_semigroup α] (h : periodic f c) (a : α) :
  periodic (λ x, f (a + x)) c :=
λ x, by simpa [add_assoc] using h (a + x)

lemma periodic.add_const [add_comm_semigroup α] (h : periodic f c) (a : α) :
  periodic (λ x, f (x + a)) c :=
by simpa only [add_comm] using h.const_add a

lemma periodic.const_sub [add_comm_group α] (h : periodic f c) (a : α) :
  periodic (λ x, f (a - x)) c :=
λ x, by simp only [← sub_sub, h.sub_eq]

lemma periodic.sub_const [add_comm_group α] (h : periodic f c) (a : α) :
  periodic (λ x, f (x - a)) c :=
by simpa only [sub_eq_add_neg] using h.add_const (-a)

lemma periodic.nsmul [add_monoid α] (h : periodic f c) (n : ℕ) :
  periodic f (n • c) :=
by induction n; simp [nat.succ_eq_add_one, add_nsmul, ← add_assoc, zero_nsmul, *] at *

lemma periodic.nat_mul [semiring α] (h : periodic f c) (n : ℕ) :
  periodic f (n * c) :=
by simpa only [nsmul_eq_mul] using h.nsmul n

lemma periodic.neg_nsmul [add_group α] (h : periodic f c) (n : ℕ) :
  periodic f (-(n • c)) :=
(h.nsmul n).neg

lemma periodic.neg_nat_mul [ring α] (h : periodic f c) (n : ℕ) :
  periodic f (-(n * c)) :=
(h.nat_mul n).neg

lemma periodic.sub_nsmul_eq [add_group α] (h : periodic f c) (n : ℕ) :
  f (x - n • c) = f x :=
by simpa only [sub_eq_add_neg] using h.neg_nsmul n x

lemma periodic.sub_nat_mul_eq [ring α] (h : periodic f c) (n : ℕ) :
  f (x - n * c) = f x :=
by simpa only [nsmul_eq_mul] using h.sub_nsmul_eq n

lemma periodic.nsmul_sub_eq [add_comm_group α] (h : periodic f c) (n : ℕ) :
  f (n • c - x) = f (-x) :=
(h.nsmul n).sub_eq'

lemma periodic.nat_mul_sub_eq [ring α] (h : periodic f c) (n : ℕ) :
  f (n * c - x) = f (-x) :=
by simpa only [sub_eq_neg_add] using h.nat_mul n (-x)

protected lemma periodic.zsmul [add_group α] (h : periodic f c) (n : ℤ) :
  periodic f (n • c) :=
begin
  cases n,
  { simpa only [int.of_nat_eq_coe, coe_nat_zsmul] using h.nsmul n },
  { simpa only [zsmul_neg_succ_of_nat] using (h.nsmul n.succ).neg },
end

protected

lemma periodic.sub_zsmul_eq [add_group α] (h : periodic f c) (n : ℤ) :
  f (x - n • c) = f x :=
(h.zsmul n).sub_eq x

lemma periodic.sub_int_mul_eq [ring α] (h : periodic f c) (n : ℤ) :
  f (x - n * c) = f x :=
(h.int_mul n).sub_eq x

lemma periodic.zsmul_sub_eq [add_comm_group α] (h : periodic f c) (n : ℤ) :
  f (n • c - x) = f (-x) :=
(h.zsmul _).sub_eq'

lemma periodic.int_mul_sub_eq [ring α] (h : periodic f c) (n : ℤ) :
  f (n * c - x) = f (-x) :=
(h.int_mul _).sub_eq'

protected lemma periodic.eq [add_zero_class α] (h : periodic f c) :
  f c = f 0 :=
by simpa only [zero_add] using h 0

protected lemma periodic.neg_eq [add_group α] (h : periodic f c) :
  f (-c) = f 0 :=
h.neg.eq

protected lemma periodic.nsmul_eq [add_monoid α] (h : periodic f c) (n : ℕ) :
  f (n • c) = f 0 :=
(h.nsmul n).eq

lemma periodic.nat_mul_eq [semiring α] (h : periodic f c) (n : ℕ) :
  f (n * c) = f 0 :=
(h.nat_mul n).eq

lemma periodic.zsmul_eq [add_group α] (h : periodic f c) (n : ℤ) :
  f (n • c) = f 0 :=
(h.zsmul n).eq

lemma periodic.int_mul_eq [ring α] (h : periodic f c) (n : ℤ) :
  f (n * c) = f 0 :=
(h.int_mul n).eq

/-- If a function `f` is `periodic` with positive period `c`, then for all `x` there exists some
  `y ∈ Ico 0 c` such that `f x = f y`. -/
lemma periodic.exists_mem_Ico₀ [linear_ordered_add_comm_group α] [archimedean α]
  (h : periodic f c) (hc : 0 < c) (x) :
  ∃ y ∈ set.Ico 0 c, f x = f y :=
let ⟨n, H, _⟩ := exists_unique_zsmul_near_of_pos' hc x in
⟨x - n • c, H, (h.sub_zsmul_eq n).symm⟩

/-- If a function `f` is `periodic` with positive period `c`, then for all `x` there exists some
  `y ∈ Ico a (a + c)` such that `f x = f y`. -/
lemma periodic.exists_mem_Ico [linear_ordered_add_comm_group α] [archimedean α]
  (h : periodic f c) (hc : 0 < c) (x a) :
  ∃ y ∈ set.Ico a (a + c), f x = f y :=
let ⟨n, H, _⟩ := exists_unique_add_zsmul_mem_Ico hc x a in
⟨x + n • c, H, (h.zsmul n x).symm⟩

/-- If a function `f` is `periodic` with positive period `c`, then for all `x` there exists some
  `y ∈ Ioc a (a + c)` such that `f x = f y`. -/
lemma periodic.exists_mem_Ioc [linear_ordered_add_comm_group α] [archimedean α]
  (h : periodic f c) (hc : 0 < c) (x a) :
  ∃ y ∈ set.Ioc a (a + c), f x = f y :=
let ⟨n, H, _⟩ := exists_unique_add_zsmul_mem_Ioc hc x a in
⟨x + n • c, H, (h.zsmul n x).symm⟩

lemma periodic.image_Ioc [linear_ordered_add_comm_group α] [archimedean α]
  (h : periodic f c) (hc : 0 < c) (a : α) :
  f '' set.Ioc a (a + c) = set.range f :=
(set.image_subset_range _ _).antisymm $ set.range_subset_iff.2 $ λ x,
  let ⟨y, hy, hyx⟩ := h.exists_mem_Ioc hc x a in ⟨y, hy, hyx.symm⟩

lemma periodic_with_period_zero [add_zero_class α]
  (f : α → β) :
  periodic f 0 :=
λ x, by rw add_zero

lemma periodic.map_vadd_zmultiples [add_comm_group α] (hf : periodic f c)
  (a : add_subgroup.zmultiples c) (x : α) :
  f (a +ᵥ x) = f x :=
by { rcases a with ⟨_, m, rfl⟩, simp [add_subgroup.vadd_def, add_comm _ x, hf.zsmul m x] }

lemma periodic.map_vadd_multiples [add_comm_monoid α] (hf : periodic f c)
  (a : add_submonoid.multiples c) (x : α) :
  f (a +ᵥ x) = f x :=
by { rcases a with ⟨_, m, rfl⟩, simp [add_submonoid.vadd_def, add_comm _ x, hf.nsmul m x] }

/-- Lift a periodic function to a function from the quotient group. -/
def periodic.lift [add_group α] (h : periodic f c) (x : α ⧸ add_subgroup.zmultiples c) : β :=
quotient.lift_on' x f $
  λ a b h', (begin
    rw quotient_add_group.left_rel_apply at h',
    obtain ⟨k, hk⟩ := h',
    exact (h.zsmul k _).symm.trans (congr_arg f (add_eq_of_eq_neg_add hk)),
   end)

@[simp] lemma periodic.lift_coe [add_group α] (h : periodic f c) (a : α) :
  h.lift (a : α ⧸ add_subgroup.zmultiples c) = f a :=
rfl

/-! ### Antiperiodicity -/

/-- A function `f` is said to be `antiperiodic` with antiperiod `c` if for all `x`,
  `f (x + c) = -f x`. -/
@[simp] def antiperiodic [has_add α] [has_neg β] (f : α → β) (c : α) : Prop :=
∀ x : α, f (x + c) = -f x

protected lemma antiperiodic.funext [has_add α] [has_neg β] (h : antiperiodic f c) :
  (λ x, f (x + c)) = -f :=
funext h

protected lemma antiperiodic.funext' [has_add α] [has_involutive_neg β] (h : antiperiodic f c) :
  (λ x, -f (x + c)) = f :=
neg_eq_iff_eq_neg.mpr h.funext

/-- If a function is `antiperiodic` with antiperiod `c`, then it is also `periodic` with period
  `2 * c`. -/
protected lemma antiperiodic.periodic [semiring α] [has_involutive_neg β] (h : antiperiodic f c) :
  periodic f (2 * c) :=
by simp [two_mul, ← add_assoc, h _]

protected lemma antiperiodic.eq [add_zero_class α] [has_neg β] (h : antiperiodic f c) :
  f c = -f 0 :=
by simpa only [zero_add] using h 0

lemma antiperiodic.nat_even_mul_periodic [semiring α] [has_involutive_neg β]
  (h : antiperiodic f c) (n : ℕ) :
  periodic f (n * (2 * c)) :=
h.periodic.nat_mul n

lemma antiperiodic.nat_odd_mul_antiperiodic [semiring α] [has_involutive_neg β]
  (h : antiperiodic f c) (n : ℕ) :
  antiperiodic f (n * (2 * c) + c) :=
λ x, by rw [← add_assoc, h, h.periodic.nat_mul]

lemma antiperiodic.int_even_mul_periodic [ring α] [has_involutive_neg β]
  (h : antiperiodic f c) (n : ℤ) :
  periodic f (n * (2 * c)) :=
h.periodic.int_mul n

lemma antiperiodic.int_odd_mul_antiperiodic [ring α] [has_involutive_neg β]
  (h : antiperiodic f c) (n : ℤ) :
  antiperiodic f (n * (2 * c) + c) :=
λ x, by rw [← add_assoc, h, h.periodic.int_mul]

lemma antiperiodic.sub_eq [add_group α] [has_involutive_neg β]
  (h : antiperiodic f c) (x : α) :
  f (x - c) = -f x :=
by rw [← neg_eq_iff_eq_neg, ← h (x - c), sub_add_cancel]

lemma antiperiodic.sub_eq' [add_comm_group α] [has_neg β] (h : antiperiodic f c) :
  f (c - x) = -f (-x) :=
by simpa only [sub_eq_neg_add] using h (-x)

protected lemma antiperiodic.neg [add_group α] [has_involutive_neg β]
  (h : antiperiodic f c) :
  antiperiodic f (-c) :=
by simpa only [sub_eq_add_neg, antiperiodic] using h.sub_eq

lemma antiperiodic.neg_eq [add_group α] [has_involutive_neg β]
  (h : antiperiodic f c) :
  f (-c) = -f 0 :=
by simpa only [zero_add] using h.neg 0

lemma antiperiodic.nat_mul_eq_of_eq_zero [ring α] [neg_zero_class β]
  (h : antiperiodic f c) (hi : f 0 = 0) : ∀ n : ℕ, f (n * c) = 0
| 0 := by rwa [nat.cast_zero, zero_mul]
| (n + 1) := by simp [add_mul, antiperiodic.nat_mul_eq_of_eq_zero n, h _]

lemma antiperiodic.int_mul_eq_of_eq_zero [ring α] [subtraction_monoid β]
  (h : antiperiodic f c) (hi : f 0 = 0) : ∀ n : ℤ, f (n * c) = 0
| (n : ℕ) := by rwa [int.cast_coe_nat, h.nat_mul_eq_of_eq_zero]
| -[1+n] := by rw [int.cast_neg_succ_of_nat, neg_mul, ← mul_neg, h.neg.nat_mul_eq_of_eq_zero hi]

lemma antiperiodic.const_add [add_semigroup α] [has_neg β] (h : antiperiodic f c) (a : α) :
  antiperiodic (λ x, f (a + x)) c :=
λ x, by simpa [add_assoc] using h (a + x)

lemma antiperiodic.add_const [add_comm_semigroup α] [has_neg β] (h : antiperiodic f c) (a : α) :
  antiperiodic (λ x, f (x + a)) c :=
λ x, by simpa only [add_right_comm] using h (x + a)

lemma antiperiodic.const_sub [add_comm_group α] [has_involutive_neg β] (h : antiperiodic f c)
  (a : α) : antiperiodic (λ x, f (a - x)) c :=
λ x, by simp only [← sub_sub, h.sub_eq]

lemma antiperiodic.sub_const [add_comm_group α] [has_neg β] (h : antiperiodic f c) (a : α) :
  antiperiodic (λ x, f (x - a)) c :=
by simpa only [sub_eq_add_neg] using h.add_const (-a)

protected lemma antiperiodic.smul [has_add α] [monoid γ] [add_group β] [distrib_mul_action γ β]
  (h : antiperiodic f c) (a : γ) :
  antiperiodic (a • f) c :=
by simp * at *

lemma antiperiodic.const_smul [add_monoid α] [has_neg β] [group γ] [distrib_mul_action γ α]
  (h : antiperiodic f c) (a : γ) :
  antiperiodic (λ x, f (a • x)) (a⁻¹ • c) :=
λ x, by simpa only [smul_add, smul_inv_smul] using h (a • x)

lemma antiperiodic.const_smul₀ [add_comm_monoid α] [has_neg β] [division_semiring γ] [module γ α]
  (h : antiperiodic f c) {a : γ} (ha : a ≠ 0) :
  antiperiodic (λ x, f (a • x)) (a⁻¹ • c) :=
λ x, by simpa only [smul_add, smul_inv_smul₀ ha] using h (a • x)

lemma antiperiodic.const_mul [division_semiring α] [has_neg β]
  (h : antiperiodic f c) {a : α} (ha : a ≠ 0) :
  antiperiodic (λ x, f (a * x)) (a⁻¹ * c) :=
h.const_smul₀ ha

lemma antiperiodic.const_inv_smul [add_monoid α] [has_neg β] [group γ] [distrib_mul_action γ α]
  (h : antiperiodic f c) (a : γ) :
  antiperiodic (λ x, f (a⁻¹ • x)) (a • c) :=
by simpa only [inv_inv] using h.const_smul a⁻¹

lemma antiperiodic.const_inv_smul₀
  [add_comm_monoid α] [has_neg β] [division_semiring γ] [module γ α]
  (h : antiperiodic f c) {a : γ} (ha : a ≠ 0) :
  antiperiodic (λ x, f (a⁻¹ • x)) (a • c) :=
by simpa only [inv_inv] using h.const_smul₀ (inv_ne_zero ha)

lemma antiperiodic.const_inv_mul [division_semiring α] [has_neg β]
  (h : antiperiodic f c) {a : α} (ha : a ≠ 0) :
  antiperiodic (λ x, f (a⁻¹ * x)) (a * c) :=
h.const_inv_smul₀ ha

lemma antiperiodic.mul_const [division_semiring α] [has_neg β]
  (h : antiperiodic f c) {a : α} (ha : a ≠ 0) :
  antiperiodic (λ x, f (x * a)) (c * a⁻¹) :=
h.const_smul₀ $ (mul_opposite.op_ne_zero_iff a).mpr ha

lemma antiperiodic.mul_const' [division_semiring α] [has_neg β]
  (h : antiperiodic f c) {a : α} (ha : a ≠ 0) :
  antiperiodic (λ x, f (x * a)) (c / a) :=
by simpa only [div_eq_mul_inv] using h.mul_const ha

lemma antiperiodic.mul_const_inv [division_semiring α] [has_neg β]
  (h : antiperiodic f c) {a : α} (ha : a ≠ 0) :
  antiperiodic (λ x, f (x * a⁻¹)) (c * a) :=
h.const_inv_smul₀ $ (mul_opposite.op_ne_zero_iff a).mpr ha

protected lemma antiperiodic.div_inv [division_semiring α] [has_neg β]
  (h : antiperiodic f c) {a : α} (ha : a ≠ 0) :
  antiperiodic (λ x, f (x / a)) (c * a) :=
by simpa only [div_eq_mul_inv] using h.mul_const_inv ha

protected lemma antiperiodic.add [add_group α] [has_involutive_neg β]
  (h1 : antiperiodic f c₁) (h2 : antiperiodic f c₂) :
  periodic f (c₁ + c₂) :=
by simp [*, ← add_assoc] at *

protected lemma antiperiodic.sub [add_group α] [has_involutive_neg β]
  (h1 : antiperiodic f c₁) (h2 : antiperiodic f c₂) :
  periodic f (c₁ - c₂) :=
by simpa only [sub_eq_add_neg] using h1.add h2.neg

lemma periodic.add_antiperiod [add_group α] [has_neg β]
  (h1 : periodic f c₁) (h2 : antiperiodic f c₂) :
  antiperiodic f (c₁ + c₂) :=
by simp [*, ← add_assoc] at *

lemma periodic.sub_antiperiod [add_group α] [has_involutive_neg β]
  (h1 : periodic f c₁) (h2 : antiperiodic f c₂) :
  antiperiodic f (c₁ - c₂) :=
by simpa only [sub_eq_add_neg] using h1.add_antiperiod h2.neg

lemma periodic.add_antiperiod_eq [add_group α] [has_neg β]
  (h1 : periodic f c₁) (h2 : antiperiodic f c₂) :
  f (c₁ + c₂) = -f 0 :=
(h1.add_antiperiod h2).eq

lemma periodic.sub_antiperiod_eq [add_group α] [has_involutive_neg β]
  (h1 : periodic f c₁) (h2 : antiperiodic f c₂) :
  f (c₁ - c₂) = -f 0 :=
(h1.sub_antiperiod h2).eq

protected lemma antiperiodic.mul [has_add α] [has_mul β] [has_distrib_neg β]
  (hf : antiperiodic f c) (hg : antiperiodic g c) :
  periodic (f * g) c :=
by simp * at *

protected lemma antiperiodic.div [has_add α] [division_monoid β] [has_distrib_neg β]
  (hf : antiperiodic f c) (hg : antiperiodic g c) :
  periodic (f / g) c :=
by simp [*, neg_div_neg_eq] at *

end function

lemma int.fract_periodic (α) [linear_ordered_ring α] [floor_ring α] :
  function.periodic int.fract (1 : α) :=
by exact_mod_cast λ a, int.fract_add_int a 1


/-
Copyright (c) 2020 Kevin Kappelmann. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Kappelmann
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.continued_fractions.computation.approximations
import Mathlib.algebra.continued_fractions.computation.correctness_terminating
import Mathlib.data.rat.default
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Termination of Continued Fraction Computations (`gcf.of`)

## Summary
We show that the continued fraction for a value `v`, as defined in
`algebra.continued_fractions.computation.basic`, terminates if and only if `v` corresponds to a
rational number, that is `↑v = q` for some `q : ℚ`.

## Main Theorems

- `gcf.coe_of_rat` shows that `gcf.of v = gcf.of q` for `v : α` given that `↑v = q` and `q : ℚ`.
- `gcf.terminates_iff_rat` shows that `gcf.of v` terminates if and only if `↑v = q` for some
  `q : ℚ`.

## Tags

rational, continued fraction, termination
-/

namespace generalized_continued_fraction

/-
We will have to constantly coerce along our structures in the following proofs using their provided
map functions.
-/

/-!
We want to show that the computation of a continued fraction `gcf.of v` terminates if and only if
`v ∈ ℚ`. In the next section, we show the implication from left to right.

We first show that every finite convergent corresponds to a rational number `q` and then use the
finite correctness proof (`of_correctness_of_terminates`) of `gcf.of` to show that `v = ↑q`.
-/

theorem exists_gcf_pair_rat_eq_of_nth_conts_aux {K : Type u_1} [linear_ordered_field K]
    [floor_ring K] (v : K) (n : ℕ) :
    ∃ (conts : pair ℚ),
        continuants_aux (generalized_continued_fraction.of v) n = pair.map coe conts :=
  sorry

theorem exists_gcf_pair_rat_eq_nth_conts {K : Type u_1} [linear_ordered_field K] [floor_ring K]
    (v : K) (n : ℕ) :
    ∃ (conts : pair ℚ), continuants (generalized_continued_fraction.of v) n = pair.map coe conts :=
  sorry

theorem exists_rat_eq_nth_numerator {K : Type u_1} [linear_ordered_field K] [floor_ring K] (v : K)
    (n : ℕ) : ∃ (q : ℚ), numerators (generalized_continued_fraction.of v) n = ↑q :=
  sorry

theorem exists_rat_eq_nth_denominator {K : Type u_1} [linear_ordered_field K] [floor_ring K] (v : K)
    (n : ℕ) : ∃ (q : ℚ), denominators (generalized_continued_fraction.of v) n = ↑q :=
  sorry

/-- Every finite convergent corresponds to a rational number. -/
theorem exists_rat_eq_nth_convergent {K : Type u_1} [linear_ordered_field K] [floor_ring K] (v : K)
    (n : ℕ) : ∃ (q : ℚ), convergents (generalized_continued_fraction.of v) n = ↑q :=
  sorry

/-- Every terminating continued fraction corresponds to a rational number. -/
theorem exists_rat_eq_of_terminates {K : Type u_1} [linear_ordered_field K] [floor_ring K] {v : K}
    (terminates : terminates (generalized_continued_fraction.of v)) : ∃ (q : ℚ), v = ↑q :=
  sorry

/-!
Before we can show that the continued fraction of a rational number terminates, we have to prove
some technical translation lemmas. More precisely, in this section, we show that, given a rational
number `q : ℚ` and value `v : K` with `v = ↑q`, the continued fraction of `q` and `v` coincide.
In particular, we show that `(↑(gcf.of q : gcf ℚ) : gcf K) = gcf.of v` in `gcf.coe_of_rat`.

To do this, we proceed bottom-up, showing the correspondence between the basic functions involved in
the computation first and then lift the results step-by-step.
-/

/- The lifting works for arbitrary linear ordered, archimedean fields with a floor function. -/

/-! First, we show the correspondence for the very basic functions in `gcf.int_fract_pair`. -/

namespace int_fract_pair

theorem coe_of_rat_eq {K : Type u_1} [linear_ordered_field K] [floor_ring K] [archimedean K] {v : K}
    {q : ℚ} (v_eq_q : v = ↑q) : mapFr coe (int_fract_pair.of q) = int_fract_pair.of v :=
  sorry

theorem coe_stream_nth_rat_eq {K : Type u_1} [linear_ordered_field K] [floor_ring K] [archimedean K]
    {v : K} {q : ℚ} (v_eq_q : v = ↑q) (n : ℕ) :
    option.map (mapFr coe) (int_fract_pair.stream q n) = int_fract_pair.stream v n :=
  sorry

theorem coe_stream_rat_eq {K : Type u_1} [linear_ordered_field K] [floor_ring K] [archimedean K]
    {v : K} {q : ℚ} (v_eq_q : v = ↑q) :
    stream.map (option.map (mapFr coe)) (int_fract_pair.stream q) = int_fract_pair.stream v :=
  funext fun (n : ℕ) => coe_stream_nth_rat_eq v_eq_q n

end int_fract_pair

/-! Now we lift the coercion results to the continued fraction computation. -/

theorem coe_of_h_rat_eq {K : Type u_1} [linear_ordered_field K] [floor_ring K] [archimedean K]
    {v : K} {q : ℚ} (v_eq_q : v = ↑q) :
    ↑(h (generalized_continued_fraction.of q)) = h (generalized_continued_fraction.of v) :=
  sorry

theorem coe_of_s_nth_rat_eq {K : Type u_1} [linear_ordered_field K] [floor_ring K] [archimedean K]
    {v : K} {q : ℚ} (v_eq_q : v = ↑q) (n : ℕ) :
    option.map (pair.map coe) (seq.nth (s (generalized_continued_fraction.of q)) n) =
        seq.nth (s (generalized_continued_fraction.of v)) n :=
  sorry

theorem coe_of_s_rat_eq {K : Type u_1} [linear_ordered_field K] [floor_ring K] [archimedean K]
    {v : K} {q : ℚ} (v_eq_q : v = ↑q) :
    seq.map (pair.map coe) (s (generalized_continued_fraction.of q)) =
        s (generalized_continued_fraction.of v) :=
  sorry

/-- Given `(v : K), (q : ℚ), and v = q`, we have that `gcf.of q = gcf.of v` -/
theorem coe_of_rat_eq {K : Type u_1} [linear_ordered_field K] [floor_ring K] [archimedean K] {v : K}
    {q : ℚ} (v_eq_q : v = ↑q) :
    mk (↑(h (generalized_continued_fraction.of q)))
          (seq.map (pair.map coe) (s (generalized_continued_fraction.of q))) =
        generalized_continued_fraction.of v :=
  sorry

theorem of_terminates_iff_of_rat_terminates {K : Type u_1} [linear_ordered_field K] [floor_ring K]
    [archimedean K] {v : K} {q : ℚ} (v_eq_q : v = ↑q) :
    terminates (generalized_continued_fraction.of v) ↔
        terminates (generalized_continued_fraction.of q) :=
  sorry

/-!
Finally, we show that the continued fraction of a rational number terminates.

The crucial insight is that, given any `q : ℚ` with `0 < q < 1`, the numerator of `fract q` is
smaller than the numerator of `q`. As the continued fraction computation recursively operates on
the fractional part of a value `v` and `0 ≤ fract v < 1`, we infer that the numerator of the
fractional part in the computation decreases by at least one in each step. As `0 ≤ fract v`,
this process must stop after finite number of steps, and the computation hence terminates.
-/

namespace int_fract_pair

/--
Shows that for any `q : ℚ` with `0 < q < 1`, the numerator of the fractional part of
`int_fract_pair.of q⁻¹` is smaller than the numerator of `q`.
-/
theorem of_inv_fr_num_lt_num_of_pos {q : ℚ} (q_pos : 0 < q) :
    rat.num (fr (int_fract_pair.of (q⁻¹))) < rat.num q :=
  rat.fract_inv_num_lt_num_of_pos q_pos

/-- Shows that the sequence of numerators of the fractional parts of the stream is strictly
monotonically decreasing. -/
theorem stream_succ_nth_fr_num_lt_nth_fr_num_rat {q : ℚ} {n : ℕ} {ifp_n : int_fract_pair ℚ}
    {ifp_succ_n : int_fract_pair ℚ} (stream_nth_eq : int_fract_pair.stream q n = some ifp_n)
    (stream_succ_nth_eq : int_fract_pair.stream q (n + 1) = some ifp_succ_n) :
    rat.num (fr ifp_succ_n) < rat.num (fr ifp_n) :=
  sorry

theorem stream_nth_fr_num_le_fr_num_sub_n_rat {q : ℚ} {n : ℕ} {ifp_n : int_fract_pair ℚ} :
    int_fract_pair.stream q n = some ifp_n →
        rat.num (fr ifp_n) ≤ rat.num (fr (int_fract_pair.of q)) - ↑n :=
  sorry

theorem exists_nth_stream_eq_none_of_rat (q : ℚ) : ∃ (n : ℕ), int_fract_pair.stream q n = none :=
  sorry

end int_fract_pair

/-- The continued fraction of a rational number terminates. -/
theorem terminates_of_rat (q : ℚ) : terminates (generalized_continued_fraction.of q) := sorry

/-- The continued fraction `gcf.of v` terminates if and only if `v ∈ ℚ`. -/
theorem terminates_iff_rat {K : Type u_1} [linear_ordered_field K] [floor_ring K] [archimedean K]
    (v : K) : terminates (generalized_continued_fraction.of v) ↔ ∃ (q : ℚ), v = ↑q :=
  sorry

end Mathlib


/-
Copyright (c) 2018 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon, Patrick Massot
-/
import data.pi
import data.set.function
import tactic.pi_instances
import algebra.group.hom_instances

/-!
# Pi instances for groups and monoids

This file defines instances for group, monoid, semigroup and related structures on Pi types.
-/

universes u v w
variable {I : Type u}     -- The indexing type
variable {f : I → Type v} -- The family of types already equipped with instances
variables (x y : Π i, f i) (i : I)

namespace pi

@[to_additive]
instance semigroup [∀ i, semigroup $ f i] : semigroup (Π i : I, f i) :=
by refine_struct { mul := (*), .. }; tactic.pi_instance_derive_field

instance semigroup_with_zero [∀ i, semigroup_with_zero $ f i] :
  semigroup_with_zero (Π i : I, f i) :=
by refine_struct { zero := (0 : Π i, f i), mul := (*), .. }; tactic.pi_instance_derive_field

@[to_additive]
instance comm_semigroup [∀ i, comm_semigroup $ f i] : comm_semigroup (Π i : I, f i) :=
by refine_struct { mul := (*), .. }; tactic.pi_instance_derive_field

@[to_additive]
instance mul_one_class [∀ i, mul_one_class $ f i] : mul_one_class (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), .. }; tactic.pi_instance_derive_field

@[to_additive]
instance monoid [∀ i, monoid $ f i] : monoid (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), npow := λ n x i, (x i) ^ n };
tactic.pi_instance_derive_field

@[simp]
lemma pow_apply [∀ i, monoid $ f i] (n : ℕ) : (x^n) i = (x i)^n :=
begin
  induction n with n ih,
  { simp, },
  { simp [pow_succ, ih], },
end

@[to_additive]
instance comm_monoid [∀ i, comm_monoid $ f i] : comm_monoid (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), npow := monoid.npow };
tactic.pi_instance_derive_field

@[to_additive]
instance div_inv_monoid [∀ i, div_inv_monoid $ f i] :
  div_inv_monoid (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), inv := has_inv.inv, div := has_div.div,
  npow := monoid.npow, zpow := λ z x i, (x i) ^ z }; tactic.pi_instance_derive_field

@[to_additive]
instance group [∀ i, group $ f i] : group (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), inv := has_inv.inv, div := has_div.div,
  npow := monoid.npow, zpow := div_inv_monoid.zpow }; tactic.pi_instance_derive_field

@[to_additive]
instance comm_group [∀ i, comm_group $ f i] : comm_group (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), inv := has_inv.inv, div := has_div.div,
  npow := monoid.npow, zpow := div_inv_monoid.zpow }; tactic.pi_instance_derive_field

@[to_additive add_left_cancel_semigroup]
instance left_cancel_semigroup [∀ i, left_cancel_semigroup $ f i] :
  left_cancel_semigroup (Π i : I, f i) :=
by refine_struct { mul := (*) }; tactic.pi_instance_derive_field

@[to_additive add_right_cancel_semigroup]
instance right_cancel_semigroup [∀ i, right_cancel_semigroup $ f i] :
  right_cancel_semigroup (Π i : I, f i) :=
by refine_struct { mul := (*) }; tactic.pi_instance_derive_field

@[to_additive add_left_cancel_monoid]
instance left_cancel_monoid [∀ i, left_cancel_monoid $ f i] :
  left_cancel_monoid (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), npow := monoid.npow };
tactic.pi_instance_derive_field

@[to_additive add_right_cancel_monoid]
instance right_cancel_monoid [∀ i, right_cancel_monoid $ f i] :
  right_cancel_monoid (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), npow := monoid.npow, .. };
tactic.pi_instance_derive_field

@[to_additive add_cancel_monoid]
instance cancel_monoid [∀ i, cancel_monoid $ f i] :
  cancel_monoid (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), npow := monoid.npow };
tactic.pi_instance_derive_field

@[to_additive add_cancel_comm_monoid]
instance cancel_comm_monoid [∀ i, cancel_comm_monoid $ f i] :
  cancel_comm_monoid (Π i : I, f i) :=
by refine_struct { one := (1 : Π i, f i), mul := (*), npow := monoid.npow };
tactic.pi_instance_derive_field

instance mul_zero_class [∀ i, mul_zero_class $ f i] :
  mul_zero_class (Π i : I, f i) :=
by refine_struct { zero := (0 : Π i, f i), mul := (*), .. }; tactic.pi_instance_derive_field

instance mul_zero_one_class [∀ i, mul_zero_one_class $ f i] :
  mul_zero_one_class (Π i : I, f i) :=
by refine_struct { zero := (0 : Π i, f i), one := (1 : Π i, f i), mul := (*), .. };
  tactic.pi_instance_derive_field

instance monoid_with_zero [∀ i, monoid_with_zero $ f i] :
  monoid_with_zero (Π i : I, f i) :=
by refine_struct { zero := (0 : Π i, f i), one := (1 : Π i, f i), mul := (*),
  npow := monoid.npow }; tactic.pi_instance_derive_field

instance comm_monoid_with_zero [∀ i, comm_monoid_with_zero $ f i] :
  comm_monoid_with_zero (Π i : I, f i) :=
by refine_struct { zero := (0 : Π i, f i), one := (1 : Π i, f i), mul := (*),
  npow := monoid.npow }; tactic.pi_instance_derive_field

end pi

section monoid_hom

variables (f) [Π i, mul_one_class (f i)]

/-- Evaluation of functions into an indexed collection of monoids at a point is a monoid
homomorphism.
This is `function.eval i` as a `monoid_hom`. -/
@[to_additive "Evaluation of functions into an indexed collection of additive monoids at a
point is an additive monoid homomorphism.
This is `function.eval i` as an `add_monoid_hom`.", simps]
def pi.eval_monoid_hom (i : I) : (Π i, f i) →* f i :=
{ to_fun := λ g, g i,
  map_one' := pi.one_apply i,
  map_mul' := λ x y, pi.mul_apply _ _ i, }

/-- `function.const` as a `monoid_hom`. -/
@[to_additive "`function.const` as an `add_monoid_hom`.", simps]
def pi.const_monoid_hom (α β : Type*) [mul_one_class β] : β →* (α → β) :=
{ to_fun := function.const α,
  map_one' := rfl,
  map_mul' := λ _ _, rfl }

/-- Coercion of a `monoid_hom` into a function is itself a `monoid_hom`.

See also `monoid_hom.eval`. -/
@[to_additive "Coercion of an `add_monoid_hom` into a function is itself a `add_monoid_hom`.

See also `add_monoid_hom.eval`. ", simps]
def monoid_hom.coe_fn (α β : Type*) [mul_one_class α] [comm_monoid β] : (α →* β) →* (α → β) :=
{ to_fun := λ g, g,
  map_one' := rfl,
  map_mul' := λ x y, rfl, }

/-- Monoid homomorphism between the function spaces `I → α` and `I → β`, induced by a monoid
homomorphism `f` between `α` and `β`. -/
@[to_additive "Additive monoid homomorphism between the function spaces `I → α` and `I → β`,
induced by an additive monoid homomorphism `f` between `α` and `β`", simps]
protected def monoid_hom.comp_left {α β : Type*} [mul_one_class α] [mul_one_class β] (f : α →* β)
  (I : Type*) :
  (I → α) →* (I → β) :=
{ to_fun := λ h, f ∘ h,
  map_one' := by ext; simp,
  map_mul' := λ _ _, by ext; simp }

end monoid_hom

section single
variables [decidable_eq I]
open pi

variables (f)

/-- The zero-preserving homomorphism including a single value
into a dependent family of values, as functions supported at a point.

This is the `zero_hom` version of `pi.single`. -/
@[simps] def zero_hom.single [Π i, has_zero $ f i] (i : I) : zero_hom (f i) (Π i, f i) :=
{ to_fun := single i,
  map_zero' := single_zero i }

/-- The additive monoid homomorphism including a single additive monoid
into a dependent family of additive monoids, as functions supported at a point.

This is the `add_monoid_hom` version of `pi.single`. -/
@[simps] def add_monoid_hom.single [Π i, add_zero_class $ f i] (i : I) : f i →+ Π i, f i :=
{ to_fun := single i,
  map_add' := single_op₂ (λ _, (+)) (λ _, zero_add _) _,
  .. (zero_hom.single f i) }

/-- The multiplicative homomorphism including a single `mul_zero_class`
into a dependent family of `mul_zero_class`es, as functions supported at a point.

This is the `mul_hom` version of `pi.single`. -/
@[simps] def mul_hom.single [Π i, mul_zero_class $ f i] (i : I) : mul_hom (f i) (Π i, f i) :=
{ to_fun := single i,
  map_mul' := single_op₂ (λ _, (*)) (λ _, zero_mul _) _, }

variables {f}

lemma pi.single_add [Π i, add_zero_class $ f i] (i : I) (x y : f i) :
  single i (x + y) = single i x + single i y :=
(add_monoid_hom.single f i).map_add x y

lemma pi.single_neg [Π i, add_group $ f i] (i : I) (x : f i) :
  single i (-x) = -single i x :=
(add_monoid_hom.single f i).map_neg x

lemma pi.single_sub [Π i, add_group $ f i] (i : I) (x y : f i) :
  single i (x - y) = single i x - single i y :=
(add_monoid_hom.single f i).map_sub x y

lemma pi.single_mul [Π i, mul_zero_class $ f i] (i : I) (x y : f i) :
  single i (x * y) = single i x * single i y :=
(mul_hom.single f i).map_mul x y

lemma pi.update_eq_sub_add_single [Π i, add_group $ f i] (g : Π (i : I), f i) (x : f i) :
  function.update g i x = g - single i (g i) + single i x :=
begin
  ext j,
  rcases eq_or_ne i j with rfl|h,
  { simp },
  { simp [function.update_noteq h.symm, h] }
end

end single

namespace function

@[simp, to_additive]
lemma update_one [Π i, has_one (f i)] [decidable_eq I] (i : I) :
  update (1 : Π i, f i) i 1 = 1 :=
update_eq_self i 1

@[to_additive]
lemma update_mul [Π i, has_mul (f i)] [decidable_eq I]
  (f₁ f₂ : Π i, f i) (i : I) (x₁ : f i) (x₂ : f i) :
  update (f₁ * f₂) i (x₁ * x₂) = update f₁ i x₁ * update f₂ i x₂ :=
funext $ λ j, (apply_update₂ (λ i, (*)) f₁ f₂ i x₁ x₂ j).symm

@[to_additive]
lemma update_inv [Π i, has_inv (f i)] [decidable_eq I]
  (f₁ : Π i, f i) (i : I) (x₁ : f i) :
  update (f₁⁻¹) i (x₁⁻¹) = (update f₁ i x₁)⁻¹ :=
funext $ λ j, (apply_update (λ i, has_inv.inv) f₁ i x₁ j).symm

@[to_additive]
lemma update_div [Π i, has_div (f i)] [decidable_eq I]
  (f₁ f₂ : Π i, f i) (i : I) (x₁ : f i) (x₂ : f i) :
  update (f₁ / f₂) i (x₁ / x₂) = update f₁ i x₁ / update f₂ i x₂ :=
funext $ λ j, (apply_update₂ (λ i, (/)) f₁ f₂ i x₁ x₂ j).symm

end function

section piecewise

@[to_additive]
lemma set.piecewise_mul [Π i, has_mul (f i)] (s : set I) [Π i, decidable (i ∈ s)]
  (f₁ f₂ g₁ g₂ : Π i, f i) :
  s.piecewise (f₁ * f₂) (g₁ * g₂) = s.piecewise f₁ g₁ * s.piecewise f₂ g₂ :=
s.piecewise_op₂ _ _ _ _ (λ _, (*))

@[to_additive]
lemma set.piecewise_inv [Π i, has_inv (f i)] (s : set I) [Π i, decidable (i ∈ s)]
  (f₁ g₁ : Π i, f i) :
  s.piecewise (f₁⁻¹) (g₁⁻¹) = (s.piecewise f₁ g₁)⁻¹ :=
s.piecewise_op f₁ g₁ (λ _ x, x⁻¹)

@[to_additive]
lemma set.piecewise_div [Π i, has_div (f i)] (s : set I) [Π i, decidable (i ∈ s)]
  (f₁ f₂ g₁ g₂ : Π i, f i) :
  s.piecewise (f₁ / f₂) (g₁ / g₂) = s.piecewise f₁ g₁ / s.piecewise f₂ g₂ :=
s.piecewise_op₂ _ _ _ _ (λ _, (/))

end piecewise

section extend

variables {ι : Type u} {η : Type v} (R : Type w) (s : ι → η)

/-- `function.extend s f 1` as a bundled hom. -/
@[to_additive function.extend_by_zero.hom "`function.extend s f 0` as a bundled hom.", simps]
noncomputable def function.extend_by_one.hom [mul_one_class R] : (ι → R) →* (η → R) :=
{ to_fun := λ f, function.extend s f 1,
  map_one' := function.extend_one s,
  map_mul' := λ f g, by { simpa using function.extend_mul s f g 1 1 } }

end extend


/-
Copyright (c) 2020 Anne Baanen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nathaniel Thomas, Jeremy Avigad, Johannes Hölzl, Mario Carneiro, Anne Baanen
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.group.hom
import Mathlib.algebra.module.basic
import Mathlib.algebra.group_action_hom
import Mathlib.PostPort

universes u v w l x u_1 u_2 y z

namespace Mathlib

/-!
# Linear maps and linear equivalences

In this file we define

* `linear_map R M M₂`, `M →ₗ[R] M₂` : a linear map between two R-`semimodule`s.

* `is_linear_map R f` : predicate saying that `f : M → M₂` is a linear map.

* `linear_equiv R M ₂`, `M ≃ₗ[R] M₂`: an invertible linear map

## Tags

linear map, linear equiv, linear equivalences, linear isomorphism, linear isomorphic
-/

/-- A map `f` between semimodules over a semiring is linear if it satisfies the two properties
`f (x + y) = f x + f y` and `f (c • x) = c • f x`. The predicate `is_linear_map R f` asserts this
property. A bundled version is available with `linear_map`, and should be favored over
`is_linear_map` most of the time. -/
structure is_linear_map (R : Type u) {M : Type v} {M₂ : Type w} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : M → M₂)
where
  map_add : ∀ (x y : M), f (x + y) = f x + f y
  map_smul : ∀ (c : R) (x : M), f (c • x) = c • f x

/-- A map `f` between semimodules over a semiring is linear if it satisfies the two properties
`f (x + y) = f x + f y` and `f (c • x) = c • f x`. Elements of `linear_map R M M₂` (available under
the notation `M →ₗ[R] M₂`) are bundled versions of such maps. An unbundled version is available with
the predicate `is_linear_map`, but it should be avoided most of the time. -/
structure linear_map (R : Type u) (M : Type v) (M₂ : Type w) [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂]
extends mul_action_hom R M M₂, add_hom M M₂
where

/-- The `add_hom` underlying a `linear_map`. -/
/-- The `mul_action_hom` underlying a `linear_map`. -/
namespace linear_map

protected instance has_coe_to_fun {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] : has_coe_to_fun (linear_map R M M₂) :=
  has_coe_to_fun.mk (fun (x : linear_map R M M₂) => M → M₂) to_fun

@[simp] theorem coe_mk {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : M → M₂) (h₁ : ∀ (x y : M), f (x + y) = f x + f y) (h₂ : ∀ (m : R) (x : M), f (m • x) = m • f x) : ⇑(mk f h₁ h₂) = f :=
  rfl

/-- Identity map as a `linear_map` -/
def id {R : Type u} {M : Type w} [semiring R] [add_comm_monoid M] [semimodule R M] : linear_map R M M :=
  mk id sorry sorry

theorem id_apply {R : Type u} {M : Type w} [semiring R] [add_comm_monoid M] [semimodule R M] (x : M) : coe_fn id x = x :=
  rfl

@[simp] theorem id_coe {R : Type u} {M : Type w} [semiring R] [add_comm_monoid M] [semimodule R M] : ⇑id = id :=
  funext fun (x : M) => Eq.refl (coe_fn id x)

@[simp] theorem to_fun_eq_coe {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) : to_fun f = ⇑f :=
  rfl

theorem is_linear {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) : is_linear_map R ⇑f :=
  is_linear_map.mk (map_add' f) (map_smul' f)

theorem coe_injective {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] : function.injective fun (f : linear_map R M M₂) => (fun (this : M → M₂) => this) ⇑f := sorry

theorem ext {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {f : linear_map R M M₂} {g : linear_map R M M₂} (H : ∀ (x : M), coe_fn f x = coe_fn g x) : f = g :=
  coe_injective (funext H)

protected theorem congr_arg {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {f : linear_map R M M₂} {x : M} {x' : M} : x = x' → coe_fn f x = coe_fn f x' := sorry

/-- If two linear maps are equal, they are equal at each point. -/
protected theorem congr_fun {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {f : linear_map R M M₂} {g : linear_map R M M₂} (h : f = g) (x : M) : coe_fn f x = coe_fn g x :=
  h ▸ rfl

theorem ext_iff {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {f : linear_map R M M₂} {g : linear_map R M M₂} : f = g ↔ ∀ (x : M), coe_fn f x = coe_fn g x :=
  { mp := fun (ᾰ : f = g) (x : M) => Eq._oldrec (Eq.refl (coe_fn f x)) ᾰ, mpr := ext }

@[simp] theorem mk_coe {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) (h₁ : ∀ (x y : M), coe_fn f (x + y) = coe_fn f x + coe_fn f y) (h₂ : ∀ (m : R) (x : M), coe_fn f (m • x) = m • coe_fn f x) : mk (⇑f) h₁ h₂ = f :=
  ext fun (_x : M) => rfl

@[simp] theorem map_add {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) (x : M) (y : M) : coe_fn f (x + y) = coe_fn f x + coe_fn f y :=
  map_add' f x y

@[simp] theorem map_smul {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) (c : R) (x : M) : coe_fn f (c • x) = c • coe_fn f x :=
  map_smul' f c x

@[simp] theorem map_zero {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) : coe_fn f 0 = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (coe_fn f 0 = 0)) (Eq.symm (zero_smul R 0))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (coe_fn f (0 • 0) = 0)) (map_smul f 0 0)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 • coe_fn f 0 = 0)) (zero_smul R (coe_fn f 0)))) (Eq.refl 0)))

/--
A typeclass for `has_scalar` structures which can be moved through a `linear_map`.
This typeclass is generated automatically from a `is_scalar_tower` instance, but exists so that
we can also add an instance for `add_comm_group.int_module`, allowing `z •` to be moved even if
`R` does not support negation.
-/
class compatible_smul (M : Type w) (M₂ : Type x) [add_comm_monoid M] [add_comm_monoid M₂] (R : Type u_1) (S : Type u_2) [semiring S] [has_scalar R M] [semimodule S M] [has_scalar R M₂] [semimodule S M₂]
where
  map_smul : ∀ (f : linear_map S M M₂) (c : R) (x : M), coe_fn f (c • x) = c • coe_fn f x

protected instance compatible_smul.is_scalar_tower {M : Type w} {M₂ : Type x} [add_comm_monoid M] [add_comm_monoid M₂] {R : Type u_1} {S : Type u_2} [semiring S] [has_scalar R S] [has_scalar R M] [semimodule S M] [is_scalar_tower R S M] [has_scalar R M₂] [semimodule S M₂] [is_scalar_tower R S M₂] : compatible_smul M M₂ R S :=
  compatible_smul.mk sorry

@[simp] theorem map_smul_of_tower {M : Type w} {M₂ : Type x} [add_comm_monoid M] [add_comm_monoid M₂] {R : Type u_1} {S : Type u_2} [semiring S] [has_scalar R M] [semimodule S M] [has_scalar R M₂] [semimodule S M₂] [compatible_smul M M₂ R S] (f : linear_map S M M₂) (c : R) (x : M) : coe_fn f (c • x) = c • coe_fn f x :=
  compatible_smul.map_smul f c x

protected instance is_add_monoid_hom {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) : is_add_monoid_hom ⇑f :=
  is_add_monoid_hom.mk (map_zero f)

/-- convert a linear map to an additive map -/
def to_add_monoid_hom {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) : M →+ M₂ :=
  add_monoid_hom.mk (⇑f) (map_zero f) (map_add f)

@[simp] theorem to_add_monoid_hom_coe {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) : ⇑(to_add_monoid_hom f) = ⇑f :=
  rfl

/-- If `M` and `M₂` are both `R`-semimodules and `S`-semimodules and `R`-semimodule structures
are defined by an action of `R` on `S` (formally, we have two scalar towers), then any `S`-linear
map from `M` to `M₂` is `R`-linear.

See also `linear_map.map_smul_of_tower`. -/
def restrict_scalars (R : Type u) {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {S : Type u_1} [semiring S] [semimodule S M] [semimodule S M₂] [compatible_smul M M₂ R S] (f : linear_map S M M₂) : linear_map R M M₂ :=
  mk (⇑f) (map_add f) sorry

@[simp] theorem map_sum {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) {ι : Type u_1} {t : finset ι} {g : ι → M} : coe_fn f (finset.sum t fun (i : ι) => g i) = finset.sum t fun (i : ι) => coe_fn f (g i) :=
  add_monoid_hom.map_sum (to_add_monoid_hom f) (fun (i : ι) => g i) t

theorem to_add_monoid_hom_injective {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] : function.injective to_add_monoid_hom :=
  fun (f g : linear_map R M M₂) (h : to_add_monoid_hom f = to_add_monoid_hom g) => ext (add_monoid_hom.congr_fun h)

/-- If two `R`-linear maps from `R` are equal on `1`, then they are equal. -/
theorem ext_ring {R : Type u} {M : Type w} [semiring R] [add_comm_monoid M] [semimodule R M] {f : linear_map R R M} {g : linear_map R R M} (h : coe_fn f 1 = coe_fn g 1) : f = g := sorry

/-- Composition of two linear maps is a linear map -/
def comp {R : Type u} {M : Type w} {M₂ : Type x} {M₃ : Type y} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [add_comm_monoid M₃] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {semimodule_M₃ : semimodule R M₃} (f : linear_map R M₂ M₃) (g : linear_map R M M₂) : linear_map R M M₃ :=
  mk (⇑f ∘ ⇑g) sorry sorry

@[simp] theorem comp_apply {R : Type u} {M : Type w} {M₂ : Type x} {M₃ : Type y} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [add_comm_monoid M₃] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {semimodule_M₃ : semimodule R M₃} (f : linear_map R M₂ M₃) (g : linear_map R M M₂) (x : M) : coe_fn (comp f g) x = coe_fn f (coe_fn g x) :=
  rfl

theorem comp_coe {R : Type u} {M : Type w} {M₂ : Type x} {M₃ : Type y} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [add_comm_monoid M₃] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {semimodule_M₃ : semimodule R M₃} (f : linear_map R M₂ M₃) (g : linear_map R M M₂) : ⇑f ∘ ⇑g = ⇑(comp f g) :=
  rfl

/-- If a function `g` is a left and right inverse of a linear map `f`, then `g` is linear itself. -/
def inverse {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_map R M M₂) (g : M₂ → M) (h₁ : function.left_inverse g ⇑f) (h₂ : function.right_inverse g ⇑f) : linear_map R M₂ M :=
  id (fun (h₂ : ∀ (x : M₂), coe_fn f (g x) = x) => id (fun (h₁ : ∀ (x : M), g (coe_fn f x) = x) => mk g sorry sorry) h₁)
    h₂

@[simp] theorem map_neg {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_group M] [add_comm_group M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (f : linear_map R M M₂) (x : M) : coe_fn f (-x) = -coe_fn f x :=
  add_monoid_hom.map_neg (to_add_monoid_hom f) x

@[simp] theorem map_sub {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_group M] [add_comm_group M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (f : linear_map R M M₂) (x : M) (y : M) : coe_fn f (x - y) = coe_fn f x - coe_fn f y :=
  add_monoid_hom.map_sub (to_add_monoid_hom f) x y

protected instance is_add_group_hom {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_group M] [add_comm_group M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (f : linear_map R M M₂) : is_add_group_hom ⇑f :=
  is_add_group_hom.mk

protected instance compatible_smul.int_module {M : Type w} {M₂ : Type x} [add_comm_group M] [add_comm_group M₂] {S : Type u_1} [semiring S] [semimodule ℤ M] [semimodule S M] [semimodule ℤ M₂] [semimodule S M₂] : compatible_smul M M₂ ℤ S :=
  compatible_smul.mk sorry

end linear_map

namespace is_linear_map

/-- Convert an `is_linear_map` predicate to a `linear_map` -/
def mk' {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : M → M₂) (H : is_linear_map R f) : linear_map R M M₂ :=
  linear_map.mk f (map_add H) (map_smul H)

@[simp] theorem mk'_apply {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {f : M → M₂} (H : is_linear_map R f) (x : M) : coe_fn (mk' f H) x = f x :=
  rfl

theorem is_linear_map_smul {R : Type u_1} {M : Type u_2} [comm_semiring R] [add_comm_monoid M] [semimodule R M] (c : R) : is_linear_map R fun (z : M) => c • z := sorry

theorem is_linear_map_smul' {R : Type u_1} {M : Type u_2} [semiring R] [add_comm_monoid M] [semimodule R M] (a : M) : is_linear_map R fun (c : R) => c • a :=
  mk (fun (x y : R) => add_smul x y a) fun (x y : R) => mul_smul x y a

theorem map_zero {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {f : M → M₂} (lin : is_linear_map R f) : f 0 = 0 :=
  linear_map.map_zero (mk' f lin)

theorem is_linear_map_neg {R : Type u} {M : Type w} [semiring R] [add_comm_group M] [semimodule R M] : is_linear_map R fun (z : M) => -z :=
  mk neg_add fun (x : R) (y : M) => Eq.symm (smul_neg x y)

theorem map_neg {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_group M] [add_comm_group M₂] [semimodule R M] [semimodule R M₂] {f : M → M₂} (lin : is_linear_map R f) (x : M) : f (-x) = -f x :=
  linear_map.map_neg (mk' f lin) x

theorem map_sub {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_group M] [add_comm_group M₂] [semimodule R M] [semimodule R M₂] {f : M → M₂} (lin : is_linear_map R f) (x : M) (y : M) : f (x - y) = f x - f y :=
  linear_map.map_sub (mk' f lin) x y

end is_linear_map

/-- Linear endomorphisms of a module, with associated ring structure
`linear_map.endomorphism_semiring` and algebra structure `module.endomorphism_algebra`. -/
def module.End (R : Type u) (M : Type v) [semiring R] [add_comm_monoid M] [semimodule R M] :=
  linear_map R M M

/-- Reinterpret an additive homomorphism as a `ℤ`-linear map. -/
def add_monoid_hom.to_int_linear_map {M : Type w} {M₂ : Type x} [add_comm_group M] [add_comm_group M₂] (f : M →+ M₂) : linear_map ℤ M M₂ :=
  linear_map.mk ⇑f sorry sorry

/-- Reinterpret an additive homomorphism as a `ℚ`-linear map. -/
def add_monoid_hom.to_rat_linear_map {M : Type w} {M₂ : Type x} [add_comm_group M] [vector_space ℚ M] [add_comm_group M₂] [vector_space ℚ M₂] (f : M →+ M₂) : linear_map ℚ M M₂ :=
  linear_map.mk (add_monoid_hom.to_fun f) sorry (add_monoid_hom.map_rat_module_smul f)

/-! ### Linear equivalences -/

/-- A linear equivalence is an invertible linear map. -/
structure linear_equiv (R : Type u) (M : Type v) (M₂ : Type w) [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂]
extends M ≃+ M₂, linear_map R M M₂
where

namespace linear_equiv

-- see Note [function coercion]

protected instance linear_map.has_coe {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] : has_coe (linear_equiv R M M₂) (linear_map R M M₂) :=
  has_coe.mk to_linear_map

protected instance has_coe_to_fun {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] : has_coe_to_fun (linear_equiv R M M₂) :=
  has_coe_to_fun.mk (fun (f : linear_equiv R M M₂) => M → M₂) fun (f : linear_equiv R M M₂) => to_fun f

@[simp] theorem mk_apply {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {to_fun : M → M₂} {inv_fun : M₂ → M} {map_add : ∀ (x y : M), to_fun (x + y) = to_fun x + to_fun y} {map_smul : ∀ (m : R) (x : M), to_fun (m • x) = m • to_fun x} {left_inv : function.left_inverse inv_fun to_fun} {right_inv : function.right_inverse inv_fun to_fun} {a : M} : coe_fn (mk to_fun map_add map_smul inv_fun left_inv right_inv) a = to_fun a :=
  rfl

-- This exists for compatibility, previously `≃ₗ[R]` extended `≃` instead of `≃+`.

def to_equiv {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] : linear_equiv R M M₂ → M ≃ M₂ :=
  fun (f : linear_equiv R M M₂) => add_equiv.to_equiv (to_add_equiv f)

theorem injective_to_equiv {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] : function.injective to_equiv := sorry

@[simp] theorem to_equiv_inj {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {e₁ : linear_equiv R M M₂} {e₂ : linear_equiv R M M₂} : to_equiv e₁ = to_equiv e₂ ↔ e₁ = e₂ :=
  function.injective.eq_iff injective_to_equiv

theorem injective_to_linear_map {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] : function.injective coe :=
  fun (e₁ e₂ : linear_equiv R M M₂) (H : ↑e₁ = ↑e₂) => injective_to_equiv (equiv.ext (linear_map.congr_fun H))

@[simp] theorem to_linear_map_inj {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] {e₁ : linear_equiv R M M₂} {e₂ : linear_equiv R M M₂} : ↑e₁ = ↑e₂ ↔ e₁ = e₂ :=
  function.injective.eq_iff injective_to_linear_map

@[simp] theorem coe_coe {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : ⇑↑e = ⇑e :=
  rfl

@[simp] theorem coe_to_equiv {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : ⇑(to_equiv e) = ⇑e :=
  rfl

@[simp] theorem to_fun_apply {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) {m : M} : to_fun e m = coe_fn e m :=
  rfl

theorem ext {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {e : linear_equiv R M M₂} {e' : linear_equiv R M M₂} (h : ∀ (x : M), coe_fn e x = coe_fn e' x) : e = e' :=
  injective_to_equiv (equiv.ext h)

protected theorem congr_arg {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {e : linear_equiv R M M₂} {x : M} {x' : M} : x = x' → coe_fn e x = coe_fn e x' := sorry

protected theorem congr_fun {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {e : linear_equiv R M M₂} {e' : linear_equiv R M M₂} (h : e = e') (x : M) : coe_fn e x = coe_fn e' x :=
  h ▸ rfl

theorem ext_iff {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {e : linear_equiv R M M₂} {e' : linear_equiv R M M₂} : e = e' ↔ ∀ (x : M), coe_fn e x = coe_fn e' x :=
  { mp := fun (h : e = e') (x : M) => h ▸ rfl, mpr := ext }

/-- The identity map is a linear equivalence. -/
def refl (R : Type u) (M : Type w) [semiring R] [add_comm_monoid M] [semimodule R M] : linear_equiv R M M :=
  mk (linear_map.to_fun linear_map.id) sorry sorry (equiv.inv_fun (equiv.refl M)) sorry sorry

@[simp] theorem refl_apply {R : Type u} {M : Type w} [semiring R] [add_comm_monoid M] [semimodule R M] (x : M) : coe_fn (refl R M) x = x :=
  rfl

/-- Linear equivalences are symmetric. -/
def symm {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : linear_equiv R M₂ M :=
  mk (linear_map.to_fun (linear_map.inverse (to_linear_map e) (inv_fun e) (left_inv e) (right_inv e))) sorry sorry
    (equiv.inv_fun (equiv.symm (to_equiv e))) sorry sorry

/-- See Note [custom simps projection] -/
def simps.inv_fun {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (e : linear_equiv R M M₂) : M₂ → M :=
  ⇑(symm e)

@[simp] theorem inv_fun_eq_symm {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : inv_fun e = ⇑(symm e) :=
  rfl

/-- Linear equivalences are transitive. -/
def trans {R : Type u} {M : Type w} {M₂ : Type x} {M₃ : Type y} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [add_comm_monoid M₃] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {semimodule_M₃ : semimodule R M₃} (e₁ : linear_equiv R M M₂) (e₂ : linear_equiv R M₂ M₃) : linear_equiv R M M₃ :=
  mk (linear_map.to_fun (linear_map.comp (to_linear_map e₂) (to_linear_map e₁))) sorry sorry
    (equiv.inv_fun (equiv.trans (to_equiv e₁) (to_equiv e₂))) sorry sorry

@[simp] theorem coe_to_add_equiv {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : ⇑(to_add_equiv e) = ⇑e :=
  rfl

@[simp] theorem trans_apply {R : Type u} {M : Type w} {M₂ : Type x} {M₃ : Type y} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [add_comm_monoid M₃] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {semimodule_M₃ : semimodule R M₃} (e₁ : linear_equiv R M M₂) (e₂ : linear_equiv R M₂ M₃) (c : M) : coe_fn (trans e₁ e₂) c = coe_fn e₂ (coe_fn e₁ c) :=
  rfl

@[simp] theorem apply_symm_apply {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) (c : M₂) : coe_fn e (coe_fn (symm e) c) = c :=
  right_inv e c

@[simp] theorem symm_apply_apply {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) (b : M) : coe_fn (symm e) (coe_fn e b) = b :=
  left_inv e b

@[simp] theorem symm_trans_apply {R : Type u} {M : Type w} {M₂ : Type x} {M₃ : Type y} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [add_comm_monoid M₃] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} {semimodule_M₃ : semimodule R M₃} (e₁ : linear_equiv R M M₂) (e₂ : linear_equiv R M₂ M₃) (c : M₃) : coe_fn (symm (trans e₁ e₂)) c = coe_fn (symm e₁) (coe_fn (symm e₂) c) :=
  rfl

@[simp] theorem trans_refl {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : trans e (refl R M₂) = e :=
  injective_to_equiv (equiv.trans_refl (to_equiv e))

@[simp] theorem refl_trans {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : trans (refl R M) e = e :=
  injective_to_equiv (equiv.refl_trans (to_equiv e))

theorem symm_apply_eq {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) {x : M₂} {y : M} : coe_fn (symm e) x = y ↔ x = coe_fn e y :=
  equiv.symm_apply_eq (to_equiv e)

theorem eq_symm_apply {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) {x : M₂} {y : M} : y = coe_fn (symm e) x ↔ coe_fn e y = x :=
  equiv.eq_symm_apply (to_equiv e)

@[simp] theorem trans_symm {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_equiv R M M₂) : trans f (symm f) = refl R M := sorry

@[simp] theorem symm_trans {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [semimodule R M] [semimodule R M₂] (f : linear_equiv R M M₂) : trans (symm f) f = refl R M₂ := sorry

@[simp] theorem refl_to_linear_map {R : Type u} {M : Type w} [semiring R] [add_comm_monoid M] [semimodule R M] : ↑(refl R M) = linear_map.id :=
  rfl

@[simp] theorem comp_coe {R : Type u} {M : Type w} {M₂ : Type x} {M₃ : Type y} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] [add_comm_monoid M₃] [semimodule R M] [semimodule R M₂] [semimodule R M₃] (f : linear_equiv R M M₂) (f' : linear_equiv R M₂ M₃) : linear_map.comp ↑f' ↑f = ↑(trans f f') :=
  rfl

@[simp] theorem map_add {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) (a : M) (b : M) : coe_fn e (a + b) = coe_fn e a + coe_fn e b :=
  map_add' e a b

@[simp] theorem map_zero {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : coe_fn e 0 = 0 :=
  linear_map.map_zero (to_linear_map e)

@[simp] theorem map_smul {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) (c : R) (x : M) : coe_fn e (c • x) = c • coe_fn e x :=
  map_smul' e c x

@[simp] theorem map_sum {R : Type u} {M : Type w} {M₂ : Type x} {ι : Type z} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) {s : finset ι} (u : ι → M) : coe_fn e (finset.sum s fun (i : ι) => u i) = finset.sum s fun (i : ι) => coe_fn e (u i) :=
  linear_map.map_sum (to_linear_map e)

@[simp] theorem map_eq_zero_iff {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) {x : M} : coe_fn e x = 0 ↔ x = 0 :=
  add_equiv.map_eq_zero_iff (to_add_equiv e)

theorem map_ne_zero_iff {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) {x : M} : coe_fn e x ≠ 0 ↔ x ≠ 0 :=
  add_equiv.map_ne_zero_iff (to_add_equiv e)

@[simp] theorem symm_symm {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : symm (symm e) = e := sorry

protected theorem bijective {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : function.bijective ⇑e :=
  equiv.bijective (to_equiv e)

protected theorem injective {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : function.injective ⇑e :=
  equiv.injective (to_equiv e)

protected theorem surjective {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) : function.surjective ⇑e :=
  equiv.surjective (to_equiv e)

protected theorem image_eq_preimage {R : Type u} {M : Type w} {M₂ : Type x} [semiring R] [add_comm_monoid M] [add_comm_monoid M₂] {semimodule_M : semimodule R M} {semimodule_M₂ : semimodule R M₂} (e : linear_equiv R M M₂) (s : set M) : ⇑e '' s = ⇑(symm e) ⁻¹' s :=
  equiv.image_eq_preimage (to_equiv e) s

/-- An involutive linear map is a linear equivalence. -/
def of_involutive {R : Type u} {M : Type w} [semiring R] [add_comm_monoid M] [semimodule R M] (f : linear_map R M M) (hf : function.involutive ⇑f) : linear_equiv R M M :=
  mk (linear_map.to_fun f) (linear_map.map_add' f) (linear_map.map_smul' f)
    (equiv.inv_fun (function.involutive.to_equiv (⇑f) hf)) sorry sorry

@[simp] theorem coe_of_involutive {R : Type u} {M : Type w} [semiring R] [add_comm_monoid M] [semimodule R M] (f : linear_map R M M) (hf : function.involutive ⇑f) : ⇑(of_involutive f hf) = ⇑f :=
  rfl


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kevin Buzzard, Scott Morrison, Jakob von Raumer

! This file was ported from Lean 3 source module algebra.category.Module.monoidal
! leanprover-community/mathlib commit 0caf3701139ef2e69c215717665361cda205a90b
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Monoidal.Braided
import Mathbin.CategoryTheory.Closed.Monoidal
import Mathbin.Algebra.Category.Module.Basic
import Mathbin.LinearAlgebra.TensorProduct
import Mathbin.CategoryTheory.Linear.Yoneda
import Mathbin.CategoryTheory.Monoidal.Linear

/-!
# The symmetric monoidal category structure on R-modules

Mostly this uses existing machinery in `linear_algebra.tensor_product`.
We just need to provide a few small missing pieces to build the
`monoidal_category` instance and then the `symmetric_category` instance.

Note the universe level of the modules must be at least the universe level of the ring,
so that we have a monoidal unit.
For now, we simplify by insisting both universe levels are the same.

We then construct the monoidal closed structure on `Module R`.

If you're happy using the bundled `Module R`, it may be possible to mostly
use this as an interface and not need to interact much with the implementation details.
-/

universe v w x u

open CategoryTheory

namespace ModuleCat

variable {R : Type u} [CommRing R]

namespace MonoidalCategory

-- The definitions inside this namespace are essentially private.
-- After we build the `monoidal_category (Module R)` instance,
-- you should use that API.
open TensorProduct

attribute [local ext] TensorProduct.ext

/-- (implementation) tensor product of R-modules -/
def tensorObj (M N : ModuleCat R) : ModuleCat R :=
  ModuleCat.of R (M ⊗[R] N)
#align Module.monoidal_category.tensor_obj ModuleCat.MonoidalCategory.tensorObj

/-- (implementation) tensor product of morphisms R-modules -/
def tensorHom {M N M' N' : ModuleCat R} (f : M ⟶ N) (g : M' ⟶ N') :
    tensorObj M M' ⟶ tensorObj N N' :=
  TensorProduct.map f g
#align Module.monoidal_category.tensor_hom ModuleCat.MonoidalCategory.tensorHom

theorem tensor_id (M N : ModuleCat R) : tensorHom (𝟙 M) (𝟙 N) = 𝟙 (ModuleCat.of R (M ⊗ N)) :=
  by
  ext1
  rfl
#align Module.monoidal_category.tensor_id ModuleCat.MonoidalCategory.tensor_id

theorem tensor_comp {X₁ Y₁ Z₁ X₂ Y₂ Z₂ : ModuleCat R} (f₁ : X₁ ⟶ Y₁) (f₂ : X₂ ⟶ Y₂) (g₁ : Y₁ ⟶ Z₁)
    (g₂ : Y₂ ⟶ Z₂) : tensorHom (f₁ ≫ g₁) (f₂ ≫ g₂) = tensorHom f₁ f₂ ≫ tensorHom g₁ g₂ :=
  by
  ext1
  rfl
#align Module.monoidal_category.tensor_comp ModuleCat.MonoidalCategory.tensor_comp

/-- (implementation) the associator for R-modules -/
def associator (M : ModuleCat.{v} R) (N : ModuleCat.{w} R) (K : ModuleCat.{x} R) :
    tensorObj (tensorObj M N) K ≅ tensorObj M (tensorObj N K) :=
  (TensorProduct.assoc R M N K).toModuleIso
#align Module.monoidal_category.associator ModuleCat.MonoidalCategory.associator

section

/-! The `associator_naturality` and `pentagon` lemmas below are very slow to elaborate.

We give them some help by expressing the lemmas first non-categorically, then using
`convert _aux using 1` to have the elaborator work as little as possible. -/

open TensorProduct (and_assoc map)

private theorem associator_naturality_aux {X₁ X₂ X₃ : Type _} [AddCommMonoid X₁] [AddCommMonoid X₂]
    [AddCommMonoid X₃] [Module R X₁] [Module R X₂] [Module R X₃] {Y₁ Y₂ Y₃ : Type _}
    [AddCommMonoid Y₁] [AddCommMonoid Y₂] [AddCommMonoid Y₃] [Module R Y₁] [Module R Y₂]
    [Module R Y₃] (f₁ : X₁ →ₗ[R] Y₁) (f₂ : X₂ →ₗ[R] Y₂) (f₃ : X₃ →ₗ[R] Y₃) :
    ↑(assoc R Y₁ Y₂ Y₃) ∘ₗ map (map f₁ f₂) f₃ = map f₁ (map f₂ f₃) ∘ₗ ↑(assoc R X₁ X₂ X₃) :=
  by
  apply TensorProduct.ext_threefold
  intro x y z
  rfl
#align Module.monoidal_category.associator_naturality_aux Module.monoidal_category.associator_naturality_aux

variable (R)

private theorem pentagon_aux (W X Y Z : Type _) [AddCommMonoid W] [AddCommMonoid X]
    [AddCommMonoid Y] [AddCommMonoid Z] [Module R W] [Module R X] [Module R Y] [Module R Z] :
    ((map (1 : W →ₗ[R] W) (assoc R X Y Z).toLinearMap).comp
            (assoc R W (X ⊗[R] Y) Z).toLinearMap).comp
        (map ↑(assoc R W X Y) (1 : Z →ₗ[R] Z)) =
      (assoc R W X (Y ⊗[R] Z)).toLinearMap.comp (assoc R (W ⊗[R] X) Y Z).toLinearMap :=
  by
  apply TensorProduct.ext_fourfold
  intro w x y z
  rfl
#align Module.monoidal_category.pentagon_aux Module.monoidal_category.pentagon_aux

end

theorem associator_naturality {X₁ X₂ X₃ Y₁ Y₂ Y₃ : ModuleCat R} (f₁ : X₁ ⟶ Y₁) (f₂ : X₂ ⟶ Y₂)
    (f₃ : X₃ ⟶ Y₃) :
    tensorHom (tensorHom f₁ f₂) f₃ ≫ (associator Y₁ Y₂ Y₃).Hom =
      (associator X₁ X₂ X₃).Hom ≫ tensorHom f₁ (tensorHom f₂ f₃) :=
  by convert associator_naturality_aux f₁ f₂ f₃ using 1
#align Module.monoidal_category.associator_naturality ModuleCat.MonoidalCategory.associator_naturality

theorem pentagon (W X Y Z : ModuleCat R) :
    tensorHom (associator W X Y).Hom (𝟙 Z) ≫
        (associator W (tensorObj X Y) Z).Hom ≫ tensorHom (𝟙 W) (associator X Y Z).Hom =
      (associator (tensorObj W X) Y Z).Hom ≫ (associator W X (tensorObj Y Z)).Hom :=
  by convert pentagon_aux R W X Y Z using 1
#align Module.monoidal_category.pentagon ModuleCat.MonoidalCategory.pentagon

/-- (implementation) the left unitor for R-modules -/
def leftUnitor (M : ModuleCat.{u} R) : ModuleCat.of R (R ⊗[R] M) ≅ M :=
  (LinearEquiv.toModuleIso (TensorProduct.lid R M) : of R (R ⊗ M) ≅ of R M).trans (ofSelfIso M)
#align Module.monoidal_category.left_unitor ModuleCat.MonoidalCategory.leftUnitor

theorem leftUnitor_naturality {M N : ModuleCat R} (f : M ⟶ N) :
    tensorHom (𝟙 (ModuleCat.of R R)) f ≫ (leftUnitor N).Hom = (leftUnitor M).Hom ≫ f :=
  by
  ext (x y); dsimp
  erw [TensorProduct.lid_tmul, TensorProduct.lid_tmul]
  rw [LinearMap.map_smul]
  rfl
#align Module.monoidal_category.left_unitor_naturality ModuleCat.MonoidalCategory.leftUnitor_naturality

/-- (implementation) the right unitor for R-modules -/
def rightUnitor (M : ModuleCat.{u} R) : ModuleCat.of R (M ⊗[R] R) ≅ M :=
  (LinearEquiv.toModuleIso (TensorProduct.rid R M) : of R (M ⊗ R) ≅ of R M).trans (ofSelfIso M)
#align Module.monoidal_category.right_unitor ModuleCat.MonoidalCategory.rightUnitor

theorem rightUnitor_naturality {M N : ModuleCat R} (f : M ⟶ N) :
    tensorHom f (𝟙 (ModuleCat.of R R)) ≫ (rightUnitor N).Hom = (rightUnitor M).Hom ≫ f :=
  by
  ext (x y); dsimp
  erw [TensorProduct.rid_tmul, TensorProduct.rid_tmul]
  rw [LinearMap.map_smul]
  rfl
#align Module.monoidal_category.right_unitor_naturality ModuleCat.MonoidalCategory.rightUnitor_naturality

theorem triangle (M N : ModuleCat.{u} R) :
    (associator M (ModuleCat.of R R) N).Hom ≫ tensorHom (𝟙 M) (leftUnitor N).Hom =
      tensorHom (rightUnitor M).Hom (𝟙 N) :=
  by
  apply TensorProduct.ext_threefold
  intro x y z
  change R at y
  dsimp [tensor_hom, associator]
  erw [TensorProduct.lid_tmul, TensorProduct.rid_tmul]
  exact (TensorProduct.smul_tmul _ _ _).symm
#align Module.monoidal_category.triangle ModuleCat.MonoidalCategory.triangle

end MonoidalCategory

open MonoidalCategory

instance monoidalCategory : MonoidalCategory (ModuleCat.{u} R)
    where
  -- data
  tensorObj := tensorObj
  tensorHom := @tensorHom _ _
  tensorUnit := ModuleCat.of R R
  associator := associator
  leftUnitor := leftUnitor
  rightUnitor := rightUnitor
  -- properties
  tensor_id' M N := tensor_id M N
  tensor_comp' M N K M' N' K' f g h := tensor_comp f g h
  associator_naturality' M N K M' N' K' f g h := associator_naturality f g h
  leftUnitor_naturality' M N f := leftUnitor_naturality f
  rightUnitor_naturality' M N f := rightUnitor_naturality f
  pentagon' M N K L := pentagon M N K L
  triangle' M N := triangle M N
#align Module.monoidal_category ModuleCat.monoidalCategory

/-- Remind ourselves that the monoidal unit, being just `R`, is still a commutative ring. -/
instance : CommRing ((𝟙_ (ModuleCat.{u} R) : ModuleCat.{u} R) : Type u) :=
  (by infer_instance : CommRing R)

namespace MonoidalCategory

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem hom_apply {K L M N : ModuleCat.{u} R} (f : K ⟶ L) (g : M ⟶ N) (k : K) (m : M) :
    (f ⊗ g) (k ⊗ₜ m) = f k ⊗ₜ g m :=
  rfl
#align Module.monoidal_category.hom_apply ModuleCat.monoidalCategory.hom_apply

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem leftUnitor_hom_apply {M : ModuleCat.{u} R} (r : R) (m : M) :
    ((λ_ M).Hom : 𝟙_ (ModuleCat R) ⊗ M ⟶ M) (r ⊗ₜ[R] m) = r • m :=
  TensorProduct.lid_tmul m r
#align Module.monoidal_category.left_unitor_hom_apply ModuleCat.monoidalCategory.leftUnitor_hom_apply

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem leftUnitor_inv_apply {M : ModuleCat.{u} R} (m : M) :
    ((λ_ M).inv : M ⟶ 𝟙_ (ModuleCat.{u} R) ⊗ M) m = 1 ⊗ₜ[R] m :=
  TensorProduct.lid_symm_apply m
#align Module.monoidal_category.left_unitor_inv_apply ModuleCat.monoidalCategory.leftUnitor_inv_apply

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem rightUnitor_hom_apply {M : ModuleCat.{u} R} (m : M) (r : R) :
    ((ρ_ M).Hom : M ⊗ 𝟙_ (ModuleCat R) ⟶ M) (m ⊗ₜ r) = r • m :=
  TensorProduct.rid_tmul m r
#align Module.monoidal_category.right_unitor_hom_apply ModuleCat.monoidalCategory.rightUnitor_hom_apply

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem rightUnitor_inv_apply {M : ModuleCat.{u} R} (m : M) :
    ((ρ_ M).inv : M ⟶ M ⊗ 𝟙_ (ModuleCat.{u} R)) m = m ⊗ₜ[R] 1 :=
  TensorProduct.rid_symm_apply m
#align Module.monoidal_category.right_unitor_inv_apply ModuleCat.monoidalCategory.rightUnitor_inv_apply

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem associator_hom_apply {M N K : ModuleCat.{u} R} (m : M) (n : N) (k : K) :
    ((α_ M N K).Hom : (M ⊗ N) ⊗ K ⟶ M ⊗ N ⊗ K) (m ⊗ₜ n ⊗ₜ k) = m ⊗ₜ (n ⊗ₜ k) :=
  rfl
#align Module.monoidal_category.associator_hom_apply ModuleCat.monoidalCategory.associator_hom_apply

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem associator_inv_apply {M N K : ModuleCat.{u} R} (m : M) (n : N) (k : K) :
    ((α_ M N K).inv : M ⊗ N ⊗ K ⟶ (M ⊗ N) ⊗ K) (m ⊗ₜ (n ⊗ₜ k)) = m ⊗ₜ n ⊗ₜ k :=
  rfl
#align Module.monoidal_category.associator_inv_apply ModuleCat.monoidalCategory.associator_inv_apply

end MonoidalCategory

/-- (implementation) the braiding for R-modules -/
def braiding (M N : ModuleCat R) : tensorObj M N ≅ tensorObj N M :=
  LinearEquiv.toModuleIso (TensorProduct.comm R M N)
#align Module.braiding ModuleCat.braiding

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem braiding_naturality {X₁ X₂ Y₁ Y₂ : ModuleCat.{u} R} (f : X₁ ⟶ Y₁) (g : X₂ ⟶ Y₂) :
    (f ⊗ g) ≫ (Y₁.braiding Y₂).Hom = (X₁.braiding X₂).Hom ≫ (g ⊗ f) :=
  by
  apply TensorProduct.ext'
  intro x y
  rfl
#align Module.braiding_naturality ModuleCat.braiding_naturality

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem hexagon_forward (X Y Z : ModuleCat.{u} R) :
    (α_ X Y Z).Hom ≫ (braiding X _).Hom ≫ (α_ Y Z X).Hom =
      ((braiding X Y).Hom ⊗ 𝟙 Z) ≫ (α_ Y X Z).Hom ≫ (𝟙 Y ⊗ (braiding X Z).Hom) :=
  by
  apply TensorProduct.ext_threefold
  intro x y z
  rfl
#align Module.hexagon_forward ModuleCat.hexagon_forward

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem hexagon_reverse (X Y Z : ModuleCat.{u} R) :
    (α_ X Y Z).inv ≫ (braiding _ Z).Hom ≫ (α_ Z X Y).inv =
      (𝟙 X ⊗ (Y.braiding Z).Hom) ≫ (α_ X Z Y).inv ≫ ((X.braiding Z).Hom ⊗ 𝟙 Y) :=
  by
  apply (cancel_epi (α_ X Y Z).Hom).1
  apply TensorProduct.ext_threefold
  intro x y z
  rfl
#align Module.hexagon_reverse ModuleCat.hexagon_reverse

attribute [local ext] TensorProduct.ext

/-- The symmetric monoidal structure on `Module R`. -/
instance symmetricCategory : SymmetricCategory (ModuleCat.{u} R)
    where
  braiding := braiding
  braiding_naturality' X₁ X₂ Y₁ Y₂ f g := braiding_naturality f g
  hexagon_forward' := hexagon_forward
  hexagon_reverse' := hexagon_reverse
#align Module.symmetric_category ModuleCat.symmetricCategory

namespace MonoidalCategory

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem braiding_hom_apply {M N : ModuleCat.{u} R} (m : M) (n : N) :
    ((β_ M N).Hom : M ⊗ N ⟶ N ⊗ M) (m ⊗ₜ n) = n ⊗ₜ m :=
  rfl
#align Module.monoidal_category.braiding_hom_apply ModuleCat.monoidalCategory.braiding_hom_apply

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem braiding_inv_apply {M N : ModuleCat.{u} R} (m : M) (n : N) :
    ((β_ M N).inv : N ⊗ M ⟶ M ⊗ N) (n ⊗ₜ m) = m ⊗ₜ n :=
  rfl
#align Module.monoidal_category.braiding_inv_apply ModuleCat.monoidalCategory.braiding_inv_apply

end MonoidalCategory

open Opposite

instance : MonoidalPreadditive (ModuleCat.{u} R) := by
  refine' ⟨_, _, _, _⟩ <;> dsimp only [autoParam] <;> intros <;>
      refine' TensorProduct.ext (LinearMap.ext fun x => LinearMap.ext fun y => _) <;>
    simp only [LinearMap.compr₂_apply, TensorProduct.mk_apply, monoidal_category.hom_apply,
      LinearMap.zero_apply, TensorProduct.tmul_zero, TensorProduct.zero_tmul, LinearMap.add_apply,
      TensorProduct.tmul_add, TensorProduct.add_tmul]

instance : MonoidalLinear R (ModuleCat.{u} R) := by
  refine' ⟨_, _⟩ <;> dsimp only [autoParam] <;> intros <;>
      refine' TensorProduct.ext (LinearMap.ext fun x => LinearMap.ext fun y => _) <;>
    simp only [LinearMap.compr₂_apply, TensorProduct.mk_apply, monoidal_category.hom_apply,
      LinearMap.smul_apply, TensorProduct.tmul_smul, TensorProduct.smul_tmul]

/-- Auxiliary definition for the `monoidal_closed` instance on `Module R`.
(This is only a separate definition in order to speed up typechecking. )
-/
@[simps]
def monoidalClosedHomEquiv (M N P : ModuleCat.{u} R) :
    ((MonoidalCategory.tensorLeft M).obj N ⟶ P) ≃
      (N ⟶ ((linearCoyoneda R (ModuleCat R)).obj (op M)).obj P)
    where
  toFun f := LinearMap.compr₂ (TensorProduct.mk R N M) ((β_ N M).Hom ≫ f)
  invFun f := (β_ M N).Hom ≫ TensorProduct.lift f
  left_inv f := by
    ext (m n)
    simp only [TensorProduct.mk_apply, TensorProduct.lift.tmul, LinearMap.compr₂_apply,
      Function.comp_apply, coe_comp, monoidal_category.braiding_hom_apply]
  right_inv f := by
    ext (m n)
    simp only [TensorProduct.mk_apply, TensorProduct.lift.tmul, LinearMap.compr₂_apply,
      symmetric_category.symmetry_assoc]
#align Module.monoidal_closed_hom_equiv ModuleCat.monoidalClosedHomEquiv

instance : MonoidalClosed (ModuleCat.{u} R)
    where closed' M :=
    {
      isAdj :=
        { right := (linearCoyoneda R (ModuleCat.{u} R)).obj (op M)
          adj := Adjunction.mkOfHomEquiv { homEquiv := fun N P => monoidalClosedHomEquiv M N P } } }

theorem ihom_map_apply {M N P : ModuleCat.{u} R} (f : N ⟶ P) (g : ModuleCat.of R (M ⟶ N)) :
    (ihom M).map f g = g ≫ f :=
  rfl
#align Module.ihom_map_apply ModuleCat.ihom_map_apply

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
-- I can't seem to express the function coercion here without writing `@coe_fn`.
@[simp]
theorem monoidalClosed_curry {M N P : ModuleCat.{u} R} (f : M ⊗ N ⟶ P) (x : M) (y : N) :
    @coeFn _ _ LinearMap.hasCoeToFun ((MonoidalClosed.curry f : N →ₗ[R] M →ₗ[R] P) y) x =
      f (x ⊗ₜ[R] y) :=
  rfl
#align Module.monoidal_closed_curry ModuleCat.monoidalClosed_curry

@[simp]
theorem monoidalClosed_uncurry {M N P : ModuleCat.{u} R} (f : N ⟶ M ⟶[ModuleCat.{u} R] P) (x : M)
    (y : N) : MonoidalClosed.uncurry f (x ⊗ₜ[R] y) = (@coeFn _ _ LinearMap.hasCoeToFun (f y)) x :=
  rfl
#align Module.monoidal_closed_uncurry ModuleCat.monoidalClosed_uncurry

/-- Describes the counit of the adjunction `M ⊗ - ⊣ Hom(M, -)`. Given an `R`-module `N` this
should give a map `M ⊗ Hom(M, N) ⟶ N`, so we flip the order of the arguments in the identity map
`Hom(M, N) ⟶ (M ⟶ N)` and uncurry the resulting map `M ⟶ Hom(M, N) ⟶ N.` -/
theorem ihom_ev_app (M N : ModuleCat.{u} R) :
    (ihom.ev M).app N = TensorProduct.uncurry _ _ _ _ LinearMap.id.flip :=
  by
  ext
  exact ModuleCat.monoidalClosed_uncurry _ _ _
#align Module.ihom_ev_app ModuleCat.ihom_ev_app

/-- Describes the unit of the adjunction `M ⊗ - ⊣ Hom(M, -)`. Given an `R`-module `N` this should
define a map `N ⟶ Hom(M, M ⊗ N)`, which is given by flipping the arguments in the natural
`R`-bilinear map `M ⟶ N ⟶ M ⊗ N`. -/
theorem ihom_coev_app (M N : ModuleCat.{u} R) :
    (ihom.coev M).app N = (TensorProduct.mk _ _ _).flip :=
  rfl
#align Module.ihom_coev_app ModuleCat.ihom_coev_app

theorem monoidalClosed_pre_app {M N : ModuleCat.{u} R} (P : ModuleCat.{u} R) (f : N ⟶ M) :
    (MonoidalClosed.pre f).app P = LinearMap.lcomp R _ f :=
  rfl
#align Module.monoidal_closed_pre_app ModuleCat.monoidalClosed_pre_app

end ModuleCat


/-
Copyright (c) 2020 Aaron Anderson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author:  Aaron Anderson.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.order.bounded_lattice
import Mathlib.data.set.intervals.basic
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Intervals in Lattices

In this file, we provide instances of lattice structures on intervals within lattices.
Some of them depend on the order of the endpoints of the interval, and thus are not made
global instances. These are probably not all of the lattice instances that could be placed on these
intervals, but more can be added easily along the same lines when needed.

## Main definitions
In the following, `*` can represent either `c`, `o`, or `i`.
  * `set.Ic*.semilattice_inf_bot`
  * `set.Ii*.semillatice_inf`
  * `set.I*c.semilattice_sup_top`
  * `set.I*c.semillatice_inf`
  * `set.Iic.bounded_lattice`, within a `bounded_lattice`
  * `set.Ici.bounded_lattice`, within a `bounded_lattice`

-/

namespace set

namespace Ico

protected instance semilattice_inf {α : Type u_1} {a : α} {b : α} [semilattice_inf α] : semilattice_inf ↥(Ico a b) :=
  subtype.semilattice_inf sorry

/-- `Ico a b` has a bottom element whenever `a < b`. -/
def order_bot {α : Type u_1} {a : α} {b : α} [partial_order α] (h : a < b) : order_bot ↥(Ico a b) :=
  order_bot.mk { val := a, property := sorry } partial_order.le partial_order.lt sorry sorry sorry sorry

/-- `Ico a b` is a `semilattice_inf_bot` whenever `a < b`. -/
def semilattice_inf_bot {α : Type u_1} {a : α} {b : α} [semilattice_inf α] (h : a < b) : semilattice_inf_bot ↥(Ico a b) :=
  semilattice_inf_bot.mk order_bot.bot semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry semilattice_inf.inf
    sorry sorry sorry

end Ico

namespace Iio

protected instance semilattice_inf {α : Type u_1} [semilattice_inf α] {a : α} : semilattice_inf ↥(Iio a) :=
  subtype.semilattice_inf sorry

end Iio

namespace Ioc

protected instance semilattice_sup {α : Type u_1} {a : α} {b : α} [semilattice_sup α] : semilattice_sup ↥(Ioc a b) :=
  subtype.semilattice_sup sorry

/-- `Ioc a b` has a top element whenever `a < b`. -/
def order_top {α : Type u_1} {a : α} {b : α} [partial_order α] (h : a < b) : order_top ↥(Ioc a b) :=
  order_top.mk { val := b, property := sorry } partial_order.le partial_order.lt sorry sorry sorry sorry

/-- `Ioc a b` is a `semilattice_sup_top` whenever `a < b`. -/
def semilattice_sup_top {α : Type u_1} {a : α} {b : α} [semilattice_sup α] (h : a < b) : semilattice_sup_top ↥(Ioc a b) :=
  semilattice_sup_top.mk order_top.top semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry semilattice_sup.sup
    sorry sorry sorry

end Ioc

namespace Iio

protected instance set.Ioi.semilattice_sup {α : Type u_1} [semilattice_sup α] {a : α} : semilattice_sup ↥(Ioi a) :=
  subtype.semilattice_sup sorry

end Iio

namespace Iic

protected instance semilattice_inf {α : Type u_1} {a : α} [semilattice_inf α] : semilattice_inf ↥(Iic a) :=
  subtype.semilattice_inf sorry

protected instance semilattice_sup {α : Type u_1} {a : α} [semilattice_sup α] : semilattice_sup ↥(Iic a) :=
  subtype.semilattice_sup sorry

protected instance lattice {α : Type u_1} {a : α} [lattice α] : lattice ↥(Iic a) :=
  lattice.mk semilattice_sup.sup semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry sorry sorry
    semilattice_inf.inf sorry sorry sorry

protected instance order_top {α : Type u_1} {a : α} [partial_order α] : order_top ↥(Iic a) :=
  order_top.mk { val := a, property := sorry } partial_order.le partial_order.lt sorry sorry sorry sorry

@[simp] theorem coe_top {α : Type u_1} [partial_order α] {a : α} : ↑⊤ = a :=
  rfl

protected instance semilattice_inf_top {α : Type u_1} {a : α} [semilattice_inf α] : semilattice_inf_top ↥(Iic a) :=
  semilattice_inf_top.mk order_top.top semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry semilattice_inf.inf
    sorry sorry sorry

protected instance semilattice_sup_top {α : Type u_1} {a : α} [semilattice_sup α] : semilattice_sup_top ↥(Iic a) :=
  semilattice_sup_top.mk order_top.top semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry semilattice_sup.sup
    sorry sorry sorry

protected instance order_bot {α : Type u_1} {a : α} [order_bot α] : order_bot ↥(Iic a) :=
  order_bot.mk { val := ⊥, property := bot_le } partial_order.le partial_order.lt sorry sorry sorry sorry

@[simp] theorem coe_bot {α : Type u_1} [order_bot α] {a : α} : ↑⊥ = ⊥ :=
  rfl

protected instance semilattice_inf_bot {α : Type u_1} {a : α} [semilattice_inf_bot α] : semilattice_inf_bot ↥(Iic a) :=
  semilattice_inf_bot.mk order_bot.bot semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry semilattice_inf.inf
    sorry sorry sorry

protected instance semilattice_sup_bot {α : Type u_1} {a : α} [semilattice_sup_bot α] : semilattice_sup_bot ↥(Iic a) :=
  semilattice_sup_bot.mk order_bot.bot semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry semilattice_sup.sup
    sorry sorry sorry

protected instance bounded_lattice {α : Type u_1} {a : α} [bounded_lattice α] : bounded_lattice ↥(Iic a) :=
  bounded_lattice.mk lattice.sup order_top.le order_top.lt sorry sorry sorry sorry sorry sorry lattice.inf sorry sorry
    sorry order_top.top sorry order_bot.bot sorry

end Iic

namespace Ici

protected instance semilattice_inf {α : Type u_1} {a : α} [semilattice_inf α] : semilattice_inf ↥(Ici a) :=
  subtype.semilattice_inf sorry

protected instance semilattice_sup {α : Type u_1} {a : α} [semilattice_sup α] : semilattice_sup ↥(Ici a) :=
  subtype.semilattice_sup sorry

protected instance lattice {α : Type u_1} {a : α} [lattice α] : lattice ↥(Ici a) :=
  lattice.mk semilattice_sup.sup semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry sorry sorry
    semilattice_inf.inf sorry sorry sorry

protected instance order_bot {α : Type u_1} {a : α} [partial_order α] : order_bot ↥(Ici a) :=
  order_bot.mk { val := a, property := sorry } partial_order.le partial_order.lt sorry sorry sorry sorry

@[simp] theorem coe_bot {α : Type u_1} [partial_order α] {a : α} : ↑⊥ = a :=
  rfl

protected instance semilattice_inf_bot {α : Type u_1} {a : α} [semilattice_inf α] : semilattice_inf_bot ↥(Ici a) :=
  semilattice_inf_bot.mk order_bot.bot semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry semilattice_inf.inf
    sorry sorry sorry

protected instance semilattice_sup_bot {α : Type u_1} {a : α} [semilattice_sup α] : semilattice_sup_bot ↥(Ici a) :=
  semilattice_sup_bot.mk order_bot.bot semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry semilattice_sup.sup
    sorry sorry sorry

protected instance order_top {α : Type u_1} {a : α} [order_top α] : order_top ↥(Ici a) :=
  order_top.mk { val := ⊤, property := le_top } partial_order.le partial_order.lt sorry sorry sorry sorry

@[simp] theorem coe_top {α : Type u_1} [order_top α] {a : α} : ↑⊤ = ⊤ :=
  rfl

protected instance semilattice_sup_top {α : Type u_1} {a : α} [semilattice_sup_top α] : semilattice_sup_top ↥(Ici a) :=
  semilattice_sup_top.mk order_top.top semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry semilattice_sup.sup
    sorry sorry sorry

protected instance semilattice_inf_top {α : Type u_1} {a : α} [semilattice_inf_top α] : semilattice_inf_top ↥(Ici a) :=
  semilattice_inf_top.mk order_top.top semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry semilattice_inf.inf
    sorry sorry sorry

protected instance bounded_lattice {α : Type u_1} {a : α} [bounded_lattice α] : bounded_lattice ↥(Ici a) :=
  bounded_lattice.mk lattice.sup order_top.le order_top.lt sorry sorry sorry sorry sorry sorry lattice.inf sorry sorry
    sorry order_top.top sorry order_bot.bot sorry

end Ici

namespace Icc

protected instance semilattice_inf {α : Type u_1} [semilattice_inf α] {a : α} {b : α} : semilattice_inf ↥(Icc a b) :=
  subtype.semilattice_inf sorry

protected instance semilattice_sup {α : Type u_1} [semilattice_sup α] {a : α} {b : α} : semilattice_sup ↥(Icc a b) :=
  subtype.semilattice_sup sorry

protected instance lattice {α : Type u_1} [lattice α] {a : α} {b : α} : lattice ↥(Icc a b) :=
  lattice.mk semilattice_sup.sup semilattice_inf.le semilattice_inf.lt sorry sorry sorry sorry sorry sorry
    semilattice_inf.inf sorry sorry sorry

/-- `Icc a b` has a bottom element whenever `a ≤ b`. -/
def order_bot {α : Type u_1} [partial_order α] {a : α} {b : α} (h : a ≤ b) : order_bot ↥(Icc a b) :=
  order_bot.mk { val := a, property := sorry } partial_order.le partial_order.lt sorry sorry sorry sorry

/-- `Icc a b` has a top element whenever `a ≤ b`. -/
def order_top {α : Type u_1} [partial_order α] {a : α} {b : α} (h : a ≤ b) : order_top ↥(Icc a b) :=
  order_top.mk { val := b, property := sorry } partial_order.le partial_order.lt sorry sorry sorry sorry

/-- `Icc a b` is a `semilattice_inf_bot` whenever `a ≤ b`. -/
def semilattice_inf_bot {α : Type u_1} [semilattice_inf α] {a : α} {b : α} (h : a ≤ b) : semilattice_inf_bot ↥(Icc a b) :=
  semilattice_inf_bot.mk order_bot.bot order_bot.le order_bot.lt sorry sorry sorry sorry semilattice_inf.inf sorry sorry
    sorry

/-- `Icc a b` is a `semilattice_inf_top` whenever `a ≤ b`. -/
def semilattice_inf_top {α : Type u_1} [semilattice_inf α] {a : α} {b : α} (h : a ≤ b) : semilattice_inf_top ↥(Icc a b) :=
  semilattice_inf_top.mk order_top.top order_top.le order_top.lt sorry sorry sorry sorry semilattice_inf.inf sorry sorry
    sorry

/-- `Icc a b` is a `semilattice_sup_bot` whenever `a ≤ b`. -/
def semilattice_sup_bot {α : Type u_1} [semilattice_sup α] {a : α} {b : α} (h : a ≤ b) : semilattice_sup_bot ↥(Icc a b) :=
  semilattice_sup_bot.mk order_bot.bot order_bot.le order_bot.lt sorry sorry sorry sorry semilattice_sup.sup sorry sorry
    sorry

/-- `Icc a b` is a `semilattice_sup_top` whenever `a ≤ b`. -/
def semilattice_sup_top {α : Type u_1} [semilattice_sup α] {a : α} {b : α} (h : a ≤ b) : semilattice_sup_top ↥(Icc a b) :=
  semilattice_sup_top.mk order_top.top order_top.le order_top.lt sorry sorry sorry sorry semilattice_sup.sup sorry sorry
    sorry

/-- `Icc a b` is a `bounded_lattice` whenever `a ≤ b`. -/
def bounded_lattice {α : Type u_1} [lattice α] {a : α} {b : α} (h : a ≤ b) : bounded_lattice ↥(Icc a b) :=
  bounded_lattice.mk semilattice_sup_top.sup semilattice_inf_bot.le semilattice_inf_bot.lt sorry sorry sorry sorry sorry
    sorry semilattice_inf_bot.inf sorry sorry sorry semilattice_sup_top.top sorry semilattice_inf_bot.bot sorry