/-
Copyright (c) 2021 Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bhavik Mehta
-/
import .triangle_removal
import combinatorics.additive.salem_spencer

/-!
# The corners theorem and Roth's theorem

This file proves the corners theorem and Roth's theorem.
-/

open_locale classical
open finset

variables {N : ℕ}

/-- A corner in `s` is three points of the form `(x, y), (x + h, y), (x, y + h)` with `0 < h`. Here,
we record `x`, `y`, `h`. -/
def is_corner (s : finset (ℕ × ℕ)) : ℕ → ℕ → ℕ → Prop :=
λ x y h, (x, y) ∈ s ∧ (x + h, y) ∈ s ∧ (x, y + h) ∈ s

/-- A corner in `s` is three points of the form `(x, y), (x + h, y), (x, y + h)` with `h < 0`. Here,
we record `x`, `y`, `h`. Note that we use a slightly different formulation because of natural
subtraction.-/
def is_anticorner (s : finset (ℕ × ℕ)) : ℕ → ℕ → ℕ → Prop :=
λ x y h, (x, y) ∈ s ∧ (h ≤ x ∧ (x - h, y) ∈ s) ∧ (h ≤ y ∧ (x, y - h) ∈ s)

/-- The type of vertices of the corners graph. -/
inductive corners_vertices (N : ℕ)
| horiz : fin N → corners_vertices
| vert : fin N → corners_vertices
| diag : fin (2 * N) → corners_vertices

open corners_vertices

instance (N : ℕ) : inhabited (corners_vertices (N + 1)) := ⟨vert default⟩

instance : fintype (corners_vertices N) :=
fintype.of_equiv (fin N ⊕ fin N ⊕ fin (2 * N))
{ to_fun := sum.elim horiz (sum.elim vert diag),
  inv_fun := λ i, corners_vertices.rec_on i sum.inl (sum.inr ∘ sum.inl) (sum.inr ∘ sum.inr),
  left_inv := λ i, by { rcases i with (_ | _ | _); refl },
  right_inv := λ i, by { rcases i with (_ | _ | _); refl } }

@[simp]
lemma card_corners_vertices : fintype.card (corners_vertices N) = 4 * N :=
by { simp only [fintype.of_equiv_card, fintype.card_fin, fintype.card_sum], ring }

/-- The edges of the corners graph. -/
inductive corners_edge (s : finset (ℕ × ℕ)) : corners_vertices N → corners_vertices N → Prop
| hv {h v : fin N} : ((h : ℕ), (v : ℕ)) ∈ s → corners_edge (horiz h) (vert v)
| vh {h v : fin N} : ((h : ℕ), (v : ℕ)) ∈ s → corners_edge (vert v) (horiz h)
| hd {h : fin N} {k : fin (2 * N)} :
    (h : ℕ) ≤ k → ((h : ℕ), (k : ℕ) - h) ∈ s → corners_edge (horiz h) (diag k)
| dh {h : fin N} {k : fin (2 * N)} :
    (h : ℕ) ≤ k → ((h : ℕ), (k : ℕ) - h) ∈ s → corners_edge (diag k) (horiz h)
| vd {v : fin N} {k : fin (2 * N)} :
    (v : ℕ) ≤ k → ((k : ℕ) - v, (v : ℕ)) ∈ s → corners_edge (vert v) (diag k)
| dv {v : fin N} {k : fin (2 * N)} :
    (v : ℕ) ≤ k → ((k : ℕ) - v, (v : ℕ)) ∈ s → corners_edge (diag k) (vert v)

variables {s : finset (ℕ × ℕ)}

section corners_edge
open corners_edge

lemma corners_edge_symm : ∀ (x y : corners_vertices N), corners_edge s x y → corners_edge s y x
| _ _ (hv h) := vh h
| _ _ (vh h) := hv h
| _ _ (hd h₁ h₂) := dh h₁ h₂
| _ _ (dh h₁ h₂) := hd h₁ h₂
| _ _ (vd h₁ h₂) := dv h₁ h₂
| _ _ (dv h₁ h₂) := vd h₁ h₂

lemma corners_edge_irrefl : ∀ (x : corners_vertices N), ¬ corners_edge s x x.

/-- Picturing a `N × N` grid, this is the graph whose vertices are vertical, horizontal and diagonal
(one way) lines and whose edges are the pairs of lines that meet within `s`. -/
def corners_graph (N : ℕ) (s : finset (ℕ × ℕ)) : simple_graph (corners_vertices N) :=
{ adj := corners_edge s, symm := corners_edge_symm, loopless := corners_edge_irrefl }

@[simp] lemma corners_edge_horiz_vert {h v : fin N} :
  (corners_graph _ s).adj (horiz h) (vert v) ↔ ((h : ℕ), (v : ℕ)) ∈ s :=
⟨by { rintro ⟨⟩, assumption }, corners_edge.hv⟩

@[simp] lemma corners_edge_horiz_diag {h : fin N} {k} :
  (corners_graph _ s).adj (horiz h) (diag k) ↔ (h : ℕ) ≤ k ∧ ((h : ℕ), (k : ℕ) - h) ∈ s :=
⟨by { rintro ⟨⟩, tauto }, λ i, corners_edge.hd i.1 i.2⟩

@[simp] lemma corners_edge_vert_diag {v : fin N} {k} :
  (corners_graph _ s).adj (vert v) (diag k) ↔ (v : ℕ) ≤ k ∧ ((k : ℕ) - v, (v : ℕ)) ∈ s :=
⟨by { rintro ⟨⟩, tauto }, λ i, corners_edge.vd i.1 i.2⟩

