/-
Copyright (c) 2018 Sander Dahmen, Johannes Hölzl, Robert Y. Lewis. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sander Dahmen, Johannes Hölzl, Robert Y. Lewis

"On large subsets of 𝔽ⁿ_q with no three-term arithmetic progression"
by J. S. Ellenberg and D. Gijswijt

This file establishes the bound on the cardinality of a generalized cap set.
It corresponds to sections 10-12 of our blueprint.
-/

import library section_9

open mv_polynomial

section

parameters
  {α : Type} [discrete_field α] [fintype α] -- 𝔽_q: q is cardinality of `α`
  (n : ℕ)

def q : ℕ := fintype.card α

lemma q_pos : q > 0 := fintype.card_pos_iff.2 ⟨1⟩

lemma one_le_q : 1 ≤ q := q_pos

lemma one_le_q_real : 1 ≤ (q : ℚ) :=
suffices ↑(1 : ℕ) ≤ (q : ℚ), by simpa,
nat.cast_le.2 one_le_q

/-- Polynomials in α - seen as a vector space -/
@[reducible] def S := mv_polynomial (fin n) α

instance S.module : module α S := by dunfold S; apply_instance
instance S.vector_space : vector_space α S := { .. S.module }

instance fin_q_has_zero : has_zero (fin q) := ⟨⟨0, one_le_q⟩⟩

def M : finset (mv_polynomial (fin n) α):=
(finset.univ.image $ λ f : fin n →₀ fin q, f.map_range fin.val rfl).image (λd : fin n →₀ ℕ, monomial d (1:α))

lemma linear_independent_M : linear_independent α (↑M : set (mv_polynomial (fin n) α)) :=
(mv_polynomial.is_basis_monomials _ _).1.mono $ λ p hp,
  let ⟨s, _, hsp⟩ := finset.mem_image.1 hp in
  set.mem_range.2 ⟨s, hsp⟩

def fin_map_to_mono_map (f : fin n → fin q) : fin n →₀ ℕ := λ k, (f k).val
def fmtmm : has_coe (fin n → fin q) (fin n →₀ ℕ) := ⟨fin_map_to_mono_map⟩
local attribute [instance] fmtmm

lemma monomial_mem_M (f : fin n → fin q) : monomial ↑f (1 : α) ∈ M :=
begin
  apply finset.mem_image.2,
  refine ⟨f, _, rfl⟩,
  apply finset.mem_image.2,
  use [f, finset.mem_univ _], ext, refl
end

@[simp] lemma coe_fin_eq_coe_fin (f g : (fin n → fin q)) : (f : fin n →₀ ℕ) = g ↔ f = g :=
begin
  split,
  { intro h,
    ext,
    apply (fin.ext_iff _ _).2,
    apply finsupp.congr_fun h },
  { intro h, rw h }
end

lemma M_spec {x} (hx : x ∈ M) : ∃ s : fin n → fin q, monomial ↑s 1 = x :=
begin
  rcases finset.mem_image.1 hx with ⟨d, ⟨hd, hdmon⟩⟩,
  have h : ∀ k, d k < q,
  { intro k,
    rcases finset.mem_image.1 hd with ⟨l, ⟨hl, hdl⟩⟩,
    have hdl' : (finsupp.map_range fin.val (_) l : fin n → ℕ) k = d k, {rw hdl},
    rw finsupp.map_range_apply at hdl', rw ←hdl', apply fin.is_lt },
  use λ k, ⟨d k, h k⟩,
  rw ←hdmon, congr, ext, refl
end

noncomputable def monom_exps (p : (mv_polynomial (fin n) α)) : (fin n → fin q) :=
if hp : p ∈ M then classical.some (M_spec hp) else 0

lemma monom_exps_def {x} (hx : x ∈ M) : monomial ↑(monom_exps x) 1 = x :=
by simpa [hx, monom_exps] using (classical.some_spec (M_spec hx))

lemma monom_exps_inj {x y} (hx : x ∈ M) (hy : y ∈ M) (hxy : monom_exps x = monom_exps y) : x = y :=
have h1 : _, from monom_exps_def hx, have h2 : _, from monom_exps_def hy,
by rw [←h1, ←h2, hxy]

@[simp] lemma monom_exps_monomial (f : fin n → fin q) : monom_exps (monomial f 1) = f :=
by simpa using finsupp.eq_of_single_eq_left (monom_exps_def (monomial_mem_M _)) one_ne_zero

lemma monom_exps_surj (f : fin n → fin q) : ∃ x, x ∈ M ∧ monom_exps x = f :=
⟨monomial f 1, monomial_mem_M _, monom_exps_monomial _⟩

-- do more generally
lemma monomial_total_degree (f : fin n →₀ ℕ) :
  mv_polynomial.total_degree (monomial f (1 : α)) = finsupp.sum f (λ _, id) :=
suffices finset.sup {f} (λ (s : fin n →₀ ℕ), finsupp.sum s (λ _, id)) =
    finsupp.sum f (λ _, id), by simpa [monomial, finsupp.single, mv_polynomial.total_degree],
by rw finset.sup_singleton

lemma mv_polynomial_eval_eq (p : mv_polynomial (fin n) α) (a : fin n → α) :
  p.eval a = p.sum (λi c, c * (i.to_multiset.map $ λn, a n).prod) :=
begin
  rw [eval, eval₂],
  congr, funext i c, congr,
  rw [finsupp.to_multiset_map, finsupp.prod_to_multiset,
    finsupp.prod_map_domain_index pow_zero pow_add]
end

lemma eval_add (p : S) (a b : fin n → α) :
  p.eval (a + b) = p.sum (λe x, multiset.sum $
    e.to_multiset.diagonal.map $ λp, x * ((p.1.map $ λi, a i).prod * (p.2.map $ λi, b i).prod)) :=
by simp [mv_polynomial_eval_eq, multiset.sum_map_mul_left, multiset.prod_map_add]

section

def M' (d : ℚ) := M.filter (λ m, d ≥ mv_polynomial.total_degree m)
end

lemma linear_independent_M' (d) : linear_independent α (↑(M' d) : set (mv_polynomial (fin n) α)) :=
linear_independent_M.mono $ λ x hx, M.filter_subset hx

lemma mem_M_of_mem_M' {x d} (hx : x ∈ M' d) : x ∈ M := (finset.mem_filter.1 hx).1

def S' (d : ℚ) := submodule.span α (↑(M' d) : set (mv_polynomial (fin n) α))

