-- König's lemma (weak form)

import data.set
import tactic
import data.fintype.basic

namespace konig

open set

attribute [instance] classical.prop_decidable

universe u
variable {X : Type u}

def inv_system {X} (S : ℕ → set X) (fns : ℕ → X → X) :=
∀ n, ∀ x ∈ S(n+1), fns n x ∈ S n

section S_and_fns

variables {S : ℕ → set X} {fns : ℕ → X → X}

def inv_limit (sys : inv_system S fns) (u : ℕ → X) :=
∀ n, u n ∈ S n ∧ u n = fns n (u (n + 1))

def partial_inv_limit (sys : inv_system S fns) (u : ℕ → X) (m : ℕ) :=
(∀ n ≤ m, u n ∈ S n) ∧ (∀ n < m, u n = fns n (u (n + 1)))

-- Given an element x ∈ S m, one can take recursive images of x
-- through the directed system to produce a "partial" inverse limit
-- that's defined for 0, 1, ..., m.
def partial_inv_limit_from (sys : inv_system S fns) (m : ℕ) (x : X) (xel : x ∈ S m)
: ∃ (u : ℕ → X), partial_inv_limit sys u m ∧ u m = x :=
begin
  induction m with m ih generalizing x,
  use (λ n, x), simp, split, simpa, simp,
  rcases ih (fns m x) (sys m x xel) with ⟨u₀, ih⟩,
  use (λ n, if n > m then x else u₀ n),
  split, {
    split, {
      intros n nlt,
      dsimp only,
      split_ifs,
      change n ≥ m.succ at h,
      rwa (by linarith : n = m.succ),
      refine ih.left.left _ _,
      push_neg at h, exact h,
    }, {
      intros n nlt, change n < m + 1 at nlt,
      dsimp only,
      split_ifs,
      exfalso, linarith,
      exfalso, linarith,
      rwa (by linarith : n = m),
      exact ih.2,
      exact ih.1.2 n (by linarith),
    },
  }, {
    split_ifs, refl,
    push_neg at h,
    change m + 1 ≤ m at h,
    exfalso, linarith only [h],
  },
end

-- Given a partial limit up to n, one can restrict it to a shorter partial limit.
lemma partial_inv_limit_restrict (sys : inv_system S fns) (u : ℕ → X) (n m : ℕ)
(h : n ≥ m) (lim : partial_inv_limit sys u n)
: partial_inv_limit sys u m :=
⟨(λ k h, lim.left k (by linarith)), (λ k h, lim.right k (by linarith))⟩

-- Given a "choice function," construct a sequence starting with a given value.
def construct (f : ℕ → X → X) (x₀ : X) : ℕ → X
| 0 := x₀
| (n+1) := (f n (construct n))

-- An element x of (S n) is "very extendable" if partial inverse limits u with u n = x exist for all lengths.
def very_extendable (sys : inv_system S fns) (n : ℕ) (x : X) :=
∀ m ≥ n, ∃ u, partial_inv_limit sys u m ∧ u n = x

-- In an inverse system of finite sets, every set has at least one very extendable element.
lemma exists_very_extendable
(sys : inv_system S fns) (nonempty : ∀ n, ∃ x, x ∈ S n) [fin : ∀ n, fintype (S n)]
: ∀ n, ∃ x, x ∈ S n ∧ very_extendable sys n x :=
begin
  by_contradiction f,
  dsimp only [very_extendable] at f, push_neg at f,
  rcases f with ⟨N,f⟩,
  have f' : ∀ x, ∃ m, m ≥ N ∧ ∀ (u : ℕ → X), x ∈ S N → u N = x → ¬partial_inv_limit sys u m, {
    intro x,
    cases f x,
    use [N, (by linarith)], intros u xnel, tauto,
    rcases h with ⟨m, m_ge_n, f⟩, use [m, m_ge_n], intros u unx, specialize f u, tauto,
  },
  choose v fv using f',
  let N' := (S N).to_finset.sup v,
  have N'big : N ≤ N', {
    rcases nonempty N with ⟨x, xin⟩,
    have h : v x ≤ N', apply finset.le_sup, simp, exact xin,
    linarith [(fv x).1],
  },
  rcases nonempty N' with ⟨xN', xN'in⟩,
  rcases partial_inv_limit_from sys N' xN' xN'in with ⟨u, hplim, uisx⟩,
  set x := u N with xeq,
  rcases fv x with ⟨vxge, uprop⟩,
  have xelt := hplim.left N N'big,
  rw ←xeq at xelt,
  have vx_lt_N' : v x ≤ N', apply finset.le_sup, simp, exact xelt,
  exact uprop u xelt xeq (partial_inv_limit_restrict sys u N' (v x) vx_lt_N' hplim),
end

lemma sys_restrict_very_extendable (sys : inv_system S fns)
: inv_system (λ n, {x ∈ S n | very_extendable sys n x}) fns :=
begin
  intros n x xel, simp at xel ⊢,
  use sys n x xel.left,
  intros m mge,
  rcases xel.right (max m (n + 1)) le_sup_right with ⟨u, plim, un1_eq_x⟩,
  use u,
  use partial_inv_limit_restrict sys u (max m (n + 1)) m le_sup_left plim,
  rw ← un1_eq_x,
  have hle : n < max m (n + 1), linarith [le_max_right m (n + 1)],
  exact plim.right n hle,
end

