-- import data.set
import tactic

-- dEAduction imports
import structures2
import utils

-- General principles :
-- Type should be defined as parameters, in order to be implicit everywhere
-- other parameters are implicit in definitions, i.e. defined using '{}' (e.g. {A : set X} )
-- but explicit everywhere else, i.e. defined using '()' (e.g. (A : set X) )
-- each definition must be an iff statement (since it will be called with 'rw' or 'symp_rw')

-- no Magic button ("compute")
/- dEAduction
Title
    Logique propositionnelle (tutoriel)
Author
    Frédéric Le Roux
Institution
    Université du Monde
Description
    Ce fichier, qui peut servir de tutoriel, contient quelques énoncés de logiques propositionnelle.
    Le but n'est pas de les démontrer du point de vue de la logique propositionnelle,
    mais plutôt de voir comment l'interface fonctionne sur ces énoncés.
DefaultAvailableProof
    NONE
DefaultAvailableMagic
    Assumption
-/

-- logic names ['and', 'or', 'negate', 'implies', 'iff', 'forall', 'exists']
-- proofs names ['proof_methods', 'new_object', 'apply']
-- magic names ['compute', 'assumption']

local attribute [instance] classical.prop_decidable
---------------------------------------------
-- global parameters = implicit variables --
---------------------------------------------
section course
variables (P Q R: Prop) -- NOT global
notation [parsing_only] P ` \and ` Q := P ∧ Q
notation [parsing_only]  P ` \or ` Q := P ∨ Q
notation [parsing_only]  ` \not ` P := ¬ P
notation [parsing_only]  P ` \implies ` Q := P → Q
notation [parsing_only]  P ` \iff ` Q := P ↔ Q

------------------
-- COURSE TITLE --
------------------
namespace Logique_propositionnelle

lemma exercise.tautologie :
P → P
:=
/- dEAduction
PrettyName
    La tautologie
Description
    Le bouton "=>" permet de démontrer une implication : pour montrer
    "P => Q", on suppose P, et on montre Q.
AvailableLogic
    implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.et :
P ∧ Q → P
:=
/- dEAduction
PrettyName
    P et Q implique P
Description
    Le bouton "ET" permet de découper une hypothèse
AvailableLogic
    and implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.et_commutatif_I :
P ∧ Q → Q ∧ P
:=
/- dEAduction
PrettyName
    Le "ET" est commutatif (version faible)
Description
    Le bouton "ET" permet aussi de découper le but en deux buts distincts
AvailableLogic
    and implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.et_commutatif_II :
P ∧ Q ↔ Q ∧ P
:=
/- dEAduction
PrettyName
    Le "ET" est commutatif
Description
    Le bouton "↔" permet de découper le but en deux implications.
    On peut alors appliquer le résultat de l'exercice précédent en le
    sélectionnant dans la liste...
AvailableLogic
    and iff implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.ou_commutatif :
P ∨ Q ↔ Q ∨ P
:=
/- dEAduction
PrettyName
    Le "OU" est commutatif
Description
    Pour utiliser l'hypothèse "P OU Q", on sépare les cas :
    dans le premier cas on suppose P, dans le second cas on suppose Q.
    Pour démontrer "Q OU P", on doit démontrer soit P, soit Q.
AvailableLogic
    and or implies iff
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.non_non :
¬ ¬ P ↔ P
:=
/- dEAduction
PrettyName
    Double négation
Description
    Le bouton "NON" permet d'utiliser les règles logiques du "NON".
    On peut l'utiliser uniquement sur les propriétés qui sont des négations,
    c'est-à-dire de la forme "NON (...)".
AvailableLogic
    and or negate implies iff
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.non_ET :
¬ (P ∧ Q) ↔ (¬ P) ∨ (¬ Q)
:=
/- dEAduction
PrettyName
    Négation d'un "ET"
Description
    En général, le bouton "NON" tente de "pousser" la négation le long de la
    propriété.
AvailableLogic
    and or negate implies iff
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.contradiction :
R ∨ ¬ R
:=
/- dEAduction
PrettyName
    Le tiers exclu : l'une des deux propriétés "R" et "NON R" est vraie
Description
    Le mécanisme de preuve inclus le tiers exclu, de façon un peu cachée...
AvailableLogic
    and or negate implies iff
AvailableProof
    use_proof_methods
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.contraposition :
(P → Q) ↔ (¬ Q → ¬ P)
:=
/- dEAduction
PrettyName
    Contraposition
Description
    Le bouton "=>" permet également d'appliquer une implication "P => Q" à la
    propriété "P" pour obtenir la propriété "Q". Attention, avant de
    l'actionner il faut sélectionner toutes les propriétés requises !
AvailableLogic
    and or negate implies iff
AvailableProof
    use_proof_methods apply
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.ou_implication_I :
(P → Q) ↔ (¬ P ∨ Q)
:=
/- dEAduction
PrettyName
    Implication sous forme de "OU"
Description
    Cette propriété permet de transformer une implication en une disjonction
AvailableLogic
    and or negate implies iff
AvailableProof
    use_proof_methods apply
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.ou_implication_II :
(P ∨ Q) ↔ (¬ P → Q)
:=
/- dEAduction
PrettyName
    "OU" sous forme d'implication
Description
    Cette propriété est très proche de la précédente.
    On peut la redémontrer entièrement, mais on peut aussi tenter d'appliquer
    le résultat de l'exercice précédent. Pour cela, il faudra introduire
    un nouvel objet, avant de lui appliquer le résultat précédent...
AvailableLogic
    and or negate implies iff
AvailableProof
    use_proof_methods new_object apply
AvailableMagic
    assumption
-/
begin
    sorry
end

end Logique_propositionnelle

end course


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/

import algebra.big_operators.basic
import data.finset.intervals
import tactic.linarith

/-!
# Results about big operators over intervals

We prove results about big operators over intervals (mostly the `ℕ`-valued `Ico m n`).
-/

universes u v w

open_locale big_operators nat

namespace finset

variables {α : Type u} {β : Type v} {γ : Type w} {s₂ s₁ s : finset α} {a : α} {g f : α → β}
  [comm_monoid β]

lemma sum_Ico_add {δ : Type*} [add_comm_monoid δ] (f : ℕ → δ) (m n k : ℕ) :
  (∑ l in Ico m n, f (k + l)) = (∑ l in Ico (m + k) (n + k), f l) :=
Ico.image_add m n k ▸ eq.symm $ sum_image $ λ x hx y hy h, nat.add_left_cancel h

@[to_additive]
lemma prod_Ico_add (f : ℕ → β) (m n k : ℕ) :
  (∏ l in Ico m n, f (k + l)) = (∏ l in Ico (m + k) (n + k), f l) :=
@sum_Ico_add (additive β) _ f m n k

lemma sum_Ico_succ_top {δ : Type*} [add_comm_monoid δ] {a b : ℕ}
  (hab : a ≤ b) (f : ℕ → δ) : (∑ k in Ico a (b + 1), f k) = (∑ k in Ico a b, f k) + f b :=
by rw [Ico.succ_top hab, sum_insert Ico.not_mem_top, add_comm]

@[to_additive]
lemma prod_Ico_succ_top {a b : ℕ} (hab : a ≤ b) (f : ℕ → β) :
  (∏ k in Ico a (b + 1), f k) = (∏ k in Ico a b, f k) * f b :=
@sum_Ico_succ_top (additive β) _ _ _ hab _

lemma sum_eq_sum_Ico_succ_bot {δ : Type*} [add_comm_monoid δ] {a b : ℕ}
  (hab : a < b) (f : ℕ → δ) : (∑ k in Ico a b, f k) = f a + (∑ k in Ico (a + 1) b, f k) :=
have ha : a ∉ Ico (a + 1) b, by simp,
by rw [← sum_insert ha, Ico.insert_succ_bot hab]

@[to_additive]
lemma prod_eq_prod_Ico_succ_bot {a b : ℕ} (hab : a < b) (f : ℕ → β) :
  (∏ k in Ico a b, f k) = f a * (∏ k in Ico (a + 1) b, f k) :=
@sum_eq_sum_Ico_succ_bot (additive β) _ _ _ hab _

@[to_additive]
lemma prod_Ico_consecutive (f : ℕ → β) {m n k : ℕ} (hmn : m ≤ n) (hnk : n ≤ k) :
  (∏ i in Ico m n, f i) * (∏ i in Ico n k, f i) = (∏ i in Ico m k, f i) :=
Ico.union_consecutive hmn hnk ▸ eq.symm $ prod_union $ Ico.disjoint_consecutive m n k

@[to_additive]
lemma prod_range_mul_prod_Ico (f : ℕ → β) {m n : ℕ} (h : m ≤ n) :
  (∏ k in range m, f k) * (∏ k in Ico m n, f k) = (∏ k in range n, f k) :=
Ico.zero_bot m ▸ Ico.zero_bot n ▸ prod_Ico_consecutive f (nat.zero_le m) h

@[to_additive]
lemma prod_Ico_eq_mul_inv {δ : Type*} [comm_group δ] (f : ℕ → δ) {m n : ℕ} (h : m ≤ n) :
  (∏ k in Ico m n, f k) = (∏ k in range n, f k) * (∏ k in range m, f k)⁻¹ :=
eq_mul_inv_iff_mul_eq.2 $ by rw [mul_comm]; exact prod_range_mul_prod_Ico f h

lemma sum_Ico_eq_sub {δ : Type*} [add_comm_group δ] (f : ℕ → δ) {m n : ℕ} (h : m ≤ n) :
  (∑ k in Ico m n, f k) = (∑ k in range n, f k) - (∑ k in range m, f k) :=
by simpa only [sub_eq_add_neg] using sum_Ico_eq_add_neg f h

/-- The two ways of summing over `(i,j)` in the range `a<=i<=j<b` are equal. -/
lemma sum_Ico_Ico_comm {M : Type*} [add_comm_monoid M]
  (a b : ℕ) (f : ℕ → ℕ → M) :
  ∑ i in finset.Ico a b, ∑ j in finset.Ico i b, f i j =
  ∑ j in finset.Ico a b, ∑ i in finset.Ico a (j+1), f i j :=
begin
  rw [finset.sum_sigma', finset.sum_sigma'],
  refine finset.sum_bij'
    (λ (x : Σ (i : ℕ), ℕ) _, (⟨x.2, x.1⟩ : Σ (i : ℕ), ℕ)) _ (λ _ _, rfl)
    (λ (x : Σ (i : ℕ), ℕ) _, (⟨x.2, x.1⟩ : Σ (i : ℕ), ℕ)) _
    (by rintro ⟨⟩ _; refl) (by rintro ⟨⟩ _; refl);
  simp only [finset.Ico.mem, sigma.forall, finset.mem_sigma];
  rintros a b ⟨⟨h₁,h₂⟩, ⟨h₃, h₄⟩⟩; refine ⟨⟨_, _⟩, ⟨_, _⟩⟩; linarith
end

@[to_additive]
lemma prod_Ico_eq_prod_range (f : ℕ → β) (m n : ℕ) :
  (∏ k in Ico m n, f k) = (∏ k in range (n - m), f (m + k)) :=
begin
  by_cases h : m ≤ n,
  { rw [← Ico.zero_bot, prod_Ico_add, zero_add, nat.sub_add_cancel h] },
  { replace h : n ≤ m :=  le_of_not_ge h,
     rw [Ico.eq_empty_of_le h, nat.sub_eq_zero_of_le h, range_zero, prod_empty, prod_empty] }
end

lemma prod_Ico_reflect (f : ℕ → β) (k : ℕ) {m n : ℕ} (h : m ≤ n + 1) :
  ∏ j in Ico k m, f (n - j) = ∏ j in Ico (n + 1 - m) (n + 1 - k), f j :=
begin
  have : ∀ i < m, i ≤ n,
  { intros i hi,
    exact (add_le_add_iff_right 1).1 (le_trans (nat.lt_iff_add_one_le.1 hi) h) },
  cases lt_or_le k m with hkm hkm,
  { rw [← finset.Ico.image_const_sub (this _ hkm)],
    refine (prod_image _).symm,
    simp only [Ico.mem],
    rintros i ⟨ki, im⟩ j ⟨kj, jm⟩ Hij,
    rw [← nat.sub_sub_self (this _ im), Hij, nat.sub_sub_self (this _ jm)] },
  { simp [Ico.eq_empty_of_le, nat.sub_le_sub_left, hkm] }
end

lemma sum_Ico_reflect {δ : Type*} [add_comm_monoid δ] (f : ℕ → δ) (k : ℕ) {m n : ℕ}
  (h : m ≤ n + 1) :
  ∑ j in Ico k m, f (n - j) = ∑ j in Ico (n + 1 - m) (n + 1 - k), f j :=
@prod_Ico_reflect (multiplicative δ) _ f k m n h

lemma prod_range_reflect (f : ℕ → β) (n : ℕ) :
  ∏ j in range n, f (n - 1 - j) = ∏ j in range n, f j :=