lemma mem_S'_of_mem_M' {d : ℚ} {p} (hp : p ∈ M' d) : p ∈ S' d :=
submodule.mem_span.2 $ λ t hmt, hmt hp

lemma S'_le_restrict_q (d : ℚ) : S' d ≤ mv_polynomial.restrict_degree _ _ (q - 1) :=
submodule.span_le.2 $ assume p hp,
begin
  have : p ∈ M := mem_M_of_mem_M' hp,
  rcases M_spec this with ⟨s, rfl⟩,
  assume s hs n,
  rw [finset.mem_coe, mv_polynomial.monomial,
    finsupp.support_single_ne_zero zero_ne_one.symm, finset.singleton_eq_singleton,
    finset.mem_singleton] at hs,
  subst hs,
  exact (nat.le_sub_right_iff_add_le one_le_q).2 (s n).2
end

lemma le_floor_to_nat (n : ℕ) (d : ℚ) (hd : 0 ≤ d) :
  n ≤ ⌊d⌋.to_nat ↔ (n : ℚ) ≤ d :=
begin
  rw [int.to_nat_le_iff, le_floor, ← coe_coe],
  rwa [le_floor]
end

lemma S'_le_restrict_d (d : ℚ) (hd : 0 ≤ d):
  S' d ≤ mv_polynomial.restrict_total_degree _ _ (⌊d⌋).to_nat :=
submodule.span_le.2 $ assume p hp, (mv_polynomial.mem_restrict_total_degree _ _ _ _).2
begin
  have hp := (finset.mem_filter.1 hp).2,
  rwa [le_floor_to_nat _ _ hd]
end

parameter (α)
noncomputable def m (d : ℚ) : ℕ := (vector_space.dim α (S' d)).to_nat
parameter {α}

lemma M'_card (d : ℚ) : (M' d).card = m d :=
by rw [m, S', dim_span (linear_independent_M' _), ←cardinal.finset_card, cardinal.to_nat_coe]

section Propostion_11_1
parameters {d : ℚ} (d_nonneg : 0 ≤ d) --(d_upper : d ≤ (fintype.card α) * n)

set_option class.instance_max_depth 200

parameters
  {p : S}
  (A : finset (fin n → α))
  (a b c : α)
  (hp : p ∈ S' d)
  (habc : a + b + c = 0)
  (h : ∀x:fin n → α, x∈A → ∀y∈A, x ≠ y → mv_polynomial.eval (a • x + b • y) p = (0:α))

include habc
lemma a_plus_b : a + b = -c :=
calc a + b = a + b + c + (-c) : by simp
       ... = -c : by simp [habc]
omit habc

include hp

section
open multiset finsupp

/-- The coefficients `(m₁, m₂) ⊢> ∑ c*X^e ∈ p | m₁ + m₂ = e, c * a ^ |m₁| * b ^ |m₂|` -/
def coeff (a b : α) : (multiset (fin n) × multiset (fin n)) →₀ α :=
p.sum $ λe c, sum $ e.to_multiset.diagonal.map $ λm, single m (c * a ^ card m.1 * b ^ card m.2)

/-- `π x m := Πi∈m, x i`  -/
def π (x : fin n → α) (m : multiset (fin n)) : α := prod $ m.map $ λi, x i

def split_left (a b : α) : (multiset (fin n) × multiset (fin n)) →₀ α :=
(coeff a b).filter $ λp, d / 2 > p.1.card

def split_right (a b : α) : (multiset (fin n) × multiset (fin n)) →₀ α :=
((coeff a b).filter (λp:multiset (fin n)×multiset (fin n), ¬ d / 2 > p.1.card)).map_domain prod.swap

lemma mem_coeff_support {a b : α} (m₀ m₁ : multiset (fin n)) (h : (m₀, m₁) ∈ (coeff a b).support) :
  of_multiset (m₀ + m₁) ∈ p.support :=
begin
  rcases finsupp.mem_support_finset_sum _ h with ⟨f, hf, hfm⟩,
  rcases finsupp.mem_support_multiset_sum _ hfm with ⟨e, he, hem⟩, clear hfm,
  rcases multiset.mem_map.1 he with ⟨m, hm, rfl⟩, clear he,
  rw [mem_support_single] at hem,
  rcases hem with ⟨rfl, hb⟩,
  rw [mem_diagonal] at hm,
  rw [hm],
  show equiv_multiset.inv_fun (equiv_multiset.to_fun f) ∈ p.support,
  rwa [equiv_multiset.left_inv f]
end

section
include d_nonneg

lemma mem_coeff_support_d {a b : α} (m₀ m₁ : multiset (fin n))
  (h : (m₀, m₁) ∈ (coeff a b).support) :
  d ≥ (m₀ + m₁).card :=
begin
  have : of_multiset (m₀ + m₁) ∈ p.support := mem_coeff_support m₀ m₁ h,
  have hp := (mv_polynomial.mem_restrict_total_degree _ _ _ _).1 (S'_le_restrict_d d d_nonneg hp),
  rw [le_floor_to_nat _ _ d_nonneg, total_degree_eq] at hp,
  refine le_trans (nat.cast_le.2 $ le_trans _ (finset.le_sup this)) hp,
  convert le_refl _,
  exact finsupp.equiv_multiset.4 _
end

lemma mem_coeff_support_q {a b : α} (m₀ m₁ : multiset (fin n))
  (h : (m₀, m₁) ∈ (coeff a b).support) (i : fin n) :
  (m₀ + m₁).count i < q :=
begin
  have := S'_le_restrict_q _ hp (mem_coeff_support _ _ h),
  exact (nat.le_sub_right_iff_add_le one_le_q).1 (this i)
end

lemma split_left_curry_card (a b : α) : (split_left a b).curry.support.card ≤ m (d/2) :=
begin
  rw [← M'_card, M'],
  refine finset.card_le_card_of_inj_on (λm, monomial (finsupp.of_multiset m) (1:α)) _ _,
  { simp only [finset.mem_filter, finset.mem_image, exists_prop, finset.mem_univ, true_and, M],
    assume m hm',
    rcases finset.mem_image.1 (finsupp.support_curry _ hm') with ⟨⟨m₀, m₁⟩, hm, rfl⟩, clear hm',
    rw [split_left, finsupp.support_filter, finset.mem_filter] at hm,
    let f : fin n → fin q := λi, ⟨m₀.count i, lt_of_le_of_lt
      (multiset.count_le_of_le _ $ le_add_right _ _) (mem_coeff_support_q _ _ hm.1 i)⟩,
    refine ⟨⟨_, ⟨f, rfl⟩, _⟩, _⟩,
    { congr, ext i, refl },
    { rw [monomial_total_degree, ← card_to_multiset],
      convert le_of_lt hm.2,
      exact finsupp.equiv_multiset.4 _ } },
  { assume m₀ _ m₁ _ eq,
    have := (single_eq_single_iff _ _ _ _).1 eq,
    simp only [(zero_ne_one : (0:α) ≠ 1).symm, or_false, and_false] at this,
    exact (finsupp.equiv_multiset.symm.apply_eq_iff_eq m₀ m₁).1 this.1 }
end

lemma split_right_curry_card (a b : α) : (split_right a b).curry.support.card ≤ m (d/2) :=
begin
  rw [← M'_card, M'],
  refine finset.card_le_card_of_inj_on (λm, monomial (finsupp.of_multiset m) (1:α)) _ _,
  { simp only [finset.mem_filter, finset.mem_image, exists_prop, finset.mem_univ, true_and, M],
    assume m hm',
    rcases finset.mem_image.1 (finsupp.support_curry _ hm') with ⟨⟨m₁, m₀⟩, hm'', rfl⟩, clear hm',
    rcases finset.mem_image.1 (map_domain_support hm'') with ⟨⟨m₀', m₁'⟩, hm, eq⟩,
    cases eq, clear eq, clear hm'',
    rw [finsupp.support_filter, finset.mem_filter] at hm,
    let f : fin n → fin q := λi, ⟨m₁.count i, lt_of_le_of_lt
      (multiset.count_le_of_le _ $ le_add_left _ _) (mem_coeff_support_q _ _ hm.1 i)⟩,
    refine ⟨⟨_, ⟨f, rfl⟩, _⟩, _⟩,
    { congr, ext i, refl },
    { rw [monomial_total_degree, ← card_to_multiset],
      have d_le : d / 2 ≤ ↑(card m₀) := le_of_not_gt hm.2,
      have d_ge : d ≥ card (m₀ + m₁) := mem_coeff_support_d _ _ hm.1,
      rw [multiset.card_add, nat.cast_add] at d_ge,
      have : d / 2 ≥ ↑(card m₁),
      { refine le_of_not_gt (assume h, _),
        have : (card m₀ : ℚ) + card m₁ > d / 2 + d / 2 := add_lt_add_of_le_of_lt d_le h,
        rw [add_halves] at this,
        exact not_lt_of_ge d_ge this },
      convert this,
      exact finsupp.equiv_multiset.4 _ } },
  { assume m₀ _ m₁ _ eq,
    have := (single_eq_single_iff _ _ _ _).1 eq,
    simp only [(zero_ne_one : (0:α) ≠ 1).symm, or_false, and_false] at this,
    exact (finsupp.equiv_multiset.symm.apply_eq_iff_eq m₀ m₁).1 this.1 }
end

end

lemma eval_add_eq_split_sum {x y : fin n → α} {a b : α} :
  p.eval (a • x + b • y) =
    (split_left a b).curry.sum (λm f, π x m * f.sum (λm c, c * π y m)) +
    (split_right a b).curry.sum (λm f, π y m * f.sum (λm c, c * π x m)) :=
calc p.eval (a • x + b • y) = (coeff a b).sum (λm c, c * π x m.1 * π y m.2) :
  by
    rw [coeff, eval_add];
    simp [π, finsupp.multiset_sum_sum_index, sum_sum_index, sum_single_index,
      add_mul, mul_comm, mul_left_comm, mul_assoc]
  ... =
    (split_left a b).sum (λm c, c * π x m.1 * π y m.2) +
    (split_right a b).sum (λm c, c * π x m.2 * π y m.1) :
  begin
    rw [split_left, split_right, sum_map_domain_index]; simp [- not_lt],
    rw [← sum_add_index, filter_pos_add_filter_neg]; simp [add_mul],
    simp [add_mul]
  end
  ... =
    (split_left a b).curry.sum (λm f, π x m * f.sum (λm c, c * π y m)) +
    (split_right a b).curry.sum (λm f, π y m * f.sum (λm c, c * π x m)) :
  by
    rw [
      ← (sum_curry_index (split_left a b) (λm₀ m₁ c, c * π x m₀ * π y m₁) _ _),
      ← (sum_curry_index (split_right a b) (λm₀ m₁ c, c * π x m₁ * π y m₀) _ _)];
    simp [finsupp.mul_sum, mul_comm, mul_assoc, mul_left_comm, add_mul];
    simp [finsupp.sum, mul_comm, mul_assoc, mul_left_comm]

end

lemma sum_is_matrix_mul_left (a b : α) (x y) :
  ((split_left a b).curry.sum (λm f, π x m * f.sum (λm c, c * π y m))) =
  (split_left a b).curry.sum (λ m f, matrix.vec_mul_vec (λ x : fin n → α, π x m)
                                      (λ y : fin n → α, f.sum (λ m c, c * π y m))) x y :=
begin
  rw ←finsupp.sum_matrix _ _ x y,
  refl
end

lemma sum_is_matrix_mul_right (a b : α) (x y) :
  ((split_right a b).curry.sum (λm f, π y m * f.sum (λm c, c * π x m))) =
  (split_right a b).curry.sum (λ m f, matrix.vec_mul_vec (λ x : fin n → α, f.sum (λ m c, c * π x m))
                               (λ y : fin n → α, π y m)) x y :=
begin
  rw ←finsupp.sum_matrix _ _ x y,
  congr, ext, simp [matrix.vec_mul_vec, mul_comm]
end

lemma vmv_rank (m : multiset (fin n)) (f : multiset (fin n) →₀ α) :
  rank (matrix.vec_mul_vec
    (λ x : fin n → α, π x m) (λ y : fin n → α, f.sum (λ m c, c * π y m))).to_lin ≤ 1 :=
matrix.rank_vec_mul_vec _ _

def B : matrix {x // x ∈ A} {x // x ∈ A} α
| x y := p.eval (a • x + b • y)

lemma B_entry_eq_sum (i j) : B i j =
  (split_left a b).curry.sum (λm f, π i.val m * f.sum (λm c, c * π j.val m)) +
  (split_right a b).curry.sum (λm f, π j.val m * f.sum (λm c, c * π i.val m)) :=
eval_add_eq_split_sum

lemma B_entry_eq_sum_matrix (i j) : B i j =
  (split_left a b).curry.sum (λ m f, matrix.vec_mul_vec (λ x, π x m)
                                      (λ y, f.sum (λ m c, c * π y m))) i.val j.val +
  (split_right a b).curry.sum (λ m f, matrix.vec_mul_vec (λ x, f.sum (λ m c, c * π x m))
                               (λ y, π y m)) i.val j.val :=
begin
  rw [B_entry_eq_sum, sum_is_matrix_mul_left, sum_is_matrix_mul_right]
end

lemma B_eq_sum_matrix : B =
  (split_left a b).curry.sum (λ m f, matrix.vec_mul_vec (λ x, π x.val m)
                                      (λ y, f.sum (λ m c, c * π y.val m))) +
  (split_right a b).curry.sum (λ m f, matrix.vec_mul_vec (λ x, f.sum (λ m c, c * π x.val m))
                               (λ y, π y.val m)) :=
begin
  ext, rw B_entry_eq_sum_matrix, congr;
  { rw [finsupp.sum_matrix, finsupp.sum_matrix], refl }
end

lemma B_diagonal_card : fintype.card {i : {x // x ∈ A} // p.eval (-c • i.val) ≠ 0} =
  (A.filter (λ a, p.eval (-c • a) ≠ 0)).card :=
suffices fintype.card { i // i ∈ A ∧ p.eval (-c • i) ≠ 0} = (A.filter (λ a, p.eval (-c • a) ≠ 0)).card,
  begin
    rw ←this,
    apply fintype.card_congr,
    refine {to_fun := λ x, ⟨x.1.1, x.1.2, x.2⟩, inv_fun := λ x, ⟨⟨x.1, x.2.1⟩, x.2.2⟩, ..},
    { simp [function.left_inverse] },
    { simp [function.right_inverse, function.left_inverse] }
  end,
fintype.card_of_subtype _ $ λ a, finset.mem_filter

-- strategy: B = Σ Mᵢ + Σ Nᵢ, where there are ≤ m (d/2) i's and rank(Mᵢ), rank(Nᵢ) ≤ 1
include d_nonneg
lemma B_rank_le : (rank B.to_lin) ≤ ↑(2 * m (d/2)) :=
suffices rank B.to_lin ≤ ↑(m (d/2)) + ↑(m (d/2)), by simpa [two_mul],
begin
  rw [B_eq_sum_matrix, matrix.to_lin_add],
  transitivity,
  apply rank_add_le,
  apply cardinal.add_le_add;
  `[rw [finsupp.sum_matrix_to_lin],
    transitivity,
    apply rank_finset_sum_le,
    rw [←mul_one (m (d/2)), nat.cast_mul, nat.cast_one, ←add_monoid.smul_eq_mul],
    transitivity add_monoid.smul _ (1 : cardinal),
    { apply finset.sum_le_card_mul_of_bdd,
      intros, apply matrix.rank_vec_mul_vec },
    apply add_monoid.smul_le_smul (cardinal.zero_le _)],
    apply split_left_curry_card, apply split_right_curry_card
end

include habc h
lemma B_diagonal : B = matrix.diagonal (λ n, p.eval (-c • n.val)) :=
matrix.ext $ λ i j,
  if hij : i = j then by rw [B, hij, ←add_smul, a_plus_b]; simp; refl
  else begin
    rw [B, h],
    { simp [hij] },
    { exact i.2 },
    { exact j.2 },
    { intro h2, apply hij, exact subtype.eq h2 }
  end

lemma B_rank_eq :
  (rank B.to_lin).to_nat = (A.filter (λa : fin n → α, mv_polynomial.eval (- c • a) p ≠ 0 )).card :=
begin
  rw [B_diagonal, matrix.rank_diagonal, ←B_diagonal_card, cardinal.to_nat_coe],
  congr -- WHY??
end

theorem proposition_11_1 :
  (A.filter (λa : fin n → α, mv_polynomial.eval (- c • a) p ≠ 0 )).card ≤ 2 * m (d / 2) :=
begin
  rw ←B_rank_eq,
  transitivity,
  { apply cardinal.to_nat_le_of_le _,
    apply B_rank_le,
    apply cardinal.nat_lt_omega },
  { rw cardinal.to_nat_coe }
end

end Propostion_11_1

-- corresponds to section 12 of Sander's writeup
section Theorem4
set_option class.instance_max_depth 200
parameters {a b c : α} (h_a_ne_zero : c ≠ 0) (h_sum : a + b + c = 0) (h_n_pos : n > 0)
           {A : finset (fin n → α)}
           (h_A : ∀ x y z ∈ A, a•x + b•y + c•z = (0 : fin n → α) → x = y ∧ x = z)

section
parameters {d : ℚ} (hd : d ≤ (q-1)*n) (d_nonneg : 0 ≤ d) -- q is the cardinality of α

def neg_cA : finset (fin n → α) := A.image (λ z, (-c) • z)

include h_a_ne_zero

lemma map_is_bijective : function.bijective (λ x : fin n → α, -c•x) :=
equiv.bijective $ linear_equiv.to_equiv $ linear_equiv.smul_of_ne_zero (fin n → α) (-c) $
  by simp [h_a_ne_zero]

omit h_a_ne_zero

lemma neg_cA_card : neg_cA.card = A.card :=
finset.card_image_of_injective _ map_is_bijective.1

lemma A_card_le_α_card_n : A.card ≤ fintype.card α^n :=
calc A.card ≤ finset.univ.card : finset.card_le_of_subset (finset.subset_univ _)
        ... = fintype.card (fin n → α) : finset.card_univ
        ... = fintype.card α^n : by simp

def V : set S := zero_set (S' d) (finset.univ \ neg_cA)

def V' : subspace α S := zero_set_subspace (S' d) (finset.univ \ neg_cA)

lemma p_in_V_vanishes {p : S} : p ∈ V →
  ∀ {x : fin n → α}, x ∉ neg_cA → p.eval x = 0
| ⟨_, p_spec⟩ _ h_xnmem := p_spec _ $ finset.mem_sdiff.2 ⟨finset.mem_univ _, h_xnmem⟩

lemma V_dim_finite : (vector_space.dim α V') < cardinal.omega :=
zero_set_subspace_dim _ _ (dim_span_of_finset _)

noncomputable def V_dim := (vector_space.dim α V').to_nat

include h_n_pos h_a_ne_zero

lemma diff_card_comp : (finset.univ \ neg_cA).card + A.card = q^n :=
by rw [finset.card_univ_diff, fintype.card_fin_arrow, neg_cA_card,
  nat.sub_add_cancel A_card_le_α_card_n]; refl

theorem lemma_12_2 :  q^n + V_dim ≥ m d + A.card :=
have V_dim + (finset.univ \ neg_cA).card ≥ m d,
  from lemma_9_2 _ _ V_dim_finite,
by linarith [diff_card_comp]

omit h_a_ne_zero

def sup (p : S) : finset (fin n → α) :=
finset.univ.filter $ λ x, mv_polynomial.eval x p ≠ 0

lemma mem_sup_iff {p : S} {x : fin n → α} : x ∈ sup p ↔ p.eval x ≠ 0 :=
by rw [sup, finset.mem_filter, and_iff_right (finset.mem_univ _)]

lemma mem_sup {p : S} {x : fin n → α} : p.eval x ≠ 0 → x ∈ sup p := mem_sup_iff.2

lemma exi_max_sup : ∃ P ∈ V, ∀ P' ∈ V, sup P ⊆ sup P' → sup P = sup P' :=
begin
  refine set.finite.exists_maximal_wrt sup _ _ _,
  { refine cardinal.lt_omega_iff_fintype.1 _,
    apply cardinal_lt_omega_of_dim_lt_omega V_dim_finite },
  exact set.ne_empty_iff_exists_mem.2 ⟨0, V'.zero_mem⟩
end

noncomputable def P := classical.some exi_max_sup

lemma P_V : P ∈ V := have _ := classical.some_spec exi_max_sup, by tauto

lemma P_in_S' : P ∈ S' d := P_V.1

lemma P_spec : ∀ P' ∈ V, sup P ⊆ sup P' → sup P = sup P' :=
have _ := classical.some_spec exi_max_sup, by tauto

noncomputable def P_sig := sup P

lemma eval_ne_zero_of_mem_P_sig {x} (hx : x ∈ P_sig) : P.eval x ≠ 0 :=
(finset.mem_filter.1 hx).2

lemma eval_eq_zero_of_not_mem_neg_cA {x} (hx : x ∉ neg_cA) : P.eval x = 0 :=
P_V.2 _ $ (finset.mem_sdiff.2 ⟨finset.mem_univ _, hx⟩)

def W : set S := zero_set V' P_sig

lemma eval_mem_W_eq_zero_of_not_mem_neg_cA {Q} (hQ : Q ∈ W) {x} (hx : x ∉ neg_cA) : Q.eval x = 0 :=
hQ.1.2 _ $ finset.mem_sdiff.2 ⟨finset.mem_univ _, hx⟩

noncomputable def W_dim := (vector_space.dim α (zero_set_subspace V' P_sig)).to_nat

lemma W_dim_ge : W_dim + P_sig.card ≥ V_dim :=
lemma_9_2 V' P_sig (zero_set_subspace_dim _ _ V_dim_finite)

theorem lemma_12_3 : P_sig.card ≥ V_dim :=
le_of_not_gt $
assume hlt : P_sig.card < V_dim,
have W_dim > 0, by linarith [W_dim_ge],
let ⟨Q, hQW, hQ⟩ := exists_mem_ne_zero_of_dim_pos (cardinal.pos_of_to_nat_pos this) in
have hPQ : ∀ x ∈ P_sig, (P + Q).eval x ≠ 0, from
  assume x hxP_sig,
  have hxP : P.eval x ≠ 0, from eval_ne_zero_of_mem_P_sig hxP_sig,
  have hxQ : Q.eval x = 0, from hQW.2 _ hxP_sig,
  by simp [hxP, hxQ],
have hQq : Q ∈ restrict_degree (fin n) α (q - 1) :=
begin
  refine S'_le_restrict_q _ _, -- if instantiated with `d` and `hQW.1.1` it doesn't terminate!
  exact d,
  exact hQW.1.1,
end,
have ∃ y, Q.eval y ≠ 0, from
  not_forall.1 $ mt (assume h, Q.eq_zero_of_eval_eq_zero _ _ h hQq) hQ,
let ⟨y, hQy⟩ := this in
have hPsig_y : y ∉ P_sig, from λ h, hQy $ hQW.2 _ h,
have hPy : P.eval y = 0, from by_contradiction $ λ h, hPsig_y (mem_sup h),
have (P + Q).eval y ≠ 0, by simp [hPy, hQy],
have hPQsup_y : y ∈ sup (P + Q), from mem_sup this,
have hxPQ : ∀ x, x ∈ finset.univ \ neg_cA → (P + Q).eval x = 0, from
  λ _ hx, by cases finset.mem_sdiff.1 hx with _ hx;
             simp [eval_eq_zero_of_not_mem_neg_cA hx, eval_mem_W_eq_zero_of_not_mem_neg_cA hQW hx],
have hmemS' : P + Q ∈ (S' d), from submodule.add_mem _ P_V.1 hQW.1.1,
have hPQV : (P + Q) ∈ V, from ⟨hmemS', hxPQ⟩,
have hSup : P_sig = sup (P + Q), from P_spec _ hPQV $ λ x hx, mem_sup (hPQ _ hx),
hPsig_y $ by rwa ←hSup at hPQsup_y

def A_antidiag : finset ((fin n → α) × (fin n → α)) :=
(A.product A).filter (λ p, p.1 ≠ p.2)

def abS : finset (fin n → α) := A_antidiag.image $ λ p, a•p.1 + b•p.2

include h_A
lemma inter_empty : abS ∩ neg_cA = ∅ :=
begin
  apply finset.eq_empty_of_forall_not_mem,
  intros x h_xmem,
  rcases finset.mem_image.1 (finset.mem_of_mem_inter_left h_xmem) with ⟨⟨l, r⟩, hlr, hx_sum⟩,
  rcases finset.mem_image.1 (finset.mem_of_mem_inter_right h_xmem) with ⟨w, hw, hx_w⟩,
  cases finset.mem_filter.1 hlr with hp h_lrne,
  cases finset.mem_product.1 hp with h_la h_ra,
  dsimp at hx_sum,
  have : a•l + b•r +c•w = 0, {rw [hx_sum, ←hx_w], simp},
  apply h_lrne,
  refine (h_A _ _ _ _ _ _ this).left; assumption
end

lemma P_vanishes_off_A_image (x : fin n → α) (h_xmemS : x ∉ neg_cA) :
  mv_polynomial.eval x P = 0 :=
p_in_V_vanishes P_V h_xmemS

lemma P_vanishes_on_abS (x : fin n → α) (h_xmemS : x ∈ abS) : mv_polynomial.eval x P = 0 :=
begin
  apply P_vanishes_off_A_image,
  intro h_xmemimage,
  apply finset.not_mem_empty x,
  rw ←inter_empty,
  apply finset.mem_inter_of_mem; assumption
end

lemma filter_eq : finset.univ.filter (λ x : fin n → α, mv_polynomial.eval x P ≠ 0) =
  finset.filter (λ x, mv_polynomial.eval x P ≠ 0) neg_cA :=
finset.ext.2 $ λ x,
⟨ λ h_xmem,
  let ⟨_, h_xeval⟩ := finset.mem_filter.1 h_xmem in
  finset.mem_filter.2 ⟨have _, from P_vanishes_off_A_image x, by clear_aux_decl; tauto, h_xeval⟩,
  λ h_xmem, finset.mem_filter.2 ⟨finset.mem_univ _, (finset.mem_filter.1 h_xmem).2⟩ ⟩

theorem lemma_12_4 : P_sig.card ≤ 2 * m (d/2) :=
have h_P_vanishes' : ∀ x, x ∈ A → ∀ y, y ∈ A → x ≠ y → mv_polynomial.eval (a•x + b•y) P = 0,
begin
  intros x hx y hy hxy,
  apply P_vanishes_on_abS (a•x + b•y),
  apply finset.mem_image.2,
  have : (x, y) ∈ A_antidiag, from finset.mem_filter.2 ⟨finset.mem_product.2 ⟨hx, hy⟩, hxy⟩,
  apply exists.intro,
  existsi this,
  refl
end,
have h_bound : _ := proposition_11_1 d_nonneg A a b c P_in_S' h_sum h_P_vanishes',
have h_P_sig_def : P_sig = finset.image (λ z, (-c) • z) (A.filter (λ x, P.eval (-c•x) ≠ 0)), from calc
    P_sig = finset.univ.filter (λ x, P.eval x ≠ 0) : rfl
      ... = finset.filter (λ x, P.eval x ≠ 0) neg_cA : filter_eq
      ... = finset.image (λ z, (-c) • z) (A.filter (λ x, P.eval (-c•x) ≠ 0)) : finset.image_filter,
have h_P_card : P_sig.card = (A.filter (λ x, P.eval (-c•x) ≠ 0)).card, from calc
    P_sig.card = (finset.image (λ z, (-c) • z) (A.filter (λ a, P.eval (-c•a) ≠ 0))).card : by rw h_P_sig_def
           ... = ((A.filter (λ x, P.eval (-c•x) ≠ 0))).card : finset.card_image_of_injective _ map_is_bijective.1,
by rwa ←h_P_card at h_bound

omit h_A

section
omit h_n_pos

def l125_A : finset (fin n → fin q) := finset.univ

@[reducible] def monom (v : fin n → fin q) : mv_polynomial (fin n) α := monomial v 1

lemma total_degree_monom (v : fin n → fin q) : total_degree (monom v) = fin.sum (v : fin n → ℕ)  :=
by simp only [monom, monomial_total_degree, fin_sum_finsupp_sum]; refl

def l125_B := l125_A.filter $ λ v, ↑(total_degree (monom v)) ≤ d
def l125_C := l125_A.filter $ λ v, ↑(total_degree (monom v)) > d
def l125_D := l125_A.filter $ λ v, ↑(total_degree (monom v)) < ((q : ℚ) - 1)*n - d
def l125_E := l125_A.filter $ λ v, ↑(total_degree (monom v)) ≤ ((q : ℚ) - 1)*n - d

lemma monom_exps_M'_card (k : ℚ) : ((M' k).image monom_exps).card = (M' k).card :=
finset.card_image_of_inj_on $
  λ v1 hv1 v2 hv2 hv, monom_exps_inj (finset.mem_filter.1 hv1).1 (finset.mem_filter.1 hv2).1 hv

lemma monom_exps_M'_card_m (k : ℚ) : ((M' k).image monom_exps).card = m k :=
by rw [monom_exps_M'_card, M'_card]

lemma B_C_disjoint : disjoint l125_B l125_C :=
λ x hx, by linarith [(finset.mem_filter.1 (finset.mem_of_mem_inter_left hx)).2,
                     (finset.mem_filter.1 (finset.mem_of_mem_inter_right hx)).2]

lemma B_C_union : l125_B ∪ l125_C = l125_A :=
finset.ext.2 $ λ x,
⟨ λ _, finset.mem_univ _,
  λ _, finset.mem_union.2 $
    if hx : ↑(total_degree (monom x)) ≤ d then or.inl (finset.mem_filter.2 ⟨finset.mem_univ _, hx⟩)
    else or.inr (finset.mem_filter.2 ⟨finset.mem_univ _, lt_of_not_ge hx⟩) ⟩

lemma filter_eq_image (r : ℚ) :
  (l125_A.filter $ λ v, ↑(total_degree (monom v)) ≤ r) = (M' r).image monom_exps :=
finset.ext.2 $ λ x,
⟨ λ h, finset.mem_image.2
  ⟨ monomial x 1, finset.mem_filter.2 ⟨monomial_mem_M _, (finset.mem_filter.1 h).2⟩, by simp ⟩,
  λ h, finset.mem_filter.2
  ⟨ finset.mem_univ _,
    let ⟨a, ha, hax⟩ := finset.mem_image.1 h,
        ha' := (finset.mem_filter.1 ha).2 in
    by rw [←hax, monom, monom_exps_def (mem_M_of_mem_M' ha)]; exact ha' ⟩⟩

lemma le_helper (v : ℕ) : q - (v + 1) < q := nat.sub_lt one_le_q (nat.succ_pos _)

def invol (v : fin n → fin q) : fin n → fin q :=
λ x, ⟨q - (v x + 1), le_helper _⟩

lemma invol_add (v i) : ((invol v) i : ℕ) + v i = q - 1 :=
show q - (↑(v i) + 1) + ↑(v i) = q - 1,
begin
  rw ←nat.sub_add_comm,
  { apply nat.sub_eq_of_eq_add,
    rw [add_assoc _ 1, nat.add_sub_cancel'],
    { ac_refl },
    { exact one_le_q } },
  { apply nat.succ_le_of_lt,
    apply fin.is_lt }
end

-- not sure why it appears like this below
lemma invol_add' (v i) : (⇑(invol v) : fin n → ℕ) i + (⇑v : fin n → ℕ) i = q - 1 :=
invol_add _ _

lemma invol_is_involution (x : fin n → fin q) : invol (invol x) = x :=
funext $ λ i, (fin.ext_iff _ _).2 $
  show q - (q - (x i + 1) + 1) = x i,
  begin
    rw [←nat.sub_sub, ←nat.sub_sub, nat.sub_sub, nat.sub_add_cancel, nat.sub_sub_self],
    { apply le_of_lt, apply fin.is_lt },
    { apply (nat.add_le_to_le_sub _ _).1,
      { rw add_comm, apply fin.is_lt },
      { apply le_of_lt, apply fin.is_lt }}
  end

lemma invol_inj : function.injective invol :=
λ v1 v2 hv, by rw [←invol_is_involution v1, ←invol_is_involution v2, hv]

lemma total_degree_invol_eq (v : fin n → fin q) :
   ↑(total_degree (monom (invol v))) = ((q : ℚ)-1)*n - ↑(total_degree (monom v)) :=
begin
  simp only [total_degree_monom],
  apply eq_sub_of_add_eq,
  rw [←nat.cast_add, fin.sum_add],
  simp only [invol_add'],
  have qcast: ((q : ℚ) - 1) * n = ↑((q - 1)*n), {simp [nat.cast_sub one_le_q]},
  rw [qcast, mul_comm], congr, -- something funny with the casts here
  convert fin.sum_const _ _,
  simp
end

lemma invol_total_degree (v : fin n → fin q) :
  ↑(total_degree (monom v)) > d ↔ ↑(total_degree (monom (invol v))) < ((q : ℚ) - 1)*n - d :=
by constructor; { rw total_degree_invol_eq, intro, linarith }

lemma D_eq_C_image_invol : l125_D = l125_C.image invol :=
finset.ext.2 $ λ v,
  ⟨ λ h, finset.mem_image.2
     ⟨invol v, finset.mem_filter.2 ⟨finset.mem_univ _,
       by rw [invol_total_degree, invol_is_involution];
          apply (finset.mem_filter.1 h).2⟩, invol_is_involution _⟩,
    λ h, finset.mem_filter.2 ⟨finset.mem_univ _,
      begin
        rcases finset.mem_image.1 h with ⟨v', hv', hvv'⟩,
        rw [←hvv', ←invol_total_degree],
        apply (finset.mem_filter.1 hv').2
      end⟩⟩

-- this is useful in asymptotics
parameters (α n d)
lemma h_B_card : l125_B.card = m d :=
by rw [l125_B, filter_eq_image, monom_exps_M'_card_m]
parameters {α n d}

include h_n_pos
theorem lemma_12_5 : q^n ≤ m (((q : ℚ) - 1)*n - d) + m d :=
have h_A_card : l125_A.card = q^n, by simp [finset.card_univ, l125_A],
have h_B_card : l125_B.card = m d, by rw [l125_B, filter_eq_image, monom_exps_M'_card_m],
have h_B_C_card : l125_B.card + l125_C.card = l125_A.card,
  by rw [←finset.card_disjoint_union B_C_disjoint, B_C_union],
have h_C_D_card : l125_C.card = l125_D.card,
  by rw [←finset.card_image_of_injective _ invol_inj, D_eq_C_image_invol],
have h_D_E_card : l125_D.card ≤ l125_E.card, from
  finset.card_le_card_of_inj_on id
    (λ a ha, finset.mem_filter.2 ⟨finset.mem_univ _, le_of_lt ((finset.mem_filter.1 ha).2)⟩)
    (λ _ _ _ _, id),
have h_E_card : l125_E.card = m ((q-1)*n - d), by rw [l125_E, filter_eq_image, monom_exps_M'_card_m],
by linarith

include h_A hd h_a_ne_zero h_sum d_nonneg
theorem lemma_12_6 : A.card ≤ 2 * m (d/2) + m ((q - 1)*n - d) :=
by linarith [lemma_12_2, lemma_12_3, lemma_12_4, lemma_12_5]

end

end

include h_A h_n_pos h_a_ne_zero h_sum
theorem theorem_12_1 : A.card ≤ 3*(m (1/3*((q-1)*n))) :=
begin
  set qn := ((q : ℚ) - 1)*n with hqn,
  set! d := 2/3*qn with hd,
  have : qn ≥ 0, from mul_nonneg (sub_nonneg_of_le one_le_q_real) (nat.cast_nonneg _),
  have : d ≤ qn, {linarith},
  have h_d2 : d / 2 = 1/3 * qn, {linarith},
  have h_qnd : qn - d = 1/3 * qn, {linarith},
  have h_l5, from lemma_12_6 this,
  rw [h_d2, h_qnd] at h_l5,
  convert h_l5 _,
  { ring },
  { apply mul_nonneg,
    { norm_num },
    { assumption } }
end

end Theorem4
end


import tactic

/-
# Level 2 : Using a conjunction
-/

namespace math3345 -- hide

/-
P and Q are propositions.
-/

variable P : Prop
variable Q : Prop

/- Lemma : no-side-bar

`h` is evidence that `P ∧ Q`, and we can extract evidence for `P` by
using `h.left` and extract evidence for `Q` with `h.right`.  Then
finish the proof with `exact`.

-/
lemma and_elim_1 (h : P ∧ Q) : Q :=
begin

  sorry,

end

end math3345 -- hide


/-
Copyright (c) 2022 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov, Heather Macbeth
-/
import analysis.normed.group.basic

/-!
# Negation on spheres and balls

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

In this file we define `has_involutive_neg` instances for spheres, open balls, and closed balls in a
semi normed group.
-/

open metric set

variables {E : Type*} [seminormed_add_comm_group E] {r : ℝ}

/-- We equip the sphere, in a seminormed group, with a formal operation of negation, namely the
antipodal map. -/
instance : has_involutive_neg (sphere (0 : E) r) :=
{ neg := subtype.map has_neg.neg $ λ w, by simp,
  neg_neg := λ x, subtype.ext $ neg_neg x }

@[simp] lemma coe_neg_sphere {r : ℝ} (v : sphere (0 : E) r) :
  ↑(-v) = (-v : E) :=
rfl

instance : has_continuous_neg (sphere (0 : E) r) := ⟨continuous_neg.subtype_map _⟩

/-- We equip the ball, in a seminormed group, with a formal operation of negation, namely the
antipodal map. -/
instance {r : ℝ} : has_involutive_neg (ball (0 : E) r) :=
{ neg := subtype.map has_neg.neg $ λ w, by simp,
  neg_neg := λ x, subtype.ext $ neg_neg x }

@[simp] lemma coe_neg_ball {r : ℝ} (v : ball (0 : E) r) :
  ↑(-v) = (-v : E) :=
rfl

instance : has_continuous_neg (ball (0 : E) r) := ⟨continuous_neg.subtype_map _⟩

/-- We equip the closed ball, in a seminormed group, with a formal operation of negation, namely the
antipodal map. -/
instance {r : ℝ} : has_involutive_neg (closed_ball (0 : E) r) :=
{ neg := subtype.map has_neg.neg $ λ w, by simp,
  neg_neg := λ x, subtype.ext $ neg_neg x }

@[simp] lemma coe_neg_closed_ball {r : ℝ} (v : closed_ball (0 : E) r) :
  ↑(-v) = (-v : E) :=
rfl

instance : has_continuous_neg (closed_ball (0 : E) r) := ⟨continuous_neg.subtype_map _⟩


import data.int.modeq data.int.basic data.nat.modeq data.equiv data.fintype data.nat.prime data.nat.gcd data.pnat .int_gcd data.nat.prime

@[simp] lemma int.mod_mod (a b : ℤ) : a % b % b = a % b :=
by conv {to_rhs, rw [← int.mod_add_div a b, int.add_mul_mod_self_left]}

@[simp] lemma int.mod_neg_mod (a b : ℤ) : (-(a % b) % b) = (-a % b) :=
by conv {to_rhs, rw [← int.mod_add_div a b, neg_add, neg_mul_eq_mul_neg, int.add_mul_mod_self_left]}

open nat nat.modeq int

def Zmod (n : ℕ) := subtype (< n)

namespace Zmod

-- Copied from core, but marked as private in core
lemma mlt {n b : nat} : ∀ {a}, n > a → b % n < n
| 0     h := nat.mod_lt _ h
| (a+1) h :=
  have n > 0, from lt.trans (nat.zero_lt_succ _) h,
  nat.mod_lt _ this

class pos (n : ℕ) := (pos : 0 < n)

attribute [class] nat.prime

instance pos_of_prime (p : ℕ) [hp : nat.prime p] : pos p := ⟨hp.pos⟩

def add_aux {n : ℕ} (a b : Zmod n) : Zmod n :=
⟨(a.1 + b.1) % n, mlt a.2⟩

def mul_aux {n : ℕ} (a b : Zmod n) : Zmod n :=
⟨(a.1 * b.1) % n, mlt a.2⟩

def neg_aux {n : ℕ} (a : Zmod n) : Zmod n :=
⟨nat_mod (-(a.1 : ℤ)) n,
begin
  cases n with n,
  { exact (nat.not_lt_zero _ a.2).elim },
  { have h : (nat.succ n : ℤ) ≠ 0 := dec_trivial,
    rw [← int.coe_nat_lt, nat_mod, to_nat_of_nonneg (int.mod_nonneg _ h)],
    exact int.mod_lt _ h }
end⟩

instance (n : ℕ) : add_comm_semigroup (Zmod n) :=
{ add := add_aux,
  add_assoc := λ ⟨a, ha⟩ ⟨b, hb⟩ ⟨c, hc⟩, subtype.eq
    (show ((a + b) % n + c) ≡ (a + (b + c) % n) [MOD n],
    from calc ((a + b) % n + c) ≡ a + b + c [MOD n] : modeq_add (nat.mod_mod _ _) rfl
      ... ≡ a + (b + c) [MOD n] : by rw add_assoc
      ... ≡ (a + (b + c) % n) [MOD n] : modeq_add rfl (nat.mod_mod _ _).symm),
  add_comm := λ a b, subtype.eq (show (a.1 + b.1) % n = (b.1 + a.1) % n, by rw add_comm) }

instance (n : ℕ) : comm_semigroup (Zmod n) :=
{ mul := mul_aux,
  mul_assoc := λ ⟨a, ha⟩ ⟨b, hb⟩ ⟨c, hc⟩, subtype.eq
    (calc ((a * b) % n * c) ≡ a * b * c [MOD n] : modeq_mul (nat.mod_mod _ _) rfl
      ... ≡ a * (b * c) [MOD n] : by rw mul_assoc
      ... ≡ a * (b * c % n) [MOD n] : modeq_mul rfl (nat.mod_mod _ _).symm),
  mul_comm := λ a b, subtype.eq (show (a.1 * b.1) % n = (b.1 * a.1) % n, by rw mul_comm) }

def one_aux (n : ℕ) [h0 : pos n] : Zmod n := ⟨1 % n, nat.mod_lt _ h0.pos⟩

lemma one_mul_aux (n : ℕ) [h0 : pos n] : ∀ a : Zmod n, one_aux n * a = a :=
λ ⟨a, ha⟩, subtype.eq (show (1 % n * a) % n = a,
begin
  resetI,
  clear _fun_match,
  cases n with n,
  { simp },
  { cases n with n,
    { rw [nat.mod_self, zero_mul];
      exact (nat.eq_zero_of_le_zero (le_of_lt_succ ha)).symm },
    { have h : 1 < n + 2 := dec_trivial,
      have ha : a < succ (succ n) := ha,
      rw [nat.mod_eq_of_lt h, one_mul, nat.mod_eq_of_lt ha] } }
end)

lemma left_distrib_aux (n : ℕ) : ∀ a b c : Zmod n, a * (b + c) = a * b + a * c :=
λ ⟨a, ha⟩ ⟨b, hb⟩ ⟨c, hc⟩, subtype.eq
(calc a * ((b + c) % n) ≡ a * (b + c) [MOD n] : modeq_mul rfl (nat.mod_mod _ _)
  ... ≡ a * b + a * c [MOD n] : by rw mul_add
  ... ≡ (a * b) % n + (a * c) % n [MOD n] : modeq_add (nat.mod_mod _ _).symm (nat.mod_mod _ _).symm)

instance (n : ℕ) : has_neg (Zmod n) := ⟨neg_aux⟩

instance (n : ℕ) : distrib (Zmod n) :=
{ left_distrib := left_distrib_aux n,
  right_distrib := λ a b c, by rw [mul_comm, left_distrib_aux, mul_comm _ b, mul_comm]; refl,
  ..Zmod.add_comm_semigroup n,
  ..Zmod.comm_semigroup n }

instance (n : ℕ) [h0 : pos n] : comm_ring (Zmod n) :=
{ zero := ⟨0, h0.pos⟩,
  zero_add := λ ⟨a, ha⟩, subtype.eq (show (0 + a) % n = a, by rw zero_add; exact nat.mod_eq_of_lt ha),
  add_zero := λ ⟨a, ha⟩, subtype.eq (nat.mod_eq_of_lt ha),
  neg := has_neg.neg,
  add_left_neg :=
    λ ⟨a, ha⟩, subtype.eq (show (((-a : ℤ) % n).to_nat + a) % n = 0,
      from int.coe_nat_inj
      begin
        have hn : (n : ℤ) ≠ 0 := (ne_of_lt (int.coe_nat_lt.2 h0.pos)).symm,
        rw [int.coe_nat_mod, int.coe_nat_add, to_nat_of_nonneg (int.mod_nonneg _ hn), add_comm],
        simp,
      end),
  one := ⟨1 % n, nat.mod_lt _ h0.pos⟩,
  one_mul := one_mul_aux n,
  mul_one := λ a, by rw mul_comm; exact one_mul_aux n a,
  ..Zmod.distrib n,
  ..Zmod.add_comm_semigroup n,
  ..Zmod.comm_semigroup n }

-- instance {n : ℕ+} : has_coe ℤ (Zmod n) := ⟨of_int n⟩

-- instance Zmod_coe_nat (n : ℕ+) : has_coe ℕ (Zmod n) := ⟨of_nat n⟩

-- @[simp] lemma coe_nat_coe_int (n : ℕ+) (a : ℕ) : (((a : ℕ) : ℤ) : Zmod n) = a :=
-- subtype.eq $ int.coe_nat_inj $ (show (nat_mod a n : ℤ)= a % n, by simp)

-- lemma coe_eq_of_nat {n : ℕ+} (a : ℕ) : (a : Zmod n) = of_nat n a := rfl

-- lemma coe_int_add (n : ℕ+) (a b : ℤ) : ((a + b : ℤ) : Zmod n) = a + b :=
-- subtype.eq (show nat_mod (a + b) n = (nat_mod a n + nat_mod b n) % n,
--   from int.coe_nat_inj (by simp [int.coe_nat_add]))

-- lemma coe_int_mul (n : ℕ+) (a b : ℤ) : ((a * b : ℤ) : Zmod n) = a * b :=
-- subtype.eq (show nat_mod (a * b) n = (nat_mod a n * nat_mod b n) % n,
--   from int.coe_nat_inj (begin
--     rw [coe_coe, nat_mod_coe_pnat, int.coe_nat_mod, int.coe_nat_mul, nat_mod_coe_pnat, nat_mod_coe_pnat],
--     exact int.modeq.modeq_mul (int.mod_mod _ _).symm (int.mod_mod _ _).symm
--   end))

-- lemma coe_int_neg (n : ℕ+) (a : ℤ) : ((-a : ℤ) : Zmod n) = -a :=
-- subtype.eq (show nat_mod (-a) n = nat_mod (-↑(nat_mod a n)) n,
-- from int.coe_nat_inj (by simp))

-- lemma coe_int_sub (n : ℕ+) (a b : ℤ) : ((a - b : ℤ) : Zmod n) = a - b :=
-- by rw [sub_eq_add_neg, coe_int_add, coe_int_neg, sub_eq_add_neg]

-- lemma coe_nat_add (n : ℕ+) (a b : ℕ) : ((a + b : ℕ) : Zmod n) = a + b :=
-- subtype.eq (modeq_add (nat.mod_mod _ _).symm (nat.mod_mod _ _).symm)

-- lemma coe_nat_mul (n : ℕ+) (a b : ℕ) : ((a * b : ℕ) : Zmod n) = a * b :=
-- subtype.eq (modeq_mul (nat.mod_mod _ _).symm (nat.mod_mod _ _).symm)

-- lemma coe_nat_sub (n : ℕ+) {a b : ℕ} (h : b ≤ a) : ((a - b : ℕ) : Zmod n) = a - b :=
-- by rw [← coe_nat_coe_int, int.coe_nat_sub h, coe_int_sub]; simp

-- @[simp] lemma coe_eq_zero' (n : ℕ+) : ((n : ℕ) : Zmod n) = 0 :=
-- subtype.eq (nat.mod_self _)

-- lemma eq_iff_modeq_nat {n : ℕ+} {a b : ℕ} : (a : Zmod n) = b ↔ a ≡ b [MOD n] :=
-- ⟨subtype.mk.inj, λ h, subtype.eq h⟩

-- @[simp] lemma val_coe {n : ℕ+} (a : Zmod n) : (a.val : Zmod n) = a :=
-- subtype.eq (nat.mod_eq_of_lt a.2)

-- lemma eq_iff_modeq_int {n : ℕ+} {a b : ℤ} : (a : Zmod n) = b ↔ a ≡ b [ZMOD n] :=
-- ⟨λ h, begin
--   have := (int.coe_nat_eq_coe_nat_iff _ _).2 (subtype.mk.inj h),
--   rw [coe_coe, nat_mod_coe_pnat, nat_mod_coe_pnat] at this,
--   exact this
-- end,
-- λ h, subtype.eq (show nat_mod a n = nat_mod b n, from
--   int.coe_nat_inj begin
--   rw [coe_coe, nat_mod_coe_pnat, nat_mod_coe_pnat],
--   exact h
-- end)⟩

-- @[simp] lemma coe_int_mod (n : ℕ+) (a : ℤ) : ((a % ((n : ℕ) : ℤ) : ℤ) : Zmod n) = a :=
-- eq_iff_modeq_int.2 (int.mod_mod _ _)

-- @[simp] lemma coe_nat_mod (n : ℕ+) (a : ℕ) : ((a % n : ℕ) : Zmod n) = a :=
-- eq_iff_modeq_nat.2 (nat.mod_mod _ _)

-- @[simp] lemma coe_nat_zero (n : ℕ+) : ((0 : ℕ) : Zmod n) = 0 :=
-- subtype.eq (nat.zero_mod _)

-- @[simp] lemma coe_int_zero (n : ℕ+) : ((0 : ℤ) : Zmod n) = 0 :=
-- by rw [← int.coe_nat_zero, coe_nat_coe_int, coe_nat_zero]

-- @[simp] lemma coe_nat_one (n : ℕ+) : ((1 : ℕ) : Zmod n) = 1 := rfl

-- @[simp] lemma coe_int_one (n : ℕ+) : ((1 : ℤ) : Zmod n) = 1 := rfl

-- @[simp] lemma coe_nat_bit0 (n : ℕ+) (a : ℕ) : ((bit0 a : ℕ) : Zmod n) = bit0 (a : Zmod n) := coe_nat_add _ _ _

-- @[simp] lemma coe_nat_bit1 (n : ℕ+) (a : ℕ) : ((bit1 a : ℕ) : Zmod n) = bit1 (a : Zmod n) :=
-- by rw [bit1, bit1, coe_nat_add, coe_nat_bit0, coe_nat_one]

-- @[simp] lemma coe_int_bit0 (n : ℕ+) (a : ℤ) : ((bit0 a : ℤ) : Zmod n) = bit0 (a : Zmod n) := coe_int_add _ _ _

-- @[simp] lemma coe_int_bit1 (n : ℕ+) (a : ℤ) : ((bit1 a : ℤ) : Zmod n) = bit1 (a : Zmod n) :=
-- by rw [bit1, bit1, coe_int_add, coe_int_bit0, coe_int_one]

def to_nat {n : ℕ} (a : Zmod n) : ℕ := a.1

lemma to_nat_lt {n : ℕ} (a : Zmod n) : a.to_nat < n := a.2

@[simp] lemma to_nat_zero (n : ℕ) [h0 : pos n] : (0 : Zmod n).to_nat = 0 := rfl

@[simp] lemma cast_to_nat {n : ℕ} [h0 : pos n] (a : ℕ) : (a : Zmod n).to_nat = a % n :=
begin
  induction a with a ih,
  { rw [nat.zero_mod]; refl },
  { show ((a : Zmod n).to_nat + 1 % n) % n = (a + 1) % n,
    rw ih,
    exact nat.modeq.modeq_add (nat.mod_mod _ _) (nat.mod_mod _ _) }
end

@[simp] lemma eq_zero (n : ℕ) [h0 : pos n] : (n : Zmod n) = 0 :=
subtype.eq (show (n : Zmod n).to_nat = 0, by simp [cast_to_nat])

@[simp] lemma cast_nat_mod (n : ℕ) [h0 : pos n] (a : ℕ) : ((a % n : ℕ) : Zmod n) = a :=
subtype.eq (show ((a % n : ℕ) : Zmod n).to_nat = (a : Zmod n).to_nat, by simp)

-- @[simp] lemma cast_int_mod (n : ℕ) [h0 : pos n] (a : ℤ) : ((a % n : ℤ) : Zmod n) = a :=
-- begin

-- end

-- lemma eq_zero_iff_dvd_nat (n : ℕ) [h0 : pos n] (a : ℕ) : (a : Zmod n) = 0 ↔ (n : ℕ) ∣ a :=
-- ⟨begin end, sorry⟩

-- lemma eq_zero_iff_dvd_int (n : ℕ) [h0 : pos n] (a : ℤ) : (a : Zmod n) = 0 ↔ (n : ℤ) ∣ a :=
-- ⟨λ h, int.modeq.modeq_zero_iff.1 (eq_iff_modeq_int.1 (by  rw [h, coe_int_zero])),
-- λ h, by rwa [← int.modeq.modeq_zero_iff, ← eq_iff_modeq_int, coe_int_zero] at h⟩

-- def equiv_fin (n : ℕ) : Zmod n ≃ fin (nat_abs n) :=
-- { to_fun := λ ⟨a, h⟩, ⟨a, h⟩,
--   inv_fun := λ ⟨a, h⟩, ⟨a, h⟩,
--   left_inv := λ ⟨_, _⟩, rfl,
--   right_inv := λ ⟨_, _⟩, rfl }

-- instance (n : ℕ) : fintype (Zmod n) :=
-- fintype.of_equiv _ (equiv_fin n).symm

-- lemma card_Zmod {n : ℕ} : fintype.card (Zmod n) = n :=
-- eq.trans (fintype.card_congr (equiv_fin n)) (fintype.card_fin _)

-- private def inv_aux {n : ℕ+} (a : Zmod n) : Zmod n := gcd_a a.1 n

-- instance (n : ℕ+) : has_inv (Zmod n) := ⟨inv_aux⟩

-- @[simp] lemma int.gcd_neg (a b : ℤ) : int.gcd (-a) b = int.gcd a b :=
-- by unfold int.gcd; rw nat_abs_neg

-- lemma gcd_a_modeq (a b : ℕ) : (a : ℤ) * gcd_a a b ≡ nat.gcd a b [ZMOD b] :=
-- by rw [← add_zero ((a : ℤ) * _), gcd_eq_gcd_ab];
--   exact int.modeq.modeq_add rfl (int.modeq.modeq_zero_iff.2 (dvd_mul_right _ _)).symm

-- lemma mul_inv_eq_gcd_nat (n : ℕ+) (a : ℕ) : (a : Zmod n) * a⁻¹ = nat.gcd a n :=
-- eq_iff_modeq_nat.2 $ int.modeq.coe_nat_modeq_iff.1
--   (calc ((a % n * nat_mod (gcd_a (a % n) n) ↑n : ℕ) : ℤ) ≡ (a % n : ℕ) * gcd_a (a % n) n [ZMOD n] :
--     by rw [coe_coe, int.coe_nat_mul, nat_mod_coe_pnat]; exact int.modeq.modeq_mul rfl (int.mod_mod _ _)
--   ... ≡ nat.gcd (a % n) n [ZMOD n] : gcd_a_modeq _ _
--   ... = (nat.gcd a n : ℤ) : by rw [← gcd_rec, nat.gcd_comm])

-- lemma mul_inv_eq_gcd_int (n : ℕ+) (a : ℤ) : (a : Zmod n) * a⁻¹ = int.gcd a n :=
-- have h : ((n : ℕ) : ℤ) ≠ 0 := (ne_of_lt (int.coe_nat_lt.2 n.2)).symm,
-- begin
--   rw [int.gcd_comm, ← int.gcd_mod, int.gcd, coe_coe, nat_abs_of_nat, ← mul_inv_eq_gcd_nat,
--     ← coe_nat_coe_int _ (nat_abs _), nat_abs_of_nonneg (mod_nonneg _ h)],
--   simp,
-- end

-- private lemma mul_inv_cancel_aux {p : {n : ℕ+ // nat.prime n.1}} (a : Zmod (p : ℕ+)) (ha : a ≠ 0) : a * a⁻¹ = 1 :=
-- begin
--   rw [← val_coe a, ne.def, eq_zero_iff_dvd_nat] at ha,
--   have : coprime ((p : ℕ+) : ℕ) a.val := p.2.coprime_iff_not_dvd.2 ha,
--   rw [← val_coe a, mul_inv_eq_gcd_nat, nat.gcd_comm, coprime.gcd_eq_one this],
--   refl,
-- end

-- instance Zmod_prime_field (p : {n : ℕ+ // nat.prime n.1}) : field (Zmod (p : ℕ+)) :=
-- { inv := has_inv.inv,
--   zero_ne_one := λ h, begin
--     have : 0 = 1 % p.1.1 := subtype.mk.inj h,
--     rw nat.mod_eq_of_lt (p.2.gt_one) at this,
--     exact nat.no_confusion this,
--   end,
--   mul_inv_cancel := mul_inv_cancel_aux,
--   inv_mul_cancel := λ a ha, by rw mul_comm; exact mul_inv_cancel_aux a ha,
--   ..Zmod.comm_ring p.1 }

end Zmod


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

! This file was ported from Lean 3 source module data.fin.vec_notation
! leanprover-community/mathlib commit 63f84d91dd847f50bae04a01071f3a5491934e36
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Fin.Tuple.Basic
import Mathbin.Data.List.Range
import Mathbin.GroupTheory.GroupAction.Pi
import Mathbin.Meta.Univs

/-!
# Matrix and vector notation

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

This file defines notation for vectors and matrices. Given `a b c d : α`,
the notation allows us to write `![a, b, c, d] : fin 4 → α`.
Nesting vectors gives coefficients of a matrix, so `![![a, b], ![c, d]] : fin 2 → fin 2 → α`.
In later files we introduce `!![a, b; c, d]` as notation for `matrix.of ![![a, b], ![c, d]]`.

## Main definitions

* `vec_empty` is the empty vector (or `0` by `n` matrix) `![]`
* `vec_cons` prepends an entry to a vector, so `![a, b]` is `vec_cons a (vec_cons b vec_empty)`

## Implementation notes

The `simp` lemmas require that one of the arguments is of the form `vec_cons _ _`.
This ensures `simp` works with entries only when (some) entries are already given.
In other words, this notation will only appear in the output of `simp` if it
already appears in the input.

## Notations

The main new notation is `![a, b]`, which gets expanded to `vec_cons a (vec_cons b vec_empty)`.

## Examples

Examples of usage can be found in the `test/matrix.lean` file.
-/

namespace Matrix

universe u

variable {α : Type u}

section MatrixNotation

#print Matrix.vecEmpty /-
/-- `![]` is the vector with no entries. -/
def vecEmpty : Fin 0 → α :=
  Fin.elim0'
#align matrix.vec_empty Matrix.vecEmpty
-/

#print Matrix.vecCons /-
/-- `vec_cons h t` prepends an entry `h` to a vector `t`.

The inverse functions are `vec_head` and `vec_tail`.
The notation `![a, b, ...]` expands to `vec_cons a (vec_cons b ...)`.
-/
def vecCons {n : ℕ} (h : α) (t : Fin n → α) : Fin n.succ → α :=
  Fin.cons h t
#align matrix.vec_cons Matrix.vecCons
-/

-- mathport name: «expr![ ,]»
notation3"!["(l", "* => foldr (h t => vecCons h t) vecEmpty)"]" => l

#print Matrix.vecHead /-
/-- `vec_head v` gives the first entry of the vector `v` -/
def vecHead {n : ℕ} (v : Fin n.succ → α) : α :=
  v 0
#align matrix.vec_head Matrix.vecHead
-/

#print Matrix.vecTail /-
/-- `vec_tail v` gives a vector consisting of all entries of `v` except the first -/
def vecTail {n : ℕ} (v : Fin n.succ → α) : Fin n → α :=
  v ∘ Fin.succ
#align matrix.vec_tail Matrix.vecTail
-/

variable {m n : ℕ}

#print PiFin.hasRepr /-
/-- Use `![...]` notation for displaying a vector `fin n → α`, for example:

```
#eval ![1, 2] + ![3, 4] -- ![4, 6]
```
-/
instance PiFin.hasRepr [Repr α] : Repr (Fin n → α)
    where repr f :=
    "![" ++ String.intercalate ", " ((List.finRange n).map fun n => repr (f n)) ++ "]"
#align pi_fin.has_repr PiFin.hasRepr
-/

end MatrixNotation

variable {m n o : ℕ} {m' n' o' : Type _}

#print Matrix.empty_eq /-
theorem empty_eq (v : Fin 0 → α) : v = ![] :=
  Subsingleton.elim _ _
#align matrix.empty_eq Matrix.empty_eq
-/

section Val

#print Matrix.head_fin_const /-
@[simp]
theorem head_fin_const (a : α) : (vecHead fun i : Fin (n + 1) => a) = a :=
  rfl
#align matrix.head_fin_const Matrix.head_fin_const
-/

/- warning: matrix.cons_val_zero -> Matrix.cons_val_zero is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {m : Nat} (x : α) (u : (Fin m) -> α), Eq.{succ u1} α (Matrix.vecCons.{u1} α m x u (OfNat.ofNat.{0} (Fin (Nat.succ m)) 0 (OfNat.mk.{0} (Fin (Nat.succ m)) 0 (Zero.zero.{0} (Fin (Nat.succ m)) (Fin.hasZeroOfNeZero (Nat.succ m) (NeZero.succ m)))))) x
but is expected to have type
  forall {α : Type.{u1}} {m : Nat} (x : α) (u : (Fin m) -> α), Eq.{succ u1} α (Matrix.vecCons.{u1} α m x u (OfNat.ofNat.{0} (Fin (Nat.succ m)) 0 (Fin.instOfNatFin (Nat.succ m) 0 (NeZero.succ m)))) x
Case conversion may be inaccurate. Consider using '#align matrix.cons_val_zero Matrix.cons_val_zeroₓ'. -/
@[simp]
theorem cons_val_zero (x : α) (u : Fin m → α) : vecCons x u 0 = x :=
  rfl
#align matrix.cons_val_zero Matrix.cons_val_zero

#print Matrix.cons_val_zero' /-
theorem cons_val_zero' (h : 0 < m.succ) (x : α) (u : Fin m → α) : vecCons x u ⟨0, h⟩ = x :=
  rfl
#align matrix.cons_val_zero' Matrix.cons_val_zero'
-/

#print Matrix.cons_val_succ /-
@[simp]
theorem cons_val_succ (x : α) (u : Fin m → α) (i : Fin m) : vecCons x u i.succ = u i := by
  simp [vec_cons]
#align matrix.cons_val_succ Matrix.cons_val_succ
-/

#print Matrix.cons_val_succ' /-
@[simp]
theorem cons_val_succ' {i : ℕ} (h : i.succ < m.succ) (x : α) (u : Fin m → α) :
    vecCons x u ⟨i.succ, h⟩ = u ⟨i, Nat.lt_of_succ_lt_succ h⟩ := by
  simp only [vec_cons, Fin.cons, Fin.cases_succ']
#align matrix.cons_val_succ' Matrix.cons_val_succ'
-/

#print Matrix.head_cons /-
@[simp]
theorem head_cons (x : α) (u : Fin m → α) : vecHead (vecCons x u) = x :=
  rfl
#align matrix.head_cons Matrix.head_cons
-/

#print Matrix.tail_cons /-
@[simp]
theorem tail_cons (x : α) (u : Fin m → α) : vecTail (vecCons x u) = u :=
  by
  ext
  simp [vec_tail]
#align matrix.tail_cons Matrix.tail_cons
-/

/- warning: matrix.empty_val' -> Matrix.empty_val' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {n' : Type.{u2}} (j : n'), Eq.{succ u1} ((Fin (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> α) (fun (i : Fin (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) => Matrix.vecEmpty.{max u2 u1} (n' -> α) i j) (Matrix.vecEmpty.{u1} α)
but is expected to have type
  forall {α : Type.{u2}} {n' : Type.{u1}} (j : n'), Eq.{succ u2} ((Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> α) (fun (i : Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) => Matrix.vecEmpty.{max u2 u1} (n' -> α) i j) (Matrix.vecEmpty.{u2} α)
Case conversion may be inaccurate. Consider using '#align matrix.empty_val' Matrix.empty_val'ₓ'. -/
@[simp]
theorem empty_val' {n' : Type _} (j : n') : (fun i => (![] : Fin 0 → n' → α) i j) = ![] :=
  empty_eq _
#align matrix.empty_val' Matrix.empty_val'

#print Matrix.cons_head_tail /-
@[simp]
theorem cons_head_tail (u : Fin m.succ → α) : vecCons (vecHead u) (vecTail u) = u :=
  Fin.cons_self_tail _
#align matrix.cons_head_tail Matrix.cons_head_tail
-/

/- warning: matrix.range_cons -> Matrix.range_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {n : Nat} (x : α) (u : (Fin n) -> α), Eq.{succ u1} (Set.{u1} α) (Set.range.{u1, 1} α (Fin (Nat.succ n)) (Matrix.vecCons.{u1} α n x u)) (Union.union.{u1} (Set.{u1} α) (Set.hasUnion.{u1} α) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) x) (Set.range.{u1, 1} α (Fin n) u))
but is expected to have type
  forall {α : Type.{u1}} {n : Nat} (x : α) (u : (Fin n) -> α), Eq.{succ u1} (Set.{u1} α) (Set.range.{u1, 1} α (Fin (Nat.succ n)) (Matrix.vecCons.{u1} α n x u)) (Union.union.{u1} (Set.{u1} α) (Set.instUnionSet.{u1} α) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) x) (Set.range.{u1, 1} α (Fin n) u))
Case conversion may be inaccurate. Consider using '#align matrix.range_cons Matrix.range_consₓ'. -/
@[simp]
theorem range_cons (x : α) (u : Fin n → α) : Set.range (vecCons x u) = {x} ∪ Set.range u :=
  Set.ext fun y => by simp [Fin.exists_fin_succ, eq_comm]
#align matrix.range_cons Matrix.range_cons

#print Matrix.range_empty /-
@[simp]
theorem range_empty (u : Fin 0 → α) : Set.range u = ∅ :=
  Set.range_eq_empty _
#align matrix.range_empty Matrix.range_empty
-/

#print Matrix.range_cons_empty /-
@[simp]
theorem range_cons_empty (x : α) (u : Fin 0 → α) : Set.range (Matrix.vecCons x u) = {x} := by
  rw [range_cons, range_empty, Set.union_empty]
#align matrix.range_cons_empty Matrix.range_cons_empty
-/

#print Matrix.range_cons_cons_empty /-
@[simp]
theorem range_cons_cons_empty (x y : α) (u : Fin 0 → α) :
    Set.range (vecCons x <| vecCons y u) = {x, y} := by
  rw [range_cons, range_cons_empty, Set.singleton_union]
#align matrix.range_cons_cons_empty Matrix.range_cons_cons_empty
-/

#print Matrix.vecCons_const /-
@[simp]
theorem vecCons_const (a : α) : (vecCons a fun k : Fin n => a) = fun _ => a :=
  funext <| Fin.forall_fin_succ.2 ⟨rfl, cons_val_succ _ _⟩
#align matrix.vec_cons_const Matrix.vecCons_const
-/

#print Matrix.vec_single_eq_const /-
theorem vec_single_eq_const (a : α) : ![a] = fun _ => a :=
  funext <| Unique.forall_iff.2 rfl
#align matrix.vec_single_eq_const Matrix.vec_single_eq_const
-/

#print Matrix.cons_val_one /-
/-- `![a, b, ...] 1` is equal to `b`.

  The simplifier needs a special lemma for length `≥ 2`, in addition to
  `cons_val_succ`, because `1 : fin 1 = 0 : fin 1`.
-/
@[simp]
theorem cons_val_one (x : α) (u : Fin m.succ → α) : vecCons x u 1 = vecHead u :=
  by
  rw [← Fin.succ_zero_eq_one, cons_val_succ]
  rfl
#align matrix.cons_val_one Matrix.cons_val_one
-/

#print Matrix.cons_val_fin_one /-
@[simp]
theorem cons_val_fin_one (x : α) (u : Fin 0 → α) (i : Fin 1) : vecCons x u i = x :=
  by
  refine' Fin.forall_fin_one.2 _ i
  rfl
#align matrix.cons_val_fin_one Matrix.cons_val_fin_one
-/

#print Matrix.cons_fin_one /-
theorem cons_fin_one (x : α) (u : Fin 0 → α) : vecCons x u = fun _ => x :=
  funext (cons_val_fin_one x u)
#align matrix.cons_fin_one Matrix.cons_fin_one
-/

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `reflect_name #[] -/
/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `reflect_name #[] -/
unsafe instance _root_.pi_fin.reflect [reflected_univ.{u}] [reflected _ α] [has_reflect α] :
    ∀ {n}, has_reflect (Fin n → α)
  | 0, v =>
    (Subsingleton.elim vecEmpty v).rec
      ((by
            trace
              "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `reflect_name #[]" :
            reflected _ @vecEmpty.{u}).subst
        q(α))
  | n + 1, v =>
    (cons_head_tail v).rec <|
      (by
            trace
              "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:73:14: unsupported tactic `reflect_name #[]" :
            reflected _ @vecCons.{u}).subst₄
        q(α) q(n) q(_) (_root_.pi_fin.reflect _)
#align pi_fin.reflect pi_fin.reflect

/-- Convert a vector of pexprs to the pexpr constructing that vector.-/
unsafe def _root_.pi_fin.to_pexpr : ∀ {n}, (Fin n → pexpr) → pexpr
  | 0, v => ``(![])
  | n + 1, v => ``(vecCons $(v 0) $(_root_.pi_fin.to_pexpr <| vecTail v))
#align pi_fin.to_pexpr pi_fin.to_pexpr

/-! ### Numeral (`bit0` and `bit1`) indices
The following definitions and `simp` lemmas are to allow any
numeral-indexed element of a vector given with matrix notation to
be extracted by `simp` (even when the numeral is larger than the
number of elements in the vector, which is taken modulo that number
of elements by virtue of the semantics of `bit0` and `bit1` and of
addition on `fin n`).
-/

#print Matrix.vecAppend /-
/-- `vec_append ho u v` appends two vectors of lengths `m` and `n` to produce
one of length `o = m + n`. This is a variant of `fin.append` with an additional `ho` argument,
which provides control of definitional equality for the vector length.

This turns out to be helpful when providing simp lemmas to reduce `![a, b, c] n`, and also means
that `vec_append ho u v 0` is valid. `fin.append u v 0` is not valid in this case because there is
no `has_zero (fin (m + n))` instance. -/
def vecAppend {α : Type _} {o : ℕ} (ho : o = m + n) (u : Fin m → α) (v : Fin n → α) : Fin o → α :=
  Fin.append u v ∘ Fin.cast ho
#align matrix.vec_append Matrix.vecAppend
-/

/- warning: matrix.vec_append_eq_ite -> Matrix.vecAppend_eq_ite is a dubious translation:
lean 3 declaration is
  forall {m : Nat} {n : Nat} {α : Type.{u1}} {o : Nat} (ho : Eq.{1} Nat o (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m n)) (u : (Fin m) -> α) (v : (Fin n) -> α), Eq.{succ u1} ((Fin o) -> α) (Matrix.vecAppend.{u1} m n α o ho u v) (fun (i : Fin o) => dite.{succ u1} α (LT.lt.{0} Nat Nat.hasLt ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) m) (Nat.decidableLt ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) m) (fun (h : LT.lt.{0} Nat Nat.hasLt ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) m) => u (Fin.mk m ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) h)) (fun (h : Not (LT.lt.{0} Nat Nat.hasLt ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) m)) => v (Fin.mk n (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) m) (Iff.mpr (LT.lt.{0} Nat (Preorder.toLT.{0} Nat (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder))) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat Nat.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) m) n) (LT.lt.{0} Nat (Preorder.toLT.{0} Nat (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat (AddSemigroup.toHasAdd.{0} Nat (AddCommSemigroup.toAddSemigroup.{0} Nat Nat.addCommSemigroup))) m n)) (tsub_lt_iff_left.{0} Nat Nat.addCommSemigroup (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder) (CanonicallyOrderedAddMonoid.existsAddOfLE.{0} Nat (CanonicallyOrderedCommSemiring.toCanonicallyOrderedAddMonoid.{0} Nat Nat.canonicallyOrderedCommSemiring)) (OrderedAddCommMonoid.to_covariantClass_left.{0} Nat (OrderedSemiring.toOrderedAddCommMonoid.{0} Nat Nat.orderedSemiring)) Nat.hasSub Nat.hasOrderedSub ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) m n (AddLeftCancelSemigroup.contravariant_add_le_of_contravariant_add_lt.{0} Nat (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Nat (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Nat (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)) (OrderedCancelAddCommMonoid.to_contravariantClass_left.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (le_of_not_lt.{0} Nat Nat.linearOrder m ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) h)) (Eq.subst.{1} Nat (fun (_x : Nat) => LT.lt.{0} Nat (Preorder.toLT.{0} Nat (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) (Fin o) Nat (HasLiftT.mk.{1, 1} (Fin o) Nat (CoeTCₓ.coe.{1, 1} (Fin o) Nat (coeBase.{1, 1} (Fin o) Nat (Fin.coeToNat o)))) i) _x) o (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m n) ho (Fin.property o i))))))