lemma lift_very_extendable (sys : inv_system S fns) {n : ℕ} {x : X} [fin : ∀ n, fintype (S n)]
(h : very_extendable sys n x)
: ∃ x', x = fns n x' ∧ x' ∈ S(n + 1) ∧ very_extendable sys (n + 1) x' :=
begin
  dsimp [very_extendable] at h,
  let Sx := λ k, {y ∈ S k | ∃ u, partial_inv_limit sys u (max n k) ∧ u n = x ∧ u k = y},
  have Sx_nonempty : ∀ k, ∃ y, y ∈ Sx k, {
    intro k,
    by_cases klt : k < n, {
      rcases h n (by linarith) with ⟨u, plim, uneq⟩,
      use [u k, plim.left k (by linarith), u],
      rw (max_eq_left_of_lt klt),
      exact ⟨plim, uneq, rfl⟩,
    }, {
      push_neg at klt,
      rcases h k klt with ⟨u, plim, un_eq⟩,
      use [u k, plim.left k (by linarith), u],
      rw (max_eq_right klt),
      use [plim, un_eq],
    },
  },
  have Sx_invsys : inv_system Sx fns, {
    rintros k y ⟨yelt, u, plim, un_eq, uk_eq⟩,
    split,
    exact sys k y yelt,
    have mle : max n k ≤ max n (k + 1) := max_le (le_max_left _ _) (by linarith [le_max_right n (k + 1)]),
    use [u, partial_inv_limit_restrict sys u _ _ mle plim, un_eq],
    rw ←uk_eq,
    exact (plim.2 k) (by linarith [le_max_right n (k + 1)]),
  },
  rcases exists_very_extendable Sx_invsys Sx_nonempty (n + 1) with ⟨x', ⟨x'elt, u, plim, ueq_x, ueq_x'⟩, vext⟩,
  use x',
  split,
  rw [←ueq_x, ←ueq_x'],
  rw max_eq_right (by linarith : n ≤ n + 1) at plim,
  use plim.2 n (by linarith),
  use x'elt,
  intros m mgen,
  rcases vext m mgen with ⟨v, plim, veq_x'⟩,
  exact ⟨v, ⟨(λ n nlem, (plim.1 n nlem).1), (λ n nltm, plim.2 n nltm)⟩, veq_x'⟩,
end

lemma surj_inv_sys_has_inv_lim (sys : inv_system S fns) (s : ∀ n x, x ∈ S n → ∃ x', x' ∈ S (n + 1) ∧ x = fns n x') (start : ∃x₀, x₀ ∈ S 0)
: ∃ u, inv_limit sys u :=
begin
  have s' : ∀ n x, ∃ x', x ∈ S n → x' ∈ S (n + 1) ∧ x = fns n x', {
    intros n x,
    by_cases xel : x ∈ S n, {
      rcases s n x xel with ⟨x', h⟩,
      use x', intro, exact h,
    }, {
      use x,
    },
  },
  choose u hu using s',
  rcases start with ⟨x₀, x₀elt⟩,
  use construct u x₀,
  intro n,
  induction n with n ih, {
    exact ⟨x₀elt, (hu 0 x₀ x₀elt).2⟩,
  }, {
    exact ⟨(hu _ _ ih.1).1, (hu _ _ (hu _ _ ih.1).1).2⟩,
  },
end

-- This is that an ℕ-indexed inverse system of finite sets has a nonempty inverse limit.
-- This would follow from Tychonoff's theorem applied to the product of these finite sets.
-- One could also prove this using ultrafilters.
lemma weak_konig_lemma (sys : inv_system S fns) (nonempty : ∀ n, ∃ x, x ∈ S n) [fin : ∀ n, fintype (S n)]
: ∃ (u : ℕ → X), inv_limit sys u
:= begin
  let S' := λ n, {x | x ∈ S n ∧ very_extendable sys n x},
  have nonempty' : ∀ n, ∃ x, x ∈ S' n := exists_very_extendable sys nonempty,

  have liftable : ∀ n x, x ∈ S' n → ∃ x', x' ∈ S' (n + 1) ∧ x = fns n x', {
    intros n x xel,
    rcases lift_very_extendable sys xel.2 with ⟨x', xeq, x'el⟩,
    exact ⟨x', x'el, xeq⟩,
  },
  have sys' : inv_system S' fns := sys_restrict_very_extendable sys,
  have stronger : ∃ (u : ℕ → X), inv_limit sys' u := surj_inv_sys_has_inv_lim sys' liftable (nonempty' 0),

  rcases stronger with ⟨u, invlim⟩,
  use u, intro n,
  specialize invlim n,
  exact ⟨invlim.left.left, invlim.right⟩,
end

end S_and_fns

end konig


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

! This file was ported from Lean 3 source module category_theory.preadditive.of_biproducts
! leanprover-community/mathlib commit 932872382355f00112641d305ba0619305dc8642
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.CategoryTheory.Limits.Shapes.Biproducts
import Mathbin.GroupTheory.EckmannHilton

/-!
# Constructing a semiadditive structure from binary biproducts

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

We show that any category with zero morphisms and binary biproducts is enriched over the category
of commutative monoids.

-/

noncomputable section

universe v u

open CategoryTheory

open CategoryTheory.Limits

namespace CategoryTheory.SemiadditiveOfBinaryBiproducts

variable {C : Type u} [Category.{v} C] [HasZeroMorphisms C] [HasBinaryBiproducts C]

section

variable (X Y : C)

#print CategoryTheory.SemiadditiveOfBinaryBiproducts.leftAdd /-
/-- `f +ₗ g` is the composite `X ⟶ Y ⊞ Y ⟶ Y`, where the first map is `(f, g)` and the second map
    is `(𝟙 𝟙)`. -/
@[simp]
def leftAdd (f g : X ⟶ Y) : X ⟶ Y :=
  biprod.lift f g ≫ biprod.desc (𝟙 Y) (𝟙 Y)
#align category_theory.semiadditive_of_binary_biproducts.left_add CategoryTheory.SemiadditiveOfBinaryBiproducts.leftAdd
-/

#print CategoryTheory.SemiadditiveOfBinaryBiproducts.rightAdd /-
/-- `f +ᵣ g` is the composite `X ⟶ X ⊞ X ⟶ Y`, where the first map is `(𝟙, 𝟙)` and the second map
    is `(f g)`. -/
@[simp]
def rightAdd (f g : X ⟶ Y) : X ⟶ Y :=
  biprod.lift (𝟙 X) (𝟙 X) ≫ biprod.desc f g
#align category_theory.semiadditive_of_binary_biproducts.right_add CategoryTheory.SemiadditiveOfBinaryBiproducts.rightAdd
-/

-- mathport name: «expr +ₗ »
local infixr:65 " +ₗ " => leftAdd X Y

-- mathport name: «expr +ᵣ »
local infixr:65 " +ᵣ " => rightAdd X Y

#print CategoryTheory.SemiadditiveOfBinaryBiproducts.isUnital_leftAdd /-
theorem isUnital_leftAdd : EckmannHilton.IsUnital (· +ₗ ·) 0 :=
  ⟨⟨fun f => by simp [show biprod.lift (0 : X ⟶ Y) f = f ≫ biprod.inr by ext <;> simp]⟩,
    ⟨fun f => by simp [show biprod.lift f (0 : X ⟶ Y) = f ≫ biprod.inl by ext <;> simp]⟩⟩
#align category_theory.semiadditive_of_binary_biproducts.is_unital_left_add CategoryTheory.SemiadditiveOfBinaryBiproducts.isUnital_leftAdd
-/

#print CategoryTheory.SemiadditiveOfBinaryBiproducts.isUnital_rightAdd /-
theorem isUnital_rightAdd : EckmannHilton.IsUnital (· +ᵣ ·) 0 :=
  ⟨⟨fun f => by simp [show biprod.desc (0 : X ⟶ Y) f = biprod.snd ≫ f by ext <;> simp]⟩,
    ⟨fun f => by simp [show biprod.desc f (0 : X ⟶ Y) = biprod.fst ≫ f by ext <;> simp]⟩⟩
#align category_theory.semiadditive_of_binary_biproducts.is_unital_right_add CategoryTheory.SemiadditiveOfBinaryBiproducts.isUnital_rightAdd
-/

#print CategoryTheory.SemiadditiveOfBinaryBiproducts.distrib /-
theorem distrib (f g h k : X ⟶ Y) : (f +ᵣ g) +ₗ h +ᵣ k = (f +ₗ h) +ᵣ g +ₗ k :=
  by
  let diag : X ⊞ X ⟶ Y ⊞ Y := biprod.lift (biprod.desc f g) (biprod.desc h k)
  have hd₁ : biprod.inl ≫ diag = biprod.lift f h := by ext <;> simp
  have hd₂ : biprod.inr ≫ diag = biprod.lift g k := by ext <;> simp
  have h₁ : biprod.lift (f +ᵣ g) (h +ᵣ k) = biprod.lift (𝟙 X) (𝟙 X) ≫ diag := by ext <;> simp
  have h₂ : diag ≫ biprod.desc (𝟙 Y) (𝟙 Y) = biprod.desc (f +ₗ h) (g +ₗ k) := by
    ext <;> simp [reassoc_of hd₁, reassoc_of hd₂]
  rw [leftAdd, h₁, category.assoc, h₂, rightAdd]
#align category_theory.semiadditive_of_binary_biproducts.distrib CategoryTheory.SemiadditiveOfBinaryBiproducts.distrib
-/

#print CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts /-
/-- In a category with binary biproducts, the morphisms form a commutative monoid. -/
def addCommMonoidHomOfHasBinaryBiproducts : AddCommMonoid (X ⟶ Y)
    where
  add := (· +ᵣ ·)
  add_assoc :=
    (EckmannHilton.mul_assoc (isUnital_leftAdd X Y) (isUnital_rightAdd X Y) (distrib X Y)).and_assoc
  zero := 0
  zero_add := (isUnital_rightAdd X Y).left_id
  add_zero := (isUnital_rightAdd X Y).right_id
  add_comm :=
    (EckmannHilton.mul_comm (isUnital_leftAdd X Y) (isUnital_rightAdd X Y) (distrib X Y)).comm
#align category_theory.semiadditive_of_binary_biproducts.add_comm_monoid_hom_of_has_binary_biproducts CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts
-/

end

section

variable {X Y Z : C}

attribute [local instance] add_comm_monoid_hom_of_has_binary_biproducts

/- warning: category_theory.semiadditive_of_binary_biproducts.add_eq_right_addition -> CategoryTheory.SemiadditiveOfBinaryBiproducts.add_eq_right_addition is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_2 : CategoryTheory.Limits.HasZeroMorphisms.{u1, u2} C _inst_1] [_inst_3 : CategoryTheory.Limits.HasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddZeroClass.toHasAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Y))))) f g) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X (CategoryTheory.Limits.biprod.{u1, u2} C _inst_1 _inst_2 X X (CategoryTheory.Limits.HasBinaryBiproducts.hasBinaryBiproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 X X)) Y (CategoryTheory.Limits.biprod.lift.{u1, u2} C _inst_1 _inst_2 X X X (CategoryTheory.Limits.HasBinaryBiproducts.hasBinaryBiproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 X X) (CategoryTheory.CategoryStruct.id.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X) (CategoryTheory.CategoryStruct.id.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X)) (CategoryTheory.Limits.biprod.desc.{u1, u2} C _inst_1 _inst_2 Y X X (CategoryTheory.Limits.HasBinaryBiproducts.hasBinaryBiproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 X X) f g))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_2 : CategoryTheory.Limits.HasZeroMorphisms.{u1, u2} C _inst_1] [_inst_3 : CategoryTheory.Limits.HasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddZeroClass.toAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Y))))) f g) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X (CategoryTheory.Limits.biprod.{u1, u2} C _inst_1 _inst_2 X X (CategoryTheory.Limits.HasBinaryBiproducts.has_binary_biproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 X X)) Y (CategoryTheory.Limits.biprod.lift.{u1, u2} C _inst_1 _inst_2 X X X (CategoryTheory.Limits.HasBinaryBiproducts.has_binary_biproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 X X) (CategoryTheory.CategoryStruct.id.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X) (CategoryTheory.CategoryStruct.id.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X)) (CategoryTheory.Limits.biprod.desc.{u1, u2} C _inst_1 _inst_2 Y X X (CategoryTheory.Limits.HasBinaryBiproducts.has_binary_biproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 X X) f g))
Case conversion may be inaccurate. Consider using '#align category_theory.semiadditive_of_binary_biproducts.add_eq_right_addition CategoryTheory.SemiadditiveOfBinaryBiproducts.add_eq_right_additionₓ'. -/
theorem add_eq_right_addition (f g : X ⟶ Y) : f + g = biprod.lift (𝟙 X) (𝟙 X) ≫ biprod.desc f g :=
  rfl
#align category_theory.semiadditive_of_binary_biproducts.add_eq_right_addition CategoryTheory.SemiadditiveOfBinaryBiproducts.add_eq_right_addition

/- warning: category_theory.semiadditive_of_binary_biproducts.add_eq_left_addition -> CategoryTheory.SemiadditiveOfBinaryBiproducts.add_eq_left_addition is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_2 : CategoryTheory.Limits.HasZeroMorphisms.{u1, u2} C _inst_1] [_inst_3 : CategoryTheory.Limits.HasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddZeroClass.toHasAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Y))))) f g) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X (CategoryTheory.Limits.biprod.{u1, u2} C _inst_1 _inst_2 Y Y (CategoryTheory.Limits.HasBinaryBiproducts.hasBinaryBiproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 Y Y)) Y (CategoryTheory.Limits.biprod.lift.{u1, u2} C _inst_1 _inst_2 X Y Y (CategoryTheory.Limits.HasBinaryBiproducts.hasBinaryBiproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 Y Y) f g) (CategoryTheory.Limits.biprod.desc.{u1, u2} C _inst_1 _inst_2 Y Y Y (CategoryTheory.Limits.HasBinaryBiproducts.hasBinaryBiproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 Y Y) (CategoryTheory.CategoryStruct.id.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) Y) (CategoryTheory.CategoryStruct.id.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) Y)))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_2 : CategoryTheory.Limits.HasZeroMorphisms.{u1, u2} C _inst_1] [_inst_3 : CategoryTheory.Limits.HasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2] {X : C} {Y : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddZeroClass.toAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Y))))) f g) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X (CategoryTheory.Limits.biprod.{u1, u2} C _inst_1 _inst_2 Y Y (CategoryTheory.Limits.HasBinaryBiproducts.has_binary_biproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 Y Y)) Y (CategoryTheory.Limits.biprod.lift.{u1, u2} C _inst_1 _inst_2 X Y Y (CategoryTheory.Limits.HasBinaryBiproducts.has_binary_biproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 Y Y) f g) (CategoryTheory.Limits.biprod.desc.{u1, u2} C _inst_1 _inst_2 Y Y Y (CategoryTheory.Limits.HasBinaryBiproducts.has_binary_biproduct.{u1, u2} C _inst_1 _inst_2 _inst_3 Y Y) (CategoryTheory.CategoryStruct.id.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) Y) (CategoryTheory.CategoryStruct.id.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) Y)))
Case conversion may be inaccurate. Consider using '#align category_theory.semiadditive_of_binary_biproducts.add_eq_left_addition CategoryTheory.SemiadditiveOfBinaryBiproducts.add_eq_left_additionₓ'. -/
theorem add_eq_left_addition (f g : X ⟶ Y) : f + g = biprod.lift f g ≫ biprod.desc (𝟙 Y) (𝟙 Y) :=
  congr_fun₂ (EckmannHilton.mul (isUnital_leftAdd X Y) (isUnital_rightAdd X Y) (distrib X Y)).symm f
    g