begin
  cases n,
  { simp },
  { simp only [range_eq_Ico, nat.succ_sub_succ_eq_sub, nat.sub_zero],
    rw [prod_Ico_reflect _ _ (le_refl _)],
    simp }
end

lemma sum_range_reflect {δ : Type*} [add_comm_monoid δ] (f : ℕ → δ) (n : ℕ) :
  ∑ j in range n, f (n - 1 - j) = ∑ j in range n, f j :=
@prod_range_reflect (multiplicative δ) _ f n

@[simp] lemma prod_Ico_id_eq_factorial : ∀ n : ℕ, ∏ x in Ico 1 (n + 1), x = n!
| 0 := rfl
| (n+1) := by rw [prod_Ico_succ_top $ nat.succ_le_succ $ zero_le n,
  nat.factorial_succ, prod_Ico_id_eq_factorial n, nat.succ_eq_add_one, mul_comm]

@[simp] lemma prod_range_add_one_eq_factorial : ∀ n : ℕ, ∏ x in range n, (x+1) = n!
| 0 := rfl
| (n+1) := by simp [finset.range_succ, prod_range_add_one_eq_factorial n]

section gauss_sum

/-- Gauss' summation formula -/
lemma sum_range_id_mul_two (n : ℕ) :
  (∑ i in range n, i) * 2 = n * (n - 1) :=
calc (∑ i in range n, i) * 2 = (∑ i in range n, i) + (∑ i in range n, (n - 1 - i)) :
  by rw [sum_range_reflect (λ i, i) n, mul_two]
... = ∑ i in range n, (i + (n - 1 - i)) : sum_add_distrib.symm
... = ∑ i in range n, (n - 1) : sum_congr rfl $ λ i hi, nat.add_sub_cancel' $
  nat.le_pred_of_lt $ mem_range.1 hi
... = n * (n - 1) : by rw [sum_const, card_range, nat.nsmul_eq_mul]

/-- Gauss' summation formula -/
lemma sum_range_id (n : ℕ) : (∑ i in range n, i) = (n * (n - 1)) / 2 :=
by rw [← sum_range_id_mul_two n, nat.mul_div_cancel]; exact dec_trivial

end gauss_sum

end finset


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad

The order relation on the integers.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.data.int.basic
import Mathlib.Lean3Lib.init.data.ordering.basic

namespace Mathlib

namespace int

def nonneg (a : ℤ) := int.cases_on a (fun (n : ℕ) => True) fun (n : ℕ) => False

protected def le (a : ℤ) (b : ℤ) := nonneg (b - a)

protected instance has_le : HasLessEq ℤ := { LessEq := int.le }

protected def lt (a : ℤ) (b : ℤ) := a + 1 ≤ b

protected instance has_lt : HasLess ℤ := { Less := int.lt }

def decidable_nonneg (a : ℤ) : Decidable (nonneg a) :=
  int.cases_on a (fun (a : ℕ) => decidable.true) fun (a : ℕ) => decidable.false

protected instance decidable_le (a : ℤ) (b : ℤ) : Decidable (a ≤ b) := decidable_nonneg (b - a)

protected instance decidable_lt (a : ℤ) (b : ℤ) : Decidable (a < b) :=
  decidable_nonneg (b - (a + 1))

theorem lt_iff_add_one_le (a : ℤ) (b : ℤ) : a < b ↔ a + 1 ≤ b := iff.refl (a < b)

theorem nonneg.elim {a : ℤ} : nonneg a → ∃ (n : ℕ), a = ↑n :=
  int.cases_on a (fun (n : ℕ) (H : nonneg (Int.ofNat n)) => exists.intro n rfl)
    fun (n' : ℕ) => false.elim

theorem nonneg_or_nonneg_neg (a : ℤ) : nonneg a ∨ nonneg (-a) :=
  int.cases_on a (fun (n : ℕ) => Or.inl trivial) fun (n : ℕ) => Or.inr trivial

theorem le.intro_sub {a : ℤ} {b : ℤ} {n : ℕ} (h : b - a = ↑n) : a ≤ b :=
  (fun (this : nonneg (b - a)) => this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (nonneg (b - a))) h)) trivial)

theorem le.intro {a : ℤ} {b : ℤ} {n : ℕ} (h : a + ↑n = b) : a ≤ b := sorry

theorem le.dest_sub {a : ℤ} {b : ℤ} (h : a ≤ b) : ∃ (n : ℕ), b - a = ↑n := nonneg.elim h

theorem le.dest {a : ℤ} {b : ℤ} (h : a ≤ b) : ∃ (n : ℕ), a + ↑n = b := sorry

theorem le.elim {a : ℤ} {b : ℤ} (h : a ≤ b) {P : Prop} (h' : ∀ (n : ℕ), a + ↑n = b → P) : P :=
  exists.elim (le.dest h) h'

protected theorem le_total (a : ℤ) (b : ℤ) : a ≤ b ∨ b ≤ a := sorry

theorem coe_nat_le_coe_nat_of_le {m : ℕ} {n : ℕ} (h : m ≤ n) : ↑m ≤ ↑n := sorry

theorem le_of_coe_nat_le_coe_nat {m : ℕ} {n : ℕ} (h : ↑m ≤ ↑n) : m ≤ n :=
  le.elim h
    fun (k : ℕ) (hk : ↑m + ↑k = ↑n) =>
      (fun (this : m + k = n) => nat.le.intro this)
        (int.coe_nat_inj (Eq.trans (int.coe_nat_add m k) hk))

theorem coe_nat_le_coe_nat_iff (m : ℕ) (n : ℕ) : ↑m ≤ ↑n ↔ m ≤ n :=
  { mp := le_of_coe_nat_le_coe_nat, mpr := coe_nat_le_coe_nat_of_le }

theorem coe_zero_le (n : ℕ) : 0 ≤ ↑n := coe_nat_le_coe_nat_of_le (nat.zero_le n)

theorem eq_coe_of_zero_le {a : ℤ} (h : 0 ≤ a) : ∃ (n : ℕ), a = ↑n := sorry

theorem eq_succ_of_zero_lt {a : ℤ} (h : 0 < a) : ∃ (n : ℕ), a = ↑(Nat.succ n) := sorry

theorem lt_add_succ (a : ℤ) (n : ℕ) : a < a + ↑(Nat.succ n) := sorry

theorem lt.intro {a : ℤ} {b : ℤ} {n : ℕ} (h : a + ↑(Nat.succ n) = b) : a < b := h ▸ lt_add_succ a n

theorem lt.dest {a : ℤ} {b : ℤ} (h : a < b) : ∃ (n : ℕ), a + ↑(Nat.succ n) = b := sorry

theorem lt.elim {a : ℤ} {b : ℤ} (h : a < b) {P : Prop} (h' : ∀ (n : ℕ), a + ↑(Nat.succ n) = b → P) :
    P :=
  exists.elim (lt.dest h) h'

theorem coe_nat_lt_coe_nat_iff (n : ℕ) (m : ℕ) : ↑n < ↑m ↔ n < m := sorry

theorem lt_of_coe_nat_lt_coe_nat {m : ℕ} {n : ℕ} (h : ↑m < ↑n) : m < n :=
  iff.mp (coe_nat_lt_coe_nat_iff m n) h

theorem coe_nat_lt_coe_nat_of_lt {m : ℕ} {n : ℕ} (h : m < n) : ↑m < ↑n :=
  iff.mpr (coe_nat_lt_coe_nat_iff m n) h

/- show that the integers form an ordered additive group -/

protected theorem le_refl (a : ℤ) : a ≤ a := le.intro (int.add_zero a)

protected theorem le_trans {a : ℤ} {b : ℤ} {c : ℤ} (h₁ : a ≤ b) (h₂ : b ≤ c) : a ≤ c := sorry

protected theorem le_antisymm {a : ℤ} {b : ℤ} (h₁ : a ≤ b) (h₂ : b ≤ a) : a = b := sorry

protected theorem lt_irrefl (a : ℤ) : ¬a < a := sorry

protected theorem ne_of_lt {a : ℤ} {b : ℤ} (h : a < b) : a ≠ b :=
  fun (this : a = b) => absurd (eq.mp (Eq._oldrec (Eq.refl (a < b)) this) h) (int.lt_irrefl b)

theorem le_of_lt {a : ℤ} {b : ℤ} (h : a < b) : a ≤ b :=
  lt.elim h fun (n : ℕ) (hn : a + ↑(Nat.succ n) = b) => le.intro hn

protected theorem lt_iff_le_and_ne (a : ℤ) (b : ℤ) : a < b ↔ a ≤ b ∧ a ≠ b := sorry

theorem lt_succ (a : ℤ) : a < a + 1 := int.le_refl (a + 1)

protected theorem add_le_add_left {a : ℤ} {b : ℤ} (h : a ≤ b) (c : ℤ) : c + a ≤ c + b := sorry

protected theorem add_lt_add_left {a : ℤ} {b : ℤ} (h : a < b) (c : ℤ) : c + a < c + b := sorry

protected theorem mul_nonneg {a : ℤ} {b : ℤ} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a * b := sorry

protected theorem mul_pos {a : ℤ} {b : ℤ} (ha : 0 < a) (hb : 0 < b) : 0 < a * b := sorry

protected theorem zero_lt_one : 0 < 1 := trivial

protected theorem lt_iff_le_not_le {a : ℤ} {b : ℤ} : a < b ↔ a ≤ b ∧ ¬b ≤ a := sorry

protected instance linear_order : linear_order ℤ :=
  linear_order.mk int.le int.lt int.le_refl int.le_trans int.le_antisymm int.le_total
    int.decidable_le int.decidable_eq int.decidable_lt

theorem eq_nat_abs_of_zero_le {a : ℤ} (h : 0 ≤ a) : a = ↑(nat_abs a) := sorry

theorem le_nat_abs {a : ℤ} : a ≤ ↑(nat_abs a) := sorry

theorem neg_succ_lt_zero (n : ℕ) : Int.negSucc n < 0 := sorry

theorem eq_neg_succ_of_lt_zero {a : ℤ} : a < 0 → ∃ (n : ℕ), a = Int.negSucc n := sorry

/- int is an ordered add comm group -/

protected theorem eq_neg_of_eq_neg {a : ℤ} {b : ℤ} (h : a = -b) : b = -a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (b = -a)) h))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b = --b)) (int.neg_neg b))) (Eq.refl b))

protected theorem neg_add_cancel_left (a : ℤ) (b : ℤ) : -a + (a + b) = b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (-a + (a + b) = b)) (Eq.symm (int.add_assoc (-a) a b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (-a + a + b = b)) (int.add_left_neg a)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 + b = b)) (int.zero_add b))) (Eq.refl b)))

protected theorem add_neg_cancel_left (a : ℤ) (b : ℤ) : a + (-a + b) = b :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a + (-a + b) = b)) (Eq.symm (int.add_assoc a (-a) b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + -a + b = b)) (int.add_right_neg a)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (0 + b = b)) (int.zero_add b))) (Eq.refl b)))

protected theorem add_neg_cancel_right (a : ℤ) (b : ℤ) : a + b + -b = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a + b + -b = a)) (int.add_assoc a b (-b))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + (b + -b) = a)) (int.add_right_neg b)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (a + 0 = a)) (int.add_zero a))) (Eq.refl a)))

protected theorem neg_add_cancel_right (a : ℤ) (b : ℤ) : a + -b + b = a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a + -b + b = a)) (int.add_assoc a (-b) b)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + (-b + b) = a)) (int.add_left_neg b)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (a + 0 = a)) (int.add_zero a))) (Eq.refl a)))

protected theorem sub_self (a : ℤ) : a - a = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a - a = 0)) int.sub_eq_add_neg))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + -a = 0)) (int.add_right_neg a))) (Eq.refl 0))

protected theorem sub_eq_zero_of_eq {a : ℤ} {b : ℤ} (h : a = b) : a - b = 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a - b = 0)) h))
    (eq.mpr (id (Eq._oldrec (Eq.refl (b - b = 0)) (int.sub_self b))) (Eq.refl 0))

protected theorem eq_of_sub_eq_zero {a : ℤ} {b : ℤ} (h : a - b = 0) : a = b := sorry

protected theorem sub_eq_zero_iff_eq {a : ℤ} {b : ℤ} : a - b = 0 ↔ a = b :=
  { mp := int.eq_of_sub_eq_zero, mpr := int.sub_eq_zero_of_eq }

@[simp] protected theorem neg_eq_of_add_eq_zero {a : ℤ} {b : ℤ} (h : a + b = 0) : -a = b := sorry

protected theorem neg_mul_eq_neg_mul (a : ℤ) (b : ℤ) : -(a * b) = -a * b := sorry

protected theorem neg_mul_eq_mul_neg (a : ℤ) (b : ℤ) : -(a * b) = a * -b := sorry

@[simp] theorem neg_mul_eq_neg_mul_symm (a : ℤ) (b : ℤ) : -a * b = -(a * b) :=
  Eq.symm (int.neg_mul_eq_neg_mul a b)