but is expected to have type
  forall {m : Nat} {n : Nat} {α : Type.{u1}} {o : Nat} (ho : Eq.{1} Nat o (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m n)) (u : (Fin m) -> α) (v : (Fin n) -> α), Eq.{succ u1} ((Fin o) -> α) (Matrix.vecAppend.{u1} m n α o ho u v) (fun (i : Fin o) => dite.{succ u1} α (LT.lt.{0} Nat instLTNat (Fin.val o i) m) (Nat.decLt (Fin.val o i) m) (fun (h : LT.lt.{0} Nat instLTNat (Fin.val o i) m) => u (Fin.mk m (Fin.val o i) h)) (fun (h : Not (LT.lt.{0} Nat instLTNat (Fin.val o i) m)) => v (Fin.mk n (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Fin.val o i) m) (Iff.mpr (LT.lt.{0} Nat (Preorder.toLT.{0} Nat (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder))) (HSub.hSub.{0, 0, 0} Nat Nat Nat (instHSub.{0} Nat instSubNat) (Fin.val o i) m) n) (LT.lt.{0} Nat (Preorder.toLT.{0} Nat (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder))) (Fin.val o i) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat (AddSemigroup.toAdd.{0} Nat (AddCommSemigroup.toAddSemigroup.{0} Nat Nat.addCommSemigroup))) m n)) (tsub_lt_iff_left.{0} Nat Nat.addCommSemigroup (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder) (CanonicallyOrderedAddMonoid.existsAddOfLE.{0} Nat (CanonicallyOrderedCommSemiring.toCanonicallyOrderedAddMonoid.{0} Nat Nat.canonicallyOrderedCommSemiring)) (OrderedAddCommMonoid.to_covariantClass_left.{0} Nat (OrderedSemiring.toOrderedAddCommMonoid.{0} Nat Nat.orderedSemiring)) instSubNat Nat.instOrderedSubNatInstLENatInstAddNatInstSubNat (Fin.val o i) m n (AddLeftCancelSemigroup.contravariant_add_le_of_contravariant_add_lt.{0} Nat (AddLeftCancelMonoid.toAddLeftCancelSemigroup.{0} Nat (AddCancelCommMonoid.toAddLeftCancelMonoid.{0} Nat (OrderedCancelAddCommMonoid.toCancelAddCommMonoid.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring) (OrderedCancelAddCommMonoid.to_contravariantClass_left.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))) (le_of_not_lt.{0} Nat Nat.linearOrder m (Fin.val o i) h)) (Eq.rec.{0, 1} Nat o (fun (x._@.Mathlib.Data.Fin.VecNotation._hyg.1668 : Nat) (h._@.Mathlib.Data.Fin.VecNotation._hyg.1669 : Eq.{1} Nat o x._@.Mathlib.Data.Fin.VecNotation._hyg.1668) => LT.lt.{0} Nat (Preorder.toLT.{0} Nat (PartialOrder.toPreorder.{0} Nat (LinearOrder.toPartialOrder.{0} Nat Nat.linearOrder))) (Fin.val o i) x._@.Mathlib.Data.Fin.VecNotation._hyg.1668) (Fin.isLt o i) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m n) ho)))))