#align category_theory.semiadditive_of_binary_biproducts.add_eq_left_addition CategoryTheory.SemiadditiveOfBinaryBiproducts.add_eq_left_addition

/- warning: category_theory.semiadditive_of_binary_biproducts.add_comp -> CategoryTheory.SemiadditiveOfBinaryBiproducts.add_comp is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_2 : CategoryTheory.Limits.HasZeroMorphisms.{u1, u2} C _inst_1] [_inst_3 : CategoryTheory.Limits.HasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2] {X : C} {Y : C} {Z : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (h : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddZeroClass.toHasAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Y))))) f g) h) (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddZeroClass.toHasAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Z))))) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z f h) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z g h))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_2 : CategoryTheory.Limits.HasZeroMorphisms.{u1, u2} C _inst_1] [_inst_3 : CategoryTheory.Limits.HasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2] {X : C} {Y : C} {Z : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (h : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddZeroClass.toAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Y))))) f g) h) (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddZeroClass.toAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Z))))) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z f h) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z g h))
Case conversion may be inaccurate. Consider using '#align category_theory.semiadditive_of_binary_biproducts.add_comp CategoryTheory.SemiadditiveOfBinaryBiproducts.add_compₓ'. -/
theorem add_comp (f g : X ⟶ Y) (h : Y ⟶ Z) : (f + g) ≫ h = f ≫ h + g ≫ h :=
  by
  simp only [add_eq_right_addition, category.assoc]
  congr
  ext <;> simp
#align category_theory.semiadditive_of_binary_biproducts.add_comp CategoryTheory.SemiadditiveOfBinaryBiproducts.add_comp

/- warning: category_theory.semiadditive_of_binary_biproducts.comp_add -> CategoryTheory.SemiadditiveOfBinaryBiproducts.comp_add is a dubious translation:
lean 3 declaration is
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_2 : CategoryTheory.Limits.HasZeroMorphisms.{u1, u2} C _inst_1] [_inst_3 : CategoryTheory.Limits.HasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2] {X : C} {Y : C} {Z : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (h : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z f (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (AddZeroClass.toHasAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 Y Z))))) g h)) (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddZeroClass.toHasAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Z))))) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z f g) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z f h))
but is expected to have type
  forall {C : Type.{u2}} [_inst_1 : CategoryTheory.Category.{u1, u2} C] [_inst_2 : CategoryTheory.Limits.HasZeroMorphisms.{u1, u2} C _inst_1] [_inst_3 : CategoryTheory.Limits.HasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2] {X : C} {Y : C} {Z : C} (f : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Y) (g : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (h : Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z), Eq.{succ u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z f (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (AddZeroClass.toAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) Y Z) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 Y Z))))) g h)) (HAdd.hAdd.{u1, u1, u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (instHAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddZeroClass.toAdd.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddMonoid.toAddZeroClass.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (AddCommMonoid.toAddMonoid.{u1} (Quiver.Hom.{succ u1, u2} C (CategoryTheory.CategoryStruct.toQuiver.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1)) X Z) (CategoryTheory.SemiadditiveOfBinaryBiproducts.addCommMonoidHomOfHasBinaryBiproducts.{u1, u2} C _inst_1 _inst_2 _inst_3 X Z))))) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z f g) (CategoryTheory.CategoryStruct.comp.{u1, u2} C (CategoryTheory.Category.toCategoryStruct.{u1, u2} C _inst_1) X Y Z f h))
Case conversion may be inaccurate. Consider using '#align category_theory.semiadditive_of_binary_biproducts.comp_add CategoryTheory.SemiadditiveOfBinaryBiproducts.comp_addₓ'. -/
theorem comp_add (f : X ⟶ Y) (g h : Y ⟶ Z) : f ≫ (g + h) = f ≫ g + f ≫ h :=
  by
  simp only [add_eq_left_addition, ← category.assoc]
  congr
  ext <;> simp
#align category_theory.semiadditive_of_binary_biproducts.comp_add CategoryTheory.SemiadditiveOfBinaryBiproducts.comp_add

end

end CategoryTheory.SemiadditiveOfBinaryBiproducts


/-
Copyright (c) 2020 Patrick Stevens. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Stevens
-/
import algebra.big_operators.associated
import data.nat.choose.sum
import data.nat.parity
import tactic.ring_exp

/-!
# Primorial

This file defines the primorial function (the product of primes less than or equal to some bound),
and proves that `primorial n ≤ 4 ^ n`.

## Notations

We use the local notation `n#` for the primorial of `n`: that is, the product of the primes less
than or equal to `n`.
-/

open finset
open nat
open_locale big_operators nat

/-- The primorial `n#` of `n` is the product of the primes less than or equal to `n`.
-/
def primorial (n : ℕ) : ℕ := ∏ p in (filter nat.prime (range (n + 1))), p
local notation x`#` := primorial x

lemma primorial_succ {n : ℕ} (n_big : 1 < n) (r : n % 2 = 1) : (n + 1)# = n# :=
begin
  refine prod_congr _ (λ _ _, rfl),
  rw [range_succ, filter_insert, if_neg (λ h, _)],
  have two_dvd : 2 ∣ n + 1 := dvd_iff_mod_eq_zero.mpr (by rw [← mod_add_mod, r, mod_self]),
  linarith [(h.dvd_iff_eq (nat.bit0_ne_one 1)).mp two_dvd],
end

lemma dvd_choose_of_middling_prime (p : ℕ) (is_prime : nat.prime p) (m : ℕ)
  (p_big : m + 1 < p) (p_small : p ≤ 2 * m + 1) : p ∣ choose (2 * m + 1) (m + 1) :=
begin
  have m_size : m + 1 ≤ 2 * m + 1 := le_of_lt (lt_of_lt_of_le p_big p_small),
  have s : ¬(p ∣ (m + 1)!),
  { intros p_div_fact,
    exact lt_le_antisymm p_big (is_prime.dvd_factorial.mp p_div_fact), },
  have t : ¬(p ∣ (2 * m + 1 - (m + 1))!),
  { intros p_div_fact,
    refine lt_le_antisymm (lt_of_succ_lt p_big) _,
    convert is_prime.dvd_factorial.mp p_div_fact,
    rw [two_mul, add_assoc, nat.add_sub_cancel] },
  have expanded :
    choose (2 * m + 1) (m + 1) * (m + 1)! * (2 * m + 1 - (m + 1))! = (2 * m + 1)! :=
    @choose_mul_factorial_mul_factorial (2 * m + 1) (m + 1) m_size,
  have p_div_big_fact : p ∣ (2 * m + 1)! := (prime.dvd_factorial is_prime).mpr p_small,
  rw [←expanded, mul_assoc] at p_div_big_fact,
  obtain p_div_choose | p_div_facts : p ∣ choose (2 * m + 1) (m + 1) ∨ p ∣ _! * _! :=
    (prime.dvd_mul is_prime).1 p_div_big_fact,
  { exact p_div_choose, },
  cases (prime.dvd_mul is_prime).1 p_div_facts,
  exacts [(s h).elim, (t h).elim],
end