@[simp] theorem mul_neg_eq_neg_mul_symm (a : ℤ) (b : ℤ) : a * -b = -(a * b) :=
  Eq.symm (int.neg_mul_eq_mul_neg a b)

protected theorem neg_mul_neg (a : ℤ) (b : ℤ) : -a * -b = a * b := sorry

protected theorem neg_mul_comm (a : ℤ) (b : ℤ) : -a * b = a * -b := sorry

protected theorem mul_sub (a : ℤ) (b : ℤ) (c : ℤ) : a * (b - c) = a * b - a * c := sorry

protected theorem sub_mul (a : ℤ) (b : ℤ) (c : ℤ) : (a - b) * c = a * c - b * c := sorry

protected theorem le_of_add_le_add_left {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b ≤ a + c) : b ≤ c := sorry

protected theorem lt_of_add_lt_add_left {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b < a + c) : b < c := sorry

protected theorem add_le_add_right {a : ℤ} {b : ℤ} (h : a ≤ b) (c : ℤ) : a + c ≤ b + c :=
  int.add_comm c a ▸ int.add_comm c b ▸ int.add_le_add_left h c

protected theorem add_lt_add_right {a : ℤ} {b : ℤ} (h : a < b) (c : ℤ) : a + c < b + c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a + c < b + c)) (int.add_comm a c)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (c + a < b + c)) (int.add_comm b c)))
      (int.add_lt_add_left h c))

protected theorem add_le_add {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (h₁ : a ≤ b) (h₂ : c ≤ d) :
    a + c ≤ b + d :=
  le_trans (int.add_le_add_right h₁ c) (int.add_le_add_left h₂ b)

protected theorem le_add_of_nonneg_right {a : ℤ} {b : ℤ} (h : b ≥ 0) : a ≤ a + b :=
  (fun (this : a + b ≥ a + 0) => eq.mp (Eq._oldrec (Eq.refl (a + b ≥ a + 0)) (int.add_zero a)) this)
    (int.add_le_add_left h a)

protected theorem le_add_of_nonneg_left {a : ℤ} {b : ℤ} (h : b ≥ 0) : a ≤ b + a :=
  (fun (this : 0 + a ≤ b + a) => eq.mp (Eq._oldrec (Eq.refl (0 + a ≤ b + a)) (int.zero_add a)) this)
    (int.add_le_add_right h a)

protected theorem add_lt_add {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (h₁ : a < b) (h₂ : c < d) :
    a + c < b + d :=
  lt_trans (int.add_lt_add_right h₁ c) (int.add_lt_add_left h₂ b)

protected theorem add_lt_add_of_le_of_lt {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (h₁ : a ≤ b) (h₂ : c < d) :
    a + c < b + d :=
  lt_of_le_of_lt (int.add_le_add_right h₁ c) (int.add_lt_add_left h₂ b)

protected theorem add_lt_add_of_lt_of_le {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (h₁ : a < b) (h₂ : c ≤ d) :
    a + c < b + d :=
  lt_of_lt_of_le (int.add_lt_add_right h₁ c) (int.add_le_add_left h₂ b)

protected theorem lt_add_of_pos_right (a : ℤ) {b : ℤ} (h : b > 0) : a < a + b :=
  (fun (this : a + 0 < a + b) => eq.mp (Eq._oldrec (Eq.refl (a + 0 < a + b)) (int.add_zero a)) this)
    (int.add_lt_add_left h a)

protected theorem lt_add_of_pos_left (a : ℤ) {b : ℤ} (h : b > 0) : a < b + a :=
  (fun (this : 0 + a < b + a) => eq.mp (Eq._oldrec (Eq.refl (0 + a < b + a)) (int.zero_add a)) this)
    (int.add_lt_add_right h a)

protected theorem le_of_add_le_add_right {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b ≤ c + b) : a ≤ c :=
  sorry

protected theorem lt_of_add_lt_add_right {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b < c + b) : a < c :=
  sorry

-- here we start using properties of zero.

protected theorem add_nonneg {a : ℤ} {b : ℤ} (ha : 0 ≤ a) (hb : 0 ≤ b) : 0 ≤ a + b :=
  int.zero_add 0 ▸ int.add_le_add ha hb

protected theorem add_pos {a : ℤ} {b : ℤ} (ha : 0 < a) (hb : 0 < b) : 0 < a + b :=
  int.zero_add 0 ▸ int.add_lt_add ha hb

protected theorem add_pos_of_pos_of_nonneg {a : ℤ} {b : ℤ} (ha : 0 < a) (hb : 0 ≤ b) : 0 < a + b :=
  int.zero_add 0 ▸ int.add_lt_add_of_lt_of_le ha hb

protected theorem add_pos_of_nonneg_of_pos {a : ℤ} {b : ℤ} (ha : 0 ≤ a) (hb : 0 < b) : 0 < a + b :=
  int.zero_add 0 ▸ int.add_lt_add_of_le_of_lt ha hb

protected theorem add_nonpos {a : ℤ} {b : ℤ} (ha : a ≤ 0) (hb : b ≤ 0) : a + b ≤ 0 :=
  int.zero_add 0 ▸ int.add_le_add ha hb

protected theorem add_neg {a : ℤ} {b : ℤ} (ha : a < 0) (hb : b < 0) : a + b < 0 :=
  int.zero_add 0 ▸ int.add_lt_add ha hb

protected theorem add_neg_of_neg_of_nonpos {a : ℤ} {b : ℤ} (ha : a < 0) (hb : b ≤ 0) : a + b < 0 :=
  int.zero_add 0 ▸ int.add_lt_add_of_lt_of_le ha hb

protected theorem add_neg_of_nonpos_of_neg {a : ℤ} {b : ℤ} (ha : a ≤ 0) (hb : b < 0) : a + b < 0 :=
  int.zero_add 0 ▸ int.add_lt_add_of_le_of_lt ha hb

protected theorem lt_add_of_le_of_pos {a : ℤ} {b : ℤ} {c : ℤ} (hbc : b ≤ c) (ha : 0 < a) :
    b < c + a :=
  int.add_zero b ▸ int.add_lt_add_of_le_of_lt hbc ha

protected theorem sub_add_cancel (a : ℤ) (b : ℤ) : a - b + b = a := int.neg_add_cancel_right a b

protected theorem add_sub_cancel (a : ℤ) (b : ℤ) : a + b - b = a := int.add_neg_cancel_right a b

protected theorem add_sub_assoc (a : ℤ) (b : ℤ) (c : ℤ) : a + b - c = a + (b - c) := sorry

protected theorem neg_le_neg {a : ℤ} {b : ℤ} (h : a ≤ b) : -b ≤ -a := sorry

protected theorem le_of_neg_le_neg {a : ℤ} {b : ℤ} (h : -b ≤ -a) : a ≤ b := sorry

protected theorem nonneg_of_neg_nonpos {a : ℤ} (h : -a ≤ 0) : 0 ≤ a :=
  (fun (this : -a ≤ -0) => int.le_of_neg_le_neg this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (-a ≤ -0)) int.neg_zero)) h)

protected theorem neg_nonpos_of_nonneg {a : ℤ} (h : 0 ≤ a) : -a ≤ 0 :=
  (fun (this : -a ≤ -0) => eq.mp (Eq._oldrec (Eq.refl (-a ≤ -0)) int.neg_zero) this)
    (int.neg_le_neg h)

protected theorem nonpos_of_neg_nonneg {a : ℤ} (h : 0 ≤ -a) : a ≤ 0 :=
  (fun (this : -0 ≤ -a) => int.le_of_neg_le_neg this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (-0 ≤ -a)) int.neg_zero)) h)

protected theorem neg_nonneg_of_nonpos {a : ℤ} (h : a ≤ 0) : 0 ≤ -a :=
  (fun (this : -0 ≤ -a) => eq.mp (Eq._oldrec (Eq.refl (-0 ≤ -a)) int.neg_zero) this)
    (int.neg_le_neg h)

protected theorem neg_lt_neg {a : ℤ} {b : ℤ} (h : a < b) : -b < -a := sorry

protected theorem lt_of_neg_lt_neg {a : ℤ} {b : ℤ} (h : -b < -a) : a < b :=
  int.neg_neg a ▸ int.neg_neg b ▸ int.neg_lt_neg h

protected theorem pos_of_neg_neg {a : ℤ} (h : -a < 0) : 0 < a :=
  (fun (this : -a < -0) => int.lt_of_neg_lt_neg this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (-a < -0)) int.neg_zero)) h)

protected theorem neg_neg_of_pos {a : ℤ} (h : 0 < a) : -a < 0 :=
  (fun (this : -a < -0) => eq.mp (Eq._oldrec (Eq.refl (-a < -0)) int.neg_zero) this)
    (int.neg_lt_neg h)

protected theorem neg_of_neg_pos {a : ℤ} (h : 0 < -a) : a < 0 :=
  (fun (this : -0 < -a) => int.lt_of_neg_lt_neg this)
    (eq.mpr (id (Eq._oldrec (Eq.refl (-0 < -a)) int.neg_zero)) h)

protected theorem neg_pos_of_neg {a : ℤ} (h : a < 0) : 0 < -a :=
  (fun (this : -0 < -a) => eq.mp (Eq._oldrec (Eq.refl (-0 < -a)) int.neg_zero) this)
    (int.neg_lt_neg h)

protected theorem le_neg_of_le_neg {a : ℤ} {b : ℤ} (h : a ≤ -b) : b ≤ -a :=
  eq.mp (Eq._oldrec (Eq.refl ( --b ≤ -a)) (int.neg_neg b)) (int.neg_le_neg h)

protected theorem neg_le_of_neg_le {a : ℤ} {b : ℤ} (h : -a ≤ b) : -b ≤ a :=
  eq.mp (Eq._oldrec (Eq.refl (-b ≤ --a)) (int.neg_neg a)) (int.neg_le_neg h)

protected theorem lt_neg_of_lt_neg {a : ℤ} {b : ℤ} (h : a < -b) : b < -a :=
  eq.mp (Eq._oldrec (Eq.refl ( --b < -a)) (int.neg_neg b)) (int.neg_lt_neg h)

protected theorem neg_lt_of_neg_lt {a : ℤ} {b : ℤ} (h : -a < b) : -b < a :=
  eq.mp (Eq._oldrec (Eq.refl (-b < --a)) (int.neg_neg a)) (int.neg_lt_neg h)

protected theorem sub_nonneg_of_le {a : ℤ} {b : ℤ} (h : b ≤ a) : 0 ≤ a - b :=
  eq.mp (Eq._oldrec (Eq.refl (b + -b ≤ a + -b)) (int.add_right_neg b)) (int.add_le_add_right h (-b))

protected theorem le_of_sub_nonneg {a : ℤ} {b : ℤ} (h : 0 ≤ a - b) : b ≤ a :=
  eq.mp (Eq._oldrec (Eq.refl (0 + b ≤ a)) (int.zero_add b))
    (eq.mp (Eq._oldrec (Eq.refl (0 + b ≤ a - b + b)) (int.sub_add_cancel a b))
      (int.add_le_add_right h b))

protected theorem sub_nonpos_of_le {a : ℤ} {b : ℤ} (h : a ≤ b) : a - b ≤ 0 :=
  eq.mp (Eq._oldrec (Eq.refl (a + -b ≤ b + -b)) (int.add_right_neg b)) (int.add_le_add_right h (-b))

protected theorem le_of_sub_nonpos {a : ℤ} {b : ℤ} (h : a - b ≤ 0) : a ≤ b :=
  eq.mp (Eq._oldrec (Eq.refl (a ≤ 0 + b)) (int.zero_add b))
    (eq.mp (Eq._oldrec (Eq.refl (a - b + b ≤ 0 + b)) (int.sub_add_cancel a b))
      (int.add_le_add_right h b))

protected theorem sub_pos_of_lt {a : ℤ} {b : ℤ} (h : b < a) : 0 < a - b :=
  eq.mp (Eq._oldrec (Eq.refl (b + -b < a + -b)) (int.add_right_neg b)) (int.add_lt_add_right h (-b))

protected theorem lt_of_sub_pos {a : ℤ} {b : ℤ} (h : 0 < a - b) : b < a :=
  eq.mp (Eq._oldrec (Eq.refl (0 + b < a)) (int.zero_add b))
    (eq.mp (Eq._oldrec (Eq.refl (0 + b < a - b + b)) (int.sub_add_cancel a b))
      (int.add_lt_add_right h b))

protected theorem sub_neg_of_lt {a : ℤ} {b : ℤ} (h : a < b) : a - b < 0 :=
  eq.mp (Eq._oldrec (Eq.refl (a + -b < b + -b)) (int.add_right_neg b)) (int.add_lt_add_right h (-b))

protected theorem lt_of_sub_neg {a : ℤ} {b : ℤ} (h : a - b < 0) : a < b :=
  eq.mp (Eq._oldrec (Eq.refl (a < 0 + b)) (int.zero_add b))
    (eq.mp (Eq._oldrec (Eq.refl (a - b + b < 0 + b)) (int.sub_add_cancel a b))
      (int.add_lt_add_right h b))

