import tactic
-- 1.
-- #eval is_true (∀ x, x < 5 → ∃ y, y < x ∧ y*y = x)

-- 2.
namespace ex_2

constants (prime: ℕ → Prop) (coprime : ℕ → ℕ → Prop)
theorem prime.coprime_iff_not_dvd {p n : ℕ} (pp : prime p) :
  coprime p n ↔ ¬ p ∣ n := sorry

theorem prime.dvd_iff_not_coprime {p n : ℕ} (pp : prime p) :
  p ∣ n ↔ ¬ coprime p n := sorry

theorem prime.dvd_mul {p m n : ℕ} (pp : prime p) :
  p ∣ n * m ↔ p ∣ m ∨ p ∣ n := sorry

end ex_2

-- source: https://github.com/enharsha/hello-world
namespace ex_3

variables (α : Type) (p q : α → Prop)

example: (∀x, p x ∧ q x) ↔ (∀ x, p x) ∧ (∀ x, q x) :=
iff.intro
(assume h: (∀ x,p x ∧ q x), and.intro (assume h1 : α, (h h1).left) (assume h2 : α, (h h2).right))
(assume h: (∀ x, p x) ∧ (∀ x, q x), assume h1 : α, and.intro (h.left h1) (h.right h1))

example: (∀ x, p x → q x) → (∀ x, p x) → (∀ x, q x) :=
assume h1: (∀ x, p x → q x), assume h2: (∀ x, p x), assume h3: α, (h1 h3) (h2 h3)

example: (∀ x, p x) ∨ (∀ x, q x) → ∀ x, p x ∨ q x :=
assume h: (∀ x, p x) ∨ (∀ x, q x), assume x : α,
or.elim h (assume h1 : ∀ x, p x, or.inl (h1 x)) (assume h2 : ∀ x, q x, or.inr (h2 x))

variables (men : Type) (barber : men)
variable (shaves : men → men → Prop)

--- false means contradiction
example (h : ∀ x : men, shaves barber x ↔ ¬ shaves x x) : false :=
have h1 : shaves barber barber ↔ ¬shaves barber barber, from (h barber),
have h2 : ¬ shaves barber barber, from (assume h3 : shaves barber barber, absurd h3 (h1.mp h3)),
false.elim (h2 (h1.mpr h2))

end ex_3

-- source: https://raw.githubusercontent.com/leanprover/lean2/227fcad22ab2bc27bb7471be7911075d101ba3f9/library/theories/number_theory/primes.lean
namespace ex_4

/-
definition infinite_primes (n : nat) : {p | p ≥ n ∧ prime p} :=
let m := fact (n + 1) in
have m ≥ 1,     from le_of_lt_succ (succ_lt_succ (fact_pos _)),
have m + 1 ≥ 2, from succ_le_succ this,
obtain p `prime p` `p ∣ m + 1`, from sub_prime_and_dvd this,
have p ≥ 2, from ge_two_of_prime `prime p`,
have p > 0, from lt_of_succ_lt (lt_of_succ_le `p ≥ 2`),
have p ≥ n, from by_contradiction
  (suppose ¬ p ≥ n,
    have p < n,     from lt_of_not_ge this,
    have p ≤ n + 1, from le_of_lt (lt.step this),
    have p ∣ m,     from dvd_fact `p > 0` this,
    have p ∣ 1,     from dvd_of_dvd_add_right (!add.comm ▸ `p ∣ m + 1`) this,
    have p ≤ 1,     from le_of_dvd zero_lt_one this,
    show false,     from absurd (le.trans `2 ≤ p` `p ≤ 1`) dec_trivial),
subtype.tag p (and.intro this `prime p`)

lemma exists_infinite_primes (n : nat) : ∃ p, p ≥ n ∧ prime p :=
exists_of_subtype (infinite_primes n)
-/
end ex_4

-- https://github.com/HoanNguyen92/example/tree/master/Lean
namespace ex_5

open nat int

#eval (-15 : ℤ) / 6 -- -3
#eval (-15 : ℤ) % 6 -- 3

lemma Lem1a (a : ℤ) : a ∣ 0 :=
begin
  simp[(∣)],
  existsi (0 : ℤ),
  rw mul_zero,
end

lemma Lem1b (a b : ℤ) : abs(b) < a ∧ b ≠ 0 → ¬ (b ∣ a) := sorry
theorem Thm1 (a b c : ℤ) : a ∣ b ∧ b ∣ c → a ∣ c := sorry
theorem Thm2 (a b c m n : ℤ) : c ∣ a ∧  c ∣ a → c ∣ (a*m + b*n) := sorry

def sumSeries {α : Type} (f : ℕ → α) [has_add α] : ℕ → α
| 0 := f 0
| (succ n) := (sumSeries n) + (f (n+1))

theorem Thm3 (f : ℕ → ℤ) (n : ℕ) (a : ℤ) :
  (∀ k : ℕ, k ≤ n → a ∣ f k) →
  (∀ g : ℕ → ℤ, a ∣ sumSeries (λ x : ℕ, (f x) * (g x)) n) := sorry

#eval gcd (24:ℤ) 18 -- 6

#eval nat_abs (-1) -- 1

-- def zgcd (a b : ℤ) : ℤ := gcd (int.nat_abs a) (int.nat_abs b)
/- ambiguous overload, possible interpretations
  ↑(↑(a.nat_abs).gcd ↑(b.nat_abs))
  ↑(a.nat_abs.gcd b.nat_abs) -/

def zgcd (a b : ℤ) : ℕ := nat.gcd (int.nat_abs a) (int.nat_abs b)

#check gcd (-10:ℤ) (24) -- gcd works with ℤ type

lemma Lem2 (a b q r : ℤ) :
  a = b * q + r → gcd a b = gcd b r := sorry

-- def EuclideaAlgorithm : ℤ → ℤ → ℕ
-- | 0 b := nat_abs b
-- | a 0 := nat_abs a
-- | a b := EuclideaAlgorithm b (a % b)
-- failed to prove recursive application is decreasing

meta def EuclideaAlgorithm : ℤ → ℤ → ℕ
| 0 b := nat_abs b
| a 0 := nat_abs a
| a b := EuclideaAlgorithm b (a % b)

#eval EuclideaAlgorithm 24 18 -- 6
#eval EuclideaAlgorithm 4147 10672 -- 29
#eval gcd (4147:ℤ) 10672 -- 29

-- invalid definition, it uses untrusted declaration 'ex_5.EuclideaAlgorithm'
-- example : gcd (4147:ℤ) 10672 = EuclideaAlgorithm 4147 10672 := rfl

-- (n ≥ 2) ∧ (∀ m : ℕ, m ≥ 1 ∧ m ∣ n → (m = 1 ∨ m =n)) <=>
def isPrime (n : ℕ) : Prop :=
  n ≥ 2 ∧ ∀ (m : ℕ), m ≥ 1 ∧ m ∣ n → m = 1 ∨ m = n

#print isPrime -- let's look for the bracket structure

lemma helper : ∀ (a b : ℕ), a ≤ b + 1 → a = b + 1 ∨ a ≤ b :=
begin
  intros a b H,
  /-
    H : a ≤ b + 1
    ⊢ a = b + 1 ∨ a ≤ b
  if a ≤ b then a ≤ b + 1 using less_than_or_equal.step

  ¬ (a = b + 1 ∨ a ≤ b) ↔ ¬ (a = b + 1) ∧ ¬ (a ≤ b)
  a > b ∧ a ≠ b + 1 → a > b + 1 ↔ ¬ H
  (¬ goal → ¬ H) → (H → goal)
  apply (H → goal) H, Q.E.D
  -/
  sorry,
end

lemma two_is_prime : isPrime 2 :=
begin
  rw isPrime,
  split,
  change 2 ≤ 2, -- lean don't need this line
  exact less_than_or_equal.refl,
  intros m H,
  by_cases (m = 1),
  {
    left,
    exact h,
  },
  {
    have H2 := H.2,
    -- cases H2 with k,
    have lem1 : ∀ (a b : ℕ), a ∣ b → a ≤ b, {
      sorry,
    },
    type_check lem1 m 2,
    have m_le_2 := (lem1 m 2) H2,
    have H3 := helper m 1 m_le_2,
    change m = 2 ∨ m ≤ 1 at H3,
    sorry,
  },
end

lemma Lem3 (n : ℕ) :
  n > 1 → ∃ m : ℕ, isPrime m ∧ m ∣ n := sorry

theorem Thm4 (n : ℕ) :
  n > 1 ∧ ¬ isPrime n → ∃ m : ℕ, isPrime m ∧ m^2 ≤ n ∧ m ∣ n := sorry

def infinite (s : set ℕ) : Prop :=
  ∀ n : ℕ, ∃ m ∈ s, n < m

def TheSetOfPrimes := {n : ℕ | isPrime n}

theorem Thm5 : infinite TheSetOfPrimes :=
begin
  rw [TheSetOfPrimes, infinite],
  -- ⊢ ∀ (n : ℕ), ∃ (m : ℕ) (H : m ∈ {n : ℕ | isPrime n}), n < m
  sorry,
end

theorem DirichletThm (a b : ℤ) :
  a > 0 ∧ gcd a b = 1 →
  infinite {n | isPrime n ∧ (∃ m : ℤ, ↑n = a * m + b)} := sorry

def modulo (a b : ℤ) (m : ℕ) : Prop :=
  ↑m ∣ a - b

local notation a `≡` b `mod` m  := modulo a b m

set_option trace.simplify.rewrite true

lemma LemCongruence (a b : ℤ) (m : ℕ) :
  (a ≡ b mod m) → ∃ k : ℤ, a = b + m * k :=
begin
  rw modulo,
  simp only [(∣)],
  intro H,
  cases H with t Ht,
  existsi t,
  rw ←Ht, -- rw (symm Ht),
  rw add_comm,
  rw sub_add_cancel,
  -- rw add_assoc,
  -- rw add_left_neg,
  -- rw add_zero,
end

def phi (n : ℕ) : ℕ → ℕ
| 0 := 0
| 1 := if n ≠ 0 then 1 else 0
| (m+2) := if nat.gcd ↑(m+2) n = 1 then phi (m+1) + 1 else phi (m+1)

def φ (n : ℕ) : ℕ := phi n (n-1)
#eval φ 8 -- 4
#eval φ 13 -- 12
#eval φ 1000 -- 400

end ex_5

-- https://github.com/hanzhi713/lean-proofs/blob/65d149e2185779c7444041f033d09b495cd86032/src/lessons/lesson8.lean
namespace ex_6

variables P Q : Prop

variable forward: P → Q
variable backward: Q → P

def pqEquiv : P ↔ Q := (iff.intro forward backward)
lemma pqEquiv' : P ↔ Q := (iff.intro forward backward)
#check pqEquiv -- ∀ (P Q : Prop), (P → Q) → (Q → P) → (P ↔ Q)
#check pqEquiv' -- ∀ (P Q : Prop), (P → Q) → (Q → P) → (P ↔ Q)
#check iff.elim_left (pqEquiv P Q forward backward) -- P → Q

end ex_6


/-
Copyright (c) 2020 Anne Baanen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro, Alexander Bentkamp, Anne Baanen
-/
import algebra.big_operators.fin
import linear_algebra.finsupp
import linear_algebra.prod
import set_theory.cardinal.basic

/-!

# Linear independence

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

This file defines linear independence in a module or vector space.

It is inspired by Isabelle/HOL's linear algebra, and hence indirectly by HOL Light.

We define `linear_independent R v` as `ker (finsupp.total ι M R v) = ⊥`. Here `finsupp.total` is the
linear map sending a function `f : ι →₀ R` with finite support to the linear combination of vectors
from `v` with these coefficients. Then we prove that several other statements are equivalent to this
one, including injectivity of `finsupp.total ι M R v` and some versions with explicitly written
linear combinations.

## Main definitions
All definitions are given for families of vectors, i.e. `v : ι → M` where `M` is the module or
vector space and `ι : Type*` is an arbitrary indexing type.

* `linear_independent R v` states that the elements of the family `v` are linearly independent.

* `linear_independent.repr hv x` returns the linear combination representing `x : span R (range v)`
on the linearly independent vectors `v`, given `hv : linear_independent R v`
(using classical choice). `linear_independent.repr hv` is provided as a linear map.

## Main statements

We prove several specialized tests for linear independence of families of vectors and of sets of
vectors.

* `fintype.linear_independent_iff`: if `ι` is a finite type, then any function `f : ι → R` has
  finite support, so we can reformulate the statement using `∑ i : ι, f i • v i` instead of a sum
  over an auxiliary `s : finset ι`;
* `linear_independent_empty_type`: a family indexed by an empty type is linearly independent;
* `linear_independent_unique_iff`: if `ι` is a singleton, then `linear_independent K v` is
  equivalent to `v default ≠ 0`;