lemma primorial_le_4_pow : ∀ (n : ℕ), n# ≤ 4 ^ n
| 0 := le_rfl
| 1 := le_of_inf_eq rfl
| (n + 2) :=
  match nat.mod_two_eq_zero_or_one (n + 1) with
  | or.inl n_odd :=
    match nat.even_iff.2 n_odd with
    | ⟨m, twice_m⟩ :=
      have recurse : m + 1 < n + 2 := by linarith,
      begin
        calc (n + 2)#
            = ∏ i in filter nat.prime (range (2 * m + 2)), i : by simpa [two_mul, ←twice_m]
        ... = ∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2) ∪ range (m + 2)), i :
              begin
                rw [range_eq_Ico, finset.union_comm, finset.Ico_union_Ico_eq_Ico],
                { exact bot_le },
                { simpa only [add_le_add_iff_right, two_mul] using nat.le_add_left m m },
              end
        ... = ∏ i in (filter nat.prime (finset.Ico (m + 2) (2 * m + 2))
              ∪ (filter nat.prime (range (m + 2)))), i :
              by rw filter_union
        ... = (∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2)), i)
              * (∏ i in filter nat.prime (range (m + 2)), i) :
              begin
                apply finset.prod_union,
                have disj : disjoint (finset.Ico (m + 2) (2 * m + 2)) (range (m + 2)),
                { simp only [finset.disjoint_left, and_imp, finset.mem_Ico, not_lt,
                    finset.mem_range],
                  intros _ pr _, exact pr, },
                exact finset.disjoint_filter_filter disj,
              end
        ... ≤ (∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2)), i) * 4 ^ (m + 1) :
              nat.mul_le_mul_left _ (primorial_le_4_pow (m + 1))
        ... ≤ (choose (2 * m + 1) (m + 1)) * 4 ^ (m + 1) :
              begin
                have s : ∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2)),
                  i ∣ choose (2 * m + 1) (m + 1),
                { refine prod_primes_dvd  (choose (2 * m + 1) (m + 1)) _ _,
                  { intros a, rw [finset.mem_filter, nat.prime_iff], apply and.right, },
                  { intros a, rw finset.mem_filter,
                    intros pr,
                    rcases pr with ⟨ size, is_prime ⟩,
                    simp only [finset.mem_Ico] at size,
                    rcases size with ⟨ a_big , a_small ⟩,
                    exact dvd_choose_of_middling_prime a is_prime m a_big
                      (nat.lt_succ_iff.mp a_small), }, },
                have r : ∏ i in filter nat.prime (finset.Ico (m + 2) (2 * m + 2)),
                  i ≤ choose (2 * m + 1) (m + 1),
                { refine @nat.le_of_dvd _ _ _ s,
                  exact @choose_pos (2 * m + 1) (m + 1) (by linarith), },
                exact nat.mul_le_mul_right _ r,
              end
        ... = (choose (2 * m + 1) m) * 4 ^ (m + 1) : by rw choose_symm_half m
        ... ≤ 4 ^ m * 4 ^ (m + 1) : nat.mul_le_mul_right _ (choose_middle_le_pow m)
        ... = 4 ^ (2 * m + 1) : by ring_exp
        ... = 4 ^ (n + 2) : by rw [two_mul, ←twice_m],
      end
    end
  | or.inr n_even :=
    begin
      obtain one_lt_n | n_le_one : 1 < n + 1 ∨ n + 1 ≤ 1 := lt_or_le 1 (n + 1),
      { rw primorial_succ one_lt_n n_even,
        calc (n + 1)#
              ≤ 4 ^ n.succ : primorial_le_4_pow (n + 1)
          ... ≤ 4 ^ (n + 2) : pow_le_pow (by norm_num) (nat.le_succ _), },
      { have n_zero : n = 0 := eq_bot_iff.2 (succ_le_succ_iff.1 n_le_one),
        norm_num [n_zero, primorial, range_succ, prod_filter, nat.not_prime_zero, nat.prime_two] },
    end

end


/-
Copyright (c) 2022 Jun Yoshida. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
-/
import Std.Data.Nat.Lemmas

import Algdata.Init.Nat
import Algdata.Data.Z256.Basic

/-!
# Basic lemmas on the quotient ring Z/256Z
-/

namespace ℤ256

/-!
## Equality
-/

protected
theorem eq : ∀ {x y : ℤ256}, x.val = y.val → x = y
| mk _, mk _, rfl => rfl

protected
theorem eq_of_toNat_eq : ∀ {x y : ℤ256}, x.toNat = y.toNat → x = y
| mk (.mk (.mk _ _)), mk (.mk (.mk _ _)), h => by cases h; rfl

/-!
## Basic arithmetics
-/

/-!
### Addition
-/
protected
theorem add_comm (x y : ℤ256) : x + y = y + x := by
  apply ℤ256.eq_of_toNat_eq
  conv => change (x.toNat + y.toNat) % UInt8.size = (y.toNat + x.toNat) % UInt8.size
  rw [Nat.add_comm]

@[simp]
protected
theorem zero_add (x : ℤ256) : 0 + x = x := by
  apply ℤ256.eq_of_toNat_eq
  conv => change (0 + x.toNat) % UInt8.size = x.toNat
  rw [Nat.zero_add]
  exact Nat.mod_eq_of_lt x.val.val.2

@[simp]
protected
theorem add_zero (x : ℤ256) : x + 0 = x := by
  rw [ℤ256.add_comm, ℤ256.zero_add]

protected
theorem add_assoc (x y z : ℤ256) : x + y + z = x + (y + z) := by
  apply ℤ256.eq_of_toNat_eq
  conv =>
    lhs; change ((x.toNat + y.toNat) % UInt8.size + z.toNat) % UInt8.size
  conv =>
    rhs; change (x.toNat + (y.toNat + z.toNat) % UInt8.size) % UInt8.size
  simp [Nat.add_assoc]

@[simp]
protected
theorem sub_self (x : ℤ256) : x - x = 0 := by
  apply ℤ256.eq_of_toNat_eq
  conv => change (x.toNat + (UInt8.size - x.toNat)) % UInt8.size = 0
  rw [←Nat.add_sub_assoc (k:=x.toNat) (Nat.le_of_lt x.val.val.2)]
  rw [Nat.add_sub_self_left]
  rfl

@[simp]
protected
theorem add_sub_assoc (x y z : ℤ256) : x + y - z = x + (y - z) := by
  apply ℤ256.eq_of_toNat_eq
  conv =>
    lhs; change ((x.toNat + y.toNat) % UInt8.size + (UInt8.size - z.toNat)) % UInt8.size
    rw [Nat.add_mod, Nat.mod_mod, ←Nat.add_mod]
    rw [Nat.add_assoc]
  conv =>
    rhs; change (x.toNat + (y.toNat + (UInt8.size - z.toNat)) % UInt8.size) % UInt8.size
    rw [Nat.add_mod, Nat.mod_mod, ←Nat.add_mod]

@[simp]
protected
theorem add_sub_self_right (x y : ℤ256) : x + y - y = x := by
  rw [ℤ256.add_sub_assoc, ℤ256.sub_self, ℤ256.add_zero]

@[simp]
protected
theorem add_sub_self_left (x y : ℤ256) : x + y - x = y := by
  rw [ℤ256.add_comm, ℤ256.add_sub_self_right]

protected
theorem neg_eq (x : ℤ256) : -x = 0 - x := rfl

/-!
### Multiplication
-/
protected
theorem mul_comm (x y : ℤ256) : x * y = y * x := by
  apply ℤ256.eq_of_toNat_eq
  conv => change (x.toNat * y.toNat) % UInt8.size = (y.toNat * x.toNat) % UInt8.size
  rw [Nat.mul_comm]

@[simp]
protected
theorem one_mul (x : ℤ256) : 1 * x = x := by
  apply ℤ256.eq_of_toNat_eq
  conv => change (1 * x.toNat) % UInt8.size = x.toNat
  rw [Nat.one_mul]
  exact Nat.mod_eq_of_lt x.val.val.2

@[simp]
protected
theorem mul_one (x : ℤ256) : x * 1 = x := by
  rw [ℤ256.mul_comm, ℤ256.one_mul]

protected
theorem mul_assoc (x y z : ℤ256) : x * y * z = x * (y * z) := by
  apply ℤ256.eq_of_toNat_eq
  conv =>
    lhs; change ((x.toNat * y.toNat) % UInt8.size * z.toNat) % UInt8.size
    rw [Nat.mod_mul_mod, Nat.mul_assoc]
  conv =>
    rhs; change (x.toNat * ((y.toNat * z.toNat) % UInt8.size)) % UInt8.size
    rw [Nat.mul_mod, Nat.mod_mod, ←Nat.mul_mod]

/-!
### Distributive law
-/

protected
theorem left_distrib (x y z : ℤ256) : x * (y + z) = x * y + x * z := by
  apply ℤ256.eq_of_toNat_eq
  conv =>
    lhs; change x.toNat * ((y.toNat + z.toNat) % UInt8.size) % UInt8.size
    rw [Nat.mul_mod, Nat.mod_mod, ←Nat.mul_mod]
    rw [Nat.left_distrib]
  conv =>
    rhs; change (x.toNat * y.toNat % UInt8.size + x.toNat * z.toNat % UInt8.size) % UInt8.size
    rw [←Nat.add_mod]

protected
theorem right_distrib (x y z : ℤ256) : (x + y) * z = x * z + y * z := by
  rw [ℤ256.mul_comm (x+y) z, ℤ256.mul_comm x z, ℤ256.mul_comm y z]
  exact ℤ256.left_distrib z x y

end ℤ256


/-
Fill in the sorry.

section
  variable U : Type
  variables A B C : U → Prop

  variable h1 : ∀ x, A x ∨ B x
  variable h2 : ∀ x, A x → C x
  variable h3 : ∀ x, B x → C x

  example : ∀ x, C x :=
  sorry
end
-/

section
  variable U : Type
  variables A B C : U → Prop

  variable h1 : ∀ x, A x ∨ B x
  variable h2 : ∀ x, A x → C x
  variable h3 : ∀ x, B x → C x

  example : ∀ x, C x :=
  assume x: U,
  have hAxoBx: A(x) ∨ B(x), from h1(x),
  show C(x), from or.elim(hAxoBx)(
    λ hAx: A(x),
    show C(x), from h2 x hAx
  )(
    λ hBx: B(x),
    show C(x), from h3 x hBx
  )
end


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_76
  (f : ℤ → ℤ)
  (h₀ : ∀n, odd n → f n = n^2)
  (h₁ : ∀ n, even n → f n = n^2 - 4*n -1) :
  f 4 = -1 :=
begin
  suffices : f 4 = 4^2 - 4*4 - 1, rw this; ring_nf,
  apply h₁,
  refine even_iff_two_dvd.mpr _,
  exact two_dvd_bit0,
end


/-
Copyright (c) 2022 Riccardo Brasca. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Riccardo Brasca, Eric Rodriguez

! This file was ported from Lean 3 source module number_theory.number_field.norm
! leanprover-community/mathlib commit ee0c179cd3c8a45aa5bffbf1b41d8dbede452865
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.NumberTheory.NumberField.Basic
import Mathbin.RingTheory.Norm

/-!
# Norm in number fields
Given a finite extension of number fields, we define the norm morphism as a function between the
rings of integers.

## Main definitions
* `ring_of_integers.norm K` : `algebra.norm` as a morphism `(𝓞 L) →* (𝓞 K)`.
## Main results
* `algebra.dvd_norm` : if `L/K` is a finite Galois extension of fields, then, for all `(x : 𝓞 L)`
  we have that `x ∣ algebra_map (𝓞 K) (𝓞 L) (norm K x)`.

-/

open NumberField BigOperators

open Finset NumberField Algebra

namespace RingOfIntegers