protected theorem add_le_of_le_neg_add {a : ℤ} {b : ℤ} {c : ℤ} (h : b ≤ -a + c) : a + b ≤ c :=
  eq.mp (Eq._oldrec (Eq.refl (a + b ≤ a + (-a + c))) (int.add_neg_cancel_left a c))
    (int.add_le_add_left h a)

protected theorem le_neg_add_of_add_le {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b ≤ c) : b ≤ -a + c :=
  eq.mp (Eq._oldrec (Eq.refl (-a + (a + b) ≤ -a + c)) (int.neg_add_cancel_left a b))
    (int.add_le_add_left h (-a))

protected theorem add_le_of_le_sub_left {a : ℤ} {b : ℤ} {c : ℤ} (h : b ≤ c - a) : a + b ≤ c :=
  eq.mp (Eq._oldrec (Eq.refl (a + b ≤ c + a - a)) (int.add_sub_cancel c a))
    (eq.mp (Eq._oldrec (Eq.refl (a + b ≤ a + c - a)) (int.add_comm a c))
      (eq.mp (Eq._oldrec (Eq.refl (a + b ≤ a + (c - a))) (Eq.symm (int.add_sub_assoc a c a)))
        (int.add_le_add_left h a)))

protected theorem le_sub_left_of_add_le {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b ≤ c) : b ≤ c - a :=
  eq.mp (Eq._oldrec (Eq.refl (b + a + -a ≤ c + -a)) (int.add_neg_cancel_right b a))
    (eq.mp (Eq._oldrec (Eq.refl (a + b + -a ≤ c + -a)) (int.add_comm a b))
      (int.add_le_add_right h (-a)))

protected theorem add_le_of_le_sub_right {a : ℤ} {b : ℤ} {c : ℤ} (h : a ≤ c - b) : a + b ≤ c :=
  eq.mp (Eq._oldrec (Eq.refl (a + b ≤ c - b + b)) (int.sub_add_cancel c b))
    (int.add_le_add_right h b)

protected theorem le_sub_right_of_add_le {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b ≤ c) : a ≤ c - b :=
  eq.mp (Eq._oldrec (Eq.refl (a + b + -b ≤ c + -b)) (int.add_neg_cancel_right a b))
    (int.add_le_add_right h (-b))

protected theorem le_add_of_neg_add_le {a : ℤ} {b : ℤ} {c : ℤ} (h : -b + a ≤ c) : a ≤ b + c :=
  eq.mp (Eq._oldrec (Eq.refl (b + (-b + a) ≤ b + c)) (int.add_neg_cancel_left b a))
    (int.add_le_add_left h b)

protected theorem neg_add_le_of_le_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a ≤ b + c) : -b + a ≤ c :=
  eq.mp (Eq._oldrec (Eq.refl (-b + a ≤ -b + (b + c))) (int.neg_add_cancel_left b c))
    (int.add_le_add_left h (-b))

protected theorem le_add_of_sub_left_le {a : ℤ} {b : ℤ} {c : ℤ} (h : a - b ≤ c) : a ≤ b + c :=
  eq.mp (Eq._oldrec (Eq.refl (a ≤ c + b)) (int.add_comm c b))
    (eq.mp (Eq._oldrec (Eq.refl (a - b + b ≤ c + b)) (int.sub_add_cancel a b))
      (int.add_le_add_right h b))

protected theorem sub_left_le_of_le_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a ≤ b + c) : a - b ≤ c :=
  eq.mp (Eq._oldrec (Eq.refl (a + -b ≤ c + b + -b)) (int.add_neg_cancel_right c b))
    (eq.mp (Eq._oldrec (Eq.refl (a + -b ≤ b + c + -b)) (int.add_comm b c))
      (int.add_le_add_right h (-b)))

protected theorem le_add_of_sub_right_le {a : ℤ} {b : ℤ} {c : ℤ} (h : a - c ≤ b) : a ≤ b + c :=
  eq.mp (Eq._oldrec (Eq.refl (a - c + c ≤ b + c)) (int.sub_add_cancel a c))
    (int.add_le_add_right h c)

protected theorem sub_right_le_of_le_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a ≤ b + c) : a - c ≤ b :=
  eq.mp (Eq._oldrec (Eq.refl (a + -c ≤ b + c + -c)) (int.add_neg_cancel_right b c))
    (int.add_le_add_right h (-c))

protected theorem le_add_of_neg_add_le_left {a : ℤ} {b : ℤ} {c : ℤ} (h : -b + a ≤ c) : a ≤ b + c :=
  int.le_add_of_sub_left_le (eq.mp (Eq._oldrec (Eq.refl (-b + a ≤ c)) (int.add_comm (-b) a)) h)

protected theorem neg_add_le_left_of_le_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a ≤ b + c) : -b + a ≤ c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (-b + a ≤ c)) (int.add_comm (-b) a)))
    (int.sub_left_le_of_le_add h)

protected theorem le_add_of_neg_add_le_right {a : ℤ} {b : ℤ} {c : ℤ} (h : -c + a ≤ b) : a ≤ b + c :=
  int.le_add_of_sub_right_le (eq.mp (Eq._oldrec (Eq.refl (-c + a ≤ b)) (int.add_comm (-c) a)) h)

protected theorem neg_add_le_right_of_le_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a ≤ b + c) : -c + a ≤ b :=
  int.neg_add_le_left_of_le_add (eq.mp (Eq._oldrec (Eq.refl (a ≤ b + c)) (int.add_comm b c)) h)

protected theorem le_add_of_neg_le_sub_left {a : ℤ} {b : ℤ} {c : ℤ} (h : -a ≤ b - c) : c ≤ a + b :=
  int.le_add_of_neg_add_le_left (int.add_le_of_le_sub_right h)

protected theorem neg_le_sub_left_of_le_add {a : ℤ} {b : ℤ} {c : ℤ} (h : c ≤ a + b) : -a ≤ b - c :=
  eq.mp (Eq._oldrec (Eq.refl (-a ≤ -c + b)) (int.add_comm (-c) b))
    (int.le_neg_add_of_add_le (int.sub_left_le_of_le_add h))

protected theorem le_add_of_neg_le_sub_right {a : ℤ} {b : ℤ} {c : ℤ} (h : -b ≤ a - c) : c ≤ a + b :=
  int.le_add_of_sub_right_le (int.add_le_of_le_sub_left h)

protected theorem neg_le_sub_right_of_le_add {a : ℤ} {b : ℤ} {c : ℤ} (h : c ≤ a + b) : -b ≤ a - c :=
  int.le_sub_left_of_add_le (int.sub_right_le_of_le_add h)

protected theorem sub_le_of_sub_le {a : ℤ} {b : ℤ} {c : ℤ} (h : a - b ≤ c) : a - c ≤ b :=
  int.sub_left_le_of_le_add (int.le_add_of_sub_right_le h)

protected theorem sub_le_sub_left {a : ℤ} {b : ℤ} (h : a ≤ b) (c : ℤ) : c - b ≤ c - a :=
  int.add_le_add_left (int.neg_le_neg h) c

protected theorem sub_le_sub_right {a : ℤ} {b : ℤ} (h : a ≤ b) (c : ℤ) : a - c ≤ b - c :=
  int.add_le_add_right h (-c)

protected theorem sub_le_sub {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hab : a ≤ b) (hcd : c ≤ d) :
    a - d ≤ b - c :=
  int.add_le_add hab (int.neg_le_neg hcd)

protected theorem add_lt_of_lt_neg_add {a : ℤ} {b : ℤ} {c : ℤ} (h : b < -a + c) : a + b < c :=
  eq.mp (Eq._oldrec (Eq.refl (a + b < a + (-a + c))) (int.add_neg_cancel_left a c))
    (int.add_lt_add_left h a)

protected theorem lt_neg_add_of_add_lt {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b < c) : b < -a + c :=
  eq.mp (Eq._oldrec (Eq.refl (-a + (a + b) < -a + c)) (int.neg_add_cancel_left a b))
    (int.add_lt_add_left h (-a))

protected theorem add_lt_of_lt_sub_left {a : ℤ} {b : ℤ} {c : ℤ} (h : b < c - a) : a + b < c :=
  eq.mp (Eq._oldrec (Eq.refl (a + b < c + a - a)) (int.add_sub_cancel c a))
    (eq.mp (Eq._oldrec (Eq.refl (a + b < a + c - a)) (int.add_comm a c))
      (eq.mp (Eq._oldrec (Eq.refl (a + b < a + (c - a))) (Eq.symm (int.add_sub_assoc a c a)))
        (int.add_lt_add_left h a)))

protected theorem lt_sub_left_of_add_lt {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b < c) : b < c - a :=
  eq.mp (Eq._oldrec (Eq.refl (b + a + -a < c + -a)) (int.add_neg_cancel_right b a))
    (eq.mp (Eq._oldrec (Eq.refl (a + b + -a < c + -a)) (int.add_comm a b))
      (int.add_lt_add_right h (-a)))

protected theorem add_lt_of_lt_sub_right {a : ℤ} {b : ℤ} {c : ℤ} (h : a < c - b) : a + b < c :=
  eq.mp (Eq._oldrec (Eq.refl (a + b < c - b + b)) (int.sub_add_cancel c b))
    (int.add_lt_add_right h b)

protected theorem lt_sub_right_of_add_lt {a : ℤ} {b : ℤ} {c : ℤ} (h : a + b < c) : a < c - b :=
  eq.mp (Eq._oldrec (Eq.refl (a + b + -b < c + -b)) (int.add_neg_cancel_right a b))
    (int.add_lt_add_right h (-b))

protected theorem lt_add_of_neg_add_lt {a : ℤ} {b : ℤ} {c : ℤ} (h : -b + a < c) : a < b + c :=
  eq.mp (Eq._oldrec (Eq.refl (b + (-b + a) < b + c)) (int.add_neg_cancel_left b a))
    (int.add_lt_add_left h b)

protected theorem neg_add_lt_of_lt_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a < b + c) : -b + a < c :=
  eq.mp (Eq._oldrec (Eq.refl (-b + a < -b + (b + c))) (int.neg_add_cancel_left b c))
    (int.add_lt_add_left h (-b))

protected theorem lt_add_of_sub_left_lt {a : ℤ} {b : ℤ} {c : ℤ} (h : a - b < c) : a < b + c :=
  eq.mp (Eq._oldrec (Eq.refl (a < c + b)) (int.add_comm c b))
    (eq.mp (Eq._oldrec (Eq.refl (a - b + b < c + b)) (int.sub_add_cancel a b))
      (int.add_lt_add_right h b))

protected theorem sub_left_lt_of_lt_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a < b + c) : a - b < c :=
  eq.mp (Eq._oldrec (Eq.refl (a + -b < c + b + -b)) (int.add_neg_cancel_right c b))
    (eq.mp (Eq._oldrec (Eq.refl (a + -b < b + c + -b)) (int.add_comm b c))
      (int.add_lt_add_right h (-b)))

protected theorem lt_add_of_sub_right_lt {a : ℤ} {b : ℤ} {c : ℤ} (h : a - c < b) : a < b + c :=
  eq.mp (Eq._oldrec (Eq.refl (a - c + c < b + c)) (int.sub_add_cancel a c))
    (int.add_lt_add_right h c)

protected theorem sub_right_lt_of_lt_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a < b + c) : a - c < b :=
  eq.mp (Eq._oldrec (Eq.refl (a + -c < b + c + -c)) (int.add_neg_cancel_right b c))
    (int.add_lt_add_right h (-c))

protected theorem lt_add_of_neg_add_lt_left {a : ℤ} {b : ℤ} {c : ℤ} (h : -b + a < c) : a < b + c :=
  int.lt_add_of_sub_left_lt (eq.mp (Eq._oldrec (Eq.refl (-b + a < c)) (int.add_comm (-b) a)) h)

protected theorem neg_add_lt_left_of_lt_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a < b + c) : -b + a < c :=
  eq.mpr (id (Eq._oldrec (Eq.refl (-b + a < c)) (int.add_comm (-b) a)))
    (int.sub_left_lt_of_lt_add h)

protected theorem lt_add_of_neg_add_lt_right {a : ℤ} {b : ℤ} {c : ℤ} (h : -c + a < b) : a < b + c :=
  int.lt_add_of_sub_right_lt (eq.mp (Eq._oldrec (Eq.refl (-c + a < b)) (int.add_comm (-c) a)) h)

protected theorem neg_add_lt_right_of_lt_add {a : ℤ} {b : ℤ} {c : ℤ} (h : a < b + c) : -c + a < b :=
  int.neg_add_lt_left_of_lt_add (eq.mp (Eq._oldrec (Eq.refl (a < b + c)) (int.add_comm b c)) h)

protected theorem lt_add_of_neg_lt_sub_left {a : ℤ} {b : ℤ} {c : ℤ} (h : -a < b - c) : c < a + b :=
  int.lt_add_of_neg_add_lt_left (int.add_lt_of_lt_sub_right h)