Case conversion may be inaccurate. Consider using '#align matrix.vec_append_eq_ite Matrix.vecAppend_eq_iteₓ'. -/
theorem vecAppend_eq_ite {α : Type _} {o : ℕ} (ho : o = m + n) (u : Fin m → α) (v : Fin n → α) :
    vecAppend ho u v = fun i =>
      if h : (i : ℕ) < m then u ⟨i, h⟩
      else v ⟨(i : ℕ) - m, (tsub_lt_iff_left (le_of_not_lt h)).2 (ho ▸ i.property)⟩ :=
  by
  ext i
  rw [vec_append, Fin.append, Function.comp_apply, Fin.addCases]
  congr with hi
  simp only [eq_rec_constant]
  rfl
#align matrix.vec_append_eq_ite Matrix.vecAppend_eq_ite

/- warning: matrix.vec_append_apply_zero -> Matrix.vecAppend_apply_zero is a dubious translation:
lean 3 declaration is
  forall {m : Nat} {n : Nat} {α : Type.{u1}} {o : Nat} (ho : Eq.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) o (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) n)) (u : (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))))) -> α) (v : (Fin n) -> α), Eq.{succ u1} α (Matrix.vecAppend.{u1} (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) n α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) o (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) ho u v (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) o (One.one.{0} Nat Nat.hasOne))) 0 (OfNat.mk.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) o (One.one.{0} Nat Nat.hasOne))) 0 (Zero.zero.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) o (One.one.{0} Nat Nat.hasOne))) (Fin.hasZeroOfNeZero (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) o (One.one.{0} Nat Nat.hasOne)) (NeZero.succ o)))))) (u (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (One.one.{0} Nat Nat.hasOne))) 0 (OfNat.mk.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (One.one.{0} Nat Nat.hasOne))) 0 (Zero.zero.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (One.one.{0} Nat Nat.hasOne))) (Fin.hasZeroOfNeZero (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (One.one.{0} Nat Nat.hasOne)) (NeZero.succ m))))))