* linear_independent_option`, `linear_independent_sum`, `linear_independent_fin_cons`,
  `linear_independent_fin_succ`: type-specific tests for linear independence of families of vector
  fields;
* `linear_independent_insert`, `linear_independent_union`, `linear_independent_pair`,
  `linear_independent_singleton`: linear independence tests for set operations.

In many cases we additionally provide dot-style operations (e.g., `linear_independent.union`) to
make the linear independence tests usable as `hv.insert ha` etc.

We also prove that, when working over a division ring,
any family of vectors includes a linear independent subfamily spanning the same subspace.

## Implementation notes

We use families instead of sets because it allows us to say that two identical vectors are linearly
dependent.

If you want to use sets, use the family `(λ x, x : s → M)` given a set `s : set M`. The lemmas
`linear_independent.to_subtype_range` and `linear_independent.of_subtype_range` connect those two
worlds.

## Tags

linearly dependent, linear dependence, linearly independent, linear independence

-/

noncomputable theory

open function set submodule
open_locale classical big_operators cardinal

universes u

variables {ι : Type*} {ι' : Type*} {R : Type*} {K : Type*}
variables {M : Type*} {M' M'' : Type*} {V : Type u} {V' : Type*}

section module

variables {v : ι → M}
variables [semiring R] [add_comm_monoid M] [add_comm_monoid M'] [add_comm_monoid M'']
variables [module R M] [module R M'] [module R M'']
variables {a b : R} {x y : M}

variables (R) (v)

/-- `linear_independent R v` states the family of vectors `v` is linearly independent over `R`. -/
def linear_independent : Prop := (finsupp.total ι M R v).ker = ⊥
variables {R} {v}

theorem linear_independent_iff : linear_independent R v ↔
  ∀l, finsupp.total ι M R v l = 0 → l = 0 :=
by simp [linear_independent, linear_map.ker_eq_bot']

theorem linear_independent_iff' : linear_independent R v ↔
  ∀ s : finset ι, ∀ g : ι → R, ∑ i in s, g i • v i = 0 → ∀ i ∈ s, g i = 0 :=
linear_independent_iff.trans
⟨λ hf s g hg i his, have h : _ := hf (∑ i in s, finsupp.single i (g i)) $
      by simpa only [linear_map.map_sum, finsupp.total_single] using hg, calc
    g i = (finsupp.lapply i : (ι →₀ R) →ₗ[R] R) (finsupp.single i (g i)) :
      by rw [finsupp.lapply_apply, finsupp.single_eq_same]
    ... = ∑ j in s, (finsupp.lapply i : (ι →₀ R) →ₗ[R] R) (finsupp.single j (g j)) :
      eq.symm $ finset.sum_eq_single i
        (λ j hjs hji, by rw [finsupp.lapply_apply, finsupp.single_eq_of_ne hji])
        (λ hnis, hnis.elim his)
    ... = (∑ j in s, finsupp.single j (g j)) i : (finsupp.lapply i : (ι →₀ R) →ₗ[R] R).map_sum.symm
    ... = 0 : finsupp.ext_iff.1 h i,
λ hf l hl, finsupp.ext $ λ i, classical.by_contradiction $ λ hni, hni $ hf _ _ hl _ $
  finsupp.mem_support_iff.2 hni⟩

theorem linear_independent_iff'' :
  linear_independent R v ↔ ∀ (s : finset ι) (g : ι → R) (hg : ∀ i ∉ s, g i = 0),
    ∑ i in s, g i • v i = 0 → ∀ i, g i = 0 :=
linear_independent_iff'.trans ⟨λ H s g hg hv i, if his : i ∈ s then H s g hv i his else hg i his,
λ H s g hg i hi, by { convert H s (λ j, if j ∈ s then g j else 0) (λ j hj, if_neg hj)
    (by simp_rw [ite_smul, zero_smul, finset.sum_extend_by_zero, hg]) i,
  exact (if_pos hi).symm }⟩

theorem not_linear_independent_iff : ¬ linear_independent R v ↔
  ∃ s : finset ι, ∃ g : ι → R, (∑ i in s, g i • v i) = 0 ∧ (∃ i ∈ s, g i ≠ 0) :=
begin
  rw linear_independent_iff',
  simp only [exists_prop, not_forall],
end

theorem fintype.linear_independent_iff [fintype ι] :
  linear_independent R v ↔ ∀ g : ι → R, ∑ i, g i • v i = 0 → ∀ i, g i = 0 :=
begin
  refine ⟨λ H g, by simpa using linear_independent_iff'.1 H finset.univ g,
    λ H, linear_independent_iff''.2 $ λ s g hg hs i, H _ _ _⟩,
  rw ← hs,
  refine (finset.sum_subset (finset.subset_univ _) (λ i _ hi, _)).symm,
  rw [hg i hi, zero_smul]
end

/-- A finite family of vectors `v i` is linear independent iff the linear map that sends
`c : ι → R` to `∑ i, c i • v i` has the trivial kernel. -/
theorem fintype.linear_independent_iff' [fintype ι] :
  linear_independent R v ↔
    (linear_map.lsum R (λ i : ι, R) ℕ (λ i, linear_map.id.smul_right (v i))).ker = ⊥ :=
by simp [fintype.linear_independent_iff, linear_map.ker_eq_bot', funext_iff]; skip

lemma fintype.not_linear_independent_iff [fintype ι] :
  ¬linear_independent R v ↔ ∃ g : ι → R, (∑ i, g i • v i) = 0 ∧ (∃ i, g i ≠ 0) :=
by simpa using (not_iff_not.2 fintype.linear_independent_iff)

lemma linear_independent_empty_type [is_empty ι] : linear_independent R v :=
linear_independent_iff.mpr $ λ v hv, subsingleton.elim v 0

lemma linear_independent.ne_zero [nontrivial R]
  (i : ι) (hv : linear_independent R v) : v i ≠ 0 :=
λ h, zero_ne_one' R $ eq.symm begin
  suffices : (finsupp.single i 1 : ι →₀ R) i = 0, {simpa},
  rw linear_independent_iff.1 hv (finsupp.single i 1),
  { simp },
  { simp [h] }
end

/-- A subfamily of a linearly independent family (i.e., a composition with an injective map) is a
linearly independent family. -/
lemma linear_independent.comp
  (h : linear_independent R v) (f : ι' → ι) (hf : injective f) : linear_independent R (v ∘ f) :=
begin
  rw [linear_independent_iff, finsupp.total_comp],
  intros l hl,
  have h_map_domain : ∀ x, (finsupp.map_domain f l) (f x) = 0,
    by rw linear_independent_iff.1 h (finsupp.map_domain f l) hl; simp,
  ext x,
  convert h_map_domain x,
  rw [finsupp.map_domain_apply hf]
end

lemma linear_independent.coe_range (i : linear_independent R v) :
  linear_independent R (coe : range v → M) :=
by simpa using i.comp _ (range_splitting_injective v)

/-- If `v` is a linearly independent family of vectors and the kernel of a linear map `f` is
disjoint with the submodule spanned by the vectors of `v`, then `f ∘ v` is a linearly independent
family of vectors. See also `linear_independent.map'` for a special case assuming `ker f = ⊥`. -/
lemma linear_independent.map (hv : linear_independent R v) {f : M →ₗ[R] M'}
  (hf_inj : disjoint (span R (range v)) f.ker) : linear_independent R (f ∘ v) :=
begin
  rw [disjoint_iff_inf_le, ← set.image_univ, finsupp.span_image_eq_map_total,
    map_inf_eq_map_inf_comap,
    map_le_iff_le_comap, comap_bot, finsupp.supported_univ, top_inf_eq] at hf_inj,
  unfold linear_independent at hv ⊢,
  rw [hv, le_bot_iff] at hf_inj,
  haveI : inhabited M := ⟨0⟩,
  rw [finsupp.total_comp, @finsupp.lmap_domain_total _ _ R _ _ _ _ _ _ _ _ _ _ f,
    linear_map.ker_comp, hf_inj],
  exact λ _, rfl,
end

/-- An injective linear map sends linearly independent families of vectors to linearly independent
families of vectors. See also `linear_independent.map` for a more general statement. -/
lemma linear_independent.map' (hv : linear_independent R v) (f : M →ₗ[R] M')
  (hf_inj : f.ker = ⊥) : linear_independent R (f ∘ v) :=
hv.map $ by simp [hf_inj]