protected theorem neg_lt_sub_left_of_lt_add {a : ℤ} {b : ℤ} {c : ℤ} (h : c < a + b) : -a < b - c :=
  eq.mp (Eq._oldrec (Eq.refl (-a < -c + b)) (int.add_comm (-c) b))
    (int.lt_neg_add_of_add_lt (int.sub_left_lt_of_lt_add h))

protected theorem lt_add_of_neg_lt_sub_right {a : ℤ} {b : ℤ} {c : ℤ} (h : -b < a - c) : c < a + b :=
  int.lt_add_of_sub_right_lt (int.add_lt_of_lt_sub_left h)

protected theorem neg_lt_sub_right_of_lt_add {a : ℤ} {b : ℤ} {c : ℤ} (h : c < a + b) : -b < a - c :=
  int.lt_sub_left_of_add_lt (int.sub_right_lt_of_lt_add h)

protected theorem sub_lt_of_sub_lt {a : ℤ} {b : ℤ} {c : ℤ} (h : a - b < c) : a - c < b :=
  int.sub_left_lt_of_lt_add (int.lt_add_of_sub_right_lt h)

protected theorem sub_lt_sub_left {a : ℤ} {b : ℤ} (h : a < b) (c : ℤ) : c - b < c - a :=
  int.add_lt_add_left (int.neg_lt_neg h) c

protected theorem sub_lt_sub_right {a : ℤ} {b : ℤ} (h : a < b) (c : ℤ) : a - c < b - c :=
  int.add_lt_add_right h (-c)

protected theorem sub_lt_sub {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hab : a < b) (hcd : c < d) :
    a - d < b - c :=
  int.add_lt_add hab (int.neg_lt_neg hcd)

protected theorem sub_lt_sub_of_le_of_lt {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hab : a ≤ b)
    (hcd : c < d) : a - d < b - c :=
  int.add_lt_add_of_le_of_lt hab (int.neg_lt_neg hcd)

protected theorem sub_lt_sub_of_lt_of_le {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hab : a < b)
    (hcd : c ≤ d) : a - d < b - c :=
  int.add_lt_add_of_lt_of_le hab (int.neg_le_neg hcd)

protected theorem sub_le_self (a : ℤ) {b : ℤ} (h : b ≥ 0) : a - b ≤ a :=
  trans_rel_left LessEq
    (trans_rel_right LessEq rfl (int.add_le_add_left (int.neg_nonpos_of_nonneg h) a))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + 0 = a)) (int.add_zero a))) (Eq.refl a))

protected theorem sub_lt_self (a : ℤ) {b : ℤ} (h : b > 0) : a - b < a :=
  trans_rel_left Less (trans_rel_right Less rfl (int.add_lt_add_left (int.neg_neg_of_pos h) a))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a + 0 = a)) (int.add_zero a))) (Eq.refl a))

protected theorem add_le_add_three {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} {e : ℤ} {f : ℤ} (h₁ : a ≤ d)
    (h₂ : b ≤ e) (h₃ : c ≤ f) : a + b + c ≤ d + e + f :=
  le_trans (int.add_le_add (int.add_le_add h₁ h₂) h₃) (le_refl (d + e + f))

/- missing facts -/

protected theorem mul_lt_mul_of_pos_left {a : ℤ} {b : ℤ} {c : ℤ} (h₁ : a < b) (h₂ : 0 < c) :
    c * a < c * b :=
  sorry

protected theorem mul_lt_mul_of_pos_right {a : ℤ} {b : ℤ} {c : ℤ} (h₁ : a < b) (h₂ : 0 < c) :
    a * c < b * c :=
  sorry

protected theorem mul_le_mul_of_nonneg_left {a : ℤ} {b : ℤ} {c : ℤ} (h₁ : a ≤ b) (h₂ : 0 ≤ c) :
    c * a ≤ c * b :=
  sorry

protected theorem mul_le_mul_of_nonneg_right {a : ℤ} {b : ℤ} {c : ℤ} (h₁ : a ≤ b) (h₂ : 0 ≤ c) :
    a * c ≤ b * c :=
  sorry

-- TODO: there are four variations, depending on which variables we assume to be nonneg

protected theorem mul_le_mul {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hac : a ≤ c) (hbd : b ≤ d)
    (nn_b : 0 ≤ b) (nn_c : 0 ≤ c) : a * b ≤ c * d :=
  le_trans (int.mul_le_mul_of_nonneg_right hac nn_b) (int.mul_le_mul_of_nonneg_left hbd nn_c)

protected theorem mul_nonpos_of_nonneg_of_nonpos {a : ℤ} {b : ℤ} (ha : a ≥ 0) (hb : b ≤ 0) :
    a * b ≤ 0 :=
  (fun (h : a * b ≤ a * 0) => eq.mp (Eq._oldrec (Eq.refl (a * b ≤ a * 0)) (int.mul_zero a)) h)
    (int.mul_le_mul_of_nonneg_left hb ha)

protected theorem mul_nonpos_of_nonpos_of_nonneg {a : ℤ} {b : ℤ} (ha : a ≤ 0) (hb : b ≥ 0) :
    a * b ≤ 0 :=
  (fun (h : a * b ≤ 0 * b) => eq.mp (Eq._oldrec (Eq.refl (a * b ≤ 0 * b)) (int.zero_mul b)) h)
    (int.mul_le_mul_of_nonneg_right ha hb)

protected theorem mul_lt_mul {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (hac : a < c) (hbd : b ≤ d)
    (pos_b : 0 < b) (nn_c : 0 ≤ c) : a * b < c * d :=
  lt_of_lt_of_le (int.mul_lt_mul_of_pos_right hac pos_b) (int.mul_le_mul_of_nonneg_left hbd nn_c)

protected theorem mul_lt_mul' {a : ℤ} {b : ℤ} {c : ℤ} {d : ℤ} (h1 : a ≤ c) (h2 : b < d) (h3 : b ≥ 0)
    (h4 : c > 0) : a * b < c * d :=
  lt_of_le_of_lt (int.mul_le_mul_of_nonneg_right h1 h3) (int.mul_lt_mul_of_pos_left h2 h4)

protected theorem mul_neg_of_pos_of_neg {a : ℤ} {b : ℤ} (ha : a > 0) (hb : b < 0) : a * b < 0 :=
  (fun (h : a * b < a * 0) => eq.mp (Eq._oldrec (Eq.refl (a * b < a * 0)) (int.mul_zero a)) h)
    (int.mul_lt_mul_of_pos_left hb ha)

protected theorem mul_neg_of_neg_of_pos {a : ℤ} {b : ℤ} (ha : a < 0) (hb : b > 0) : a * b < 0 :=
  (fun (h : a * b < 0 * b) => eq.mp (Eq._oldrec (Eq.refl (a * b < 0 * b)) (int.zero_mul b)) h)
    (int.mul_lt_mul_of_pos_right ha hb)

protected theorem mul_le_mul_of_nonpos_right {a : ℤ} {b : ℤ} {c : ℤ} (h : b ≤ a) (hc : c ≤ 0) :
    a * c ≤ b * c :=
  sorry

protected theorem mul_nonneg_of_nonpos_of_nonpos {a : ℤ} {b : ℤ} (ha : a ≤ 0) (hb : b ≤ 0) :
    0 ≤ a * b :=
  (fun (this : 0 * b ≤ a * b) => eq.mp (Eq._oldrec (Eq.refl (0 * b ≤ a * b)) (int.zero_mul b)) this)
    (int.mul_le_mul_of_nonpos_right ha hb)

protected theorem mul_lt_mul_of_neg_left {a : ℤ} {b : ℤ} {c : ℤ} (h : b < a) (hc : c < 0) :
    c * a < c * b :=
  sorry

protected theorem mul_lt_mul_of_neg_right {a : ℤ} {b : ℤ} {c : ℤ} (h : b < a) (hc : c < 0) :
    a * c < b * c :=
  sorry

protected theorem mul_pos_of_neg_of_neg {a : ℤ} {b : ℤ} (ha : a < 0) (hb : b < 0) : 0 < a * b :=
  (fun (this : 0 * b < a * b) => eq.mp (Eq._oldrec (Eq.refl (0 * b < a * b)) (int.zero_mul b)) this)
    (int.mul_lt_mul_of_neg_right ha hb)

protected theorem mul_self_le_mul_self {a : ℤ} {b : ℤ} (h1 : 0 ≤ a) (h2 : a ≤ b) : a * a ≤ b * b :=
  int.mul_le_mul h2 h2 h1 (le_trans h1 h2)

protected theorem mul_self_lt_mul_self {a : ℤ} {b : ℤ} (h1 : 0 ≤ a) (h2 : a < b) : a * a < b * b :=
  int.mul_lt_mul' (le_of_lt h2) h2 h1 (lt_of_le_of_lt h1 h2)

/- more facts specific to int -/

theorem of_nat_nonneg (n : ℕ) : 0 ≤ Int.ofNat n := trivial

theorem coe_succ_pos (n : ℕ) : ↑(Nat.succ n) > 0 := coe_nat_lt_coe_nat_of_lt (nat.succ_pos n)

theorem exists_eq_neg_of_nat {a : ℤ} (H : a ≤ 0) : ∃ (n : ℕ), a = -↑n := sorry

theorem nat_abs_of_nonneg {a : ℤ} (H : a ≥ 0) : ↑(nat_abs a) = a := sorry

theorem of_nat_nat_abs_of_nonpos {a : ℤ} (H : a ≤ 0) : ↑(nat_abs a) = -a :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(nat_abs a) = -a)) (Eq.symm (nat_abs_neg a))))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (↑(nat_abs (-a)) = -a))
          (nat_abs_of_nonneg (int.neg_nonneg_of_nonpos H))))
      (Eq.refl (-a)))

theorem lt_of_add_one_le {a : ℤ} {b : ℤ} (H : a + 1 ≤ b) : a < b := H

theorem add_one_le_of_lt {a : ℤ} {b : ℤ} (H : a < b) : a + 1 ≤ b := H

theorem lt_add_one_of_le {a : ℤ} {b : ℤ} (H : a ≤ b) : a < b + 1 := int.add_le_add_right H 1

theorem le_of_lt_add_one {a : ℤ} {b : ℤ} (H : a < b + 1) : a ≤ b := int.le_of_add_le_add_right H

theorem sub_one_le_of_lt {a : ℤ} {b : ℤ} (H : a ≤ b) : a - 1 < b :=
  int.sub_right_lt_of_lt_add (lt_add_one_of_le H)

theorem lt_of_sub_one_le {a : ℤ} {b : ℤ} (H : a - 1 < b) : a ≤ b :=
  le_of_lt_add_one (int.lt_add_of_sub_right_lt H)

theorem le_sub_one_of_lt {a : ℤ} {b : ℤ} (H : a < b) : a ≤ b - 1 := int.le_sub_right_of_add_le H

theorem lt_of_le_sub_one {a : ℤ} {b : ℤ} (H : a ≤ b - 1) : a < b := int.add_le_of_le_sub_right H

theorem sign_of_succ (n : ℕ) : sign ↑(Nat.succ n) = 1 := rfl

theorem sign_eq_one_of_pos {a : ℤ} (h : 0 < a) : sign a = 1 := sorry

theorem sign_eq_neg_one_of_neg {a : ℤ} (h : a < 0) : sign a = -1 := sorry

theorem eq_zero_of_sign_eq_zero {a : ℤ} : sign a = 0 → a = 0 := sorry

theorem pos_of_sign_eq_one {a : ℤ} : sign a = 1 → 0 < a := sorry

theorem neg_of_sign_eq_neg_one {a : ℤ} : sign a = -1 → a < 0 := sorry

theorem sign_eq_one_iff_pos (a : ℤ) : sign a = 1 ↔ 0 < a :=
  { mp := pos_of_sign_eq_one, mpr := sign_eq_one_of_pos }

theorem sign_eq_neg_one_iff_neg (a : ℤ) : sign a = -1 ↔ a < 0 :=
  { mp := neg_of_sign_eq_neg_one, mpr := sign_eq_neg_one_of_neg }

theorem sign_eq_zero_iff_zero (a : ℤ) : sign a = 0 ↔ a = 0 := sorry

protected theorem eq_zero_or_eq_zero_of_mul_eq_zero {a : ℤ} {b : ℤ} (h : a * b = 0) :
    a = 0 ∨ b = 0 :=
  sorry

protected theorem eq_of_mul_eq_mul_right {a : ℤ} {b : ℤ} {c : ℤ} (ha : a ≠ 0) (h : b * a = c * a) :
    b = c :=
  sorry

protected theorem eq_of_mul_eq_mul_left {a : ℤ} {b : ℤ} {c : ℤ} (ha : a ≠ 0) (h : a * b = a * c) :
    b = c :=
  sorry

theorem eq_one_of_mul_eq_self_left {a : ℤ} {b : ℤ} (Hpos : a ≠ 0) (H : b * a = a) : b = 1 :=
  int.eq_of_mul_eq_mul_right Hpos
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a = 1 * a)) (int.one_mul a)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (b * a = a)) H)) (Eq.refl a)))