but is expected to have type
  forall {m : Nat} {n : Nat} {α : Type.{u1}} {o : Nat} (ho : Eq.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) o (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) n)) (u : (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) -> α) (v : (Fin n) -> α), Eq.{succ u1} α (Matrix.vecAppend.{u1} (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) n α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) o (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) ho u v (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) o (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) 0 (Fin.instOfNatFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) o (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) 0 (NeZero.succ o)))) (u (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))) 0 (Fin.instOfNatFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) 0 (NeZero.succ m))))
Case conversion may be inaccurate. Consider using '#align matrix.vec_append_apply_zero Matrix.vecAppend_apply_zeroₓ'. -/
@[simp]
theorem vecAppend_apply_zero {α : Type _} {o : ℕ} (ho : o + 1 = m + 1 + n) (u : Fin (m + 1) → α)
    (v : Fin n → α) : vecAppend ho u v 0 = u 0 :=
  rfl
#align matrix.vec_append_apply_zero Matrix.vecAppend_apply_zero

/- warning: matrix.empty_vec_append -> Matrix.empty_vecAppend is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {n : Nat} (v : (Fin n) -> α), Eq.{succ u1} ((Fin n) -> α) (Matrix.vecAppend.{u1} (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat (AddZeroClass.toHasZero.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))))) n α n (Eq.symm.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat (AddZeroClass.toHasAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat (AddZeroClass.toHasZero.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))))) n) n (zero_add.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) n)) (Matrix.vecEmpty.{u1} α) v) v
but is expected to have type
  forall {α : Type.{u1}} {n : Nat} (v : (Fin n) -> α), Eq.{succ u1} ((Fin n) -> α) (Matrix.vecAppend.{u1} (OfNat.ofNat.{0} Nat 0 (Zero.toOfNat0.{0} Nat (AddZeroClass.toZero.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) n α n (Eq.symm.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat (AddZeroClass.toAdd.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid))) (OfNat.ofNat.{0} Nat 0 (Zero.toOfNat0.{0} Nat (AddZeroClass.toZero.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid)))) n) n (zero_add.{0} Nat (AddMonoid.toAddZeroClass.{0} Nat Nat.addMonoid) n)) (Matrix.vecEmpty.{u1} α) v) v
