/- A matroid is defined as a rank function, so this file is the biggest part of the
   matroid API.
-/

import matroid.axioms  matroid.dual
import prelim.collections prelim.minmax
open set

universes u

open_locale classical
open_locale big_operators
noncomputable theory
----------------------------------------------------------------
namespace matroid
variables {α : Type*} [fintype α] {M : matroid α} {e f x y z : α}
{X Y Z X' Y' Z' F B I C C₁ C₂ F₁ F₂ P  L₁ L₂ : set α}

-- probably split up these set notations by section...

section /- rank -/ rank

def rank (M : matroid α) := M.r univ

def r_nat (M : matroid α) (X : set α) := (M.r X).to_nat

def rank_nat (M : matroid α) := M.r_nat univ

@[simp] lemma rank_eq (M : matroid α) : M.rank = M.r univ := rfl

lemma r_nat_eq : M.r_nat X = (M.r X).to_nat := rfl

lemma rank_nat_eq : M.rank_nat = (M.r univ).to_nat := rfl

/-- rank is nonnegative -/
lemma rank_nonneg (M : matroid α) (X : set α) :
  0 ≤ M.r X :=
M.R0 X

@[simp] lemma coe_r_nat (M : matroid α) (X : set α) :
  (M.r_nat X : ℤ) = M.r X :=
int.to_nat_of_nonneg (M.rank_nonneg X)

@[simp] lemma coe_rank_nat (M : matroid α) :
  (M.rank_nat : ℤ) = M.r univ :=
int.to_nat_of_nonneg (M.rank_nonneg _)

/-- rank is bounded above by size -/
lemma rank_le_size (M : matroid α) (X : set α) :
  M.r X ≤ size X :=
M.R1 X

/-- rank is monotone wrt set inclusion -/
lemma rank_mono (M : matroid α) :
  X ⊆ Y → M.r X ≤ M.r Y :=
M.R2 X Y

/-- rank is submodular -/
lemma rank_submod (M : matroid α) (X Y : set α) :
  M.r (X ∪ Y) + M.r (X ∩ Y) ≤ M.r X + M.r Y :=
M.R3 X Y

lemma rank_mono_inter_left (M : matroid α) (X Y : set α) :
  M.r (X ∩ Y) ≤ M.r X :=
M.rank_mono (inter_subset_left X Y)

lemma rank_mono_union_left (M : matroid α) (X Y : set α) :
  M.r X ≤ M.r (X ∪ Y) :=
M.rank_mono (subset_union_left X Y)

lemma rank_mono_inter_right (M : matroid α) (X Y : set α) :
  M.r (X ∩ Y) ≤ M.r Y :=
M.rank_mono (inter_subset_right X Y)

lemma rank_mono_union_right (M : matroid α) (X Y : set α) :
  M.r Y ≤ M.r (X ∪ Y) :=
M.rank_mono (subset_union_right X Y)

lemma rank_mono_diff (M : matroid α) (X Y : set α) :
  M.r (X \ Y) ≤ M.r X :=
by {rw diff_eq, apply rank_mono_inter_left}

lemma rank_eq_zero_of_le_zero :
  M.r X ≤ 0 → M.r X = 0 :=
λ h, le_antisymm h (M.rank_nonneg X)

lemma rank_zero_of_subset_rank_zero :
  X ⊆ Y → M.r Y = 0 → M.r X = 0 :=
λ hXY hY, by {apply rank_eq_zero_of_le_zero, rw ←hY, exact rank_mono M hXY}

lemma rank_zero_of_inter_rank_zero (X : set α) :
  M.r Y = 0 → M.r (X ∩ Y) = 0 :=
λ hY, by {apply rank_zero_of_subset_rank_zero _ hY, simp }

@[simp] lemma rank_empty (M : matroid α) :
  M.r ∅ = 0 :=
rank_eq_zero_of_le_zero (by {convert M.rank_le_size _, rw size_empty })

lemma rank_lt_size_ne_empty :
  M.r X < size X → X ≠ ∅ :=
λ h hX, by {rw [hX, size_empty, rank_empty] at h, from lt_irrefl _ h,  }

lemma nonempty_of_r_nonzero (M : matroid α) :
  M.r X ≠ 0 → X.nonempty :=
by {contrapose!, intro h, rw not_nonempty_iff_eq_empty at h, rw [h, rank_empty],  }

lemma rank_single_ub (M : matroid α) (e : α) :
  M.r {e} ≤ 1 :=
by {rw ←(size_singleton e), exact M.rank_le_size {e}}

lemma rank_le_univ (M : matroid α) (X : set α) :
  M.r X ≤ M.r univ :=
M.rank_mono (subset_univ X)

lemma rank_compl_univ (M : matroid α) :
  M.r (univᶜ) = 0 :=
by rw [compl_univ, rank_empty]

lemma rank_gt_zero_of_ne :
  M.r X ≠ 0 → 0 < M.r X :=
λ h, lt_of_le_of_ne (M.rank_nonneg X) (ne.symm h)

lemma rank_eq_of_le_supset :
  X ⊆ Y → (M.r Y ≤ M.r X) → M.r X = M.r Y :=
λ h h', (le_antisymm (M.rank_mono h) h')

lemma rank_eq_of_le_union :
  M.r (X ∪ Y) ≤ M.r X → M.r (X ∪ Y) = M.r X :=
λ h, ((rank_eq_of_le_supset ((subset_union_left _ _))) h).symm

lemma rank_eq_of_le_inter :
  M.r X ≤ M.r (X ∩ Y) →  M.r (X ∩ Y) = M.r X :=
λ h, (rank_eq_of_le_supset (inter_subset_left _ _) h)

lemma rank_eq_of_not_lt_supset :
  X ⊆ Y → ¬(M.r X < M.r Y) → M.r X = M.r Y :=
λ h h', rank_eq_of_le_supset h (int.le_of_not_gt' h')

lemma rank_eq_of_not_lt_union :
  ¬ (M.r X < M.r (X ∪ Y)) → M.r (X ∪ Y) = M.r X :=
λ h', rank_eq_of_le_union (int.le_of_not_gt' h')

@[simp] lemma rank_eq_rank_union_rank_zero (X : set α){Y :set α} (hY : M.r Y = 0) :
  M.r (X ∪ Y) = M.r X :=
by {apply rank_eq_of_le_union, linarith [M.rank_nonneg (X ∩ Y ), M.rank_submod X Y],}

lemma rank_eq_rank_diff_rank_zero (X : set α) (hY : M.r Y = 0) :
  M.r (X \ Y) = M.r X :=
begin
  refine le_antisymm (rank_mono_diff _ _ _) _,
  rw ←rank_eq_rank_union_rank_zero (X \ Y) hY,
   exact rank_mono _ (λ x hx, by {rw [mem_union, mem_diff ], tauto,}),
end

lemma rank_zero_of_union_rank_zero :
  M.r X = 0 → M.r Y = 0 → M.r (X ∪ Y) = 0 :=
λ hX hY, by {rw (rank_eq_rank_union_rank_zero _ hY), exact hX }

lemma rank_eq_of_union_eq_rank_subset (Z: set α) :
  X ⊆ Y → M.r X = M.r Y → M.r (X ∪ Z) = M.r (Y ∪ Z) :=
begin
  intros hXY hr, apply rank_eq_of_le_supset (union_subset_union_left Z hXY),
  have : M.r ((X ∪ Z) ∩ Y) = _ := by rw [inter_distrib_right, subset_iff_inter_eq_left.mp hXY] ,
  have : M.r ((X ∪ Z) ∪ Y) = _ := by rw [union_assoc, union_comm Z Y, ←union_assoc,
                                      subset_iff_union_eq_left.mp hXY ],
  linarith [M.rank_submod (X ∪ Z) Y , M.rank_mono_union_left X (Z ∩ Y) ],
end

lemma rank_eq_of_union_eq_rank_subsets (hX : X ⊆ X') (hY : Y ⊆ Y')
(hXX' : M.r X = M.r X') (hYY' : M.r Y = M.r Y') :
  M.r (X ∪ Y) = M.r (X' ∪ Y') :=