variable {L : Type _} (K : Type _) [Field K] [Field L] [Algebra K L] [FiniteDimensional K L]

/-- `algebra.norm` as a morphism betwen the rings of integers. -/
@[simps]
noncomputable def norm [IsSeparable K L] : 𝓞 L →* 𝓞 K :=
  ((Algebra.norm K).restrict (𝓞 L)).codRestrict (𝓞 K) fun x => isIntegral_norm K x.2
#align ring_of_integers.norm RingOfIntegers.norm

attribute [local instance] NumberField.ringOfIntegersAlgebra

theorem coe_algebraMap_norm [IsSeparable K L] (x : 𝓞 L) :
    (algebraMap (𝓞 K) (𝓞 L) (norm K x) : L) = algebraMap K L (Algebra.norm K (x : L)) :=
  rfl
#align ring_of_integers.coe_algebra_map_norm RingOfIntegers.coe_algebraMap_norm

theorem isUnit_norm [IsGalois K L] {x : 𝓞 L} : IsUnit (norm K x) ↔ IsUnit x := by
  classical
    refine' ⟨fun hx => _, IsUnit.map _⟩
    replace hx : IsUnit (algebraMap (𝓞 K) (𝓞 L) <| norm K x) := hx.map (algebraMap (𝓞 K) <| 𝓞 L)
    refine'
      @isUnit_of_mul_isUnit_right (𝓞 L) _
        ⟨(univ \ {AlgEquiv.refl}).Prod fun σ : L ≃ₐ[K] L => σ x,
          prod_mem fun σ hσ => map_isIntegral (σ : L →+* L).toIntAlgHom x.2⟩
        _ _
    convert hx using 1
    ext
    push_cast
    convert_to(((univ \ {AlgEquiv.refl}).Prod fun σ : L ≃ₐ[K] L => σ x) *
          ∏ σ : L ≃ₐ[K] L in {AlgEquiv.refl}, σ (x : L)) =
        _
    · rw [prod_singleton, AlgEquiv.coe_refl, id]
    · rw [prod_sdiff <| subset_univ _, ← norm_eq_prod_automorphisms, coe_algebra_map_norm]
#align ring_of_integers.is_unit_norm RingOfIntegers.isUnit_norm

/-- If `L/K` is a finite Galois extension of fields, then, for all `(x : 𝓞 L)` we have that
`x ∣ algebra_map (𝓞 K) (𝓞 L) (norm K x)`. -/
theorem dvd_norm [IsGalois K L] (x : 𝓞 L) : x ∣ algebraMap (𝓞 K) (𝓞 L) (norm K x) := by
  classical
    have hint : (∏ σ : L ≃ₐ[K] L in univ.erase AlgEquiv.refl, σ x) ∈ 𝓞 L :=
      Subalgebra.prod_mem _ fun σ hσ =>
        (mem_ring_of_integers _ _).2 (map_isIntegral σ (ring_of_integers.is_integral_coe x))
    refine' ⟨⟨_, hint⟩, Subtype.ext _⟩
    rw [coe_algebra_map_norm K x, norm_eq_prod_automorphisms]
    simp [← Finset.mul_prod_erase _ _ (mem_univ AlgEquiv.refl)]
#align ring_of_integers.dvd_norm RingOfIntegers.dvd_norm

end RingOfIntegers


def appendFin (x: α) (xs: List α) (i : Fin (xs.length)): Fin ((x :: xs).length) :=
  ⟨ Nat.succ i.val, Nat.succ_le_succ i.isLt ⟩

def shuffleIndex {n: Nat}(i : Fin (n + 2)): Fin (n + 2)  :=
match i.val with
  | 0 => ⟨1, Nat.succ_le_succ (Nat.zero_lt_succ _) ⟩
  | 1 => ⟨0, Nat.zero_lt_succ _⟩
  | _ + 2 => i

theorem shuffleCancelable {n: Nat} {i : Fin (n + 2)}: shuffleIndex (shuffleIndex i) = i := by
  cases i with | mk v lt =>
  cases v with
  | zero => simp [shuffleIndex]
  | succ v1 => cases v1 <;> simp [shuffleIndex]

theorem shuffledGet {x y : α} {xs : List α} (i : Fin (xs.length + 2)):
  (x :: y :: xs).get i = (y :: x :: xs).get (shuffleIndex i) := by
  cases i with | mk v vlt =>
  cases v
  . simp [List.get]
  . case succ v1 => cases v1 <;> simp [List.get]


example (h : a = b) : Nat.succ (a + 1) = Nat.succ (b + 1) := by
  congr

example (h : a = b) : Nat.succ (a + 1) = Nat.succ (b + 1) := by
  congr 1
  show a + 1 = b + 1
  rw [h]

def f (p : Prop) (a : Nat) (h : a > 0) [Decidable p] : Nat :=
  if p then
    a - 1
  else
    a + 1

example (h : a = b) : f True (a + 1) (by simp_arith) = f (0 = 0) (b + 1) (by simp_arith) := by
  congr
  decide

example (h : a = b) : f True (a + 1) (by simp_arith) = f (0 = 0) (b + 1) (by simp_arith) := by
  congr 1
  · decide
  · show a + 1 = b + 1
    rw [h]

example (h₁ : α = β) (h₂ : α = γ) (a : α) : HEq (cast h₁ a) (cast h₂ a) := by
  congr
  · subst h₁ h₂; rfl
  · subst h₁ h₂; apply heq_of_eq; rfl

example (f : Nat → Nat) (g : Nat → Nat) : f (g (x + y)) = f (g (y + x)) := by
  congr 2
  rw [Nat.add_comm]

example (p q r : Prop) (h : q = r) : (p → q) = (p → r) := by
  congr

example (p q r s : Prop) (h₁ : q = r) (h₂ : r = s) : (p → q) = (p → s) := by
  congr
  rw [h₁, h₂]


import .topologia
open set
open topological_space

variables {X : Type}

def is_basis [topological_space X] (I : set (set X)) : Prop :=
(∀ {B : set X}, B ∈ I → is_open B) ∧
(∀ U, ∀ x, is_open U → x ∈ U → ∃ B ∈ I, x ∈ B ∧ B ⊆ U)

class basis_condition (ℬ : set (set X)) :=
(univ : ∀ x : X, ∃ U ∈ ℬ, x ∈ U)
(filter : ∀ U V ∈ ℬ, ∀ x ∈ U ∩ V, ∃ W ∈ ℬ, x ∈ W ∧ W ⊆ U ∩ V)

lemma basis_condition_inter (ℬ : set (set X)) [basis_condition ℬ] {U V : set X}
(hU : U ∈ ℬ) (hV : V ∈ ℬ) : ∃ J ⊆ ℬ, U ∩ V = ⋃₀ J :=
begin
  apply sUnion_eq_of_pointwise.mpr (basis_condition.filter U V hU hV),
end

instance basis_has_basis_condition [topological_space X] {I : set (set X)} (h: is_basis I):
  basis_condition I := {
    univ :=
    begin
      intro x,
      simp,
      have hunivopen: is_open univ, by simp [univ_mem],
      have h2 := h.2 univ x hunivopen (mem_univ x),
      tauto,
    end
    ,
    filter :=
    begin
      intros U V hU hV x hx,
      have hUopen: is_open U := h.1 hU,
      have hVopen : is_open V := h.1 hV,
      have hUVopen : is_open (U ∩ V),
      {
        apply inter;
        assumption,
      },
      apply h.2;
      assumption,
    end
     }

/-- A basis defines a topological space, by decreeing open the arbitrary
  unions of basis elements --/
def generate_from_basis {ℬ : set (set X)} (h: basis_condition ℬ):
  topological_space X := {
  is_open := λ U, ∃ J ⊆ ℬ, U = ⋃₀ J,
  univ_mem :=
  begin
    use ℬ,
    split,
    { tauto },
    symmetry,
    have H := h.univ,
    finish, -- improve it?
  end,
  union :=
  begin
    intros Y hY,
    simp at hY,
    choose ι h using hY,
    have h1 := λ y hy, (h y hy).1,
    have h2 := λ y hy, (h y hy).2,
    clear h,
    norm_num,
    use ⋃ (y : set X) (hy : y ∈ Y), ι y hy,
    rw ←sUnion_bUnion h2,
    simp,
    intros x hx,
    simp at hx,
    obtain ⟨y, hy1, hy2⟩ := hx,
    specialize h1 y hy1,
    tauto,
  end,
  inter :=
  begin
    intros U V hU hV,
    obtain ⟨J, hJ1, hJ2⟩ := hU,
    obtain ⟨K, hK1, hK2⟩ := hV,
    have hUV : U ∩ V = ⋃₀ J ∩ ⋃₀ K,
    {
      exact congr (congr_arg has_inter.inter hJ2) hK2,
    },
    rw sUnion_inter_sUnion at hUV,
    suffices : ∀ x ∈ U ∩ V, ∃ W ∈ ℬ, x ∈ W ∧ W ⊆ U ∩ V,
    {
      use {W ∈ ℬ | W ⊆ U ∩ V},
      split,
      {
        intro W,
        simp,
        tauto,
      },
      {
        ext,
        split,
        {
          intro hx,
          specialize this x hx,
          obtain ⟨W, hW⟩ := this,
          simp at hW,
          simp,
          use W,
          tauto,
        },
        {
          intro hx,
          simp at hx,
          obtain ⟨a, ⟨ha1, ha2⟩, ha3⟩ := hx,
          have : a ⊆ U ∩ V,
          {
            norm_num,
            exact ha2,
          },
          tauto,
        }
      },
    },
    intros x hx,
    rw hUV at hx,
    simp at hx,
    obtain ⟨j, k, ⟨hjk1, hjk2⟩, hjk3, hjk4 ⟩ := hx,
    have hh : ∃ W ∈ ℬ, x ∈ W ∧ W ⊆ j ∩ k,
    {
      apply basis_condition.filter,
      tauto,
      tauto,
      finish,
    },
    obtain ⟨W, hxW, hWjk⟩ := hh,
    use W,
    have hWUV : W ⊆ U ∩ V,
    {
      have hjU : j ⊆ U,
      {
        rw hJ2,
        apply subset_sUnion_of_mem hjk1,
      },
      have hkV : k ⊆ V,
      {
        rw hK2,
        apply subset_sUnion_of_mem hjk2,
      },
      have hjkUV : j ∩ k ⊆ U ∩ V := inter_subset_inter hjU hkV,
      --replace hWjk := hWjk.2,
      tauto,
    },
    tauto,
  end
  }