Case conversion may be inaccurate. Consider using '#align matrix.empty_vec_append Matrix.empty_vecAppendₓ'. -/
@[simp]
theorem empty_vecAppend (v : Fin n → α) : vecAppend (zero_add _).symm ![] v = v :=
  by
  ext
  simp [vec_append_eq_ite]
#align matrix.empty_vec_append Matrix.empty_vecAppend

#print Matrix.cons_vecAppend /-
@[simp]
theorem cons_vecAppend (ho : o + 1 = m + 1 + n) (x : α) (u : Fin m → α) (v : Fin n → α) :
    vecAppend ho (vecCons x u) v =
      vecCons x
        (vecAppend (by rwa [add_assoc, add_comm 1, ← add_assoc, add_right_cancel_iff] at ho) u v) :=
  by
  ext i
  simp_rw [vec_append_eq_ite]
  split_ifs with h
  · rcases i with ⟨⟨⟩ | i, hi⟩
    · simp
    · simp only [Nat.succ_eq_add_one, add_lt_add_iff_right, Fin.val_mk] at h
      simp [h]
  · rcases i with ⟨⟨⟩ | i, hi⟩
    · simpa using h
    · rw [not_lt, Fin.val_mk, Nat.succ_eq_add_one, add_le_add_iff_right] at h
      simp [h]