/-- Throwaway tactic. -/
meta def sets_simp : tactic unit :=
`[ext] >> `[simp only [finset.mem_insert, finset.mem_singleton]] >> `[try {tauto}]

lemma corners_triple {s : finset (ℕ × ℕ)} {N : ℕ} :
  ∀ x y z, (corners_graph N s).adj x y → (corners_graph N s).adj x z → (corners_graph N s).adj y z →
    ∃ h v {k : fin (2 * N)}, {horiz h, vert v, diag k} = ({x, y, z} : finset (corners_vertices N)) ∧
      (corners_graph _ s).adj (horiz h) (vert v) ∧ (corners_graph _ s).adj (horiz h) (diag k) ∧
        (corners_graph _ s).adj (vert v) (diag k)
| _ _ _ h₁@(hv _) h₂@(hd _ _) h₃ := ⟨_, _, _, rfl, h₁, h₂, h₃⟩
| _ _ _ (vh h₁) (vd h₂ i₂) (hd h₃ i₃) := ⟨_, _, _, by sets_simp, hv h₁, hd h₃ i₃, vd h₂ i₂⟩
| _ _ _ (hd h₁ i₁) (hv h₂) (dv h₃ i₃) := ⟨_, _, _, by sets_simp, hv h₂, hd h₁ i₁, vd h₃ i₃⟩
| _ _ _ (dh h₁ i₁) (dv h₂ i₂) (hv h₃) := ⟨_, _, _, by sets_simp, hv h₃, hd h₁ i₁, vd h₂ i₂⟩
| _ _ _ (vd h₁ i₁) (vh h₂) (dh h₃ i₃) := ⟨_, _, _, by sets_simp, hv h₂, hd h₃ i₃, vd h₁ i₁⟩
| _ _ _ (dv h₁ i₁) (dh h₂ i₂) (vh h₃) := ⟨_, _, _, by sets_simp, hv h₃, hd h₂ i₂, vd h₁ i₁⟩

end corners_edge

/-- Maps an horizontal, a vertical and a diagonal line to their three points of intersection. -/
noncomputable def triangle_map : fin N × fin N × fin (2 * N) → finset (corners_vertices N) :=
λ hvk, {horiz hvk.1, vert hvk.2.1, diag hvk.2.2}

/-- State whether an horizontal, a vertical and a diagonal line meet within `s`. -/
@[derive decidable_pred]
def explicit_triangles (s : finset (ℕ × ℕ)) (N : ℕ) : fin N × fin N × fin (2 * N) → Prop :=
λ (i : fin N × fin N × fin (2 * N)),
  (↑i.1, ↑i.2.1) ∈ s ∧ ((i.1 : ℕ) ≤ i.2.2 ∧ (↑i.1, ↑i.2.2 - ↑i.1) ∈ s) ∧
    ((i.2.1 : ℕ) ≤ i.2.2 ∧ (↑i.2.2 - ↑i.2.1, ↑i.2.1) ∈ s)

lemma triangle_map_mem (x : fin N × fin N × fin (2 * N)) (hx : explicit_triangles s N x) :
  triangle_map x ∈ (corners_graph N s).triangle_finset :=
by simpa [simple_graph.mem_triangle_finset'', explicit_triangles, triangle_map] using hx

lemma triangle_map_injective :
  function.injective (triangle_map : _ → finset (corners_vertices N)) :=
begin
  rintro ⟨h₁, v₁, k₁⟩ ⟨h₂, v₂, k₂⟩,
  simpa only [triangle_map, finset.subset.antisymm_iff, subset_iff, mem_insert, mem_singleton,
    forall_eq_or_imp, forall_eq, prod.mk.inj_iff, or_false, false_or] using and.left,
end

lemma triangle_map_surj {t} (ht : t ∈ (corners_graph N s).triangle_finset) :
  ∃ a, explicit_triangles s N a ∧ triangle_map a = t :=
begin
  rw simple_graph.mem_triangle_finset''' at ht,
  obtain ⟨x, y, z, xy, xz, yz, rfl⟩ := ht,
  obtain ⟨h, v, k, i₀, i₁, i₂, i₃⟩ := corners_triple _ _ _ xy xz yz,
  exact ⟨⟨h, v, k⟩, ⟨by simpa using i₁, by simpa using i₂, by simpa using i₃⟩, i₀⟩,
end

lemma triangles_corners_graph {s : finset (ℕ × ℕ)} {N : ℕ} :
  (corners_graph N s).triangle_finset.card =
    (univ.filter (explicit_triangles s N)).card :=
begin
  refine (card_congr (λ a _, triangle_map a) (λ t ht, _) (λ x y _ _, _) (λ t ht, _)).symm,
  { apply triangle_map_mem _ (mem_filter.1 ht).2 },
  { apply triangle_map_injective },
  obtain ⟨_, ht', rfl⟩ := triangle_map_surj ht,
  exact ⟨w, by simpa using ht', rfl⟩,
end

lemma triangle_gives_corner_or_anticorner {h v : fin N} {k : fin (2 * N)}
  (hv : (↑h, ↑v) ∈ s)
  (hk₁ : (h : ℕ) ≤ k) (hk₁' : ((h : ℕ), (k : ℕ) - h) ∈ s)
  (vk₁ : (v : ℕ) ≤ k) (vk₁' : ((k : ℕ) - v, (v : ℕ)) ∈ s) :
  ((h : ℕ) + v ≤ k ∧ is_corner s h v (k - (h + v))) ∨
    ((k : ℕ) ≤ h + v ∧ is_anticorner s h v (h + v - k)) :=
begin
  cases le_total ((h : ℕ) + v) (k : ℕ) with hvk hvk,
  { refine or.inl ⟨hvk, hv, _, _⟩,
    { rwa [←nat.add_sub_assoc hvk, nat.add_sub_add_left] },
    { rwa [←nat.add_sub_assoc hvk, add_comm (v : ℕ), nat.add_sub_add_right] } },
  { have hvkh : (h : ℕ) + v - k ≤ h,
    { rwa [tsub_le_iff_right, add_le_add_iff_left], },
    have hvkv : (h : ℕ) + v - k ≤ v,
    { rwa [tsub_le_iff_right, add_comm, add_le_add_iff_left] },
    refine or.inr ⟨hvk, hv, ⟨hvkh, _⟩, ⟨hvkv, _⟩⟩,
    { convert vk₁' using 2,
      rw [tsub_eq_iff_eq_add_of_le hvkh, ←nat.sub_add_comm vk₁, nat.add_sub_of_le hvk,
        add_tsub_cancel_right] },
    { convert hk₁' using 2,
      rw [eq_tsub_iff_add_eq_of_le hk₁, add_comm, ←nat.add_sub_assoc hvkv, nat.sub_sub_self hvk] } }
end

lemma triangle_trivial_of_no_corners (cs : ∀ (x y h : ℕ), is_corner s x y h → h = 0)
  (as : ∀ (x y h : ℕ), is_anticorner s x y h → h = 0)
  {h v : fin N} {k : fin (2 * N)} (hv : (↑h, ↑v) ∈ s)
  (hk₁ : (h : ℕ) ≤ k) (hk₁' : ((h : ℕ), (k : ℕ) - h) ∈ s)
  (vk₁ : (v : ℕ) ≤ k) (vk₁' : ((k : ℕ) - v, (v : ℕ)) ∈ s) :
  (k : ℕ) = h + v :=
begin
  rcases triangle_gives_corner_or_anticorner hv hk₁ hk₁' vk₁ vk₁' with (⟨i₁, i₂⟩ | ⟨i₁, i₂⟩),
  { apply le_antisymm (nat.le_of_sub_eq_zero (cs _ _ _ i₂)) i₁ },
  { apply le_antisymm i₁ (nat.le_of_sub_eq_zero (as _ _ _ i₂)) },
end

lemma trivial_triangles_corners_graph {s : finset (ℕ × ℕ)} {n : ℕ}
  (cs : ∀ (x y h : ℕ), is_corner s x y h → h = 0)
  (as : ∀ (x y h : ℕ), is_anticorner s x y h → h = 0) :
  (corners_graph n s).triangle_finset.card ≤ n^2 :=
begin
  have : ((range n).product (range n)).card = n^2,
  { simp [sq] },
  rw [←this, triangles_corners_graph],
  refine card_le_card_of_inj_on (λ i, ⟨i.1, i.2.1⟩) _ _,
  { rintro ⟨h, v, k⟩ -,
    simp only [mem_range, mem_product],
    exact ⟨h.prop, v.prop⟩ },
  simp only [true_and, prod.forall, mem_filter, mem_univ, prod.mk.inj_iff, explicit_triangles],
  rintro h ⟨v, k₁⟩ ⟨hv, ⟨hk₁', hk₁⟩, vk₁', vk₁⟩ h₂ ⟨v₂, k₂⟩ ⟨-, ⟨hk₂', hk₂⟩, vk₂', vk₂⟩ ⟨heq, veq⟩,
  dsimp at *,
  rw subtype.coe_injective.eq_iff at heq veq,
  substs heq veq,
  simp only [true_and, prod.mk.inj_iff, eq_self_iff_true, subtype.ext_iff],
  rw triangle_trivial_of_no_corners cs as hv hk₁' hk₁ vk₁' vk₁,
  rw triangle_trivial_of_no_corners cs as hv hk₂' hk₂ vk₂' vk₂,
end

/-- The trivial triangles -/
def trivial_triangles (N : ℕ) (s : finset (ℕ × ℕ)) : finset (fin N × fin N × fin (2 * N)) :=
univ.filter (λ xyz, (↑xyz.1, ↑xyz.2.1) ∈ s ∧ (xyz.1 : ℕ) + xyz.2.1 = xyz.2.2)

lemma trivial_triangles_mem :
  ∀ x ∈ trivial_triangles N s, explicit_triangles s N x :=
λ ⟨h, v, k⟩ t,
begin
  simp only [trivial_triangles, true_and, mem_filter, mem_univ] at t,
  exact ⟨t.1, by simp [←t.2, t.1]⟩,
end

lemma card_trivial_triangles (hA : s ⊆ (range N).product (range N)) :
  (trivial_triangles N s).card = s.card :=
begin
  refine card_congr (λ xyz _, (xyz.1, xyz.2.1)) _ _ _,
  { rintro ⟨x, y, z⟩ t,
    apply (mem_filter.1 t).2.1 },
  { rintro ⟨x₁, y₁, z₁⟩ ⟨x₂, y₂, z₂⟩ t₁ t₂ hxy,
    simp only [prod.mk.inj_iff] at hxy,
    simp only [prod.mk.inj_iff, subtype.ext_iff, ←and.assoc, hxy, true_and, eq_self_iff_true],
    rw ←(mem_filter.1 t₁).2.2,
    rw ←(mem_filter.1 t₂).2.2,
    simp [hxy.1, hxy.2] },
  rintro ⟨x, y⟩ hxy,
  have := hA hxy,
  simp only [mem_range, mem_product] at this,
  refine ⟨⟨⟨_, this.1⟩, ⟨_, this.2⟩, ⟨x + y, _⟩⟩, mem_filter.2 ⟨mem_univ _, hxy, rfl⟩, rfl⟩,
  rw two_mul,
  apply add_lt_add this.1 this.2,
end

lemma disjoint_triangles {ε : ℝ} (hA : s ⊆ (range N).product (range N)) (hA' : ε * N^2 ≤ s.card) :
  (corners_graph N s).triangle_free_far (ε/16) :=
begin
  refine simple_graph.triangle_free_far_of_disjoint_triangles
    ((trivial_triangles N s).map ⟨_, triangle_map_injective⟩) _ _ _,
  { simp only [subset_iff, and_imp, exists_prop, forall_exists_index, function.embedding.coe_fn_mk,
      mem_map],
    rintro _ x hx rfl,
    apply triangle_map_mem _ (trivial_triangles_mem _ hx), },
  { simp only [set.pairwise, mem_map, mem_coe, forall_exists_index, prod.forall, prod.forall',
      function.embedding.coe_fn_mk, trivial_triangles, true_and, and_imp, mem_filter, mem_univ],
    rintro _ ⟨h₁, _⟩ ⟨⟨v₁, _⟩, ⟨_, k₁⟩⟩ t₁ i₁ rfl _ ⟨h₂, _⟩ ⟨⟨v₂, _⟩, ⟨_, k₂⟩⟩ t₂ i₂ rfl q,
    dsimp at i₁ i₂ t₁ t₂,
    substs i₁ i₂,
    rw triangle_map_injective.ne_iff at q,
    dsimp [triangle_map],
    simp only [prod.mk.inj_iff, subtype.mk_eq_mk, ne.def] at q,
    rw finset.card_le_one,
    simp only [and_imp, mem_insert, mem_inter, mem_singleton, true_and, forall_eq_or_imp, and_true,
      false_or, forall_eq, implies_true_iff, eq_self_iff_true, subtype.mk_eq_mk, or_false,
      forall_and_distrib, and_assoc, @imp.swap (_ + _ = _)],
    refine ⟨_, _, _, _, _, _⟩;
    { intros i₁ i₂,
      apply q,
      simpa [i₁] using i₂ } },
  rw [card_map, card_trivial_triangles hA, card_corners_vertices],
  norm_num,
  linarith,
end

lemma weak_corners_theorem {ε : ℝ} (hε : 0 < ε) :
  ∃ n₀ : ℕ, ∀ n, n₀ ≤ n →
    ∀ s ⊆ (range n).product (range n), ε * n^2 ≤ s.card →
      ∃ x y h, 0 < h ∧ (is_corner s x y h ∨ is_anticorner s x y h) :=
begin
  refine ⟨⌊1/(simple_graph.triangle_removal_bound (ε / 16) * 64)⌋₊ + 1, λ n hn s hA hA', _⟩,
  rw nat.add_one_le_iff at hn,
  have n_pos : 0 < n := (nat.zero_le _).trans_lt hn,
  have : ε ≤ 1,
  { have := hA'.trans (nat.cast_le.2 (card_le_of_subset hA)),
    simp only [sq, card_range, nat.cast_mul, card_product] at this,
    rwa mul_le_iff_le_one_left at this,
    exact mul_pos (nat.cast_pos.2 n_pos) (nat.cast_pos.2 n_pos) },
  have tf : (corners_graph n s).triangle_free_far (ε/16) := disjoint_triangles hA hA',
  by_contra h,
  simp only [not_and', or_imp_distrib, forall_and_distrib, not_exists, not_lt, le_zero_iff] at h,
  have h₁ := simple_graph.triangle_removal_2 (show 0 < ε/16, by linarith) (by linarith) tf,
  rw card_corners_vertices at h₁,
  have i := h₁.trans (nat.cast_le.2 (trivial_triangles_corners_graph h.1 h.2)),
  rw [nat.cast_mul, mul_pow, nat.cast_pow, ←mul_assoc] at i,
  norm_num at i,
  have : simple_graph.triangle_removal_bound (ε / 16) * 64 * n ≤ 1,
  { apply le_of_mul_le_mul_right _ (sq_pos_of_ne_zero (n : ℝ) (nat.cast_ne_zero.2 n_pos.ne')),
    rwa [one_mul, mul_assoc, ←pow_succ] },
  have po : 0 < simple_graph.triangle_removal_bound (ε / 16) * 64 :=
    mul_pos (simple_graph.triangle_removal_bound_pos (by linarith) (by linarith)) (by norm_num),
  apply not_lt_of_le this,
  rwa [nat.floor_lt (one_div_nonneg.2 po.le), div_lt_iff' po] at hn,
end

lemma alt_set (c : ℕ × ℕ) (s : finset (ℕ × ℕ)) :
  (s.filter (λ (x : ℕ × ℕ), x.1 ≤ c.1 ∧ x.2 ≤ c.2 ∧ (c.1 - x.1, c.2 - x.2) ∈ s)).card =
    ((s.product s).filter (λ (x : (ℕ × ℕ) × ℕ × ℕ), (x.1.1 + x.2.1, x.1.2 + x.2.2) = c)).card :=
begin
  rcases c with ⟨c₁, c₂⟩,
  refine (card_congr (λ (a : _ × _) ha, a.1) _ _ _).symm,
  { rintro ⟨⟨a₁, a₂⟩, b₁, b₂⟩ i,
    dsimp,
    simp only [mem_filter, mem_product, prod.mk.inj_iff] at i,
    simp only [mem_filter],
    rw [←i.2.1, ←i.2.2],
    simpa using i.1 },
  { dsimp,
    simp only [prod.forall, mem_filter, mem_product, prod.mk.inj_iff],
    rintro a₁ a₂ ⟨a₃, a₄⟩ ⟨⟨a₅, a₆⟩, a₇, a₈⟩ ⟨-, rfl, rfl⟩ ⟨_, h₁⟩ ⟨⟩,
    simpa [eq_comm] using h₁ },
  simp only [and_imp, exists_prop, prod.forall, mem_filter, exists_and_distrib_right,
    prod.mk.inj_iff, exists_eq_right_right, exists_eq_right, prod.exists, mem_product],
  intros x y xy hx hy t,
  refine ⟨_, _, ⟨xy, t⟩, _, _⟩,
  { rw [←nat.add_sub_assoc hx, nat.add_sub_cancel_left] },
  { rw [←nat.add_sub_assoc hy, nat.add_sub_cancel_left] },
end

lemma correlate {n : ℕ} (hn : 0 < n) (s : finset (ℕ × ℕ)) (hA : s ⊆ (range n).product (range n)) :
  ∃ c ∈ (range (2 * n)).product (range (2 * n)),
    (s.card : ℝ)^2 / ((2 * n)^2) ≤
      (s.filter (λ (xy : ℕ × ℕ), xy.1 ≤ c.1 ∧ xy.2 ≤ c.2 ∧ (c.1 - xy.1, c.2 - xy.2) ∈ s)).card :=
begin
  simp_rw [alt_set _ s],
  let f : (ℕ × ℕ) × ℕ × ℕ → ℕ × ℕ := λ ab, (ab.1.1 + ab.2.1, ab.1.2 + ab.2.2),
  have : ∀ a ∈ s.product s, f a ∈ (range (2 * n)).product (range (2 * n)),
  { rintro ⟨⟨a₁, a₂⟩, a₃, a₄⟩ h,
    simp only [mem_product] at h,
    have i := and.intro (hA h.1) (hA h.2),
    simp only [mem_range, mem_product] at i,
    simp only [mem_product, mem_range, two_mul],
    exact ⟨add_lt_add i.1.1 i.2.1, add_lt_add i.1.2 i.2.2⟩ },
  refine exists_le_card_fiber_of_mul_le_card_of_maps_to' this _ _,
  { simp [hn.ne'] },
  { simp only [card_product, card_range, nsmul_eq_mul, nat.cast_pow, nat.cast_two,
      nat.cast_mul, ←sq],
    rw [mul_div_assoc', mul_div_cancel_left],
    simp [hn.ne'] }
end

lemma corners_theorem {ε : ℝ} (hε : 0 < ε) :
  ∃ n₀ : ℕ, ∀ n, n₀ ≤ n →
    ∀ s ⊆ (range n).product (range n), ε * n^2 ≤ s.card →
      ∃ x y h, 0 < h ∧ is_corner s x y h :=
begin
  let ε' : ℝ := (ε/2)^2,
  have hε' : 0 < ε' := pow_pos (by linarith) _,
  obtain ⟨n₀, hn₀⟩ := weak_corners_theorem hε',
  refine ⟨n₀ + 1, λ n hn s hA hAcard, _⟩,
  obtain ⟨⟨c₁, c₂⟩, -, hA'card⟩ := correlate (nat.succ_pos'.trans_le hn) s hA,
  let A' : finset (ℕ × ℕ) := s.filter (λ xy, xy.1 ≤ c₁ ∧ xy.2 ≤ c₂ ∧ (c₁ - xy.1, c₂ - xy.2) ∈ s),
  have hA' : A' ⊆ s := filter_subset _ _,
  have : (s.card : ℝ)^2 / ((2 * n)^2) ≤ A'.card := hA'card,
  by_contra h,
  simp only [not_and', or_imp_distrib, forall_and_distrib, not_exists, not_lt, le_zero_iff] at h,
  have nc : ¬(∃ (x y h : ℕ), 0 < h ∧ (is_corner A' x y h ∨ is_anticorner A' x y h)),
  { simp only [not_exists, not_and', not_lt, or_imp_distrib, le_zero_iff, forall_and_distrib],
    refine ⟨λ x y k i, h x y k ⟨hA' i.1, hA' i.2.1, hA' i.2.2⟩, _⟩,
    { rintro x y k ⟨xy, ⟨kx, xky⟩, ky, xyk⟩,
      simp only [mem_filter] at xy xky xyk,
      rw tsub_tsub_assoc xy.2.2.1 ky at xyk,
      rw tsub_tsub_assoc xy.2.1 kx at xky,
      apply h (c₁ - x) (c₂ - y) k ⟨xy.2.2.2, xky.2.2.2, xyk.2.2.2⟩ } },
  refine nc (hn₀ _ ((nat.le_succ _).trans hn) A' (hA'.trans hA) (le_trans _ this)),
  rw [←mul_pow, ←div_pow],
  refine pow_le_pow_of_le_left (mul_nonneg (by linarith) (nat.cast_nonneg _)) _ _,
  rw le_div_iff,
  { exact (le_of_eq (by ring)).trans hAcard },
  exact mul_pos zero_lt_two (nat.cast_pos.2 (nat.succ_pos'.trans_le hn)),
end

lemma roth (δ : ℝ) (hδ : 0 < δ) :
  ∃ n₀ : ℕ, ∀ n, n₀ ≤ n →
    ∀ s ⊆ range n, δ * n ≤ s.card → ∃ a d, 0 < d ∧ a ∈ s ∧ a + d ∈ s ∧ a + 2 * d ∈ s :=
begin
  let δ' : ℝ := δ/4,
  have hδ' : 0 < δ' := div_pos hδ (by norm_num),
  obtain ⟨n₀, hn₀⟩ := corners_theorem hδ',
  refine ⟨n₀, λ n hn s hA hAcard, _⟩,
  let B : finset (ℕ × ℕ) :=
    ((range (2 * n)).product (range (2 * n))).filter (λ xy, xy.1 ≤ xy.2 ∧ xy.2 - xy.1 ∈ s),
  have : n * card s ≤ card B,
  { rw [←card_range n, ←card_product],
    refine card_le_card_of_inj_on (λ ia, (ia.1, ia.1 + ia.2)) _ _,
    { rintro ⟨i, a⟩ t,
      simp only [mem_range, mem_product] at t,
      simp only [true_and, mem_filter, add_tsub_cancel_left, mem_range, le_add_iff_nonneg_right,
        zero_le', mem_product, t.2, and_true, two_mul],
      exact ⟨t.1.trans_le (nat.le_add_right _ _), add_lt_add t.1 (mem_range.1 (hA t.2))⟩ },
    simp only [and_imp, prod.forall, mem_range, prod.mk.inj_iff, mem_product],
    rintro i a₁ hi ha₁ _ a₂ - ha₂ rfl,
    simp },
  have : δ' * ↑(2 * n) ^ 2 ≤ ↑(B.card),
  { refine le_trans _ (nat.cast_le.2 this),
    rw [nat.cast_mul, nat.cast_two, mul_pow, ←mul_assoc, nat.cast_mul],
    norm_num,
    rw [sq, ←mul_assoc, mul_comm _ (s.card : ℝ)],
    apply mul_le_mul_of_nonneg_right hAcard (nat.cast_nonneg _) },
  obtain ⟨x, y, k, hk, xy, xky, xyk⟩ :=
    hn₀ _ (hn.trans (nat.le_mul_of_pos_left zero_lt_two)) B (filter_subset _ _) this,
  refine ⟨y - (x + k), k, hk, (mem_filter.1 xky).2.2, _, _⟩,
  { rw [←nat.sub_add_comm (mem_filter.1 xky).2.1, nat.add_sub_add_right],
    apply (mem_filter.1 xy).2.2 },
  { rw [←nat.sub_add_comm (mem_filter.1 xky).2.1, two_mul, ←add_assoc, nat.add_sub_add_right],
    apply (mem_filter.1 xyk).2.2 },
end

lemma roth' (δ : ℝ) (hδ : 0 < δ) :
  ∃ n₀ : ℕ, ∀ n, n₀ ≤ n → ∀ s ⊆ range n, δ * n ≤ s.card → ¬ add_salem_spencer (s : set ℕ) :=
begin
  obtain ⟨n₀, hn₀⟩ := roth δ hδ,
  refine ⟨n₀, λ n hn s hA hAcard hA', _⟩,
  obtain ⟨a, d, hd, x, y, z⟩ := hn₀ n hn s hA hAcard,
  exact mul_ne_zero two_ne_zero hd.ne' (self_eq_add_right.1 $ hA' x z y $ by ring),
end

open asymptotics filter

lemma roth_asymptotic : is_o (λ N, (roth_number_nat N : ℝ)) (λ N, (N : ℝ)) at_top :=
begin
  rw is_o_iff,
  intros δ hδ,
  rw eventually_at_top,
  obtain ⟨n₀, hn₀⟩ := roth' δ hδ,
  refine ⟨n₀, λ n hn, _⟩,
  simp only [real.norm_coe_nat, ←not_lt],
  obtain ⟨s, hA₁, hA₂, hA₃⟩ := add_roth_number_spec (range n),
  refine λ h, (hn₀ n hn _ hA₁ _) hA₃,
  rw hA₂,
  exact h.le,
end


/-
Copyright (c) 2020 Johan Commelin, Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Damiano Testa, Yaël Dillies
-/
import logic.equiv.defs
import logic.nontrivial
import order.basic

/-!
# Type synonyms

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

This file provides two type synonyms for order theory:
* `order_dual α`: Type synonym of `α` to equip it with the dual order (`a ≤ b` becomes `b ≤ a`).
* `lex α`: Type synonym of `α` to equip it with its lexicographic order. The precise meaning depends
  on the type we take the lex of. Examples include `prod`, `sigma`, `list`, `finset`.

## Notation

`αᵒᵈ` is notation for `order_dual α`.

The general rule for notation of `lex` types is to append `ₗ` to the usual notation.

## Implementation notes

One should not abuse definitional equality between `α` and `αᵒᵈ`/`lex α`. Instead, explicit
coercions should be inserted:
* `order_dual`: `order_dual.to_dual : α → αᵒᵈ` and `order_dual.of_dual : αᵒᵈ → α`
* `lex`: `to_lex : α → lex α` and `of_lex : lex α → α`.

In fact, those are bundled as `equiv`s to put goals in the right syntactic form for rewriting with
the `equiv` API (`⇑to_lex a` where `⇑` is `coe_fn : (α ≃ lex α) → α → lex α`, instead of a bare
`to_lex a`).

## See also

This file is similar to `algebra.group.type_tags`.
-/

variables {α β γ : Type*}

/-! ### Order dual -/

namespace order_dual

instance [h : nontrivial α] : nontrivial (αᵒᵈ) := h

/-- `to_dual` is the identity function to the `order_dual` of a linear order.  -/
def to_dual : α ≃ αᵒᵈ := equiv.refl _

/-- `of_dual` is the identity function from the `order_dual` of a linear order.  -/
def of_dual : αᵒᵈ ≃ α := equiv.refl _

@[simp] lemma to_dual_symm_eq : (@to_dual α).symm = of_dual := rfl
@[simp] lemma of_dual_symm_eq : (@of_dual α).symm = to_dual := rfl
@[simp] lemma to_dual_of_dual (a : αᵒᵈ) : to_dual (of_dual a) = a := rfl
@[simp] lemma of_dual_to_dual (a : α) : of_dual (to_dual a) = a := rfl
@[simp] lemma to_dual_inj {a b : α} : to_dual a = to_dual b ↔ a = b := iff.rfl
@[simp] lemma of_dual_inj {a b : αᵒᵈ} : of_dual a = of_dual b ↔ a = b := iff.rfl

@[simp] lemma to_dual_le_to_dual [has_le α] {a b : α} : to_dual a ≤ to_dual b ↔ b ≤ a := iff.rfl
@[simp] lemma to_dual_lt_to_dual [has_lt α] {a b : α} : to_dual a < to_dual b ↔ b < a := iff.rfl
@[simp] lemma of_dual_le_of_dual [has_le α] {a b : αᵒᵈ} : of_dual a ≤ of_dual b ↔ b ≤ a := iff.rfl
@[simp] lemma of_dual_lt_of_dual [has_lt α] {a b : αᵒᵈ} : of_dual a < of_dual b ↔ b < a := iff.rfl
lemma le_to_dual [has_le α] {a : αᵒᵈ} {b : α} : a ≤ to_dual b ↔ b ≤ of_dual a := iff.rfl
lemma lt_to_dual [has_lt α] {a : αᵒᵈ} {b : α} : a < to_dual b ↔ b < of_dual a := iff.rfl
lemma to_dual_le [has_le α] {a : α} {b : αᵒᵈ} : to_dual a ≤ b ↔ of_dual b ≤ a := iff.rfl
lemma to_dual_lt [has_lt α] {a : α} {b : αᵒᵈ} : to_dual a < b ↔ of_dual b < a := iff.rfl

/-- Recursor for `αᵒᵈ`. -/
@[elab_as_eliminator]
protected def rec {C : αᵒᵈ → Sort*} (h₂ : Π a : α, C (to_dual a)) : Π a : αᵒᵈ, C a := h₂

@[simp] protected lemma «forall» {p : αᵒᵈ → Prop} : (∀ a, p a) ↔ ∀ a, p (to_dual a) := iff.rfl
@[simp] protected lemma «exists» {p : αᵒᵈ → Prop} : (∃ a, p a) ↔ ∃ a, p (to_dual a) := iff.rfl

alias to_dual_le_to_dual ↔ _ _root_.has_le.le.dual
alias to_dual_lt_to_dual ↔ _ _root_.has_lt.lt.dual
alias of_dual_le_of_dual ↔ _ _root_.has_le.le.of_dual
alias of_dual_lt_of_dual ↔ _ _root_.has_lt.lt.of_dual

end order_dual

/-! ### Lexicographic order -/

/-- A type synonym to equip a type with its lexicographic order. -/
def lex (α : Type*) := α

/-- `to_lex` is the identity function to the `lex` of a type.  -/
@[pattern] def to_lex : α ≃ lex α := equiv.refl _

/-- `of_lex` is the identity function from the `lex` of a type.  -/
@[pattern] def of_lex : lex α ≃ α := equiv.refl _

@[simp] lemma to_lex_symm_eq : (@to_lex α).symm = of_lex := rfl
@[simp] lemma of_lex_symm_eq : (@of_lex α).symm = to_lex := rfl
@[simp] lemma to_lex_of_lex (a : lex α) : to_lex (of_lex a) = a := rfl
@[simp] lemma of_lex_to_lex (a : α) : of_lex (to_lex a) = a := rfl
@[simp] lemma to_lex_inj {a b : α} : to_lex a = to_lex b ↔ a = b := iff.rfl
@[simp] lemma of_lex_inj {a b : lex α} :  of_lex a = of_lex b ↔ a = b := iff.rfl

/-- A recursor for `lex`. Use as `induction x using lex.rec`. -/
protected def lex.rec {β : lex α → Sort*} (h : Π a, β (to_lex a)) : Π a, β a := λ a, h (of_lex a)


import Meta.Util

open Nat List Classical

theorem notImplies1 : ∀ {P Q : Prop}, ¬ (P → Q) → P := by
  intros P Q h
  cases Classical.em P with
  | inl p  => exact p
  | inr np => apply False.elim
              apply h
              intro p
              exact False.elim (np p)

theorem notImplies2 : ∀ {P Q : Prop}, ¬ (P → Q) → ¬ Q := by
  intros P Q h
  cases Classical.em Q with
  | inl q  => exact False.elim (h (λ _ => q))
  | inr nq => exact nq

theorem contradiction : ∀ {P : Prop}, P → ¬ P → False := λ p np => np p

theorem orComm : ∀ {P Q : Prop}, P ∨ Q → Q ∨ P := by
  intros P Q h
  cases h with
  | inl hp => exact Or.inr hp
  | inr hq => exact Or.inl hq

theorem orAssocDir : ∀ {P Q R: Prop}, P ∨ Q ∨ R → (P ∨ Q) ∨ R := by
  intros P Q R h
  cases h with
  | inl h₁ => exact Or.inl (Or.inl h₁)
  | inr h₂ => cases h₂ with
              | inl h₃ => exact Or.inl (Or.inr h₃)
              | inr h₄ => exact Or.inr h₄

theorem orAssocConv : ∀ {P Q R : Prop}, (P ∨ Q) ∨ R → P ∨ Q ∨ R := by
  intros P Q R h
  cases h with
  | inl h₁ => cases h₁ with
              | inl h₃ => exact Or.inl h₃
              | inr h₄ => exact (Or.inr (Or.inl h₄))
  | inr h₂ => exact Or.inr (Or.inr h₂)

theorem congOrRight : ∀ {P Q R : Prop}, (P → Q) → P ∨ R → Q ∨ R := by
  intros P Q R h₁ h₂
  cases h₂ with
  | inl h₃ => exact Or.inl (h₁ h₃)
  | inr h₄ => exact Or.inr h₄

theorem congOrLeft : ∀ {R P Q : Prop}, (P → Q) → R ∨ P → R ∨ Q := by
  intros P Q R h₁ h₂
  apply orComm
  exact congOrRight h₁ (orComm h₂)

theorem orImplies : ∀ {p q : Prop}, (¬ p → q) → p ∨ q :=
  by intros p q h
     exact match em p with
     | Or.inl pp => Or.inl pp
     | Or.inr npp => match em q with
                     | Or.inl pq => Or.inr pq
                     | Or.inr npq => False.elim (npq (h npp))

theorem orImplies₂ : ∀ {p q : Prop}, (¬ p) ∨ q → p → q := by
  intros P Q h p
  cases h with
  | inl np => exact False.elim (np p)
  | inr q  => exact q

theorem orImplies₃ : ∀ {p q : Prop}, p ∨ q → ¬ p → q := by
  intros P Q h np
  cases h with
  | inl p => exact False.elim (np p)
  | inr q => exact q

theorem scope : ∀ {p q : Prop}, (p → q) → ¬ p ∨ q :=
  by intros p q h
     exact match em p with
     | Or.inl pp =>  Or.inr (h pp)
     | Or.inr npp => Or.inl npp

def impliesElim : ∀ {p q : Prop}, (p → q) → ¬ p ∨ q := scope

theorem deMorganSmall : ∀ {p q : Prop}, ¬ (p ∨ q) → ¬ p ∧ ¬ q :=
  by intros p q h
     exact match em p, em q with
     | Or.inl pp,  _          => False.elim (h (Or.inl pp))
     | Or.inr _,   Or.inl pq  => False.elim (h (Or.inr pq))
     | Or.inr npp, Or.inr npq => And.intro npp npq

theorem deMorganSmall₂ : ∀ {p q : Prop}, ¬ p ∧ ¬ q → ¬ (p ∨ q) :=
  by intros p q h
     have ⟨np, nq⟩ := h
     exact match em p, em q with
     | Or.inl pp,  _   => False.elim (np pp)
     | _        ,  Or.inl pq  => False.elim (nq pq)
     | Or.inr npp, Or.inr npq => λ h₂ =>
                                    match h₂ with
                                    | Or.inl pp => False.elim (npp pp)
                                    | Or.inr pq => False.elim (npq pq)

theorem doubleNeg : ∀ {p : Prop}, ¬ ¬ p → p :=
  by intros p h
     exact match em p with
     | Or.inl pp => pp
     | Or.inr np => False.elim (h (λ p => np p))

theorem doubleNeg₂ : ∀ {p : Prop}, p → ¬ ¬ p := λ p np => np p

theorem deMorgan : ∀ {l : List Prop}, ¬ orN (notList l) → andN l :=
  by intros l h
     exact match l with
     | []   => True.intro
     | [t]  => by simp [andN]
                  simp [notList, orN, map] at h
                  cases em t with
                  | inl tt  => exact tt
                  | inr ntt => exact False.elim (h ntt)
     | h₁::h₂::t => by simp [orN, notList, map] at h
                       have ⟨ t₁, t₂ ⟩ := deMorganSmall h
                       simp [orN] at t₂
                       have ih := @deMorgan (h₂::t) t₂
                       simp [andN]
                       have t₁' := doubleNeg t₁
                       exact ⟨ t₁', ih ⟩

theorem deMorgan₂ : ∀ {l : List Prop}, andN l → ¬ orN (notList l) :=
  by intros l h
     exact match l with
     | [] => by simp [orN, notList]
     | [t] => by simp [orN, notList]; simp [andN] at h; exact doubleNeg₂ h
     | h₁::h₂::t => by simp [orN, notList, map]
                       simp [andN] at h
                       apply deMorganSmall₂
                       have nnh₁ := doubleNeg₂ (And.left h)
                       have ih := @deMorgan₂ (h₂::t) (And.right h)
                       exact ⟨nnh₁, ih⟩

theorem cnfAndNeg : ∀ (l : List Prop), andN l ∨ orN (notList l) :=
  by intro l
     apply orComm
     apply orImplies
     intro h
     exact deMorgan h

theorem cnfAndPos : ∀ (l : List Prop) (i : Nat), ¬ (andN l) ∨ List.getD l i True :=
  by intros l i
     apply orImplies
     intro h
     have h' := doubleNeg h
     match l with
     | [] => exact True.intro
     | [p] =>
       match i with
       | 0 => exact h'
       | _ + 1 => exact True.intro
     | p₁::p₂::ps =>
       match i with
       | zero => exact And.left h'
       | succ i' =>
         simp [List.getD]
         have IH :=  cnfAndPos (p₂::ps) i'
         exact orImplies₂ IH (And.right h')

theorem cong : ∀ {A B : Type u} {f₁ f₂ : A → B} {t₁ t₂ : A},
  f₁ = f₂ → t₁ = t₂ → f₁ t₁ = f₂ t₂ :=
  by intros A B f₁ f₂ t₁ t₂ h₁ h₂
     rewrite [h₁, h₂]
     exact rfl

theorem eqResolve {P Q : Prop} : P → (P ↔ Q) → Q := by
  intros h₁ h₂
  rewrite [← h₂]
  exact h₁

theorem dupOr {P Q : Prop} : P ∨ P ∨ Q → P ∨ Q := λ h =>
  match h with
  | Or.inl p          => Or.inl p
  | Or.inr (Or.inl p) => Or.inl p
  | Or.inr (Or.inr q) => Or.inr q

theorem dupOr₂ {P : Prop} : P ∨ P → P := λ h =>
  match h with
  | Or.inl p => p
  | Or.inr p => p


/-
Copyright (c) 2019 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.linear_algebra.dimension
import Mathlib.ring_theory.principal_ideal_domain
import Mathlib.algebra.algebra.subalgebra
import Mathlib.PostPort

universes u_1 u_2 u v w v'

namespace Mathlib

/-!
# Finite dimensional vector spaces

Definition and basic properties of finite dimensional vector spaces, of their dimensions, and
of linear maps on such spaces.

## Main definitions

Assume `V` is a vector space over a field `K`. There are (at least) three equivalent definitions of
finite-dimensionality of `V`:

- it admits a finite basis.
- it is finitely generated.
- it is noetherian, i.e., every subspace is finitely generated.

We introduce a typeclass `finite_dimensional K V` capturing this property. For ease of transfer of
proof, it is defined using the third point of view, i.e., as `is_noetherian`. However, we prove
that all these points of view are equivalent, with the following lemmas
(in the namespace `finite_dimensional`):

- `exists_is_basis_finite` states that a finite-dimensional vector space has a finite basis
- `of_fintype_basis` states that the existence of a basis indexed by a finite type implies
  finite-dimensionality
- `of_finset_basis` states that the existence of a basis indexed by a `finset` implies
  finite-dimensionality
- `of_finite_basis` states that the existence of a basis indexed by a finite set implies
  finite-dimensionality
- `iff_fg` states that the space is finite-dimensional if and only if it is finitely generated

Also defined is `findim`, the dimension of a finite dimensional space, returning a `nat`,
as opposed to `dim`, which returns a `cardinal`. When the space has infinite dimension, its
`findim` is by convention set to `0`.

Preservation of finite-dimensionality and formulas for the dimension are given for
- submodules
- quotients (for the dimension of a quotient, see `findim_quotient_add_findim`)
- linear equivs, in `linear_equiv.finite_dimensional` and `linear_equiv.findim_eq`
- image under a linear map (the rank-nullity formula is in `findim_range_add_findim_ker`)

Basic properties of linear maps of a finite-dimensional vector space are given. Notably, the
equivalence of injectivity and surjectivity is proved in `linear_map.injective_iff_surjective`,
and the equivalence between left-inverse and right-inverse in `mul_eq_one_comm` and
`comp_eq_id_comm`.

## Implementation notes

Most results are deduced from the corresponding results for the general dimension (as a cardinal),
in `dimension.lean`. Not all results have been ported yet.

One of the characterizations of finite-dimensionality is in terms of finite generation. This
property is currently defined only for submodules, so we express it through the fact that the
maximal submodule (which, as a set, coincides with the whole space) is finitely generated. This is
not very convenient to use, although there are some helper functions. However, this becomes very
convenient when speaking of submodules which are finite-dimensional, as this notion coincides with
the fact that the submodule is finitely generated (as a submodule of the whole space). This
equivalence is proved in `submodule.fg_iff_finite_dimensional`.
-/

/-- `finite_dimensional` vector spaces are defined to be noetherian modules.
Use `finite_dimensional.iff_fg` or `finite_dimensional.of_fintype_basis` to prove finite dimension
from a conventional definition. -/
def finite_dimensional (K : Type u_1) (V : Type u_2) [field K] [add_comm_group V] [vector_space K V] :=
  is_noetherian K V

namespace finite_dimensional

/-- A vector space is finite-dimensional if and only if its dimension (as a cardinal) is strictly
less than the first infinite cardinal `omega`. -/
theorem finite_dimensional_iff_dim_lt_omega {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] : finite_dimensional K V ↔ vector_space.dim K V < cardinal.omega := sorry

/-- The dimension of a finite-dimensional vector space, as a cardinal, is strictly less than the
first infinite cardinal `omega`. -/
theorem dim_lt_omega (K : Type u_1) (V : Type v) [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] : vector_space.dim K V < cardinal.omega :=
  iff.mp finite_dimensional_iff_dim_lt_omega

/-- In a finite dimensional space, there exists a finite basis. A basis is in general given as a
function from an arbitrary type to the vector space. Here, we think of a basis as a set (instead of
a function), and use as parametrizing type this set (and as a function the coercion
  `coe : s → V`).
-/
theorem exists_is_basis_finite (K : Type u) (V : Type v) [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] : ∃ (s : set V), is_basis K coe ∧ set.finite s :=
  Exists.dcases_on (exists_is_basis K V)
    fun (s : set V) (hs : is_basis K fun (i : ↥s) => ↑i) =>
      Exists.intro s { left := hs, right := finite_of_linear_independent (and.left hs) }

/-- In a finite dimensional space, there exists a finite basis. Provides the basis as a finset.
This is in contrast to `exists_is_basis_finite`, which provides a set and a `set.finite`.
-/
theorem exists_is_basis_finset (K : Type u) (V : Type v) [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] : ∃ (b : finset V), is_basis K coe := sorry

/-- A finite dimensional vector space over a finite field is finite -/
def fintype_of_fintype (K : Type u) (V : Type v) [field K] [add_comm_group V] [vector_space K V] [fintype K] [finite_dimensional K V] : fintype V :=
  module.fintype_of_fintype sorry

/-- A vector space is finite-dimensional if and only if it is finitely generated. As the
finitely-generated property is a property of submodules, we formulate this in terms of the
maximal submodule, equal to the whole space as a set by definition.-/
theorem iff_fg {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] : finite_dimensional K V ↔ submodule.fg ⊤ := sorry

/-- If a vector space has a finite basis, then it is finite-dimensional. -/
theorem of_fintype_basis {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type w} [fintype ι] {b : ι → V} (h : is_basis K b) : finite_dimensional K V := sorry

/-- If a vector space has a basis indexed by elements of a finite set, then it is
finite-dimensional. -/
theorem of_finite_basis {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type u_1} {s : set ι} {b : ↥s → V} (h : is_basis K b) (hs : set.finite s) : finite_dimensional K V :=
  of_fintype_basis h

/-- If a vector space has a finite basis, then it is finite-dimensional, finset style. -/
theorem of_finset_basis {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type u_1} {s : finset ι} {b : ↥↑s → V} (h : is_basis K b) : finite_dimensional K V :=
  of_finite_basis h (finset.finite_to_set s)

/-- A subspace of a finite-dimensional space is also finite-dimensional. -/
protected instance finite_dimensional_submodule {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (S : submodule K V) : finite_dimensional K ↥S :=
  iff.mpr finite_dimensional_iff_dim_lt_omega (lt_of_le_of_lt (dim_submodule_le S) (dim_lt_omega K V))

/-- A quotient of a finite-dimensional space is also finite-dimensional. -/
protected instance finite_dimensional_quotient {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (S : submodule K V) : finite_dimensional K (submodule.quotient S) :=
  iff.mpr finite_dimensional_iff_dim_lt_omega (lt_of_le_of_lt (dim_quotient_le S) (dim_lt_omega K V))

/-- The dimension of a finite-dimensional vector space as a natural number. Defined by convention to
be `0` if the space is infinite-dimensional. -/
def findim (K : Type u_1) (V : Type v) [field K] [add_comm_group V] [vector_space K V] : ℕ :=
  dite (vector_space.dim K V < cardinal.omega) (fun (h : vector_space.dim K V < cardinal.omega) => classical.some sorry)
    fun (h : ¬vector_space.dim K V < cardinal.omega) => 0

/-- In a finite-dimensional space, its dimension (seen as a cardinal) coincides with its `findim`. -/
theorem findim_eq_dim (K : Type u) (V : Type v) [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] : ↑(findim K V) = vector_space.dim K V :=
  eq.mpr (id (Eq._oldrec (Eq.refl (↑(findim K V) = vector_space.dim K V)) (dif_pos (dim_lt_omega K V))))
    (Eq.symm (classical.some_spec (iff.mp cardinal.lt_omega (dim_lt_omega K V))))

theorem findim_of_infinite_dimensional {K : Type u_1} {V : Type u_2} [field K] [add_comm_group V] [vector_space K V] (h : ¬finite_dimensional K V) : findim K V = 0 :=
  dif_neg (mt (iff.mpr finite_dimensional_iff_dim_lt_omega) h)

/-- If a vector space has a finite basis, then its dimension (seen as a cardinal) is equal to the
cardinality of the basis. -/
theorem dim_eq_card_basis {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type w} [fintype ι] {b : ι → V} (h : is_basis K b) : vector_space.dim K V = ↑(fintype.card ι) := sorry

/-- If a vector space has a finite basis, then its dimension is equal to the cardinality of the
basis. -/
theorem findim_eq_card_basis {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type w} [fintype ι] {b : ι → V} (h : is_basis K b) : findim K V = fintype.card ι :=
  eq.mp (propext cardinal.nat_cast_inj)
    (eq.mp (Eq._oldrec (Eq.refl (vector_space.dim K V = ↑(fintype.card ι))) (Eq.symm (findim_eq_dim K V)))
      (dim_eq_card_basis h))

/-- If a vector space is finite-dimensional, then the cardinality of any basis is equal to its
`findim`. -/
theorem findim_eq_card_basis' {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {ι : Type w} {b : ι → V} (h : is_basis K b) : ↑(findim K V) = cardinal.mk ι := sorry

/-- If a vector space has a finite basis, then its dimension is equal to the cardinality of the
basis. This lemma uses a `finset` instead of indexed types. -/
theorem findim_eq_card_finset_basis {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {b : finset V} (h : is_basis K subtype.val) : findim K V = finset.card b := sorry

theorem equiv_fin {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type u_1} [finite_dimensional K V] {v : ι → V} (hv : is_basis K v) : ∃ (g : fin (findim K V) ≃ ι), is_basis K (v ∘ ⇑g) := sorry

theorem fin_basis (K : Type u) (V : Type v) [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] : ∃ (v : fin (findim K V) → V), is_basis K v := sorry

theorem cardinal_mk_le_findim_of_linear_independent {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {ι : Type w} {b : ι → V} (h : linear_independent K b) : cardinal.mk ι ≤ ↑(findim K V) := sorry

theorem fintype_card_le_findim_of_linear_independent {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {ι : Type u_1} [fintype ι] {b : ι → V} (h : linear_independent K b) : fintype.card ι ≤ findim K V := sorry

theorem finset_card_le_findim_of_linear_independent {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {b : finset V} (h : linear_independent K fun (x : ↥↑b) => ↑x) : finset.card b ≤ findim K V :=
  eq.mpr (id (Eq._oldrec (Eq.refl (finset.card b ≤ findim K V)) (Eq.symm (fintype.card_coe b))))
    (fintype_card_le_findim_of_linear_independent h)

theorem lt_omega_of_linear_independent {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type w} [finite_dimensional K V] {v : ι → V} (h : linear_independent K v) : cardinal.mk ι < cardinal.omega := sorry

theorem not_linear_independent_of_infinite {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type w} [inf : infinite ι] [finite_dimensional K V] (v : ι → V) : ¬linear_independent K v :=
  id
    fun (h_lin_indep : linear_independent K v) =>
      absurd (iff.mp cardinal.infinite_iff inf) (iff.mpr not_le (lt_omega_of_linear_independent h_lin_indep))

/-- A finite dimensional space has positive `findim` iff it has a nonzero element. -/
theorem findim_pos_iff_exists_ne_zero {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] : 0 < findim K V ↔ ∃ (x : V), x ≠ 0 := sorry

/-- A finite dimensional space has positive `findim` iff it is nontrivial. -/
theorem findim_pos_iff {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] : 0 < findim K V ↔ nontrivial V := sorry

/-- A nontrivial finite dimensional space has positive `findim`. -/
theorem findim_pos {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] [h : nontrivial V] : 0 < findim K V :=
  iff.mpr findim_pos_iff h

/--
If a finset has cardinality larger than the dimension of the space,
then there is a nontrivial linear relation amongst its elements.
-/
theorem exists_nontrivial_relation_of_dim_lt_card {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {t : finset V} (h : findim K V < finset.card t) : ∃ (f : V → K), (finset.sum t fun (e : V) => f e • e) = 0 ∧ ∃ (x : V), ∃ (H : x ∈ t), f x ≠ 0 := sorry

/--
If a finset has cardinality larger than `findim + 1`,
then there is a nontrivial linear relation amongst its elements,
such that the coefficients of the relation sum to zero.
-/
theorem exists_nontrivial_relation_sum_zero_of_dim_succ_lt_card {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {t : finset V} (h : findim K V + 1 < finset.card t) : ∃ (f : V → K),
  (finset.sum t fun (e : V) => f e • e) = 0 ∧ (finset.sum t fun (e : V) => f e) = 0 ∧ ∃ (x : V), ∃ (H : x ∈ t), f x ≠ 0 := sorry

/--
A slight strengthening of `exists_nontrivial_relation_sum_zero_of_dim_succ_lt_card`
available when working over an ordered field:
we can ensure a positive coefficient, not just a nonzero coefficient.
-/
theorem exists_relation_sum_zero_pos_coefficient_of_dim_succ_lt_card {L : Type u_1} [linear_ordered_field L] {W : Type v} [add_comm_group W] [vector_space L W] [finite_dimensional L W] {t : finset W} (h : findim L W + 1 < finset.card t) : ∃ (f : W → L),
  (finset.sum t fun (e : W) => f e • e) = 0 ∧ (finset.sum t fun (e : W) => f e) = 0 ∧ ∃ (x : W), ∃ (H : x ∈ t), 0 < f x := sorry

/-- If a submodule has maximal dimension in a finite dimensional space, then it is equal to the
whole space. -/
theorem eq_top_of_findim_eq {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {S : submodule K V} (h : findim K ↥S = findim K V) : S = ⊤ := sorry

/-- A field is one-dimensional as a vector space over itself. -/
@[simp] theorem findim_of_field (K : Type u) [field K] : findim K K = 1 := sorry

/-- The vector space of functions on a fintype has finite dimension. -/
protected instance finite_dimensional_fintype_fun (K : Type u) [field K] {ι : Type u_1} [fintype ι] : finite_dimensional K (ι → K) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (finite_dimensional K (ι → K))) (propext finite_dimensional_iff_dim_lt_omega)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (vector_space.dim K (ι → K) < cardinal.omega)) dim_fun'))
      (cardinal.nat_lt_omega (fintype.card ι)))

/-- The vector space of functions on a fintype ι has findim equal to the cardinality of ι. -/
@[simp] theorem findim_fintype_fun_eq_card (K : Type u) [field K] {ι : Type v} [fintype ι] : findim K (ι → K) = fintype.card ι :=
  eq.mp (Eq._oldrec (Eq.refl (↑(findim K (ι → K)) = ↑(fintype.card ι))) (propext cardinal.nat_cast_inj))
    (eq.mp (Eq._oldrec (Eq.refl (vector_space.dim K (ι → K) = ↑(fintype.card ι))) (Eq.symm (findim_eq_dim K (ι → K))))
      dim_fun')

/-- The vector space of functions on `fin n` has findim equal to `n`. -/
@[simp] theorem findim_fin_fun (K : Type u) [field K] {n : ℕ} : findim K (fin n → K) = n := sorry

/-- The submodule generated by a finite set is finite-dimensional. -/
theorem span_of_finite (K : Type u) {V : Type v} [field K] [add_comm_group V] [vector_space K V] {A : set V} (hA : set.finite A) : finite_dimensional K ↥(submodule.span K A) :=
  is_noetherian_span_of_finite K hA

/-- The submodule generated by a single element is finite-dimensional. -/
protected instance submodule.span.finite_dimensional (K : Type u) {V : Type v} [field K] [add_comm_group V] [vector_space K V] (x : V) : finite_dimensional K ↥(submodule.span K (singleton x)) :=
  span_of_finite K
    (eq.mpr (id (propext ((fun {α : Type v} (a : α) => iff_true_intro (set.finite_singleton a)) x))) trivial)

end finite_dimensional

theorem finite_dimensional_of_dim_eq_zero {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (h : vector_space.dim K V = 0) : finite_dimensional K V :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (finite_dimensional K V)) (propext finite_dimensional.finite_dimensional_iff_dim_lt_omega)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (vector_space.dim K V < cardinal.omega)) h)) cardinal.omega_pos)

theorem finite_dimensional_of_dim_eq_one {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (h : vector_space.dim K V = 1) : finite_dimensional K V :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (finite_dimensional K V)) (propext finite_dimensional.finite_dimensional_iff_dim_lt_omega)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (vector_space.dim K V < cardinal.omega)) h)) cardinal.one_lt_omega)

theorem findim_eq_zero_of_dim_eq_zero {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (h : vector_space.dim K V = 0) : finite_dimensional.findim K V = 0 := sorry

theorem finite_dimensional_bot (K : Type u) (V : Type v) [field K] [add_comm_group V] [vector_space K V] : finite_dimensional K ↥⊥ := sorry

@[simp] theorem findim_bot (K : Type u) (V : Type v) [field K] [add_comm_group V] [vector_space K V] : finite_dimensional.findim K ↥⊥ = 0 := sorry

theorem bot_eq_top_of_dim_eq_zero {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (h : vector_space.dim K V = 0) : ⊥ = ⊤ := sorry

@[simp] theorem dim_eq_zero {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {S : submodule K V} : vector_space.dim K ↥S = 0 ↔ S = ⊥ := sorry

@[simp] theorem findim_eq_zero {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {S : submodule K V} [finite_dimensional K ↥S] : finite_dimensional.findim K ↥S = 0 ↔ S = ⊥ := sorry

namespace submodule

/-- A submodule is finitely generated if and only if it is finite-dimensional -/
theorem fg_iff_finite_dimensional {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (s : submodule K V) : fg s ↔ finite_dimensional K ↥s := sorry

/-- A submodule contained in a finite-dimensional submodule is
finite-dimensional. -/
theorem finite_dimensional_of_le {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {S₁ : submodule K V} {S₂ : submodule K V} [finite_dimensional K ↥S₂] (h : S₁ ≤ S₂) : finite_dimensional K ↥S₁ :=
  iff.mpr finite_dimensional.finite_dimensional_iff_dim_lt_omega
    (lt_of_le_of_lt (dim_le_of_submodule S₁ S₂ h) (finite_dimensional.dim_lt_omega K ↥S₂))

/-- The inf of two submodules, the first finite-dimensional, is
finite-dimensional. -/
protected instance finite_dimensional_inf_left {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (S₁ : submodule K V) (S₂ : submodule K V) [finite_dimensional K ↥S₁] : finite_dimensional K ↥(S₁ ⊓ S₂) :=
  finite_dimensional_of_le inf_le_left

/-- The inf of two submodules, the second finite-dimensional, is
finite-dimensional. -/
protected instance finite_dimensional_inf_right {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (S₁ : submodule K V) (S₂ : submodule K V) [finite_dimensional K ↥S₂] : finite_dimensional K ↥(S₁ ⊓ S₂) :=
  finite_dimensional_of_le inf_le_right

/-- The sup of two finite-dimensional submodules is
finite-dimensional. -/
protected instance finite_dimensional_sup {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (S₁ : submodule K V) (S₂ : submodule K V) [h₁ : finite_dimensional K ↥S₁] [h₂ : finite_dimensional K ↥S₂] : finite_dimensional K ↥(S₁ ⊔ S₂) :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (finite_dimensional K ↥(S₁ ⊔ S₂))) (Eq.symm (propext (fg_iff_finite_dimensional (S₁ ⊔ S₂))))))
    (fg_sup
      (eq.mp (Eq._oldrec (Eq.refl (finite_dimensional K ↥S₁)) (Eq.symm (propext (fg_iff_finite_dimensional S₁)))) h₁)
      (eq.mp (Eq._oldrec (Eq.refl (finite_dimensional K ↥S₂)) (Eq.symm (propext (fg_iff_finite_dimensional S₂)))) h₂))

/-- In a finite-dimensional vector space, the dimensions of a submodule and of the corresponding
quotient add up to the dimension of the space. -/
theorem findim_quotient_add_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (s : submodule K V) : finite_dimensional.findim K (quotient s) + finite_dimensional.findim K ↥s = finite_dimensional.findim K V := sorry

/-- The dimension of a submodule is bounded by the dimension of the ambient space. -/
theorem findim_le {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (s : submodule K V) : finite_dimensional.findim K ↥s ≤ finite_dimensional.findim K V := sorry

/-- The dimension of a strict submodule is strictly bounded by the dimension of the ambient space. -/
theorem findim_lt {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {s : submodule K V} (h : s < ⊤) : finite_dimensional.findim K ↥s < finite_dimensional.findim K V := sorry

/-- The dimension of a quotient is bounded by the dimension of the ambient space. -/
theorem findim_quotient_le {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (s : submodule K V) : finite_dimensional.findim K (quotient s) ≤ finite_dimensional.findim K V := sorry

/-- The sum of the dimensions of s + t and s ∩ t is the sum of the dimensions of s and t -/
theorem dim_sup_add_dim_inf_eq {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (s : submodule K V) (t : submodule K V) [finite_dimensional K ↥s] [finite_dimensional K ↥t] : finite_dimensional.findim K ↥(s ⊔ t) + finite_dimensional.findim K ↥(s ⊓ t) =
  finite_dimensional.findim K ↥s + finite_dimensional.findim K ↥t := sorry

theorem eq_top_of_disjoint {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (s : submodule K V) (t : submodule K V) (hdim : finite_dimensional.findim K ↥s + finite_dimensional.findim K ↥t = finite_dimensional.findim K V) (hdisjoint : disjoint s t) : s ⊔ t = ⊤ := sorry

end submodule

namespace linear_equiv

/-- Finite dimensionality is preserved under linear equivalence. -/
protected theorem finite_dimensional {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {V₂ : Type v'} [add_comm_group V₂] [vector_space K V₂] (f : linear_equiv K V V₂) [finite_dimensional K V] : finite_dimensional K V₂ :=
  is_noetherian_of_linear_equiv f

/-- The dimension of a finite dimensional space is preserved under linear equivalence. -/
theorem findim_eq {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {V₂ : Type v'} [add_comm_group V₂] [vector_space K V₂] (f : linear_equiv K V V₂) [finite_dimensional K V] : finite_dimensional.findim K V = finite_dimensional.findim K V₂ := sorry

end linear_equiv

namespace finite_dimensional

/--
Two finite-dimensional vector spaces are isomorphic if they have the same (finite) dimension.
-/
theorem nonempty_linear_equiv_of_findim_eq {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {V₂ : Type v'} [add_comm_group V₂] [vector_space K V₂] [finite_dimensional K V] [finite_dimensional K V₂] (cond : findim K V = findim K V₂) : Nonempty (linear_equiv K V V₂) := sorry

/--
Two finite-dimensional vector spaces are isomorphic if and only if they have the same (finite) dimension.
-/
theorem nonempty_linear_equiv_iff_findim_eq {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {V₂ : Type v'} [add_comm_group V₂] [vector_space K V₂] [finite_dimensional K V] [finite_dimensional K V₂] : Nonempty (linear_equiv K V V₂) ↔ findim K V = findim K V₂ := sorry

/--
Two finite-dimensional vector spaces are isomorphic if they have the same (finite) dimension.
-/
def linear_equiv.of_findim_eq {K : Type u} (V : Type v) [field K] [add_comm_group V] [vector_space K V] (V₂ : Type v') [add_comm_group V₂] [vector_space K V₂] [finite_dimensional K V] [finite_dimensional K V₂] (cond : findim K V = findim K V₂) : linear_equiv K V V₂ :=
  Classical.choice (nonempty_linear_equiv_of_findim_eq cond)

theorem eq_of_le_of_findim_le {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {S₁ : submodule K V} {S₂ : submodule K V} [finite_dimensional K ↥S₂] (hle : S₁ ≤ S₂) (hd : findim K ↥S₂ ≤ findim K ↥S₁) : S₁ = S₂ := sorry

/-- If a submodule is less than or equal to a finite-dimensional
submodule with the same dimension, they are equal. -/
theorem eq_of_le_of_findim_eq {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {S₁ : submodule K V} {S₂ : submodule K V} [finite_dimensional K ↥S₂] (hle : S₁ ≤ S₂) (hd : findim K ↥S₁ = findim K ↥S₂) : S₁ = S₂ :=
  eq_of_le_of_findim_le hle (eq.ge hd)

end finite_dimensional

namespace linear_map

/-- On a finite-dimensional space, an injective linear map is surjective. -/
theorem surjective_of_injective {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {f : linear_map K V V} (hinj : function.injective ⇑f) : function.surjective ⇑f := sorry

/-- On a finite-dimensional space, a linear map is injective if and only if it is surjective. -/
theorem injective_iff_surjective {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {f : linear_map K V V} : function.injective ⇑f ↔ function.surjective ⇑f := sorry

theorem ker_eq_bot_iff_range_eq_top {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {f : linear_map K V V} : ker f = ⊥ ↔ range f = ⊤ := sorry

/-- In a finite-dimensional space, if linear maps are inverse to each other on one side then they
are also inverse to each other on the other side. -/
theorem mul_eq_one_of_mul_eq_one {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {f : linear_map K V V} {g : linear_map K V V} (hfg : f * g = 1) : g * f = 1 := sorry

/-- In a finite-dimensional space, linear maps are inverse to each other on one side if and only if
they are inverse to each other on the other side. -/
theorem mul_eq_one_comm {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {f : linear_map K V V} {g : linear_map K V V} : f * g = 1 ↔ g * f = 1 :=
  { mp := mul_eq_one_of_mul_eq_one, mpr := mul_eq_one_of_mul_eq_one }

/-- In a finite-dimensional space, linear maps are inverse to each other on one side if and only if
they are inverse to each other on the other side. -/
theorem comp_eq_id_comm {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {f : linear_map K V V} {g : linear_map K V V} : comp f g = id ↔ comp g f = id :=
  mul_eq_one_comm

/-- The image under an onto linear map of a finite-dimensional space is also finite-dimensional. -/
theorem finite_dimensional_of_surjective {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {V₂ : Type v'} [add_comm_group V₂] [vector_space K V₂] [h : finite_dimensional K V] (f : linear_map K V V₂) (hf : range f = ⊤) : finite_dimensional K V₂ :=
  is_noetherian_of_surjective V f hf

/-- The range of a linear map defined on a finite-dimensional space is also finite-dimensional. -/
protected instance finite_dimensional_range {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {V₂ : Type v'} [add_comm_group V₂] [vector_space K V₂] [h : finite_dimensional K V] (f : linear_map K V V₂) : finite_dimensional K ↥(range f) :=
  linear_equiv.finite_dimensional (quot_ker_equiv_range f)

/-- rank-nullity theorem : the dimensions of the kernel and the range of a linear map add up to
the dimension of the source space. -/
theorem findim_range_add_findim_ker {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {V₂ : Type v'} [add_comm_group V₂] [vector_space K V₂] [finite_dimensional K V] (f : linear_map K V V₂) : finite_dimensional.findim K ↥(range f) + finite_dimensional.findim K ↥(ker f) = finite_dimensional.findim K V := sorry

end linear_map

namespace linear_equiv

/-- The linear equivalence corresponging to an injective endomorphism. -/
def of_injective_endo {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : linear_map K V V) (h_inj : linear_map.ker f = ⊥) : linear_equiv K V V :=
  trans (of_injective f h_inj) (of_top (linear_map.range f) sorry)

@[simp] theorem coe_of_injective_endo {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : linear_map K V V) (h_inj : linear_map.ker f = ⊥) : ⇑(of_injective_endo f h_inj) = ⇑f :=
  rfl

@[simp] theorem of_injective_endo_right_inv {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : linear_map K V V) (h_inj : linear_map.ker f = ⊥) : f * ↑(symm (of_injective_endo f h_inj)) = 1 :=
  linear_map.ext (apply_symm_apply (of_injective_endo f h_inj))

@[simp] theorem of_injective_endo_left_inv {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : linear_map K V V) (h_inj : linear_map.ker f = ⊥) : ↑(symm (of_injective_endo f h_inj)) * f = 1 :=
  linear_map.ext (symm_apply_apply (of_injective_endo f h_inj))

end linear_equiv

namespace linear_map

theorem is_unit_iff {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : linear_map K V V) : is_unit f ↔ ker f = ⊥ := sorry

end linear_map

@[simp] theorem findim_top {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] : finite_dimensional.findim K ↥⊤ = finite_dimensional.findim K V := sorry

namespace linear_map

theorem injective_iff_surjective_of_findim_eq_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {V₂ : Type v'} [add_comm_group V₂] [vector_space K V₂] [finite_dimensional K V] [finite_dimensional K V₂] (H : finite_dimensional.findim K V = finite_dimensional.findim K V₂) {f : linear_map K V V₂} : function.injective ⇑f ↔ function.surjective ⇑f := sorry

theorem findim_le_findim_of_injective {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {V₂ : Type v'} [add_comm_group V₂] [vector_space K V₂] [finite_dimensional K V] [finite_dimensional K V₂] {f : linear_map K V V₂} (hf : function.injective ⇑f) : finite_dimensional.findim K V ≤ finite_dimensional.findim K V₂ := sorry

end linear_map

namespace alg_hom

theorem bijective {F : Type u_1} [field F] {E : Type u_2} [field E] [algebra F E] [finite_dimensional F E] (ϕ : alg_hom F E E) : function.bijective ⇑ϕ :=
  (fun (inj : function.injective ⇑(to_linear_map ϕ)) =>
      { left := inj, right := iff.mp (linear_map.injective_iff_surjective_of_findim_eq_findim rfl) inj })
    (ring_hom.injective (to_ring_hom ϕ))

end alg_hom

/-- Bijection between algebra equivalences and algebra homomorphisms -/
def alg_equiv_equiv_alg_hom (F : Type u) [field F] (E : Type v) [field E] [algebra F E] [finite_dimensional F E] : alg_equiv F E E ≃ alg_hom F E E :=
  equiv.mk (fun (ϕ : alg_equiv F E E) => alg_equiv.to_alg_hom ϕ)
    (fun (ϕ : alg_hom F E E) => alg_equiv.of_bijective ϕ (alg_hom.bijective ϕ)) sorry sorry

/-- An integral domain that is module-finite as an algebra over a field is a field. -/
def field_of_finite_dimensional (F : Type u_1) (K : Type u_2) [field F] [integral_domain K] [algebra F K] [finite_dimensional F K] : field K :=
  field.mk integral_domain.add integral_domain.add_assoc integral_domain.zero integral_domain.zero_add
    integral_domain.add_zero integral_domain.neg integral_domain.sub integral_domain.add_left_neg integral_domain.add_comm
    integral_domain.mul integral_domain.mul_assoc integral_domain.one integral_domain.one_mul integral_domain.mul_one
    integral_domain.left_distrib integral_domain.right_distrib integral_domain.mul_comm
    (fun (x : K) => dite (x = 0) (fun (H : x = 0) => 0) fun (H : ¬x = 0) => classical.some sorry)
    integral_domain.exists_pair_ne sorry sorry

namespace submodule

theorem findim_mono {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] : monotone fun (s : submodule K V) => finite_dimensional.findim K ↥s :=
  fun (s t : submodule K V) (hst : s ≤ t) =>
    trans_rel_right LessEq (linear_equiv.findim_eq (linear_equiv.symm (comap_subtype_equiv_of_le hst)))
      (findim_le (comap (submodule.subtype t) s))

theorem lt_of_le_of_findim_lt_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {s : submodule K V} {t : submodule K V} (le : s ≤ t) (lt : finite_dimensional.findim K ↥s < finite_dimensional.findim K ↥t) : s < t := sorry

theorem lt_top_of_findim_lt_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {s : submodule K V} (lt : finite_dimensional.findim K ↥s < finite_dimensional.findim K V) : s < ⊤ :=
  lt_of_le_of_findim_lt_findim le_top
    (eq.mp (Eq._oldrec (Eq.refl (finite_dimensional.findim K ↥s < finite_dimensional.findim K V)) (Eq.symm findim_top))
      lt)

theorem findim_lt_findim_of_lt {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {s : submodule K V} {t : submodule K V} (hst : s < t) : finite_dimensional.findim K ↥s < finite_dimensional.findim K ↥t := sorry

end submodule

theorem findim_span_le_card {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (s : set V) [fin : fintype ↥s] : finite_dimensional.findim K ↥(submodule.span K s) ≤ finset.card (set.to_finset s) := sorry

theorem findim_span_eq_card {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type u_1} [fintype ι] {b : ι → V} (hb : linear_independent K b) : finite_dimensional.findim K ↥(submodule.span K (set.range b)) = fintype.card ι := sorry

theorem findim_span_set_eq_card {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] (s : set V) [fin : fintype ↥s] (hs : linear_independent K coe) : finite_dimensional.findim K ↥(submodule.span K s) = finset.card (set.to_finset s) := sorry

theorem span_lt_of_subset_of_card_lt_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {s : set V} [fintype ↥s] {t : submodule K V} (subset : s ⊆ ↑t) (card_lt : finset.card (set.to_finset s) < finite_dimensional.findim K ↥t) : submodule.span K s < t :=
  submodule.lt_of_le_of_findim_lt_findim (iff.mpr submodule.span_le subset)
    (lt_of_le_of_lt (findim_span_le_card s) card_lt)

theorem span_lt_top_of_card_lt_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {s : set V} [fintype ↥s] (card_lt : finset.card (set.to_finset s) < finite_dimensional.findim K V) : submodule.span K s < ⊤ :=
  submodule.lt_top_of_findim_lt_findim (lt_of_le_of_lt (findim_span_le_card s) card_lt)

theorem findim_span_singleton {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {v : V} (hv : v ≠ 0) : finite_dimensional.findim K ↥(submodule.span K (singleton v)) = 1 := sorry

theorem linear_independent_of_span_eq_top_of_card_eq_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type u_1} [fintype ι] {b : ι → V} (span_eq : submodule.span K (set.range b) = ⊤) (card_eq : fintype.card ι = finite_dimensional.findim K V) : linear_independent K b := sorry

/-- A finite family of vectors is linearly independent if and only if
its cardinality equals the dimension of its span. -/
theorem linear_independent_iff_card_eq_findim_span {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type u_1} [fintype ι] {b : ι → V} : linear_independent K b ↔ fintype.card ι = finite_dimensional.findim K ↥(submodule.span K (set.range b)) := sorry

theorem is_basis_of_span_eq_top_of_card_eq_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type u_1} [fintype ι] {b : ι → V} (span_eq : submodule.span K (set.range b) = ⊤) (card_eq : fintype.card ι = finite_dimensional.findim K V) : is_basis K b :=
  { left := linear_independent_of_span_eq_top_of_card_eq_findim span_eq card_eq, right := span_eq }

theorem finset_is_basis_of_span_eq_top_of_card_eq_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {s : finset V} (span_eq : submodule.span K ↑s = ⊤) (card_eq : finset.card s = finite_dimensional.findim K V) : is_basis K coe :=
  is_basis_of_span_eq_top_of_card_eq_findim (Eq.symm subtype.range_coe_subtype ▸ span_eq)
    (trans (fintype.card_coe s) card_eq)

theorem set_is_basis_of_span_eq_top_of_card_eq_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {s : set V} [fintype ↥s] (span_eq : submodule.span K s = ⊤) (card_eq : finset.card (set.to_finset s) = finite_dimensional.findim K V) : is_basis K fun (x : ↥s) => ↑x :=
  is_basis_of_span_eq_top_of_card_eq_findim (Eq.symm subtype.range_coe_subtype ▸ span_eq)
    (trans (Eq.symm (set.to_finset_card s)) card_eq)

theorem span_eq_top_of_linear_independent_of_card_eq_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type u_1} [hι : Nonempty ι] [fintype ι] {b : ι → V} (lin_ind : linear_independent K b) (card_eq : fintype.card ι = finite_dimensional.findim K V) : submodule.span K (set.range b) = ⊤ := sorry

theorem is_basis_of_linear_independent_of_card_eq_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {ι : Type u_1} [Nonempty ι] [fintype ι] {b : ι → V} (lin_ind : linear_independent K b) (card_eq : fintype.card ι = finite_dimensional.findim K V) : is_basis K b :=
  { left := lin_ind, right := span_eq_top_of_linear_independent_of_card_eq_findim lin_ind card_eq }

theorem finset_is_basis_of_linear_independent_of_card_eq_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {s : finset V} (hs : finset.nonempty s) (lin_ind : linear_independent K coe) (card_eq : finset.card s = finite_dimensional.findim K V) : is_basis K coe :=
  is_basis_of_linear_independent_of_card_eq_findim lin_ind (trans (fintype.card_coe s) card_eq)

theorem set_is_basis_of_linear_independent_of_card_eq_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {s : set V} [Nonempty ↥s] [fintype ↥s] (lin_ind : linear_independent K coe) (card_eq : finset.card (set.to_finset s) = finite_dimensional.findim K V) : is_basis K coe :=
  is_basis_of_linear_independent_of_card_eq_findim lin_ind (trans (Eq.symm (set.to_finset_card s)) card_eq)

theorem subalgebra.dim_eq_one_of_eq_bot {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] {S : subalgebra F E} (h : S = ⊥) : vector_space.dim F ↥S = 1 := sorry

@[simp] theorem subalgebra.dim_bot {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] : vector_space.dim F ↥⊥ = 1 :=
  subalgebra.dim_eq_one_of_eq_bot rfl

theorem subalgebra_top_dim_eq_submodule_top_dim {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] : vector_space.dim F ↥⊤ = vector_space.dim F ↥⊤ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (vector_space.dim F ↥⊤ = vector_space.dim F ↥⊤)) (Eq.symm algebra.coe_top)))
    (Eq.refl (vector_space.dim F ↥⊤))

theorem subalgebra_top_findim_eq_submodule_top_findim {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] : finite_dimensional.findim F ↥⊤ = finite_dimensional.findim F ↥⊤ := sorry

theorem subalgebra.dim_top {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] : vector_space.dim F ↥⊤ = vector_space.dim F E :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (vector_space.dim F ↥⊤ = vector_space.dim F E)) subalgebra_top_dim_eq_submodule_top_dim))
    dim_top

theorem subalgebra.finite_dimensional_bot {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] : finite_dimensional F ↥⊥ :=
  finite_dimensional_of_dim_eq_one subalgebra.dim_bot

@[simp] theorem subalgebra.findim_bot {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] : finite_dimensional.findim F ↥⊥ = 1 := sorry

theorem subalgebra.findim_eq_one_of_eq_bot {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] {S : subalgebra F E} (h : S = ⊥) : finite_dimensional.findim F ↥S = 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (finite_dimensional.findim F ↥S = 1)) h)) subalgebra.findim_bot

theorem subalgebra.eq_bot_of_findim_one {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] {S : subalgebra F E} (h : finite_dimensional.findim F ↥S = 1) : S = ⊥ := sorry

theorem subalgebra.eq_bot_of_dim_one {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] {S : subalgebra F E} (h : vector_space.dim F ↥S = 1) : S = ⊥ := sorry

@[simp] theorem subalgebra.bot_eq_top_of_dim_eq_one {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] (h : vector_space.dim F E = 1) : ⊥ = ⊤ := sorry

@[simp] theorem subalgebra.bot_eq_top_of_findim_eq_one {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] (h : finite_dimensional.findim F E = 1) : ⊥ = ⊤ := sorry

@[simp] theorem subalgebra.dim_eq_one_iff {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] {S : subalgebra F E} : vector_space.dim F ↥S = 1 ↔ S = ⊥ :=
  { mp := subalgebra.eq_bot_of_dim_one, mpr := subalgebra.dim_eq_one_of_eq_bot }

@[simp] theorem subalgebra.findim_eq_one_iff {F : Type u_1} {E : Type u_2} [field F] [field E] [algebra F E] {S : subalgebra F E} : finite_dimensional.findim F ↥S = 1 ↔ S = ⊥ :=
  { mp := subalgebra.eq_bot_of_findim_one, mpr := subalgebra.findim_eq_one_of_eq_bot }

namespace module

namespace End

theorem exists_ker_pow_eq_ker_pow_succ {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : End K V) : ∃ (k : ℕ), k ≤ finite_dimensional.findim K V ∧ linear_map.ker (f ^ k) = linear_map.ker (f ^ Nat.succ k) := sorry

theorem ker_pow_constant {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] {f : End K V} {k : ℕ} (h : linear_map.ker (f ^ k) = linear_map.ker (f ^ Nat.succ k)) (m : ℕ) : linear_map.ker (f ^ k) = linear_map.ker (f ^ (k + m)) := sorry

theorem ker_pow_eq_ker_pow_findim_of_le {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] {f : End K V} {m : ℕ} (hm : finite_dimensional.findim K V ≤ m) : linear_map.ker (f ^ m) = linear_map.ker (f ^ finite_dimensional.findim K V) := sorry

theorem ker_pow_le_ker_pow_findim {K : Type u} {V : Type v} [field K] [add_comm_group V] [vector_space K V] [finite_dimensional K V] (f : End K V) (m : ℕ) : linear_map.ker (f ^ m) ≤ linear_map.ker (f ^ finite_dimensional.findim K V) := sorry


import bridge
import algebra.char_zero
import algebra.char_p

noncomputable theory
open_locale tensor_product
open_locale classical

open module

variables {K F : Type*} [field K] [field F]
variables (p ℓ : ℕ) [fact (nat.prime p)]

theorem bridge_char
  [char_p K p]
  [char_p F ℓ]
  (HH : p ≠ ℓ)
  (H : submodule F (mul_base_change K F))
  (hH : H.dual_annihilator.acl)
  (u v : Kˣ)
  (hu : u.as ∉ H) (hv : v.as ∉ H)
  (hu₁ : ∀ y : Kˣ, (y : K) = 1 + u → y.as ∉ H)
  (hu₂ : ∀ y : Kˣ, (y : K) = 1 + u → (u⁻¹ * y).as ∉ H)
  (hv₁: ∀ y : Kˣ, (y : K) = 1 + v → y.as ∉ H)
  (hv₂ : ∀ y : Kˣ, (y : K) = 1 + v → (v⁻¹ * y).as ∉ H) :
  ¬ linear_independent F ![H.mkq u.as, H.mkq v.as] :=
begin
  intro c,
  let S : bridge.setup H hH := ⟨u,v,hu,hv,hu₁,hu₂,hv₁,hv₂,c⟩,
  let E := S.make,
  --obtain ⟨m,n,hn,hh⟩ := is_prime_field.cond (1 - S.A),
  have key := E.main_theorem_char (p+1) (by simp),
  push_cast at key,
  have : (p : K) = 0 := char_p.cast_eq_zero _ _,
  simp only [this, zero_add, sub_self, one_mul] at key,
  rw S.φ_map_u at key,
  replace key := projectivization.affine_embedding_injective _ _ key,
  simp only [prod.mk.inj_iff, self_eq_add_left, nat.cast_eq_zero, eq_self_iff_true, and_true] at key,
  apply HH,
  erw char_p.cast_eq_zero_iff F ℓ at key,
  by_cases hℓ : ℓ = 0,
  { rw hℓ at key, simp only [zero_dvd_iff] at key, rwa hℓ },
  { have : nat.prime ℓ,
    { cases char_p.char_is_prime_or_zero F ℓ, { exact h }, exact false.elim (hℓ h) },
    rw nat.dvd_prime at key, cases key,
    { have := nat.prime.ne_one this, exact false.elim (this key) },
    { exact key.symm },
    exact fact.out _ },
end

lemma bridge_char_finite_dimensional
  [char_p K p]
  [char_p F ℓ]
  (HH : p ≠ ℓ)
  (T : submodule F (mul_base_change K F))
  (hT : T.dual_annihilator.acl) :
  finite_dimensional F (T.nonrig ⧸ T.comap T.nonrig.subtype) :=
begin
  by_cases hh : ∃ (u : Kˣ), T.nonrig_condition u,
  swap,
  { push_neg at hh,
    have : set_of T.nonrig_condition = ∅,
    { rw set.eq_empty_iff_forall_not_mem,
      exact hh },
    dsimp [submodule.nonrig],
    rw [this, set.image_empty],
    have he := submodule.mkq_comp_sup_mod_surjective (submodule.span F ∅) T,
    let e : _ →ₗ[_] _ := _, change function.surjective e at he,
    haveI : finite_dimensional F (submodule.span F (∅ : set (mul_base_change K F))),
    { apply finite_dimensional.span_of_finite, exact set.finite_empty },
    apply e.finite_dimensional_of_surjective,
    rwa linear_map.range_eq_top },
  obtain ⟨u,hu⟩ := hh,
  apply T.finite_dimensional_of_not_linear_independent
    (units.as '' set_of T.nonrig_condition) u.as ⟨u,hu,rfl⟩ hu.1,
  rintros a ⟨a,ha,rfl⟩,
  apply bridge_char p ℓ HH T hT a u ha.1 hu.1
    (λ y hy, (ha.2 y hy).1)
    (λ y hy, (ha.2 y hy).2)
    (λ y hy, (hu.2 y hy).1)
    (λ y hy, (hu.2 y hy).2)
end

theorem bridge_char_codim
  [char_p K p]
  [char_p F ℓ]
  (HH : p ≠ ℓ)
  (T : submodule F (mul_base_change K F))
  (hT : T.dual_annihilator.acl) :
  finite_dimensional.finrank F (T.nonrig ⧸ T.comap T.nonrig.subtype) ≤ 1 :=
begin
  have fd : finite_dimensional F (T.nonrig ⧸ T.comap T.nonrig.subtype) :=
    bridge_char_finite_dimensional p ℓ HH T hT,
  by_cases hh : ∃ (u : Kˣ), T.nonrig_condition u,
  swap,
  { push_neg at hh,
    have : set_of T.nonrig_condition = ∅,
    { rw set.eq_empty_iff_forall_not_mem,
      exact hh },
    dsimp [submodule.nonrig] at *,
    rw [this, set.image_empty] at *,
    have he := submodule.mkq_comp_sup_mod_surjective (submodule.span F ∅) T,
    resetI,
    rw finrank_le_one_iff,
    use 0, rintros ⟨w,hw⟩, use 0, rw submodule.mem_sup at hw,
    obtain ⟨y,hy,z,hz,rfl⟩ := hw,
    simp only [submodule.span_empty, submodule.mem_bot] at hz,
    symmetry,
    simpa only [hz, add_zero, submodule.quotient.quot_mk_eq_mk, smul_zero,
      submodule.quotient.mk_eq_zero] },
  obtain ⟨u,hu⟩ := hh,
  apply T.finrank_le_one_of_not_linear_independent
    (units.as '' set_of T.nonrig_condition) u.as ⟨u,hu,rfl⟩ hu.1,
  rintros a ⟨a,ha,rfl⟩,
  apply bridge_char p ℓ HH T hT a u ha.1 hu.1
    (λ y hy, (ha.2 y hy).1)
    (λ y hy, (ha.2 y hy).2)
    (λ y hy, (hu.2 y hy).1)
    (λ y hy, (hu.2 y hy).2)
end


namespace fol_05

variable A : Type
variables P Q : A → Prop

theorem fol_05 : (∃ x, P x ∨ Q x) → (∀ x, ¬ Q x) → ∃ y, P y :=
assume h1: ∃ x, P x ∨ Q x,
assume h2: ∀ x, ¬ Q x,
show ∃ (y : A), P y, from
  (exists.elim h1
   (assume t (h3: P t ∨ Q t),
    have h4: P t, from or.elim h3
      (assume h5: P t,
       h5)
      (assume h6: Q t,
       have h7: ¬ Q t, from h2 t,
       have h8: false, from h7 h6,
       false.elim h8),
     exists.intro t h4))

end fol_05


import TL
import category

namespace TT

  variable A : type

  def proof_eq (a₁ a₂ : tset A) := ⊨ (a₁ ≃[A] a₂)

  section equivalence_relation

  theorem proof_eq_refl : reflexive (proof_eq A) :=
  begin
    intro a,
    unfold proof_eq,
    apply proof.all_intro 𝒫 A,
    apply proof.and_intro,
    all_goals {
        apply to_imp,
        apply proof.axm,
        apply_rules WF_rules,
        apply WF.lift,
        exact a.property,
        refl
      }
  end

  theorem proof_eq_symm : symmetric (proof_eq A) :=
  begin
    intros a₁ a₂ H,
    apply proof.all_intro 𝒫 A,
    apply proof.and_intro,
    apply proof.and_right _ ((↑a₁ ∈ 𝟘) ⟹ (↑a₂ ∈ 𝟘)) _,
    apply proof.all_elim, sorry,
    -- exact H,
    apply proof.and_left _ _ ((↑a₂ ∈ 𝟘) ⟹ (↑a₁ ∈ 𝟘)),
    apply proof.all_elim,
    sorry,
    -- exact H,
  end

  theorem proof_eq_trans : transitive (proof_eq A) := sorry

  theorem proof_eq_equiv : equivalence (proof_eq A) :=
    ⟨proof_eq_refl A, proof_eq_symm A, proof_eq_trans A⟩

  end equivalence_relation

  definition TT.setoid : setoid (closed_term A) :=
    {r := proof_eq A, iseqv := proof_eq_equiv A}

  #check TT.setoid

  local attribute [instance] TT.setoid

  def tset := quotient (TT.setoid (𝒫 A))

  def to_tset := quot.mk (proof_eq (𝒫 A))

  def coe_term_tset : has_coe (closed_term 𝒫 A) (tset A) := ⟨to_tset A⟩
  local attribute [instance] coe_term_tset

  def elem_maker {A : type} (a : closed_term A) : tset A → Prop :=
    quotient.lift (λ α : closed_term (𝒫 A), ⊨ a ∈ α) (begin
      intros α₁ α₂ heq,
      simp,
      constructor,
      intro h,

      sorry, sorry
    end)

  def tset.star_singleton : tset Unit :=
    by {apply to_tset, apply closed_term.mk _ ⟦Unit | ⊤⟧, apply WF.comp, exact WF.top}

  variables {A B : type}

  def AB_setoid : setoid (closed_term (𝒫 (A ×× B))) := TT.setoid (𝒫 (A ×× B))

  local attribute [instance] AB_setoid

  def term_is_subset_of_terms {A B : type} (α : closed_term 𝒫 A) (β : closed_term 𝒫 B) (F : closed_term 𝒫 (A××B)) : Prop :=
    ⊨ F ⊆[𝒫 A] (@term_prod A B α β)

  def lifted_is_subset_of_terms {A B : type} : tset A → tset B → tset (A ×× B) → Prop :=
    λ α β, (
      quotient.lift  (λ F : closed_term (𝒫 (A ×× B)), term_is_subset_of_terms α β F) (begin
        intros F₁ F₂ heq,
        simp,
        unfold term_is_subset_of_terms,
        constructor; sorry
      end)
    )

end TT


import .basic

universes u v

section circuit

variables {α : Type u} {i j b b' b'' x y c d r s t : α}
[complete_lattice α] [is_atomistic α] [is_coatomistic α] {M : supermatroid α}

namespace supermatroid

/-- A circuit is a minimal dependent element-/
def circuit (M : supermatroid α) : α → Prop := minimals (≤) M.indepᶜ

/-- A cocircuit is a maximally nonspanning element-/
def cocircuit (M : supermatroid α) : α → Prop := maximals (≤) M.spanningᶜ

lemma circuit.not_indep (hc : M.circuit c) : ¬ M.indep c := hc.1

lemma circuit.dep (hc : M.circuit c) : M.dep c := hc.1

lemma circuit.indep_of_lt (hC : M.circuit c) (hiC : i < c) : M.indep i :=
  by_contra (λ h, hiC.ne.symm (hC.2 h hiC.le))

def cov_between (x a b : α) : Prop := a ⋖ x ∧ x ⋖ b

lemma foo {a b i i' j : α} (hii' : i ≠ i') (hi_dep : M.dep i) (hi'_dep : M.dep i')
  (hi : cov_between i a b) (hi' : cov_between i' a b) (hj : cov_between j a b) : M.dep j :=
begin
  intro hj_ind,
  have ha := hj_ind.indep_of_le hj.1.le,
end

lemma dep.exists_circuit_le (hx : M.dep x) : ∃ c, M.circuit c ∧ c ≤ x :=
begin
  obtain ⟨i,hi⟩ := M.exists_basis_of x,

  obtain ⟨z, hzx, hzi, hzx⟩ := exists_atom_of_not_le (λ h, hx (hi.indep_of_le h)),

  set ps := {p | is_coatom p ∧ M.dep (p ⊓ (i ⊔ z))} with hps,
  refine ⟨Inf ps, ⟨λ hpi, _ ,sorry⟩, le_of_le_forall_coatom (λ q hq hxq, _)⟩,
  { },
  convert Inf_le _,
  simp only [mem_set_of_eq, hps],
  refine ⟨hq, hi.not_indep_of_lt
    (lt_of_le_of_ne (le_inf (hi.le.trans hxq) le_sup_left)
      (λ h, hzx (by {rw h, simp [hzi.trans hxq]})))
    (inf_le_of_right_le (sup_le hi.le hzi))⟩,

  --set sc := {x ∈ si | M.indep (Sup ((si.insert z) \ {x}))}.insert z with hsc,

  --have hciz : Sup sc ≤ Sup (si.insert z) := Sup_le_Sup (insert_subset_insert (sep_subset _ _)),

  -- refine ⟨Sup sc, ⟨λ (hci : M.indep (Sup sc)),_,_⟩,_⟩,
  -- { obtain ⟨j,hj,hcj⟩ := hci.le_basis_of hciz, },

end

end supermatroid

end circuit