lemma is_open_of_basis_set {ℬ : set (set X)} (h: basis_condition ℬ) {U : set X} (hU : U ∈ ℬ)
: @is_open X (generate_from_basis h) U :=
begin
  use {U},
  split,
  { exact singleton_subset_iff.mpr hU },
  { simp only [sUnion_singleton] }
end

@[simp]
lemma generate_from_basis_open_iff {ℬ : set (set X)} (h: basis_condition ℬ) {U : set X}
: @is_open X (generate_from_basis h) U ↔ ∃ J ⊆ ℬ, U = ⋃₀ J := by refl

lemma generate_from_basis_open_iff' {ℬ : set (set X)} (h: basis_condition ℬ) {U : set X}
: @is_open X (generate_from_basis h) U ↔ ∀ x ∈ U, ∃ B ∈ ℬ, x ∈ B ∧ B ⊆ U :=
begin
  rw generate_from_basis_open_iff,
  exact sUnion_eq_of_pointwise,
end

lemma generate_from_basis_simp {X : Type} {I : set (set X)} (h: basis_condition I) :
  generate_from I = generate_from_basis h :=
begin
  ext U,
  split,
  {
    intro hU,
    induction hU with V hV J hJ h W1 W2 hW1 hW2 hW1' hW2',
    {
      simp [univ_mem],
    },
    {
      fconstructor,
      use {V},
      finish,
    },
    {
      apply union,
      assumption,
    },
    {
      apply inter;
      assumption,
    }
  },
  {
    intro hU,
    induction hU with J hJ,
    simp at hJ,
    rw hJ.2,
    apply union,
    intros V hV,
    fconstructor,
    replace hJ := hJ.1,
    solve_by_elim,
  }
end

/-- A set ℬ satisfying the basis condition is a basis for the topology it generates -/
lemma prop22 (ℬ : set (set X)) (h : basis_condition ℬ) :
  @is_basis _ (generate_from_basis h) ℬ :=
begin
  unfold is_basis,
  split,
  {
    intros B hB,
    use {B},
    exact ⟨singleton_subset_iff.mpr hB, by simp⟩,
  },
  {
    intros U x hU hx,
    induction hU with J hJ,
    simp at hJ,
    have hxJ: ∃ Uj ∈ J, x ∈ Uj,
    {
      rw [hJ.2, mem_sUnion] at hx,
      assumption,
    },
    obtain ⟨Uj, hUjJ, hUjx⟩ := hxJ,
    use Uj,
    have hUjI : Uj ∈ ℬ,
    {
      replace hJ := hJ.1,
      tauto,
    },
    have Uj_in_U : Uj ⊆ U,
    {
      rw hJ.2,
      apply subset_sUnion_of_subset J Uj _ hUjJ,
      tauto,
    },
    exact ⟨hUjI,hUjx,Uj_in_U⟩,
  }
end

-- Axiomes "countability"
--


import algebra.module.basic
import linear_algebra.affine_space.affine_equiv
import algebra.direct_sum.basic
import algebra.direct_sum.module
import tactic.linarith
import .lin2kcoord

open_locale direct_sum
universes u

-- Let K be any non-empty field
variables
  (K : Type u)  -- scalar type
  [inhabited K] -- non-empty
  [field K]     -- forms a field

-- Let n by a positive integer dimension
  (n : ℕ)       -- dimension
  (ng1 : n > 1) -- dim is > 1

/-
- set:      https://leanprover-community.github.io/mathlib_docs/init/data/set.html#set
- fin:      https://leanprover-community.github.io/mathlib_docs/init/data/fin/basic.html#fin
- finset:   https://leanprover-community.github.io/mathlib_docs/data/finset/basic.html
- multiset: https://leanprover-community.github.io/mathlib_docs/data/multiset/basic.html#multiset
            -- represented as quotient of a list by permutation (an unordered with duplicates possible)
-/

/-
Next we introduce notation for the direct sum of
a set of types with an associated module structure
(we actually require additive commutative monoids).
⨁ ι, β ι is now available notation for n-ary direct
sum of a collection of additive commutitive monoids:
types, β i (indexed by ι), with proofs that each is
equipped with this structure.

def direct_sum
  (ι : Type v)
  (β : ι → Type w)
  [Π (i : ι), add_comm_monoid (β i)] :
  Type (max v w)

This is all from mathlib.
-/

/-
We use this function to compute direct sums of our
2-D K-vector spaces. The idea is that we'll use these
n-tuples of K × K values to represent points and vectors
in affine spaces.

Such a function takes a natural number, n, and returns
the direct sum (think product type), informally (K × K)ⁿ
where for each ι in the index set, the corresponding type
is constant K × K.

lin2k_sum :
  Π (K : Type u_1)
  [_inst_1 : inhabited K]
  [_inst_2 : field K],
  ℕ →
  Type u_1

A direct product type combines a family of types indexed by ι
-/

/-
Given a finite set of 2D modules over K, indexed
by i = [0,..,n-1] (finite nats), return the direct
sum, ⨁(i : fin n), (λ j, K × K) i. Read this as
the direct sum over i of a set of types indexed by
ι, where each type is just K × K. The result can
be thought of as (K × K)ⁿ, represented notationally
as Π₀ (i : fin n), K × K.
-/

#check ⨁(i : fin n), (λ (j : fin n), K × K) i
#reduce ⨁(i : fin n), (λ (j : fin n), K × K) i
-- Π₀ (i : fin n), K × K (see this notation)

-- we define (lin2k_sum K n) to be this type, (K × K)ⁿ
-- note: ∀ j in i, (λ j, K × K) returns the type, K × K
abbreviation lin2k_sum := ⨁(i : fin n), (λ j, K × K) i

#check @lin2k_sum
#reduce @lin2k_sum              -- returns Π₀ (i : fin n), K × K
#reduce lin2k_sum ℚ 3           -- direct sum of 3 copies of Q × Q
#reduce Π₀ (i : fin 3), ℚ × ℚ   -- alternative notation, (ℚ × ℚ)³

/-
mk β s x is the element of ⨁ i, β i that is zero outside s and
has coefficient x i for i in s
-/
#check @direct_sum.mk
/-
direct_sum.mk :
  Π {ι : Type v} [dec_ι : decidable_eq ι]
    (β : ι → Type w) [_inst_1 : Π (i : ι), add_comm_monoid (β i)]
    (s : finset ι),
      (Π (i : ↥↑s), β i.val) →+
      ⨁ (i : ι), β i

M →+ N is the type of functions M → N that preserve the
add_zero_class structure. add_monoid_hom is also used for group
homomorphisms. When possible, instead of parametrizing results over
(f : M →+ N), parametrize over (F : Type*) [add_monoid_hom_class F M N]
(f : F).
-/

/-
Finally, we imbuing each such  space with the structure of
a module. This is  easy and automated in Lean, by typeclass
instantiation. A direct sum of K × K-modules with scalars of
type K is thus proven also to be a module with scalars in K.
-/
instance : module K (lin2k_sum K n) := by apply_instance

/-
How do I make a finset?
-/
def y : multiset (fin 1) := { ⟨ 0, sorry⟩ }

def x : finset (fin 1) := ⟨ y, sorry ⟩

-- requires a multiset of fin 1 values

def myβ := λ (i : fin 1), ℚ × ℚ
def foo : lin2k_sum ℚ 1 :=
  @direct_sum.mk
    (fin 1)       -- index set
    _             -- cert decidable equality
    myβ           -- indexed family of types
    sorry             -- cert all are modules
    x
    _            -- finite set of indices

#check @direct_sum.mk (fin 2) _ my_β
/-
Π [_inst_1 : Π (i : fin 2), add_comm_monoid (my_β i)]
  (s : finset (fin 2)),
(Π (i : ↥↑s), my_β i.val) →+ ⨁ (i : fin 2), my_β i

So i here has to be an element of finset (fin 2), which
is to say a pair, ⟨val, val < n ⟩, with i.val an element
of fin 2 (see above), and my_β i.val the type, ℚ × ℚ in
this case.

How do I make a value of type fin 2?
How do I make a value of type finset (fin 2)?
-/

example : fin 2 := ⟨ 0, by norm_num ⟩ -- good
example : fin 2 := ⟨ 1, by norm_num ⟩ -- good
example : fin 2 := ⟨ 2, by norm_num ⟩ -- bad

/-
has_coe_t (finset α) (set α): Convert finset to set (up-arrow)
has_coe_to_sort (finset α) (Type u): Coerce finset to subtype (bar-up)
-/

/-
Move this us an explain the overall program.

The idea is to use (n+1)-sums of these pairs as parametric
representations of a n-dimension affine points and vectors.
The approach is a slight adaptation of an otherwise standard
homogeneous coordinate representation, with the constraints
that the first component of a representation of an affine
vector has coordinate (0 : K), and the first component of a
representation of an affine point has coordinate (1 : K).

For example, a 1-d affine vector, v, with coordinate tuple,
(k), would be represented by ⟨0, k⟩ : ℚ × ℚ. An affine point,
p, with coordinate k would be represented as ⟨1, k⟩ : ℚ × ℚ.
More generally, Lean libraries overload these angle brackets
to define structures (product types) by giving their field
values.
-/

example : ℚ × ℚ := ⟨ 1/2, -2/3 ⟩

/-
It seems that if lift 1-tuples of these pairs to be 1-direct
sums, then from there we work in terms of build bigger and
bigger direct sums. Do we need to do this? Maybe not. We do
have module structures all the way down to the bare pair, as
well as in the direct sum, spaces. Hold off on this. Think
it through.
-/