theorem eq_one_of_mul_eq_self_right {a : ℤ} {b : ℤ} (Hpos : b ≠ 0) (H : b * a = b) : a = 1 :=
  int.eq_of_mul_eq_mul_left Hpos
    (eq.mpr (id (Eq._oldrec (Eq.refl (b * a = b * 1)) (int.mul_one b)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (b * a = b)) H)) (Eq.refl b)))

end Mathlib


import algebra.ring.basic
import data.real.basic
import data.real.sqrt
import algebra.field
import data.int.cast

def mk_ring_sqrt_d (d : ℤ) : set ℝ  := {z | ∃ a b : ℤ , z= (int.cast a) + (int.cast b)*real.sqrt d}
theorem cpge_anneaux_3 {A : Type*} [ring A] (a b : A): ∀ (d:ℤ ), is_subring (mk_ring_sqrt_d d) := sorry


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

! This file was ported from Lean 3 source module representation_theory.Rep
! leanprover-community/mathlib commit c04bc6e93e23aa0182aba53661a2211e80b6feac
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.RepresentationTheory.Basic
import Mathbin.RepresentationTheory.Action
import Mathbin.Algebra.Category.Module.Abelian
import Mathbin.Algebra.Category.Module.Colimits
import Mathbin.Algebra.Category.Module.Monoidal
import Mathbin.Algebra.Category.Module.Adjunctions
import Mathbin.CategoryTheory.Closed.FunctorCategory

/-!
# `Rep k G` is the category of `k`-linear representations of `G`.

If `V : Rep k G`, there is a coercion that allows you to treat `V` as a type,
and this type comes equipped with a `module k V` instance.
Also `V.ρ` gives the homomorphism `G →* (V →ₗ[k] V)`.

Conversely, given a homomorphism `ρ : G →* (V →ₗ[k] V)`,
you can construct the bundled representation as `Rep.of ρ`.

We construct the categorical equivalence `Rep k G ≌ Module (monoid_algebra k G)`.
We verify that `Rep k G` is a `k`-linear abelian symmetric monoidal category with all (co)limits.
-/

universe u

open CategoryTheory

open CategoryTheory.Limits

/- ./././Mathport/Syntax/Translate/Command.lean:323:31: unsupported: @[derive] abbrev -/
/-- The category of `k`-linear representations of a monoid `G`. -/
abbrev Rep (k G : Type u) [Ring k] [Monoid G] :=
  Action (ModuleCat.{u} k) (MonCat.of G)
#align Rep Rep

instance (k G : Type u) [CommRing k] [Monoid G] : Linear k (Rep k G) := by infer_instance

namespace Rep

variable {k G : Type u} [CommRing k]

section

variable [Monoid G]

instance : CoeSort (Rep k G) (Type u) :=
  ConcreteCategory.hasCoeToSort _

instance (V : Rep k G) : AddCommGroup V :=
  by
  change AddCommGroup ((forget₂ (Rep k G) (ModuleCat k)).obj V)
  infer_instance

instance (V : Rep k G) : Module k V :=
  by
  change Module k ((forget₂ (Rep k G) (ModuleCat k)).obj V)
  infer_instance

/-- Specialize the existing `Action.ρ`, changing the type to `representation k G V`.
-/
def ρ (V : Rep k G) : Representation k G V :=
  V.ρ
#align Rep.ρ Rep.ρ

/-- Lift an unbundled representation to `Rep`. -/
def of {V : Type u} [AddCommGroup V] [Module k V] (ρ : G →* V →ₗ[k] V) : Rep k G :=
  ⟨ModuleCat.of k V, ρ⟩
#align Rep.of Rep.of

@[simp]
theorem coe_of {V : Type u} [AddCommGroup V] [Module k V] (ρ : G →* V →ₗ[k] V) :
    (of ρ : Type u) = V :=
  rfl
#align Rep.coe_of Rep.coe_of

@[simp]
theorem of_ρ {V : Type u} [AddCommGroup V] [Module k V] (ρ : G →* V →ₗ[k] V) : (of ρ).ρ = ρ :=
  rfl
#align Rep.of_ρ Rep.of_ρ

@[simp]
theorem Action_ρ_eq_ρ {A : Rep k G} : Action.ρ A = A.ρ :=
  rfl
#align Rep.Action_ρ_eq_ρ Rep.Action_ρ_eq_ρ

/-- Allows us to apply lemmas about the underlying `ρ`, which would take an element `g : G` rather
than `g : Mon.of G` as an argument. -/
theorem of_ρ_apply {V : Type u} [AddCommGroup V] [Module k V] (ρ : Representation k G V)
    (g : MonCat.of G) : (Rep.of ρ).ρ g = ρ (g : G) :=
  rfl
#align Rep.of_ρ_apply Rep.of_ρ_apply

variable (k G)

/-- The trivial `k`-linear `G`-representation on a `k`-module `V.` -/
def trivial (V : Type u) [AddCommGroup V] [Module k V] : Rep k G :=
  Rep.of (@Representation.trivial k G V _ _ _ _)
#align Rep.trivial Rep.trivial

variable {k G}

theorem trivial_def {V : Type u} [AddCommGroup V] [Module k V] (g : G) (v : V) :
    (trivial k G V).ρ g v = v :=
  rfl
#align Rep.trivial_def Rep.trivial_def

-- Verify that limits are calculated correctly.
noncomputable example : PreservesLimits (forget₂ (Rep k G) (ModuleCat.{u} k)) := by infer_instance

noncomputable example : PreservesColimits (forget₂ (Rep k G) (ModuleCat.{u} k)) := by infer_instance

@[simp]
theorem MonoidalCategory.braiding_hom_apply {A B : Rep k G} (x : A) (y : B) :
    Action.Hom.hom (β_ A B).hom (TensorProduct.tmul k x y) = TensorProduct.tmul k y x :=
  rfl
#align Rep.monoidal_category.braiding_hom_apply Rep.MonoidalCategory.braiding_hom_apply

@[simp]
theorem MonoidalCategory.braiding_inv_apply {A B : Rep k G} (x : A) (y : B) :
    Action.Hom.hom (β_ A B).inv (TensorProduct.tmul k y x) = TensorProduct.tmul k x y :=
  rfl
#align Rep.monoidal_category.braiding_inv_apply Rep.MonoidalCategory.braiding_inv_apply

section Linearization

variable (k G)

/-- The monoidal functor sending a type `H` with a `G`-action to the induced `k`-linear
`G`-representation on `k[H].` -/
noncomputable def linearization : MonoidalFunctor (Action (Type u) (MonCat.of G)) (Rep k G) :=
  (ModuleCat.monoidalFree k).mapAction (MonCat.of G)
#align Rep.linearization Rep.linearization

variable {k G}

@[simp]
theorem linearization_obj_ρ (X : Action (Type u) (MonCat.of G)) (g : G) (x : X.V →₀ k) :
    ((linearization k G).obj X).ρ g x = Finsupp.lmapDomain k k (X.ρ g) x :=
  rfl
#align Rep.linearization_obj_ρ Rep.linearization_obj_ρ

@[simp]
theorem linearization_of (X : Action (Type u) (MonCat.of G)) (g : G) (x : X.V) :
    ((linearization k G).obj X).ρ g (Finsupp.single x (1 : k)) = Finsupp.single (X.ρ g x) (1 : k) :=
  by rw [linearization_obj_ρ, Finsupp.lmapDomain_apply, Finsupp.mapDomain_single]
#align Rep.linearization_of Rep.linearization_of

variable {X Y : Action (Type u) (MonCat.of G)} (f : X ⟶ Y)

@[simp]
theorem linearization_map_hom : ((linearization k G).map f).hom = Finsupp.lmapDomain k k f.hom :=
  rfl
#align Rep.linearization_map_hom Rep.linearization_map_hom

theorem linearization_map_hom_single (x : X.V) (r : k) :
    ((linearization k G).map f).hom (Finsupp.single x r) = Finsupp.single (f.hom x) r := by
  rw [linearization_map_hom, Finsupp.lmapDomain_apply, Finsupp.mapDomain_single]
#align Rep.linearization_map_hom_single Rep.linearization_map_hom_single