/-- If the image of a family of vectors under a linear map is linearly independent, then so is
the original family. -/
lemma linear_independent.of_comp (f : M →ₗ[R] M') (hfv : linear_independent R (f ∘ v)) :
  linear_independent R v :=
linear_independent_iff'.2 $ λ s g hg i his,
have ∑ (i : ι) in s, g i • f (v i) = 0,
  by simp_rw [← f.map_smul, ← f.map_sum, hg, f.map_zero],
linear_independent_iff'.1 hfv s g this i his

/-- If `f` is an injective linear map, then the family `f ∘ v` is linearly independent
if and only if the family `v` is linearly independent. -/
protected lemma linear_map.linear_independent_iff (f : M →ₗ[R] M') (hf_inj : f.ker = ⊥) :
  linear_independent R (f ∘ v) ↔ linear_independent R v :=
⟨λ h, h.of_comp f, λ h, h.map $ by simp only [hf_inj, disjoint_bot_right]⟩

@[nontriviality]
lemma linear_independent_of_subsingleton [subsingleton R] : linear_independent R v :=
linear_independent_iff.2 (λ l hl, subsingleton.elim _ _)

theorem linear_independent_equiv (e : ι ≃ ι') {f : ι' → M} :
  linear_independent R (f ∘ e) ↔ linear_independent R f :=
⟨λ h, function.comp.right_id f ▸ e.self_comp_symm ▸ h.comp _ e.symm.injective,
λ h, h.comp _ e.injective⟩

theorem linear_independent_equiv' (e : ι ≃ ι') {f : ι' → M} {g : ι → M} (h : f ∘ e = g) :
  linear_independent R g ↔ linear_independent R f :=
h ▸ linear_independent_equiv e

theorem linear_independent_subtype_range {ι} {f : ι → M} (hf : injective f) :
  linear_independent R (coe : range f → M) ↔ linear_independent R f :=
iff.symm $ linear_independent_equiv' (equiv.of_injective f hf) rfl

alias linear_independent_subtype_range ↔ linear_independent.of_subtype_range _

theorem linear_independent_image {ι} {s : set ι} {f : ι → M} (hf : set.inj_on f s) :
  linear_independent R (λ x : s, f x) ↔ linear_independent R (λ x : f '' s, (x : M)) :=
linear_independent_equiv' (equiv.set.image_of_inj_on _ _ hf) rfl

lemma linear_independent_span (hs : linear_independent R v) :
  @linear_independent ι R (span R (range v))
      (λ i : ι, ⟨v i, subset_span (mem_range_self i)⟩) _ _ _ :=
linear_independent.of_comp (span R (range v)).subtype hs

/-- See `linear_independent.fin_cons` for a family of elements in a vector space. -/
lemma linear_independent.fin_cons' {m : ℕ} (x : M) (v : fin m → M)
  (hli : linear_independent R v)
  (x_ortho : (∀ (c : R) (y : submodule.span R (set.range v)), c • x + y = (0 : M) → c = 0)) :
  linear_independent R (fin.cons x v : fin m.succ → M) :=
begin
  rw fintype.linear_independent_iff at hli ⊢,
  rintros g total_eq j,
  simp_rw [fin.sum_univ_succ, fin.cons_zero, fin.cons_succ] at total_eq,
  have : g 0 = 0,
  { refine x_ortho (g 0) ⟨∑ (i : fin m), g i.succ • v i, _⟩ total_eq,
    exact sum_mem (λ i _, smul_mem _ _ (subset_span ⟨i, rfl⟩)) },
  rw [this, zero_smul, zero_add] at total_eq,
  exact fin.cases this (hli _ total_eq) j,
end

/-- A set of linearly independent vectors in a module `M` over a semiring `K` is also linearly
independent over a subring `R` of `K`.
The implementation uses minimal assumptions about the relationship between `R`, `K` and `M`.
The version where `K` is an `R`-algebra is `linear_independent.restrict_scalars_algebras`.
 -/
lemma linear_independent.restrict_scalars [semiring K] [smul_with_zero R K] [module K M]
  [is_scalar_tower R K M]
  (hinj : function.injective (λ r : R, r • (1 : K))) (li : linear_independent K v) :
  linear_independent R v :=
begin
  refine linear_independent_iff'.mpr (λ s g hg i hi, hinj (eq.trans _ (zero_smul _ _).symm)),
  refine (linear_independent_iff'.mp li : _) _ _ _ i hi,
  simp_rw [smul_assoc, one_smul],
  exact hg,
end

/-- Every finite subset of a linearly independent set is linearly independent. -/
lemma linear_independent_finset_map_embedding_subtype
  (s : set M) (li : linear_independent R (coe : s → M)) (t : finset s) :
  linear_independent R (coe : (finset.map (embedding.subtype s) t) → M) :=
begin
  let f : t.map (embedding.subtype s) → s := λ x, ⟨x.1, begin
    obtain ⟨x, h⟩ := x,
    rw [finset.mem_map] at h,
    obtain ⟨a, ha, rfl⟩ := h,
    simp only [subtype.coe_prop, embedding.coe_subtype],
  end⟩,
  convert linear_independent.comp li f _,
  rintros ⟨x, hx⟩ ⟨y, hy⟩,
  rw [finset.mem_map] at hx hy,
  obtain ⟨a, ha, rfl⟩ := hx,
  obtain ⟨b, hb, rfl⟩ := hy,
  simp only [imp_self, subtype.mk_eq_mk],
end

/--
If every finite set of linearly independent vectors has cardinality at most `n`,
then the same is true for arbitrary sets of linearly independent vectors.
-/
lemma linear_independent_bounded_of_finset_linear_independent_bounded {n : ℕ}
  (H : ∀ s : finset M, linear_independent R (λ i : s, (i : M)) → s.card ≤ n) :
  ∀ s : set M, linear_independent R (coe : s → M) → #s ≤ n :=
begin
  intros s li,
  apply cardinal.card_le_of,
  intro t,
  rw ← finset.card_map (embedding.subtype s),
  apply H,
  apply linear_independent_finset_map_embedding_subtype _ li,
end

section subtype
/-! The following lemmas use the subtype defined by a set in `M` as the index set `ι`. -/

theorem linear_independent_comp_subtype {s : set ι} :
  linear_independent R (v ∘ coe : s → M) ↔
  ∀ l ∈ (finsupp.supported R R s), (finsupp.total ι M R v) l = 0 → l = 0 :=
begin
  simp only [linear_independent_iff, (∘), finsupp.mem_supported, finsupp.total_apply,
    set.subset_def, finset.mem_coe],
  split,
  { intros h l hl₁ hl₂,
    have := h (l.subtype_domain s) ((finsupp.sum_subtype_domain_index hl₁).trans hl₂),
    exact (finsupp.subtype_domain_eq_zero_iff hl₁).1 this },
  { intros h l hl,
    refine finsupp.emb_domain_eq_zero.1 (h (l.emb_domain $ function.embedding.subtype s) _ _),
    { suffices : ∀ i hi, ¬l ⟨i, hi⟩ = 0 → i ∈ s, by simpa,
      intros, assumption },
    { rwa [finsupp.emb_domain_eq_map_domain, finsupp.sum_map_domain_index],
      exacts [λ _, zero_smul _ _, λ _ _ _, add_smul _ _ _] } }
end

lemma linear_dependent_comp_subtype' {s : set ι} :
  ¬ linear_independent R (v ∘ coe : s → M) ↔
  ∃ f : ι →₀ R, f ∈ finsupp.supported R R s ∧ finsupp.total ι M R v f = 0 ∧ f ≠ 0 :=
by simp [linear_independent_comp_subtype]

/-- A version of `linear_dependent_comp_subtype'` with `finsupp.total` unfolded. -/
lemma linear_dependent_comp_subtype {s : set ι} :
  ¬ linear_independent R (v ∘ coe : s → M) ↔
  ∃ f : ι →₀ R, f ∈ finsupp.supported R R s ∧ ∑ i in f.support, f i • v i = 0 ∧ f ≠ 0 :=
linear_dependent_comp_subtype'

theorem linear_independent_subtype {s : set M} :
  linear_independent R (λ x, x : s → M) ↔
  ∀ l ∈ (finsupp.supported R R s), (finsupp.total M M R id) l = 0 → l = 0 :=
by apply @linear_independent_comp_subtype _ _ _ id

theorem linear_independent_comp_subtype_disjoint {s : set ι} :
  linear_independent R (v ∘ coe : s → M) ↔
  disjoint (finsupp.supported R R s) (finsupp.total ι M R v).ker :=
by rw [linear_independent_comp_subtype, linear_map.disjoint_ker]

theorem linear_independent_subtype_disjoint {s : set M} :
  linear_independent R (λ x, x : s → M) ↔
  disjoint (finsupp.supported R R s) (finsupp.total M M R id).ker :=
by apply @linear_independent_comp_subtype_disjoint _ _ _ id

theorem linear_independent_iff_total_on {s : set M} :
  linear_independent R (λ x, x : s → M) ↔ (finsupp.total_on M M R id s).ker = ⊥ :=
by rw [finsupp.total_on, linear_map.ker, linear_map.comap_cod_restrict, map_bot, comap_bot,
  linear_map.ker_comp, linear_independent_subtype_disjoint, disjoint_iff_inf_le,
  ← map_comap_subtype, map_le_iff_le_comap, comap_bot, ker_subtype, le_bot_iff]

lemma linear_independent.restrict_of_comp_subtype {s : set ι}
  (hs : linear_independent R (v ∘ coe : s → M)) :
  linear_independent R (s.restrict v) :=
hs

variables (R M)
lemma linear_independent_empty : linear_independent R (λ x, x : (∅ : set M) → M) :=
by simp [linear_independent_subtype_disjoint]
variables {R M}

lemma linear_independent.mono {t s : set M} (h : t ⊆ s) :
  linear_independent R (λ x, x : s → M) → linear_independent R (λ x, x : t → M) :=
begin
 simp only [linear_independent_subtype_disjoint],
 exact (disjoint.mono_left (finsupp.supported_mono h))
end

lemma linear_independent_of_finite (s : set M)
  (H : ∀ t ⊆ s, set.finite t → linear_independent R (λ x, x : t → M)) :
  linear_independent R (λ x, x : s → M) :=
linear_independent_subtype.2 $
  λ l hl, linear_independent_subtype.1 (H _ hl (finset.finite_to_set _)) l (subset.refl _)

lemma linear_independent_Union_of_directed {η : Type*}
  {s : η → set M} (hs : directed (⊆) s)
  (h : ∀ i, linear_independent R (λ x, x : s i → M)) :
  linear_independent R (λ x, x : (⋃ i, s i) → M) :=
begin
  by_cases hη : nonempty η,
  { resetI,
    refine linear_independent_of_finite (⋃ i, s i) (λ t ht ft, _),
    rcases finite_subset_Union ft ht with ⟨I, fi, hI⟩,
    rcases hs.finset_le fi.to_finset with ⟨i, hi⟩,
    exact (h i).mono (subset.trans hI $ Union₂_subset $
      λ j hj, hi j (fi.mem_to_finset.2 hj)) },
  { refine (linear_independent_empty _ _).mono _,
    rintro _ ⟨_, ⟨i, _⟩, _⟩, exact hη ⟨i⟩ }
end

lemma linear_independent_sUnion_of_directed {s : set (set M)}
  (hs : directed_on (⊆) s)
  (h : ∀ a ∈ s, linear_independent R (λ x, x : (a : set M) → M)) :
  linear_independent R (λ x, x : (⋃₀ s) → M) :=
by rw sUnion_eq_Union; exact
linear_independent_Union_of_directed hs.directed_coe (by simpa using h)

lemma linear_independent_bUnion_of_directed {η} {s : set η} {t : η → set M}
  (hs : directed_on (t ⁻¹'o (⊆)) s) (h : ∀a∈s, linear_independent R (λ x, x : t a → M)) :
  linear_independent R (λ x, x : (⋃a∈s, t a) → M) :=
by rw bUnion_eq_Union; exact
linear_independent_Union_of_directed (directed_comp.2 $ hs.directed_coe) (by simpa using h)

end subtype

end module

/-! ### Properties which require `ring R` -/

section module
variables {v : ι → M}
variables [ring R] [add_comm_group M] [add_comm_group M'] [add_comm_group M'']
variables [module R M] [module R M'] [module R M'']
variables {a b : R} {x y : M}

theorem linear_independent_iff_injective_total : linear_independent R v ↔
  function.injective (finsupp.total ι M R v) :=
linear_independent_iff.trans
  (injective_iff_map_eq_zero (finsupp.total ι M R v).to_add_monoid_hom).symm

alias linear_independent_iff_injective_total ↔ linear_independent.injective_total _

lemma linear_independent.injective [nontrivial R] (hv : linear_independent R v) :
  injective v :=
begin
  intros i j hij,
  let l : ι →₀ R := finsupp.single i (1 : R) - finsupp.single j 1,
  have h_total : finsupp.total ι M R v l = 0,
  { simp_rw [linear_map.map_sub, finsupp.total_apply],
    simp [hij] },
  have h_single_eq : finsupp.single i (1 : R) = finsupp.single j 1,
  { rw linear_independent_iff at hv,
    simp [eq_add_of_sub_eq' (hv l h_total)] },
  simpa [finsupp.single_eq_single_iff] using h_single_eq
end

theorem linear_independent.to_subtype_range {ι} {f : ι → M} (hf : linear_independent R f) :
  linear_independent R (coe : range f → M) :=
begin
  nontriviality R,
  exact (linear_independent_subtype_range hf.injective).2 hf
end

theorem linear_independent.to_subtype_range' {ι} {f : ι → M} (hf : linear_independent R f)
  {t} (ht : range f = t) :
  linear_independent R (coe : t → M) :=
ht ▸ hf.to_subtype_range

theorem linear_independent.image_of_comp {ι ι'} (s : set ι) (f : ι → ι') (g : ι' → M)
  (hs : linear_independent R (λ x : s, g (f x))) :
  linear_independent R (λ x : f '' s, g x) :=
begin
  nontriviality R,
  have : inj_on f s, from inj_on_iff_injective.2 hs.injective.of_comp,
  exact (linear_independent_equiv' (equiv.set.image_of_inj_on f s this) rfl).1 hs
end

theorem linear_independent.image {ι} {s : set ι} {f : ι → M}
  (hs : linear_independent R (λ x : s, f x)) : linear_independent R (λ x : f '' s, (x : M)) :=
by convert linear_independent.image_of_comp s f id hs

lemma linear_independent.group_smul
  {G : Type*} [hG : group G] [distrib_mul_action G R] [distrib_mul_action G M]
  [is_scalar_tower G R M] [smul_comm_class G R M] {v : ι → M} (hv : linear_independent R v)
  (w : ι → G) : linear_independent R (w • v) :=
begin
  rw linear_independent_iff'' at hv ⊢,
  intros s g hgs hsum i,
  refine (smul_eq_zero_iff_eq (w i)).1 _,
  refine hv s (λ i, w i • g i) (λ i hi, _) _ i,
  { dsimp only,
    exact (hgs i hi).symm ▸ smul_zero _ },
  { rw [← hsum, finset.sum_congr rfl _],
    intros, erw [pi.smul_apply, smul_assoc, smul_comm] },
end

-- This lemma cannot be proved with `linear_independent.group_smul` since the action of
-- `Rˣ` on `R` is not commutative.
lemma linear_independent.units_smul {v : ι → M} (hv : linear_independent R v)
  (w : ι → Rˣ) : linear_independent R (w • v) :=
begin
  rw linear_independent_iff'' at hv ⊢,
  intros s g hgs hsum i,
  rw ← (w i).mul_left_eq_zero,
  refine hv s (λ i, g i • w i) (λ i hi, _) _ i,
  { dsimp only,
    exact (hgs i hi).symm ▸ zero_smul _ _ },
  { rw [← hsum, finset.sum_congr rfl _],
    intros,
    erw [pi.smul_apply, smul_assoc],
    refl }
end

section maximal
universes v w

/--
A linearly independent family is maximal if there is no strictly larger linearly independent family.
-/
@[nolint unused_arguments]
def linear_independent.maximal {ι : Type w} {R : Type u} [semiring R]
  {M : Type v} [add_comm_monoid M] [module R M] {v : ι → M} (i : linear_independent R v) : Prop :=
∀ (s : set M) (i' : linear_independent R (coe : s → M)) (h : range v ≤ s), range v = s

/--
An alternative characterization of a maximal linearly independent family,
quantifying over types (in the same universe as `M`) into which the indexing family injects.
-/
lemma linear_independent.maximal_iff {ι : Type w} {R : Type u} [ring R] [nontrivial R]
  {M : Type v} [add_comm_group M] [module R M] {v : ι → M} (i : linear_independent R v) :
  i.maximal ↔ ∀ (κ : Type v) (w : κ → M) (i' : linear_independent R w)
    (j : ι → κ) (h : w ∘ j = v), surjective j :=
begin
  fsplit,
  { rintros p κ w i' j rfl,
    specialize p (range w) i'.coe_range (range_comp_subset_range _ _),
    rw [range_comp, ←@image_univ _ _ w] at p,
    exact range_iff_surjective.mp (image_injective.mpr i'.injective p), },
  { intros p w i' h,
    specialize p w (coe : w → M) i'
      (λ i, ⟨v i, range_subset_iff.mp h i⟩)
      (by { ext, simp, }),
    have q := congr_arg (λ s, (coe : w → M) '' s) p.range_eq,
    dsimp at q,
    rw [←image_univ, image_image] at q,
    simpa using q, },
end

end maximal

/-- Linear independent families are injective, even if you multiply either side. -/
lemma linear_independent.eq_of_smul_apply_eq_smul_apply {M : Type*} [add_comm_group M] [module R M]
  {v : ι → M} (li : linear_independent R v) (c d : R) (i j : ι)
  (hc : c ≠ 0) (h : c • v i = d • v j) : i = j :=
begin
  let l : ι →₀ R := finsupp.single i c - finsupp.single j d,
  have h_total : finsupp.total ι M R v l = 0,
  { simp_rw [linear_map.map_sub, finsupp.total_apply],
    simp [h] },
  have h_single_eq : finsupp.single i c = finsupp.single j d,
  { rw linear_independent_iff at li,
    simp [eq_add_of_sub_eq' (li l h_total)] },
  rcases (finsupp.single_eq_single_iff _ _ _ _).mp h_single_eq with ⟨this, _⟩ | ⟨hc, _⟩,
  { exact this },
  { contradiction },
end

section subtype
/-! The following lemmas use the subtype defined by a set in `M` as the index set `ι`. -/

lemma linear_independent.disjoint_span_image (hv : linear_independent R v) {s t : set ι}
  (hs : disjoint s t) :
  disjoint (submodule.span R $ v '' s) (submodule.span R $ v '' t) :=
begin
  simp only [disjoint_def, finsupp.mem_span_image_iff_total],
  rintros _ ⟨l₁, hl₁, rfl⟩ ⟨l₂, hl₂, H⟩,
  rw [hv.injective_total.eq_iff] at H, subst l₂,
  have : l₁ = 0 :=
    submodule.disjoint_def.mp (finsupp.disjoint_supported_supported hs) _ hl₁ hl₂,
  simp [this]
end

lemma linear_independent.not_mem_span_image [nontrivial R] (hv : linear_independent R v) {s : set ι}
  {x : ι} (h : x ∉ s) :
  v x ∉ submodule.span R (v '' s) :=
begin
  have h' : v x ∈ submodule.span R (v '' {x}),
  { rw set.image_singleton,
    exact mem_span_singleton_self (v x), },
  intro w,
  apply linear_independent.ne_zero x hv,
  refine disjoint_def.1 (hv.disjoint_span_image _) (v x) h' w,
  simpa using h,
end

lemma linear_independent.total_ne_of_not_mem_support [nontrivial R] (hv : linear_independent R v)
  {x : ι} (f : ι →₀ R) (h : x ∉ f.support) :
  finsupp.total ι M R v f ≠ v x :=
begin
  replace h : x ∉ (f.support : set ι) := h,
  have p := hv.not_mem_span_image h,
  intro w,
  rw ←w at p,
  rw finsupp.span_image_eq_map_total at p,
  simp only [not_exists, not_and, mem_map] at p,
  exact p f (f.mem_supported_support R) rfl,
end

lemma linear_independent_sum {v : ι ⊕ ι' → M} :
  linear_independent R v ↔ linear_independent R (v ∘ sum.inl) ∧
    linear_independent R (v ∘ sum.inr) ∧
      disjoint (submodule.span R (range (v ∘ sum.inl))) (submodule.span R (range (v ∘ sum.inr))) :=
begin
  rw [range_comp v, range_comp v],
  refine ⟨λ h, ⟨h.comp _ sum.inl_injective, h.comp _ sum.inr_injective,
    h.disjoint_span_image is_compl_range_inl_range_inr.1⟩, _⟩,
  rintro ⟨hl, hr, hlr⟩,
  rw [linear_independent_iff'] at *,
  intros s g hg i hi,
  have : ∑ i in s.preimage sum.inl (sum.inl_injective.inj_on _), (λ x, g x • v x) (sum.inl i) +
    ∑ i in s.preimage sum.inr (sum.inr_injective.inj_on _), (λ x, g x • v x) (sum.inr i) = 0,
  { rw [finset.sum_preimage', finset.sum_preimage', ← finset.sum_union, ← finset.filter_or],
    { simpa only [← mem_union, range_inl_union_range_inr, mem_univ, finset.filter_true] },
    { exact finset.disjoint_filter.2
        (λ x _ hx, disjoint_left.1 is_compl_range_inl_range_inr.1 hx) } },
  { rw ← eq_neg_iff_add_eq_zero at this,
    rw [disjoint_def'] at hlr,
    have A := hlr _ (sum_mem $ λ i hi, _) _ (neg_mem $ sum_mem $ λ i hi, _) this,
    { cases i with i i,
      { exact hl _ _ A i (finset.mem_preimage.2 hi) },
      { rw [this, neg_eq_zero] at A,
        exact hr _ _ A i (finset.mem_preimage.2 hi) } },
    { exact smul_mem _ _ (subset_span ⟨sum.inl i, mem_range_self _, rfl⟩) },
    { exact smul_mem _ _ (subset_span ⟨sum.inr i, mem_range_self _, rfl⟩) } }
end

lemma linear_independent.sum_type {v' : ι' → M} (hv : linear_independent R v)
  (hv' : linear_independent R v')
  (h : disjoint (submodule.span R (range v)) (submodule.span R (range v'))) :
  linear_independent R (sum.elim v v') :=
linear_independent_sum.2 ⟨hv, hv', h⟩

lemma linear_independent.union {s t : set M}
  (hs : linear_independent R (λ x, x : s → M)) (ht : linear_independent R (λ x, x : t → M))
  (hst : disjoint (span R s) (span R t)) :
  linear_independent R (λ x, x : (s ∪ t) → M) :=
(hs.sum_type ht $ by simpa).to_subtype_range' $ by simp

lemma linear_independent_Union_finite_subtype {ι : Type*} {f : ι → set M}
  (hl : ∀i, linear_independent R (λ x, x : f i → M))
  (hd : ∀i, ∀t:set ι, t.finite → i ∉ t → disjoint (span R (f i)) (⨆i∈t, span R (f i))) :
  linear_independent R (λ x, x : (⋃i, f i) → M) :=
begin
  rw [Union_eq_Union_finset f],
  apply linear_independent_Union_of_directed,
  { apply directed_of_sup,
    exact (λ t₁ t₂ ht, Union_mono $ λ i, Union_subset_Union_const $ λ h, ht h) },
  assume t,
  induction t using finset.induction_on with i s his ih,
  { refine (linear_independent_empty _ _).mono _,
    simp },
  { rw [finset.set_bUnion_insert],
    refine (hl _).union ih _,
    rw span_Union₂,
    exact hd i s s.finite_to_set his }
end

lemma linear_independent_Union_finite {η : Type*} {ιs : η → Type*}
  {f : Π j : η, ιs j → M}
  (hindep : ∀j, linear_independent R (f j))
  (hd : ∀i, ∀t:set η, t.finite → i ∉ t →
      disjoint (span R (range (f i))) (⨆i∈t, span R (range (f i)))) :
  linear_independent R (λ ji : Σ j, ιs j, f ji.1 ji.2) :=
begin
  nontriviality R,
  apply linear_independent.of_subtype_range,
  { rintros ⟨x₁, x₂⟩ ⟨y₁, y₂⟩ hxy,
    by_cases h_cases : x₁ = y₁,
    subst h_cases,
    { apply sigma.eq,
      rw linear_independent.injective (hindep _) hxy,
      refl },
    { have h0 : f x₁ x₂ = 0,
      { apply disjoint_def.1 (hd x₁ {y₁} (finite_singleton y₁)
          (λ h, h_cases (eq_of_mem_singleton h))) (f x₁ x₂) (subset_span (mem_range_self _)),
        rw supr_singleton,
        simp only at hxy,
        rw hxy,
        exact (subset_span (mem_range_self y₂)) },
      exact false.elim ((hindep x₁).ne_zero _ h0) } },
  rw range_sigma_eq_Union_range,
  apply linear_independent_Union_finite_subtype (λ j, (hindep j).to_subtype_range) hd,
end

end subtype

section repr
variables (hv : linear_independent R v)

/-- Canonical isomorphism between linear combinations and the span of linearly independent vectors.
-/
@[simps {rhs_md := semireducible}]
def linear_independent.total_equiv (hv : linear_independent R v) :
  (ι →₀ R) ≃ₗ[R] span R (range v) :=
begin
  apply linear_equiv.of_bijective
    (linear_map.cod_restrict (span R (range v)) (finsupp.total ι M R v) _),
  split,
  { rw [← linear_map.ker_eq_bot, linear_map.ker_cod_restrict],
    apply hv },
  { rw [← linear_map.range_eq_top, linear_map.range_eq_map, linear_map.map_cod_restrict,
      ← linear_map.range_le_iff_comap, range_subtype, map_top],
    rw finsupp.range_total,
    exact le_rfl },
  { intro l,
    rw ← finsupp.range_total,
    rw linear_map.mem_range,
    apply mem_range_self l }
end

/-- Linear combination representing a vector in the span of linearly independent vectors.

Given a family of linearly independent vectors, we can represent any vector in their span as
a linear combination of these vectors. These are provided by this linear map.
It is simply one direction of `linear_independent.total_equiv`. -/
def linear_independent.repr (hv : linear_independent R v) :
  span R (range v) →ₗ[R] ι →₀ R := hv.total_equiv.symm

@[simp] lemma linear_independent.total_repr (x) : finsupp.total ι M R v (hv.repr x) = x :=
subtype.ext_iff.1 (linear_equiv.apply_symm_apply hv.total_equiv x)

lemma linear_independent.total_comp_repr :
  (finsupp.total ι M R v).comp hv.repr = submodule.subtype _ :=
linear_map.ext $ hv.total_repr

lemma linear_independent.repr_ker : hv.repr.ker = ⊥ :=
by rw [linear_independent.repr, linear_equiv.ker]

lemma linear_independent.repr_range : hv.repr.range = ⊤ :=
by rw [linear_independent.repr, linear_equiv.range]

lemma linear_independent.repr_eq
  {l : ι →₀ R} {x} (eq : finsupp.total ι M R v l = ↑x) :
  hv.repr x = l :=
begin
  have : ↑((linear_independent.total_equiv hv : (ι →₀ R) →ₗ[R] span R (range v)) l)
      = finsupp.total ι M R v l := rfl,
  have : (linear_independent.total_equiv hv : (ι →₀ R) →ₗ[R] span R (range v)) l = x,
  { rw eq at this,
    exact subtype.ext_iff.2 this },
  rw ←linear_equiv.symm_apply_apply hv.total_equiv l,
  rw ←this,
  refl,
end

lemma linear_independent.repr_eq_single (i) (x) (hx : ↑x = v i) :
  hv.repr x = finsupp.single i 1 :=
begin
  apply hv.repr_eq,
  simp [finsupp.total_single, hx]
end

lemma linear_independent.span_repr_eq [nontrivial R] (x) :
  span.repr R (set.range v) x = (hv.repr x).equiv_map_domain (equiv.of_injective _ hv.injective) :=
begin
  have p : (span.repr R (set.range v) x).equiv_map_domain (equiv.of_injective _ hv.injective).symm =
    hv.repr x,
  { apply (linear_independent.total_equiv hv).injective,
    ext,
    simp only [linear_independent.total_equiv_apply_coe, equiv.self_comp_of_injective_symm,
       linear_independent.total_repr, finsupp.total_equiv_map_domain, span.finsupp_total_repr], },
  ext ⟨_, ⟨i, rfl⟩⟩,
  simp [←p],
end

-- TODO: why is this so slow?
lemma linear_independent_iff_not_smul_mem_span :
  linear_independent R v ↔ (∀ (i : ι) (a : R), a • (v i) ∈ span R (v '' (univ \ {i})) → a = 0) :=
⟨ λ hv i a ha, begin
  rw [finsupp.span_image_eq_map_total, mem_map] at ha,
  rcases ha with ⟨l, hl, e⟩,
  rw sub_eq_zero.1 (linear_independent_iff.1 hv (l - finsupp.single i a) (by simp [e])) at hl,
  by_contra hn,
  exact (not_mem_of_mem_diff (hl $ by simp [hn])) (mem_singleton _),
end, λ H, linear_independent_iff.2 $ λ l hl, begin
  ext i, simp only [finsupp.zero_apply],
  by_contra hn,
  refine hn (H i _ _),
  refine (finsupp.mem_span_image_iff_total _).2 ⟨finsupp.single i (l i) - l, _, _⟩,
  { rw finsupp.mem_supported',
    intros j hj,
    have hij : j = i :=
      not_not.1
          (λ hij : j ≠ i, hj ((mem_diff _).2 ⟨mem_univ _, λ h, hij (eq_of_mem_singleton h)⟩)),
    simp [hij] },
  { simp [hl] }
end⟩

/-- See also `complete_lattice.independent_iff_linear_independent_of_ne_zero`. -/
lemma linear_independent.independent_span_singleton (hv : linear_independent R v) :
  complete_lattice.independent $ λ i, R ∙ v i :=
begin
  refine complete_lattice.independent_def.mp (λ i, _),
  rw disjoint_iff_inf_le,
  intros m hm,
  simp only [mem_inf, mem_span_singleton, supr_subtype', ← span_range_eq_supr] at hm,
  obtain ⟨⟨r, rfl⟩, hm⟩ := hm,
  suffices : r = 0, { simp [this], },
  apply linear_independent_iff_not_smul_mem_span.mp hv i,
  convert hm,
  ext,
  simp,
end

variable (R)

lemma exists_maximal_independent' (s : ι → M) :
  ∃ I : set ι, linear_independent R (λ x : I, s x) ∧
    ∀ J : set ι, I ⊆ J → linear_independent R (λ x : J, s x) → I = J :=
begin
  let indep : set ι → Prop := λ I, linear_independent R (s ∘ coe : I → M),
  let X := { I : set ι // indep I },
  let r : X → X → Prop := λ I J, I.1 ⊆ J.1,
  have key : ∀ c : set X, is_chain r c → indep (⋃ (I : X) (H : I ∈ c), I),
  { intros c hc,
    dsimp [indep],
    rw [linear_independent_comp_subtype],
    intros f hsupport hsum,
    rcases eq_empty_or_nonempty c with rfl | hn,
    { simpa using hsupport },
    haveI : is_refl X r := ⟨λ _, set.subset.refl _⟩,
    obtain ⟨I, I_mem, hI⟩ : ∃ I ∈ c, (f.support : set ι) ⊆ I :=
      hc.directed_on.exists_mem_subset_of_finset_subset_bUnion hn hsupport,
    exact linear_independent_comp_subtype.mp I.2 f hI hsum },
  have trans : transitive r := λ I J K, set.subset.trans,
  obtain ⟨⟨I, hli : indep I⟩, hmax : ∀ a, r ⟨I, hli⟩ a → r a ⟨I, hli⟩⟩ :=
    @exists_maximal_of_chains_bounded _ r
    (λ c hc, ⟨⟨⋃ I ∈ c, (I : set ι), key c hc⟩, λ I, set.subset_bUnion_of_mem⟩) trans,
  exact ⟨I, hli, λ J hsub hli, set.subset.antisymm hsub (hmax ⟨J, hli⟩ hsub)⟩,
end

lemma exists_maximal_independent (s : ι → M) : ∃ I : set ι, linear_independent R (λ x : I, s x) ∧
  ∀ i ∉ I, ∃ a : R, a ≠ 0 ∧ a • s i ∈ span R (s '' I) :=
begin
  classical,
  rcases exists_maximal_independent' R s with ⟨I, hIlinind, hImaximal⟩,
  use [I, hIlinind],
  intros i hi,
  specialize hImaximal (I ∪ {i}) (by simp),
  set J := I ∪ {i} with hJ,
  have memJ : ∀ {x}, x ∈ J ↔ x = i ∨ x ∈ I, by simp [hJ],
  have hiJ : i ∈ J := by simp,
  have h := mt hImaximal _, swap,
  { intro h2,
    rw h2 at hi,
    exact absurd hiJ hi },
  obtain ⟨f, supp_f, sum_f, f_ne⟩ := linear_dependent_comp_subtype.mp h,
  have hfi : f i ≠ 0,
  { contrapose hIlinind,
    refine linear_dependent_comp_subtype.mpr ⟨f, _, sum_f, f_ne⟩,
    simp only [finsupp.mem_supported, hJ] at ⊢ supp_f,
    rintro x hx,
    refine (memJ.mp (supp_f hx)).resolve_left _,
    rintro rfl,
    exact hIlinind (finsupp.mem_support_iff.mp hx) },
  use [f i, hfi],
  have hfi' : i ∈ f.support := finsupp.mem_support_iff.mpr hfi,
  rw [← finset.insert_erase hfi', finset.sum_insert (finset.not_mem_erase _ _),
      add_eq_zero_iff_eq_neg] at sum_f,
  rw sum_f,
  refine neg_mem (sum_mem (λ c hc, smul_mem _ _ (subset_span ⟨c, _, rfl⟩))),
  exact (memJ.mp (supp_f (finset.erase_subset _ _ hc))).resolve_left (finset.ne_of_mem_erase hc),
end
end repr

lemma surjective_of_linear_independent_of_span [nontrivial R]
  (hv : linear_independent R v) (f : ι' ↪ ι)
  (hss : range v ⊆ span R (range (v ∘ f))) :
  surjective f :=
begin
  intros i,
  let repr : (span R (range (v ∘ f)) : Type*) → ι' →₀ R := (hv.comp f f.injective).repr,
  let l := (repr ⟨v i, hss (mem_range_self i)⟩).map_domain f,
  have h_total_l : finsupp.total ι M R v l = v i,
  { dsimp only [l],
    rw finsupp.total_map_domain,
    rw (hv.comp f f.injective).total_repr,
    { refl } },
  have h_total_eq : (finsupp.total ι M R v) l = (finsupp.total ι M R v) (finsupp.single i 1),
    by rw [h_total_l, finsupp.total_single, one_smul],
  have l_eq : l = _ := linear_map.ker_eq_bot.1 hv h_total_eq,
  dsimp only [l] at l_eq,
  rw ←finsupp.emb_domain_eq_map_domain at l_eq,
  rcases finsupp.single_of_emb_domain_single (repr ⟨v i, _⟩) f i (1 : R) zero_ne_one.symm l_eq
    with ⟨i', hi'⟩,
  use i',
  exact hi'.2
end

lemma eq_of_linear_independent_of_span_subtype [nontrivial R] {s t : set M}
  (hs : linear_independent R (λ x, x : s → M)) (h : t ⊆ s) (hst : s ⊆ span R t) : s = t :=
begin
  let f : t ↪ s := ⟨λ x, ⟨x.1, h x.2⟩, λ a b hab, subtype.coe_injective (subtype.mk.inj hab)⟩,
  have h_surj : surjective f,
  { apply surjective_of_linear_independent_of_span hs f _,
    convert hst; simp [f, comp], },
  show s = t,
  { apply subset.antisymm _ h,
    intros x hx,
    rcases h_surj ⟨x, hx⟩ with ⟨y, hy⟩,
    convert y.mem,
    rw ← subtype.mk.inj hy,
    refl }
end

open linear_map

lemma linear_independent.image_subtype {s : set M} {f : M →ₗ[R] M'}
  (hs : linear_independent R (λ x, x : s → M))
  (hf_inj : disjoint (span R s) f.ker) : linear_independent R (λ x, x : f '' s → M') :=
begin
  rw [← @subtype.range_coe _ s] at hf_inj,
  refine (hs.map hf_inj).to_subtype_range' _,
  simp [set.range_comp f]
end

lemma linear_independent.inl_union_inr {s : set M} {t : set M'}
  (hs : linear_independent R (λ x, x : s → M))
  (ht : linear_independent R (λ x, x : t → M')) :
  linear_independent R (λ x, x : inl R M M' '' s ∪ inr R M M' '' t → M × M') :=
begin
  refine (hs.image_subtype _).union (ht.image_subtype _) _; [simp, simp, skip],
  simp only [span_image],
  simp [disjoint_iff, prod_inf_prod]
end

lemma linear_independent_inl_union_inr' {v : ι → M} {v' : ι' → M'}
  (hv : linear_independent R v) (hv' : linear_independent R v') :
  linear_independent R (sum.elim (inl R M M' ∘ v) (inr R M M' ∘ v')) :=
(hv.map' (inl R M M') ker_inl).sum_type (hv'.map' (inr R M M') ker_inr) $
begin
  refine is_compl_range_inl_inr.disjoint.mono _ _;
    simp only [span_le, range_coe, range_comp_subset_range],
end

/-- Dedekind's linear independence of characters -/
-- See, for example, Keith Conrad's note
--  <https://kconrad.math.uconn.edu/blurbs/galoistheory/linearchar.pdf>
theorem linear_independent_monoid_hom (G : Type*) [monoid G] (L : Type*) [comm_ring L]
  [no_zero_divisors L] :
  @linear_independent _ L (G → L) (λ f, f : (G →* L) → (G → L)) _ _ _ :=
by letI := classical.dec_eq (G →* L);
   letI : mul_action L L := distrib_mul_action.to_mul_action;
-- We prove linear independence by showing that only the trivial linear combination vanishes.
exact linear_independent_iff'.2
-- To do this, we use `finset` induction,
(λ s, finset.induction_on s (λ g hg i, false.elim) $ λ a s has ih g hg,
-- Here
-- * `a` is a new character we will insert into the `finset` of characters `s`,
-- * `ih` is the fact that only the trivial linear combination of characters in `s` is zero
-- * `hg` is the fact that `g` are the coefficients of a linear combination summing to zero
-- and it remains to prove that `g` vanishes on `insert a s`.

-- We now make the key calculation:
-- For any character `i` in the original `finset`, we have `g i • i = g i • a` as functions on the
-- monoid `G`.
have h1 : ∀ i ∈ s, (g i • i : G → L) = g i • a, from λ i his, funext $ λ x : G,
  -- We prove these expressions are equal by showing
  -- the differences of their values on each monoid element `x` is zero
  eq_of_sub_eq_zero $ ih (λ j, g j * j x - g j * a x)
    (funext $ λ y : G, calc
    -- After that, it's just a chase scene.
          (∑ i in s, ((g i * i x - g i * a x) • i : G → L)) y
        = ∑ i in s, (g i * i x - g i * a x) * i y : finset.sum_apply _ _ _
    ... = ∑ i in s, (g i * i x * i y - g i * a x * i y) : finset.sum_congr rfl
      (λ _ _, sub_mul _ _ _)
    ... = ∑ i in s, g i * i x * i y - ∑ i in s, g i * a x * i y : finset.sum_sub_distrib
    ... = (g a * a x * a y + ∑ i in s, g i * i x * i y)
          - (g a * a x * a y + ∑ i in s, g i * a x * i y) : by rw add_sub_add_left_eq_sub
    ... = ∑ i in insert a s, g i * i x * i y - ∑ i in insert a s, g i * a x * i y :
      by rw [finset.sum_insert has, finset.sum_insert has]
    ... = ∑ i in insert a s, g i * i (x * y) - ∑ i in insert a s, a x * (g i * i y) :
      congr (congr_arg has_sub.sub (finset.sum_congr rfl $ λ i _, by rw [i.map_mul, mul_assoc]))
        (finset.sum_congr rfl $ λ _ _, by rw [mul_assoc, mul_left_comm])
    ... = (∑ i in insert a s, (g i • i : G → L)) (x * y)
          - a x * (∑ i in insert a s, (g i • i : G → L)) y :
      by rw [finset.sum_apply, finset.sum_apply, finset.mul_sum]; refl
    ... = 0 - a x * 0 : by rw hg; refl
    ... = 0 : by rw [mul_zero, sub_zero])
    i
    his,
-- On the other hand, since `a` is not already in `s`, for any character `i ∈ s`
-- there is some element of the monoid on which it differs from `a`.
have h2 : ∀ i : G →* L, i ∈ s → ∃ y, i y ≠ a y, from λ i his,
  classical.by_contradiction $ λ h,
  have hia : i = a, from monoid_hom.ext $ λ y, classical.by_contradiction $ λ hy, h ⟨y, hy⟩,
  has $ hia ▸ his,
-- From these two facts we deduce that `g` actually vanishes on `s`,
have h3 : ∀ i ∈ s, g i = 0, from λ i his, let ⟨y, hy⟩ := h2 i his in
  have h : g i • i y = g i • a y, from congr_fun (h1 i his) y,
  or.resolve_right (mul_eq_zero.1 $ by rw [mul_sub, sub_eq_zero]; exact h) (sub_ne_zero_of_ne hy),
-- And so, using the fact that the linear combination over `s` and over `insert a s` both vanish,
-- we deduce that `g a = 0`.
have h4 : g a = 0, from calc
  g a = g a * 1 : (mul_one _).symm
  ... = (g a • a : G → L) 1 : by rw ← a.map_one; refl
  ... = (∑ i in insert a s, (g i • i : G → L)) 1 : begin
      rw finset.sum_eq_single a,
      { intros i his hia, rw finset.mem_insert at his,
        rw [h3 i (his.resolve_left hia), zero_smul] },
      { intros haas, exfalso, apply haas, exact finset.mem_insert_self a s }
    end
  ... = 0 : by rw hg; refl,
-- Now we're done; the last two facts together imply that `g` vanishes on every element
-- of `insert a s`.
(finset.forall_mem_insert _ _ _).2 ⟨h4, h3⟩)

lemma le_of_span_le_span [nontrivial R] {s t u: set M}
  (hl : linear_independent R (coe : u → M )) (hsu : s ⊆ u) (htu : t ⊆ u)
  (hst : span R s ≤ span R t) : s ⊆ t :=
begin
  have := eq_of_linear_independent_of_span_subtype
    (hl.mono (set.union_subset hsu htu))
    (set.subset_union_right _ _)
    (set.union_subset (set.subset.trans subset_span hst) subset_span),
  rw ← this, apply set.subset_union_left
end

lemma span_le_span_iff [nontrivial R] {s t u: set M}
  (hl : linear_independent R (coe : u → M)) (hsu : s ⊆ u) (htu : t ⊆ u) :
  span R s ≤ span R t ↔ s ⊆ t :=
⟨le_of_span_le_span hl hsu htu, span_mono⟩

end module

section nontrivial

variables [ring R] [nontrivial R] [add_comm_group M] [add_comm_group M']
variables [module R M] [no_zero_smul_divisors R M] [module R M']
variables {v : ι → M} {s t : set M} {x y z : M}

lemma linear_independent_unique_iff
  (v : ι → M) [unique ι] :
  linear_independent R v ↔ v default ≠ 0 :=
begin
  simp only [linear_independent_iff, finsupp.total_unique, smul_eq_zero],
  refine ⟨λ h hv, _, λ hv l hl, finsupp.unique_ext $ hl.resolve_right hv⟩,
  have := h (finsupp.single default 1) (or.inr hv),
  exact one_ne_zero (finsupp.single_eq_zero.1 this)
end

alias linear_independent_unique_iff ↔ _ linear_independent_unique

lemma linear_independent_singleton {x : M} (hx : x ≠ 0) :
  linear_independent R (λ x, x : ({x} : set M) → M) :=
linear_independent_unique coe hx

end nontrivial

/-!
### Properties which require `division_ring K`

These can be considered generalizations of properties of linear independence in vector spaces.
-/

section module

variables [division_ring K] [add_comm_group V] [add_comm_group V']
variables [module K V] [module K V']
variables {v : ι → V} {s t : set V} {x y z : V}

open submodule

/- TODO: some of the following proofs can generalized with a zero_ne_one predicate type class
   (instead of a data containing type class) -/

lemma mem_span_insert_exchange : x ∈ span K (insert y s) → x ∉ span K s → y ∈ span K (insert x s) :=
begin
  simp [mem_span_insert],
  rintro a z hz rfl h,
  refine ⟨a⁻¹, -a⁻¹ • z, smul_mem _ _ hz, _⟩,
  have a0 : a ≠ 0, {rintro rfl, simp * at *},
  simp [a0, smul_add, smul_smul]
end

lemma linear_independent_iff_not_mem_span :
  linear_independent K v ↔ (∀i, v i ∉ span K (v '' (univ \ {i}))) :=
begin
  apply linear_independent_iff_not_smul_mem_span.trans,
  split,
  { intros h i h_in_span,
    apply one_ne_zero (h i 1 (by simp [h_in_span])) },
  { intros h i a ha,
    by_contradiction ha',
    exact false.elim (h _ ((smul_mem_iff _ ha').1 ha)) }
end

lemma linear_independent.insert (hs : linear_independent K (λ b, b : s → V)) (hx : x ∉ span K s) :
  linear_independent K (λ b, b : insert x s → V) :=
begin
  rw ← union_singleton,
  have x0 : x ≠ 0 := mt (by rintro rfl; apply zero_mem (span K s)) hx,
  apply hs.union (linear_independent_singleton x0),
  rwa [disjoint_span_singleton' x0]
end

lemma linear_independent_option' :
  linear_independent K (λ o, option.cases_on' o x v : option ι → V) ↔
    linear_independent K v ∧ (x ∉ submodule.span K (range v)) :=
begin
  rw [← linear_independent_equiv (equiv.option_equiv_sum_punit ι).symm, linear_independent_sum,
    @range_unique _ punit, @linear_independent_unique_iff punit, disjoint_span_singleton],
  dsimp [(∘)],
  refine ⟨λ h, ⟨h.1, λ hx, h.2.1 $ h.2.2 hx⟩, λ h, ⟨h.1, _, λ hx, (h.2 hx).elim⟩⟩,
  rintro rfl,
  exact h.2 (zero_mem _)
end

lemma linear_independent.option (hv : linear_independent K v)
  (hx : x ∉ submodule.span K (range v)) :
  linear_independent K (λ o, option.cases_on' o x v : option ι → V) :=
linear_independent_option'.2 ⟨hv, hx⟩

lemma linear_independent_option {v : option ι → V} :
  linear_independent K v ↔
    linear_independent K (v ∘ coe : ι → V) ∧ v none ∉ submodule.span K (range (v ∘ coe : ι → V)) :=
by simp only [← linear_independent_option', option.cases_on'_none_coe]

theorem linear_independent_insert' {ι} {s : set ι} {a : ι} {f : ι → V} (has : a ∉ s) :
  linear_independent K (λ x : insert a s, f x) ↔
  linear_independent K (λ x : s, f x) ∧ f a ∉ submodule.span K (f '' s) :=
by { rw [← linear_independent_equiv ((equiv.option_equiv_sum_punit _).trans
  (equiv.set.insert has).symm), linear_independent_option], simp [(∘), range_comp f] }

theorem linear_independent_insert (hxs : x ∉ s) :
  linear_independent K (λ b : insert x s, (b : V)) ↔
  linear_independent K (λ b : s, (b : V)) ∧ x ∉ submodule.span K s :=
(@linear_independent_insert' _ _ _ _ _ _ _ _ id hxs).trans $ by simp

lemma linear_independent_pair {x y : V} (hx : x ≠ 0) (hy : ∀ a : K, a • x ≠ y) :
  linear_independent K (coe : ({x, y} : set V) → V) :=
pair_comm y x ▸ (linear_independent_singleton hx).insert $ mt mem_span_singleton.1
  (not_exists.2 hy)

lemma linear_independent_fin_cons {n} {v : fin n → V} :
  linear_independent K (fin.cons x v : fin (n + 1) → V) ↔
    linear_independent K v ∧ x ∉ submodule.span K (range v) :=
begin
  rw [← linear_independent_equiv (fin_succ_equiv n).symm, linear_independent_option],
  convert iff.rfl,
  { ext,
    -- TODO: why doesn't simp use `fin_succ_equiv_symm_coe` here?
    rw [comp_app, comp_app, fin_succ_equiv_symm_coe, fin.cons_succ] },
  { ext,
    rw [comp_app, comp_app, fin_succ_equiv_symm_coe, fin.cons_succ] }
end

lemma linear_independent_fin_snoc {n} {v : fin n → V} :
  linear_independent K (fin.snoc v x : fin (n + 1) → V) ↔
    linear_independent K v ∧ x ∉ submodule.span K (range v) :=
by rw [fin.snoc_eq_cons_rotate, linear_independent_equiv, linear_independent_fin_cons]

/-- See `linear_independent.fin_cons'` for an uglier version that works if you
only have a module over a semiring. -/
lemma linear_independent.fin_cons {n} {v : fin n → V} (hv : linear_independent K v)
  (hx : x ∉ submodule.span K (range v)) :
  linear_independent K (fin.cons x v : fin (n + 1) → V) :=
linear_independent_fin_cons.2 ⟨hv, hx⟩

lemma linear_independent_fin_succ {n} {v : fin (n + 1) → V} :
  linear_independent K v ↔
    linear_independent K (fin.tail v) ∧ v 0 ∉ submodule.span K (range $ fin.tail v) :=
by rw [← linear_independent_fin_cons, fin.cons_self_tail]

lemma linear_independent_fin_succ' {n} {v : fin (n + 1) → V} :
  linear_independent K v ↔
    linear_independent K (fin.init v) ∧ v (fin.last _) ∉ submodule.span K (range $ fin.init v) :=
by rw [← linear_independent_fin_snoc, fin.snoc_init_self]

lemma linear_independent_fin2 {f : fin 2 → V} :
  linear_independent K f ↔ f 1 ≠ 0 ∧ ∀ a : K, a • f 1 ≠ f 0 :=
by rw [linear_independent_fin_succ, linear_independent_unique_iff, range_unique,
  mem_span_singleton, not_exists,
  show fin.tail f default = f 1, by rw ← fin.succ_zero_eq_one; refl]

lemma exists_linear_independent_extension (hs : linear_independent K (coe : s → V)) (hst : s ⊆ t) :
  ∃b⊆t, s ⊆ b ∧ t ⊆ span K b ∧ linear_independent K (coe : b → V) :=
begin
  rcases zorn_subset_nonempty {b | b ⊆ t ∧ linear_independent K (coe : b → V)} _ _
    ⟨hst, hs⟩ with ⟨b, ⟨bt, bi⟩, sb, h⟩,
  { refine ⟨b, bt, sb, λ x xt, _, bi⟩,
    by_contra hn,
    apply hn,
    rw ← h _ ⟨insert_subset.2 ⟨xt, bt⟩, bi.insert hn⟩ (subset_insert _ _),
    exact subset_span (mem_insert _ _) },
  { refine λ c hc cc c0, ⟨⋃₀ c, ⟨_, _⟩, λ x, _⟩,
    { exact sUnion_subset (λ x xc, (hc xc).1) },
    { exact linear_independent_sUnion_of_directed cc.directed_on (λ x xc, (hc xc).2) },
    { exact subset_sUnion_of_mem } }
end

variables (K t)

lemma exists_linear_independent :
  ∃ b ⊆ t, span K b = span K t ∧ linear_independent K (coe : b → V) :=
begin
  obtain ⟨b, hb₁, -, hb₂, hb₃⟩ :=
    exists_linear_independent_extension (linear_independent_empty K V) (set.empty_subset t),
  exact ⟨b, hb₁, (span_eq_of_le _ hb₂ (submodule.span_mono hb₁)).symm, hb₃⟩,
end

variables {K t}

/-- `linear_independent.extend` adds vectors to a linear independent set `s ⊆ t` until it spans
all elements of `t`. -/
noncomputable def linear_independent.extend (hs : linear_independent K (λ x, x : s → V))
  (hst : s ⊆ t) : set V :=
classical.some (exists_linear_independent_extension hs hst)

lemma linear_independent.extend_subset (hs : linear_independent K (λ x, x : s → V))
  (hst : s ⊆ t) : hs.extend hst ⊆ t :=
let ⟨hbt, hsb, htb, hli⟩ := classical.some_spec (exists_linear_independent_extension hs hst) in hbt

lemma linear_independent.subset_extend (hs : linear_independent K (λ x, x : s → V))
  (hst : s ⊆ t) : s ⊆ hs.extend hst :=
let ⟨hbt, hsb, htb, hli⟩ := classical.some_spec (exists_linear_independent_extension hs hst) in hsb

lemma linear_independent.subset_span_extend (hs : linear_independent K (λ x, x : s → V))
  (hst : s ⊆ t) : t ⊆ span K (hs.extend hst) :=
let ⟨hbt, hsb, htb, hli⟩ := classical.some_spec (exists_linear_independent_extension hs hst) in htb

lemma linear_independent.linear_independent_extend (hs : linear_independent K (λ x, x : s → V))
  (hst : s ⊆ t) : linear_independent K (coe : hs.extend hst → V) :=
let ⟨hbt, hsb, htb, hli⟩ := classical.some_spec (exists_linear_independent_extension hs hst) in hli

variables {K V}

-- TODO(Mario): rewrite?
lemma exists_of_linear_independent_of_finite_span {t : finset V}
  (hs : linear_independent K (λ x, x : s → V)) (hst : s ⊆ (span K ↑t : submodule K V)) :
  ∃t':finset V, ↑t' ⊆ s ∪ ↑t ∧ s ⊆ ↑t' ∧ t'.card = t.card :=
have ∀t, ∀(s' : finset V), ↑s' ⊆ s → s ∩ ↑t = ∅ → s ⊆ (span K ↑(s' ∪ t) : submodule K V) →
  ∃t':finset V, ↑t' ⊆ s ∪ ↑t ∧ s ⊆ ↑t' ∧ t'.card = (s' ∪ t).card :=
assume t, finset.induction_on t
  (assume s' hs' _ hss',
    have s = ↑s',
      from eq_of_linear_independent_of_span_subtype hs hs' $
          by simpa using hss',
    ⟨s', by simp [this]⟩)
  (assume b₁ t hb₁t ih s' hs' hst hss',
    have hb₁s : b₁ ∉ s,
      from assume h,
      have b₁ ∈ s ∩ ↑(insert b₁ t), from ⟨h, finset.mem_insert_self _ _⟩,
      by rwa [hst] at this,
    have hb₁s' : b₁ ∉ s', from assume h, hb₁s $ hs' h,
    have hst : s ∩ ↑t = ∅,
      from eq_empty_of_subset_empty $ subset.trans
        (by simp [inter_subset_inter, subset.refl]) (le_of_eq hst),
    classical.by_cases
      (assume : s ⊆ (span K ↑(s' ∪ t) : submodule K V),
        let ⟨u, hust, hsu, eq⟩ := ih _ hs' hst this in
        have hb₁u : b₁ ∉ u, from assume h, (hust h).elim hb₁s hb₁t,
        ⟨insert b₁ u, by simp [insert_subset_insert hust],
          subset.trans hsu (by simp), by simp [eq, hb₁t, hb₁s', hb₁u]⟩)
      (assume : ¬ s ⊆ (span K ↑(s' ∪ t) : submodule K V),
        let ⟨b₂, hb₂s, hb₂t⟩ := not_subset.mp this in
        have hb₂t' : b₂ ∉ s' ∪ t, from assume h, hb₂t $ subset_span h,
        have s ⊆ (span K ↑(insert b₂ s' ∪ t) : submodule K V), from
          assume b₃ hb₃,
          have ↑(s' ∪ insert b₁ t) ⊆ insert b₁ (insert b₂ ↑(s' ∪ t) : set V),
            by simp [insert_eq, -singleton_union, -union_singleton, union_subset_union, subset.refl,
              subset_union_right],
          have hb₃ : b₃ ∈ span K (insert b₁ (insert b₂ ↑(s' ∪ t) : set V)),
            from span_mono this (hss' hb₃),
          have s ⊆ (span K (insert b₁ ↑(s' ∪ t)) : submodule K V),
            by simpa [insert_eq, -singleton_union, -union_singleton] using hss',
          have hb₁ : b₁ ∈ span K (insert b₂ ↑(s' ∪ t)),
            from mem_span_insert_exchange (this hb₂s) hb₂t,
          by rw [span_insert_eq_span hb₁] at hb₃; simpa using hb₃,
        let ⟨u, hust, hsu, eq⟩ := ih _ (by simp [insert_subset, hb₂s, hs']) hst this in
        ⟨u, subset.trans hust $ union_subset_union (subset.refl _) (by simp [subset_insert]),
          hsu, by simp [eq, hb₂t', hb₁t, hb₁s']⟩)),
begin
  have eq : t.filter (λx, x ∈ s) ∪ t.filter (λx, x ∉ s) = t,
  { ext1 x,
    by_cases x ∈ s; simp * },
  apply exists.elim (this (t.filter (λx, x ∉ s)) (t.filter (λx, x ∈ s))
    (by simp [set.subset_def]) (by simp [set.ext_iff] {contextual := tt}) (by rwa [eq])),
  intros u h,
  exact ⟨u, subset.trans h.1 (by simp [subset_def, and_imp, or_imp_distrib] {contextual:=tt}),
    h.2.1, by simp only [h.2.2, eq]⟩
end

lemma exists_finite_card_le_of_finite_of_linear_independent_of_span
  (ht : t.finite) (hs : linear_independent K (λ x, x : s → V)) (hst : s ⊆ span K t) :
  ∃h : s.finite, h.to_finset.card ≤ ht.to_finset.card :=
have s ⊆ (span K ↑(ht.to_finset) : submodule K V), by simp; assumption,
let ⟨u, hust, hsu, eq⟩ := exists_of_linear_independent_of_finite_span hs this in
have s.finite, from u.finite_to_set.subset hsu,
⟨this, by rw [←eq]; exact (finset.card_le_of_subset $ finset.coe_subset.mp $ by simp [hsu])⟩

end module


/-
Copyright (c) 2019 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Reid Barton, Mario Carneiro, Isabel Longbottom, Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.logic.embedding
import Mathlib.data.nat.cast
import Mathlib.data.fin
import Mathlib.PostPort

universes u l u_1 u_2

namespace Mathlib

/-!
# Combinatorial (pre-)games.

The basic theory of combinatorial games, following Conway's book `On Numbers and Games`. We
construct "pregames", define an ordering and arithmetic operations on them, then show that the
operations descend to "games", defined via the equivalence relation `p ≈ q ↔ p ≤ q ∧ q ≤ p`.

The surreal numbers will be built as a quotient of a subtype of pregames.

A pregame (`pgame` below) is axiomatised via an inductive type, whose sole constructor takes two
types (thought of as indexing the the possible moves for the players Left and Right), and a pair of
functions out of these types to `pgame` (thought of as describing the resulting game after making a
move).

Combinatorial games themselves, as a quotient of pregames, are constructed in `game.lean`.

## Conway induction

By construction, the induction principle for pregames is exactly "Conway induction". That is, to
prove some predicate `pgame → Prop` holds for all pregames, it suffices to prove that for every
pregame `g`, if the predicate holds for every game resulting from making a move, then it also holds
for `g`.

While it is often convenient to work "by induction" on pregames, in some situations this becomes
awkward, so we also define accessor functions `left_moves`, `right_moves`, `move_left` and
`move_right`. There is a relation `subsequent p q`, saying that `p` can be reached by playing some
non-empty sequence of moves starting from `q`, an instance `well_founded subsequent`, and a local
tactic `pgame_wf_tac` which is helpful for discharging proof obligations in inductive proofs relying
on this relation.

## Order properties

Pregames have both a `≤` and a `<` relation, which are related in quite a subtle way. In particular,
it is worth noting that in Lean's (perhaps unfortunate?) definition of a `preorder`, we have
`lt_iff_le_not_le : ∀ a b : α, a < b ↔ (a ≤ b ∧ ¬ b ≤ a)`, but this is _not_ satisfied by the usual
`≤` and `<` relations on pregames. (It is satisfied once we restrict to the surreal numbers.) In
particular, `<` is not transitive; there is an example below showing `0 < star ∧ star < 0`.

We do have
```
theorem not_le {x y : pgame} : ¬ x ≤ y ↔ y < x := ...
theorem not_lt {x y : pgame} : ¬ x < y ↔ y ≤ x := ...
```

The statement `0 ≤ x` means that Left has a good response to any move by Right; in particular, the
theorem `zero_le` below states
```
0 ≤ x ↔ ∀ j : x.right_moves, ∃ i : (x.move_right j).left_moves, 0 ≤ (x.move_right j).move_left i
```
On the other hand the statement `0 < x` means that Left has a good move right now; in particular the
theorem `zero_lt` below states
```
0 < x ↔ ∃ i : left_moves x, ∀ j : right_moves (x.move_left i), 0 < (x.move_left i).move_right j
```

The theorems `le_def`, `lt_def`, give a recursive characterisation of each relation, in terms of
themselves two moves later. The theorems `le_def_lt` and `lt_def_lt` give recursive
characterisations of each relation in terms of the other relation one move later.

We define an equivalence relation `equiv p q ↔ p ≤ q ∧ q ≤ p`. Later, games will be defined as the
quotient by this relation.

## Algebraic structures

We next turn to defining the operations necessary to make games into a commutative additive group.
Addition is defined for $x = \{xL | xR\}$ and $y = \{yL | yR\}$ by $x + y = \{xL + y, x + yL | xR +
y, x + yR\}$. Negation is defined by $\{xL | xR\} = \{-xR | -xL\}$.

The order structures interact in the expected way with addition, so we have
```
theorem le_iff_sub_nonneg {x y : pgame} : x ≤ y ↔ 0 ≤ y - x := sorry
theorem lt_iff_sub_pos {x y : pgame} : x < y ↔ 0 < y - x := sorry
```

We show that these operations respect the equivalence relation, and hence descend to games. At the
level of games, these operations satisfy all the laws of a commutative group. To prove the necessary
equivalence relations at the level of pregames, we introduce the notion of a `relabelling` of a
game, and show, for example, that there is a relabelling between `x + (y + z)` and `(x + y) + z`.

## Future work
* The theory of dominated and reversible positions, and unique normal form for short games.
* Analysis of basic domineering positions.
* Hex.
* Temperature.
* The development of surreal numbers, based on this development of combinatorial games, is still
  quite incomplete.

## References

The material here is all drawn from
* [Conway, *On numbers and games*][conway2001]

An interested reader may like to formalise some of the material from
* [Andreas Blass, *A game semantics for linear logic*][MR1167694]
* [André Joyal, *Remarques sur la théorie des jeux à deux personnes*][joyal1997]
-/

/-- The type of pre-games, before we have quotiented
  by extensionality. In ZFC, a combinatorial game is constructed from
  two sets of combinatorial games that have been constructed at an earlier
  stage. To do this in type theory, we say that a pre-game is built
  inductively from two families of pre-games indexed over any type
  in Type u. The resulting type `pgame.{u}` lives in `Type (u+1)`,
  reflecting that it is a proper class in ZFC. -/
inductive pgame
where
| mk : (α β : Type u) → (α → pgame) → (β → pgame) → pgame

namespace pgame

/--
Construct a pre-game from list of pre-games describing the available moves for Left and Right.
-/
-- TODO provide some API describing the interaction with

-- `left_moves`, `right_moves`, `move_left` and `move_right` below.

-- TODO define this at the level of games, as well, and perhaps also for finsets of games.

def of_lists (L : List pgame) (R : List pgame) : pgame :=
  mk (fin (list.length L)) (fin (list.length R)) (fun (i : fin (list.length L)) => list.nth_le L ↑i sorry)
    fun (j : fin (list.length R)) => list.nth_le R (subtype.val j) sorry

/-- The indexing type for allowable moves by Left. -/
def left_moves : pgame → Type u :=
  sorry

/-- The indexing type for allowable moves by Right. -/
def right_moves : pgame → Type u :=
  sorry

/-- The new game after Left makes an allowed move. -/
def move_left (g : pgame) : left_moves g → pgame :=
  sorry

/-- The new game after Right makes an allowed move. -/
def move_right (g : pgame) : right_moves g → pgame :=
  sorry

@[simp] theorem left_moves_mk {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} : left_moves (mk xl xr xL xR) = xl :=
  rfl

@[simp] theorem move_left_mk {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {i : left_moves (mk xl xr xL xR)} : move_left (mk xl xr xL xR) i = xL i :=
  rfl

@[simp] theorem right_moves_mk {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} : right_moves (mk xl xr xL xR) = xr :=
  rfl

@[simp] theorem move_right_mk {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {j : right_moves (mk xl xr xL xR)} : move_right (mk xl xr xL xR) j = xR j :=
  rfl

/-- `subsequent p q` says that `p` can be obtained by playing
  some nonempty sequence of moves from `q`. -/
inductive subsequent : pgame → pgame → Prop
where
| left : ∀ (x : pgame) (i : left_moves x), subsequent (move_left x i) x
| right : ∀ (x : pgame) (j : right_moves x), subsequent (move_right x j) x
| trans : ∀ (x y z : pgame), subsequent x y → subsequent y z → subsequent x z

theorem wf_subsequent : well_founded subsequent := sorry

protected instance has_well_founded : has_well_founded pgame :=
  has_well_founded.mk subsequent wf_subsequent

/-- A move by Left produces a subsequent game. (For use in pgame_wf_tac.) -/
theorem subsequent.left_move {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {i : xl} : subsequent (xL i) (mk xl xr xL xR) :=
  subsequent.left (mk xl xr xL xR) i

/-- A move by Right produces a subsequent game. (For use in pgame_wf_tac.) -/
theorem subsequent.right_move {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {j : xr} : subsequent (xR j) (mk xl xr xL xR) :=
  subsequent.right (mk xl xr xL xR) j

/-- A local tactic for proving well-foundedness of recursive definitions involving pregames. -/
/-- The pre-game `zero` is defined by `0 = { | }`. -/
protected instance has_zero : HasZero pgame :=
  { zero := mk pempty pempty pempty.elim pempty.elim }

@[simp] theorem zero_left_moves : left_moves 0 = pempty :=
  rfl

@[simp] theorem zero_right_moves : right_moves 0 = pempty :=
  rfl

protected instance inhabited : Inhabited pgame :=
  { default := 0 }

/-- The pre-game `one` is defined by `1 = { 0 | }`. -/
protected instance has_one : HasOne pgame :=
  { one := mk PUnit pempty (fun (_x : PUnit) => 0) pempty.elim }

@[simp] theorem one_left_moves : left_moves 1 = PUnit :=
  rfl

@[simp] theorem one_move_left : move_left 1 PUnit.unit = 0 :=
  rfl

@[simp] theorem one_right_moves : right_moves 1 = pempty :=
  rfl

/-- Define simultaneously by mutual induction the `<=` and `<`
  relation on pre-games. The ZFC definition says that `x = {xL | xR}`
  is less or equal to `y = {yL | yR}` if `∀ x₁ ∈ xL, x₁ < y`
  and `∀ y₂ ∈ yR, x < y₂`, where `x < y` is the same as `¬ y <= x`.
  This is a tricky induction because it only decreases one side at
  a time, and it also swaps the arguments in the definition of `<`.
  The solution is to define `x < y` and `x <= y` simultaneously. -/
def le_lt (x : pgame) (y : pgame) : Prop × Prop :=
  sorry

protected instance has_le : HasLessEq pgame :=
  { LessEq := fun (x y : pgame) => prod.fst (le_lt x y) }

protected instance has_lt : HasLess pgame :=
  { Less := fun (x y : pgame) => prod.snd (le_lt x y) }

/-- Definition of `x ≤ y` on pre-games built using the constructor. -/
@[simp] theorem mk_le_mk {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {yl : Type u_1} {yr : Type u_1} {yL : yl → pgame} {yR : yr → pgame} : mk xl xr xL xR ≤ mk yl yr yL yR ↔ (∀ (i : xl), xL i < mk yl yr yL yR) ∧ ∀ (j : yr), mk xl xr xL xR < yR j :=
  iff.rfl

/-- Definition of `x ≤ y` on pre-games, in terms of `<` -/
theorem le_def_lt {x : pgame} {y : pgame} : x ≤ y ↔ (∀ (i : left_moves x), move_left x i < y) ∧ ∀ (j : right_moves y), x < move_right y j := sorry

/-- Definition of `x < y` on pre-games built using the constructor. -/
@[simp] theorem mk_lt_mk {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {yl : Type u_1} {yr : Type u_1} {yL : yl → pgame} {yR : yr → pgame} : mk xl xr xL xR < mk yl yr yL yR ↔ (∃ (i : yl), mk xl xr xL xR ≤ yL i) ∨ ∃ (j : xr), xR j ≤ mk yl yr yL yR :=
  iff.rfl

/-- Definition of `x < y` on pre-games, in terms of `≤` -/
theorem lt_def_le {x : pgame} {y : pgame} : x < y ↔ (∃ (i : left_moves y), x ≤ move_left y i) ∨ ∃ (j : right_moves x), move_right x j ≤ y := sorry

/-- The definition of `x ≤ y` on pre-games, in terms of `≤` two moves later. -/
theorem le_def {x : pgame} {y : pgame} : x ≤ y ↔
  (∀ (i : left_moves x),
      (∃ (i' : left_moves y), move_left x i ≤ move_left y i') ∨
        ∃ (j : right_moves (move_left x i)), move_right (move_left x i) j ≤ y) ∧
    ∀ (j : right_moves y),
      (∃ (i : left_moves (move_right y j)), x ≤ move_left (move_right y j) i) ∨
        ∃ (j' : right_moves x), move_right x j' ≤ move_right y j := sorry

/-- The definition of `x < y` on pre-games, in terms of `<` two moves later. -/
theorem lt_def {x : pgame} {y : pgame} : x < y ↔
  (∃ (i : left_moves y),
      (∀ (i' : left_moves x), move_left x i' < move_left y i) ∧
        ∀ (j : right_moves (move_left y i)), x < move_right (move_left y i) j) ∨
    ∃ (j : right_moves x),
      (∀ (i : left_moves (move_right x j)), move_left (move_right x j) i < y) ∧
        ∀ (j' : right_moves y), move_right x j < move_right y j' := sorry

/-- The definition of `x ≤ 0` on pre-games, in terms of `≤ 0` two moves later. -/
theorem le_zero {x : pgame} : x ≤ 0 ↔ ∀ (i : left_moves x), ∃ (j : right_moves (move_left x i)), move_right (move_left x i) j ≤ 0 := sorry

/-- The definition of `0 ≤ x` on pre-games, in terms of `0 ≤` two moves later. -/
theorem zero_le {x : pgame} : 0 ≤ x ↔ ∀ (j : right_moves x), ∃ (i : left_moves (move_right x j)), 0 ≤ move_left (move_right x j) i := sorry

/-- The definition of `x < 0` on pre-games, in terms of `< 0` two moves later. -/
theorem lt_zero {x : pgame} : x < 0 ↔ ∃ (j : right_moves x), ∀ (i : left_moves (move_right x j)), move_left (move_right x j) i < 0 := sorry

/-- The definition of `0 < x` on pre-games, in terms of `< x` two moves later. -/
theorem zero_lt {x : pgame} : 0 < x ↔ ∃ (i : left_moves x), ∀ (j : right_moves (move_left x i)), 0 < move_right (move_left x i) j := sorry

/-- Given a right-player-wins game, provide a response to any move by left. -/
def right_response {x : pgame} (h : x ≤ 0) (i : left_moves x) : right_moves (move_left x i) :=
  classical.some sorry

/-- Show that the response for right provided by `right_response`
    preserves the right-player-wins condition. -/
theorem right_response_spec {x : pgame} (h : x ≤ 0) (i : left_moves x) : move_right (move_left x i) (right_response h i) ≤ 0 :=
  classical.some_spec (iff.mp le_zero h i)

/-- Given a left-player-wins game, provide a response to any move by right. -/
def left_response {x : pgame} (h : 0 ≤ x) (j : right_moves x) : left_moves (move_right x j) :=
  classical.some sorry

/-- Show that the response for left provided by `left_response`
    preserves the left-player-wins condition. -/
theorem left_response_spec {x : pgame} (h : 0 ≤ x) (j : right_moves x) : 0 ≤ move_left (move_right x j) (left_response h j) :=
  classical.some_spec (iff.mp zero_le h j)

theorem lt_of_le_mk {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {y : pgame} {i : xl} : mk xl xr xL xR ≤ y → xL i < y :=
  pgame.cases_on y
    fun (y_α y_β : Type u_1) (y_ᾰ : y_α → pgame) (y_ᾰ_1 : y_β → pgame) (h : mk xl xr xL xR ≤ mk y_α y_β y_ᾰ y_ᾰ_1) =>
      and.left h i

theorem lt_of_mk_le {x : pgame} {yl : Type u_1} {yr : Type u_1} {yL : yl → pgame} {yR : yr → pgame} {i : yr} : x ≤ mk yl yr yL yR → x < yR i :=
  pgame.cases_on x
    fun (x_α x_β : Type u_1) (x_ᾰ : x_α → pgame) (x_ᾰ_1 : x_β → pgame) (h : mk x_α x_β x_ᾰ x_ᾰ_1 ≤ mk yl yr yL yR) =>
      and.right h i

theorem mk_lt_of_le {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {y : pgame} {i : xr} : xR i ≤ y → mk xl xr xL xR < y :=
  pgame.cases_on y
    fun (y_α y_β : Type u_1) (y_ᾰ : y_α → pgame) (y_ᾰ_1 : y_β → pgame) (h : xR i ≤ mk y_α y_β y_ᾰ y_ᾰ_1) =>
      Or.inr (Exists.intro i h)

theorem lt_mk_of_le {x : pgame} {yl : Type u_1} {yr : Type u_1} {yL : yl → pgame} {yR : yr → pgame} {i : yl} : x ≤ yL i → x < mk yl yr yL yR :=
  pgame.cases_on x
    fun (x_α x_β : Type u_1) (x_ᾰ : x_α → pgame) (x_ᾰ_1 : x_β → pgame) (h : mk x_α x_β x_ᾰ x_ᾰ_1 ≤ yL i) =>
      Or.inl (Exists.intro i h)

theorem not_le_lt {x : pgame} {y : pgame} : (¬x ≤ y ↔ y < x) ∧ (¬x < y ↔ y ≤ x) := sorry

theorem not_le {x : pgame} {y : pgame} : ¬x ≤ y ↔ y < x :=
  and.left not_le_lt

theorem not_lt {x : pgame} {y : pgame} : ¬x < y ↔ y ≤ x :=
  and.right not_le_lt

theorem le_refl (x : pgame) : x ≤ x := sorry

theorem lt_irrefl (x : pgame) : ¬x < x :=
  iff.mpr not_lt (le_refl x)

theorem ne_of_lt {x : pgame} {y : pgame} : x < y → x ≠ y := sorry

theorem le_trans_aux {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {yl : Type u_1} {yr : Type u_1} {yL : yl → pgame} {yR : yr → pgame} {zl : Type u_1} {zr : Type u_1} {zL : zl → pgame} {zR : zr → pgame} (h₁ : ∀ (i : xl), mk yl yr yL yR ≤ mk zl zr zL zR → mk zl zr zL zR ≤ xL i → mk yl yr yL yR ≤ xL i) (h₂ : ∀ (i : zr), zR i ≤ mk xl xr xL xR → mk xl xr xL xR ≤ mk yl yr yL yR → zR i ≤ mk yl yr yL yR) : mk xl xr xL xR ≤ mk yl yr yL yR → mk yl yr yL yR ≤ mk zl zr zL zR → mk xl xr xL xR ≤ mk zl zr zL zR := sorry

theorem le_trans {x : pgame} {y : pgame} {z : pgame} : x ≤ y → y ≤ z → x ≤ z := sorry

theorem lt_of_le_of_lt {x : pgame} {y : pgame} {z : pgame} (hxy : x ≤ y) (hyz : y < z) : x < z :=
  eq.mpr (id (Eq._oldrec (Eq.refl (x < z)) (Eq.symm (propext not_le))))
    (mt (fun (H : z ≤ x) => le_trans H hxy) (eq.mp (Eq._oldrec (Eq.refl (y < z)) (Eq.symm (propext not_le))) hyz))

theorem lt_of_lt_of_le {x : pgame} {y : pgame} {z : pgame} (hxy : x < y) (hyz : y ≤ z) : x < z :=
  eq.mpr (id (Eq._oldrec (Eq.refl (x < z)) (Eq.symm (propext not_le))))
    (mt (fun (H : z ≤ x) => le_trans hyz H) (eq.mp (Eq._oldrec (Eq.refl (x < y)) (Eq.symm (propext not_le))) hxy))

/-- Define the equivalence relation on pre-games. Two pre-games
  `x`, `y` are equivalent if `x ≤ y` and `y ≤ x`. -/
def equiv (x : pgame) (y : pgame) :=
  x ≤ y ∧ y ≤ x

theorem equiv_refl (x : pgame) : equiv x x :=
  { left := le_refl x, right := le_refl x }

theorem equiv_symm {x : pgame} {y : pgame} : equiv x y → equiv y x :=
  fun (ᾰ : equiv x y) =>
    and.dcases_on ᾰ fun (ᾰ_left : x ≤ y) (ᾰ_right : y ≤ x) => idRhs (y ≤ x ∧ x ≤ y) { left := ᾰ_right, right := ᾰ_left }

theorem equiv_trans {x : pgame} {y : pgame} {z : pgame} : equiv x y → equiv y z → equiv x z := sorry

theorem lt_of_lt_of_equiv {x : pgame} {y : pgame} {z : pgame} (h₁ : x < y) (h₂ : equiv y z) : x < z :=
  lt_of_lt_of_le h₁ (and.left h₂)

theorem le_of_le_of_equiv {x : pgame} {y : pgame} {z : pgame} (h₁ : x ≤ y) (h₂ : equiv y z) : x ≤ z :=
  le_trans h₁ (and.left h₂)

theorem lt_of_equiv_of_lt {x : pgame} {y : pgame} {z : pgame} (h₁ : equiv x y) (h₂ : y < z) : x < z :=
  lt_of_le_of_lt (and.left h₁) h₂

theorem le_of_equiv_of_le {x : pgame} {y : pgame} {z : pgame} (h₁ : equiv x y) (h₂ : y ≤ z) : x ≤ z :=
  le_trans (and.left h₁) h₂

theorem le_congr {x₁ : pgame} {y₁ : pgame} {x₂ : pgame} {y₂ : pgame} : equiv x₁ x₂ → equiv y₁ y₂ → (x₁ ≤ y₁ ↔ x₂ ≤ y₂) := sorry

theorem lt_congr {x₁ : pgame} {y₁ : pgame} {x₂ : pgame} {y₂ : pgame} (hx : equiv x₁ x₂) (hy : equiv y₁ y₂) : x₁ < y₁ ↔ x₂ < y₂ :=
  iff.trans (iff.symm not_le) (iff.trans (not_congr (le_congr hy hx)) not_le)

theorem equiv_congr_left {y₁ : pgame} {y₂ : pgame} : equiv y₁ y₂ ↔ ∀ (x₁ : pgame), equiv x₁ y₁ ↔ equiv x₁ y₂ := sorry

theorem equiv_congr_right {x₁ : pgame} {x₂ : pgame} : equiv x₁ x₂ ↔ ∀ (y₁ : pgame), equiv x₁ y₁ ↔ equiv x₂ y₁ := sorry

/-- `restricted x y` says that Left always has no more moves in `x` than in `y`,
     and Right always has no more moves in `y` than in `x` -/
inductive restricted : pgame → pgame → Type (u + 1)
where
| mk : {x y : pgame} →
  (L : left_moves x → left_moves y) →
    (R : right_moves y → right_moves x) →
      ((i : left_moves x) → restricted (move_left x i) (move_left y (L i))) →
        ((j : right_moves y) → restricted (move_right x (R j)) (move_right y j)) → restricted x y

/-- The identity restriction. -/
def restricted.refl (x : pgame) : restricted x x :=
  sorry

-- TODO trans for restricted

theorem le_of_restricted {x : pgame} {y : pgame} (r : restricted x y) : x ≤ y := sorry

/--
`relabelling x y` says that `x` and `y` are really the same game, just dressed up differently.
Specifically, there is a bijection between the moves for Left in `x` and in `y`, and similarly
for Right, and under these bijections we inductively have `relabelling`s for the consequent games.
-/
inductive relabelling : pgame → pgame → Type (u + 1)
where
| mk : {x y : pgame} →
  (L : left_moves x ≃ left_moves y) →
    (R : right_moves x ≃ right_moves y) →
      ((i : left_moves x) → relabelling (move_left x i) (move_left y (coe_fn L i))) →
        ((j : right_moves y) → relabelling (move_right x (coe_fn (equiv.symm R) j)) (move_right y j)) → relabelling x y

/-- If `x` is a relabelling of `y`, then Left and Right have the same moves in either game,
    so `x` is a restriction of `y`. -/
def restricted_of_relabelling {x : pgame} {y : pgame} (r : relabelling x y) : restricted x y :=
  sorry

-- It's not the case that `restricted x y → restricted y x → relabelling x y`,

-- but if we insisted that the maps in a restriction were injective, then one

-- could use Schröder-Bernstein for do this.

/-- The identity relabelling. -/
def relabelling.refl (x : pgame) : relabelling x x :=
  sorry

/-- Reverse a relabelling. -/
def relabelling.symm {x : pgame} {y : pgame} : relabelling x y → relabelling y x :=
  sorry

/-- Transitivity of relabelling -/
def relabelling.trans {x : pgame} {y : pgame} {z : pgame} : relabelling x y → relabelling y z → relabelling x z :=
  sorry

theorem le_of_relabelling {x : pgame} {y : pgame} (r : relabelling x y) : x ≤ y :=
  le_of_restricted (restricted_of_relabelling r)

/-- A relabelling lets us prove equivalence of games. -/
theorem equiv_of_relabelling {x : pgame} {y : pgame} (r : relabelling x y) : equiv x y :=
  { left := le_of_relabelling r, right := le_of_relabelling (relabelling.symm r) }

protected instance equiv.has_coe {x : pgame} {y : pgame} : has_coe (relabelling x y) (equiv x y) :=
  has_coe.mk equiv_of_relabelling

/-- Replace the types indexing the next moves for Left and Right by equivalent types. -/
def relabel {x : pgame} {xl' : Type u_1} {xr' : Type u_1} (el : left_moves x ≃ xl') (er : right_moves x ≃ xr') : pgame :=
  mk xl' xr' (fun (i : xl') => move_left x (coe_fn (equiv.symm el) i))
    fun (j : xr') => move_right x (coe_fn (equiv.symm er) j)

@[simp] theorem relabel_move_left' {x : pgame} {xl' : Type u_1} {xr' : Type u_1} (el : left_moves x ≃ xl') (er : right_moves x ≃ xr') (i : xl') : move_left (relabel el er) i = move_left x (coe_fn (equiv.symm el) i) :=
  rfl

@[simp] theorem relabel_move_left {x : pgame} {xl' : Type u_1} {xr' : Type u_1} (el : left_moves x ≃ xl') (er : right_moves x ≃ xr') (i : left_moves x) : move_left (relabel el er) (coe_fn el i) = move_left x i := sorry

@[simp] theorem relabel_move_right' {x : pgame} {xl' : Type u_1} {xr' : Type u_1} (el : left_moves x ≃ xl') (er : right_moves x ≃ xr') (j : xr') : move_right (relabel el er) j = move_right x (coe_fn (equiv.symm er) j) :=
  rfl

@[simp] theorem relabel_move_right {x : pgame} {xl' : Type u_1} {xr' : Type u_1} (el : left_moves x ≃ xl') (er : right_moves x ≃ xr') (j : right_moves x) : move_right (relabel el er) (coe_fn er j) = move_right x j := sorry

/-- The game obtained by relabelling the next moves is a relabelling of the original game. -/
def relabel_relabelling {x : pgame} {xl' : Type u_1} {xr' : Type u_1} (el : left_moves x ≃ xl') (er : right_moves x ≃ xr') : relabelling x (relabel el er) :=
  relabelling.mk el er (fun (i : left_moves x) => eq.mpr sorry (relabelling.refl (move_left x i)))
    fun (j : right_moves (relabel el er)) => eq.mpr sorry (relabelling.refl (move_right x (coe_fn (equiv.symm er) j)))

/-- The negation of `{L | R}` is `{-R | -L}`. -/
def neg : pgame → pgame :=
  sorry

protected instance has_neg : Neg pgame :=
  { neg := neg }

@[simp] theorem neg_def {xl : Type u_1} {xr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} : -mk xl xr xL xR = mk xr xl (fun (j : xr) => -xR j) fun (i : xl) => -xL i :=
  rfl

@[simp] theorem neg_neg {x : pgame} : --x = x := sorry

@[simp] theorem neg_zero : -0 = 0 := sorry

/-- An explicit equivalence between the moves for Left in `-x` and the moves for Right in `x`. -/
-- This equivalence is useful to avoid having to use `cases` unnecessarily.

def left_moves_neg (x : pgame) : left_moves (-x) ≃ right_moves x :=
  pgame.cases_on x
    fun (x_α x_β : Type u_1) (x_ᾰ : x_α → pgame) (x_ᾰ_1 : x_β → pgame) => equiv.refl (left_moves (-mk x_α x_β x_ᾰ x_ᾰ_1))

/-- An explicit equivalence between the moves for Right in `-x` and the moves for Left in `x`. -/
def right_moves_neg (x : pgame) : right_moves (-x) ≃ left_moves x :=
  pgame.cases_on x
    fun (x_α x_β : Type u_1) (x_ᾰ : x_α → pgame) (x_ᾰ_1 : x_β → pgame) => equiv.refl (right_moves (-mk x_α x_β x_ᾰ x_ᾰ_1))

@[simp] theorem move_right_left_moves_neg {x : pgame} (i : left_moves (-x)) : move_right x (coe_fn (left_moves_neg x) i) = -move_left (-x) i := sorry

@[simp] theorem move_left_left_moves_neg_symm {x : pgame} (i : right_moves x) : move_left (-x) (coe_fn (equiv.symm (left_moves_neg x)) i) = -move_right x i := sorry

@[simp] theorem move_left_right_moves_neg {x : pgame} (i : right_moves (-x)) : move_left x (coe_fn (right_moves_neg x) i) = -move_right (-x) i := sorry

@[simp] theorem move_right_right_moves_neg_symm {x : pgame} (i : left_moves x) : move_right (-x) (coe_fn (equiv.symm (right_moves_neg x)) i) = -move_left x i := sorry

theorem le_iff_neg_ge {x : pgame} {y : pgame} : x ≤ y ↔ -y ≤ -x := sorry

theorem neg_congr {x : pgame} {y : pgame} (h : equiv x y) : equiv (-x) (-y) :=
  { left := iff.mp le_iff_neg_ge (and.right h), right := iff.mp le_iff_neg_ge (and.left h) }

theorem lt_iff_neg_gt {x : pgame} {y : pgame} : x < y ↔ -y < -x :=
  eq.mpr (id (Eq._oldrec (Eq.refl (x < y ↔ -y < -x)) (Eq.symm (propext not_le))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (¬y ≤ x ↔ -y < -x)) (Eq.symm (propext not_le))))
      (eq.mpr (id (Eq._oldrec (Eq.refl (¬y ≤ x ↔ ¬-x ≤ -y)) (propext not_iff_not))) le_iff_neg_ge))

theorem zero_le_iff_neg_le_zero {x : pgame} : 0 ≤ x ↔ -x ≤ 0 := sorry

theorem le_zero_iff_zero_le_neg {x : pgame} : x ≤ 0 ↔ 0 ≤ -x := sorry

/-- The sum of `x = {xL | xR}` and `y = {yL | yR}` is `{xL + y, x + yL | xR + y, x + yR}`. -/
def add (x : pgame) (y : pgame) : pgame :=
  pgame.rec
    (fun (xl xr : Type u_1) (xL : xl → pgame) (xR : xr → pgame)
      (IHxl : (ᾰ : xl) → (fun (x : pgame) => pgame → pgame) (xL ᾰ))
      (IHxr : (ᾰ : xr) → (fun (x : pgame) => pgame → pgame) (xR ᾰ)) (y : pgame) =>
      pgame.rec
        (fun (yl yr : Type u_2) (yL : yl → pgame) (yR : yr → pgame) (IHyl : (ᾰ : yl) → (fun (y : pgame) => pgame) (yL ᾰ))
          (IHyr : (ᾰ : yr) → (fun (y : pgame) => pgame) (yR ᾰ)) =>
          mk (xl ⊕ yl) (xr ⊕ yr) (sum.rec (fun (i : xl) => IHxl i (mk yl yr yL yR)) fun (i : yl) => IHyl i)
            (sum.rec (fun (i : xr) => IHxr i (mk yl yr yL yR)) fun (i : yr) => IHyr i))
        y)
    x y

protected instance has_add : Add pgame :=
  { add := add }

/-- `x + 0` has exactly the same moves as `x`. -/
def add_zero_relabelling (x : pgame) : relabelling (x + 0) x :=
  sorry

/-- `x + 0` is equivalent to `x`. -/
theorem add_zero_equiv (x : pgame) : equiv (x + 0) x :=
  equiv_of_relabelling (add_zero_relabelling x)

/-- `0 + x` has exactly the same moves as `x`. -/
def zero_add_relabelling (x : pgame) : relabelling (0 + x) x :=
  sorry

/-- `0 + x` is equivalent to `x`. -/
theorem zero_add_equiv (x : pgame) : equiv (0 + x) x :=
  equiv_of_relabelling (zero_add_relabelling x)

/-- An explicit equivalence between the moves for Left in `x + y` and the type-theory sum
    of the moves for Left in `x` and in `y`. -/
def left_moves_add (x : pgame) (y : pgame) : left_moves (x + y) ≃ left_moves x ⊕ left_moves y :=
  pgame.cases_on x
    fun (x_α x_β : Type u_1) (x_ᾰ : x_α → pgame) (x_ᾰ_1 : x_β → pgame) =>
      pgame.cases_on y
        fun (y_α y_β : Type u_1) (y_ᾰ : y_α → pgame) (y_ᾰ_1 : y_β → pgame) =>
          equiv.refl (left_moves (mk x_α x_β x_ᾰ x_ᾰ_1 + mk y_α y_β y_ᾰ y_ᾰ_1))

/-- An explicit equivalence between the moves for Right in `x + y` and the type-theory sum
    of the moves for Right in `x` and in `y`. -/
def right_moves_add (x : pgame) (y : pgame) : right_moves (x + y) ≃ right_moves x ⊕ right_moves y :=
  pgame.cases_on x
    fun (x_α x_β : Type u_1) (x_ᾰ : x_α → pgame) (x_ᾰ_1 : x_β → pgame) =>
      pgame.cases_on y
        fun (y_α y_β : Type u_1) (y_ᾰ : y_α → pgame) (y_ᾰ_1 : y_β → pgame) =>
          equiv.refl (right_moves (mk x_α x_β x_ᾰ x_ᾰ_1 + mk y_α y_β y_ᾰ y_ᾰ_1))

@[simp] theorem mk_add_move_left_inl {xl : Type u_1} {xr : Type u_1} {yl : Type u_1} {yr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {yL : yl → pgame} {yR : yr → pgame} {i : xl} : move_left (mk xl xr xL xR + mk yl yr yL yR) (sum.inl i) = move_left (mk xl xr xL xR) i + mk yl yr yL yR :=
  rfl

@[simp] theorem add_move_left_inl {x : pgame} {y : pgame} {i : left_moves x} : move_left (x + y) (coe_fn (equiv.symm (left_moves_add x y)) (sum.inl i)) = move_left x i + y := sorry

@[simp] theorem mk_add_move_right_inl {xl : Type u_1} {xr : Type u_1} {yl : Type u_1} {yr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {yL : yl → pgame} {yR : yr → pgame} {i : xr} : move_right (mk xl xr xL xR + mk yl yr yL yR) (sum.inl i) = move_right (mk xl xr xL xR) i + mk yl yr yL yR :=
  rfl

@[simp] theorem add_move_right_inl {x : pgame} {y : pgame} {i : right_moves x} : move_right (x + y) (coe_fn (equiv.symm (right_moves_add x y)) (sum.inl i)) = move_right x i + y := sorry

@[simp] theorem mk_add_move_left_inr {xl : Type u_1} {xr : Type u_1} {yl : Type u_1} {yr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {yL : yl → pgame} {yR : yr → pgame} {i : yl} : move_left (mk xl xr xL xR + mk yl yr yL yR) (sum.inr i) = mk xl xr xL xR + move_left (mk yl yr yL yR) i :=
  rfl

@[simp] theorem add_move_left_inr {x : pgame} {y : pgame} {i : left_moves y} : move_left (x + y) (coe_fn (equiv.symm (left_moves_add x y)) (sum.inr i)) = x + move_left y i := sorry

@[simp] theorem mk_add_move_right_inr {xl : Type u_1} {xr : Type u_1} {yl : Type u_1} {yr : Type u_1} {xL : xl → pgame} {xR : xr → pgame} {yL : yl → pgame} {yR : yr → pgame} {i : yr} : move_right (mk xl xr xL xR + mk yl yr yL yR) (sum.inr i) = mk xl xr xL xR + move_right (mk yl yr yL yR) i :=
  rfl

@[simp] theorem add_move_right_inr {x : pgame} {y : pgame} {i : right_moves y} : move_right (x + y) (coe_fn (equiv.symm (right_moves_add x y)) (sum.inr i)) = x + move_right y i := sorry

protected instance has_sub : Sub pgame :=
  { sub := fun (x y : pgame) => x + -y }

/-- `-(x+y)` has exactly the same moves as `-x + -y`. -/
def neg_add_relabelling (x : pgame) (y : pgame) : relabelling (-(x + y)) (-x + -y) :=
  sorry

theorem neg_add_le {x : pgame} {y : pgame} : -(x + y) ≤ -x + -y :=
  le_of_relabelling (neg_add_relabelling x y)

/-- `x+y` has exactly the same moves as `y+x`. -/
def add_comm_relabelling (x : pgame) (y : pgame) : relabelling (x + y) (y + x) :=
  sorry

theorem add_comm_le {x : pgame} {y : pgame} : x + y ≤ y + x :=
  le_of_relabelling (add_comm_relabelling x y)

theorem add_comm_equiv {x : pgame} {y : pgame} : equiv (x + y) (y + x) :=
  equiv_of_relabelling (add_comm_relabelling x y)

/-- `(x + y) + z` has exactly the same moves as `x + (y + z)`. -/
def add_assoc_relabelling (x : pgame) (y : pgame) (z : pgame) : relabelling (x + y + z) (x + (y + z)) :=
  sorry

theorem add_assoc_equiv {x : pgame} {y : pgame} {z : pgame} : equiv (x + y + z) (x + (y + z)) :=
  equiv_of_relabelling (add_assoc_relabelling x y z)

theorem add_le_add_right {x : pgame} {y : pgame} {z : pgame} (h : x ≤ y) : x + z ≤ y + z := sorry

theorem add_le_add_left {x : pgame} {y : pgame} {z : pgame} (h : y ≤ z) : x + y ≤ x + z :=
  le_trans (le_trans add_comm_le (add_le_add_right h)) add_comm_le

theorem add_congr {w : pgame} {x : pgame} {y : pgame} {z : pgame} (h₁ : equiv w x) (h₂ : equiv y z) : equiv (w + y) (x + z) :=
  { left := le_trans (add_le_add_left (and.left h₂)) (add_le_add_right (and.left h₁)),
    right := le_trans (add_le_add_left (and.right h₂)) (add_le_add_right (and.right h₁)) }

theorem add_left_neg_le_zero {x : pgame} : -x + x ≤ 0 := sorry

theorem zero_le_add_left_neg {x : pgame} : 0 ≤ -x + x :=
  eq.mpr (id (Eq._oldrec (Eq.refl (0 ≤ -x + x)) (propext le_iff_neg_ge)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (-(-x + x) ≤ -0)) neg_zero)) (le_trans neg_add_le add_left_neg_le_zero))

theorem add_left_neg_equiv {x : pgame} : equiv (-x + x) 0 :=
  { left := add_left_neg_le_zero, right := zero_le_add_left_neg }

theorem add_right_neg_le_zero {x : pgame} : x + -x ≤ 0 :=
  le_trans add_comm_le add_left_neg_le_zero

theorem zero_le_add_right_neg {x : pgame} : 0 ≤ x + -x :=
  le_trans zero_le_add_left_neg add_comm_le

theorem add_lt_add_right {x : pgame} {y : pgame} {z : pgame} (h : x < y) : x + z < y + z := sorry

theorem add_lt_add_left {x : pgame} {y : pgame} {z : pgame} (h : y < z) : x + y < x + z :=
  lt_of_lt_of_le (lt_of_le_of_lt add_comm_le (add_lt_add_right h)) add_comm_le

theorem le_iff_sub_nonneg {x : pgame} {y : pgame} : x ≤ y ↔ 0 ≤ y - x := sorry

theorem lt_iff_sub_pos {x : pgame} {y : pgame} : x < y ↔ 0 < y - x := sorry

/-- The pre-game `star`, which is fuzzy/confused with zero. -/
def star : pgame :=
  of_lists [0] [0]

theorem star_lt_zero : star < 0 := sorry

theorem zero_lt_star : 0 < star := sorry

/-- The pre-game `ω`. (In fact all ordinals have game and surreal representatives.) -/
def omega : pgame :=
  mk (ulift ℕ) pempty (fun (n : ulift ℕ) => ↑(ulift.down n)) pempty.elim