/-
********************************************************
-/

/-
Lean mathlib background:

The type, fin n, is the subtype of all finite ℕ values
less than any given ℕ, n: { i : ℕ | i < n}. A value, v,
of this type is a pair: val, the naturan number value;
and property, a proof certifying statically that val < n.

structure fin (n : ℕ) :=
(val : nat)
(property : val < n)
-/

/-
The type `finset α` is the type of finite sets of values
of type `α`, implemented as a multiset, in turn a list up
to permutation, certified as having no duplicate elements.
-/
#check finset ℕ
/-
structure finset (α : Type*) :=
(val : multiset α)
(nodup : nodup val)
-/

/-
The type, finset (fin n), is thus the type of
finite sets of finite natural number values, each
in turn represented as a value/proof pair. The
proof again certifies that val < n.

Finsets provide basic foundation in Lean for
defining finite sums and products over types:

    ∑ i in (s : finset α), f i;
    ∏ i in (s : finset α), f i.

Lean refers to these operations as big_operators.
More can be found in algebra.big_operators.basic.

Sullivan: Using?
-/

/-
Finally fintype α is a typeclass an instance of
which consists of a universal/complete finset α
(containing every term of α). In Lean this value
is called univ. See data.fintype.basic.
-/
#check fintype ℕ

/-Having a fintype α instance shows α is finite:
there are only finitely many distinct elements of
type α. The evidence of this, in turn, is a finset,
elems (a list up to permutation without duplicates),
together with a proof that everything of type α is
in that list.
-/

/-
We now define finset_fin_N as a function that
takes n : ℕ, and returns the universal finset of
finite natural numbers up to n. Recall that each
finite natural number is turn being a pair of a
value and proof that it's within range. The term
finset.univ (from mathlib) provides this value.
-/

def finset_fin_N : finset (fin n) := finset.univ
/-
finset_fin_N : Π (n : ℕ), finset (fin n),
where the return value is the universal finset
of value of fin n.
-/
#eval finset_fin_N 3  -- Lean pretty prints it

/-
We define set_fin_N as a function that takes a
natural number, n, and returns a complete set
of the finite natural number (value-proof pairs)
in the range [0,..,n-1].
-/

/-
Given n, return the set of finite natural numbers
[0, n-1]. Implemented by applying finset_fin_N to
n to obtain a universal finset of finite ℕ values,
[0,..,n-1], and then coercing it to a set of such
values. Lean's mathlib provides this coercion. A
set in Lean is define by its membership predicate.
-/
def set_fin_N := (((finset_fin_N n)) : set (fin n))
#check @set_fin_N
/-
set_fin_N : Π (n : ℕ), set (fin n) -- complete set
-/
#reduce set_fin_N 2   -- the set membership predicate

/-
λ (x : fin 2), x = ⟨0, _⟩ ∨ x = ⟨1, _⟩ ∨ false
-/