#align matrix.cons_vec_append Matrix.cons_vecAppend
-/

#print Matrix.vecAlt0 /-
/-- `vec_alt0 v` gives a vector with half the length of `v`, with
only alternate elements (even-numbered). -/
def vecAlt0 (hm : m = n + n) (v : Fin m → α) (k : Fin n) : α :=
  v ⟨(k : ℕ) + k, hm.symm ▸ add_lt_add k.property k.property⟩
#align matrix.vec_alt0 Matrix.vecAlt0
-/

#print Matrix.vecAlt1 /-
/-- `vec_alt1 v` gives a vector with half the length of `v`, with
only alternate elements (odd-numbered). -/
def vecAlt1 (hm : m = n + n) (v : Fin m → α) (k : Fin n) : α :=
  v ⟨(k : ℕ) + k + 1, hm.symm ▸ Nat.add_succ_lt_add k.property k.property⟩
#align matrix.vec_alt1 Matrix.vecAlt1
-/

#print Matrix.vecAlt0_vecAppend /-
theorem vecAlt0_vecAppend (v : Fin n → α) : vecAlt0 rfl (vecAppend rfl v v) = v ∘ bit0 :=
  by
  ext i
  simp_rw [Function.comp, bit0, vec_alt0, vec_append_eq_ite]
  split_ifs with h <;> congr
  · rw [Fin.val_mk] at h
    simp only [Fin.ext_iff, Fin.val_add, Fin.val_mk]
    exact (Nat.mod_eq_of_lt h).symm
  · rw [Fin.val_mk, not_lt] at h
    simp only [Fin.ext_iff, Fin.val_add, Fin.val_mk, Nat.mod_eq_sub_mod h]
    refine' (Nat.mod_eq_of_lt _).symm
    rw [tsub_lt_iff_left h]
    exact add_lt_add i.property i.property
#align matrix.vec_alt0_vec_append Matrix.vecAlt0_vecAppend
-/

#print Matrix.vecAlt1_vecAppend /-
theorem vecAlt1_vecAppend (v : Fin (n + 1) → α) : vecAlt1 rfl (vecAppend rfl v v) = v ∘ bit1 :=
  by
  ext i
  simp_rw [Function.comp, vec_alt1, vec_append_eq_ite]
  cases n
  · simp
    congr
  · split_ifs with h <;> simp_rw [bit1, bit0] <;> congr
    · simp only [Fin.ext_iff, Fin.val_add, Fin.val_mk]
      rw [Fin.val_mk] at h
      rw [Fin.val_one]
      rw [Nat.mod_eq_of_lt (Nat.lt_of_succ_lt h)]
      rw [Nat.mod_eq_of_lt h]
    · rw [Fin.val_mk, not_lt] at h
      simp only [Fin.ext_iff, Fin.val_add, Fin.val_mk, Nat.mod_add_mod, Fin.val_one,
        Nat.mod_eq_sub_mod h]
      refine' (Nat.mod_eq_of_lt _).symm
      rw [tsub_lt_iff_left h]
      exact Nat.add_succ_lt_add i.property i.property
#align matrix.vec_alt1_vec_append Matrix.vecAlt1_vecAppend
-/

/- warning: matrix.vec_head_vec_alt0 -> Matrix.vecHead_vecAlt0 is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {m : Nat} {n : Nat} (hm : Eq.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) n (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))))) (v : (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))))) -> α), Eq.{succ u1} α (Matrix.vecHead.{u1} α n (Matrix.vecAlt0.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (OfNat.ofNat.{0} Nat 2 (OfNat.mk.{0} Nat 2 (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))))) (Nat.succ n) hm v)) (v (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))) 0 (OfNat.mk.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))) 0 (Zero.zero.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne)))) (Fin.hasZeroOfNeZero (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (bit0.{0} Nat Nat.hasAdd (One.one.{0} Nat Nat.hasOne))) (NeZero.succ (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) m (One.one.{0} Nat Nat.hasOne))))))))
but is expected to have type
  forall {α : Type.{u1}} {m : Nat} {n : Nat} (hm : Eq.{1} Nat (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) n (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))))) (v : (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)))) -> α), Eq.{succ u1} α (Matrix.vecHead.{u1} α n (Matrix.vecAlt0.{u1} α (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) (Nat.succ n) hm v)) (v (OfNat.ofNat.{0} (Fin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2)))) 0 (Fin.instOfNatFin (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 2 (instOfNatNat 2))) 0 (NeZero.succ (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) m (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)))))))
Case conversion may be inaccurate. Consider using '#align matrix.vec_head_vec_alt0 Matrix.vecHead_vecAlt0ₓ'. -/
@[simp]
theorem vecHead_vecAlt0 (hm : m + 2 = n + 1 + (n + 1)) (v : Fin (m + 2) → α) :
    vecHead (vecAlt0 hm v) = v 0 :=
  rfl
#align matrix.vec_head_vec_alt0 Matrix.vecHead_vecAlt0

#print Matrix.vecHead_vecAlt1 /-
@[simp]
theorem vecHead_vecAlt1 (hm : m + 2 = n + 1 + (n + 1)) (v : Fin (m + 2) → α) :
    vecHead (vecAlt1 hm v) = v 1 := by simp [vec_head, vec_alt1]
#align matrix.vec_head_vec_alt1 Matrix.vecHead_vecAlt1
-/

#print Matrix.cons_vec_bit0_eq_alt0 /-
@[simp]
theorem cons_vec_bit0_eq_alt0 (x : α) (u : Fin n → α) (i : Fin (n + 1)) :
    vecCons x u (bit0 i) = vecAlt0 rfl (vecAppend rfl (vecCons x u) (vecCons x u)) i := by
  rw [vec_alt0_vec_append]