@[simp]
theorem linearization_μ_hom (X Y : Action (Type u) (MonCat.of G)) :
    ((linearization k G).μ X Y).hom = (finsuppTensorFinsupp' k X.V Y.V).toLinearMap :=
  rfl
#align Rep.linearization_μ_hom Rep.linearization_μ_hom

@[simp]
theorem linearization_μ_inv_hom (X Y : Action (Type u) (MonCat.of G)) :
    (inv ((linearization k G).μ X Y)).hom = (finsuppTensorFinsupp' k X.V Y.V).symm.toLinearMap :=
  by
  simp_rw [← Action.forget_map, functor.map_inv, Action.forget_map, linearization_μ_hom]
  apply is_iso.inv_eq_of_hom_inv_id _
  exact LinearMap.ext fun x => LinearEquiv.symm_apply_apply _ _
#align Rep.linearization_μ_inv_hom Rep.linearization_μ_inv_hom

@[simp]
theorem linearization_ε_hom : (linearization k G).ε.hom = Finsupp.lsingle PUnit.unit :=
  rfl
#align Rep.linearization_ε_hom Rep.linearization_ε_hom

@[simp]
theorem linearization_ε_inv_hom_apply (r : k) :
    (inv (linearization k G).ε).hom (Finsupp.single PUnit.unit r) = r :=
  by
  simp_rw [← Action.forget_map, functor.map_inv, Action.forget_map]
  rw [← Finsupp.lsingle_apply PUnit.unit r]
  apply is_iso.hom_inv_id_apply _ _
#align Rep.linearization_ε_inv_hom_apply Rep.linearization_ε_inv_hom_apply

variable (k G)

/-- The linearization of a type `X` on which `G` acts trivially is the trivial `G`-representation
on `k[X]`. -/
@[simps]
noncomputable def linearizationTrivialIso (X : Type u) :
    (linearization k G).obj (Action.mk X 1) ≅ trivial k G (X →₀ k) :=
  Action.mkIso (Iso.refl _) fun g => by
    ext1
    ext1
    exact linearization_of _ _ _
#align Rep.linearization_trivial_iso Rep.linearizationTrivialIso

variable (k G)

/-- Given a `G`-action on `H`, this is `k[H]` bundled with the natural representation
`G →* End(k[H])` as a term of type `Rep k G`. -/
noncomputable abbrev ofMulAction (H : Type u) [MulAction G H] : Rep k G :=
  of <| Representation.ofMulAction k G H
#align Rep.of_mul_action Rep.ofMulAction

/-- The `k`-linear `G`-representation on `k[G]`, induced by left multiplication. -/
noncomputable def leftRegular : Rep k G :=
  ofMulAction k G G
#align Rep.left_regular Rep.leftRegular

/-- The `k`-linear `G`-representation on `k[Gⁿ]`, induced by left multiplication. -/
noncomputable def diagonal (n : ℕ) : Rep k G :=
  ofMulAction k G (Fin n → G)
#align Rep.diagonal Rep.diagonal

/-- The linearization of a type `H` with a `G`-action is definitionally isomorphic to the
`k`-linear `G`-representation on `k[H]` induced by the `G`-action on `H`. -/
noncomputable def linearizationOfMulActionIso (H : Type u) [MulAction G H] :
    (linearization k G).obj (Action.ofMulAction G H) ≅ ofMulAction k G H :=
  Iso.refl _
#align Rep.linearization_of_mul_action_iso Rep.linearizationOfMulActionIso

variable {k G}

/-- Given an element `x : A`, there is a natural morphism of representations `k[G] ⟶ A` sending
`g ↦ A.ρ(g)(x).` -/
@[simps]
noncomputable def leftRegularHom (A : Rep k G) (x : A) : Rep.ofMulAction k G G ⟶ A
    where
  hom := Finsupp.lift _ _ _ fun g => A.ρ g x
  comm' g := by
    refine' Finsupp.lhom_ext' fun y => LinearMap.ext_ring _
    simpa only [LinearMap.comp_apply, ModuleCat.comp_def, Finsupp.lsingle_apply, Finsupp.lift_apply,
      Action_ρ_eq_ρ, of_ρ_apply, Representation.ofMulAction_single, Finsupp.sum_single_index,
      zero_smul, one_smul, smul_eq_mul, A.ρ.map_mul]
#align Rep.left_regular_hom Rep.leftRegularHom

theorem leftRegularHom_apply {A : Rep k G} (x : A) :
    (leftRegularHom A x).hom (Finsupp.single 1 1) = x := by
  simpa only [left_regular_hom_hom, Finsupp.lift_apply, Finsupp.sum_single_index, one_smul,
    A.ρ.map_one, zero_smul]
#align Rep.left_regular_hom_apply Rep.leftRegularHom_apply

/-- Given a `k`-linear `G`-representation `A`, there is a `k`-linear isomorphism between
representation morphisms `Hom(k[G], A)` and `A`. -/
@[simps]
noncomputable def leftRegularHomEquiv (A : Rep k G) : (Rep.ofMulAction k G G ⟶ A) ≃ₗ[k] A
    where
  toFun f := f.hom (Finsupp.single 1 1)
  map_add' x y := rfl
  map_smul' r x := rfl
  invFun x := leftRegularHom A x
  left_inv f :=
    by
    refine' Action.Hom.ext _ _ (Finsupp.lhom_ext' fun x : G => LinearMap.ext_ring _)
    have :
      f.hom ((of_mul_action k G G).ρ x (Finsupp.single (1 : G) (1 : k))) =
        A.ρ x (f.hom (Finsupp.single (1 : G) (1 : k))) :=
      LinearMap.ext_iff.1 (f.comm x) (Finsupp.single 1 1)
    simp only [LinearMap.comp_apply, Finsupp.lsingle_apply, left_regular_hom_hom,
      Finsupp.lift_apply, Finsupp.sum_single_index, one_smul, ← this, zero_smul, of_ρ_apply,
      Representation.ofMulAction_single x (1 : G) (1 : k), smul_eq_mul, mul_one]
  right_inv x := leftRegularHom_apply x
#align Rep.left_regular_hom_equiv Rep.leftRegularHomEquiv

theorem leftRegularHomEquiv_symm_single {A : Rep k G} (x : A) (g : G) :
    ((leftRegularHomEquiv A).symm x).hom (Finsupp.single g 1) = A.ρ g x := by
  simp only [left_regular_hom_equiv_symm_apply, left_regular_hom_hom, Finsupp.lift_apply,
    Finsupp.sum_single_index, zero_smul, one_smul]
#align Rep.left_regular_hom_equiv_symm_single Rep.leftRegularHomEquiv_symm_single

end Linearization

end

section

open Action

variable [Group G] (A B C : Rep k G)

noncomputable instance : MonoidalClosed (Rep k G) :=
  MonoidalClosed.ofEquiv (functorCategoryMonoidalEquivalence _ _)

/-- Explicit description of the 'internal Hom' `iHom(A, B)` of two representations `A, B`:
this is `F⁻¹(iHom(F(A), F(B)))`, where `F` is an equivalence
`Rep k G ≌ (single_obj G ⥤ Module k)`. Just used to prove `Rep.ihom_obj_ρ`. -/
theorem ihom_obj_ρ_def :
    ((ihom A).obj B).ρ =
      (FunctorCategoryEquivalence.inverse.obj
          ((FunctorCategoryEquivalence.functor.obj A).closedIhom.obj
            (FunctorCategoryEquivalence.functor.obj B))).ρ :=
  rfl
#align Rep.ihom_obj_ρ_def Rep.ihom_obj_ρ_def

/-- Given `k`-linear `G`-representations `(A, ρ₁), (B, ρ₂)`, the 'internal Hom' is the
representation on `Homₖ(A, B)` sending `g : G` and `f : A →ₗ[k] B` to `(ρ₂ g) ∘ₗ f ∘ₗ (ρ₁ g⁻¹)`. -/
@[simp]
theorem ihom_obj_ρ : ((ihom A).obj B).ρ = A.ρ.linHom B.ρ :=
  by
  refine' MonoidHom.ext fun g => _
  simpa only [ihom_obj_ρ_def, functor_category_equivalence.inverse_obj_ρ_apply,
    functor.closed_ihom_obj_map, ← functor.map_inv, single_obj.inv_as_inv]
#align Rep.ihom_obj_ρ Rep.ihom_obj_ρ

@[simp]
theorem ihom_map_hom {B C : Rep k G} (f : B ⟶ C) :
    ((ihom A).map f).hom = LinearMap.llcomp k A B C f.hom :=
  rfl
#align Rep.ihom_map_hom Rep.ihom_map_hom

/-- Unfolds the unit in the adjunction `A ⊗ - ⊣ iHom(A, -)`; just used to prove
`Rep.ihom_coev_app_hom`. -/
theorem ihom_coev_app_def :
    (ihom.coev A).app B =
      FunctorCategoryEquivalence.unitIso.hom.app B ≫
        FunctorCategoryEquivalence.inverse.map
          ((FunctorCategoryEquivalence.functor.obj A).closedUnit.app _ ≫
            (FunctorCategoryEquivalence.functor.obj A).closedIhom.map
              ((functorCategoryMonoidalEquivalence (ModuleCat.{u} k) (MonCat.of G)).μ A B)) :=
  rfl
#align Rep.ihom_coev_app_def Rep.ihom_coev_app_def

/-- Describes the unit in the adjunction `A ⊗ - ⊣ iHom(A, -)`; given another `k`-linear
`G`-representation `B,` the `k`-linear map underlying the resulting map `B ⟶ iHom(A, A ⊗ B)` is
given by flipping the arguments in the natural `k`-bilinear map `A →ₗ[k] B →ₗ[k] A ⊗ B`. -/
@[simp]
theorem ihom_coev_app_hom : Action.Hom.hom ((ihom.coev A).app B) = (TensorProduct.mk _ _ _).flip :=
  by
  refine' LinearMap.ext fun x => LinearMap.ext fun y => _
  simpa only [ihom_coev_app_def, functor.map_comp, comp_hom,
    functor_category_equivalence.inverse_map_hom, functor.closed_ihom_map_app,
    functor_category_monoidal_equivalence.μ_app]
#align Rep.ihom_coev_app_hom Rep.ihom_coev_app_hom

variable {A B C}

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- Given a `k`-linear `G`-representation `A`, the adjunction `A ⊗ - ⊣ iHom(A, -)` defines a
bijection `Hom(A ⊗ B, C) ≃ Hom(B, iHom(A, C))` for all `B, C`. Given `f : A ⊗ B ⟶ C`, this lemma
describes the `k`-linear map underlying `f`'s image under the bijection. It is given by currying the
`k`-linear map underlying `f`, giving a map `A →ₗ[k] B →ₗ[k] C`, then flipping the arguments. -/
@[simp]
theorem monoidalClosed_curry_hom (f : A ⊗ B ⟶ C) :
    (MonoidalClosed.curry f).hom = (TensorProduct.curry f.hom).flip :=
  by
  rw [monoidal_closed.curry_eq, comp_hom, ihom_coev_app_hom]
  rfl
#align Rep.monoidal_closed_curry_hom Rep.monoidalClosed_curry_hom

/-- Given a `k`-linear `G`-representation `A`, the adjunction `A ⊗ - ⊣ iHom(A, -)` defines a
bijection `Hom(A ⊗ B, C) ≃ Hom(B, iHom(A, C))` for all `B, C`. Given `f : B ⟶ iHom(A, C)`, this
lemma describes the `k`-linear map underlying `f`'s image under the bijection. It is given by
flipping the arguments of the `k`-linear map underlying `f`, giving a map `A →ₗ[k] B →ₗ[k] C`, then
uncurrying. -/
@[simp]
theorem monoidalClosed_uncurry_hom (f : B ⟶ (ihom A).obj C) :
    (MonoidalClosed.uncurry f).hom = TensorProduct.uncurry _ _ _ _ f.hom.flip :=
  by
  simp only [monoidal_closed.of_equiv_uncurry_def, comp_inv_iso_inv_app,
    monoidal_functor.comm_tensor_left_inv_app, comp_hom,
    functor_category_monoidal_equivalence.inverse_map, functor_category_equivalence.inverse_map_hom,
    functor_category_monoidal_equivalence.μ_iso_inv_app]
  ext
  rfl
#align Rep.monoidal_closed_uncurry_hom Rep.monoidalClosed_uncurry_hom

/-- Describes the counit in the adjunction `A ⊗ - ⊣ iHom(A, -)`; given another `k`-linear
`G`-representation `B,` the `k`-linear map underlying the resulting morphism `A ⊗ iHom(A, B) ⟶ B`
is given by uncurrying the map `A →ₗ[k] (A →ₗ[k] B) →ₗ[k] B` defined by flipping the arguments in
the identity map on `Homₖ(A, B).` -/
@[simp]
theorem ihom_ev_app_hom :
    Action.Hom.hom ((ihom.ev A).app B) = TensorProduct.uncurry _ _ _ _ LinearMap.id.flip :=
  monoidalClosed_uncurry_hom _
#align Rep.ihom_ev_app_hom Rep.ihom_ev_app_hom

variable (A B C)

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- There is a `k`-linear isomorphism between the sets of representation morphisms`Hom(A ⊗ B, C)`
and `Hom(B, Homₖ(A, C))`. -/
noncomputable def MonoidalClosed.linearHomEquiv : (A ⊗ B ⟶ C) ≃ₗ[k] B ⟶ A ⟶[Rep k G] C :=
  {
    (ihom.adjunction A).homEquiv _
      _ with
    map_add' := fun f g => rfl
    map_smul' := fun r f => rfl }
#align Rep.monoidal_closed.linear_hom_equiv Rep.MonoidalClosed.linearHomEquiv

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- There is a `k`-linear isomorphism between the sets of representation morphisms`Hom(A ⊗ B, C)`
and `Hom(A, Homₖ(B, C))`. -/
noncomputable def MonoidalClosed.linearHomEquivComm : (A ⊗ B ⟶ C) ≃ₗ[k] A ⟶ B ⟶[Rep k G] C :=
  Linear.homCongr k (β_ A B) (Iso.refl _) ≪≫ₗ MonoidalClosed.linearHomEquiv _ _ _
#align Rep.monoidal_closed.linear_hom_equiv_comm Rep.MonoidalClosed.linearHomEquivComm

variable {A B C}

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem MonoidalClosed.linearHomEquiv_hom (f : A ⊗ B ⟶ C) :
    (MonoidalClosed.linearHomEquiv A B C f).hom = (TensorProduct.curry f.hom).flip :=
  monoidalClosed_curry_hom _
#align Rep.monoidal_closed.linear_hom_equiv_hom Rep.MonoidalClosed.linearHomEquiv_hom

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem MonoidalClosed.linearHomEquivComm_hom (f : A ⊗ B ⟶ C) :
    (MonoidalClosed.linearHomEquivComm A B C f).hom = TensorProduct.curry f.hom :=
  by
  dsimp only [monoidal_closed.linear_hom_equiv_comm]
  refine' LinearMap.ext fun x => LinearMap.ext fun y => _
  simp only [LinearEquiv.trans_apply, monoidal_closed.linear_hom_equiv_hom, linear.hom_congr_apply,
    iso.refl_hom, iso.symm_hom, LinearMap.toFun_eq_coe, LinearMap.coe_comp, Function.comp_apply,
    linear.left_comp_apply, linear.right_comp_apply, category.comp_id, Action.comp_hom,
    LinearMap.flip_apply, TensorProduct.curry_apply, ModuleCat.coe_comp, Function.comp_apply,
    monoidal_category.braiding_inv_apply]
#align Rep.monoidal_closed.linear_hom_equiv_comm_hom Rep.MonoidalClosed.linearHomEquivComm_hom

theorem MonoidalClosed.linearHomEquiv_symm_hom (f : B ⟶ A ⟶[Rep k G] C) :
    ((MonoidalClosed.linearHomEquiv A B C).symm f).hom = TensorProduct.uncurry k A B C f.hom.flip :=
  monoidalClosed_uncurry_hom _
#align Rep.monoidal_closed.linear_hom_equiv_symm_hom Rep.MonoidalClosed.linearHomEquiv_symm_hom

theorem MonoidalClosed.linearHomEquivComm_symm_hom (f : A ⟶ B ⟶[Rep k G] C) :
    ((MonoidalClosed.linearHomEquivComm A B C).symm f).hom = TensorProduct.uncurry k A B C f.hom :=
  by
  dsimp only [monoidal_closed.linear_hom_equiv_comm]
  refine'
    TensorProduct.AlgebraTensorModule.curry_injective
      (LinearMap.ext fun x => LinearMap.ext fun y => _)
  simp only [LinearEquiv.trans_symm, LinearEquiv.trans_apply, linear.hom_congr_symm_apply,
    iso.refl_inv, LinearMap.coe_comp, Function.comp_apply, category.comp_id, Action.comp_hom,
    monoidal_closed.linear_hom_equiv_symm_hom, TensorProduct.AlgebraTensorModule.curry_apply,
    LinearMap.coe_restrictScalars, LinearMap.toFun_eq_coe, LinearMap.flip_apply,
    TensorProduct.curry_apply, ModuleCat.coe_comp, Function.comp_apply,
    monoidal_category.braiding_hom_apply, TensorProduct.uncurry_apply]
#align Rep.monoidal_closed.linear_hom_equiv_comm_symm_hom Rep.MonoidalClosed.linearHomEquivComm_symm_hom

end

end Rep

namespace Representation

variable {k G : Type u} [CommRing k] [Monoid G] {V W : Type u} [AddCommGroup V] [AddCommGroup W]
  [Module k V] [Module k W] (ρ : Representation k G V) (τ : Representation k G W)

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- Tautological isomorphism to help Lean in typechecking. -/
def repOfTprodIso : Rep.of (ρ.tprod τ) ≅ Rep.of ρ ⊗ Rep.of τ :=
  Iso.refl _
#align representation.Rep_of_tprod_iso Representation.repOfTprodIso

theorem repOfTprodIso_apply (x : TensorProduct k V W) : (repOfTprodIso ρ τ).hom.hom x = x :=
  rfl
#align representation.Rep_of_tprod_iso_apply Representation.repOfTprodIso_apply

theorem repOfTprodIso_inv_apply (x : TensorProduct k V W) : (repOfTprodIso ρ τ).inv.hom x = x :=
  rfl
#align representation.Rep_of_tprod_iso_inv_apply Representation.repOfTprodIso_inv_apply

end Representation

/-!
# The categorical equivalence `Rep k G ≌ Module.{u} (monoid_algebra k G)`.
-/

namespace Rep

variable {k G : Type u} [CommRing k] [Monoid G]

-- Verify that the symmetric monoidal structure is available.
example : SymmetricCategory (Rep k G) := by infer_instance

example : MonoidalPreadditive (Rep k G) := by infer_instance

example : MonoidalLinear k (Rep k G) := by infer_instance

noncomputable section

/-- Auxilliary lemma for `to_Module_monoid_algebra`. -/
theorem to_Module_monoidAlgebra_map_aux {k G : Type _} [CommRing k] [Monoid G] (V W : Type _)
    [AddCommGroup V] [AddCommGroup W] [Module k V] [Module k W] (ρ : G →* V →ₗ[k] V)
    (σ : G →* W →ₗ[k] W) (f : V →ₗ[k] W) (w : ∀ g : G, f.comp (ρ g) = (σ g).comp f)
    (r : MonoidAlgebra k G) (x : V) :
    f ((((MonoidAlgebra.lift k G (V →ₗ[k] V)) ρ) r) x) =
      (((MonoidAlgebra.lift k G (W →ₗ[k] W)) σ) r) (f x) :=
  by
  apply MonoidAlgebra.induction_on r
  · intro g
    simp only [one_smul, MonoidAlgebra.lift_single, MonoidAlgebra.of_apply]
    exact LinearMap.congr_fun (w g) x
  · intro g h gw hw
    simp only [map_add, add_left_inj, LinearMap.add_apply, hw, gw]
  · intro r g w
    simp only [AlgHom.map_smul, w, RingHom.id_apply, LinearMap.smul_apply, LinearMap.map_smulₛₗ]
#align Rep.to_Module_monoid_algebra_map_aux Rep.to_Module_monoidAlgebra_map_aux

/-- Auxilliary definition for `to_Module_monoid_algebra`. -/
def toModuleMonoidAlgebraMap {V W : Rep k G} (f : V ⟶ W) :
    ModuleCat.of (MonoidAlgebra k G) V.ρ.AsModule ⟶ ModuleCat.of (MonoidAlgebra k G) W.ρ.AsModule :=
  { f.hom with
    map_smul' := fun r x => to_Module_monoidAlgebra_map_aux V.V W.V V.ρ W.ρ f.hom f.comm r x }
#align Rep.to_Module_monoid_algebra_map Rep.toModuleMonoidAlgebraMap

/-- Functorially convert a representation of `G` into a module over `monoid_algebra k G`. -/
def toModuleMonoidAlgebra : Rep k G ⥤ ModuleCat.{u} (MonoidAlgebra k G)
    where
  obj V := ModuleCat.of _ V.ρ.AsModule
  map V W f := toModuleMonoidAlgebraMap f
#align Rep.to_Module_monoid_algebra Rep.toModuleMonoidAlgebra

/-- Functorially convert a module over `monoid_algebra k G` into a representation of `G`. -/
def ofModuleMonoidAlgebra : ModuleCat.{u} (MonoidAlgebra k G) ⥤ Rep k G
    where
  obj M := Rep.of (Representation.ofModule k G M)
  map M N f :=
    { hom := { f with map_smul' := fun r x => f.map_smul (algebraMap k _ r) x }
      comm' := fun g => by
        ext
        apply f.map_smul }
#align Rep.of_Module_monoid_algebra Rep.ofModuleMonoidAlgebra

theorem ofModuleMonoidAlgebra_obj_coe (M : ModuleCat.{u} (MonoidAlgebra k G)) :
    (ofModuleMonoidAlgebra.obj M : Type u) = RestrictScalars k (MonoidAlgebra k G) M :=
  rfl
#align Rep.of_Module_monoid_algebra_obj_coe Rep.ofModuleMonoidAlgebra_obj_coe

theorem ofModuleMonoidAlgebra_obj_ρ (M : ModuleCat.{u} (MonoidAlgebra k G)) :
    (ofModuleMonoidAlgebra.obj M).ρ = Representation.ofModule k G M :=
  rfl
#align Rep.of_Module_monoid_algebra_obj_ρ Rep.ofModuleMonoidAlgebra_obj_ρ

/-- Auxilliary definition for `equivalence_Module_monoid_algebra`. -/
def counitIsoAddEquiv {M : ModuleCat.{u} (MonoidAlgebra k G)} :
    (ofModuleMonoidAlgebra ⋙ toModuleMonoidAlgebra).obj M ≃+ M :=
  by
  dsimp [of_Module_monoid_algebra, to_Module_monoid_algebra]
  refine' (Representation.ofModule k G ↥M).asModuleEquiv.trans (RestrictScalars.addEquiv _ _ _)
#align Rep.counit_iso_add_equiv Rep.counitIsoAddEquiv

/-- Auxilliary definition for `equivalence_Module_monoid_algebra`. -/
def unitIsoAddEquiv {V : Rep k G} : V ≃+ (toModuleMonoidAlgebra ⋙ ofModuleMonoidAlgebra).obj V :=
  by
  dsimp [of_Module_monoid_algebra, to_Module_monoid_algebra]
  refine' V.ρ.as_module_equiv.symm.trans _
  exact (RestrictScalars.addEquiv _ _ _).symm
#align Rep.unit_iso_add_equiv Rep.unitIsoAddEquiv

/-- Auxilliary definition for `equivalence_Module_monoid_algebra`. -/
def counitIso (M : ModuleCat.{u} (MonoidAlgebra k G)) :
    (ofModuleMonoidAlgebra ⋙ toModuleMonoidAlgebra).obj M ≅ M :=
  LinearEquiv.toModuleIso'
    { counitIsoAddEquiv with
      map_smul' := fun r x => by
        dsimp [counit_iso_add_equiv]
        simp }
#align Rep.counit_iso Rep.counitIso

theorem unit_iso_comm (V : Rep k G) (g : G) (x : V) :
    unitIsoAddEquiv ((V.ρ g).toFun x) =
      ((ofModuleMonoidAlgebra.obj (toModuleMonoidAlgebra.obj V)).ρ g).toFun (unitIsoAddEquiv x) :=
  by
  dsimp [unit_iso_add_equiv, of_Module_monoid_algebra, to_Module_monoid_algebra]
  simp only [AddEquiv.apply_eq_iff_eq, AddEquiv.apply_symm_apply,
    Representation.asModuleEquiv_symm_map_rho, Representation.ofModule_asModule_act]
#align Rep.unit_iso_comm Rep.unit_iso_comm

/-- Auxilliary definition for `equivalence_Module_monoid_algebra`. -/
def unitIso (V : Rep k G) : V ≅ (toModuleMonoidAlgebra ⋙ ofModuleMonoidAlgebra).obj V :=
  Action.mkIso
    (LinearEquiv.toModuleIso'
      { unitIsoAddEquiv with
        map_smul' := fun r x => by
          dsimp [unit_iso_add_equiv]
          simp only [Representation.asModuleEquiv_symm_map_smul,
            RestrictScalars.addEquiv_symm_map_algebraMap_smul] })
    fun g => by
    ext
    apply unit_iso_comm
#align Rep.unit_iso Rep.unitIso

/-- The categorical equivalence `Rep k G ≌ Module (monoid_algebra k G)`. -/
def equivalenceModuleMonoidAlgebra : Rep k G ≌ ModuleCat.{u} (MonoidAlgebra k G)
    where
  Functor := toModuleMonoidAlgebra
  inverse := ofModuleMonoidAlgebra
  unitIso := NatIso.ofComponents (fun V => unitIso V) (by tidy)
  counitIso := NatIso.ofComponents (fun M => counitIso M) (by tidy)
#align Rep.equivalence_Module_monoid_algebra Rep.equivalenceModuleMonoidAlgebra

-- TODO Verify that the equivalence with `Module (monoid_algebra k G)` is a monoidal functor.
end Rep


/-
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.field.opposite
import algebra.module.basic
import algebra.order.archimedean
import data.int.parity

/-!
# Periodicity

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 : α}

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

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

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]
lemma periodic.mul [has_add α] [has_mul β]
  (hf : periodic f c) (hg : periodic g c) :
  periodic (f * g) c :=
by simp * at *

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

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_ring γ] [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

lemma periodic.const_mul [division_ring α]
  (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_ring γ] [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_ring α]
  (h : periodic f c) (a : α) :
  periodic (λ x, f (a⁻¹ * x)) (a * c) :=
h.const_inv_smul₀ a

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

lemma periodic.mul_const' [division_ring α]
  (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_ring α]
  (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_ring α]
  (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)

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_comm_group α]
  (h1 : periodic f c₁) (h2 : periodic f c₂) :
  periodic f (c₁ - c₂) :=
let h := h2.neg in by simp [*, sub_eq_add_neg, add_comm c₁, ← add_assoc] at *

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) :=
by simpa only [sub_eq_neg_add] using h.nsmul n (-x)

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)

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

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) :=
by simpa only [sub_eq_neg_add] using h.zsmul n (-x)