/-
Given a function from [0,..,n-1] to K × K, which is to
say, an tuple of K × K pairs indexed by n, return the
the n-dimensional direct product of (K × K) spaces to
which it belongs. (This seems to be over-complicated.)
-/
/-
Type check expression used to define this function
-/
#check Π (i : set_fin_N n), ( λ j, K × K ) i.val
/-
Π (i : ↥(set_fin_N n)), (λ (j : fin n), K × K) i.val : Type u
That is, given (set_fin_N n), return sum (K × K)ⁿ
-/
#check (Π (i : set_fin_N n), ( λ j, K × K ) i.val)
#check (set_fin_N n)
#check ↥(set_fin_N n)
#reduce  (↥(set_fin_N 2))
/-
{x // x = ⟨0, _⟩ ∨ x = ⟨1, _⟩ ∨ false}
-/

/-
--  a function that takes i,
-- a complete set of finite
-- natural numbers, 0..n-1,
-- and that returns K × K
-/
#check Π (i : set_fin_N n), ( λ j, K × K ) i.val
#check (Π (i : set_fin_N n), ( λ j, K × K ) i.val)
#check (Π (i : set_fin_N 2), ( λ j : fin 2, K × K ) i.val)

/-
Given an indexed tuple of K × K pairs, return it as
an element of the direct sum of the K × K spaces.
-/
def mk_linnk : (fin n → K × K) → lin2k_sum K n :=
λ v,
  /-
  Let val be a function that takes a complete set, i,
  of finite natural numbers from [0, n-1], to the result
  of applying the constant function
  -/
  let foo
    :
      Π (i : set_fin_N n),
        --  a function that takes i,
        -- a complete set of finite
        -- natural numbers, 0..n-1,
        -- and that returns K × K
        ( λ j : fin n, K × K ) i.val
      :=
         -- subtype.val : Π {α : Type} {p : α → Prop}, subtype p → α
         -- Given index h, return the h'th pair in v
      λ h, v h
    in
      -- Direct sum of a family of R modules indexed by ι.
    direct_sum.lmk K _ _ _ foo

def x : (fin 1 → ℚ × ℚ) :=
begin
assume i,
exact ⟨ 7, 7 ⟩,
end

#check x

#check lin2k_sum K n

#check mk_linnk ℚ 1 x
-- this is a value of type lin2k_sum ℚ 2, but
-- it's independent of the argument value
-- in other words, this is useless and (lemma)
-- can be replaced by lin2k_sum ℚ 2

#check ((mk_linnk ℚ 1 x) : lin2k_sum ℚ 1)
#reduce (mk_linnk ℚ 1 x)

-- def mk_linnk : (fin n → K × K) → lin2k_sum K n :=
-- λv,
--   /-
--   What is val? Given i as a subset of indices 0..n,
--   -/
--   let val : Π (i : (↑(finset_fin_N n) : set (fin n))), (λi, K × K) i.val :=
--   /-
--   val is the function that takes
--   -/
--     λi, v i in
--       -- Create direct sum of a family M of R modules indexed over ι.
--       direct_sum.lmk K _ _ _ val

/-
def direct_sum.lmk
  (R : Type u) [semiring R]
  (ι : Type v) [dec_ι : decidable_eq ι]
  (M : ι → Type w) [Π (i : ι), add_comm_monoid (M i)] [Π (i : ι), module R (M i)]
  (s : finset ι) :
  (Π (i : ↥↑s), M i.val) →ₗ[R] direct_sum ι (λ (i : ι), M i)
-/

def mk_linnk' : (fin n → K × K) → lin2k_sum K n :=
λv,
  let val :
    Π (i : (↑(finset_fin_N n) : set (fin n))), (λ j , K × K) i.val :=
      λ j, v j in
        direct_sum.lmk K _ _ _ val


inductive pos_num : Type
| one  : pos_num
| bit0 : pos_num → pos_num
| bit1 : pos_num → pos_num

namespace pos_num

instance : has_one pos_num := ⟨pos_num.one⟩

def succ : pos_num → pos_num
| one := bit0 one
| (bit0 x) := bit1 x
| (bit1 x) := bit0 (succ x)

protected def add : pos_num → pos_num → pos_num
| one        b        := succ b
| a        one        := succ a
| (bit0 a) (bit0 b) := bit0 (add a b)
| (bit1 a) (bit1 b) := bit0 (succ (add a b))
| (bit0 a) (bit1 b) := bit1 (add a b)
| (bit1 a) (bit0 b) := bit1 (add a b)

instance : has_add pos_num := ⟨pos_num.add⟩

-- The equation compiler splits up the definition of add above
-- into the nine cases anyway, so the first two below aren't refl.
lemma succ_eq_add_one (a : pos_num) : succ a = a + 1 := by cases a;refl
lemma succ_eq_one_add (a : pos_num) : succ a = 1 + a := by cases a;refl
lemma one_add_eq_add_one (a : pos_num) : 1 + a = a + 1 := by cases a;refl

-- this is the base case for add_assoc'
theorem add_assoc'' (a : pos_num) : a + one + one = a + (one + one) :=
begin
  cases a with d d,
  { refl},
  { show bit0 (succ d) = bit0 (d + 1),
    rw succ_eq_add_one},
  { show bit1 (succ d) = bit1 (d + 1),
    rw succ_eq_add_one,
  }
end

-- this is the base case for add_assoc
theorem add_assoc' (a b : pos_num) : a + b + 1 = a + (b + 1) :=
begin
  revert a,
  induction b with d Hd d Hd,
  { exact add_assoc''}, -- base case
  { intro a, induction a with c Hc c Hc;refl },
  { intro a, induction a with c Hc c Hc,
    { refl},
    { show bit0 (succ (c + d)) = bit0 (c + succ d),
      apply congr_arg,
      rw succ_eq_add_one,
      rw succ_eq_add_one,
      exact Hd c, -- I used the inductive hypo
    },
    { show bit1 (succ (c + d)) = bit1 (c + succ d),
      apply congr_arg,
      rw succ_eq_add_one,
      rw succ_eq_add_one,
      exact Hd c, -- I used the inductive hypo
    }
  }
end

lemma add_succ (a b : pos_num) : succ (a + b) = a + succ b :=
by rw [succ_eq_add_one,succ_eq_add_one,add_assoc']

theorem add_assoc (a b c : pos_num) : (a + b) + c = a + (b + c) :=
begin
  revert b, revert a,
  induction c with d Hd d Hd,
  { exact add_assoc'}, -- base case
  { intros a b,cases a;cases b;try {refl},
    -- 7 cases left
      show bit0 (1 + d) = bit0 (succ d),
        rw succ_eq_one_add,
      show bit0 (succ b + d) = bit0 (succ (b + d)),
        rw [succ_eq_one_add, succ_eq_one_add, Hd],
      show bit0 ((a + b) + d) = bit0 (a + (b + d)),
        rw Hd,
      show bit1 ((a + b) + d) = bit1 (a + (b + d)),
        rw Hd,
      show bit0 (succ a + d) = bit0 (succ (a + d)),
        rw [succ_eq_one_add, succ_eq_one_add, Hd],
      show bit1 ((a + b) + d) = bit1 (a + (b + d)),
        rw Hd,
      show bit0 (succ (a + b) + d) = bit0 (succ (a + (b + d))),
        rw [succ_eq_one_add, succ_eq_one_add, Hd, Hd],
  },
  { intros a b, cases a; cases b; try {refl},
    -- 8 cases this time
      show bit1 (1 + d) = bit1 (succ d),
        rw succ_eq_one_add,
      show bit1 (succ b + d) = bit1 (succ (b + d)),
        rw [succ_eq_one_add, succ_eq_one_add, Hd],
      show bit1 ((a + b) + d) = bit1 (a + (b + d)),
        rw Hd,
      show bit0 (succ (a + d)) = bit0 (a + succ d),
        rw [succ_eq_one_add, succ_eq_one_add, ←Hd, ←Hd],
        rw [←succ_eq_one_add,succ_eq_add_one],
      show bit0 (succ ((a + b) + d)) = bit0 (a + succ (b + d)),
        rw [succ_eq_one_add, succ_eq_one_add, ←Hd, ←Hd, ←Hd],
        rw [one_add_eq_add_one,one_add_eq_add_one,add_assoc'],
      show bit1 (succ a + d) = bit1 (a + succ d),
        rw [←add_succ,succ_eq_one_add,succ_eq_one_add,Hd],
      show bit0 (succ ((a + b) + d)) = bit0 (succ (a + (b + d))),
        rw Hd,
      show bit1 (succ (a + b) + d) = bit1 (a + succ (b + d)),
        rw [succ_eq_one_add, succ_eq_one_add, ←Hd, ←Hd],
        rw [one_add_eq_add_one,one_add_eq_add_one,add_assoc'],
  }
end

end pos_num


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

import algebra.module.equiv
import data.finsupp.basic
import group_theory.free_abelian_group
import group_theory.is_free_group
import linear_algebra.dimension

/-!
# Isomorphism between `free_abelian_group X` and `X →₀ ℤ`

In this file we construct the canonical isomorphism between `free_abelian_group X` and `X →₀ ℤ`.
We use this to transport the notion of `support` from `finsupp` to `free_abelian_group`.

## Main declarations

- `free_abelian_group.equiv_finsupp`: group isomorphism between `free_abelian_group X` and `X →₀ ℤ`
- `free_abelian_group.coeff`: the multiplicity of `x : X` in `a : free_abelian_group X`
- `free_abelian_group.support`: the finset of `x : X` that occur in `a : free_abelian_group X`

-/

noncomputable theory

open_locale big_operators

variables {X : Type*}

/-- The group homomorphism `free_abelian_group X →+ (X →₀ ℤ)`. -/
def free_abelian_group.to_finsupp : free_abelian_group X →+ (X →₀ ℤ) :=
free_abelian_group.lift $ λ x, finsupp.single x (1 : ℤ)

/-- The group homomorphism `(X →₀ ℤ) →+ free_abelian_group X`. -/
def finsupp.to_free_abelian_group : (X →₀ ℤ) →+ free_abelian_group X :=
finsupp.lift_add_hom $ λ x, (smul_add_hom ℤ (free_abelian_group X)).flip (free_abelian_group.of x)

open finsupp free_abelian_group

@[simp] lemma finsupp.to_free_abelian_group_comp_single_add_hom (x : X) :
  finsupp.to_free_abelian_group.comp (finsupp.single_add_hom x) =
    (smul_add_hom ℤ (free_abelian_group X)).flip (of x) :=
begin
  ext,
  simp only [add_monoid_hom.coe_comp, finsupp.single_add_hom_apply, function.comp_app,
    one_smul, to_free_abelian_group, finsupp.lift_add_hom_apply_single]
end

@[simp] lemma free_abelian_group.to_finsupp_comp_to_free_abelian_group :
  to_finsupp.comp to_free_abelian_group = add_monoid_hom.id (X →₀ ℤ) :=
begin
  ext x y, simp only [add_monoid_hom.id_comp],
  rw [add_monoid_hom.comp_assoc, finsupp.to_free_abelian_group_comp_single_add_hom],
  simp only [to_finsupp, add_monoid_hom.coe_comp, finsupp.single_add_hom_apply,
    function.comp_app, one_smul, lift.of, add_monoid_hom.flip_apply,
    smul_add_hom_apply, add_monoid_hom.id_apply],
end

@[simp] lemma finsupp.to_free_abelian_group_comp_to_finsupp :
  to_free_abelian_group.comp to_finsupp = add_monoid_hom.id (free_abelian_group X) :=
begin
  ext,
  rw [to_free_abelian_group, to_finsupp, add_monoid_hom.comp_apply, lift.of,
    lift_add_hom_apply_single, add_monoid_hom.flip_apply, smul_add_hom_apply, one_smul,
    add_monoid_hom.id_apply],
end

@[simp] lemma finsupp.to_free_abelian_group_to_finsupp {X} (x : free_abelian_group X) :
  x.to_finsupp.to_free_abelian_group = x :=
by rw [← add_monoid_hom.comp_apply, finsupp.to_free_abelian_group_comp_to_finsupp,
  add_monoid_hom.id_apply]

namespace free_abelian_group
open finsupp

variable {X}

@[simp]

@[simp] lemma to_finsupp_to_free_abelian_group (f : X →₀ ℤ) :
  f.to_free_abelian_group.to_finsupp = f :=
by rw [← add_monoid_hom.comp_apply, to_finsupp_comp_to_free_abelian_group, add_monoid_hom.id_apply]

variable (X)

/-- The additive equivalence between `free_abelian_group X` and `(X →₀ ℤ)`. -/
@[simps]
def equiv_finsupp : free_abelian_group X ≃+ (X →₀ ℤ) :=
{ to_fun := to_finsupp,
  inv_fun := to_free_abelian_group,
  left_inv := to_free_abelian_group_to_finsupp,
  right_inv := to_finsupp_to_free_abelian_group,
  map_add' := to_finsupp.map_add }

/-- `A` is a basis of the ℤ-module `free_abelian_group A`. -/
noncomputable def basis (α : Type*) :
  basis α ℤ (free_abelian_group α) :=
⟨(free_abelian_group.equiv_finsupp α).to_int_linear_equiv ⟩

/-- Isomorphic free ablian groups (as modules) have equivalent bases. -/
def equiv.of_free_abelian_group_linear_equiv {α β : Type*}
  (e : free_abelian_group α ≃ₗ[ℤ] free_abelian_group β) :
  α ≃ β :=
let t : _root_.basis α ℤ (free_abelian_group β) := (free_abelian_group.basis α).map e
  in t.index_equiv $ free_abelian_group.basis _

/-- Isomorphic free abelian groups (as additive groups) have equivalent bases. -/
def equiv.of_free_abelian_group_equiv {α β : Type*}
  (e : free_abelian_group α ≃+ free_abelian_group β) :
  α ≃ β :=
equiv.of_free_abelian_group_linear_equiv e.to_int_linear_equiv

/-- Isomorphic free groups have equivalent bases. -/
def equiv.of_free_group_equiv {α β : Type*}
  (e : free_group α ≃* free_group β) :
  α ≃ β :=
equiv.of_free_abelian_group_equiv e.abelianization_congr.to_additive

open is_free_group
/-- Isomorphic free groups have equivalent bases (`is_free_group` variant`). -/
def equiv.of_is_free_group_equiv {G H : Type*}
  [group G] [group H] [is_free_group G] [is_free_group H]
  (e : G ≃* H) :
  generators G ≃ generators H :=
equiv.of_free_group_equiv $
  mul_equiv.trans ((to_free_group G).symm) $
  mul_equiv.trans e $
  to_free_group H

variable {X}

/-- `coeff x` is the additive group homomorphism `free_abelian_group X →+ ℤ`
that sends `a` to the multiplicity of `x : X` in `a`. -/
def coeff (x : X) : free_abelian_group X →+ ℤ :=
(finsupp.apply_add_hom x).comp to_finsupp

/-- `support a` for `a : free_abelian_group X` is the finite set of `x : X`
that occur in the formal sum `a`. -/
def support (a : free_abelian_group X) : finset X :=
a.to_finsupp.support

lemma mem_support_iff (x : X) (a : free_abelian_group X) :
  x ∈ a.support ↔ coeff x a ≠ 0 :=
by { rw [support, finsupp.mem_support_iff], exact iff.rfl }

lemma not_mem_support_iff (x : X) (a : free_abelian_group X) :
  x ∉ a.support ↔ coeff x a = 0 :=
by { rw [support, finsupp.not_mem_support_iff], exact iff.rfl }

@[simp] lemma support_zero : support (0 : free_abelian_group X) = ∅ :=
by simp only [support, finsupp.support_zero, add_monoid_hom.map_zero]

@[simp] lemma support_of (x : X) : support (of x) = {x} :=
by simp only [support, to_finsupp_of, finsupp.support_single_ne_zero (one_ne_zero)]

@[simp] lemma support_neg (a : free_abelian_group X) : support (-a) = support a :=
by simp only [support, add_monoid_hom.map_neg, finsupp.support_neg]

@[simp] lemma support_zsmul (k : ℤ) (h : k ≠ 0) (a : free_abelian_group X) :
  support (k • a) = support a :=
begin
  ext x,
  simp only [mem_support_iff, add_monoid_hom.map_zsmul],
  simp only [h, zsmul_int_int, false_or, ne.def, mul_eq_zero]
end

@[simp] lemma support_nsmul (k : ℕ) (h : k ≠ 0) (a : free_abelian_group X) :
  support (k • a) = support a :=
by { apply support_zsmul k _ a, exact_mod_cast h }

open_locale classical

lemma support_add (a b : free_abelian_group X) : (support (a + b)) ⊆ a.support ∪ b.support :=
begin
  simp only [support, add_monoid_hom.map_add],
  apply finsupp.support_add
end

end free_abelian_group


import tactic

example {r s : ℕ} : {n | n ≤ r} ⊆ {n | n ≤ r + s} :=
begin
  intros n hn,
  simp at hn,
  apply le_trans hn,
  exact nat.le.intro rfl,
end