#align matrix.cons_vec_bit0_eq_alt0 Matrix.cons_vec_bit0_eq_alt0
-/

#print Matrix.cons_vec_bit1_eq_alt1 /-
@[simp]
theorem cons_vec_bit1_eq_alt1 (x : α) (u : Fin n → α) (i : Fin (n + 1)) :
    vecCons x u (bit1 i) = vecAlt1 rfl (vecAppend rfl (vecCons x u) (vecCons x u)) i := by
  rw [vec_alt1_vec_append]
#align matrix.cons_vec_bit1_eq_alt1 Matrix.cons_vec_bit1_eq_alt1
-/

#print Matrix.cons_vecAlt0 /-
@[simp]
theorem cons_vecAlt0 (h : m + 1 + 1 = n + 1 + (n + 1)) (x y : α) (u : Fin m → α) :
    vecAlt0 h (vecCons x (vecCons y u)) =
      vecCons x
        (vecAlt0
          (by
            rwa [add_assoc n, add_comm 1, ← add_assoc, ← add_assoc, add_right_cancel_iff,
              add_right_cancel_iff] at h)
          u) :=
  by
  ext i
  simp_rw [vec_alt0]
  rcases i with ⟨⟨⟩ | i, hi⟩
  · rfl
  · simp [vec_alt0, Nat.add_succ, Nat.succ_add]
#align matrix.cons_vec_alt0 Matrix.cons_vecAlt0
-/

#print Matrix.empty_vecAlt0 /-
-- Although proved by simp, extracting element 8 of a five-element
-- vector does not work by simp unless this lemma is present.
@[simp]
theorem empty_vecAlt0 (α) {h} : vecAlt0 h (![] : Fin 0 → α) = ![] := by simp
#align matrix.empty_vec_alt0 Matrix.empty_vecAlt0
-/

#print Matrix.cons_vecAlt1 /-
@[simp]
theorem cons_vecAlt1 (h : m + 1 + 1 = n + 1 + (n + 1)) (x y : α) (u : Fin m → α) :
    vecAlt1 h (vecCons x (vecCons y u)) =
      vecCons y
        (vecAlt1
          (by
            rwa [add_assoc n, add_comm 1, ← add_assoc, ← add_assoc, add_right_cancel_iff,
              add_right_cancel_iff] at h)
          u) :=
  by
  ext i
  simp_rw [vec_alt1]
  rcases i with ⟨⟨⟩ | i, hi⟩
  · rfl
  · simp [vec_alt1, Nat.add_succ, Nat.succ_add]
#align matrix.cons_vec_alt1 Matrix.cons_vecAlt1
-/

#print Matrix.empty_vecAlt1 /-
-- Although proved by simp, extracting element 9 of a five-element
-- vector does not work by simp unless this lemma is present.
@[simp]
theorem empty_vecAlt1 (α) {h} : vecAlt1 h (![] : Fin 0 → α) = ![] := by simp
#align matrix.empty_vec_alt1 Matrix.empty_vecAlt1
-/

end Val

section Smul

variable {M : Type _} [SMul M α]

/- warning: matrix.smul_empty -> Matrix.smul_empty is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {M : Type.{u2}} [_inst_1 : SMul.{u2, u1} M α] (x : M) (v : (Fin (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> α), Eq.{succ u1} ((Fin (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> α) (SMul.smul.{u2, u1} M ((Fin (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> α) (Function.hasSMul.{0, u2, u1} (Fin (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) M α _inst_1) x v) (Matrix.vecEmpty.{u1} α)
but is expected to have type
  forall {α : Type.{u2}} {M : Type.{u1}} [_inst_1 : SMul.{u1, u2} M α] (x : M) (v : (Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> α), Eq.{succ u2} ((Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> α) (HSMul.hSMul.{u1, u2, u2} M ((Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> α) ((Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> α) (instHSMul.{u1, u2} M ((Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> α) (Pi.instSMul.{0, u2, u1} (Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) M (fun (a._@.Mathlib.Data.Fin.VecNotation._hyg.3287 : Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) => α) (fun (i : Fin (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) => _inst_1))) x v) (Matrix.vecEmpty.{u2} α)
Case conversion may be inaccurate. Consider using '#align matrix.smul_empty Matrix.smul_emptyₓ'. -/
@[simp]
theorem smul_empty (x : M) (v : Fin 0 → α) : x • v = ![] :=
  empty_eq _
#align matrix.smul_empty Matrix.smul_empty

/- warning: matrix.smul_cons -> Matrix.smul_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {n : Nat} {M : Type.{u2}} [_inst_1 : SMul.{u2, u1} M α] (x : M) (y : α) (v : (Fin n) -> α), Eq.{succ u1} ((Fin (Nat.succ n)) -> α) (SMul.smul.{u2, u1} M ((Fin (Nat.succ n)) -> α) (Function.hasSMul.{0, u2, u1} (Fin (Nat.succ n)) M α _inst_1) x (Matrix.vecCons.{u1} α n y v)) (Matrix.vecCons.{u1} α n (SMul.smul.{u2, u1} M α _inst_1 x y) (SMul.smul.{u2, u1} M ((Fin n) -> α) (Function.hasSMul.{0, u2, u1} (Fin n) M α _inst_1) x v))
but is expected to have type
  forall {α : Type.{u2}} {n : Nat} {M : Type.{u1}} [_inst_1 : SMul.{u1, u2} M α] (x : M) (y : α) (v : (Fin n) -> α), Eq.{succ u2} ((Fin (Nat.succ n)) -> α) (HSMul.hSMul.{u1, u2, u2} M ((Fin (Nat.succ n)) -> α) ((Fin (Nat.succ n)) -> α) (instHSMul.{u1, u2} M ((Fin (Nat.succ n)) -> α) (Pi.instSMul.{0, u2, u1} (Fin (Nat.succ n)) M (fun (a._@.Mathlib.Data.Fin.VecNotation._hyg.29 : Fin (Nat.succ n)) => α) (fun (i : Fin (Nat.succ n)) => _inst_1))) x (Matrix.vecCons.{u2} α n y v)) (Matrix.vecCons.{u2} α n (HSMul.hSMul.{u1, u2, u2} M α α (instHSMul.{u1, u2} M α _inst_1) x y) (HSMul.hSMul.{u1, u2, u2} M ((Fin n) -> α) ((Fin n) -> α) (instHSMul.{u1, u2} M ((Fin n) -> α) (Pi.instSMul.{0, u2, u1} (Fin n) M (fun (a._@.Mathlib.Data.Fin.VecNotation._hyg.3325 : Fin n) => α) (fun (i : Fin n) => _inst_1))) x v))
Case conversion may be inaccurate. Consider using '#align matrix.smul_cons Matrix.smul_consₓ'. -/
@[simp]
theorem smul_cons (x : M) (y : α) (v : Fin n → α) : x • vecCons y v = vecCons (x • y) (x • v) :=
  by
  ext i
  refine' Fin.cases _ _ i <;> simp
#align matrix.smul_cons Matrix.smul_cons

end Smul

section Add

variable [Add α]

#print Matrix.empty_add_empty /-
@[simp]
theorem empty_add_empty (v w : Fin 0 → α) : v + w = ![] :=
  empty_eq _
#align matrix.empty_add_empty Matrix.empty_add_empty
-/

#print Matrix.cons_add /-
@[simp]
theorem cons_add (x : α) (v : Fin n → α) (w : Fin n.succ → α) :
    vecCons x v + w = vecCons (x + vecHead w) (v + vecTail w) :=
  by
  ext i
  refine' Fin.cases _ _ i <;> simp [vec_head, vec_tail]
#align matrix.cons_add Matrix.cons_add
-/

#print Matrix.add_cons /-
@[simp]
theorem add_cons (v : Fin n.succ → α) (y : α) (w : Fin n → α) :
    v + vecCons y w = vecCons (vecHead v + y) (vecTail v + w) :=
  by
  ext i
  refine' Fin.cases _ _ i <;> simp [vec_head, vec_tail]
#align matrix.add_cons Matrix.add_cons
-/

#print Matrix.cons_add_cons /-
@[simp]
theorem cons_add_cons (x : α) (v : Fin n → α) (y : α) (w : Fin n → α) :
    vecCons x v + vecCons y w = vecCons (x + y) (v + w) := by simp
#align matrix.cons_add_cons Matrix.cons_add_cons
-/

#print Matrix.head_add /-
@[simp]
theorem head_add (a b : Fin n.succ → α) : vecHead (a + b) = vecHead a + vecHead b :=
  rfl
#align matrix.head_add Matrix.head_add
-/

#print Matrix.tail_add /-
@[simp]
theorem tail_add (a b : Fin n.succ → α) : vecTail (a + b) = vecTail a + vecTail b :=
  rfl
#align matrix.tail_add Matrix.tail_add
-/

end Add

section Sub

variable [Sub α]

#print Matrix.empty_sub_empty /-
@[simp]
theorem empty_sub_empty (v w : Fin 0 → α) : v - w = ![] :=
  empty_eq _
#align matrix.empty_sub_empty Matrix.empty_sub_empty
-/

#print Matrix.cons_sub /-
@[simp]
theorem cons_sub (x : α) (v : Fin n → α) (w : Fin n.succ → α) :
    vecCons x v - w = vecCons (x - vecHead w) (v - vecTail w) :=
  by
  ext i
  refine' Fin.cases _ _ i <;> simp [vec_head, vec_tail]
#align matrix.cons_sub Matrix.cons_sub
-/

#print Matrix.sub_cons /-
@[simp]
theorem sub_cons (v : Fin n.succ → α) (y : α) (w : Fin n → α) :
    v - vecCons y w = vecCons (vecHead v - y) (vecTail v - w) :=
  by
  ext i
  refine' Fin.cases _ _ i <;> simp [vec_head, vec_tail]
#align matrix.sub_cons Matrix.sub_cons
-/

#print Matrix.cons_sub_cons /-
@[simp]
theorem cons_sub_cons (x : α) (v : Fin n → α) (y : α) (w : Fin n → α) :
    vecCons x v - vecCons y w = vecCons (x - y) (v - w) := by simp
#align matrix.cons_sub_cons Matrix.cons_sub_cons
-/

#print Matrix.head_sub /-
@[simp]
theorem head_sub (a b : Fin n.succ → α) : vecHead (a - b) = vecHead a - vecHead b :=
  rfl
#align matrix.head_sub Matrix.head_sub
-/

#print Matrix.tail_sub /-
@[simp]
theorem tail_sub (a b : Fin n.succ → α) : vecTail (a - b) = vecTail a - vecTail b :=
  rfl
#align matrix.tail_sub Matrix.tail_sub
-/

end Sub

section Zero

variable [Zero α]

#print Matrix.zero_empty /-
@[simp]
theorem zero_empty : (0 : Fin 0 → α) = ![] :=
  empty_eq _
#align matrix.zero_empty Matrix.zero_empty
-/

#print Matrix.cons_zero_zero /-
@[simp]
theorem cons_zero_zero : vecCons (0 : α) (0 : Fin n → α) = 0 :=
  by
  ext (i j)
  refine' Fin.cases _ _ i
  · rfl
  simp
#align matrix.cons_zero_zero Matrix.cons_zero_zero
-/

#print Matrix.head_zero /-
@[simp]
theorem head_zero : vecHead (0 : Fin n.succ → α) = 0 :=
  rfl
#align matrix.head_zero Matrix.head_zero
-/

#print Matrix.tail_zero /-
@[simp]
theorem tail_zero : vecTail (0 : Fin n.succ → α) = 0 :=
  rfl
#align matrix.tail_zero Matrix.tail_zero
-/

#print Matrix.cons_eq_zero_iff /-
@[simp]
theorem cons_eq_zero_iff {v : Fin n → α} {x : α} : vecCons x v = 0 ↔ x = 0 ∧ v = 0 :=
  ⟨fun h =>
    ⟨congr_fun h 0, by
      convert congr_arg vec_tail h
      simp⟩,
    fun ⟨hx, hv⟩ => by simp [hx, hv]⟩
#align matrix.cons_eq_zero_iff Matrix.cons_eq_zero_iff
-/

open Classical

#print Matrix.cons_nonzero_iff /-
theorem cons_nonzero_iff {v : Fin n → α} {x : α} : vecCons x v ≠ 0 ↔ x ≠ 0 ∨ v ≠ 0 :=
  ⟨fun h => not_and_or.mp (h ∘ cons_eq_zero_iff.mpr), fun h =>
    mt cons_eq_zero_iff.mp (not_and_or.mpr h)⟩
#align matrix.cons_nonzero_iff Matrix.cons_nonzero_iff
-/

end Zero

section Neg

variable [Neg α]

#print Matrix.neg_empty /-
@[simp]
theorem neg_empty (v : Fin 0 → α) : -v = ![] :=
  empty_eq _
#align matrix.neg_empty Matrix.neg_empty
-/

#print Matrix.neg_cons /-
@[simp]
theorem neg_cons (x : α) (v : Fin n → α) : -vecCons x v = vecCons (-x) (-v) :=
  by
  ext i
  refine' Fin.cases _ _ i <;> simp
#align matrix.neg_cons Matrix.neg_cons
-/

#print Matrix.head_neg /-
@[simp]
theorem head_neg (a : Fin n.succ → α) : vecHead (-a) = -vecHead a :=
  rfl
#align matrix.head_neg Matrix.head_neg
-/

#print Matrix.tail_neg /-
@[simp]
theorem tail_neg (a : Fin n.succ → α) : vecTail (-a) = -vecTail a :=
  rfl
#align matrix.tail_neg Matrix.tail_neg
-/

end Neg

end Matrix