lemma periodic.int_mul_sub_eq [ring α]
  (h : periodic f c) (n : ℤ) :
  f (n * c - x) = f (-x) :=
by simpa only [sub_eq_neg_add] using h.int_mul n (-x)

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

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

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_with_period_zero [add_zero_class α]
  (f : α → β) :
  periodic f 0 :=
λ x, by rw add_zero

/-! ### 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

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

lemma antiperiodic.funext' [has_add α] [add_group β]
  (h : antiperiodic f c) :
  (λ x, -f (x + c)) = f :=
(eq_neg_iff_eq_neg.mp h.funext).symm

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

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 α] [add_group β]
  (h : antiperiodic f c) (n : ℕ) :
  periodic f (n * (2 * c)) :=
h.periodic.nat_mul n

lemma antiperiodic.nat_odd_mul_antiperiodic [semiring α] [add_group β]
  (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 α] [add_group β]
  (h : antiperiodic f c) (n : ℤ) :
  periodic f (n * (2 * c)) :=
h.periodic.int_mul n

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

lemma antiperiodic.nat_mul_eq_of_eq_zero [comm_semiring α] [add_group β]
  (h : antiperiodic f c) (hi : f 0 = 0) (n : ℕ) :
  f (n * c) = 0 :=
begin
  rcases nat.even_or_odd n with ⟨k, rfl⟩ | ⟨k, rfl⟩;
  have hk : (k : α) * (2 * c) = 2 * k * c := by rw [mul_left_comm, ← mul_assoc],
  { simpa [hk, hi] using (h.nat_even_mul_periodic k).eq },
  { simpa [add_mul, hk, hi] using (h.nat_odd_mul_antiperiodic k).eq },
end

lemma antiperiodic.int_mul_eq_of_eq_zero [comm_ring α] [add_group β]
  (h : antiperiodic f c) (hi : f 0 = 0) (n : ℤ) :
  f (n * c) = 0 :=
begin
  rcases int.even_or_odd n with ⟨k, rfl⟩ | ⟨k, rfl⟩;
  have hk : (k : α) * (2 * c) = 2 * k * c := by rw [mul_left_comm, ← mul_assoc],
  { simpa [hk, hi] using (h.int_even_mul_periodic k).eq },
  { simpa [add_mul, hk, hi] using (h.int_odd_mul_antiperiodic k).eq },
end

lemma antiperiodic.sub_eq [add_group α] [add_group β]
  (h : antiperiodic f c) (x : α) :
  f (x - c) = -f x :=
by simp only [eq_neg_iff_eq_neg.mp (h (x - c)), sub_add_cancel]

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

lemma antiperiodic.neg [add_group α] [add_group β]
  (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 α] [add_group β]
  (h : antiperiodic f c) :
  f (-c) = -f 0 :=
by simpa only [zero_add] using h.neg 0

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_ring γ] [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_ring α] [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_ring γ] [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_ring α] [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_ring α] [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_ring α] [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_ring α] [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

lemma antiperiodic.div_inv [division_ring α] [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

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

lemma antiperiodic.sub [add_comm_group α] [add_group β]
  (h1 : antiperiodic f c₁) (h2 : antiperiodic f c₂) :
  periodic f (c₁ - c₂) :=
let h := h2.neg in by simp [*, sub_eq_add_neg, add_comm c₁, ← add_assoc] at *

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

lemma periodic.sub_antiperiod [add_comm_group α] [add_group β]
  (h1 : periodic f c₁) (h2 : antiperiodic f c₂) :
  antiperiodic f (c₁ - c₂) :=
let h := h2.neg in by simp [*, sub_eq_add_neg, add_comm c₁, ← add_assoc] at *

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

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

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

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

end function