by rw [rank_eq_of_union_eq_rank_subset Y hX hXX', union_comm, union_comm _ Y',
       rank_eq_of_union_eq_rank_subset _ hY hYY']

lemma rank_eq_of_inter_union (X Y A : set α) :
  M.r (X ∩ A) = M.r X → M.r ((X ∩ A) ∪ Y) = M.r (X ∪ Y) :=
λ h, rank_eq_of_union_eq_rank_subset _ (inter_subset_left _ _) h

lemma rank_eq_of_union_rank_diff_eq (Z : set α) (hX : M.r (X \ Y) = M.r X) :
  M.r (Z ∪ (X \ Y)) = M.r (Z ∪ X) :=
by {rw diff_eq at *, rw [union_comm _ X, ← rank_eq_of_inter_union _ Z _ hX, union_comm Z]}

lemma rank_subadditive (M : matroid α) (X Y : set α) :
  M.r (X ∪ Y) ≤ M.r X + M.r Y :=
by linarith [M.rank_submod X Y, M.rank_nonneg (X ∩ Y)]

lemma rank_subadditive_sUnion (M : matroid α) (S : set (set α)) :
  M.r (⋃₀ S) ≤ ∑ᶠ X in S, M.r X :=
begin
  set P := λ (S : set (set α)), M.r (⋃₀ S) ≤ ∑ᶠ X in S, M.r X with hP,
  refine induction_set_size_insert P (by {rw hP, simp}) (λ X A hA hX, _) _,
  rw [hP, sUnion_insert, fin.finsum_in_insert _ hA],
  exact le_trans (rank_subadditive M _ _) (int.add_le_add_left hX _),
end

lemma rank_augment_single_ub (M : matroid α) (X : set α) (e : α) :
  M.r (X ∪ {e}) ≤ M.r X + 1 :=
by linarith [rank_subadditive M X {e}, rank_single_ub M e]

lemma rank_eq_add_one_of_ne_aug :
  M.r (X ∪ {e}) ≠ M.r X → M.r (X ∪ {e}) = M.r X + 1 :=
begin
  intro h, apply le_antisymm,
  from (rank_augment_single_ub M X e),
  from (int.add_one_le_of_lt (lt_of_le_of_ne (rank_mono_union_left M _ _) (ne.symm h))),
end

lemma rank_eq_of_le_aug :
  M.r (X ∪ {e}) ≤ M.r X → M.r (X ∪ {e}) = M.r X :=
λ h, le_antisymm h (rank_mono_union_left _ _ _)

lemma rank_diff_subadditive (M : matroid α) (X Y : set α) :
  M.r Y ≤ M.r X + M.r (Y \ X) :=
le_trans (M.rank_mono (by simp)) (rank_subadditive M X (Y \ X))

lemma rank_remove_single_lb (M : matroid α) (X : set α) (e : α) :
  M.r X - 1 ≤ M.r (X \ {e}) :=
by linarith [rank_diff_subadditive M {e} X, rank_single_ub M e]

lemma rank_eq_sub_one_of_ne_remove (M : matroid α) (X : set α) (e : α) :
  M.r X ≠ M.r (X \ {e}) → M.r (X \ {e}) = M.r X - 1 :=
begin
  intro h, apply le_antisymm  _ (rank_remove_single_lb M X e),
  apply int.le_sub_one_of_le_of_ne _ (ne.symm h),
  apply rank_mono_diff,
end

lemma rank_diff_le_size_diff (M : matroid α) (hXY : X ⊆ Y) :
  M.r Y - M.r X ≤ size Y - size X :=
by linarith [rank_diff_subadditive M X Y, diff_size hXY, M.rank_le_size (Y \ X )]

lemma submod_three_sets (M : matroid α) (X Y Y' : set α) :
  M.r (X ∪ (Y ∪ Y')) + M.r (X ∪ (Y ∩ Y')) ≤ M.r (X ∪ Y) + M.r (X ∪ Y') :=
by {have := M.rank_submod (X ∪ Y) (X ∪ Y'), rw [←union_distrib_left, ←union_distrib_union_right] at this, exact this}

lemma submod_three_sets_right (M : matroid α) (X Y Y' : set α) :
  M.r ((Y ∪ Y') ∪ X) + M.r ((Y ∩ Y') ∪ X) ≤ M.r (Y ∪ X) + M.r (Y' ∪ X) :=
by {simp_rw ←(union_comm X), apply submod_three_sets}

lemma submod_three_sets_disj (M : matroid α) (X Y Y' : set α) (hYY' : Y ∩ Y' = ∅) :
  M.r (X ∪ (Y ∪ Y')) + M.r (X) ≤ M.r (X ∪ Y) + M.r (X ∪ Y') :=
by {have := submod_three_sets M X Y Y', rw [hYY', union_empty] at this, exact this}

/-lemma union_rank_diff_le_rank_diff (M : matroid α) (X Y Z : set α) (hXY : X ⊆ Y) :
  M.r (Y ∪ Z) - M.r (X ∪ Z) ≤ M.r Y - M.r X :=
begin
  have := rank_submod M Y Z,
  have := rank_submod M X Z,
end -/

theorem rank_augment  {X Z : set α} : (M.r X < M.r Z) →
  ∃ (z : α), z ∈ Z ∧ M.r X < M.r (X ∪ {z}) :=
let P : set α → Prop := λ X',
  (M.r X' = M.r X) ∧ (X' ⊆ X ∪ Z) ∧ (∀ (e:α), e ∈ X ∪ Z → M.r (X' ∪ {e}) = M.r X') in
begin
  intro hXZ,

  by_contra h_con, push_neg at h_con,
  replace h_con : ∀ (z:α), z ∈ X ∪ Z → M.r (X ∪ {z}) = M.r X :=
  by {  intros z hz, rw mem_union_iff at hz, cases hz,
        rw union_mem_singleton hz,
        from (rank_eq_of_le_supset (subset_union_left _ _) (h_con z hz)).symm
        },

  rcases maximal_example_aug P ⟨rfl, ⟨subset_union_left _ _, h_con⟩⟩
    with ⟨Y, ⟨hXY,⟨⟨hYX, ⟨hYXZ, h_aug⟩⟩ , hYmax⟩⟩⟩,
  by_cases Y = X ∪ Z,
  rw h at hYX,
  linarith [M.rank_mono_union_right X Z],
  cases mem_diff_ssubset (ssubset_of_subset_ne hYXZ h) with e he,
  rw mem_diff_iff at he,
  have h_aug_e := h_aug e he.1,
  have hYe := hYmax e he.2, push_neg at hYe,
  rcases hYe (eq.trans h_aug_e hYX) (union_subset hYXZ (singleton_subset_iff.mpr he.1))
    with ⟨f, ⟨hf, h_aug_ef⟩⟩,
  replace h_aug_ef := rank_eq_add_one_of_ne_aug h_aug_ef,
  rw union_assoc at h_aug_ef,
  have h_aug_f := h_aug f hf,

  have hef : ({e} ∩ {f} : set α) = ∅ := inter_distinct_singles
    (λ h, by {rw [h, union_self] at h_aug_ef, linarith}),

  linarith [submod_three_sets_disj M Y {e} {f} hef],
end

lemma rank_eq_of_rank_all_insert_eq (hXY : X ⊆ Y) :
  (∀ e : Y, M.r X = M.r (X ∪ {e})) → M.r X = M.r Y :=
begin
  refine (λ h, rank_eq_of_le_supset hXY (by_contra (λ hn, _))),
  obtain ⟨f,hfY,hf⟩ := rank_augment (not_le.mp hn),
  specialize h ⟨f, hfY⟩, rw [subtype.coe_mk] at h, linarith,
end

lemma rank_eq_of_rank_all_insert_le (hXY : X ⊆ Y) :
  (∀ e : Y, M.r (X ∪ {e}) ≤ M.r X) → M.r X = M.r Y :=
begin
  refine (λ h, rank_eq_of_le_supset hXY (by_contra (λ hn, _))),
  obtain ⟨f,hfY,hf⟩ := rank_augment (not_le.mp hn),
  specialize h ⟨f, hfY⟩, rw [subtype.coe_mk] at h, linarith,
end

lemma loopy_rank_zero  (he : (∀ (e:α), e ∈ X → M.r {e} = 0)) :
  M.r X = 0 :=
begin
  by_contra h,
  replace h := rank_gt_zero_of_ne h,
  rcases rank_augment (by linarith [rank_empty M] : M.r ∅ < M.r X) with ⟨f,hf, hf'⟩,
  rw [empty_union, rank_empty, he _ hf] at hf',
  apply lt_irrefl _ hf',
end

end rank

-- Independence

section indep

/-- is independent in M; rank equals size -/
def is_indep (M : matroid α) : set α → Prop :=
  λ X, M.r X = size X

/-- independent set type -/
def indep (M : matroid α) := {I : set α // M.is_indep I}

instance coe_indep : has_coe (M.indep) (set α) :=
  coe_subtype

instance fintype_indep : fintype (M.indep) :=
by {unfold indep, apply_instance }

def is_indep_subset_of (M : matroid α) (X : set α) : set α → Prop :=
  λ I, I ⊆ X ∧ M.is_indep I

def indep_subset_of (M : matroid α) (X : set α) := {I : set α // M.is_indep_subset_of X I}

/-- is dependent in M; negation of independence -/
def is_dep (M : matroid α) : set α → Prop :=
   λ X, ¬(M.is_indep X)

lemma indep_iff_r :
  M.is_indep X ↔ M.r X = size X :=
by refl

lemma indep_iff_size_le_r :
  M.is_indep X ↔ size X ≤ M.r X :=
by {rw [indep_iff_r], exact ⟨λ h, by rw h, λ h, le_antisymm (M.rank_le_size X) h⟩}

lemma r_indep :
  M.is_indep X → M.r X = size X :=
indep_iff_r.mp

lemma dep_iff_r :
  is_dep M X ↔ M.r X < size X :=
by {unfold is_dep, rw indep_iff_r, exact ⟨λ h, (ne.le_iff_lt h).mp (M.rank_le_size X), λ h, by linarith⟩}

--instance coe_coindep : has_coe (coindep M) α := ⟨λ I, I.val⟩

lemma indep_or_dep (M : matroid α) (X : set α) :
  M.is_indep X ∨ M.is_dep X :=
by {rw [dep_iff_r, indep_iff_r], exact eq_or_lt_of_le (M.rank_le_size X)}

lemma dep_iff_not_indep :
  M.is_dep X ↔ ¬M.is_indep X :=
by {rw [indep_iff_r, dep_iff_r], exact ⟨λ h, by linarith, λ h, (ne.le_iff_lt h).mp (M.rank_le_size X)⟩}

lemma not_indep_iff_r :
  ¬is_indep M X ↔ M.r X < size X :=
by {rw ←dep_iff_not_indep, apply dep_iff_r, }

lemma indep_iff_not_dep :
  M.is_indep X ↔ ¬M.is_dep X :=
by {rw dep_iff_not_indep, simp}

lemma coindep_iff_r  :
  (dual M).is_indep X ↔ (M.r Xᶜ = M.r univ) :=
by {unfold is_indep dual, dsimp only, split; {intros h, linarith}}

lemma codep_iff_r  :
  is_dep (dual M) X ↔ (M.r Xᶜ < M.r univ) :=
by {rw [dep_iff_not_indep, coindep_iff_r], exact ⟨λ h, (ne.le_iff_lt h).mp (rank_le_univ M Xᶜ), λ h, by linarith⟩}

lemma not_coindep_iff_r :
  ¬is_indep (dual M) X ↔ (M.r Xᶜ < M.r univ) :=
by rw [←dep_iff_not_indep, codep_iff_r]

lemma empty_indep (M : matroid α) :
  M.is_indep ∅ :=
by rw [indep_iff_r, size_empty, rank_empty]

lemma dep_nonempty   (hdep : is_dep M X ) :
  X ≠ ∅ :=
λ h, let h' := empty_indep M in by {rw ←h at h', exact hdep h'}

lemma subset_indep :
  X ⊆ Y → M.is_indep Y → M.is_indep X :=
begin
  intro hXY, simp_rw indep_iff_r, intro hY,
  linarith [M.rank_le_size X, M.rank_le_size (Y \ X ), diff_size hXY, rank_diff_subadditive M X Y]
end

lemma indep_aug :
  size X < size Y → M.is_indep X → M.is_indep Y → (∃ (e:α), e ∈ Y ∧ e ∉ X ∧ M.is_indep (X ∪ {e})) :=
begin
  simp_rw indep_iff_r,
  intros hXY hIX hIY,
  rcases rank_augment (by linarith : M.r X < M.r Y) with ⟨e,⟨h₁, h₂⟩⟩,
  have hx : ¬({e} ⊆ X),
  { exact (λ he, by {rw [union_comm, subset_iff_union_eq_left.mp he] at h₂, linarith})},
  rw singleton_subset_iff at hx,
  refine ⟨e,⟨h₁,hx,_⟩⟩,
  have hs := (size_modular X {e}),
  rw [ eq.trans (inter_comm X {e}) (nonmem_disjoint hx), size_empty] at hs,
  linarith [size_singleton e, M.rank_le_size (X ∪ {e}), int.add_one_le_iff.mpr h₂],
end

lemma indep_aug_diff :
  size X < size Y → M.is_indep X → M.is_indep Y  → (∃ (e:α), e ∈ Y \ X  ∧ M.is_indep (X ∪ {e})) :=
λ h₁ h₂ h₃, by {simp_rw mem_diff_iff, simp_rw and_assoc, exact indep_aug h₁ h₂ h₃}

lemma indep_of_indep_aug :
  M.is_indep I → M.r I < M.r (I ∪ {e}) → M.is_indep (I ∪ {e}) :=
begin
  intros hI h,
  rw indep_iff_r at *,
  apply le_antisymm (M.rank_le_size _ ),
  refine le_trans (size_union_singleton_ub) _,
  rw hI at h, convert h,
end

lemma dep_subset :
  X ⊆ Y → is_dep M X → is_dep M Y :=
by {intro hXY, repeat {rw dep_iff_not_indep}, contrapose!, exact subset_indep hXY}

lemma empty_indep_r (M : matroid α) :
   M.r ∅ = size (∅ : set α) :=
(empty_indep M)

lemma subset_indep_r :
  X ⊆ Y → M.r Y = size Y → M.r X = size X :=
λ h, by {have := subset_indep h, rw [indep_iff_r, indep_iff_r] at this, assumption}

lemma mem_indep_r (he : e ∈ I) (hI : M.is_indep I) :
  M.r {e} = 1 :=
begin
  rw [←singleton_subset_iff] at he,
  rw [←size_singleton e, ←indep_iff_r],
  exact subset_indep_r he hI,
end

instance nonempty_indep : nonempty (M.indep) :=
by {apply nonempty_subtype.mpr, from ⟨∅, M.empty_indep⟩}

lemma indep_of_subset_indep :
  X ⊆ Y → M.is_indep Y → M.is_indep X :=
begin
  intro hXY, simp_rw indep_iff_r, intro hY,
  linarith [M.rank_le_size X, M.rank_le_size (Y \ X ),
  diff_size hXY, rank_diff_subadditive M X Y]
end

lemma inter_indep_of_indep_right (X Y : set α) :
  M.is_indep Y → M.is_indep (X ∩ Y) :=
λ h, indep_of_subset_indep (inter_subset_right _ _) h

lemma inter_indep_of_indep_left (X Y : set α) :
  M.is_indep X → M.is_indep (X ∩ Y) :=
λ h, indep_of_subset_indep (inter_subset_left _ _) h

lemma indep_of_union_indep_right :
  M.is_indep (X ∪ Y) → M.is_indep Y :=
λ h, indep_of_subset_indep (subset_union_right _ _) h

lemma indep_of_union_indep_left :
  M.is_indep (X ∪ Y) → M.is_indep X :=
λ h, indep_of_subset_indep (subset_union_left _ _) h

lemma I3 :
  size X < size Y → M.is_indep X → M.is_indep Y → (∃ (e:α), e ∈ Y \ X ∧ M.is_indep (X ∪ {e})) :=
  indep_aug_diff

lemma indep_inter_rank_zero (hI : M.is_indep I) (hX : M.r X = 0) :
   I ∩ X = ∅ :=
begin
  have h := inter_indep_of_indep_left I X hI,
  rwa [indep_iff_r,rank_zero_of_inter_rank_zero I hX, eq_comm, size_zero_iff_empty] at h,
end

/-- converts a matroid to an independence family -/
def to_indep_family (M : matroid α) : indep_family α :=
  ⟨M.is_indep, empty_indep M, @indep_of_subset_indep _ _ M, @I3 _ _ M⟩

instance nonempty_indep_subset_of (M : matroid α) (X : set α) : nonempty (indep_subset_of M X) :=
by {apply nonempty_subtype.mpr, exact ⟨∅,⟨empty_subset _, M.empty_indep⟩ ⟩, }

instance fintype_indep_subset_of (M : matroid α) (X : set α) : fintype (indep_subset_of M X) :=
by {unfold indep_subset_of, apply_instance, }

end indep

section /-Circuits-/ circuit

/-- is a circuit of M : minimally dependent -/
def is_circuit (M : matroid α) : set α → Prop :=
  λ X, (¬is_indep M X ∧  ∀ Y: set α, Y ⊂ X → M.is_indep Y)

/-- circuit type -/
def circuit (M : matroid α) := { C : set α // M.is_circuit C }

instance coe_circuit : has_coe (M.circuit) (set α) :=
  coe_subtype

instance fintype_circuit : fintype (M.circuit) :=
by {unfold circuit, apply_instance }

/-- is a cocircuit of M: circuit of the dual -/
def is_cocircuit (M : matroid α) : set α → Prop :=
  is_circuit (dual M)

/-- cocircuit type -/
def cocircuit (M : matroid α) := { C : set α // M.is_cocircuit C }

instance coe_cocircuit : has_coe (cocircuit M) (set α) :=
  coe_subtype
instance fintype_cocircuit : fintype (cocircuit M) :=
by {unfold cocircuit, apply_instance}

lemma circuit_iff_i :
  M.is_circuit X ↔ ¬is_indep M X ∧  ∀ Y: set α, Y ⊂ X → M.is_indep Y :=
by rw is_circuit

lemma circuit_iff_r  (X : set α) :
  M.is_circuit X ↔ (M.r X = size X - 1) ∧ (∀ Y: set α, Y ⊂ X → M.r Y = size Y) :=
begin
  unfold is_circuit,
  rw not_indep_iff_r,
  simp_rw indep_iff_r,
  split,
  { rintros ⟨hr, hmin⟩,
    split,
    { obtain ⟨Y, ⟨hY₁, hY₂⟩⟩ := has_sub_one_size_ssubset_of_ne_empty (rank_lt_size_ne_empty hr),
      specialize hmin Y hY₁,
      linarith [M.rank_mono hY₁.1]},
    exact λ Y hY, hmin _ hY},
  rintros ⟨h₁, h₂⟩,
  refine ⟨by linarith, λ Y hY, _ ⟩,
  from h₂ _ hY,
end

lemma r_cct  :
  M.is_circuit C → M.r C = size C - 1 :=
λ hC, ((circuit_iff_r C).mp hC).1

lemma r_cct_ssub  {C Y : set α} :
  M.is_circuit C → (Y ⊂ C) → M.r Y = size Y :=
λ hC hYC, (((circuit_iff_r C).mp hC).2 Y hYC)

lemma cocircuit_iff_r  (X : set α) :
  M.is_cocircuit X ↔ (M.r Xᶜ = M.r univ - 1) ∧ (∀ Y: set α, Y ⊂ X → M.r Yᶜ = M.r univ) :=
begin
  simp_rw [is_cocircuit, is_circuit, not_coindep_iff_r, coindep_iff_r],
  split, rintros ⟨h₁, h₂⟩, split,
  have h_nonempty : X ≠ ∅ := by {intros h, rw [h,compl_empty] at h₁, exact int.lt_irrefl _ h₁},
  rcases (has_sub_one_size_ssubset_of_ne_empty h_nonempty) with ⟨Y,⟨hY₁, hY₂⟩⟩ ,
  specialize h₂ _ hY₁,
  rw [←compl_compl Y, ←compl_compl X, compl_size, compl_size Xᶜ] at hY₂,
  linarith[M.rank_diff_le_size_diff (compl_subset_compl.mpr hY₁.1)],
  exact h₂, rintros ⟨h₁, h₂⟩, exact ⟨by linarith, h₂⟩,
end

lemma dep_iff_contains_circuit  :
  is_dep M X ↔ ∃ C, M.is_circuit C ∧ C ⊆ X :=
begin
  refine ⟨λ h, _, λ h, _ ⟩,
  rcases (minimal_example _ h) with ⟨Z,⟨h₁Z,h₂Z, h₃Z⟩⟩,
  refine ⟨Z, ⟨⟨h₂Z, (λ Y hY, _)⟩, h₁Z⟩⟩,
  rw indep_iff_not_dep, exact h₃Z Y hY,
  cases h with C hC, exact dep_subset hC.2 hC.1.1,
end

/-- circuits nonempty unless matroid is free -/
instance nonempty_circuit (hM : M.r univ < size univ) : nonempty (M.circuit) :=
begin
  apply nonempty_subtype.mpr,
  rw [←dep_iff_r, dep_iff_contains_circuit] at hM,
  cases hM with C hC,
  from ⟨C,hC.1⟩,
end

/-- cocircuits nonempty unless matroid is loopy -/
instance nonempty_cocircuit (hM : 0 < M.r univ) : nonempty (cocircuit M) :=
begin
  refine matroid.nonempty_circuit (_ : (dual M).r univ < size univ),
  rw [dual_r, compl_univ, M.rank_empty], linarith,
end

lemma circuit_dep :
  M.is_circuit C → M.is_dep C :=
λ h, dep_iff_contains_circuit.mpr ⟨C,h,subset_refl _⟩

lemma indep_iff_contains_no_circuit :
  M.is_indep X ↔ ¬∃ C, M.is_circuit C ∧ C ⊆ X :=
by rw [←not_iff_not, ←dep_iff_not_indep, dep_iff_contains_circuit, not_not]

lemma empty_not_cct (M : matroid α) :
  ¬M.is_circuit ∅ :=
by {rw circuit_iff_r, intros h, have := h.1, linarith [rank_empty M, size_empty α]}

lemma nested_circuits_equal (M : matroid α) :
  C₁ ⊆ C₂ → M.is_circuit C₁ → M.is_circuit C₂ → C₁ = C₂ :=
begin
  intros hC₁C₂ hC₁ hC₂,
  rw circuit_iff_r at hC₁ hC₂,
  by_contra a,
  linarith [hC₂.2 _ (ssubset_of_subset_ne hC₁C₂ a)],
end

lemma circuit_not_ssubset_circuit :
  M.is_circuit C₁ → M.is_circuit C₂ → ¬(C₁ ⊂ C₂) :=
  λ hC₁ hC₂ hC₁C₂, ne_of_ssubset hC₁C₂ (nested_circuits_equal M hC₁C₂.1 hC₁ hC₂)

lemma not_circuit_of_ssubset_circuit {X C : set α} (hXC : X ⊂ C) (hC : M.is_circuit C) :
  ¬M.is_circuit X :=
by_contra (λ hX, by {rw not_not at hX, exact circuit_not_ssubset_circuit hX hC hXC, })

lemma inter_circuits_ssubset :
  M.is_circuit C₁ → M.is_circuit C₂ → C₁ ≠ C₂ → C₁ ∩ C₂ ⊂ C₁ :=
begin
  intros hC₁ hC₂ hC₁C₂,
  refine ssubset_of_subset_ne (inter_subset_left _ _) (λ h, _),
  rw ←subset_iff_inter_eq_left at h, exact hC₁C₂ (nested_circuits_equal M h hC₁ hC₂ ),
end

lemma circuit_elim (hC₁C₂ : C₁ ≠ C₂) (hC₁ : M.is_circuit C₁) (hC₂ : M.is_circuit C₂)
(he : e ∈ C₁ ∩ C₂):
   ∃ C, M.is_circuit C ∧ C ⊆ ((C₁ ∪ C₂) \ {e}) :=
begin
  rw [←dep_iff_contains_circuit, dep_iff_r],
  have hI : C₁ ∩ C₂ ⊂ C₁ := inter_circuits_ssubset hC₁ hC₂ hC₁C₂,
  have heα := mem_of_mem_of_subset he (inter_subset_union C₁ C₂),
  have hcalc : M.r ((C₁ ∪ C₂) \ {e}) ≤ size ((C₁ ∪ C₂) \ {e}) -1 :=
  by linarith [M.rank_mono (diff_subset (C₁ ∪ C₂) {e} ), M.rank_submod C₁ C₂,
        r_cct hC₁, r_cct hC₂, r_cct_ssub hC₁ hI, size_modular C₁ C₂, size_remove_mem heα],
  exact int.le_sub_one_iff.mp hcalc,
end

def matroid_to_cct_family (M : matroid α) : cct_family α :=
  ⟨λ X, M.is_circuit X,
   empty_not_cct M,
   λ C₁ C₂, circuit_not_ssubset_circuit,
   λ C₁ C₂ h, circuit_elim ⟩

end circuit

section closure

/-- is spanning in M: closure is univ -/
@[simp] def is_spanning (M : matroid α) : set α → Prop :=
  λ X, M.r X = M.r univ

/-- X spans Y if the rank of X is the rank of X ∪ Y -/
def spans (M : matroid α) : set α → set α → Prop :=
  λ X Y, M.r (X ∪ Y) = M.r X

lemma spanning_iff_r :
  M.is_spanning X ↔ M.r X = M.r univ :=
by refl

lemma spans_iff_r  :
  M.spans X Y ↔ M.r (X ∪ Y) = M.r X :=
by refl

lemma not_spans_iff_r :
  ¬M.spans X Y ↔ M.r X < M.r (X ∪ Y) :=
by {rw [spans_iff_r, eq_comm],
    exact ⟨λ h, lt_of_le_of_ne (rank_mono_union_left M _ _) h, λ h, ne_of_lt h⟩}

lemma spanned_union (M : matroid α){X Y Y' : set α} :
  M.spans X Y → M.spans X Y' → M.spans X (Y ∪ Y') :=
begin
  unfold spans, intros h h',
  linarith [submod_three_sets M X Y Y', M.rank_mono_union_left X (Y ∩ Y'),
    M.rank_mono_union_left X (Y ∪ Y')],
end

lemma spanned_union_closed (M : matroid α) (X : set α) :
   union_closed (λ Y, spans M X Y) :=
begin
  refine ⟨_, λ Y Y' hY hY', spanned_union M hY hY'⟩,
  have : M.r (X ∪ ∅) = M.r X := by rw union_empty, assumption,
end

lemma spans_refl (M : matroid α) (X : set α) :
  M.spans X X :=
by {unfold spans, rw [union_self]}

lemma spans_subset (M : matroid α) :
  Y ⊆ Y' → M.spans X Y' → M.spans X Y :=
begin
  unfold spans, intros hYY' hXY,
  linarith [M.rank_mono_union_left X Y,  M.rank_mono (union_subset_union_right X hYY')],
end

lemma spans_rank_zero (X : set α){L : set α} (hL : M.r L = 0) :
  M.spans X L :=
by rw [spans_iff_r, rank_eq_rank_union_rank_zero X hL]

/-- closure of X in M : union of all sets spanned by X -/
def cl (M : matroid α) : set α → set α :=
  λ X, max_of_union_closed (spanned_union_closed M X)

-- cl X is the (unique) maximal set that is spanned by X
lemma cl_iff_max {X F : set α} :
  M.cl X = F ↔ M.spans X F ∧ ∀ Y, F ⊂ Y → ¬M.spans X Y :=
let huc := spanned_union_closed M X,
   h_eq := (union_closed_max_iff_in_and_ub huc F) in
by {dsimp at h_eq, unfold is_maximal at h_eq, rw [h_eq],
      unfold cl, rw [eq_comm, ←is_max_of_union_closed_iff huc]}

-- cl X is also the set spanned by X that contains all sets spanned by X
lemma cl_iff_spanned_ub {X F : set α} :
   M.cl X = F ↔ M.spans X F ∧ ∀ Y, M.spans X Y → Y ⊆ F :=
by {unfold cl, rw [eq_comm, is_max_of_union_closed_iff], refl}

lemma cl_iff_spanned_ub_r {X F : set α} :
   M.cl X = F ↔ M.r (X ∪ F) = M.r X ∧ ∀ Y, (M.r (X ∪ Y) = M.r X) → Y ⊆ F :=
by {unfold cl, rw [eq_comm, is_max_of_union_closed_iff], refl}

lemma cl_is_max :
  M.spans X (M.cl X) ∧ ∀ Y, (M.cl X) ⊂ Y → ¬M.spans X Y :=
cl_iff_max.mp rfl

lemma cl_is_ub :
  ∀ Y, M.spans X Y → Y ⊆ (M.cl X) :=
(cl_iff_spanned_ub.mp rfl).2

lemma subset_cl (M : matroid α) (X : set α) :
  X ⊆ M.cl X :=
(cl_iff_spanned_ub.mp rfl).2 _ (spans_refl M X)

lemma mem_cl_of_mem (he : e ∈ X):
  e ∈ M.cl X :=
mem_of_mem_of_subset he (M.subset_cl X)

lemma mem_cl_single (M : matroid α) (e : α) :
  e ∈ M.cl {e} :=
mem_cl_of_mem (mem_singleton e)

lemma spans_cl (M : matroid α) (X : set α) :
  M.spans X (M.cl X) :=
(cl_iff_max.mp rfl).1

lemma supset_cl (X : set α) :
  ∀ Y, (M.cl X ⊂ Y) → ¬M.spans X Y :=
(cl_iff_max.mp rfl).2

lemma spanned_subset_cl :
  M.spans X Y → Y ⊆ M.cl X :=
λ h, cl_is_ub Y h

lemma rank_zero_subset_cl (X : set α){L : set α} (hL : M.r L = 0) :
  L ⊆ M.cl X :=
spanned_subset_cl (spans_rank_zero X hL)

lemma subset_cl_iff (X Y: set α) :
  Y ⊆ M.cl X ↔ M.spans X Y :=
⟨λ h, spans_subset M h (spans_cl _ _ ), λ h, spanned_subset_cl h⟩

lemma subset_cl_iff_r (X Y : set α) :
  Y ⊆ M.cl X ↔ M.r (X ∪ Y) = M.r X :=
by {rw subset_cl_iff, refl}

lemma spanning_iff_cl_univ (X : set α) :
  M.is_spanning X ↔ M.cl X = univ :=
begin
  rw cl_iff_spanned_ub, unfold spans is_spanning, refine ⟨λ h, ⟨_,λ Y hY, _⟩, λ h, _⟩,
  rw [h, union_univ], apply subset_univ, rw [←h.1, union_univ],
end

lemma cl_univ (M : matroid α) :
  M.cl univ = univ :=
by {rw ←spanning_iff_cl_univ, obviously}

@[simp] lemma rank_cl (M : matroid α) (X : set α) :
  M.r (cl M X) = M.r X :=
begin
  have : M.r (X ∪ M.cl X) = M.r X := M.spans_cl X,
  linarith [M.rank_mono_union_right X (M.cl X), M.rank_mono (M.subset_cl X)],
end

lemma union_cl_rank_left (M : matroid α) (X Y : set α) :
  M.r ((M.cl X) ∪ Y) = M.r (X ∪ Y) :=
by {rw eq_comm, exact rank_eq_of_union_eq_rank_subset _ (subset_cl _ _) (rank_cl _ _).symm}

lemma union_cl_rank_right (M : matroid α) (X Y : set α) :
  M.r (X ∪ (M.cl Y)) = M.r (X ∪ Y) :=
by {rw [union_comm, union_comm _ Y], apply union_cl_rank_left}

lemma cl_idem (M : matroid α) (X : set α) :
  cl M (cl M X) = cl M X :=
begin
  rw cl_iff_spanned_ub, refine ⟨by apply spans_refl, λ Y hY, _⟩,
  rw subset_cl_iff, unfold spans, unfold spans at hY,
  apply rank_eq_of_le_union,
  linarith [M.rank_cl X, M.union_cl_rank_left X Y],
end

lemma spans_iff_cl_spans :
  M.spans X Y ↔ M.spans (M.cl X) Y :=
begin
  repeat {rw spans_iff_r},
  rw [rank_eq_of_union_eq_rank_subset, rank_cl],
  apply subset_cl, exact (rank_cl _ _).symm,
end

lemma cl_monotone (M : matroid α) :
  X ⊆ Y → M.cl X ⊆ M.cl Y :=
λ h, by {rw subset_cl_iff_r, apply rank_eq_of_le_union,
          rw [union_cl_rank_right, union_comm, subset_iff_union_eq_left.mp h]}

lemma nonmem_cl_iff_r :
  e ∉ M.cl X ↔ M.r (X ∪ {e}) = M.r X + 1 :=
begin
  rw [←singleton_subset_iff, subset_cl_iff_r], refine ⟨λ h, _, λ _, λ _, by linarith⟩,
  linarith [rank_augment_single_ub M X e,
  int.add_one_le_iff.mpr ((ne.symm h).le_iff_lt.mp (rank_mono_union_left M X {e}))],
end

lemma mem_cl_iff_r :
  e ∈ M.cl X ↔ M.r (X ∪ {e}) = M.r X :=
by rw [←singleton_subset_iff, subset_cl_iff_r]

lemma mem_cl_iff_spans :
  e ∈ M.cl X ↔ M.spans X {e} :=
by rw [spans_iff_r,mem_cl_iff_r]

lemma nonmem_cl_iff_nonspans :
  e ∉ M.cl X ↔ ¬M.spans X {e} :=
⟨λ h, λ hn, h (mem_cl_iff_spans.mpr hn), λ h, λ hn, h (mem_cl_iff_spans.mp hn)⟩

lemma rank_removal_iff_closure (X : set α) (e : α) (h : e ∈ X) :
  M.r (X \ {e}) = M.r X ↔ e ∈ M.cl (X \ {e}) :=
by rw [mem_cl_iff_r, remove_union_mem_singleton h, eq_comm]

lemma cl4 (M : matroid α) (X : set α) (e f : α) :
  e ∈ M.cl (X ∪ {f}) \ M.cl X  → f ∈ M.cl (X ∪ {e}) \ M.cl X :=
begin
  repeat {rw [mem_diff_iff, nonmem_cl_iff_r, mem_cl_iff_r]},
  rw union_right_comm, refine λ h, ⟨_,_⟩,
  apply rank_eq_of_le_union, linarith [rank_augment_single_ub M X f],
  cases h with h1 h2,
  linarith [h2, rank_augment_single_ub M X f, rank_mono_union_left M (X ∪ {e}) {f}],
end

end closure

section /-Flats-/ flat

/-- set for which all proper supersets have larger rank -/
def is_flat (M : matroid α) : set α → Prop :=
  λ F, ∀ (X : set α), F ⊂ X → M.r F < M.r X

/-- subtype of flats of M -/
def flat (M : matroid α) := { F : set α // M.is_flat F }

instance coe_flat : has_coe (M.flat) (set α) :=
  coe_subtype

instance fintype_flat : fintype (flat M) :=
by {unfold flat, apply_instance }

/-- flat of rank k -/
def is_rank_k_flat (M : matroid α) (k : ℤ) : set α → Prop :=
  λ F, M.is_flat F ∧ M.r F = k

/-- the unique rank zero flat -/
def loops : matroid α → set α :=
  λ M, M.cl ∅

lemma loops_def :
  M.loops = M.cl ∅ :=
rfl

/-- is a rank -one flat -/
def is_point (M : matroid α) : set α → Prop :=
  λ F, M.is_rank_k_flat 1 F

/-- is a rank-two flat -/
def is_line (M : matroid α) : set α → Prop :=
  λ F, M.is_rank_k_flat 2 F

/-- is a rank-three flat -/
def is_plane (M : matroid α) : set α → Prop :=
  λ F, M.is_rank_k_flat 3 F

/-- flat of rank r M - 1 -/
def is_hyperplane (M : matroid α) : set α → Prop :=
  λ H, M.is_rank_k_flat (M.r univ - 1) H

lemma is_point.r (h : M.is_point P): M.r P = 1 := h.2
lemma is_point.flat (h : M.is_point P) : M.is_flat P := h.1

lemma is_line.r (h : M.is_line X) : M.r X = 2 := h.2
lemma is_line.flat (h : M.is_line P) : M.is_flat P := h.1

lemma is_plane.r (h : M.is_plane X) : M.r X = 3 := h.2
lemma is_plane.flat (h : M.is_plane P) : M.is_flat P := h.1

def point (M : matroid α)  := {P : set α // M.is_point P}
instance point_fin : fintype M.point := by {unfold point, apply_instance}
instance point_coe : has_coe M.point (set α) := ⟨subtype.val⟩
lemma point.r (P : M.point): M.r P = 1 := P.2.r
lemma point.flat (P : M.point) : M.is_flat P := P.2.flat

def line (M : matroid α) := {L : set α // M.is_line L}
instance line_fin : fintype M.line := by {unfold line, apply_instance}
instance line_coe : has_coe M.line (set α) := ⟨subtype.val⟩
lemma line.r (P : M.line): M.r P = 2 := P.2.r
lemma line.flat (P : M.line) : M.is_flat P := P.2.flat

def plane (M : matroid α) := {L : set α // M.is_plane L}
instance plane_fin : fintype M.plane := by {unfold plane, apply_instance}
instance plane_coe : has_coe M.plane (set α) := ⟨subtype.val⟩
lemma plane.r (P : M.plane): M.r P = 3 := P.2.r
lemma plane.flat (P : M.plane) : M.is_flat P := P.2.flat

lemma rank_loops (M: matroid α) :
  M.r (M.loops) = 0 :=
by rw [loops, rank_cl, rank_empty]

lemma rank_zero_iff_subset_loops :
  M.r X = 0 ↔ X ⊆ M.loops :=
begin
  refine ⟨λ h, _, λ h, rank_eq_zero_of_le_zero _ ⟩,
  rw [loops, subset_cl_iff_r],
  simp, from h,
  convert M.rank_mono h,
  from eq.symm (rank_loops M),
end

lemma spans_loops (M : matroid α) (X : set α) :
  M.spans X M.loops :=
spans_rank_zero X (rank_loops M)

lemma loops_subset_cl (M : matroid α) (X : set α) :
  M.loops ⊆ M.cl X :=
rank_zero_subset_cl X (rank_loops M)

lemma rank_zero_iff_cl_eq_loops :
  M.r X = 0 ↔ M.cl X = M.loops :=
begin
  rw rank_zero_iff_subset_loops,
  refine ⟨λ h, subset.antisymm _ (M.loops_subset_cl _), λ h, _⟩,
  { rw [loops_def] at *, rw [← M.cl_idem ∅], exact M.cl_monotone h,  },
  rw ←h,
  exact subset_cl M X,
end

lemma flat_iff_r  (X : set α) :
  M.is_flat X ↔ ∀ Y, X ⊂ Y → M.r X < M.r Y :=
by refl

lemma cl_is_flat (M : matroid α) (X : set α) :
  M.is_flat (cl M X) :=
begin
  rw flat_iff_r, intros Y hY, have hne := cl_is_max.2 _ hY,
  rw [spans_iff_cl_spans, spans_iff_r] at hne,
  rw ←subset_iff_union_eq_left.mp hY.1,
  from lt_of_le_of_ne (M.rank_mono_union_left (cl M X) Y) (ne.symm hne),
end

lemma flat_iff_own_cl :
  M.is_flat F ↔ M.cl F = F :=
begin
  refine ⟨λ h, _, λ h, by {have := cl_is_flat M F, rw h at this, exact this}⟩,
  rw [cl_iff_max, spans_iff_r], simp_rw not_spans_iff_r,
  from ⟨by rw union_self, λ Y hFY, lt_of_lt_of_le (h Y hFY) (by {rw union_comm, apply rank_mono_union_left})⟩,
end

lemma loops_subset_flat (M : matroid α) (hF : M.is_flat F) :
  M.loops ⊆ F :=
by {rw ←flat_iff_own_cl.mp hF, apply loops_subset_cl}

lemma flat_iff_is_cl :
  M.is_flat  F ↔ ∃ X : set α, cl M X = F :=
⟨λ h, ⟨F, flat_iff_own_cl.mp h⟩, λ h,
    by {cases h with X hX, rw flat_iff_own_cl, rw ←hX, apply cl_idem}⟩

lemma subset_flat (X F : set α) :
  X ⊆ F → M.is_flat F → M.cl X ⊆ F :=
begin
  rw flat_iff_own_cl,
  intros hXF hF,
  rw ←hF, apply cl_monotone _ hXF,
end

lemma flat_iff_add_r :
  M.is_flat F ↔ ∀ e, e ∉ F → M.r F < M.r (F ∪ {e}) :=
begin
  rw flat_iff_r,
  refine ⟨λ h, λ e he, h _ (ssub_of_add_nonmem he), λ h, λ Y hY, _⟩,
  cases add_from_nonempty_diff.mp hY with e he,
  exact lt_of_lt_of_le (h e he.1) (M.rank_mono he.2),
end

lemma flat_iff_add :
  M.is_flat F ↔ ∀ (e : α), e ∉ F → ¬M.spans F {e} :=
by {rw [flat_iff_add_r], simp_rw not_spans_iff_r}

lemma univ_is_flat (M : matroid α) :
  M.is_flat univ :=
by {rw [flat_iff_own_cl, cl_univ]}

lemma fullrank_flat_is_univ :
  M.is_flat F → M.r F = M.r univ → F = univ :=
begin
  intros hF hFr,
  rw [flat_iff_own_cl] at hF,
  rw [←hF, ←spanning_iff_cl_univ],
  from hFr,
end

lemma flats_eq_of_nested_ge_rank
(hF₁ : M.is_flat F₁) (hF₂ : M.is_flat F₂) (hF₁F₂ : F₁ ⊆ F₂) (hr : M.r F₂ ≤ M.r F₁) :
  F₁ = F₂ :=
begin
  suffices h' : F₂ ⊆ F₁, exact subset.antisymm hF₁F₂ h',
  by_contra hn,
  rw [subset_def] at hn,
  push_neg at hn,
  obtain ⟨x, h₂, h₁⟩ := hn,
  linarith [
    flat_iff_add_r.mp hF₁ _ h₁,
    M.rank_mono (union_subset hF₁F₂ (singleton_subset_iff.mpr h₂))],
end

lemma hyperplane_iff_r  (X : set α) :
  M.is_hyperplane X ↔ M.r X = M.r univ - 1 ∧ ∀ Y, X ⊂ Y → M.r Y = M.r univ :=
begin
  unfold is_hyperplane is_rank_k_flat, rw flat_iff_r,
  refine ⟨λ h, ⟨h.2, λ Y hXY, _ ⟩, λ h, ⟨λ Y hXY, _, h.1⟩ ⟩,
  have := h.1 Y hXY, rw h.2 at this, linarith [rank_le_univ M Y],
  rw [h.1,h.2 Y hXY], exact sub_one_lt _,
end

lemma hyperplane_iff_maximal_nonspanning  (X : set α) :
  M.is_hyperplane X ↔ ¬M.is_spanning X ∧ ∀ (Y: set α), X ⊂ Y → M.is_spanning Y :=
begin
  rw hyperplane_iff_r, split,
  intro h, simp only [is_spanning], split, linarith [h.2],
  intros Y hXY, linarith [h.2 Y hXY, h.2, rank_le_univ M Y],
  simp only [is_spanning],
  refine λ h, ⟨_,h.2⟩, cases h with h1 h2,
  rcases ne_univ_has_add_one_size_ssupset (λ h', by {rw h' at h1, from h1 rfl} : X ≠ univ) with ⟨Y,hY₁, hY₂⟩,
  linarith [rank_diff_le_size_diff M hY₁.1, h2 _ hY₁,
            int.le_sub_one_of_le_of_ne (rank_le_univ M X) h1],
end

lemma hyperplane_iff_maximal_subflat  (H : set α) :
  M.is_hyperplane H ↔ H ≠ univ ∧ M.is_flat H ∧ (∀ X, M.is_flat X → H ⊂ X → X = univ) :=
begin
  refine ⟨λ h, ⟨λ hH, _,⟨h.1, λ X hX hHX, _⟩⟩, λ h, ⟨h.2.1,_⟩⟩,
  rw [hH, hyperplane_iff_r] at h, linarith,
  cases h with hHf hHr,
  rw flat_iff_r at hHf,
  rw [←(flat_iff_own_cl.mp hX), ←spanning_iff_cl_univ, is_spanning],
  linarith [hHf _ hHX, rank_le_univ M X],

  rcases h with ⟨h_univ, h_flat, hmax⟩,
  by_cases h1 : M.r H ≤ M.r univ - 2,
  rcases ne_univ_has_add_one_size_ssupset_element h_univ with ⟨e, he₁, he₂⟩,
  have := hmax (cl M (H ∪ {e})) (cl_is_flat _ _) (subset.lt_of_lt_of_le he₁ _),
  rw [←spanning_iff_cl_univ, spanning_iff_r] at this,
  linarith [rank_augment_single_ub M H e],
  apply subset_cl,
  push_neg at h1,

  by_cases h2: (M.r H < M.r univ),
  from le_antisymm (int.le_sub_one_of_lt h2) (by linarith only [h1]),

  have : M.r H = M.r univ := by linarith [rank_le_univ M H],
  from false.elim (h_univ (fullrank_flat_is_univ h_flat this)),
end

lemma cocircuit_iff_compl_hyperplane  (X : set α) :
  M.is_cocircuit X ↔ M.is_hyperplane Xᶜ :=
begin
  rw [cocircuit_iff_r, hyperplane_iff_r],
  refine ⟨λ h, ⟨h.1,λ Y hXY, _⟩ , λ h, ⟨h.1,λ Y hXY, h.2 _ (compl_ssubset_compl_iff.mpr hXY)⟩⟩,
  rw [←(h.2 _ (compl_ssubset_comm.mp hXY)), compl_compl],
end

lemma inter_flats_is_flat (M : matroid α) (F₁ F₂ : set α) :
  M.is_flat F₁ → M.is_flat F₂ → M.is_flat (F₁ ∩ F₂) :=
begin
  repeat {rw [flat_iff_add]}, simp_rw ←nonmem_cl_iff_nonspans,
  intros h₁ h₂ e he, rw nonmem_inter_iff at he, cases he,
  exact λ h, (h₁ e he) (mem_of_mem_of_subset h (cl_monotone M (inter_subset_left F₁ F₂))),
  exact λ h, (h₂ e he) (mem_of_mem_of_subset h (cl_monotone M (inter_subset_right F₁ F₂))),
end

lemma rank_inter_eq_rank_flats_lt
(hF₁ : M.is_flat F₁) (hF₂ : M.is_flat F₂) (hr : M.r F₁ = M.r F₂) (hF₁F₂ : F₁ ≠ F₂):
  M.r (F₁ ∩ F₂) < M.r F₁ :=
begin
  by_contra hn, apply hF₁F₂,
  push_neg at hn,
  replace hn := rank_eq_of_le_supset (inter_subset_left _ _) hn,
  apply subset.antisymm,
  { rw subset_iff_inter_eq_left,
    exact flats_eq_of_nested_ge_rank (M.inter_flats_is_flat _ _ hF₁ hF₂) hF₁
      (inter_subset_left _ _) (by rw hn)},
  rw hr at hn, rw subset_iff_inter_eq_right,
  apply flats_eq_of_nested_ge_rank (M.inter_flats_is_flat _ _ hF₁ hF₂) hF₂
    (inter_subset_right _ _) (by rw hn),
end

lemma rank_inter_lines_le_one (hL₁ : M.is_line L₁) (hL₂ : M.is_line L₂) (h : L₁ ≠ L₂):
  M.r (L₁ ∩ L₂) ≤ 1 :=
begin
  apply int.le_of_lt_add_one,
  convert rank_inter_eq_rank_flats_lt hL₁.flat hL₂.flat (by {rw [hL₁.r, hL₂.r]}) h,
  rw hL₁.r, norm_num,
end

/-- is both a circuit and a hyperplane -/
def is_circuit_hyperplane (M : matroid α) (C : set α) :=
  M.is_circuit C ∧ M.is_hyperplane C

lemma circuit_hyperplane_rank (hC : is_circuit_hyperplane M C) :
  M.r C = M.r univ - 1 :=
by {simp_rw [is_circuit_hyperplane, hyperplane_iff_r] at hC, from hC.2.1}

lemma circuit_hyperplane_size (hC : is_circuit_hyperplane M C) :
  size C = M.r univ :=
by {have := circuit_hyperplane_rank hC, simp_rw [is_circuit_hyperplane, circuit_iff_r] at hC, linarith [hC.1.1]}

lemma circuit_hyperplane_rank_size (hC : is_circuit_hyperplane M C) :
  M.r C = size C - 1 :=
by linarith [circuit_hyperplane_size hC, circuit_hyperplane_rank hC]

lemma circuit_hyperplane_ssubset_rank {C X : set α} (hC : is_circuit_hyperplane M C) :
  X ⊂ C → M.r X = size X :=
λ hXC, by {simp_rw [is_circuit_hyperplane, circuit_iff_r] at hC, from hC.1.2 _ hXC,}

lemma circuit_hyperplane_ssupset_rank {C X : set α} (hC : is_circuit_hyperplane M C) :
  C ⊂ X → M.r X = M.r univ :=
λ hXC, by {simp_rw [is_circuit_hyperplane, hyperplane_iff_r] at hC, from hC.2.2 _ hXC,}

lemma circuit_hyperplane_dual :
  M.is_circuit_hyperplane C ↔ (dual M).is_circuit_hyperplane Cᶜ :=
begin
  simp_rw [is_circuit_hyperplane, ←cocircuit_iff_compl_hyperplane, is_cocircuit],
  rw [dual_dual, ←is_cocircuit, cocircuit_iff_compl_hyperplane, compl_compl, and_comm],
end

--lemma closure_eq_iff_flat  {X F : set α} :
--  cl M X = F ↔ X ⊆ F ∧ is_flat M F ∧ ∀ F', is_flat

end flat

section loopnonloop
/-- is a rank-zero element -/
def is_loop (M : matroid α) : α → Prop :=
  λ e, M.r {e} = 0

/-- is a rank-one element -/
def is_nonloop (M : matroid α) : α → Prop :=
  λ e, M.r {e} = 1

/-- is a loop of the dual -/
def is_coloop (M : matroid α) : α → Prop :=
  is_loop (dual M)

/-- is not a coloop of the dual -/
def is_noncoloop (M : matroid α) : α → Prop :=
  is_coloop (dual M)

def nonloops (M : matroid α) : set α :=
  { e : α | M.is_nonloop e }

lemma nonloop_iff_r :
  M.is_nonloop e ↔ M.r {e} = 1 :=
iff.rfl

lemma nonloop_iff_mem_nonloops :
  M.is_nonloop e ↔ e ∈ M.nonloops :=
iff.rfl

lemma loop_iff_r :
  M.is_loop e ↔ M.r {e} = 0 :=
iff.rfl

lemma nonloop_iff_one_le_rank :
  M.is_nonloop e ↔ 1 ≤ M.r {e} :=
by {rw nonloop_iff_r, split; {intro, linarith [rank_single_ub M e]}}

lemma loop_iff_circuit :
  M.is_loop e ↔ M.is_circuit {e} :=
by simp [loop_iff_r, circuit_iff_r, size_singleton, ssubset_singleton_iff_empty]

lemma nonloop_iff_indep :
  M.is_nonloop e ↔ M.is_indep {e} :=
by rw [is_nonloop, indep_iff_r, size_singleton]

lemma rank_nonloop :
  M.is_nonloop e → M.r {(e : α)} = 1 :=
by {unfold is_nonloop, from λ h, h}

lemma is_nonloop.r (h : M.is_nonloop e) : M.r {e} = 1 :=
h

lemma rank_loop :
  M.is_loop e → M.r {e} = 0 :=
by {unfold is_loop, from λ h, h}

lemma cl_loop_eq_loops (he : M.is_loop e) :
  M.cl {e} = M.loops :=
rank_zero_iff_cl_eq_loops.mp (rank_loop he)

lemma loop_of_mem_rank_zero (he : e ∈ X) (hX : M.r X = 0) :
  M.is_loop e :=
by {rw loop_iff_r, apply rank_zero_of_subset_rank_zero (singleton_subset_iff.mpr he) hX,  }

lemma loop_iff_mem_loops  :
  M.is_loop e ↔ e ∈ M.loops :=
by {simp_rw [is_loop, ←singleton_subset_iff], from rank_zero_iff_subset_loops}

lemma nonloop_iff_not_mem_loops :
  M.is_nonloop e ↔ e ∉ M.loops :=
begin
  simp_rw [is_nonloop, ←singleton_subset_iff, ←rank_zero_iff_subset_loops],
  refine ⟨λ h h', by linarith, λ h, _⟩,
  linarith [rank_single_ub M e, rank_gt_zero_of_ne h],
end

lemma nonloop_iff_not_loop  :
  M.is_nonloop e ↔ ¬ M.is_loop e :=
begin
  unfold is_loop is_nonloop, refine ⟨λ h, _ ,λ h, _⟩,rw h ,
  simp only [not_false_iff, one_ne_zero],
  have := M.rank_le_size {e}, rw size_singleton at this,
  linarith [(ne.le_iff_lt (ne.symm h)).mp (M.rank_nonneg {e})],
end

lemma nonloops_eq_compl_loops (M : matroid α ):
  M.nonloops = M.loopsᶜ :=
by {ext, rw [← nonloop_iff_mem_nonloops, nonloop_iff_not_mem_loops ],  refl}

lemma loop_iff_not_nonloop  :
  M.is_loop e ↔ ¬ M.is_nonloop e :=
by simp [nonloop_iff_not_loop]

lemma loop_or_nonloop (M : matroid α) (e : α) :
  M.is_loop e ∨ M.is_nonloop e :=
by {rw [loop_iff_not_nonloop], tauto}

lemma rank_eq_rank_insert_loop (X : set α) (he : M.is_loop e) :
  M.r (X ∪ {e}) = M.r X :=
rank_eq_rank_union_rank_zero _ (loop_iff_r.mp he)

lemma rank_eq_rank_remove_loop (X : set α) (he : M.is_loop e) :
  M.r (X \ {e}) = M.r X :=
rank_eq_rank_diff_rank_zero _ (loop_iff_r.mp he)

@[simp] lemma rank_eq_rank_diff_loops (X : set α) :
  M.r (X \ M.loops) = M.r X :=
rank_eq_rank_diff_rank_zero _ (M.rank_loops)

lemma nonloop_of_one_le_rank (h : 1 ≤ M.r {e}) :
  M.is_nonloop e :=
by {rw [nonloop_iff_r, eq_comm], exact le_antisymm h (by {convert M.rank_le_size _, simp, })}

lemma nonloop_of_rank_lt_insert (h : M.r X < M.r (X ∪ {e})) :
  M.is_nonloop e :=
by_contra (λ hn,
  by {rw rank_eq_rank_insert_loop _ (loop_iff_not_nonloop.mpr hn) at h, exact lt_irrefl _ h,})

lemma coloop_iff_r  (e : α) :
  M.is_coloop e ↔ M.r {e}ᶜ = M.r univ - 1 :=
begin
  unfold is_coloop is_loop, rw [dual_r,size_singleton],
  exact ⟨λh, by linarith,λ h, by linarith⟩,
end

lemma coloop_iff_r_less  (e : α) :
  M.is_coloop e ↔ M.r {e}ᶜ < M.r univ :=
begin
  unfold is_coloop is_loop, rw [dual_r,size_singleton],
  refine ⟨λh,by linarith,λ h,_⟩,
  have := rank_diff_le_size_diff M (subset_univ {e}ᶜ),
  rw [←size_compl, size_singleton] at this,
  linarith [int.le_sub_one_iff.mpr h],
end

lemma point_eq_cl_mem
(hP : M.is_point P) (he : M.is_nonloop e) (heP : e ∈ P) :
  M.cl {e} = P :=
begin
  apply flats_eq_of_nested_ge_rank (M.cl_is_flat {e}) hP.1,
  { rw ← singleton_subset_iff at heP,
    rw ← flat_iff_own_cl.mp hP.1,
    apply M.cl_monotone heP,},
  rw [hP.2, rank_cl, rank_nonloop he],
end

/-- nonloop as subtype -/
def nonloop (M : matroid α) := { e : α // is_nonloop M e}

instance coe_nonloop : has_coe (nonloop M) (α) := ⟨λ e, e.val⟩
--def noncoloop (M : matroid α) : Type := { e : α // is_nonloop (dual M) e}

instance fin_nonloop : fintype M.nonloop :=
by {unfold nonloop, apply_instance}

lemma eq_nonloop_coe (h : M.is_nonloop e) :
  e = coe (⟨e, h⟩ : M.nonloop) :=
rfl

lemma rank_coe_nonloop (e : nonloop M) :
  M.r {(e : α)} = 1 :=
rank_nonloop (e.2)

lemma coe_nonloop_indep (e : nonloop M) :
  M.is_indep {(e : α)} :=
by {rw [indep_iff_r], simp only [size_singleton, coe_coe], apply rank_coe_nonloop e,}

lemma rank_two_nonloops_lb  (e f : nonloop M) :
  1 ≤ M.r ({e,f}) :=
begin
  rw ←union_singletons_eq_pair,
  linarith [rank_coe_nonloop e, M.rank_mono_union_left {e} {f}],
end

lemma rank_two_nonloops_ub  (e f : nonloop M) :
  M.r ({e,f}) ≤ 2 :=
begin
  rw ←union_singletons_eq_pair,
  linarith [rank_coe_nonloop e, rank_coe_nonloop f,
    M.rank_nonneg ({e} ∩ {f}), M.rank_submod {e} {f}],
end

/-- a version of rank_augment where the conclusion asserts that z is a nonloop -/
theorem rank_augment_nonloop (h : M.r X < M.r Z) :
  ∃ (z ∈ Z), M.is_nonloop z ∧ M.r X < M.r (X ∪ {z}) :=
begin
  obtain ⟨z, hz, hr⟩ := rank_augment h,
  refine ⟨z,hz, nonloop_iff_not_loop.mpr (λ hz', _), hr⟩,
  rw rank_eq_rank_insert_loop _ hz' at hr,
  exact lt_irrefl _ hr,
end

lemma contains_nonloop_of_one_le_rank (h : 1 ≤ M.r X):
  ∃ e ∈ X, M.is_nonloop e :=
begin
  obtain ⟨z, hz, hz', -⟩ := rank_augment_nonloop (by {rw rank_empty, linarith} : M.r ∅ < M.r X),
  exact ⟨z,hz,hz'⟩,
end

end loopnonloop

section /-Bases-/ basis

/-- B is a basis of X : an independent subset of X spanning X -/
def is_basis_of (M : matroid α) (B X : set α) : Prop :=
  B ⊆ X ∧ M.r B = size B ∧ M.r B = M.r X

/-- B is a basis of M: an independent set spanning M -/
def is_basis (M : matroid α) (B : set α) : Prop :=
  M.is_basis_of B univ

/-- basis type -/
def basis (M : matroid α) := {B : set α // M.is_basis B}

instance coe_subtype_basis : has_coe (M.basis) (set α) :=
  coe_subtype

instance finite_basis : fintype (M.basis) :=
by {unfold basis, apply_instance }

/-- basis of set X type -/
def basis_of (M : matroid α) (X : set α) := {B : set α // M.is_basis_of B X}

instance coe_subtype_basis_of (X : set α) : has_coe (M.basis_of X) (set α) :=
  coe_subtype

instance fintype_basis_of (X : set α) : fintype (M.basis_of X) :=
by {unfold basis_of, apply_instance }

lemma is_basis_of.size_eq_r (h : M.is_basis_of B X) :
  size B = M.r X :=
by rw [← h.2.2, ← h.2.1]

lemma is_basis_of.indep (h : M.is_basis_of B X):
  M.is_indep B :=
by rw [indep_iff_r, h.2.1]

lemma is_basis_of.size_eq_r_self (h : M.is_basis_of B X) :
  size B = M.r B :=
by rw [h.size_eq_r, h.2.2]

lemma is_basis_of.is_subset_of (h : M.is_basis_of B X):
  B ⊆ X :=
h.1

lemma size_basis :
  M.is_basis B → size B = M.r univ :=
is_basis_of.size_eq_r

lemma bases_of_equicardinal (M : matroid α){B₁ B₂ X: set α} :
  M.is_basis_of B₁ X → M.is_basis_of B₂ X → size B₁ = size B₂ :=
λ h₁ h₂, by rw[is_basis_of.size_eq_r h₁, is_basis_of.size_eq_r h₂]

lemma bases_equicardinal (M : matroid α){B₁ B₂ : set α} :
  M.is_basis B₁ → M.is_basis B₂ → size B₁ = size B₂ :=
bases_of_equicardinal M

lemma basis_iff_r  :
  M.is_basis B ↔ M.r B = size B ∧ M.r B = M.r univ :=
⟨λ h, h.2, λ h, ⟨subset_univ B,h⟩⟩

/-- is a basis of the dual -/
def is_cobasis (M : matroid α) : set α → Prop :=
  λ B, (dual M).is_basis B

def cobasis (M : matroid α) := {B : set α // M.is_cobasis B}

@[simp] lemma cobasis_iff  :
  M.is_cobasis B ↔ (dual M).is_basis B :=
by rw is_cobasis

@[simp] lemma basis_of_iff_augment :
  M.is_basis_of B X ↔ B ⊆ X ∧ M.r B = size B ∧ ∀ (e:α), e ∈ X → M.r (B ∪ {e}) = M.r B :=
begin
  refine ⟨λ h, ⟨h.1,⟨h.2.1,λ e he, _⟩⟩, λ h, ⟨h.1,⟨h.2.1,_⟩⟩⟩,
  { linarith [h.2.2,
      M.rank_mono (union_subset h.1 (singleton_subset_iff.mpr he)),
      M.rank_mono (subset_union_left B {e})]},
  refine rank_eq_of_not_lt_supset h.1 (λ hBX, _),
  cases rank_augment hBX with e he,
  linarith [h.2.2 e he.1, he.2],
end

lemma basis_iff_augment :
  M.is_basis B ↔ M.r B = size B ∧ ∀ (e:α), M.r (B ∪ {e}) = M.r B :=
begin
  unfold is_basis, rw basis_of_iff_augment,
  refine ⟨λ h, ⟨h.2.1,λ e, h.2.2 e (mem_univ e)⟩, λ h, ⟨subset_univ B, ⟨h.1,λ e he,h.2 e⟩⟩ ⟩,
end

lemma basis_of_iff_augment_i :
  M.is_basis_of B X ↔ B ⊆ X ∧ M.is_indep B ∧ ∀ (e:α), e ∈ X \ B → ¬M.is_indep (B ∪ {e}) :=
begin
  rw basis_of_iff_augment,
  refine ⟨λ h, ⟨h.1,⟨h.2.1,λ e he hi, _⟩⟩, λ h, ⟨h.1,⟨h.2.1,λ e he, _⟩ ⟩⟩,
  rw indep_iff_r at hi,
  rw mem_diff_iff at he,
  linarith [h.2.2 e he.1, size_union_nonmem_singleton he.2],
  by_cases heB: e ∈ B,
  rw (union_mem_singleton heB),
  have : e ∈ X \ B := by {rw mem_diff_iff, from ⟨he,heB⟩},
  have := h.2.2 _ this,
  rw not_indep_iff_r at this,
  have hi := h.2.1, rw indep_iff_r at hi,
  linarith [size_union_nonmem_singleton heB, M.rank_mono (subset_union_left B {e})],
end

lemma basis_iff_augment_i :
  is_basis M B ↔ M.is_indep B ∧ ∀ (e:α), e ∉ B → ¬M.is_indep (B ∪ {e}) :=
begin
  simp_rw [is_basis, basis_of_iff_augment_i, ←mem_compl_iff, univ_diff],
  from ⟨λ h, ⟨h.2.1,λ e he, h.2.2 _ he⟩, λ h, ⟨subset_univ B, h⟩⟩,
end

lemma basis_of_iff_indep_full_rank {B X : set α} :
  M.is_basis_of B X ↔ B ⊆ X ∧ M.is_indep B ∧ size B = M.r X :=
begin
  simp_rw [is_basis_of, indep_iff_r],
  refine ⟨λ h, ⟨h.1, ⟨_,_⟩⟩, λ h, ⟨h.1,⟨_,_⟩⟩⟩;
  linarith,
end

lemma basis_iff_indep_full_rank :
  M.is_basis B ↔ M.is_indep B ∧ size B = M.r univ :=
begin
  simp_rw [basis_iff_r, indep_iff_r],
  refine ⟨λ h, ⟨h.1, _⟩, λ h, ⟨h.1,_⟩⟩;
  linarith,
end

lemma basis_is_indep :
  M.is_basis B → M.is_indep B :=
  λ h, (basis_iff_indep_full_rank.mp h).1

lemma cobasis_iff_r :
  M.is_cobasis B ↔ M.r Bᶜ = size Bᶜ ∧ M.r Bᶜ = M.r univ :=
begin
  simp_rw [is_cobasis, basis_iff_r, dual],
  refine ⟨λ _, ⟨_,_⟩, λ _, ⟨_,_⟩⟩;
  linarith [size_compl B, rank_compl_univ M],
end

lemma cobasis_iff_compl_basis :
  M.is_cobasis B ↔ M.is_basis Bᶜ :=
by rw [cobasis_iff_r, basis_iff_r]

lemma compl_cobasis_iff_basis :
  M.is_cobasis Bᶜ ↔ M.is_basis B :=
by rw [cobasis_iff, ←cobasis_iff_compl_basis, cobasis_iff, dual_dual]

lemma basis_exchange (M : matroid α){B₁ B₂ : set α} (hB₁ : M.is_basis B₁)
(hB₂ : M.is_basis B₂) (he : e ∈ B₁ \ B₂) :
  ∃ (f : α), f ∈ (B₂ \ B₁) ∧ M.is_basis (B₁ \ {e} ∪ {f}) :=
begin
  rw basis_iff_indep_full_rank at hB₁ hB₂,
  simp_rw basis_iff_indep_full_rank,
  cases mem_diff_iff.mp he with he₁ he₂,
  have h' : M.is_indep (B₁ \ {e}) := subset_indep (diff_subset _ _) hB₁.1,
  rcases indep_aug_diff (by { rw size_remove_mem he₁, linarith, }) h' hB₂.1
    with ⟨f,⟨hf, hf_aug⟩⟩,
  have h'' : B₂ \ (B₁ \ {e}) = B₂ \ B₁,
  { repeat {rw diff_eq},
    rw [compl_inter, compl_compl, inter_distrib_left,
    inter_comm _ {e}, nonmem_disjoint_iff.mp he₂, union_empty]},
  rw h'' at hf,
  cases mem_diff_iff.mp hf with hf₁ hf₂,
  refine ⟨f, hf, hf_aug, _⟩,
  rw size_remove_union_singleton he₁ hf₂, exact hB₁.2,
end

lemma extends_to_basis_of :
  I ⊆ X → M.is_indep I → ∃ B, I ⊆ B ∧ M.is_basis_of B X :=
let P := λ J, I ⊆ J ∧ M.is_indep J ∧ J ⊆ X in
begin
  intros hIX hIi,
  rcases maximal_example_aug P ⟨subset_refl I,⟨hIi,hIX⟩⟩ with ⟨B, ⟨_, ⟨hPB,hBmax⟩⟩⟩ ,
  simp_rw basis_of_iff_augment_i,
  refine ⟨B, ⟨hPB.1,⟨hPB.2.2,⟨hPB.2.1,λ e he hecon,_⟩⟩ ⟩⟩,
  rw mem_diff_iff at he,
  have := hBmax _ he.2,
  push_neg at this,
  from this (subset.trans h_left (subset_union_left _ _)) hecon (union_subset hPB.2.2 (singleton_subset_iff.mpr he.1)),
end

lemma exists_basis_of (M : matroid α) (X : set α) :
  ∃ B, M.is_basis_of B X :=
by {cases extends_to_basis_of (empty_subset X) (empty_indep M) with B hB, from ⟨B,hB.2⟩}

lemma exists_basis (M : matroid α) :
  ∃ B, M.is_basis B :=
by apply exists_basis_of

lemma extends_to_basis :
  M.is_indep I → ∃ B, I ⊆ B ∧ M.is_basis B :=
  λ h, extends_to_basis_of (subset_univ I) h

lemma flat_eq_cl_basis {B F : set α} (hF : M.is_flat F) (hBF : M.is_basis_of B F) :
  F = M.cl B :=
begin
  apply subset.antisymm,
  rw [subset_cl_iff_r, subset_iff_union_eq_left.mp hBF.1, hBF.2.2],
  rw [←flat_iff_own_cl.mp hF],
  apply M.cl_monotone hBF.1,
end

lemma flat_iff_cl_indep :
  M.is_flat F ↔ ∃ I, M.is_indep I ∧ F = M.cl I :=
begin
  refine ⟨λ h, _, λ h, _⟩,
    rcases M.exists_basis_of F with ⟨I,hI⟩,
    simp_rw [indep_iff_r, flat_eq_cl_basis h hI],
    refine ⟨I, by rw hI.2.1, rfl⟩,
  rcases h with ⟨I,-,rfl⟩,
  apply cl_is_flat,
end

lemma rank_k_flat_iff_cl_indep {k : ℤ} :
  M.is_rank_k_flat k F ↔ ∃ I, M.is_indep I ∧ size I = k ∧ F = M.cl I :=
begin
  simp_rw [is_rank_k_flat, flat_iff_cl_indep],
  refine ⟨λ h, _, λ h, _⟩,
  { rcases h with ⟨⟨I,hI,rfl⟩,hF⟩,
    refine ⟨I, hI, _, rfl⟩,
    rw [←hF, rank_cl, ←(indep_iff_r.mp hI)],},
  rcases h with ⟨I, hI, hk, rfl⟩,
  refine ⟨⟨I, hI, rfl⟩,_⟩,
  rw [rank_cl, (indep_iff_r.mp hI), hk],
end

lemma point_iff_cl_nonloop :
  M.is_point P ↔ ∃ e, M.is_nonloop e ∧ P = M.cl {e} :=
begin
  simp_rw [is_point, rank_k_flat_iff_cl_indep, size_one_iff_eq_singleton],
  refine ⟨λ h, _, λ h, _⟩,
  { rcases h with ⟨I,hs,⟨e,rfl⟩,rfl⟩,
    exact ⟨e, by {rwa nonloop_iff_indep}, rfl⟩, },
  rcases h with ⟨e,he,rfl⟩,
  exact ⟨{e}, by {rwa ←nonloop_iff_indep}, ⟨e,rfl⟩, rfl⟩,
end

lemma point_of_cl_nonloop (he : M.is_nonloop e):
  M.is_point (M.cl {e}) :=
point_iff_cl_nonloop.mpr ⟨e,he,rfl⟩

lemma indep_iff_contained_in_basis :
  M.is_indep X ↔ ∃ B, X ⊆ B ∧ M.is_basis B :=
begin
  refine ⟨λ h, extends_to_basis h,  λ h, _⟩,
  cases h with B hB,
  from indep_of_subset_indep hB.1 (basis_is_indep hB.2),
end

lemma mem_cl_iff_i :
  e ∈ M.cl X
  ↔ ∃ I ⊆ X, M.is_indep I ∧ ∀ J ⊆ X ∪ {e}, is_indep M J → size J ≤ size I :=
begin
  rw mem_cl_iff_r,
  refine ⟨λ h, _, λ h, _⟩,
  rcases M.exists_basis_of X with ⟨I, ⟨hI₁, hI₂, hI₃⟩⟩,
  refine ⟨_, hI₁, hI₂, λ J hJx hJ, _⟩,
  rw [←hI₂, hI₃, ←h, ←indep_iff_r.mp hJ], exact M.rank_mono hJx,
  rw eq_comm,
  refine rank_eq_of_le_supset (subset_union_left _ _) _,
  rcases M.exists_basis_of (X ∪ {e}) with ⟨J, ⟨hJ₁, hJ₂, hJ₃⟩⟩,
  rcases h with ⟨I,hI,⟨hIind,hIX⟩⟩,
  specialize hIX J hJ₁ hJ₂,
  rw [←hJ₃,hJ₂], rw ←(r_indep hIind) at hIX,
  from le_trans hIX (M.rank_mono hI),
end

lemma rank_eq_iff_exists_basis_of (M : matroid α) (X : set α){n : ℤ} :
  M.r X = n ↔ ∃ B, M.is_basis_of B X ∧ size B = n :=
begin
  refine ⟨λ h, _, λ h, _⟩,
  subst h, cases exists_basis_of M X with B hB,
  exact ⟨B, hB, is_basis_of.size_eq_r hB⟩,
  rcases h with ⟨B,⟨⟨h₁,h₂⟩,h₃⟩⟩,
  rw [←h₂.2, h₂.1], exact h₃,
end

lemma rank_as_indep (M : matroid α) (X : set α) :
  M.r X = max_val (λ I: indep_subset_of M X, size I.val) :=
begin
  rcases max_spec (λ I: indep_subset_of M X, size I.val) with ⟨⟨B,h,h'⟩, h₁, h₂⟩,
  dsimp only at *, rw [←h₁], clear h₁,
  rw [←indep_iff_r.mp h'],
  apply le_antisymm _ (M.rank_mono h),
  by_contra hcon, push_neg at hcon,
  rcases rank_augment hcon with ⟨z, hz, hB⟩,
  specialize h₂ ⟨B ∪ {z}, _⟩, swap, dsimp at h₂,
    have : B ⊂ (B ∪ {z}), from
      ssubset_of_subset_ne (subset_union_left _ _) (λ h, by {rw ←h at hB, linarith,}),
    linarith [size_strict_monotone this],
  exact ⟨union_singleton_subset_of_subset_mem h hz, indep_of_indep_aug h' hB⟩,
end

lemma r_univ_eq_max_size_indep (M : matroid α) :
  M.r univ = max_val (λ I : M.indep, size I.val) :=
begin
  rw rank_as_indep,
  set φ : M.indep_subset_of univ → M.indep := λ X, ⟨X.val, X.property.2⟩ with hφ,
  have : function.surjective φ,
    from λ X, by {use ⟨X.val, ⟨subset_univ X.val, X.property⟩⟩, rw hφ, simp,},
  rw [max_reindex φ this (λ X, size X.val)],
  refl,
end

lemma not_indep_iff_exists_removal :
  ¬M.is_indep X ↔ ∃ (e : α), e ∈ X ∧ M.r (X \ {e}) = M.r X :=
begin
  rw not_indep_iff_r, rcases exists_basis_of M X with ⟨B, ⟨h,h',h''⟩⟩,
  refine ⟨λ h1, _, λ h1,_⟩,
  { rw [←h'', h'] at h1,
    rcases mem_diff_of_size_lt h1 with ⟨e,he1,he2⟩,
    refine ⟨e,he1,_⟩,
    apply rank_eq_of_le_supset, intro x, simp, tauto,
    rw [←h'',diff_eq],
    apply M.rank_mono (subset_inter h _),  tidy, },
  rcases h1 with ⟨e, heX, he⟩,
  rw ←he, refine lt_of_le_of_lt (M.rank_le_size _) _,
  rw size_remove_mem heX, linarith,
end

end basis

section ext

variables {M₁ M₂ : matroid α}

lemma rank_ext :
  M₁.r = M₂.r → M₁ = M₂ :=
λ h, by {ext, rw h}

lemma indep_ext :
  M₁.is_indep = M₂.is_indep → M₁ = M₂ :=
begin
  intro h, ext X,
  cases exists_basis_of M₁ X with B hB,
  rw ←is_basis_of.size_eq_r hB,
  rw basis_of_iff_augment_i at hB,
  simp_rw h at hB,
  rw ←basis_of_iff_augment_i at hB,
  rw ←is_basis_of.size_eq_r hB,
end

lemma circuit_ext :
  M₁.is_circuit = M₂.is_circuit → M₁ = M₂ :=
begin
  intro h, apply indep_ext, ext X,
  simp_rw [indep_iff_contains_no_circuit, h],
end

lemma cocircuit_ext :
  M₁.is_cocircuit = M₂.is_cocircuit → M₁ = M₂ :=
  λ h, dual_inj (circuit_ext h)

lemma hyperplane_ext :
  M₁.is_hyperplane = M₂.is_hyperplane → M₁ = M₂ :=
begin
  intro h, apply cocircuit_ext, ext X,
  simp_rw [cocircuit_iff_compl_hyperplane, h],
end

lemma flat_ext :
  M₁.is_flat = M₂.is_flat → M₁ = M₂ :=
begin
  intro h, apply hyperplane_ext, ext X,
  simp_rw [hyperplane_iff_maximal_subflat, h],
end

lemma basis_ext :
  M₁.is_basis = M₂.is_basis → M₁ = M₂ :=
begin
  intro h, apply indep_ext, ext X,
  simp_rw [indep_iff_contained_in_basis, h],
end

lemma circuit_ind_of_distinct (hM₁M₂ : M₁ ≠ M₂) :
  ∃ X, (M₁.is_circuit X ∧ M₂.is_indep X) ∨ (M₂.is_circuit X ∧ M₁.is_indep X) :=
begin
  by_contra h, push_neg at h,
  refine hM₁M₂ (indep_ext _), ext Y,
  simp_rw [indep_iff_contains_no_circuit, not_iff_not],
  refine ⟨λ h₁, _, λ h₂, _⟩,
  rcases h₁ with ⟨C, ⟨hC, hCY⟩⟩,
  have := (h C).1 hC,
  simp_rw [←dep_iff_not_indep, dep_iff_contains_circuit] at this,
  rcases this with ⟨C₂, ⟨hC₂, hC₂C⟩⟩,
  from ⟨C₂, ⟨hC₂, subset.trans hC₂C hCY⟩⟩,
  rcases h₂ with ⟨C, ⟨hC, hCY⟩⟩,
  have := (h C).2 hC,
  simp_rw [←dep_iff_not_indep, dep_iff_contains_circuit] at this,
  rcases this with ⟨C₂, ⟨hC₂, hC₂C⟩⟩,
  from ⟨C₂, ⟨hC₂, subset.trans hC₂C hCY⟩⟩,
end

end ext

end matroid


-- Estudante: Lucas Emanuel Resck Domingues

import data.set

-- Exercise 1

section
    open function int algebra

    def f (x : ℤ) : ℤ := x + 3
    def g (x : ℤ) : ℤ := -x
    def h (x : ℤ) : ℤ := 2 * x + 3

    example : injective f :=
        assume x1 x2,
            assume h1 : x1 + 3 = x2 + 3,   -- Lean knows this is the same as f x1 = f x2
                show x1 = x2, from eq_of_add_eq_add_right h1

    example : surjective f :=
        assume y,
            have h1 : f (y - 3) = y, from calc
                f (y - 3) = (y - 3) + 3 : rfl
                    ... = y           : by rw sub_add_cancel,
        show ∃ x, f x = y, from exists.intro (y - 3) h1

    example (x y : ℤ) (h : 2 * x = 2 * y) : x = y :=
            have h1 : 2 ≠ (0 : ℤ), from dec_trivial,  -- this tells Lean to figure it out itself
        show x = y, from eq_of_mul_eq_mul_left h1 h

    example (x : ℤ) : -(-x) = x := neg_neg x

    example (A B : Type) (u : A → B) (v : B → A) (h : left_inverse u v) :
        ∀ x, u (v x) = x :=
            h

    example (A B : Type) (u : A → B) (v : B → A) (h : left_inverse u v) :
        right_inverse v u :=
            h

    -- fill in the sorry's in the following proofs

    example : injective h :=
        begin
            intros a b h1,
            have h2, from eq_of_add_eq_add_right h1,
            have h3 : 2 ≠ (0 : ℤ), from dec_trivial,
            have h4, from eq_of_mul_eq_mul_left h3 h2,
            assumption
        end

    example : surjective g :=
        show ∀ y, ∃ x, g x = y, from
            assume y,
                have h1 : g (-y) = y, from
                    calc
                        g (-y) = -(-y) : rfl
                        ... = y     : neg_neg y,
            show ∃ x, g x = y, from exists.intro (-y) h1

    example (A B : Type) (u : A → B) (v1 : B → A) (v2 : B → A)
    (h1 : left_inverse v1 u) (h2 : right_inverse v2 u) : v1 = v2 :=
        funext
            (assume x,
                calc
                    v1 x = v1 (u (v2 x)) : by rw h2
                 ... = v2 x              : by rw h1)
end

-- Exercise 2

section
    open function set

    variables {X Y : Type}
    variable  f : X → Y
    variables A B : set X

    example : f '' (A ∪ B) = f '' A ∪ f '' B :=
    eq_of_subset_of_subset
    (assume y,
        assume h1 : y ∈ f '' (A ∪ B),
        exists.elim h1 $
        assume x h,
        have h2 : x ∈ A ∪ B, from h.left,
        have h3 : f x = y, from h.right,
        or.elim h2
        (assume h4 : x ∈ A,
            have h5 : y ∈ f '' A, from ⟨x, h4, h3⟩,
            show y ∈ f '' A ∪ f '' B, from or.inl h5)
        (assume h4 : x ∈ B,
            have h5 : y ∈ f ''  B, from ⟨x, h4, h3⟩,
            show y ∈ f '' A ∪ f '' B, from or.inr h5))
    (assume y,
        assume h2 : y ∈ f '' A ∪ f '' B,
        or.elim h2
        (assume h3 : y ∈ f '' A,
            exists.elim h3 $
            assume x h,
            have h4 : x ∈ A, from h.left,
            have h5 : f x = y, from h.right,
            have h6 : x ∈ A ∪ B, from or.inl h4,
            show y ∈ f '' (A ∪ B), from ⟨x, h6, h5⟩)
        (assume h3 : y ∈ f '' B,
            exists.elim h3 $
            assume x h,
            have h4 : x ∈ B, from h.left,
            have h5 : f x = y, from h.right,
            have h6 : x ∈ A ∪ B, from or.inr h4,
            show y ∈ f '' (A ∪ B), from ⟨x, h6, h5⟩))

    -- remember, x ∈ A ∩ B is the same as x ∈ A ∧ x ∈ B
    example (x : X) (h1 : x ∈ A) (h2 : x ∈ B) : x ∈ A ∩ B :=
    and.intro h1 h2

    example (x : X) (h1 : x ∈ A ∩ B) : x ∈ A :=
    and.left h1

    -- Fill in the proof below.
    -- (It should take about 8 lines.)

    example : f '' (A ∩ B) ⊆ f '' A ∩ f '' B :=
        assume y,
            assume h1 : y ∈ f '' (A ∩ B),
            show y ∈ f '' A ∩ f '' B, from
                begin
                    cases h1 with x h2,
                    cases h2 with h3 h4,
                    cases h3 with h5 h6,
                    apply and.intro,
                    have h7, from exists.intro x (and.intro h5 h4),
                    exact h7,
                    have h7, from exists.intro x (and.intro h6 h4),
                    exact h7
                end

    example : f '' (A ∩ B) ⊆ f '' A ∩ f '' B :=
        assume y,
            assume h1 : y ∈ f '' (A ∩ B),
            have h3 : ∃ x, x ∈ A ∧ f x = y, from exists.elim h1
                (assume x (h5 : x ∈ A ∩ B ∧ f x = y),
                exists.intro x (and.intro h5.left.left h5.right)),
            have h4 : ∃ x, x ∈ B ∧ f x = y, from exists.elim h1
                (assume x (h5 : x ∈ A ∩ B ∧ f x = y),
                exists.intro x (and.intro h5.left.right h5.right)),
            show y ∈ f '' A ∩ f '' B, from and.intro h3 h4
end


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

import ring_theory.roots_of_unity
import analysis.special_functions.trigonometric
import analysis.special_functions.pow

/-!
# Complex roots of unity

In this file we show that the `n`-th complex roots of unity
are exactly the complex numbers `e ^ (2 * real.pi * complex.I * (i / n))` for `i ∈ finset.range n`.

## Main declarations

* `complex.mem_roots_of_unity`: the complex `n`-th roots of unity are exactly the
  complex numbers of the form `e ^ (2 * real.pi * complex.I * (i / n))` for some `i < n`.
* `complex.card_roots_of_unity`: the number of `n`-th roots of unity is exactly `n`.

-/

namespace complex

open polynomial real
open_locale nat real

lemma is_primitive_root_exp_of_coprime (i n : ℕ) (h0 : n ≠ 0) (hi : i.coprime n) :
  is_primitive_root (exp (2 * π * I * (i / n))) n :=
begin
  rw is_primitive_root.iff_def,
  simp only [← exp_nat_mul, exp_eq_one_iff],
  have hn0 : (n : ℂ) ≠ 0, by exact_mod_cast h0,
  split,
  { use i,
    field_simp [hn0, mul_comm (i : ℂ), mul_comm (n : ℂ)] },
  { simp only [hn0, mul_right_comm _ _ ↑n, mul_left_inj' two_pi_I_ne_zero, ne.def, not_false_iff,
      mul_comm _ (i : ℂ), ← mul_assoc _ (i : ℂ), exists_imp_distrib] with field_simps,
    norm_cast,
    rintro l k hk,
    have : n ∣ i * l,
    { rw [← int.coe_nat_dvd, hk], apply dvd_mul_left },
    exact hi.symm.dvd_of_dvd_mul_left this }
end

lemma is_primitive_root_exp (n : ℕ) (h0 : n ≠ 0) : is_primitive_root (exp (2 * π * I / n)) n :=
by simpa only [nat.cast_one, one_div]
  using is_primitive_root_exp_of_coprime 1 n h0 n.coprime_one_left

lemma is_primitive_root_iff (ζ : ℂ) (n : ℕ) (hn : n ≠ 0) :
  is_primitive_root ζ n ↔ (∃ (i < (n : ℕ)) (hi : i.coprime n), exp (2 * π * I * (i / n)) = ζ) :=
begin
  have hn0 : (n : ℂ) ≠ 0 := by exact_mod_cast hn,
  split, swap,
  { rintro ⟨i, -, hi, rfl⟩, exact is_primitive_root_exp_of_coprime i n hn hi },
  intro h,
  obtain ⟨i, hi, rfl⟩ :=
    (is_primitive_root_exp n hn).eq_pow_of_pow_eq_one h.pow_eq_one (nat.pos_of_ne_zero hn),
  refine ⟨i, hi, ((is_primitive_root_exp n hn).pow_iff_coprime (nat.pos_of_ne_zero hn) i).mp h, _⟩,
  rw [← exp_nat_mul],
  congr' 1,
  field_simp [hn0, mul_comm (i : ℂ)]
end

/-- The complex `n`-th roots of unity are exactly the
complex numbers of the form `e ^ (2 * real.pi * complex.I * (i / n))` for some `i < n`. -/
lemma mem_roots_of_unity (n : ℕ+) (x : units ℂ) :
  x ∈ roots_of_unity n ℂ ↔ (∃ i < (n : ℕ), exp (2 * π * I * (i / n)) = x) :=
begin
  rw [mem_roots_of_unity, units.ext_iff, units.coe_pow, units.coe_one],
  have hn0 : (n : ℂ) ≠ 0 := by exact_mod_cast (n.ne_zero),
  split,
  { intro h,
    obtain ⟨i, hi, H⟩ : ∃ i < (n : ℕ), exp (2 * π * I / n) ^ i = x,
    { simpa only using (is_primitive_root_exp n n.ne_zero).eq_pow_of_pow_eq_one h n.pos },
    refine ⟨i, hi, _⟩,
    rw [← H, ← exp_nat_mul],
    congr' 1,
    field_simp [hn0, mul_comm (i : ℂ)] },
  { rintro ⟨i, hi, H⟩,
    rw [← H, ← exp_nat_mul, exp_eq_one_iff],
    use i,
    field_simp [hn0, mul_comm ((n : ℕ) : ℂ), mul_comm (i : ℂ)] }
end

lemma card_roots_of_unity (n : ℕ+) : fintype.card (roots_of_unity n ℂ) = n :=
(is_primitive_root_exp n n.ne_zero).card_roots_of_unity

lemma card_primitive_roots (k : ℕ) (h : k ≠ 0) : (primitive_roots k ℂ).card = φ k :=
(is_primitive_root_exp k h).card_primitive_roots (nat.pos_of_ne_zero h)

end complex


-- Ley_de_absorcion_2.lean
-- Si R es un retículo y x, y ∈ R, entonces x ⊔ (x ⊓ y) = x
-- José A. Alonso Jiménez <https://jaalonso.github.io>
-- Sevilla, 21-octubre-2022
-- ---------------------------------------------------------------------

import order.lattice
variables {R : Type*} [lattice R]
variables x y : R

-- 1ª demostración
-- ===============

example : x ⊔ (x ⊓ y) = x :=
begin
  have h1 : x ⊔ (x ⊓ y) ≤ x, finish,
  have h2 : x ≤ x ⊔ (x ⊓ y), finish,
  show x ⊔ (x ⊓ y) = x,
    by exact le_antisymm h1 h2,
end

-- 2ª demostración
-- ===============

example : x ⊔ (x ⊓ y) = x :=
begin
  have h1 : x ⊔ (x ⊓ y) ≤ x,
  { have h1a : x ≤ x := le_rfl,
    have h1b : x ⊓ y ≤ x := inf_le_left,
    show x ⊔ (x ⊓ y) ≤ x,
      by exact sup_le h1a h1b,
  },
  have h2 : x ≤ x ⊔ (x ⊓ y) := le_sup_left,
  show x ⊔ (x ⊓ y) = x,
    by exact le_antisymm h1 h2,
end

-- 3ª demostración
-- ===============

example : x ⊔ (x ⊓ y) = x :=
begin
  apply le_antisymm,
  { apply sup_le,
    { apply le_refl },
    { apply inf_le_left }},
  { apply le_sup_left },
end

-- 4ª demostración
-- ===============

example : x ⊔ (x ⊓ y) = x :=
-- by library_search
sup_inf_self

-- 4ª demostración
-- ===============

example : x ⊔ (x ⊓ y) = x :=
-- by hint
by simp


import data.complex.basic data.real.cau_seq tactic.norm_num data.nat.basic tactic.ring algebra.archimedean .limits data.nat.choose
section a
open real nat is_absolute_value finset
noncomputable theory
local attribute [instance, priority 0] classical.prop_decidable

lemma pow_inv' {α : Type*} [discrete_field α] (a : α) (n : ℕ) : (a ^ n)⁻¹ = a⁻¹ ^ n :=
by induction n; simp [_root_.pow_succ, *, mul_inv', mul_comm]

lemma pow_incrs_of_gt_one {α : Type*}  [linear_ordered_semiring α] {x : α} {n m : ℕ}
  (h1x : 1 < x) (hnm : n < m) : x ^ n < x ^ m :=
begin
  rw [← nat.sub_add_cancel hnm, _root_.pow_add, _root_.pow_succ, ← mul_assoc],
  refine (lt_mul_iff_one_lt_left (pow_pos (lt_trans (by norm_num) h1x) _)).2 _,
  rw ← one_mul (1 : α),
  refine mul_lt_mul' (one_le_pow_of_one_le (le_of_lt h1x) _) h1x (by norm_num)
    (pow_pos (lt_trans (by norm_num) h1x) _)
end

lemma pow_dcrs_of_lt_one_of_pos {α : Type*} [discrete_linear_ordered_field α] {x : α} {n m : ℕ}
  (hx1 : x < 1) (h0x : 0 < x) (hnm : n < m) : x ^ m < x ^ n :=
begin
  refine (inv_lt_inv _ _).1 _,
  { exact pow_pos h0x _ },
  { exact pow_pos h0x _ },
  { rw [pow_inv', pow_inv'],
    refine pow_incrs_of_gt_one (one_lt_inv h0x hx1) hnm }
end

open finset

@[simp] lemma sum_range_zero {α : Type*} [add_comm_monoid α] (f : ℕ → α) :
  (range 0).sum f = 0 := rfl

lemma sum_range_succ {α : Type*} [add_comm_monoid α] (f : ℕ → α) (n : ℕ) :
  (range (succ n)).sum f = f n + (range n).sum f := by simp

lemma sum_range_succ' {α : Type*} [add_comm_monoid α] (f : ℕ → α) :
  ∀ n : ℕ, (range (nat.succ n)).sum f = (range n).sum (f ∘ nat.succ) + f 0
| 0       := by simp
| (n + 1) := by rw [sum_range_succ (λ m, f (nat.succ m)), add_assoc, ← sum_range_succ'];
  exact sum_range_succ _ _

/-- The binomial theorem -/
theorem add_pow {α : Type*} [comm_semiring α] (x y : α) : ∀ n : ℕ,
    (x + y) ^ n = (range (succ n)).sum (λ m, x ^ m * y ^ (n - m) * choose n m)
| 0        := by simp
| (succ n) :=
have h₁ : x * (x ^ n * y ^ (n - n) * choose n n) =
    x ^ succ n * y ^ (succ n - succ n) * choose (succ n) (succ n),
  by simp [_root_.pow_succ, mul_assoc, mul_comm, mul_left_comm],
have  h₂ : y * (x^0 * y^(n - 0) * choose n 0) = x^0 * y^(succ n - 0) * choose (succ n) 0,
  by simp [_root_.pow_succ, mul_assoc, mul_comm, mul_left_comm],
have h₃ : (range n).sum (λ m, x * (x ^ m * y ^ (n - m) * choose n m) + y *
    (x ^ succ m * y ^ (n - succ m) * choose n (succ m)))
    = (range n).sum (λ m, x ^ succ m * y ^ (succ n - succ m) * ↑(choose (succ n) (succ m))),
  from finset.sum_congr rfl $ λ m hm,
    begin
      simp only [mul_assoc, mul_left_comm y, mul_left_comm (y ^ (n - succ m)), mul_comm y],
      rw [← _root_.pow_succ', add_one, ← succ_sub (mem_range.1 hm)],
      simp [choose_succ_succ, mul_comm, mul_assoc, mul_left_comm, add_mul, mul_add, _root_.pow_succ]
    end,
by rw [_root_.pow_succ, add_pow, add_mul, finset.mul_sum, finset.mul_sum, sum_range_succ, sum_range_succ',
    sum_range_succ, sum_range_succ', add_assoc, ← add_assoc ((range n).sum _),
    ← finset.sum_add_distrib, h₁, h₂, h₃]

lemma geo_series_eq {α : Type*} [field α] {x : α} (n : ℕ) (hx1 : x ≠ 1) :
  (range n).sum (λ m, x ^ m) = (1 - x ^ n) / (1 - x) :=
have 1 - x ≠ 0 := mt sub_eq_zero_iff_eq.1 hx1.symm,
begin
  induction n with n ih,
  { simp },
  { rw [sum_range_succ, ← mul_div_cancel (x ^ n) this, ih, ← add_div, _root_.pow_succ],
    refine congr_fun (congr_arg _ _) _,
    ring }
end

variables {α : Type*} {β : Type*}
  [discrete_linear_ordered_field α] [archimedean α] {abv : β → α}

lemma forall_ge_le_of_forall_le_succ {α : Type*} [preorder α] (f : ℕ → α) {m : ℕ}
  (h : ∀ n ≥ m, f (succ n) ≤ f n) : ∀ l, ∀ k ≥ m, k ≤ l → f l ≤ f k :=
begin
  assume l k hkm hkl,
  generalize hp : l - k = p,
  have : l = k + p := add_comm p k ▸ (nat.sub_eq_iff_eq_add hkl).1 hp,
  subst this,
  clear hkl hp,
  induction p with p ih,
  { simp },
  { exact le_trans (h _ (le_trans hkm (nat.le_add_right _ _))) ih }
end

lemma is_cau_of_dcrs_bounded {α : Type*} [discrete_linear_ordered_field α]
  [archimedean α] (f : ℕ → α)
  {a : α} {m : ℕ} (ham : ∀ n ≥ m, abs (f n) ≤ a)
  (hnm : ∀ n ≥ m, f (succ n) ≤ f n) : is_cau_seq abs f :=
λ ε ε0,
let ⟨k, hk⟩ := archimedean.arch a ε0 in
have h : ∃ l, ∀ n ≥ m, a - add_monoid.smul l ε < f n :=
  ⟨k + k + 1, λ n hnm, lt_of_lt_of_le
    (show a - add_monoid.smul (k + (k + 1)) ε < -abs (f n),
      from lt_neg.1 $ lt_of_le_of_lt (ham n hnm) (begin
        rw [neg_sub, lt_sub_iff_add_lt, add_monoid.add_smul],
        exact add_lt_add_of_le_of_lt hk (lt_of_le_of_lt hk
          (lt_add_of_pos_left _ ε0)),
      end))
    (neg_le.2 $ (abs_neg (f n)) ▸ le_abs_self _)⟩,
let l := nat.find h in
have hl : ∀ (n : ℕ), n ≥ m → f n > a - add_monoid.smul l ε := nat.find_spec h,
have hl0 : l ≠ 0 := λ hl0, not_lt_of_ge (ham m (le_refl _))
  (lt_of_lt_of_le (by have := hl m (le_refl m); simpa [hl0] using this) (le_abs_self (f m))),
begin
  cases classical.not_forall.1
    (nat.find_min h (pred_lt hl0)) with i hi,
  rw [not_imp, not_lt] at hi,
  existsi i,
  assume j hj,
  have hfij : f j ≤ f i := forall_ge_le_of_forall_le_succ f hnm _ _ hi.1 hj,
  rw [abs_of_nonpos (sub_nonpos.2 hfij), neg_sub, sub_lt_iff_lt_add'],
  exact calc f i ≤ a - add_monoid.smul (pred l) ε : hi.2
    ... = a - add_monoid.smul l ε + ε :
      by conv {to_rhs, rw [← succ_pred_eq_of_pos (nat.pos_of_ne_zero hl0), succ_smul',
        sub_add, add_sub_cancel] }
    ... < f j + ε : add_lt_add_right (hl j (le_trans hi.1 hj)) _
end

lemma is_cau_of_incrs_bounded {α : Type*} [discrete_linear_ordered_field α]
  [archimedean α] (f : ℕ → α)
  {a : α} {m : ℕ} (ham : ∀ n ≥ m, abs (f n) ≤ a)
  (hnm : ∀ n ≥ m, f n ≤ f (succ n)) : is_cau_seq abs f :=
begin
  refine @eq.rec_on (ℕ → α) _ (is_cau_seq abs) _ _
    (-⟨_, @is_cau_of_dcrs_bounded _ _ _ (λ n, -f n) a m (by simpa) (by simpa)⟩ : cau_seq α abs).2,
  ext,
  exact neg_neg _
end

lemma series_cau_of_abv_le_cau {α : Type*} {β : Type*} [discrete_linear_ordered_field α] [ring β] {f : ℕ → β}
    {g : ℕ → α} {abv : β → α} [is_absolute_value abv] (n : ℕ) : (∀ m, n ≤ m → abv (f m) ≤ g m) →
    is_cau_seq abs (λ n, (range n).sum g) → is_cau_seq abv (λ n, (range n).sum f) := begin
  assume hm hg ε ε0,cases hg (ε / 2) (div_pos ε0 (by norm_num)) with i hi,
  existsi max n i,
  assume j ji,
  have hi₁ := hi j (le_trans (le_max_right n i) ji),
  have hi₂ := hi (max n i) (le_max_right n i),
  have sub_le := abs_sub_le ((range j).sum g) ((range i).sum g) ((range (max n i)).sum g),
  have := add_lt_add hi₁ hi₂,
  rw abs_sub ((range (max n i)).sum g) at this,
  rw add_halves ε at this,
  refine lt_of_le_of_lt (le_trans (le_trans _ (le_abs_self _)) sub_le) this,
  generalize hk : j - max n i = k,clear this hi₂ hi₁ hi ε0 ε hg sub_le,
  rw nat.sub_eq_iff_eq_add ji at hk,rw hk, clear hk ji j,
  induction k with k' hi,
  { simp [abv_zero abv] },
  { dsimp at *,
    rw [succ_add, sum_range_succ, sum_range_succ, add_assoc, add_assoc],
    refine le_trans (abv_add _ _ _) _,
    exact add_le_add (hm _ (le_add_of_nonneg_of_le (nat.zero_le _) (le_max_left _ _))) hi },
end

lemma pow_abv {α β : Type*} [discrete_linear_ordered_field α] [domain β] (abv : β → α) [is_absolute_value abv]
  (a : β) (n : ℕ) : abv (a ^ n) = abv a ^ n :=
by induction n; simp [abv_mul abv, _root_.pow_succ, abv_one abv, *]

lemma series_cau_of_abv_cau {α : Type*} {β : Type*} [discrete_linear_ordered_field α] [ring β] {abv : β → α} {f : ℕ → β}
    [is_absolute_value abv] : is_cau_seq abs (λ m, (range m).sum (λ n, abv (f n))) → is_cau_seq abv (λ m, (range m).sum f) :=
   λ h, series_cau_of_abv_le_cau 0 (λ n h, le_refl _) h

lemma geo_series_cau {α β : Type*} [discrete_linear_ordered_field α] [archimedean α] [field β]
  {abv : β → α} [is_absolute_value abv]
   (x : β) (hx1 : abv x < 1) : is_cau_seq abv (λ n, (range n).sum (λ m, x ^ m)) :=
have hx1' : abv x ≠ 1 := λ h, by simpa [h, lt_irrefl] using hx1,
series_cau_of_abv_cau
begin
  simp only [pow_abv abv, geo_series_eq _ hx1'] {eta := ff},
  refine @is_cau_of_incrs_bounded _ _ _ _ ((1 : α) / (1 - abv x)) 0 _ _,
  { assume n hn,
    rw abs_of_nonneg ,
    refine div_le_div_of_le_of_pos (sub_le_self _ (pow_abv abv x n ▸ abv_nonneg _ _))
      (sub_pos.2 hx1),
    refine div_nonneg (sub_nonneg.2 _) (sub_pos.2 hx1),
    clear hn,
    induction n with n ih,
    { simp },
    { rw [_root_.pow_succ, ← one_mul (1 : α)],
      refine mul_le_mul (le_of_lt hx1) ih (pow_abv abv x n ▸ abv_nonneg _ _) (by norm_num) } },
  { assume n hn,
    refine div_le_div_of_le_of_pos (sub_le_sub_left _ _) (sub_pos.2 hx1),
    rw [← one_mul (_ ^ n), _root_.pow_succ],
    exact mul_le_mul_of_nonneg_right (le_of_lt hx1) (pow_nonneg (abv_nonneg _ _) _) }
end

lemma geo_series_const_cau {α : Type*} [discrete_linear_ordered_field α] [archimedean α] (a x : α) :
  abs x < 1 → is_cau_seq abs (λ m, (range m).sum (λ n, a * x ^ n)) :=
λ hx1, begin
  have : is_cau_seq abs (λ m, a * (range m).sum (λ n, x ^ n)) := (cau_seq.const abs a * ⟨_, geo_series_cau x hx1⟩).2,
  simpa only [mul_sum] using this
end

-- The form of ratio test with  0 ≤ r < 1, and abv (f (succ m)) ≤ r * abv (f m) handled zero terms of series the best
lemma series_ratio_test {α : Type*} {β : Type*} [discrete_linear_ordered_field α] [ring β]
    [archimedean α] {abv : β → α} [is_absolute_value abv] {f : ℕ → β} (n : ℕ) (r : α) :
    0 ≤ r → r < 1 → (∀ m, n ≤ m → abv (f (succ m)) ≤ r * abv (f m)) → is_cau_seq abv (λ m, (range m).sum f)
  := begin
  assume r0 r1 h,
  refine series_cau_of_abv_le_cau (succ n) _ (geo_series_const_cau (abv (f (succ n)) * r⁻¹ ^ (succ n)) r _),
  assume m mn,
  generalize hk : m - (succ n) = k,rw nat.sub_eq_iff_eq_add mn at hk,
  cases classical.em (r = 0) with r_zero r_pos,have m_pos := lt_of_lt_of_le (succ_pos n) mn,
  have := pred_le_pred mn,simp at this,
  have := h (pred m) this,simp[r_zero,succ_pred_eq_of_pos m_pos] at this,
  refine le_trans this _,refine mul_nonneg _ _,
  refine mul_nonneg (abv_nonneg _ _) (pow_nonneg (inv_nonneg.mpr r0) _),exact pow_nonneg r0 _,
  replace r_pos : 0 < r,cases lt_or_eq_of_le r0 with h h,exact h,exact absurd h.symm r_pos,
  revert m n,
  induction k with k' hi,assume m n h mn hk,
  rw [hk,zero_add,mul_right_comm,←pow_inv',←div_eq_mul_inv,mul_div_cancel],
  exact (ne_of_lt (pow_pos r_pos _)).symm,
  assume m n h mn hk,rw [hk,succ_add],
  have kn : k' + (succ n) ≥ (succ n), rw ←zero_add (succ n),refine add_le_add _ _,exact zero_le _,simp,
  replace hi := hi (k' + (succ n)) n h kn rfl,
  rw [(by simp [_root_.pow_succ'] : r ^ (succ (k' + succ n)) = r ^ (k' + succ n) * r),←mul_assoc],
  replace h := h (k' + succ n) (le_of_succ_le kn),rw mul_comm at h,
  exact le_trans h (mul_le_mul_of_nonneg_right hi r0),
  rwa abs_of_nonneg r0,
end

lemma sum_range_diag_flip {α : Type*} [add_comm_monoid α] (n : ℕ) (f : ℕ → ℕ → α) :
    (range n).sum (λ m, (range (m + 1)).sum (λ k, f k (m - k))) =
    (range n).sum (λ m, (range (n - m)).sum (f m)) :=
have h₁ : ((range n).sigma (range ∘ succ)).sum
    (λ (a : Σ m, ℕ), f (a.2) (a.1 - a.2)) =
    (range n).sum (λ m, (range (m + 1)).sum
    (λ k, f k (m - k))) := sum_sigma,
have h₂ : ((range n).sigma (λ m, range (n - m))).sum (λ a : Σ (m : ℕ), ℕ, f (a.1) (a.2)) =
    (range n).sum (λ m, sum (range (n - m)) (f m)) := sum_sigma,
h₁ ▸ h₂ ▸ sum_bij
(λ a _, ⟨a.2, a.1 - a.2⟩)
(λ a ha, have h₁ : a.1 < n := mem_range.1 (mem_sigma.1 ha).1,
  have h₂ : a.2 < succ a.1 := mem_range.1 (mem_sigma.1 ha).2,
    mem_sigma.2 ⟨mem_range.2 (lt_of_lt_of_le h₂ h₁),
    mem_range.2 ((nat.sub_lt_sub_right_iff (le_of_lt_succ h₂)).2 h₁)⟩)
(λ _ _, rfl)
(λ ⟨a₁, a₂⟩ ⟨b₁, b₂⟩ ha hb h,
  have ha : a₁ < n ∧ a₂ ≤ a₁ :=
      ⟨mem_range.1 (mem_sigma.1 ha).1, le_of_lt_succ (mem_range.1 (mem_sigma.1 ha).2)⟩,
  have hb : b₁ < n ∧ b₂ ≤ b₁ :=
      ⟨mem_range.1 (mem_sigma.1 hb).1, le_of_lt_succ (mem_range.1 (mem_sigma.1 hb).2)⟩,
  have h : a₂ = b₂ ∧ _ := sigma.mk.inj h,
  have h' : a₁ = b₁ - b₂ + a₂ := (nat.sub_eq_iff_eq_add ha.2).1 (eq_of_heq h.2),
  sigma.mk.inj_iff.2
    ⟨nat.sub_add_cancel hb.2 ▸ h'.symm ▸ h.1 ▸ rfl,
    (heq_of_eq h.1)⟩)
(λ ⟨a₁, a₂⟩ ha,
  have ha : a₁ < n ∧ a₂ < n - a₁ :=
      ⟨mem_range.1 (mem_sigma.1 ha).1, (mem_range.1 (mem_sigma.1 ha).2)⟩,
  ⟨⟨a₂ + a₁, a₁⟩, ⟨mem_sigma.2 ⟨mem_range.2 ((nat.lt_sub_right_iff_add_lt
      (le_of_lt ha.1)).1 ha.2),
    mem_range.2 (lt_succ_of_le (le_add_left _ _))⟩,
  sigma.mk.inj_iff.2 ⟨rfl, heq_of_eq (nat.add_sub_cancel _ _).symm⟩⟩⟩)

lemma abv_sum_le_sum_abv {α β γ : Type*} [discrete_linear_ordered_field α] [ring β]
  (abv : β → α) [is_absolute_value abv] (f : γ → β) (s : finset γ) : abv (s.sum f) ≤ s.sum (abv ∘ f) :=
finset.induction_on s (by simp [abv_zero abv]) $ λ a s has ih,
by rw [sum_insert has, sum_insert has];
  exact le_trans (abv_add abv _ _) (add_le_add_left ih _)

lemma sum_nonneg {α β : Type*} [ordered_comm_monoid α] {f : β → α} {s : finset β} :
  (∀ x ∈ s, 0 ≤ f x) → 0 ≤ s.sum f :=
finset.induction_on s (by simp) $ λ a s has ih h, begin
  rw [sum_insert has],
  exact add_nonneg' (h a (by simp)) (ih (λx hx, by simp *)),
end

lemma neg_sum {α β : Type*} [add_comm_group α] {f : β → α} {s : finset β} :
  -s.sum f = s.sum (λ x, -f x) :=
finset.induction_on s (by simp) (by simp {contextual := tt})

@[simp] lemma filter_true {α : Type*} (s : finset α) : s.filter (λ _, true) = s :=
finset.ext.2 $ by simp

lemma sum_range_sub_sum_range {α : Type*} [add_comm_group α] {f : ℕ → α}
  {n m : ℕ} (hnm : n ≤ m) : (range m).sum f - (range n).sum f =
  ((range m).filter (λ k, n ≤ k)).sum f :=
begin
  rw [← sum_sdiff (@filter_subset _ (λ k, n ≤ k) _ (range m)),
    sub_eq_iff_eq_add, ← eq_sub_iff_add_eq, add_sub_cancel'],
  refine finset.sum_congr
    (finset.ext.2 $ λ a, ⟨λ h, by simp at *; finish,
    λ h, have ham : a < m := lt_of_lt_of_le (mem_range.1 h) hnm,
      by simp * at *⟩)
    (λ _ _, rfl),
end

lemma series_cauchy_prod {α β : Type*} [discrete_linear_ordered_field α] [ring β] {a b : ℕ → β}
  {abv : β → α} [is_absolute_value abv] : is_cau_seq abs (λ m, (range m).sum (λ n, abv (a n))) → is_cau_seq abv (λ m, (range m).sum b) →
  ∀ ε : α, 0 < ε → ∃ i : ℕ, ∀ j ≥ i, abv ((range j).sum a * (range j).sum b - (range j).sum (λ n,
  (range (n + 1)).sum (λ m, a m * b (n - m))) ) < ε :=
begin
-- slightly adapted version of theorem 9.4.7 from "The Real Numbers and Real Analysis", Ethan D. Bloch
  assume ha hb ε ε0,
  cases cau_seq.bounded ⟨_, hb⟩ with Q hQ,simp at hQ,
  cases cau_seq.bounded ⟨_, ha⟩ with P hP,simp at hP,
  have P0 : 0 < P,exact lt_of_le_of_lt (abs_nonneg _) (hP 0),
  have Pε0 := div_pos ε0 (mul_pos (show (2 : α) > 0, from by norm_num) P0),
  cases cau_seq.cauchy₂ ⟨_, hb⟩ Pε0 with N hN,simp at hN,
  have Qε0 := div_pos ε0 (mul_pos (show (4 : α) > 0, from by norm_num) (lt_of_le_of_lt (abv_nonneg _ _) (hQ 0))),
  cases cau_seq.cauchy₂ ⟨_, ha⟩ Qε0 with M hM,simp at hM,
  existsi 2 * (max N M + 1),
  assume K hK,
  have := sum_range_diag_flip K (λ m n, a m * b n),
  dsimp at this,
  rw this,
  have : (λ (i : ℕ), (range (K - i)).sum (λ (k : ℕ), a i * b k)) = (λ (i : ℕ), a i * (range (K - i)).sum b),
    by simp [finset.mul_sum],
  rw this,clear this,
  have : (range K).sum (λ (i : ℕ), a i * (range (K - i)).sum b) =
    (range K).sum (λ (i : ℕ), a i * ((range (K - i)).sum b - (range K).sum b))
    + (range K).sum (λ i, a i * (range K).sum b),
    {rw ←sum_add_distrib,simp[(mul_add _ _ _).symm]},
  rw this, clear this,
  rw sum_mul, simp,
  rw abv_neg abv,
  refine lt_of_le_of_lt (abv_sum_le_sum_abv _ _ _) _,
  simp [abv_mul abv],
  suffices : (range (max N M + 1)).sum (λ (i : ℕ), abv (a i) * abv ((range (K - i)).sum b - (range K).sum b)) +
    ((range K).sum (λ (i : ℕ), abv (a i) * abv ((range (K - i)).sum b - (range K).sum b)) -(range (max N M + 1)).sum
     (λ (i : ℕ),
    abv (a i) * abv ((range (K - i)).sum b - (range K).sum b))) < ε / (2 * P) * P + ε / (4 * Q) * (2 * Q),
  { simp [(div_div_eq_div_mul _ _ _).symm] at this,
    rwa[div_mul_cancel _ (ne_of_lt P0).symm,(by norm_num : (4 : α) = 2 * 2),←div_div_eq_div_mul,mul_comm (2 : α),←_root_.mul_assoc,
    div_mul_cancel _ (ne_of_lt (lt_of_le_of_lt (abv_nonneg _ _) (hQ 0))).symm,div_mul_cancel,add_halves] at this,
    norm_num},
  refine add_lt_add _ _,
  {have : (range (max N M + 1)).sum (λ (i : ℕ), abv (a i) *
    abv ((range (K - i)).sum b - (range K).sum b)) ≤ (range (max N M + 1)).sum
  (λ (i : ℕ), abv (a i) * (ε / (2 * P))) := by
    {refine sum_le_sum _,assume m mJ,refine mul_le_mul_of_nonneg_left _ _,
      {refine le_of_lt (hN (K - m) K _ _),{
      refine nat.le_sub_left_of_add_le (le_trans _ hK),
      rw[succ_mul,_root_.one_mul],
      exact add_le_add (le_of_lt (mem_range.1 mJ)) (le_trans (le_max_left _ _) (le_of_lt (lt_add_one _)))},
      {refine le_trans _ hK,rw ←_root_.one_mul N,
      refine mul_le_mul (by norm_num) (by rw _root_.one_mul;exact le_trans (le_max_left _ _)
      (le_of_lt (lt_add_one _))) (zero_le _) (zero_le _)}},
      exact abv_nonneg abv _},
  refine lt_of_le_of_lt this _,
  rw [← sum_mul, mul_comm],
  specialize hP (max N M + 1),rwa abs_of_nonneg at hP,
  exact (mul_lt_mul_left Pε0).mpr hP,
  exact sum_nonneg (λ x h, abv_nonneg abv _)},
  {have hNMK : max N M + 1 < K := by
    {refine lt_of_lt_of_le _ hK,
    rw [succ_mul,_root_.one_mul,←add_zero (max N M + 1)],
    refine add_lt_add_of_le_of_lt (le_refl _) _,rw add_zero,
    refine add_pos_of_nonneg_of_pos (zero_le _) (by norm_num)},
  rw sum_range_sub_sum_range (le_of_lt hNMK),
  exact calc sum (filter (λ (k : ℕ), max N M + 1 ≤ k) (range K))
      (λ (i : ℕ), abv (a i) * abv (sum (range (K - i)) b - sum (range K) b))
      ≤ sum (filter (λ (k : ℕ), max N M + 1 ≤ k) (range K)) (λ i, abv (a i) * (2 * Q)) :
    sum_le_sum (begin
      assume n hn,
      refine mul_le_mul_of_nonneg_left _ (abv_nonneg _ _),
      rw sub_eq_add_neg,
      refine le_trans (abv_add _ _ _) _,
      rw [two_mul, abv_neg abv],
      refine add_le_add (le_of_lt (hQ _)) (le_of_lt (hQ _)),
    end)
    ... < _ : begin
      rw [← sum_mul, ← sum_range_sub_sum_range (le_of_lt hNMK)],
      refine (mul_lt_mul_right _).2 _,
      rw two_mul,
      refine add_pos (lt_of_le_of_lt (abv_nonneg _ _) (hQ 0))
        (lt_of_le_of_lt (abv_nonneg _ _) (hQ 0)),
      refine lt_of_le_of_lt (le_abs_self _) _,
      refine hM _ _ (le_trans (le_succ_of_le (le_max_right _ _)) (le_of_lt hNMK))
        (le_succ_of_le (le_max_right _ _))
     end }
end

end a

open nat finset
lemma complex.exp_series_abs_cau (z : ℂ) : is_cau_seq abs (λ n, (range n).sum (λ m, complex.abs (z ^ m / fact m))) := begin
  cases exists_nat_gt (complex.abs z) with n hn,
  have n_pos : (0 : ℝ) < n := lt_of_le_of_lt (complex.abs_nonneg _) hn,
  refine series_ratio_test n (complex.abs z / n) _ _ _,exact div_nonneg_of_nonneg_of_pos (complex.abs_nonneg _) n_pos,rwa [div_lt_iff n_pos,one_mul],
  assume m mn,rw [abs_of_nonneg (complex.abs_nonneg _),abs_of_nonneg (complex.abs_nonneg _)],
  unfold fact,simp only [_root_.pow_succ, complex.abs_div,complex.abs_mul,div_eq_mul_inv,mul_inv',
    nat.cast_mul,complex.abs_inv],
  have : complex.abs z * complex.abs (z ^ m) * ((complex.abs ↑(fact m))⁻¹ * (complex.abs ↑(succ m))⁻¹) = complex.abs z *
    complex.abs (z ^ m) * (complex.abs ↑(fact m))⁻¹ * (complex.abs ↑(succ m))⁻¹,ring,rw this,
  have : complex.abs z * (↑n)⁻¹ * (complex.abs (z ^ m) * (complex.abs ↑(fact m))⁻¹) = complex.abs z * complex.abs (z ^ m) * (complex.abs ↑(fact m))⁻¹ * (↑n)⁻¹,ring,
  rw this,
  rw[(by simp : (succ m : ℂ) = ((succ m : ℝ) : ℂ)),complex.abs_of_nonneg],
  refine mul_le_mul_of_nonneg_left _ _,
  rw [inv_le_inv,nat.cast_le],exact le_succ_of_le mn,
  rw [←nat.cast_zero,nat.cast_lt],exact succ_pos _,exact n_pos,rw[←complex.abs_inv,←complex.abs_mul,←complex.abs_mul],
  exact complex.abs_nonneg _,rw[←nat.cast_zero,nat.cast_le],exact zero_le _,
end

lemma complex.exp_series_cau (z : ℂ) : is_cau_seq complex.abs (λ n, (range n).sum (λ m, z ^ m / fact m)) :=
  series_cau_of_abv_cau (complex.exp_series_abs_cau z)

def exp' (z : ℂ) : cau_seq ℂ complex.abs := ⟨_, complex.exp_series_cau z⟩

open complex
def exp (z : ℂ) : ℂ := complex.lim (exp' z)

def sin (z : ℂ) : ℂ := (exp (I * z) - exp (-I * z)) / (2 * I)

def cos (z : ℂ) : ℂ := (exp (I * z) + exp (-I * z)) / 2

def tan (z : ℂ) : ℂ := sin z / cos z

def sinh (z : ℂ) : ℂ := (exp z - exp (-z)) / 2

def cosh (z : ℂ) : ℂ := (exp z + exp (-z)) / 2

def tanh (z : ℂ) : ℂ := sinh z / cosh z

@[simp] lemma exp_zero : exp 0 = 1 := begin
  unfold exp exp',
  refine lim_eq_of_equiv_const _,
  assume ε ε0,
  existsi 1,
  assume j hj,
  dsimp [exp'],
  suffices : complex.abs (sum (range j) (λ (m : ℕ), 0 ^ m / ↑(fact m)) + -1) = 0,
    rwa this,
  cases j,
  { exact absurd hj (by norm_num) },
  { induction j,
    { simpa },
    { rw ← j_ih dec_trivial,
      simp only [sum_range_succ, _root_.pow_succ],
      simp } }
end

lemma exp_add (x y : ℂ) : exp (x + y) = exp x * exp y :=
show complex.lim (⟨_, complex.exp_series_cau (x + y)⟩ : cau_seq ℂ abs) =
  complex.lim ⇑(show cau_seq ℂ abs, from ⟨_, complex.exp_series_cau x⟩)
  * complex.lim (show cau_seq ℂ abs, from ⟨_, complex.exp_series_cau y⟩),
begin
 have hxa := complex.exp_series_abs_cau x,
 have hx := complex.exp_series_cau x,
 have hy := complex.exp_series_cau y,
 have hxy := complex.exp_series_cau (x + y),
   rw complex.lim_mul_lim,
 have hj : ∀ j : ℕ, (range j).sum (λ (m : ℕ),
    (x + y) ^ m / ↑(fact m)) = (range j).sum
    (λ i, (range (i + 1)).sum (λ k, x ^ k / fact k *
    (y ^ (i - k) / fact (i - k)))),
    { assume j,
      refine finset.sum_congr rfl (λ m hm, _),
      rw [add_pow, div_eq_mul_inv, sum_mul],
      refine finset.sum_congr rfl (λ i hi, _),
      have := choose_mul_fact_mul_fact (le_of_lt_succ $ finset.mem_range.1 hi),
      rw [← this, nat.cast_mul, nat.cast_mul, mul_inv', mul_inv'],
      simp only [mul_left_comm (choose m i : ℂ), mul_assoc, mul_left_comm (choose m i : ℂ)⁻¹,
        mul_comm (choose m i : ℂ)],
      have : (choose m i : ℂ) ≠ 0 := nat.cast_ne_zero.2 (λ h,
        by have := choose_pos (le_of_lt_succ (mem_range.1 hi)); simpa [h, lt_irrefl] using this),
      rw inv_mul_cancel this,
      simp [div_eq_mul_inv, mul_comm, mul_assoc, mul_left_comm] },
  have hf := funext hj, have hxy1 := hxy, rw hf at hxy1,
  have := series_cauchy_prod hxa hy,
  refine eq.symm (lim_eq_lim_of_equiv _),
  assume ε ε0,
  dsimp,
  simp only [hj],
  exact this ε ε0,
end


/-
Copyright (c) 2019 Neil Strickland. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Neil Strickland, Yury Kudryashov
-/
import algebra.group.semiconj

/-!
# Commuting pairs of elements in monoids

We define the predicate `commute a b := a * b = b * a` and provide some operations on terms `(h :
commute a b)`. E.g., if `a`, `b`, and c are elements of a semiring, and that `hb : commute a b` and
`hc : commute a c`.  Then `hb.pow_left 5` proves `commute (a ^ 5) b` and `(hb.pow_right 2).add_right
(hb.mul_right hc)` proves `commute a (b ^ 2 + b * c)`.

Lean does not immediately recognise these terms as equations, so for rewriting we need syntax like
`rw [(hb.pow_left 5).eq]` rather than just `rw [hb.pow_left 5]`.

This file defines only a few operations (`mul_left`, `inv_right`, etc).  Other operations
(`pow_right`, field inverse etc) are in the files that define corresponding notions.

## Implementation details

Most of the proofs come from the properties of `semiconj_by`.
-/

variables {G : Type*}

/-- Two elements commute if `a * b = b * a`. -/
@[to_additive add_commute "Two elements additively commute if `a + b = b + a`"]
def commute {S : Type*} [has_mul S] (a b : S) : Prop := semiconj_by a b b

namespace commute

section has_mul

variables {S : Type*} [has_mul S]

/-- Equality behind `commute a b`; useful for rewriting. -/
@[to_additive "Equality behind `add_commute a b`; useful for rewriting."]
protected lemma eq {a b : S} (h : commute a b) : a * b = b * a := h

/-- Any element commutes with itself. -/
@[refl, simp, to_additive "Any element commutes with itself."]
protected lemma refl (a : S) : commute a a := eq.refl (a * a)

/-- If `a` commutes with `b`, then `b` commutes with `a`. -/
@[symm, to_additive "If `a` commutes with `b`, then `b` commutes with `a`."]
protected lemma symm {a b : S} (h : commute a b) : commute b a := eq.symm h

@[to_additive] protected theorem semiconj_by {a b : S} (h : commute a b) : semiconj_by a b b := h

@[to_additive]
protected theorem symm_iff {a b : S} : commute a b ↔ commute b a :=
⟨commute.symm, commute.symm⟩

end has_mul

section semigroup

variables {S : Type*} [semigroup S] {a b c : S}

/-- If `a` commutes with both `b` and `c`, then it commutes with their product. -/
@[simp, to_additive "If `a` commutes with both `b` and `c`, then it commutes with their sum."]
lemma mul_right (hab : commute a b) (hac : commute a c) : commute a (b * c) := hab.mul_right hac

/-- If both `a` and `b` commute with `c`, then their product commutes with `c`. -/
@[simp, to_additive "If both `a` and `b` commute with `c`, then their product commutes with `c`."]
lemma mul_left (hac : commute a c) (hbc : commute b c) : commute (a * b) c := hac.mul_left hbc

@[to_additive] protected lemma right_comm (h : commute b c) (a : S) :
  a * b * c = a * c * b :=
by simp only [mul_assoc, h.eq]

@[to_additive] protected lemma left_comm (h : commute a b) (c) :
  a * (b * c) = b * (a * c) :=
by simp only [← mul_assoc, h.eq]

end semigroup

@[to_additive]
protected

section mul_one_class

variables {M : Type*} [mul_one_class M]

@[simp, to_additive] theorem one_right (a : M) : commute a 1 := semiconj_by.one_right a
@[simp, to_additive] theorem one_left (a : M) : commute 1 a := semiconj_by.one_left a

end mul_one_class

section monoid

variables {M : Type*} [monoid M] {a b : M} {u u₁ u₂ : Mˣ}

@[simp, to_additive]
theorem pow_right (h : commute a b) (n : ℕ) : commute a (b ^ n) := h.pow_right n
@[simp, to_additive]
theorem pow_left (h : commute a b) (n : ℕ) : commute (a ^ n) b := (h.symm.pow_right n).symm
@[simp, to_additive]
theorem pow_pow (h : commute a b) (m n : ℕ) : commute (a ^ m) (b ^ n) :=
(h.pow_left m).pow_right n

@[simp, to_additive]
theorem self_pow (a : M) (n : ℕ) : commute a (a ^ n) := (commute.refl a).pow_right n
@[simp, to_additive]
theorem pow_self (a : M) (n : ℕ) : commute (a ^ n) a := (commute.refl a).pow_left n
@[simp, to_additive]
theorem pow_pow_self (a : M) (m n : ℕ) : commute (a ^ m) (a ^ n) :=
(commute.refl a).pow_pow m n

@[to_additive succ_nsmul'] theorem _root_.pow_succ' (a : M) (n : ℕ) : a ^ (n + 1) = a ^ n * a :=
(pow_succ a n).trans (self_pow _ _)

@[to_additive] theorem units_inv_right : commute a u → commute a ↑u⁻¹ :=
semiconj_by.units_inv_right

@[simp, to_additive] theorem units_inv_right_iff :
  commute a ↑u⁻¹ ↔ commute a u :=
semiconj_by.units_inv_right_iff

@[to_additive] theorem units_inv_left : commute ↑u a → commute ↑u⁻¹ a :=
semiconj_by.units_inv_symm_left

@[simp, to_additive]
theorem units_inv_left_iff: commute ↑u⁻¹ a ↔ commute ↑u a :=
semiconj_by.units_inv_symm_left_iff

@[to_additive]
theorem units_coe : commute u₁ u₂ → commute (u₁ : M) u₂ := semiconj_by.units_coe
@[to_additive]
theorem units_of_coe : commute (u₁ : M) u₂ → commute u₁ u₂ := semiconj_by.units_of_coe
@[simp, to_additive]
theorem units_coe_iff : commute (u₁ : M) u₂ ↔ commute u₁ u₂ := semiconj_by.units_coe_iff

@[to_additive] lemma is_unit_mul_iff (h : commute a b) :
  is_unit (a * b) ↔ is_unit a ∧ is_unit b :=
begin
  refine ⟨_, λ H, H.1.mul H.2⟩,
  rintro ⟨u, hu⟩,
  have : b * ↑u⁻¹ * a = 1,
  { have : commute a u := hu.symm ▸ (commute.refl _).mul_right h,
    rw [← this.units_inv_right.right_comm, ← h.eq, ← hu, u.mul_inv] },
  split,
  { refine ⟨⟨a, b * ↑u⁻¹, _, this⟩, rfl⟩,
    rw [← mul_assoc, ← hu, u.mul_inv] },
  { rw mul_assoc at this,
    refine ⟨⟨b, ↑u⁻¹ * a, this, _⟩, rfl⟩,
    rw [mul_assoc, ← hu, u.inv_mul] }
end

@[simp, to_additive] lemma _root_.is_unit_mul_self_iff :
  is_unit (a * a) ↔ is_unit a :=
(commute.refl a).is_unit_mul_iff.trans (and_self _)

end monoid

section division_monoid
variables [division_monoid G] {a b : G}

@[to_additive] lemma inv_inv : commute a b → commute a⁻¹ b⁻¹ := semiconj_by.inv_inv_symm
@[simp, to_additive]
lemma inv_inv_iff : commute a⁻¹ b⁻¹ ↔ commute a b := semiconj_by.inv_inv_symm_iff

end division_monoid

section group

variables [group G] {a b : G}

@[to_additive]
theorem inv_right : commute a b → commute a b⁻¹ := semiconj_by.inv_right
@[simp, to_additive]
theorem inv_right_iff : commute a b⁻¹ ↔ commute a b := semiconj_by.inv_right_iff

@[to_additive] theorem inv_left :  commute a b → commute a⁻¹ b := semiconj_by.inv_symm_left
@[simp, to_additive]
theorem inv_left_iff : commute a⁻¹ b ↔ commute a b := semiconj_by.inv_symm_left_iff

@[to_additive]
protected theorem inv_mul_cancel (h : commute a b) : a⁻¹ * b * a = b :=
by rw [h.inv_left.eq, inv_mul_cancel_right]

@[to_additive]
theorem inv_mul_cancel_assoc (h : commute a b) : a⁻¹ * (b * a) = b :=
by rw [← mul_assoc, h.inv_mul_cancel]

@[to_additive]
protected theorem mul_inv_cancel (h : commute a b) : a * b * a⁻¹ = b :=
by rw [h.eq, mul_inv_cancel_right]

@[to_additive]
theorem mul_inv_cancel_assoc (h : commute a b) : a * (b * a⁻¹) = b :=
by rw [← mul_assoc, h.mul_inv_cancel]

end group

end commute

section comm_group

variables [comm_group G] (a b : G)

@[simp, to_additive] lemma mul_inv_cancel_comm : a * b * a⁻¹ = b :=
(commute.all a b).mul_inv_cancel

@[simp, to_additive] lemma mul_inv_cancel_comm_assoc : a * (b * a⁻¹) = b :=
(commute.all a b).mul_inv_cancel_assoc

@[simp, to_additive] lemma inv_mul_cancel_comm : a⁻¹ * b * a = b :=
(commute.all a b).inv_mul_cancel

@[simp, to_additive] lemma inv_mul_cancel_comm_assoc : a⁻¹ * (b * a) = b :=
(commute.all a b).inv_mul_cancel_assoc

end comm_group