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

! This file was ported from Lean 3 source module linear_algebra.affine_space.independent
! leanprover-community/mathlib commit 2de9c37fa71dde2f1c6feff19876dd6a7b1519f0
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Finset.Sort
import Mathbin.Data.Fin.VecNotation
import Mathbin.Data.Sign
import Mathbin.LinearAlgebra.AffineSpace.Combination
import Mathbin.LinearAlgebra.AffineSpace.AffineEquiv
import Mathbin.LinearAlgebra.Basis

/-!
# Affine independence

This file defines affinely independent families of points.

## Main definitions

* `affine_independent` defines affinely independent families of points
  as those where no nontrivial weighted subtraction is `0`.  This is
  proved equivalent to two other formulations: linear independence of
  the results of subtracting a base point in the family from the other
  points in the family, or any equal affine combinations having the
  same weights.  A bundled type `simplex` is provided for finite
  affinely independent families of points, with an abbreviation
  `triangle` for the case of three points.

## References

* https://en.wikipedia.org/wiki/Affine_space

-/

noncomputable section

open BigOperators Affine

open Function

section AffineIndependent

variable (k : Type _) {V : Type _} {P : Type _} [Ring k] [AddCommGroup V] [Module k V]

variable [affine_space V P] {ι : Type _}

include V

/-- An indexed family is said to be affinely independent if no
nontrivial weighted subtractions (where the sum of weights is 0) are
0. -/
def AffineIndependent (p : ι → P) : Prop :=
  ∀ (s : Finset ι) (w : ι → k),
    (∑ i in s, w i) = 0 → s.weightedVsub p w = (0 : V) → ∀ i ∈ s, w i = 0
#align affine_independent AffineIndependent

/-- The definition of `affine_independent`. -/
theorem affineIndependent_def (p : ι → P) :
    AffineIndependent k p ↔
      ∀ (s : Finset ι) (w : ι → k),
        (∑ i in s, w i) = 0 → s.weightedVsub p w = (0 : V) → ∀ i ∈ s, w i = 0 :=
  Iff.rfl
#align affine_independent_def affineIndependent_def

/-- A family with at most one point is affinely independent. -/
theorem affineIndependent_of_subsingleton [Subsingleton ι] (p : ι → P) : AffineIndependent k p :=
  fun s w h hs i hi => Fintype.eq_of_subsingleton_of_sum_eq h i hi
#align affine_independent_of_subsingleton affineIndependent_of_subsingleton

/-- A family indexed by a `fintype` is affinely independent if and
only if no nontrivial weighted subtractions over `finset.univ` (where
the sum of the weights is 0) are 0. -/
theorem affineIndependent_iff_of_fintype [Fintype ι] (p : ι → P) :
    AffineIndependent k p ↔
      ∀ w : ι → k, (∑ i, w i) = 0 → Finset.univ.weightedVsub p w = (0 : V) → ∀ i, w i = 0 :=
  by
  constructor
  · exact fun h w hw hs i => h Finset.univ w hw hs i (Finset.mem_univ _)
  · intro h s w hw hs i hi
    rw [Finset.weightedVsub_indicator_subset _ _ (Finset.subset_univ s)] at hs
    rw [Set.sum_indicator_subset _ (Finset.subset_univ s)] at hw
    replace h := h ((↑s : Set ι).indicator w) hw hs i
    simpa [hi] using h
#align affine_independent_iff_of_fintype affineIndependent_iff_of_fintype

/-- A family is affinely independent if and only if the differences
from a base point in that family are linearly independent. -/
theorem affineIndependent_iff_linearIndependent_vsub (p : ι → P) (i1 : ι) :
    AffineIndependent k p ↔ LinearIndependent k fun i : { x // x ≠ i1 } => (p i -ᵥ p i1 : V) := by
  classical
    constructor
    · intro h
      rw [linearIndependent_iff']
      intro s g hg i hi
      set f : ι → k := fun x => if hx : x = i1 then -∑ y in s, g y else g ⟨x, hx⟩ with hfdef
      let s2 : Finset ι := insert i1 (s.map (embedding.subtype _))
      have hfg : ∀ x : { x // x ≠ i1 }, g x = f x :=
        by
        intro x
        rw [hfdef]
        dsimp only
        erw [dif_neg x.property, Subtype.coe_eta]
      rw [hfg]
      have hf : (∑ ι in s2, f ι) = 0 :=
        by
        rw [Finset.sum_insert
            (Finset.not_mem_map_subtype_of_not_property s (Classical.not_not.2 rfl)),
          Finset.sum_subtype_map_embedding fun x hx => (hfg x).symm]
        rw [hfdef]
        dsimp only
        rw [dif_pos rfl]
        exact neg_add_self _
      have hs2 : s2.weighted_vsub p f = (0 : V) :=
        by
        set f2 : ι → V := fun x => f x • (p x -ᵥ p i1) with hf2def
        set g2 : { x // x ≠ i1 } → V := fun x => g x • (p x -ᵥ p i1) with hg2def
        have hf2g2 : ∀ x : { x // x ≠ i1 }, f2 x = g2 x :=
          by
          simp_rw [hf2def, hg2def, hfg]
          exact fun x => rfl
        rw [Finset.weightedVsub_eq_weightedVsubOfPoint_of_sum_eq_zero s2 f p hf (p i1),
          Finset.weightedVsubOfPoint_insert, Finset.weightedVsubOfPoint_apply,
          Finset.sum_subtype_map_embedding fun x hx => hf2g2 x]
        exact hg
      exact h s2 f hf hs2 i (Finset.mem_insert_of_mem (Finset.mem_map.2 ⟨i, hi, rfl⟩))
    · intro h
      rw [linearIndependent_iff'] at h
      intro s w hw hs i hi
      rw [Finset.weightedVsub_eq_weightedVsubOfPoint_of_sum_eq_zero s w p hw (p i1), ←
        s.weighted_vsub_of_point_erase w p i1, Finset.weightedVsubOfPoint_apply] at hs
      let f : ι → V := fun i => w i • (p i -ᵥ p i1)
      have hs2 : (∑ i in (s.erase i1).Subtype fun i => i ≠ i1, f i) = 0 :=
        by
        rw [← hs]
        convert Finset.sum_subtype_of_mem f fun x => Finset.ne_of_mem_erase
      have h2 := h ((s.erase i1).Subtype fun i => i ≠ i1) (fun x => w x) hs2
      simp_rw [Finset.mem_subtype] at h2
      have h2b : ∀ i ∈ s, i ≠ i1 → w i = 0 := fun i his hi =>
        h2 ⟨i, hi⟩ (Finset.mem_erase_of_ne_of_mem hi his)
      exact Finset.eq_zero_of_sum_eq_zero hw h2b i hi
#align affine_independent_iff_linear_independent_vsub affineIndependent_iff_linearIndependent_vsub

/-- A set is affinely independent if and only if the differences from
a base point in that set are linearly independent. -/
theorem affineIndependent_set_iff_linearIndependent_vsub {s : Set P} {p₁ : P} (hp₁ : p₁ ∈ s) :
    AffineIndependent k (fun p => p : s → P) ↔
      LinearIndependent k (fun v => v : (fun p => (p -ᵥ p₁ : V)) '' (s \ {p₁}) → V) :=
  by
  rw [affineIndependent_iff_linearIndependent_vsub k (fun p => p : s → P) ⟨p₁, hp₁⟩]
  constructor
  · intro h
    have hv : ∀ v : (fun p => (p -ᵥ p₁ : V)) '' (s \ {p₁}), (v : V) +ᵥ p₁ ∈ s \ {p₁} := fun v =>
      (vsub_left_injective p₁).mem_set_image.1 ((vadd_vsub (v : V) p₁).symm ▸ v.property)
    let f : (fun p : P => (p -ᵥ p₁ : V)) '' (s \ {p₁}) → { x : s // x ≠ ⟨p₁, hp₁⟩ } := fun x =>
      ⟨⟨(x : V) +ᵥ p₁, Set.mem_of_mem_diff (hv x)⟩, fun hx =>
        Set.not_mem_of_mem_diff (hv x) (Subtype.ext_iff.1 hx)⟩
    convert h.comp f fun x1 x2 hx =>
        Subtype.ext (vadd_right_cancel p₁ (Subtype.ext_iff.1 (Subtype.ext_iff.1 hx)))
    ext v
    exact (vadd_vsub (v : V) p₁).symm
  · intro h
    let f : { x : s // x ≠ ⟨p₁, hp₁⟩ } → (fun p : P => (p -ᵥ p₁ : V)) '' (s \ {p₁}) := fun x =>
      ⟨((x : s) : P) -ᵥ p₁, ⟨x, ⟨⟨(x : s).property, fun hx => x.property (Subtype.ext hx)⟩, rfl⟩⟩⟩
    convert h.comp f fun x1 x2 hx =>
        Subtype.ext (Subtype.ext (vsub_left_cancel (Subtype.ext_iff.1 hx)))
#align affine_independent_set_iff_linear_independent_vsub affineIndependent_set_iff_linearIndependent_vsub

/-- A set of nonzero vectors is linearly independent if and only if,
given a point `p₁`, the vectors added to `p₁` and `p₁` itself are
affinely independent. -/
theorem linearIndependent_set_iff_affineIndependent_vadd_union_singleton {s : Set V}
    (hs : ∀ v ∈ s, v ≠ (0 : V)) (p₁ : P) :
    LinearIndependent k (fun v => v : s → V) ↔
      AffineIndependent k (fun p => p : {p₁} ∪ (fun v => v +ᵥ p₁) '' s → P) :=
  by
  rw [affineIndependent_set_iff_linearIndependent_vsub k
      (Set.mem_union_left _ (Set.mem_singleton p₁))]
  have h : (fun p => (p -ᵥ p₁ : V)) '' (({p₁} ∪ (fun v => v +ᵥ p₁) '' s) \ {p₁}) = s :=
    by
    simp_rw [Set.union_diff_left, Set.image_diff (vsub_left_injective p₁), Set.image_image,
      Set.image_singleton, vsub_self, vadd_vsub, Set.image_id']
    exact Set.diff_singleton_eq_self fun h => hs 0 h rfl
  rw [h]
#align linear_independent_set_iff_affine_independent_vadd_union_singleton linearIndependent_set_iff_affineIndependent_vadd_union_singleton

/-- A family is affinely independent if and only if any affine
combinations (with sum of weights 1) that evaluate to the same point
have equal `set.indicator`. -/
theorem affineIndependent_iff_indicator_eq_of_affineCombination_eq (p : ι → P) :
    AffineIndependent k p ↔
      ∀ (s1 s2 : Finset ι) (w1 w2 : ι → k),
        (∑ i in s1, w1 i) = 1 →
          (∑ i in s2, w2 i) = 1 →
            s1.affineCombination k p w1 = s2.affineCombination k p w2 →
              Set.indicator (↑s1) w1 = Set.indicator (↑s2) w2 :=
  by
  classical
    constructor
    · intro ha s1 s2 w1 w2 hw1 hw2 heq
      ext i
      by_cases hi : i ∈ s1 ∪ s2
      · rw [← sub_eq_zero]
        rw [Set.sum_indicator_subset _ (Finset.subset_union_left s1 s2)] at hw1
        rw [Set.sum_indicator_subset _ (Finset.subset_union_right s1 s2)] at hw2
        have hws : (∑ i in s1 ∪ s2, (Set.indicator (↑s1) w1 - Set.indicator (↑s2) w2) i) = 0 := by
          simp [hw1, hw2]
        rw [Finset.affineCombination_indicator_subset _ _ (Finset.subset_union_left s1 s2),
          Finset.affineCombination_indicator_subset _ _ (Finset.subset_union_right s1 s2), ←
          @vsub_eq_zero_iff_eq V, Finset.affineCombination_vsub] at heq
        exact ha (s1 ∪ s2) (Set.indicator (↑s1) w1 - Set.indicator (↑s2) w2) hws HEq i hi
      · rw [← Finset.mem_coe, Finset.coe_union] at hi
        simp [mt (Set.mem_union_left ↑s2) hi, mt (Set.mem_union_right ↑s1) hi]
    · intro ha s w hw hs i0 hi0
      let w1 : ι → k := Function.update (Function.const ι 0) i0 1
      have hw1 : (∑ i in s, w1 i) = 1 := by
        rw [Finset.sum_update_of_mem hi0, Finset.sum_const_zero, add_zero]
      have hw1s : s.affine_combination k p w1 = p i0 :=
        s.affine_combination_of_eq_one_of_eq_zero w1 p hi0 (Function.update_same _ _ _)
          fun _ _ hne => Function.update_noteq hne _ _
      let w2 := w + w1
      have hw2 : (∑ i in s, w2 i) = 1 := by simp [w2, Finset.sum_add_distrib, hw, hw1]
      have hw2s : s.affine_combination k p w2 = p i0 := by
        simp [w2, ← Finset.weightedVsub_vadd_affineCombination, hs, hw1s]
      replace ha := ha s s w2 w1 hw2 hw1 (hw1s.symm ▸ hw2s)
      have hws : w2 i0 - w1 i0 = 0 := by
        rw [← Finset.mem_coe] at hi0
        rw [← Set.indicator_of_mem hi0 w2, ← Set.indicator_of_mem hi0 w1, ha, sub_self]
      simpa [w2] using hws
#align affine_independent_iff_indicator_eq_of_affine_combination_eq affineIndependent_iff_indicator_eq_of_affineCombination_eq

/-- A finite family is affinely independent if and only if any affine
combinations (with sum of weights 1) that evaluate to the same point are equal. -/
theorem affineIndependent_iff_eq_of_fintype_affineCombination_eq [Fintype ι] (p : ι → P) :
    AffineIndependent k p ↔
      ∀ w1 w2 : ι → k,
        (∑ i, w1 i) = 1 →
          (∑ i, w2 i) = 1 →
            Finset.univ.affineCombination k p w1 = Finset.univ.affineCombination k p w2 → w1 = w2 :=
  by
  rw [affineIndependent_iff_indicator_eq_of_affineCombination_eq]
  constructor
  · intro h w1 w2 hw1 hw2 hweq
    simpa only [Set.indicator_univ, Finset.coe_univ] using h _ _ w1 w2 hw1 hw2 hweq
  · intro h s1 s2 w1 w2 hw1 hw2 hweq
    have hw1' : (∑ i, (s1 : Set ι).indicator w1 i) = 1 := by
      rwa [Set.sum_indicator_subset _ (Finset.subset_univ s1)] at hw1
    have hw2' : (∑ i, (s2 : Set ι).indicator w2 i) = 1 := by
      rwa [Set.sum_indicator_subset _ (Finset.subset_univ s2)] at hw2
    rw [Finset.affineCombination_indicator_subset w1 p (Finset.subset_univ s1),
      Finset.affineCombination_indicator_subset w2 p (Finset.subset_univ s2)] at hweq
    exact h _ _ hw1' hw2' hweq
#align affine_independent_iff_eq_of_fintype_affine_combination_eq affineIndependent_iff_eq_of_fintype_affineCombination_eq

variable {k}

/-- If we single out one member of an affine-independent family of points and affinely transport
all others along the line joining them to this member, the resulting new family of points is affine-
independent.

This is the affine version of `linear_independent.units_smul`. -/
theorem AffineIndependent.units_lineMap {p : ι → P} (hp : AffineIndependent k p) (j : ι)
    (w : ι → Units k) : AffineIndependent k fun i => AffineMap.lineMap (p j) (p i) (w i : k) :=
  by
  rw [affineIndependent_iff_linearIndependent_vsub k _ j] at hp⊢
  simp only [AffineMap.lineMap_vsub_left, AffineMap.coe_const, AffineMap.lineMap_same]
  exact hp.units_smul fun i => w i
#align affine_independent.units_line_map AffineIndependent.units_lineMap

theorem AffineIndependent.indicator_eq_of_affineCombination_eq {p : ι → P}
    (ha : AffineIndependent k p) (s₁ s₂ : Finset ι) (w₁ w₂ : ι → k) (hw₁ : (∑ i in s₁, w₁ i) = 1)
    (hw₂ : (∑ i in s₂, w₂ i) = 1) (h : s₁.affineCombination k p w₁ = s₂.affineCombination k p w₂) :
    Set.indicator (↑s₁) w₁ = Set.indicator (↑s₂) w₂ :=
  (affineIndependent_iff_indicator_eq_of_affineCombination_eq k p).1 ha s₁ s₂ w₁ w₂ hw₁ hw₂ h
#align affine_independent.indicator_eq_of_affine_combination_eq AffineIndependent.indicator_eq_of_affineCombination_eq

/-- An affinely independent family is injective, if the underlying
ring is nontrivial. -/
protected theorem AffineIndependent.injective [Nontrivial k] {p : ι → P}
    (ha : AffineIndependent k p) : Function.Injective p :=
  by
  intro i j hij
  rw [affineIndependent_iff_linearIndependent_vsub _ _ j] at ha
  by_contra hij'
  exact ha.ne_zero ⟨i, hij'⟩ (vsub_eq_zero_iff_eq.mpr hij)
#align affine_independent.injective AffineIndependent.injective

/-- If a family is affinely independent, so is any subfamily given by
composition of an embedding into index type with the original
family. -/
theorem AffineIndependent.comp_embedding {ι2 : Type _} (f : ι2 ↪ ι) {p : ι → P}
    (ha : AffineIndependent k p) : AffineIndependent k (p ∘ f) := by
  classical
    intro fs w hw hs i0 hi0
    let fs' := fs.map f
    let w' i := if h : ∃ i2, f i2 = i then w h.some else 0
    have hw' : ∀ i2 : ι2, w' (f i2) = w i2 := by
      intro i2
      have h : ∃ i : ι2, f i = f i2 := ⟨i2, rfl⟩
      have hs : h.some = i2 := f.injective h.some_spec
      simp_rw [w', dif_pos h, hs]
    have hw's : (∑ i in fs', w' i) = 0 :=
      by
      rw [← hw, Finset.sum_map]
      simp [hw']
    have hs' : fs'.weighted_vsub p w' = (0 : V) :=
      by
      rw [← hs, Finset.weightedVsub_map]
      congr with i
      simp [hw']
    rw [← ha fs' w' hw's hs' (f i0) ((Finset.mem_map' _).2 hi0), hw']
#align affine_independent.comp_embedding AffineIndependent.comp_embedding

/-- If a family is affinely independent, so is any subfamily indexed
by a subtype of the index type. -/
protected theorem AffineIndependent.subtype {p : ι → P} (ha : AffineIndependent k p) (s : Set ι) :
    AffineIndependent k fun i : s => p i :=
  ha.comp_embedding (Embedding.subtype _)
#align affine_independent.subtype AffineIndependent.subtype

/-- If an indexed family of points is affinely independent, so is the
corresponding set of points. -/
protected theorem AffineIndependent.range {p : ι → P} (ha : AffineIndependent k p) :
    AffineIndependent k (fun x => x : Set.range p → P) :=
  by
  let f : Set.range p → ι := fun x => x.property.some
  have hf : ∀ x, p (f x) = x := fun x => x.property.some_spec
  let fe : Set.range p ↪ ι := ⟨f, fun x₁ x₂ he => Subtype.ext (hf x₁ ▸ hf x₂ ▸ he ▸ rfl)⟩
  convert ha.comp_embedding fe
  ext
  simp [hf]
#align affine_independent.range AffineIndependent.range

theorem affineIndependent_equiv {ι' : Type _} (e : ι ≃ ι') {p : ι' → P} :
    AffineIndependent k (p ∘ e) ↔ AffineIndependent k p :=
  by
  refine' ⟨_, AffineIndependent.comp_embedding e.to_embedding⟩
  intro h
  have : p = p ∘ e ∘ e.symm.to_embedding := by
    ext
    simp
  rw [this]
  exact h.comp_embedding e.symm.to_embedding
#align affine_independent_equiv affineIndependent_equiv

/-- If a set of points is affinely independent, so is any subset. -/
protected theorem AffineIndependent.mono {s t : Set P}
    (ha : AffineIndependent k (fun x => x : t → P)) (hs : s ⊆ t) :
    AffineIndependent k (fun x => x : s → P) :=
  ha.comp_embedding (s.embeddingOfSubset t hs)
#align affine_independent.mono AffineIndependent.mono

/-- If the range of an injective indexed family of points is affinely
independent, so is that family. -/
theorem AffineIndependent.of_set_of_injective {p : ι → P}
    (ha : AffineIndependent k (fun x => x : Set.range p → P)) (hi : Function.Injective p) :
    AffineIndependent k p :=
  ha.comp_embedding
    (⟨fun i => ⟨p i, Set.mem_range_self _⟩, fun x y h => hi (Subtype.mk_eq_mk.1 h)⟩ :
      ι ↪ Set.range p)
#align affine_independent.of_set_of_injective AffineIndependent.of_set_of_injective

section Composition

variable {V₂ P₂ : Type _} [AddCommGroup V₂] [Module k V₂] [affine_space V₂ P₂]

include V₂

/-- If the image of a family of points in affine space under an affine transformation is affine-
independent, then the original family of points is also affine-independent. -/
theorem AffineIndependent.of_comp {p : ι → P} (f : P →ᵃ[k] P₂) (hai : AffineIndependent k (f ∘ p)) :
    AffineIndependent k p := by
  cases' isEmpty_or_nonempty ι with h h;
  · haveI := h
    apply affineIndependent_of_subsingleton
  obtain ⟨i⟩ := h
  rw [affineIndependent_iff_linearIndependent_vsub k p i]
  simp_rw [affineIndependent_iff_linearIndependent_vsub k (f ∘ p) i, Function.comp_apply, ←
    f.linear_map_vsub] at hai
  exact LinearIndependent.of_comp f.linear hai
#align affine_independent.of_comp AffineIndependent.of_comp

/-- The image of a family of points in affine space, under an injective affine transformation, is
affine-independent. -/
theorem AffineIndependent.map' {p : ι → P} (hai : AffineIndependent k p) (f : P →ᵃ[k] P₂)
    (hf : Function.Injective f) : AffineIndependent k (f ∘ p) :=
  by
  cases' isEmpty_or_nonempty ι with h h
  · haveI := h
    apply affineIndependent_of_subsingleton
  obtain ⟨i⟩ := h
  rw [affineIndependent_iff_linearIndependent_vsub k p i] at hai
  simp_rw [affineIndependent_iff_linearIndependent_vsub k (f ∘ p) i, Function.comp_apply, ←
    f.linear_map_vsub]
  have hf' : f.linear.ker = ⊥ := by rwa [LinearMap.ker_eq_bot, f.linear_injective_iff]
  exact LinearIndependent.map' hai f.linear hf'
#align affine_independent.map' AffineIndependent.map'

/-- Injective affine maps preserve affine independence. -/
theorem AffineMap.affineIndependent_iff {p : ι → P} (f : P →ᵃ[k] P₂) (hf : Function.Injective f) :
    AffineIndependent k (f ∘ p) ↔ AffineIndependent k p :=
  ⟨AffineIndependent.of_comp f, fun hai => AffineIndependent.map' hai f hf⟩
#align affine_map.affine_independent_iff AffineMap.affineIndependent_iff

/-- Affine equivalences preserve affine independence of families of points. -/
theorem AffineEquiv.affineIndependent_iff {p : ι → P} (e : P ≃ᵃ[k] P₂) :
    AffineIndependent k (e ∘ p) ↔ AffineIndependent k p :=
  e.toAffineMap.affineIndependent_iff e.toEquiv.Injective
#align affine_equiv.affine_independent_iff AffineEquiv.affineIndependent_iff

/-- Affine equivalences preserve affine independence of subsets. -/
theorem AffineEquiv.affineIndependent_set_of_eq_iff {s : Set P} (e : P ≃ᵃ[k] P₂) :
    AffineIndependent k (coe : e '' s → P₂) ↔ AffineIndependent k (coe : s → P) :=
  by
  have : e ∘ (coe : s → P) = (coe : e '' s → P₂) ∘ (e : P ≃ P₂).image s := rfl
  rw [← e.affine_independent_iff, this, affineIndependent_equiv]
#align affine_equiv.affine_independent_set_of_eq_iff AffineEquiv.affineIndependent_set_of_eq_iff

end Composition

/-- If a family is affinely independent, and the spans of points
indexed by two subsets of the index type have a point in common, those
subsets of the index type have an element in common, if the underlying
ring is nontrivial. -/
theorem AffineIndependent.exists_mem_inter_of_exists_mem_inter_affineSpan [Nontrivial k] {p : ι → P}
    (ha : AffineIndependent k p) {s1 s2 : Set ι} {p0 : P} (hp0s1 : p0 ∈ affineSpan k (p '' s1))
    (hp0s2 : p0 ∈ affineSpan k (p '' s2)) : ∃ i : ι, i ∈ s1 ∩ s2 :=
  by
  rw [Set.image_eq_range] at hp0s1 hp0s2
  rw [mem_affineSpan_iff_eq_affineCombination, ←
    Finset.eq_affineCombination_subset_iff_eq_affineCombination_subtype] at hp0s1 hp0s2
  rcases hp0s1 with ⟨fs1, hfs1, w1, hw1, hp0s1⟩
  rcases hp0s2 with ⟨fs2, hfs2, w2, hw2, hp0s2⟩
  rw [affineIndependent_iff_indicator_eq_of_affineCombination_eq] at ha
  replace ha := ha fs1 fs2 w1 w2 hw1 hw2 (hp0s1 ▸ hp0s2)
  have hnz : (∑ i in fs1, w1 i) ≠ 0 := hw1.symm ▸ one_ne_zero
  rcases Finset.exists_ne_zero_of_sum_ne_zero hnz with ⟨i, hifs1, hinz⟩
  simp_rw [← Set.indicator_of_mem (Finset.mem_coe.2 hifs1) w1, ha] at hinz
  use i, hfs1 hifs1, hfs2 (Set.mem_of_indicator_ne_zero hinz)
#align affine_independent.exists_mem_inter_of_exists_mem_inter_affine_span AffineIndependent.exists_mem_inter_of_exists_mem_inter_affineSpan

/-- If a family is affinely independent, the spans of points indexed
by disjoint subsets of the index type are disjoint, if the underlying
ring is nontrivial. -/
theorem AffineIndependent.affineSpan_disjoint_of_disjoint [Nontrivial k] {p : ι → P}
    (ha : AffineIndependent k p) {s1 s2 : Set ι} (hd : Disjoint s1 s2) :
    Disjoint (affineSpan k (p '' s1) : Set P) (affineSpan k (p '' s2)) :=
  by
  refine' Set.disjoint_left.2 fun p0 hp0s1 hp0s2 => _
  cases' ha.exists_mem_inter_of_exists_mem_inter_affine_span hp0s1 hp0s2 with i hi
  exact Set.disjoint_iff.1 hd hi
#align affine_independent.affine_span_disjoint_of_disjoint AffineIndependent.affineSpan_disjoint_of_disjoint

/-- If a family is affinely independent, a point in the family is in
the span of some of the points given by a subset of the index type if
and only if that point's index is in the subset, if the underlying
ring is nontrivial. -/
@[simp]
protected theorem AffineIndependent.mem_affineSpan_iff [Nontrivial k] {p : ι → P}
    (ha : AffineIndependent k p) (i : ι) (s : Set ι) : p i ∈ affineSpan k (p '' s) ↔ i ∈ s :=
  by
  constructor
  · intro hs
    have h :=
      AffineIndependent.exists_mem_inter_of_exists_mem_inter_affineSpan ha hs
        (mem_affineSpan k (Set.mem_image_of_mem _ (Set.mem_singleton _)))
    rwa [← Set.nonempty_def, Set.inter_singleton_nonempty] at h
  · exact fun h => mem_affineSpan k (Set.mem_image_of_mem p h)
#align affine_independent.mem_affine_span_iff AffineIndependent.mem_affineSpan_iff

/-- If a family is affinely independent, a point in the family is not
in the affine span of the other points, if the underlying ring is
nontrivial. -/
theorem AffineIndependent.not_mem_affineSpan_diff [Nontrivial k] {p : ι → P}
    (ha : AffineIndependent k p) (i : ι) (s : Set ι) : p i ∉ affineSpan k (p '' (s \ {i})) := by
  simp [ha]
#align affine_independent.not_mem_affine_span_diff AffineIndependent.not_mem_affineSpan_diff

theorem exists_nontrivial_relation_sum_zero_of_not_affine_ind {t : Finset V}
    (h : ¬AffineIndependent k (coe : t → V)) :
    ∃ f : V → k, (∑ e in t, f e • e) = 0 ∧ (∑ e in t, f e) = 0 ∧ ∃ x ∈ t, f x ≠ 0 := by
  classical
    rw [affineIndependent_iff_of_fintype] at h
    simp only [exists_prop, not_forall] at h
    obtain ⟨w, hw, hwt, i, hi⟩ := h
    simp only [Finset.weightedVsub_eq_weightedVsubOfPoint_of_sum_eq_zero _ w (coe : t → V) hw 0,
      vsub_eq_sub, Finset.weightedVsubOfPoint_apply, sub_zero] at hwt
    let f : ∀ x : V, x ∈ t → k := fun x hx => w ⟨x, hx⟩
    refine'
      ⟨fun x => if hx : x ∈ t then f x hx else (0 : k), _, _,
        by
        use i
        simp [hi, f]⟩
    suffices (∑ e : V in t, dite (e ∈ t) (fun hx => f e hx • e) fun hx => 0) = 0
      by
      convert this
      ext
      by_cases hx : x ∈ t <;> simp [hx]
    all_goals
      simp only [Finset.sum_dite_of_true fun x h => h, Subtype.val_eq_coe, Finset.mk_coe, f, hwt,
        hw]
#align exists_nontrivial_relation_sum_zero_of_not_affine_ind exists_nontrivial_relation_sum_zero_of_not_affine_ind

/-- Viewing a module as an affine space modelled on itself, we can characterise affine independence
in terms of linear combinations. -/
theorem affineIndependent_iff {ι} {p : ι → V} :
    AffineIndependent k p ↔
      ∀ (s : Finset ι) (w : ι → k), s.Sum w = 0 → (∑ e in s, w e • p e) = 0 → ∀ e ∈ s, w e = 0 :=
  forall₃_congr fun s w hw => by simp [s.weighted_vsub_eq_linear_combination hw]
#align affine_independent_iff affineIndependent_iff

/-- Given an affinely independent family of points, a weighted subtraction lies in the
`vector_span` of two points given as affine combinations if and only if it is a weighted
subtraction with weights a multiple of the difference between the weights of the two points. -/
theorem weightedVsub_mem_vectorSpan_pair {p : ι → P} (h : AffineIndependent k p) {w w₁ w₂ : ι → k}
    {s : Finset ι} (hw : (∑ i in s, w i) = 0) (hw₁ : (∑ i in s, w₁ i) = 1)
    (hw₂ : (∑ i in s, w₂ i) = 1) :
    s.weightedVsub p w ∈
        vectorSpan k ({s.affineCombination k p w₁, s.affineCombination k p w₂} : Set P) ↔
      ∃ r : k, ∀ i ∈ s, w i = r * (w₁ i - w₂ i) :=
  by
  rw [mem_vectorSpan_pair]
  refine' ⟨fun h => _, fun h => _⟩
  · rcases h with ⟨r, hr⟩
    refine' ⟨r, fun i hi => _⟩
    rw [s.affine_combination_vsub, ← s.weighted_vsub_const_smul, ← sub_eq_zero, ← map_sub] at hr
    have hw' : (∑ j in s, (r • (w₁ - w₂) - w) j) = 0 := by
      simp_rw [Pi.sub_apply, Pi.smul_apply, Pi.sub_apply, smul_sub, Finset.sum_sub_distrib, ←
        Finset.smul_sum, hw, hw₁, hw₂, sub_self]
    have hr' := h s _ hw' hr i hi
    rw [eq_comm, ← sub_eq_zero, ← smul_eq_mul]
    exact hr'
  · rcases h with ⟨r, hr⟩
    refine' ⟨r, _⟩
    let w' i := r * (w₁ i - w₂ i)
    change ∀ i ∈ s, w i = w' i at hr
    rw [s.weighted_vsub_congr hr fun _ _ => rfl, s.affine_combination_vsub, ←
      s.weighted_vsub_const_smul]
    congr
#align weighted_vsub_mem_vector_span_pair weightedVsub_mem_vectorSpan_pair

/-- Given an affinely independent family of points, an affine combination lies in the
span of two points given as affine combinations if and only if it is an affine combination
with weights those of one point plus a multiple of the difference between the weights of the
two points. -/
theorem affineCombination_mem_affineSpan_pair {p : ι → P} (h : AffineIndependent k p)
    {w w₁ w₂ : ι → k} {s : Finset ι} (hw : (∑ i in s, w i) = 1) (hw₁ : (∑ i in s, w₁ i) = 1)
    (hw₂ : (∑ i in s, w₂ i) = 1) :
    s.affineCombination k p w ∈ line[k, s.affineCombination k p w₁, s.affineCombination k p w₂] ↔
      ∃ r : k, ∀ i ∈ s, w i = r * (w₂ i - w₁ i) + w₁ i :=
  by
  rw [← vsub_vadd (s.affine_combination k p w) (s.affine_combination k p w₁),
    AffineSubspace.vadd_mem_iff_mem_direction _ (left_mem_affineSpan_pair _ _ _),
    direction_affineSpan, s.affine_combination_vsub, Set.pair_comm,
    weightedVsub_mem_vectorSpan_pair h _ hw₂ hw₁]
  · simp only [Pi.sub_apply, sub_eq_iff_eq_add]
  · simp_rw [Pi.sub_apply, Finset.sum_sub_distrib, hw, hw₁, sub_self]
#align affine_combination_mem_affine_span_pair affineCombination_mem_affineSpan_pair

end AffineIndependent

section DivisionRing

variable {k : Type _} {V : Type _} {P : Type _} [DivisionRing k] [AddCommGroup V] [Module k V]

variable [affine_space V P] {ι : Type _}

include V

/-- An affinely independent set of points can be extended to such a
set that spans the whole space. -/
theorem exists_subset_affineIndependent_affineSpan_eq_top {s : Set P}
    (h : AffineIndependent k (fun p => p : s → P)) :
    ∃ t : Set P, s ⊆ t ∧ AffineIndependent k (fun p => p : t → P) ∧ affineSpan k t = ⊤ :=
  by
  rcases s.eq_empty_or_nonempty with (rfl | ⟨p₁, hp₁⟩)
  · have p₁ : P := add_torsor.nonempty.some
    let hsv := Basis.ofVectorSpace k V
    have hsvi := hsv.linear_independent
    have hsvt := hsv.span_eq
    rw [Basis.coe_ofVectorSpace] at hsvi hsvt
    have h0 : ∀ v : V, v ∈ Basis.ofVectorSpaceIndex _ _ → v ≠ 0 :=
      by
      intro v hv
      simpa using hsv.ne_zero ⟨v, hv⟩
    rw [linearIndependent_set_iff_affineIndependent_vadd_union_singleton k h0 p₁] at hsvi
    exact
      ⟨{p₁} ∪ (fun v => v +ᵥ p₁) '' _, Set.empty_subset _, hsvi,
        affineSpan_singleton_union_vadd_eq_top_of_span_eq_top p₁ hsvt⟩
  · rw [affineIndependent_set_iff_linearIndependent_vsub k hp₁] at h
    let bsv := Basis.extend h
    have hsvi := bsv.linear_independent
    have hsvt := bsv.span_eq
    rw [Basis.coe_extend] at hsvi hsvt
    have hsv := h.subset_extend (Set.subset_univ _)
    have h0 : ∀ v : V, v ∈ h.extend _ → v ≠ 0 :=
      by
      intro v hv
      simpa using bsv.ne_zero ⟨v, hv⟩
    rw [linearIndependent_set_iff_affineIndependent_vadd_union_singleton k h0 p₁] at hsvi
    refine' ⟨{p₁} ∪ (fun v => v +ᵥ p₁) '' h.extend (Set.subset_univ _), _, _⟩
    · refine' Set.Subset.trans _ (Set.union_subset_union_right _ (Set.image_subset _ hsv))
      simp [Set.image_image]
    · use hsvi, affineSpan_singleton_union_vadd_eq_top_of_span_eq_top p₁ hsvt
#align exists_subset_affine_independent_affine_span_eq_top exists_subset_affineIndependent_affineSpan_eq_top

variable (k V)

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (t «expr ⊆ » s) -/
theorem exists_affineIndependent (s : Set P) :
    ∃ (t : _)(_ : t ⊆ s), affineSpan k t = affineSpan k s ∧ AffineIndependent k (coe : t → P) :=
  by
  rcases s.eq_empty_or_nonempty with (rfl | ⟨p, hp⟩)
  · exact ⟨∅, Set.empty_subset ∅, rfl, affineIndependent_of_subsingleton k _⟩
  obtain ⟨b, hb₁, hb₂, hb₃⟩ := exists_linearIndependent k ((Equiv.vaddConst p).symm '' s)
  have hb₀ : ∀ v : V, v ∈ b → v ≠ 0 := fun v hv => hb₃.ne_zero (⟨v, hv⟩ : b)
  rw [linearIndependent_set_iff_affineIndependent_vadd_union_singleton k hb₀ p] at hb₃
  refine' ⟨{p} ∪ Equiv.vaddConst p '' b, _, _, hb₃⟩
  · apply Set.union_subset (set.singleton_subset_iff.mpr hp)
    rwa [← (Equiv.vaddConst p).subset_image' b s]
  · rw [Equiv.coe_vaddConst_symm, ← vectorSpan_eq_span_vsub_set_right k hp] at hb₂
    apply AffineSubspace.ext_of_direction_eq
    · have : Submodule.span k b = Submodule.span k (insert 0 b) := by simp
      simp only [direction_affineSpan, ← hb₂, Equiv.coe_vaddConst, Set.singleton_union,
        vectorSpan_eq_span_vsub_set_right k (Set.mem_insert p _), this]
      congr
      change (Equiv.vaddConst p).symm '' insert p (Equiv.vaddConst p '' b) = _
      rw [Set.image_insert_eq, ← Set.image_comp]
      simp
    · use p
      simp only [Equiv.coe_vaddConst, Set.singleton_union, Set.mem_inter_iff, coe_affineSpan]
      exact ⟨mem_spanPoints k _ _ (Set.mem_insert p _), mem_spanPoints k _ _ hp⟩
#align exists_affine_independent exists_affineIndependent

variable (k) {V P}

/-- Two different points are affinely independent. -/
theorem affineIndependent_of_ne {p₁ p₂ : P} (h : p₁ ≠ p₂) : AffineIndependent k ![p₁, p₂] :=
  by
  rw [affineIndependent_iff_linearIndependent_vsub k ![p₁, p₂] 0]
  let i₁ : { x // x ≠ (0 : Fin 2) } := ⟨1, by norm_num⟩
  have he' : ∀ i, i = i₁ := by
    rintro ⟨i, hi⟩
    ext
    fin_cases i
    · simpa using hi
  haveI : Unique { x // x ≠ (0 : Fin 2) } := ⟨⟨i₁⟩, he'⟩
  have hz : (![p₁, p₂] ↑default -ᵥ ![p₁, p₂] 0 : V) ≠ 0 :=
    by
    rw [he' default]
    simpa using h.symm
  exact linearIndependent_unique _ hz
#align affine_independent_of_ne affineIndependent_of_ne

variable {k V P}

/-- If all but one point of a family are affinely independent, and that point does not lie in
the affine span of that family, the family is affinely independent. -/
theorem AffineIndependent.affineIndependent_of_not_mem_span {p : ι → P} {i : ι}
    (ha : AffineIndependent k fun x : { y // y ≠ i } => p x)
    (hi : p i ∉ affineSpan k (p '' { x | x ≠ i })) : AffineIndependent k p := by
  classical
    intro s w hw hs
    let s' : Finset { y // y ≠ i } := s.subtype (· ≠ i)
    let p' : { y // y ≠ i } → P := fun x => p x
    by_cases his : i ∈ s ∧ w i ≠ 0
    · refine' False.elim (hi _)
      let wm : ι → k := -(w i)⁻¹ • w
      have hms : s.weighted_vsub p wm = (0 : V) := by simp [wm, hs]
      have hwm : (∑ i in s, wm i) = 0 := by simp [wm, ← Finset.mul_sum, hw]
      have hwmi : wm i = -1 := by simp [wm, his.2]
      let w' : { y // y ≠ i } → k := fun x => wm x
      have hw' : (∑ x in s', w' x) = 1 :=
        by
        simp_rw [w', Finset.sum_subtype_eq_sum_filter]
        rw [← s.sum_filter_add_sum_filter_not (· ≠ i)] at hwm
        simp_rw [Classical.not_not, Finset.filter_eq', if_pos his.1, Finset.sum_singleton, ← wm,
          hwmi, ← sub_eq_add_neg, sub_eq_zero] at hwm
        exact hwm
      rw [← s.affine_combination_eq_of_weighted_vsub_eq_zero_of_eq_neg_one hms his.1 hwmi, ←
        (Subtype.range_coe : _ = { x | x ≠ i }), ← Set.range_comp, ←
        s.affine_combination_subtype_eq_filter]
      exact affineCombination_mem_affineSpan hw' p'
    · rw [not_and_or, Classical.not_not] at his
      let w' : { y // y ≠ i } → k := fun x => w x
      have hw' : (∑ x in s', w' x) = 0 :=
        by
        simp_rw [Finset.sum_subtype_eq_sum_filter]
        rw [Finset.sum_filter_of_ne, hw]
        rintro x hxs hwx rfl
        exact hwx (his.neg_resolve_left hxs)
      have hs' : s'.weighted_vsub p' w' = (0 : V) :=
        by
        simp_rw [Finset.weightedVsub_subtype_eq_filter]
        rw [Finset.weightedVsub_filter_of_ne, hs]
        rintro x hxs hwx rfl
        exact hwx (his.neg_resolve_left hxs)
      intro j hj
      by_cases hji : j = i
      · rw [hji] at hj
        exact hji.symm ▸ his.neg_resolve_left hj
      · exact ha s' w' hw' hs' ⟨j, hji⟩ (Finset.mem_subtype.2 hj)
#align affine_independent.affine_independent_of_not_mem_span AffineIndependent.affineIndependent_of_not_mem_span

/-- If distinct points `p₁` and `p₂` lie in `s` but `p₃` does not, the three points are affinely
independent. -/
theorem affineIndependent_of_ne_of_mem_of_mem_of_not_mem {s : AffineSubspace k P} {p₁ p₂ p₃ : P}
    (hp₁p₂ : p₁ ≠ p₂) (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) (hp₃ : p₃ ∉ s) :
    AffineIndependent k ![p₁, p₂, p₃] :=
  by
  have ha : AffineIndependent k fun x : { x : Fin 3 // x ≠ 2 } => ![p₁, p₂, p₃] x :=
    by
    rw [← affineIndependent_equiv (finSuccAboveEquiv (2 : Fin 3)).toEquiv]
    convert affineIndependent_of_ne k hp₁p₂
    ext x
    fin_cases x <;> rfl
  refine' ha.affine_independent_of_not_mem_span _
  intro h
  refine' hp₃ ((AffineSubspace.le_def' _ s).1 _ p₃ h)
  simp_rw [affineSpan_le, Set.image_subset_iff, Set.subset_def, Set.mem_preimage]
  intro x
  fin_cases x <;> simp [hp₁, hp₂]
#align affine_independent_of_ne_of_mem_of_mem_of_not_mem affineIndependent_of_ne_of_mem_of_mem_of_not_mem

/-- If distinct points `p₁` and `p₃` lie in `s` but `p₂` does not, the three points are affinely
independent. -/
theorem affineIndependent_of_ne_of_mem_of_not_mem_of_mem {s : AffineSubspace k P} {p₁ p₂ p₃ : P}
    (hp₁p₃ : p₁ ≠ p₃) (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∉ s) (hp₃ : p₃ ∈ s) :
    AffineIndependent k ![p₁, p₂, p₃] :=
  by
  rw [← affineIndependent_equiv (Equiv.swap (1 : Fin 3) 2)]
  convert affineIndependent_of_ne_of_mem_of_mem_of_not_mem hp₁p₃ hp₁ hp₃ hp₂ using 1
  ext x
  fin_cases x <;> rfl
#align affine_independent_of_ne_of_mem_of_not_mem_of_mem affineIndependent_of_ne_of_mem_of_not_mem_of_mem

/-- If distinct points `p₂` and `p₃` lie in `s` but `p₁` does not, the three points are affinely
independent. -/
theorem affineIndependent_of_ne_of_not_mem_of_mem_of_mem {s : AffineSubspace k P} {p₁ p₂ p₃ : P}
    (hp₂p₃ : p₂ ≠ p₃) (hp₁ : p₁ ∉ s) (hp₂ : p₂ ∈ s) (hp₃ : p₃ ∈ s) :
    AffineIndependent k ![p₁, p₂, p₃] :=
  by
  rw [← affineIndependent_equiv (Equiv.swap (0 : Fin 3) 2)]
  convert affineIndependent_of_ne_of_mem_of_mem_of_not_mem hp₂p₃.symm hp₃ hp₂ hp₁ using 1
  ext x
  fin_cases x <;> rfl
#align affine_independent_of_ne_of_not_mem_of_mem_of_mem affineIndependent_of_ne_of_not_mem_of_mem_of_mem

end DivisionRing

section Ordered

variable {k : Type _} {V : Type _} {P : Type _} [LinearOrderedRing k] [AddCommGroup V]

variable [Module k V] [affine_space V P] {ι : Type _}

include V

attribute [local instance] LinearOrderedRing.decidableLt

/-- Given an affinely independent family of points, suppose that an affine combination lies in
the span of two points given as affine combinations, and suppose that, for two indices, the
coefficients in the first point in the span are zero and those in the second point in the span
have the same sign. Then the coefficients in the combination lying in the span have the same
sign. -/
theorem sign_eq_of_affineCombination_mem_affineSpan_pair {p : ι → P} (h : AffineIndependent k p)
    {w w₁ w₂ : ι → k} {s : Finset ι} (hw : (∑ i in s, w i) = 1) (hw₁ : (∑ i in s, w₁ i) = 1)
    (hw₂ : (∑ i in s, w₂ i) = 1)
    (hs :
      s.affineCombination k p w ∈ line[k, s.affineCombination k p w₁, s.affineCombination k p w₂])
    {i j : ι} (hi : i ∈ s) (hj : j ∈ s) (hi0 : w₁ i = 0) (hj0 : w₁ j = 0)
    (hij : SignType.sign (w₂ i) = SignType.sign (w₂ j)) :
    SignType.sign (w i) = SignType.sign (w j) :=
  by
  rw [affineCombination_mem_affineSpan_pair h hw hw₁ hw₂] at hs
  rcases hs with ⟨r, hr⟩
  dsimp only at hr
  rw [hr i hi, hr j hj, hi0, hj0, add_zero, add_zero, sub_zero, sub_zero, sign_mul, sign_mul, hij]
#align sign_eq_of_affine_combination_mem_affine_span_pair sign_eq_of_affineCombination_mem_affineSpan_pair

/-- Given an affinely independent family of points, suppose that an affine combination lies in
the span of one point of that family and a combination of another two points of that family given
by `line_map` with coefficient between 0 and 1. Then the coefficients of those two points in the
combination lying in the span have the same sign. -/
theorem sign_eq_of_affineCombination_mem_affineSpan_single_lineMap {p : ι → P}
    (h : AffineIndependent k p) {w : ι → k} {s : Finset ι} (hw : (∑ i in s, w i) = 1) {i₁ i₂ i₃ : ι}
    (h₁ : i₁ ∈ s) (h₂ : i₂ ∈ s) (h₃ : i₃ ∈ s) (h₁₂ : i₁ ≠ i₂) (h₁₃ : i₁ ≠ i₃) (h₂₃ : i₂ ≠ i₃)
    {c : k} (hc0 : 0 < c) (hc1 : c < 1)
    (hs : s.affineCombination k p w ∈ line[k, p i₁, AffineMap.lineMap (p i₂) (p i₃) c]) :
    SignType.sign (w i₂) = SignType.sign (w i₃) := by
  classical
    rw [← s.affine_combination_affine_combination_single_weights k p h₁, ←
      s.affine_combination_affine_combination_line_map_weights p h₂ h₃ c] at hs
    refine'
      sign_eq_of_affineCombination_mem_affineSpan_pair h hw
        (s.sum_affine_combination_single_weights k h₁)
        (s.sum_affine_combination_line_map_weights h₂ h₃ c) hs h₂ h₃
        (Finset.affineCombinationSingleWeights_apply_of_ne k h₁₂.symm)
        (Finset.affineCombinationSingleWeights_apply_of_ne k h₁₃.symm) _
    rw [Finset.affineCombinationLineMapWeights_apply_left h₂₃,
      Finset.affineCombinationLineMapWeights_apply_right h₂₃]
    simp [hc0, sub_pos.2 hc1]
#align sign_eq_of_affine_combination_mem_affine_span_single_line_map sign_eq_of_affineCombination_mem_affineSpan_single_lineMap

end Ordered

namespace Affine

variable (k : Type _) {V : Type _} (P : Type _) [Ring k] [AddCommGroup V] [Module k V]

variable [affine_space V P]

include V

/-- A `simplex k P n` is a collection of `n + 1` affinely
independent points. -/
structure Simplex (n : ℕ) where
  points : Fin (n + 1) → P
  Independent : AffineIndependent k points
#align affine.simplex Affine.Simplex

/-- A `triangle k P` is a collection of three affinely independent points. -/
abbrev Triangle :=
  Simplex k P 2
#align affine.triangle Affine.Triangle

namespace Simplex

variable {P}

/-- Construct a 0-simplex from a point. -/
def mkOfPoint (p : P) : Simplex k P 0 :=
  ⟨fun _ => p, affineIndependent_of_subsingleton k _⟩
#align affine.simplex.mk_of_point Affine.Simplex.mkOfPoint

/-- The point in a simplex constructed with `mk_of_point`. -/
@[simp]
theorem mkOfPoint_points (p : P) (i : Fin 1) : (mkOfPoint k p).points i = p :=
  rfl
#align affine.simplex.mk_of_point_points Affine.Simplex.mkOfPoint_points

instance [Inhabited P] : Inhabited (Simplex k P 0) :=
  ⟨mkOfPoint k default⟩

instance nonempty : Nonempty (Simplex k P 0) :=
  ⟨mkOfPoint k <| AddTorsor.nonempty.some⟩
#align affine.simplex.nonempty Affine.Simplex.nonempty

variable {k V}

/-- Two simplices are equal if they have the same points. -/
@[ext]
theorem ext {n : ℕ} {s1 s2 : Simplex k P n} (h : ∀ i, s1.points i = s2.points i) : s1 = s2 :=
  by
  cases s1
  cases s2
  congr with i
  exact h i
#align affine.simplex.ext Affine.Simplex.ext

/-- Two simplices are equal if and only if they have the same points. -/
theorem ext_iff {n : ℕ} (s1 s2 : Simplex k P n) : s1 = s2 ↔ ∀ i, s1.points i = s2.points i :=
  ⟨fun h _ => h ▸ rfl, ext⟩
#align affine.simplex.ext_iff Affine.Simplex.ext_iff

/-- A face of a simplex is a simplex with the given subset of
points. -/
def face {n : ℕ} (s : Simplex k P n) {fs : Finset (Fin (n + 1))} {m : ℕ} (h : fs.card = m + 1) :
    Simplex k P m :=
  ⟨s.points ∘ fs.orderEmbOfFin h, s.Independent.comp_embedding (fs.orderEmbOfFin h).toEmbedding⟩
#align affine.simplex.face Affine.Simplex.face

/-- The points of a face of a simplex are given by `mono_of_fin`. -/
theorem face_points {n : ℕ} (s : Simplex k P n) {fs : Finset (Fin (n + 1))} {m : ℕ}
    (h : fs.card = m + 1) (i : Fin (m + 1)) :
    (s.face h).points i = s.points (fs.orderEmbOfFin h i) :=
  rfl
#align affine.simplex.face_points Affine.Simplex.face_points

/-- The points of a face of a simplex are given by `mono_of_fin`. -/
theorem face_points' {n : ℕ} (s : Simplex k P n) {fs : Finset (Fin (n + 1))} {m : ℕ}
    (h : fs.card = m + 1) : (s.face h).points = s.points ∘ fs.orderEmbOfFin h :=
  rfl
#align affine.simplex.face_points' Affine.Simplex.face_points'

/-- A single-point face equals the 0-simplex constructed with
`mk_of_point`. -/
@[simp]
theorem face_eq_mkOfPoint {n : ℕ} (s : Simplex k P n) (i : Fin (n + 1)) :
    s.face (Finset.card_singleton i) = mkOfPoint k (s.points i) :=
  by
  ext
  simp [face_points]
#align affine.simplex.face_eq_mk_of_point Affine.Simplex.face_eq_mkOfPoint

/-- The set of points of a face. -/
@[simp]
theorem range_face_points {n : ℕ} (s : Simplex k P n) {fs : Finset (Fin (n + 1))} {m : ℕ}
    (h : fs.card = m + 1) : Set.range (s.face h).points = s.points '' ↑fs := by
  rw [face_points', Set.range_comp, Finset.range_orderEmbOfFin]
#align affine.simplex.range_face_points Affine.Simplex.range_face_points

/-- Remap a simplex along an `equiv` of index types. -/
@[simps]
def reindex {m n : ℕ} (s : Simplex k P m) (e : Fin (m + 1) ≃ Fin (n + 1)) : Simplex k P n :=
  ⟨s.points ∘ e.symm, (affineIndependent_equiv e.symm).2 s.Independent⟩
#align affine.simplex.reindex Affine.Simplex.reindex

/-- Reindexing by `equiv.refl` yields the original simplex. -/
@[simp]
theorem reindex_refl {n : ℕ} (s : Simplex k P n) : s.reindex (Equiv.refl (Fin (n + 1))) = s :=
  ext fun _ => rfl
#align affine.simplex.reindex_refl Affine.Simplex.reindex_refl

/-- Reindexing by the composition of two equivalences is the same as reindexing twice. -/
@[simp]
theorem reindex_trans {n₁ n₂ n₃ : ℕ} (e₁₂ : Fin (n₁ + 1) ≃ Fin (n₂ + 1))
    (e₂₃ : Fin (n₂ + 1) ≃ Fin (n₃ + 1)) (s : Simplex k P n₁) :
    s.reindex (e₁₂.trans e₂₃) = (s.reindex e₁₂).reindex e₂₃ :=
  rfl
#align affine.simplex.reindex_trans Affine.Simplex.reindex_trans

/-- Reindexing by an equivalence and its inverse yields the original simplex. -/
@[simp]
theorem reindex_reindex_symm {m n : ℕ} (s : Simplex k P m) (e : Fin (m + 1) ≃ Fin (n + 1)) :
    (s.reindex e).reindex e.symm = s := by rw [← reindex_trans, Equiv.self_trans_symm, reindex_refl]
#align affine.simplex.reindex_reindex_symm Affine.Simplex.reindex_reindex_symm

/-- Reindexing by the inverse of an equivalence and that equivalence yields the original simplex. -/
@[simp]
theorem reindex_symm_reindex {m n : ℕ} (s : Simplex k P m) (e : Fin (n + 1) ≃ Fin (m + 1)) :
    (s.reindex e.symm).reindex e = s := by rw [← reindex_trans, Equiv.symm_trans_self, reindex_refl]
#align affine.simplex.reindex_symm_reindex Affine.Simplex.reindex_symm_reindex

/-- Reindexing a simplex produces one with the same set of points. -/
@[simp]
theorem reindex_range_points {m n : ℕ} (s : Simplex k P m) (e : Fin (m + 1) ≃ Fin (n + 1)) :
    Set.range (s.reindex e).points = Set.range s.points := by
  rw [reindex, Set.range_comp, Equiv.range_eq_univ, Set.image_univ]
#align affine.simplex.reindex_range_points Affine.Simplex.reindex_range_points

end Simplex

end Affine

namespace Affine

namespace Simplex

variable {k : Type _} {V : Type _} {P : Type _} [DivisionRing k] [AddCommGroup V] [Module k V]
  [affine_space V P]

include V

/-- The centroid of a face of a simplex as the centroid of a subset of
the points. -/
@[simp]
theorem face_centroid_eq_centroid {n : ℕ} (s : Simplex k P n) {fs : Finset (Fin (n + 1))} {m : ℕ}
    (h : fs.card = m + 1) : Finset.univ.centroid k (s.face h).points = fs.centroid k s.points :=
  by
  convert(finset.univ.centroid_map k (fs.order_emb_of_fin h).toEmbedding s.points).symm
  rw [← Finset.coe_inj, Finset.coe_map, Finset.coe_univ, Set.image_univ]
  simp
#align affine.simplex.face_centroid_eq_centroid Affine.Simplex.face_centroid_eq_centroid

/-- Over a characteristic-zero division ring, the centroids given by
two subsets of the points of a simplex are equal if and only if those
faces are given by the same subset of points. -/
@[simp]
theorem centroid_eq_iff [CharZero k] {n : ℕ} (s : Simplex k P n) {fs₁ fs₂ : Finset (Fin (n + 1))}
    {m₁ m₂ : ℕ} (h₁ : fs₁.card = m₁ + 1) (h₂ : fs₂.card = m₂ + 1) :
    fs₁.centroid k s.points = fs₂.centroid k s.points ↔ fs₁ = fs₂ :=
  by
  refine' ⟨fun h => _, congr_arg _⟩
  rw [Finset.centroid_eq_affineCombination_fintype, Finset.centroid_eq_affineCombination_fintype] at
    h
  have ha :=
    (affineIndependent_iff_indicator_eq_of_affineCombination_eq k s.points).1 s.independent _ _ _ _
      (fs₁.sum_centroid_weights_indicator_eq_one_of_card_eq_add_one k h₁)
      (fs₂.sum_centroid_weights_indicator_eq_one_of_card_eq_add_one k h₂) h
  simp_rw [Finset.coe_univ, Set.indicator_univ, Function.funext_iff,
    Finset.centroidWeightsIndicator_def, Finset.centroidWeights, h₁, h₂] at ha
  ext i
  specialize ha i
  have key : ∀ n : ℕ, (n : k) + 1 ≠ 0 := fun n h => by norm_cast  at h
  -- we should be able to golf this to `refine ⟨λ hi, decidable.by_contradiction (λ hni, _), ...⟩`,
      -- but for some unknown reason it doesn't work.
      constructor <;>
      intro hi <;>
    by_contra hni
  · simpa [hni, hi, key] using ha
  · simpa [hni, hi, key] using ha.symm
#align affine.simplex.centroid_eq_iff Affine.Simplex.centroid_eq_iff

/-- Over a characteristic-zero division ring, the centroids of two
faces of a simplex are equal if and only if those faces are given by
the same subset of points. -/
theorem face_centroid_eq_iff [CharZero k] {n : ℕ} (s : Simplex k P n)
    {fs₁ fs₂ : Finset (Fin (n + 1))} {m₁ m₂ : ℕ} (h₁ : fs₁.card = m₁ + 1) (h₂ : fs₂.card = m₂ + 1) :
    Finset.univ.centroid k (s.face h₁).points = Finset.univ.centroid k (s.face h₂).points ↔
      fs₁ = fs₂ :=
  by
  rw [face_centroid_eq_centroid, face_centroid_eq_centroid]
  exact s.centroid_eq_iff h₁ h₂
#align affine.simplex.face_centroid_eq_iff Affine.Simplex.face_centroid_eq_iff

/-- Two simplices with the same points have the same centroid. -/
theorem centroid_eq_of_range_eq {n : ℕ} {s₁ s₂ : Simplex k P n}
    (h : Set.range s₁.points = Set.range s₂.points) :
    Finset.univ.centroid k s₁.points = Finset.univ.centroid k s₂.points :=
  by
  rw [← Set.image_univ, ← Set.image_univ, ← Finset.coe_univ] at h
  exact
    finset.univ.centroid_eq_of_inj_on_of_image_eq k _
      (fun _ _ _ _ he => AffineIndependent.injective s₁.independent he)
      (fun _ _ _ _ he => AffineIndependent.injective s₂.independent he) h
#align affine.simplex.centroid_eq_of_range_eq Affine.Simplex.centroid_eq_of_range_eq

end Simplex

end Affine


/-
Copyright © 2021 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nicolò Cavalleri, Heather Macbeth

! This file was ported from Lean 3 source module geometry.manifold.instances.units_of_normed_algebra
! leanprover-community/mathlib commit ef901ea68d3bb1dd08f8bc3034ab6b32b2e6ecdf
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Geometry.Manifold.SmoothManifoldWithCorners
import Mathbin.Analysis.NormedSpace.Units

/-!
# Units of a normed algebra

This file is a stub, containing a construction of the charted space structure on the group of units
of a complete normed ring `R`, and of the smooth manifold structure on the group of units of a
complete normed `𝕜`-algebra `R`.

This manifold is actually a Lie group, which eventually should be the main result of this file.

An important special case of this construction is the general linear group.  For a normed space `V`
over a field `𝕜`, the `𝕜`-linear endomorphisms of `V` are a normed `𝕜`-algebra (see
`continuous_linear_map.to_normed_algebra`), so this construction provides a Lie group structure on
its group of units, the general linear group GL(`𝕜`, `V`).

## TODO

The Lie group instance requires the following fields:
```
instance : lie_group 𝓘(𝕜, R) Rˣ :=
{ smooth_mul := sorry,
  smooth_inv := sorry,
  ..units.smooth_manifold_with_corners }
```

The ingredients needed for the construction are
* smoothness of multiplication and inversion in the charts, i.e. as functions on the normed
  `𝕜`-space `R`:  see `cont_diff_at_ring_inverse` for the inversion result, and
  `cont_diff_mul` (needs to be generalized from field to algebra) for the multiplication
  result
* for an open embedding `f`, whose domain is equipped with the induced manifold structure
  `f.singleton_smooth_manifold_with_corners`, characterization of smoothness of functions to/from
  this manifold in terms of smoothness in the target space.  See the pair of lemmas
  `cont_mdiff_coe_sphere` and `cont_mdiff.cod_restrict_sphere` for a model.
None of this should be particularly difficult.

-/

noncomputable section

open Manifold

namespace Units

variable {R : Type _} [NormedRing R] [CompleteSpace R]

instance : ChartedSpace R Rˣ :=
  openEmbedding_coe.singletonChartedSpace

theorem chartAt_apply {a : Rˣ} {b : Rˣ} : chartAt R a b = b :=
  rfl
#align units.chart_at_apply Units.chartAt_apply

theorem chartAt_source {a : Rˣ} : (chartAt R a).source = Set.univ :=
  rfl
#align units.chart_at_source Units.chartAt_source

variable {𝕜 : Type _} [NontriviallyNormedField 𝕜] [NormedAlgebra 𝕜 R]

instance : SmoothManifoldWithCorners 𝓘(𝕜, R) Rˣ :=
  openEmbedding_coe.singleton_smoothManifoldWithCorners 𝓘(𝕜, R)

end Units


theorem le_trans (a b c : mynat) (hab : a ≤ b) (hbc : b ≤ c) : a ≤ c :=
begin
cases hab,
cases hbc,
use (hab_w + hbc_w),
rw ← add_assoc,
rw hbc_h,
rw hab_h,
refl,
end


import data.int.parity
import data.real.basic
import .limit_definition -- <=> exercises.limit_definition

set_option trace.simplify.rewrite true

-- Negations, proof by contradiction and contraposition.
example : false → 0 = 1 :=
begin
  intro h,
  exfalso,
  exact h,
end

example {x : real} : ¬ x < x :=
begin
  intro hyp,
  rw lt_iff_le_and_ne at hyp,
  cases hyp with hyp_inf hyp_non,
  clear hyp_inf, -- we won't use that one, so let's discard it
  change x = x → false at hyp_non, -- Lean doesn't need this psychological line
  apply hyp_non (eq.refl x),
end

open int

example (n : ℤ) (h_pair : even n) (h_non_pair : ¬ even n) : 0 = 1 :=
begin
  have H : ¬ (0 = 1) := zero_ne_one,
  sorry,
end

example (P Q : Prop) (h₁ : P ∨ Q) (h₂ : ¬ (P ∧ Q)) : ¬ P ↔ Q :=
begin
  cases h₁,
  {
    have p_true : P = true, from sorry,
    rw p_true at h₂,
    have H : (true ∧ Q) = Q, from sorry,
    rw H at h₂,
    clear H,
    -- we must use maximum information rules:
    -- ¬ Q = (Q → false) give us less information than ¬ Q = (Q ↔ false)
    have H2 : ¬ Q ↔ (Q = false), from sorry,
    rw H2 at h₂,
    rename h₂ q_false,
    rw [p_true, q_false],
    -- simp only [iff],
    rw not_true,
    -- [not_true]: ¬true ==> false
    -- [iff_self]: false ↔ false ==> true
  },
  {
    sorry
  },
end

lemma ex1 (u : ℕ → ℝ) (l l' : ℝ) : seq_limit u l → seq_limit u l' → l = l' :=
begin
  intros hl hl',
  by_contradiction H,
  change l ≠ l' at H, -- for human readability
  have ineq : |l-l'| > 0,
    begin
    apply abs_pos_of_ne_zero,
    -- backward proof: we need match goal to the right side of abs_pos_of_ne_zero:
    -- abs_pos_of_ne_zero : a ≠ 0 → |a| > 0
    --                              ↑↑↑↑↑↑↑
    -- then goal becomes left side: ⊢ l - l' ≠ 0
    apply sub_ne_zero_of_ne,
    exact H,
      -- exact abs_pos_of_ne_zero (sub_ne_zero_of_ne H),
    end,
  cases hl ( |l-l'|/4 ) (by linarith) with N hN, -- it's hard to understand this line
  cases hl' ( |l-l'|/4 ) (by linarith) with N' hN', -- and this
  let N₀ := max N N', -- this is a new tactic, whose effect should be clear
  specialize hN N₀ (le_max_left _ _),
  specialize hN' N₀ (le_max_right _ _),
  have clef : |l-l'| < |l-l'|,
    calc
    |l - l'| = |(l - u N₀) + (u N₀ - l')| : by ring
         ... ≤ |l - u N₀| + |u N₀ - l'|   : by apply abs_add -- apply in forward order, not backward? answer: no, calc implicitly executed `apply eq.trans`
         ... = |u N₀ - l| + |u N₀ - l'|   : by rw abs_sub
         ... < |l-l'| : by linarith,
  linarith, -- liarith can also find simple numerical contradictions
end

example (P Q : Prop) (h : ¬ Q → ¬ P) : P → Q :=
begin
  contrapose,
  exact h,
end


-- Union_con_su_interseccion.lean
-- Unión con su intersección
-- José A. Alonso Jiménez <https://jaalonso.github.io>
-- Sevilla, 28-abril-2022
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Demostrar que
--    s ∪ (s ∩ t) = s
-- ----------------------------------------------------------------------

import data.set.basic
open set

variable {α : Type}
variables s t : set α

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

example : s ∪ (s ∩ t) = s :=
begin
  ext x,
  split,
  { intro hx,
    cases hx with xs xst,
    { exact xs, },
    { exact xst.1, }},
  { intro xs,
    left,
    exact xs, },
end

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

example : s ∪ (s ∩ t) = s :=
begin
  ext x,
  exact ⟨λ hx, or.dcases_on hx id and.left,
         λ xs, or.inl xs⟩,
end

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

example : s ∪ (s ∩ t) = s :=
begin
  ext x,
  split,
  { rintros (xs | ⟨xs, xt⟩);
    exact xs },
  { intro xs,
    left,
    exact xs },
end

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

example : s ∪ (s ∩ t) = s :=
sup_inf_self


/-
Copyright (c) 2020 David Wärn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: David Wärn
-/
import data.equiv.encodable.basic
import order.atoms

/-!
# Order ideals, cofinal sets, and the Rasiowa–Sikorski lemma

## Main definitions

Throughout this file, `P` is at least a preorder, but some sections require more
structure, such as a bottom element, a top element, or a join-semilattice structure.
- `order.ideal P`: the type of nonempty, upward directed, and downward closed subsets of `P`.
  Dual to the notion of a filter on a preorder.
- `order.is_ideal P`: a predicate for when a `set P` is an ideal.
- `order.ideal.principal p`: the principal ideal generated by `p : P`.
- `order.ideal.is_proper P`: a predicate for proper ideals.
  Dual to the notion of a proper filter.
- `order.ideal.is_maximal`: a predicate for maximal ideals.
  Dual to the notion of an ultrafilter.
- `ideal_inter_nonempty P`: a predicate for when the intersection of any two ideals of
  `P` is nonempty.
- `ideal_Inter_nonempty P`: a predicate for when the intersection of all ideals of
  `P` is nonempty.
- `order.cofinal P`: the type of subsets of `P` containing arbitrarily large elements.
  Dual to the notion of 'dense set' used in forcing.
- `order.ideal_of_cofinals p 𝒟`, where `p : P`, and `𝒟` is a countable family of cofinal
  subsets of P: an ideal in `P` which contains `p` and intersects every set in `𝒟`. (This a form
  of the Rasiowa–Sikorski lemma.)

## References

- <https://en.wikipedia.org/wiki/Ideal_(order_theory)>
- <https://en.wikipedia.org/wiki/Cofinal_(mathematics)>
- <https://en.wikipedia.org/wiki/Rasiowa%E2%80%93Sikorski_lemma>

Note that for the Rasiowa–Sikorski lemma, Wikipedia uses the opposite ordering on `P`,
in line with most presentations of forcing.

## Tags

ideal, cofinal, dense, countable, generic

-/

namespace order

variables {P : Type*}

/-- An ideal on a preorder `P` is a subset of `P` that is
  - nonempty
  - upward directed (any pair of elements in the ideal has an upper bound in the ideal)
  - downward closed (any element less than an element of the ideal is in the ideal). -/
structure ideal (P) [preorder P] :=
(carrier   : set P)
(nonempty  : carrier.nonempty)
(directed  : directed_on (≤) carrier)
(mem_of_le : ∀ {x y : P}, x ≤ y → y ∈ carrier → x ∈ carrier)

/-- A subset of a preorder `P` is an ideal if it is
  - nonempty
  - upward directed (any pair of elements in the ideal has an upper bound in the ideal)
  - downward closed (any element less than an element of the ideal is in the ideal). -/
@[mk_iff] structure is_ideal {P} [preorder P] (I : set P) : Prop :=
(nonempty : I.nonempty)
(directed : directed_on (≤) I)
(mem_of_le : ∀ {x y : P}, x ≤ y → y ∈ I → x ∈ I)

/-- Create an element of type `order.ideal` from a set satisfying the predicate
`order.is_ideal`. -/
def is_ideal.to_ideal [preorder P] {I : set P} (h : is_ideal I) : ideal P :=
⟨I, h.1, h.2, h.3⟩

namespace ideal

section preorder
variables [preorder P] {x y : P} {I J : ideal P}

/-- The smallest ideal containing a given element. -/
def principal (p : P) : ideal P :=
{ carrier   := { x | x ≤ p },
  nonempty  := ⟨p, le_refl _⟩,
  directed  := λ x hx y hy, ⟨p, le_refl _, hx, hy⟩,
  mem_of_le := λ x y hxy hy, le_trans hxy hy, }

instance [inhabited P] : inhabited (ideal P) :=
⟨ideal.principal $ default P⟩

/-- An ideal of `P` can be viewed as a subset of `P`. -/
instance : has_coe (ideal P) (set P) := ⟨carrier⟩

/-- For the notation `x ∈ I`. -/
instance : has_mem P (ideal P) := ⟨λ x I, x ∈ (I : set P)⟩

@[simp] lemma mem_coe : x ∈ (I : set P) ↔ x ∈ I := iff_of_eq rfl

@[simp] lemma mem_principal : x ∈ principal y ↔ x ≤ y := by refl

/-- Two ideals are equal when their underlying sets are equal. -/
@[ext] lemma ext : ∀ (I J : ideal P), (I : set P) = J → I = J
| ⟨_, _, _, _⟩ ⟨_, _, _, _⟩ rfl := rfl

@[simp, norm_cast] lemma ext_set_eq {I J : ideal P} : (I : set P) = J ↔ I = J :=
⟨by ext, congr_arg _⟩

lemma ext'_iff {I J : ideal P} : I = J ↔ (I : set P) = J := ext_set_eq.symm

lemma is_ideal (I : ideal P) : is_ideal (I : set P) := ⟨I.2, I.3, I.4⟩

/-- The partial ordering by subset inclusion, inherited from `set P`. -/
instance : partial_order (ideal P) := partial_order.lift coe ext

@[trans] lemma mem_of_mem_of_le : x ∈ I → I ≤ J → x ∈ J :=
@set.mem_of_mem_of_subset P x I J

@[simp] lemma principal_le_iff : principal x ≤ I ↔ x ∈ I :=
⟨λ (h : ∀ {y}, y ≤ x → y ∈ I), h (le_refl x),
 λ h_mem y (h_le : y ≤ x), I.mem_of_le h_le h_mem⟩

lemma mem_compl_of_ge {x y : P} : x ≤ y → x ∈ (I : set P)ᶜ → y ∈ (I : set P)ᶜ :=
λ h, mt (I.mem_of_le h)

/-- A proper ideal is one that is not the whole set.
    Note that the whole set might not be an ideal. -/
@[mk_iff] class is_proper (I : ideal P) : Prop := (ne_univ : (I : set P) ≠ set.univ)

lemma is_proper_of_not_mem {I : ideal P} {p : P} (nmem : p ∉ I) : is_proper I :=
⟨λ hp, begin
  change p ∉ ↑I at nmem,
  rw hp at nmem,
  exact nmem (set.mem_univ p),
end⟩

/-- An ideal is maximal if it is maximal in the collection of proper ideals.
  Note that we cannot use the `is_coatom` class because `P` might not have a `top` element.
-/
@[mk_iff] class is_maximal (I : ideal P) extends is_proper I : Prop :=
(maximal_proper : ∀ ⦃J : ideal P⦄, I < J → (J : set P) = set.univ)

variable (P)

/-- A preorder `P` has the `ideal_inter_nonempty` property if the
    intersection of any two ideals is nonempty.
    Most importantly, a `semilattice_sup` preorder with this property
    satisfies that its ideal poset is a lattice.
-/
class ideal_inter_nonempty : Prop :=
(inter_nonempty : ∀ (I J : ideal P), ((I : set P) ∩ (J : set P)).nonempty)

/-- A preorder `P` has the `ideal_Inter_nonempty` property if the
    intersection of all ideals is nonempty.
    Most importantly, a `semilattice_sup` preorder with this property
    satisfies that its ideal poset is a complete lattice.
-/
class ideal_Inter_nonempty : Prop :=
(Inter_nonempty : (⋂ (I : ideal P), (I : set P)).nonempty)

variable {P}

lemma inter_nonempty [ideal_inter_nonempty P] :
  ∀ (I J : ideal P), ((I : set P) ∩ (J : set P)).nonempty :=
ideal_inter_nonempty.inter_nonempty

lemma Inter_nonempty [ideal_Inter_nonempty P] :
  (⋂ (I : ideal P), (I : set P)).nonempty :=
ideal_Inter_nonempty.Inter_nonempty

lemma ideal_Inter_nonempty.exists_all_mem [ideal_Inter_nonempty P] :
  ∃ a : P, ∀ I : ideal P, a ∈ I :=
begin
  change ∃ (a : P), ∀ (I : ideal P), a ∈ (I : set P),
  rw ← set.nonempty_Inter,
  exact Inter_nonempty,
end

lemma ideal_Inter_nonempty_of_exists_all_mem (h : ∃ a : P, ∀ I : ideal P, a ∈ I) :
  ideal_Inter_nonempty P :=
{ Inter_nonempty := by rwa set.nonempty_Inter }

lemma ideal_Inter_nonempty_iff :
  ideal_Inter_nonempty P ↔ ∃ a : P, ∀ I : ideal P, a ∈ I :=
⟨λ _, by exactI ideal_Inter_nonempty.exists_all_mem, ideal_Inter_nonempty_of_exists_all_mem⟩

end preorder

section order_bot
variables [preorder P] [order_bot P] {I : ideal P}

/-- A specific witness of `I.nonempty` when `P` has a bottom element. -/
@[simp] lemma bot_mem : ⊥ ∈ I :=
I.mem_of_le bot_le I.nonempty.some_mem

/-- There is a bottom ideal when `P` has a bottom element. -/
instance : order_bot (ideal P) :=
{ bot := principal ⊥,
  bot_le := by simp }

@[priority 100]
instance order_bot.ideal_Inter_nonempty : ideal_Inter_nonempty P :=
by { rw ideal_Inter_nonempty_iff, exact ⟨⊥, λ I, bot_mem⟩ }

end order_bot

section order_top

variables [preorder P] [order_top P]

/-- There is a top ideal when `P` has a top element. -/
instance : order_top (ideal P) :=
{ top := principal ⊤,
  le_top := λ I x h, le_top }

@[simp] lemma coe_top : ((⊤ : ideal P) : set P) = set.univ :=
set.univ_subset_iff.1 (λ p _, le_top)

lemma top_of_mem_top {I : ideal P} (mem_top : ⊤ ∈ I) : I = ⊤ :=
begin
  ext,
  change x ∈ I ↔ x ∈ ((⊤ : ideal P) : set P),
  split,
  { simp [coe_top] },
  { exact λ _, I.mem_of_le le_top mem_top }
end

lemma is_proper_of_ne_top {I : ideal P} (ne_top : I ≠ ⊤) : is_proper I :=
is_proper_of_not_mem (λ h, ne_top (top_of_mem_top h))

lemma is_proper.ne_top {I : ideal P} (hI : is_proper I) : I ≠ ⊤ :=
begin
  intro h,
  rw [ext'_iff, coe_top] at h,
  apply hI.ne_univ,
  assumption,
end

lemma is_proper.top_not_mem {I : ideal P} (hI : is_proper I) : ⊤ ∉ I :=
by { by_contra, exact hI.ne_top (top_of_mem_top h) }

lemma _root_.is_coatom.is_proper {I : ideal P} (hI : is_coatom I) : is_proper I :=
is_proper_of_ne_top hI.1

lemma is_proper_iff_ne_top {I : ideal P} : is_proper I ↔ I ≠ ⊤ :=
⟨λ h, h.ne_top, λ h, is_proper_of_ne_top h⟩

lemma is_maximal.is_coatom {I : ideal P} (h : is_maximal I) : is_coatom I :=
⟨is_maximal.to_is_proper.ne_top,
 λ _ _, by { rw [ext'_iff, coe_top], exact is_maximal.maximal_proper ‹_› }⟩

lemma is_maximal.is_coatom' {I : ideal P} [is_maximal I] : is_coatom I :=
is_maximal.is_coatom ‹_›

lemma _root_.is_coatom.is_maximal {I : ideal P} (hI : is_coatom I) : is_maximal I :=
{ maximal_proper := λ _ _, by simp [hI.2 _ ‹_›],
  ..is_coatom.is_proper ‹_› }

lemma is_maximal_iff_is_coatom {I : ideal P} : is_maximal I ↔ is_coatom I :=
⟨λ h, h.is_coatom, λ h, h.is_maximal⟩

end order_top

section semilattice_sup
variables [semilattice_sup P] {x y : P} {I : ideal P}

/-- A specific witness of `I.directed` when `P` has joins. -/
lemma sup_mem (x y ∈ I) : x ⊔ y ∈ I :=
let ⟨z, h_mem, hx, hy⟩ := I.directed x ‹_› y ‹_› in
I.mem_of_le (sup_le hx hy) h_mem

@[simp] lemma sup_mem_iff : x ⊔ y ∈ I ↔ x ∈ I ∧ y ∈ I :=
⟨λ h, ⟨I.mem_of_le le_sup_left h, I.mem_of_le le_sup_right h⟩,
 λ h, sup_mem x y h.left h.right⟩

end semilattice_sup

section semilattice_sup_ideal_inter_nonempty

variables [semilattice_sup P] [ideal_inter_nonempty P] {x : P} {I J K : ideal P}

/-- The intersection of two ideals is an ideal, when it is nonempty and `P` has joins. -/
def inf (I J : ideal P) : ideal P :=
{ carrier   := I ∩ J,
  nonempty  := inter_nonempty I J,
  directed  := λ x ⟨_, _⟩ y ⟨_, _⟩, ⟨x ⊔ y, ⟨sup_mem x y ‹_› ‹_›, sup_mem x y ‹_› ‹_›⟩, by simp⟩,
  mem_of_le := λ x y h ⟨_, _⟩, ⟨mem_of_le I h ‹_›, mem_of_le J h ‹_›⟩ }

/-- There is a smallest ideal containing two ideals, when their intersection is nonempty and
    `P` has joins. -/
def sup (I J : ideal P) : ideal P :=
{ carrier   := {x | ∃ (i ∈ I) (j ∈ J), x ≤ i ⊔ j},
  nonempty  := by { cases inter_nonempty I J, exact ⟨w, w, h.1, w, h.2, le_sup_left⟩ },
  directed  := λ x ⟨xi, _, xj, _, _⟩ y ⟨yi, _, yj, _, _⟩,
    ⟨x ⊔ y,
     ⟨xi ⊔ yi, sup_mem xi yi ‹_› ‹_›,
      xj ⊔ yj, sup_mem xj yj ‹_› ‹_›,
      sup_le
        (calc x ≤ xi ⊔ xj               : ‹_›
         ...    ≤ (xi ⊔ yi) ⊔ (xj ⊔ yj) : sup_le_sup le_sup_left le_sup_left)
        (calc y ≤ yi ⊔ yj               : ‹_›
         ...    ≤ (xi ⊔ yi) ⊔ (xj ⊔ yj) : sup_le_sup le_sup_right le_sup_right)⟩,
     le_sup_left, le_sup_right⟩,
  mem_of_le := λ x y _ ⟨yi, _, yj, _, _⟩, ⟨yi, ‹_›, yj, ‹_›, le_trans ‹x ≤ y› ‹_›⟩ }

lemma sup_le : I ≤ K → J ≤ K → sup I J ≤ K :=
λ hIK hJK x ⟨i, hiI, j, hjJ, hxij⟩,
K.mem_of_le hxij $ sup_mem i j (mem_of_mem_of_le hiI hIK) (mem_of_mem_of_le hjJ hJK)

instance : lattice (ideal P) :=
{ sup          := sup,
  le_sup_left  := λ I J (i ∈ I), by { cases nonempty J, exact ⟨i, ‹_›, w, ‹_›, le_sup_left⟩ },
  le_sup_right := λ I J (j ∈ J), by { cases nonempty I, exact ⟨w, ‹_›, j, ‹_›, le_sup_right⟩ },
  sup_le       := @sup_le _ _ _,
  inf          := inf,
  inf_le_left  := λ I J, set.inter_subset_left I J,
  inf_le_right := λ I J, set.inter_subset_right I J,
  le_inf       := λ I J K, set.subset_inter,
  .. ideal.partial_order }

@[simp] lemma mem_inf : x ∈ I ⊓ J ↔ x ∈ I ∧ x ∈ J := iff_of_eq rfl

@[simp] lemma mem_sup : x ∈ I ⊔ J ↔ ∃ (i ∈ I) (j ∈ J), x ≤ i ⊔ j := iff_of_eq rfl

lemma lt_sup_principal_of_not_mem (hx : x ∉ I) : I < I ⊔ principal x :=
begin
  apply lt_of_le_of_ne le_sup_left,
  intro h,
  simp at h,
  exact hx h
end

end semilattice_sup_ideal_inter_nonempty

section ideal_Inter_nonempty

variables [preorder P] [ideal_Inter_nonempty P]

@[priority 100]
instance ideal_Inter_nonempty.ideal_inter_nonempty : ideal_inter_nonempty P :=
{ inter_nonempty := λ _ _, begin
    obtain ⟨a, ha⟩ : ∃ a : P, ∀ I : ideal P, a ∈ I := ideal_Inter_nonempty.exists_all_mem,
    exact ⟨a, ha _, ha _⟩
  end }

variables {α β γ : Type*} {ι : Sort*}

lemma ideal_Inter_nonempty.all_Inter_nonempty {f : ι → ideal P} :
  (⋂ x, (f x : set P)).nonempty :=
begin
  obtain ⟨a, ha⟩ : ∃ a : P, ∀ I : ideal P, a ∈ I := ideal_Inter_nonempty.exists_all_mem,
  exact ⟨a, by simp [ha]⟩
end

lemma ideal_Inter_nonempty.all_bInter_nonempty {f : α → ideal P} {s : set α} :
  (⋂ x ∈ s, (f x : set P)).nonempty :=
begin
  obtain ⟨a, ha⟩ : ∃ a : P, ∀ I : ideal P, a ∈ I := ideal_Inter_nonempty.exists_all_mem,
  exact ⟨a, by simp [ha]⟩
end

end ideal_Inter_nonempty

section semilattice_sup_ideal_Inter_nonempty

variables [semilattice_sup P] [ideal_Inter_nonempty P] {x : P} {I J K : ideal P}

instance : has_Inf (ideal P) :=
{ Inf := λ s, { carrier := ⋂ (I ∈ s), (I : set P),
  nonempty := ideal_Inter_nonempty.all_bInter_nonempty,
  directed := λ x hx y hy, ⟨x ⊔ y, ⟨λ S ⟨I, hS⟩,
    begin
      simp only [←hS, sup_mem_iff, mem_coe, set.mem_Inter],
      intro hI,
      rw set.mem_bInter_iff at *,
      exact ⟨hx _ hI, hy _ hI⟩
    end,
    le_sup_left, le_sup_right⟩⟩,
  mem_of_le := λ x y hxy hy,
    begin
      rw set.mem_bInter_iff at *,
      exact λ I hI, mem_of_le I ‹_› (hy I hI)
    end } }

variables {s : set (ideal P)}

@[simp] lemma mem_Inf : x ∈ Inf s ↔ ∀ I ∈ s, x ∈ I :=
by { change x ∈ (⋂ (I ∈ s), (I : set P)) ↔ ∀ I ∈ s, x ∈ I, simp }

@[simp] lemma coe_Inf : ↑(Inf s) = ⋂ (I ∈ s), (I : set P) := rfl

lemma Inf_le (hI : I ∈ s) : Inf s ≤ I :=
λ _ hx, hx I ⟨I, by simp [hI]⟩

lemma le_Inf (h : ∀ J ∈ s, I ≤ J) : I ≤ Inf s :=
λ _ _, by { simp only [mem_coe, coe_Inf, set.mem_Inter], tauto }

lemma is_glb_Inf : is_glb s (Inf s) := ⟨λ _, Inf_le, λ _, le_Inf⟩

instance : complete_lattice (ideal P) :=
{ ..ideal.lattice,
  ..complete_lattice_of_Inf (ideal P) (λ _, @is_glb_Inf _ _ _ _) }

end semilattice_sup_ideal_Inter_nonempty

section semilattice_inf

variable [semilattice_inf P]

@[priority 100]
instance semilattice_inf.ideal_inter_nonempty : ideal_inter_nonempty P :=
{ inter_nonempty := λ I J, begin
    cases I.nonempty with i _,
    cases J.nonempty with j _,
    exact ⟨i ⊓ j, I.mem_of_le inf_le_left ‹_›, J.mem_of_le inf_le_right ‹_›⟩
  end }

end semilattice_inf

section distrib_lattice

variables [distrib_lattice P]
variables {I J : ideal P}

lemma eq_sup_of_le_sup {x i j: P} (hi : i ∈ I) (hj : j ∈ J) (hx : x ≤ i ⊔ j):
∃ (i' ∈ I) (j' ∈ J), x = i' ⊔ j' :=
begin
  refine ⟨x ⊓ i, I.mem_of_le inf_le_right hi, x ⊓ j, J.mem_of_le inf_le_right hj, _⟩,
  calc
  x    = x ⊓ (i ⊔ j)       : left_eq_inf.mpr hx
  ...  = (x ⊓ i) ⊔ (x ⊓ j) : inf_sup_left,
end

lemma coe_sup_eq : ↑(I ⊔ J) = {x | ∃ i ∈ I, ∃ j ∈ J, x = i ⊔ j} :=
begin
  ext,
  rw [mem_coe, mem_sup],
  exact ⟨λ ⟨_, _, _, _, _⟩, eq_sup_of_le_sup ‹_› ‹_› ‹_›,
  λ ⟨i, _, j, _, _⟩, ⟨i, ‹_›, j, ‹_›, le_of_eq ‹_›⟩⟩
end

end distrib_lattice

section boolean_algebra

variables [boolean_algebra P] {x : P} {I : ideal P}

lemma is_proper.not_mem_of_compl_mem (hI : is_proper I) (hxc : xᶜ ∈ I) : x ∉ I :=
begin
  intro hx,
  apply hI.top_not_mem,
  have ht : x ⊔ xᶜ ∈ I := sup_mem _ _ ‹_› ‹_›,
  rwa sup_compl_eq_top at ht,
end

lemma is_proper.not_mem_or_compl_not_mem (hI : is_proper I) : x ∉ I ∨ xᶜ ∉ I :=
have h : xᶜ ∈ I → x ∉ I := hI.not_mem_of_compl_mem, by tauto

end boolean_algebra

end ideal

/-- For a preorder `P`, `cofinal P` is the type of subsets of `P`
  containing arbitrarily large elements. They are the dense sets in
  the topology whose open sets are terminal segments. -/
structure cofinal (P) [preorder P] :=
(carrier : set P)
(mem_gt  : ∀ x : P, ∃ y ∈ carrier, x ≤ y)

namespace cofinal

variables [preorder P]

instance : inhabited (cofinal P) :=
⟨{ carrier := set.univ, mem_gt := λ x, ⟨x, trivial, le_refl _⟩ }⟩

instance : has_mem P (cofinal P) := ⟨λ x D, x ∈ D.carrier⟩

variables (D : cofinal P) (x : P)
/-- A (noncomputable) element of a cofinal set lying above a given element. -/
noncomputable def above : P := classical.some $ D.mem_gt x

lemma above_mem : D.above x ∈ D :=
exists.elim (classical.some_spec $ D.mem_gt x) $ λ a _, a

lemma le_above : x ≤ D.above x :=
exists.elim (classical.some_spec $ D.mem_gt x) $ λ _ b, b

end cofinal

section ideal_of_cofinals

variables [preorder P] (p : P) {ι : Type*} [encodable ι] (𝒟 : ι → cofinal P)

/-- Given a starting point, and a countable family of cofinal sets,
  this is an increasing sequence that intersects each cofinal set. -/
noncomputable def sequence_of_cofinals : ℕ → P
| 0 := p
| (n+1) := match encodable.decode ι n with
           | none   := sequence_of_cofinals n
           | some i := (𝒟 i).above (sequence_of_cofinals n)
           end

lemma sequence_of_cofinals.monotone : monotone (sequence_of_cofinals p 𝒟) :=
by { apply monotone_nat_of_le_succ, intros n, dunfold sequence_of_cofinals,
  cases encodable.decode ι n, { refl }, { apply cofinal.le_above }, }

lemma sequence_of_cofinals.encode_mem (i : ι) :
  sequence_of_cofinals p 𝒟 (encodable.encode i + 1) ∈ 𝒟 i :=
by { dunfold sequence_of_cofinals, rw encodable.encodek, apply cofinal.above_mem, }

/-- Given an element `p : P` and a family `𝒟` of cofinal subsets of a preorder `P`,
  indexed by a countable type, `ideal_of_cofinals p 𝒟` is an ideal in `P` which
  - contains `p`, according to `mem_ideal_of_cofinals p 𝒟`, and
  - intersects every set in `𝒟`, according to `cofinal_meets_ideal_of_cofinals p 𝒟`.

  This proves the Rasiowa–Sikorski lemma. -/
def ideal_of_cofinals : ideal P :=
{ carrier   := { x : P | ∃ n, x ≤ sequence_of_cofinals p 𝒟 n },
  nonempty  := ⟨p, 0, le_refl _⟩,
  directed  := λ x ⟨n, hn⟩ y ⟨m, hm⟩,
               ⟨_, ⟨max n m, le_refl _⟩,
               le_trans hn $ sequence_of_cofinals.monotone p 𝒟 (le_max_left _ _),
               le_trans hm $ sequence_of_cofinals.monotone p 𝒟 (le_max_right _ _) ⟩,
  mem_of_le := λ x y hxy ⟨n, hn⟩, ⟨n, le_trans hxy hn⟩, }

lemma mem_ideal_of_cofinals : p ∈ ideal_of_cofinals p 𝒟 := ⟨0, le_refl _⟩

/-- `ideal_of_cofinals p 𝒟` is `𝒟`-generic. -/
lemma cofinal_meets_ideal_of_cofinals (i : ι) : ∃ x : P, x ∈ 𝒟 i ∧ x ∈ ideal_of_cofinals p 𝒟 :=
⟨_, sequence_of_cofinals.encode_mem p 𝒟 i, _, le_refl _⟩

end ideal_of_cofinals

end order


import set_theory.cardinal
import tactic

variables {α β : Type}

-- Definition 5.1 (Enumeration, set-theoretic). An enumeration of a set A is a
-- bijection whose range is A and whose domain is either an initial set of
-- natural numbers {0, 1, ..., n} or the entire set of natural numbers ℕ.
--
-- Definition 5.2. A set A is countable iff either A = ∅ or there is an
-- enumeration of A. We say that A is uncountable iff A is not countable.
def countable (α : Type) := ∃ f : α → ℕ, function.injective f

example : countable ℕ := ⟨id, function.injective_id⟩

def countable' (α : Type) := ∃ f : ℕ → α, function.surjective f

noncomputable example : nonempty α → α := nonempty.some

#check @function.inv_fun
#check @function.inv_fun_surjective

-- Problem 5.1. Show that a set A is countable iff either A = ∅ or there is a
-- surjection f : ℕ → A. Show that A is countable iff there is an injection
-- g : A → ℕ.
lemma problem_5_1_a [nonempty α] : countable α → countable' α :=
begin
  rintro ⟨f, hf⟩,
  change ∃ g, function.surjective g,
  let g := function.inv_fun f,
  have hg := function.inv_fun_surjective hf,
  exact ⟨g, hg⟩
end

lemma problem_5_1_b : countable' α → countable α :=
begin
  rintro ⟨f, hf⟩,
  let g := λ x, classical.some (hf x),
  use g,
  intros x y h,
  have := classical.some_spec (hf x),
  rw ←this, clear this,
  have := classical.some_spec (hf y),
  rw ←this, clear this,
  simp only [g] at h,
  rw h
end

lemma countable_iff_countable' [nonempty α] : countable α ↔ countable' α :=
⟨problem_5_1_a, problem_5_1_b⟩

-- Theorem 5.10. ℘(ℕ) is not countable.
theorem theorem_5_10 : ¬countable (set ℕ) :=
begin
  rw countable_iff_countable',
  rintro ⟨f, hf⟩,
  unfold function.surjective at hf,
  let s := {x | x ∉ f x},
  obtain ⟨x, hx⟩ := hf s,
  have : x ∈ s,
  { intro h,
    sorry },
  sorry
end

set_option pp.universes true
#check @cardinal.mk
set_option pp.universes false

prefix # := cardinal.mk

#check @function.inv_fun
#check @function.inv_fun_surjective

-- Theorem 5.16 (Cantor). A ≺ ℘(A), for any set A.
theorem theorem_5_16 : #α < #(set α) :=
begin
  change _ ∧ _,
  split,
  { let f : α → set α := λ x, {x},
    use f,
    intros x y h,
    simpa using h },
  { rintro ⟨f, hf⟩,
    let g := function.inv_fun f,
    have hg := function.inv_fun_surjective hf,
    let s := {x | x ∉ g x},
    obtain ⟨x, hx⟩ := hg s,
    have : x ∈ s ↔ x ∈ g x, by rw ←hx,
    simp only [set.mem_set_of_eq] at this,
    simpa }
end

#check @bit0
#check @bit1

variables (x : α ⊕ β) (f : α → ℕ)

#check @sum.cases_on _ _ (λ _, ℕ) x f

-- Problem 5.3. Show that if A and B are countable, so is A ∪ B.
lemma problem_5_3 (hα : countable α) (hβ : countable β) : countable (α ⊕ β) :=
begin
  obtain ⟨fα, hfα⟩ := hα,
  obtain ⟨fβ, hfβ⟩ := hβ,
  change ∃ f, _,
  let encode : α ⊕ β → ℕ :=
    λ x, @sum.cases_on _ _ (λ _, ℕ) x (bit0 ∘ fα) (bit1 ∘ fβ),
  use encode,
  intros x y h,
  cases x; cases y,
  { rw nat.bit0_eq_bit0 at h,
    rw (hfα h) },
  { simpa [encode] using h },
  { simpa [encode] using h },
  { rw nat.bit1_eq_bit1 at h,
    rw (hfβ h) }
end

-- Problem 5.16. Show that the set of all functions f : ℕ → ℕ is uncountable by
-- an explicit diagonal argument. That is, show that if f_1, f_2, ..., is a
-- list of functions and each f_i : ℕ → ℕ, then there is some g : ℕ → ℕ not on
-- this list.
lemma problem_5_16 : ¬ countable (ℕ → ℕ) :=
begin
  rw countable_iff_countable',
  rintro ⟨f, hf⟩,
  unfold function.surjective at hf,
  let g : ℕ → ℕ := λ x, f x x + 1,
  obtain ⟨x, hx⟩ := hf g,
  suffices : ∃ y, f x y ≠ g y,
  { obtain ⟨y, hy⟩ := this,
    rw hx at hy,
    apply hy,
    refl },
  use x,
  simp
end

-- Problem 5.17. Show that if there is an injective function g : B → A, and B
-- is uncountable, then so is A. Do this by showing how you can use g to turn
-- an enumeration of A into one of B.
lemma problem_5_17 {g : β → α} (hg : function.injective g) (h : ¬ countable β) :
  ¬ countable α :=
begin
  rintro ⟨f, hf⟩,
  apply h,
  clear h,
  change ∃ f, _,
  use f ∘ g,
  exact function.injective.comp hf hg
end

-- Problem 5.25. Show that there cannot be an injection g : ℘(A) → A, for any
-- set A. Hint: Suppose g : ℘(A) → A is injective. Consider
-- D = {g(B) : B ⊆ A and g(B) ∉ B}. Let x = g(D). Use the fact that g is
-- injective to derive a contradiction.
lemma problem_5_25 : ¬ ∃ f : set α → α, function.injective f :=
begin
  rintro ⟨f, hf⟩,
  let s := f <$> {x | f x ∉ x},
  let x := f s,
  change ∀ ⦃x y⦄, f x = f y → x = y at hf,
  sorry
end


namespace sf_basics

inductive day : Type
| monday : day
| tuesday : day
| wednesday : day
| thursday : day
| friday : day
| saturday : day
| sunday : day.

inductive day1 : Type
| monday
| tuesday
| wednesday
| thursday
| friday
| saturday
| sunday.
#check day1.monday

def next_weekday : day → day
| day.monday := day.tuesday
| day.tuesday := day.wednesday
| day.wednesday := day.thursday
| day.thursday := day.friday
| day.friday := day.saturday
| day.saturday := day.sunday
| day.sunday := day.monday.

#reduce next_weekday day.friday
#reduce next_weekday day.saturday

example : (next_weekday (next_weekday day.saturday)) = day.monday
        := by {reflexivity}

inductive bool : Type
| true
| false

namespace bool
def negb : bool → bool
| true := false
| false := true.

def andb : bool → bool → bool
| true b₂ := b₂
| false _ := false.

def orb : bool → bool → bool
| true _ := true
| false b₂ := b₂.

example : (orb true false) = true :=
    by { reflexivity }

example : (orb false false) = false :=
    by { reflexivity }

example : (orb false true) = true :=
    by { reflexivity }

example : (orb true true) = true :=
    by { reflexivity }

notation x `&&` y := (andb x y)
notation x `||` y := (orb x y)

example : false || false || true = true :=
    by { reflexivity }

def nandb (x y: bool):  bool := negb (x && y)

example : (nandb true false) = true :=
    by { reflexivity }
example : (nandb false false) = true :=
    by { reflexivity }
example : (nandb false true) = true :=
    by { reflexivity }
example : (nandb true true) = false :=
    by { reflexivity }

def andb3 (x y z : bool) : bool := x && y && z

example : (andb3 true true true) = true :=
    by { reflexivity }
example : (andb3 false true true) = false :=
    by { reflexivity }
example : (andb3 true false true) = false :=
    by { reflexivity }
example : (andb3 true true false) = false :=
    by { reflexivity }.

#check true
#check (negb true)
#check negb
#check true
end bool
namespace compound
open sf_basics.bool
--Compound types
inductive rgb : Type
| red : rgb
| green : rgb
| blue : rgb.

inductive color : Type
| black : color
| white : color
| primary : rgb → color.

def monochrome (c:color) : color → bool
| color.black := true
| color.white := true
| (color.primary p) := false.

def isred : color → bool
| color.black := false
| color.white := false
| (color.primary rgb.red) := true
| (color.primary _) := false.
end compound

inductive nat : Type
| O
| S : nat → nat

namespace nat
open sf_basics.bool
#check O

def pred : nat → nat
| O := O
| (S n') := n'

@[simp] def succn : nat → nat
| O := S O
| (S n') := S (S n')

#reduce pred (S (S (S (S O))))

def minustwo : nat → nat
| O := O
| (S O) := O
| (S (S n')) := n'

#reduce minustwo (S (S (S (S O))))

def evenb : nat → bool
| O := true
| (S O) := false
| (S (S n')) := evenb n'

#reduce evenb (S (S (S (S O))))
#reduce evenb (S (S (S O)))

def oddb (n:nat) : bool := negb (evenb n)

example : oddb (S O) = true := by {reflexivity}
example : oddb (S (S (S (S O)))) = false := by {reflexivity}

def plus : nat → nat → nat
| O m := m
| (S n') m := S (plus n' m)

#reduce plus (S (S (S O))) (S (S O))

def mult : nat → nat → nat
| O _ := O
| (S n') m := plus m (mult n' m)

@[simp] def n_to_nat : ℕ → nat
| 0 := O
| (nat.succ n') := S (n_to_nat n')

@[simp] def nat_to_n : nat → ℕ
| O := 0
| (S n') := nat.succ (nat_to_n n')

#reduce nat_to_n (S (S (S O)))

lemma n_to_nat_to_n : ∀ x : ℕ, nat_to_n (n_to_nat x) = x :=
by {intros, induction x; simp [*, n_to_nat, nat_to_n]}

example : (mult (S (S (S O))) (S (S (S O)))) = n_to_nat 9 :=
    by { reflexivity }

def minus : nat → nat → nat
| O _ := O
| (S n) O := n
| (S n') (S m') := minus n' m'

def exp : nat → nat → nat
| _ O := S O
| base (S p) := mult base (exp base p)

def factorial : nat → nat
| O := (S O)
| (S n) := mult (S n) (factorial n)

example : factorial (n_to_nat 3) = n_to_nat 6 :=
    by {reflexivity}

example : factorial (n_to_nat 5) = n_to_nat 120 :=
    by {reflexivity}

example : factorial (n_to_nat 5) = mult (n_to_nat 10) (n_to_nat 12) :=
    by {reflexivity}

notation x `+` y := plus x y
notation x `-` y := minus x y
notation x `*` y := mult x y
notation x `!!` := factorial x

example : factorial (n_to_nat 5) = (n_to_nat 10) * (n_to_nat 12) :=
    by {reflexivity}

example : (n_to_nat 5) !! = (n_to_nat 10) * (n_to_nat 12) :=
    by {reflexivity}

def beq_nat : nat → nat → bool
| O O := true
| O m := false
| m O := false
| (S n') (S m') := beq_nat n' m'

def leb : nat → nat → bool
| O _ := true
| _ O := false
| (S n') (S m') := leb n' m'

example : leb (n_to_nat 2) (n_to_nat 2) = true :=
    by {reflexivity}

example : leb (n_to_nat 2) (n_to_nat 4) = true :=
    by {reflexivity}

example : leb (n_to_nat 4) (n_to_nat 2) = false :=
    by {reflexivity}

def blt_nat (n m : nat) : bool := (leb n m) && negb (beq_nat n m)

example : blt_nat (n_to_nat 2) (n_to_nat 2) = false :=
    by {reflexivity}

example : blt_nat (n_to_nat 2) (n_to_nat 4) = true :=
    by {reflexivity}

example : blt_nat (n_to_nat 4) (n_to_nat 2) = false :=
    by {reflexivity}

theorem plus_O_n : ∀ n : nat, O + n = n :=
by {intros, simp [plus]}

namespace h1
theorem plus_O_n : ∀ n : nat, O + n = n :=
by {intros, reflexivity}
end h1

theorem plus_1_n : ∀ n : nat, (S O) + n = S n :=
by {intros, simp [plus]}

theorem mult_O_n : ∀ n : nat, O * n = O :=
by {intros, simp [mult]}

theorem plus_id_example : ∀ n m : nat,
    n = m → n+n =m+m :=
by {
    intros n m h,
    rw h
}

theorem plus_id_example1 : ∀ n m : nat,
    n = m → n+n =m+m :=
by {intros, simp [*]}

theorem plus_id_exercise : ∀ n m o : nat,
    n = m → m=o → n + m = m + o
:= by {
    intros n m o h₁ h₂,
    rw h₁, rw h₂,
}

theorem plus_id_exercise1 : ∀ n m o : nat,
    n = m → m=o → n + m = m + o
:= by { intros, simp [*] }

theorem mult_O_plus : ∀ n m : nat,
    (O + n) * m = n * m
:= by {intros, rw plus_O_n}

theorem mult_O_plus1 : ∀ n m : nat,
    (O + n) * m = n * m
:= by {intros, simp [plus_O_n]}

theorem mult_S_1 : forall n m : nat,
    m = S n → m * ((S O) + n) = m * m
:= by { intros, rw plus_1_n, rw ← a }

theorem mult_S_11 : forall n m : nat,
    m = S n → m * ((S O) + n) = m * m
:= by { intros, rw plus_1_n, rw a}

theorem mult_S_12 : forall n m : nat,
    m = S n → m * ((S O) + n) = m * m
:= by { intros, simp [*, plus_1_n]}

theorem mult_S_13 : forall n m : nat,
    m = S n → m * ((S O) + n) = m * m
:= by { intros, simp *, reflexivity}

theorem plus_1_neg_O : ∀ n : nat,
    beq_nat (n + (S O)) O = false
:= by {
    intros,
    cases n,
        { reflexivity },
        { reflexivity }
}

theorem plus_1_neg_O1 : ∀ n : nat,
    beq_nat (n + (S O)) O = false
:= by {intros, cases n; reflexivity}

theorem negb_involutive : ∀ b : bool,
    negb (negb b) = b
:= by { intros, cases b, reflexivity, reflexivity }

theorem negb_involutive1 : ∀ b : bool,
    negb (negb b) = b
:= by { intros, cases b; reflexivity}

theorem andb_commutative : ∀ b c, andb b c = andb c b
:= by {
    intros,
    cases b,
        cases c,
            reflexivity,
            reflexivity,
        cases c,
            reflexivity,
            reflexivity
}

theorem andb_commutative1 : ∀ b c, andb b c = andb c b
:= by {
    intros,
    cases b,
    { cases c,
        { reflexivity },
        { reflexivity }},
    { cases c,
        { reflexivity },
        { reflexivity }}}

theorem andb_commutative2 : ∀ b c, andb b c = andb c b
:= by {intros, cases b; cases c; reflexivity}

theorem andb3_exchange : ∀ b c d,
    andb (andb b c) d = andb (andb b d) c
:= by {
    intros, cases b; cases c; cases d; reflexivity
}

theorem andb3_exchange1 : ∀ b c d,
    andb (andb b c) d = andb (andb b d) c
:= by {
    intros,
    destruct b,
    {   intros, rw a,
        destruct c,
        {
            intros, rw a_1,
            destruct d,
            { intros, rw a_2 },
            { intros, rw a_2, reflexivity }
        },
        {
            intros, rw a_1,
            destruct d,
            { intros, rw a_2, reflexivity },
            { intros, rw a_2 }
        }
    },
    {   intros, rw a,
        destruct c,
        {
            intros, rw a_1,
            destruct d,
            { intros, rw a_2 },
            { intros, rw a_2, reflexivity }
        },
        {
            intros, rw a_1,
            destruct d,
            { intros, rw a_2, reflexivity },
            { intros, rw a_2 }
        }
    }
}

theorem andb3_exchange2 : ∀ b c d,
    andb (andb b c) d = andb (andb b d) c
:=  by {
    intros, destruct b; {
        intros, rw a, destruct c; {
            intros, rw a_1, destruct d; {
                intros, { rw a_2, reflexivity } <|> rw a_2 } } }
}

theorem andb3_exchange3 : ∀ b c d,
    andb (andb b c) d = andb (andb b d) c
:=  by {
    intros, destruct b; {
        intros, rw a, destruct c; {
            intros, rw a_1, destruct d; {
                intros, rw a_2, try { reflexivity } } } }
}

theorem andb_true_elim2 : ∀ b c : bool,
    andb b c = true → c = true
:= by {
    intros b c,
    cases b,
        cases c,
            { intro, reflexivity },
            { simp [andb] },
        cases c,
            { simp [andb] },
            { simp [andb] }
}

theorem andb_true_elim21 : ∀ b c : bool,
    andb b c = true → c = true
:= by {
    intros b c, cases b; cases c; {simp [andb], try {intro, assumption}}
}

theorem zero_nbeq_plus_1 : ∀ n : nat,
    beq_nat O ( n + (S O)) = false
:= by { intros, cases n; reflexivity }

def plus' : nat → nat → nat
| n O := n
| n (S m') := S (plus' n m')

-- def lim : ℕ → ℕ
-- | 5 := 5
-- | n := if n > 5 then lim n-1 else lim n+1

theorem identity_fn_applied_twice :
    ∀ f : bool → bool, (∀ x : bool, f x = x) → ∀ b : bool, f (f b) = b
:= by { intros, cases b; simp [*] }

theorem identity_fn_applied_twice1 :
    ∀ f : bool → bool, (∀ x : bool, f x = x) → ∀ b : bool, f (f b) = b
:= by { intros, rw a, rw a }

theorem identity_fn_applied_twice2 :
    ∀ f : bool → bool, (∀ x : bool, f x = x) → ∀ b : bool, f (f b) = b
:= by { intros, repeat {rw a} }

theorem negation_fn_applied_twice :
    ∀ f : bool → bool, (∀ x : bool, f x = negb x) → ∀ b : bool, f (f b) = b
:= by { intros, repeat { rw a }, simp [negb_involutive] }

theorem negation_fn_applied_twice1 :
    ∀ f : bool → bool, (∀ x : bool, f x = negb x) → ∀ b : bool, f (f b) = b
:= by { intros, repeat { rw a }, apply negb_involutive }

theorem andb_eq_orb :
    ∀ ( b c : bool), (andb b c = orb b c) → b = c
:= by { intros b c, cases b; cases c; simp [andb, orb]; intro; rw a }

end nat

inductive bin : Type
| zero
| twice : bin → bin
| twicep1 : bin → bin

namespace bin
open sf_basics.nat
@[simp] def incr : bin → bin
| zero          := twicep1 zero
| (twice n)     := twicep1 n
| (twicep1 n)   := twice (incr n)

@[simp] def bin_to_nat : bin → nat
| zero          := O
| (twice n)     := (S (S O)) * (bin_to_nat n)
| (twicep1 n)   := ((S (S O)) * (bin_to_nat n)) + (S O)

def one     : bin := (twicep1 zero)
def two     : bin := (twice (twicep1 zero))
def three   : bin := (twicep1 (twicep1 zero))
def four    : bin := (twice (twice (twicep1 zero)))

example : bin_to_nat zero   = O                 := by { reflexivity }
example : bin_to_nat one    = S O               := by { reflexivity }
example : bin_to_nat two    = S (S O)           := by { reflexivity }
example : bin_to_nat three  = S (S (S O))       := by { reflexivity }
example : bin_to_nat four   = S( S (S (S O)))   := by { reflexivity }

example : bin_to_nat(incr zero) = S O                   := by {reflexivity}
example : bin_to_nat(incr one)  = S (S O)               := by {reflexivity}
example : bin_to_nat(incr two)  = S (S (S O))           := by {reflexivity}
example : bin_to_nat(incr three)= S (S (S (S O)))       := by {reflexivity}
example : bin_to_nat(incr four) = S (S (S (S (S O))))   := by {reflexivity}

theorem plus_n_O : ∀ n, n + O = n :=
by { intros, induction n; simp[*, plus]}

lemma p1_sa_left1 : ∀ a,  S O + a = S a := by { intros, simp [plus] }
lemma p1_sa_left2 : ∀ a, a + S O = S a := by { intros, induction a; simp [*, plus] }

lemma succ_n_n1 : ∀ x : nat,  succn x = x + S O :=
by {intros, cases x, reflexivity, simp [succn, plus], rw p1_sa_left2 }

theorem succ_comm : ∀ x y : nat,  S x + y = S (x + y) :=
by {intros,
    induction y,
    reflexivity,
    simp [plus] }

theorem plus_1_n : ∀ x : nat, S O + x = S (x) := by {intros, reflexivity}
theorem plus_n_1 : ∀ x : nat, x + S O = S (x) := by {intros, induction x, reflexivity, simp [*, succ_comm]}

theorem succ_com1 : ∀ x y : nat,  x + S y = S (x + y) :=
by {
    intros,
    induction x,
        reflexivity,
        symmetry, rw succ_comm, rw ← ih_1, symmetry, rw succ_comm
}

theorem commutativity : ∀ x y : nat, x + y = y + x :=
by {
    intros,
    induction x generalizing y,
    {simp [plus_O_n], rw plus_n_O },
    simp [plus], symmetry,
        induction y,
            simp [plus_O_n, plus_n_O],
            simp [plus], rw ih_1_1, symmetry, simp [*, plus]
}

lemma small_assoc : ∀ x y z : nat, x + y + z = x + (y + z) :=
by {
    intros,
    induction x generalizing y z,
    reflexivity,
    simp [commutative, succ_comm], rw ih_1
}

theorem associativity : ∀ x y z : nat, (x + y) + z = x + (y + z) :=
by {
    intros,
    induction z generalizing x y,
        simp [plus_n_O],
        simp [commutativity, succ_comm, small_assoc],
}

lemma shuffling : ∀ x y z : nat, x + y + z = x + z + y :=
by {
    intros, induction x,
        simp [plus_O_n, commutativity],
        simp [commutativity, succ_com1],
            rw commutativity, rw associativity, rw commutativity, rw ← ih_1,
            symmetry, rw commutativity, rw associativity, rw commutativity
}

theorem distributivity : ∀ x y z: nat, x*(y + z) = x*y + x*z :=
by {
    intros, induction x generalizing z y,
    {reflexivity},
    simp [*, mult], rw ← small_assoc, symmetry, rw ← associativity, rw shuffling, rw commutativity, rw associativity, simp [commutativity, associativity],
}

lemma mult_n_1 : ∀ x : nat, x * S O = x :=
by {
    intros, induction x,
        reflexivity,
        simp [mult], rw ih_1, simp [plus_1_n]
}

lemma plus_1_1 : S O + S O = S (S O) :=
by {reflexivity}

theorem same_result : ∀ b : bin, bin_to_nat (incr b) = succn (bin_to_nat b) :=
by {
    intros,
    induction b,
    reflexivity,
    simp [*], rw ← succn, generalize h : succn (S O) * bin_to_nat a = x, simp [succ_n_n1],
    simp [*, succ_n_n1, distributivity, mult_n_1], simp [associativity], reflexivity
}

end bin

end sf_basics


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

! This file was ported from Lean 3 source module category_theory.linear.yoneda
! leanprover-community/mathlib commit 09f981f72d43749f1fa072deade828d9c1e185bb
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Category.Module.Basic
import Mathbin.CategoryTheory.Linear.Basic
import Mathbin.CategoryTheory.Preadditive.Yoneda.Basic

/-!
# The Yoneda embedding for `R`-linear categories

The Yoneda embedding for `R`-linear categories `C`,
sends an object `X : C` to the `Module R`-valued presheaf on `C`,
with value on `Y : Cᵒᵖ` given by `Module.of R (unop Y ⟶ X)`.

TODO: `linear_yoneda R C` is `R`-linear.
TODO: In fact, `linear_yoneda` itself is additive and `R`-linear.
-/

universe w v u

open Opposite

namespace CategoryTheory

variable (R : Type w) [Ring R] (C : Type u) [Category.{v} C] [Preadditive C] [Linear R C]

/-- The Yoneda embedding for `R`-linear categories `C`,
sending an object `X : C` to the `Module R`-valued presheaf on `C`,
with value on `Y : Cᵒᵖ` given by `Module.of R (unop Y ⟶ X)`. -/
@[simps]
def linearYoneda : C ⥤ Cᵒᵖ ⥤ ModuleCat R
    where
  obj X :=
    { obj := fun Y => ModuleCat.of R (unop Y ⟶ X)
      map := fun Y Y' f => Linear.leftComp R _ f.unop
      map_comp' := fun _ _ _ f g => LinearMap.ext fun _ => Category.assoc _ _ _
      map_id' := fun Y => LinearMap.ext fun _ => Category.id_comp _ }
  map X X' f :=
    { app := fun Y => Linear.rightComp R _ f
      naturality' := fun X Y f =>
        LinearMap.ext fun x => by
          simp only [category.assoc, ModuleCat.coe_comp, Function.comp_apply,
            linear.left_comp_apply, linear.right_comp_apply] }
  map_id' X :=
    NatTrans.ext _ _ <|
      funext fun _ =>
        LinearMap.ext fun _ => by
          simp only [linear.right_comp_apply, category.comp_id, nat_trans.id_app,
            ModuleCat.id_apply]
  map_comp' _ _ _ f g :=
    NatTrans.ext _ _ <|
      funext fun _ =>
        LinearMap.ext fun _ => by
          simp only [category.assoc, linear.right_comp_apply, nat_trans.comp_app,
            ModuleCat.coe_comp, Function.comp_apply]
#align category_theory.linear_yoneda CategoryTheory.linearYoneda

/-- The Yoneda embedding for `R`-linear categories `C`,
sending an object `Y : Cᵒᵖ` to the `Module R`-valued copresheaf on `C`,
with value on `X : C` given by `Module.of R (unop Y ⟶ X)`. -/
@[simps]
def linearCoyoneda : Cᵒᵖ ⥤ C ⥤ ModuleCat R
    where
  obj Y :=
    { obj := fun X => ModuleCat.of R (unop Y ⟶ X)
      map := fun Y Y' => Linear.rightComp _ _
      map_id' := fun Y => LinearMap.ext fun _ => Category.comp_id _
      map_comp' := fun _ _ _ f g => LinearMap.ext fun _ => Eq.symm (Category.assoc _ _ _) }
  map Y Y' f :=
    { app := fun X => Linear.leftComp _ _ f.unop
      naturality' := fun X Y f =>
        LinearMap.ext fun x => by
          simp only [category.assoc, ModuleCat.coe_comp, Function.comp_apply,
            linear.right_comp_apply, linear.left_comp_apply] }
  map_id' X :=
    NatTrans.ext _ _ <|
      funext fun _ =>
        LinearMap.ext fun _ => by
          simp only [linear.left_comp_apply, unop_id, category.id_comp, nat_trans.id_app,
            ModuleCat.id_apply]
  map_comp' _ _ _ f g :=
    NatTrans.ext _ _ <|
      funext fun _ =>
        LinearMap.ext fun _ => by
          simp only [category.assoc, ModuleCat.coe_comp, Function.comp_apply,
            linear.left_comp_apply, unop_comp, nat_trans.comp_app]
#align category_theory.linear_coyoneda CategoryTheory.linearCoyoneda

instance linearYoneda_obj_additive (X : C) : ((linearYoneda R C).obj X).Additive where
#align category_theory.linear_yoneda_obj_additive CategoryTheory.linearYoneda_obj_additive

instance linearCoyoneda_obj_additive (Y : Cᵒᵖ) : ((linearCoyoneda R C).obj Y).Additive where
#align category_theory.linear_coyoneda_obj_additive CategoryTheory.linearCoyoneda_obj_additive

@[simp]
theorem whiskering_linearYoneda :
    linearYoneda R C ⋙ (whiskeringRight _ _ _).obj (forget (ModuleCat.{v} R)) = yoneda :=
  rfl
#align category_theory.whiskering_linear_yoneda CategoryTheory.whiskering_linearYoneda

@[simp]
theorem whiskering_linear_yoneda₂ :
    linearYoneda R C ⋙ (whiskeringRight _ _ _).obj (forget₂ (ModuleCat.{v} R) AddCommGroupCat.{v}) =
      preadditiveYoneda :=
  rfl
#align category_theory.whiskering_linear_yoneda₂ CategoryTheory.whiskering_linear_yoneda₂

@[simp]
theorem whiskering_linearCoyoneda :
    linearCoyoneda R C ⋙ (whiskeringRight _ _ _).obj (forget (ModuleCat.{v} R)) = coyoneda :=
  rfl
#align category_theory.whiskering_linear_coyoneda CategoryTheory.whiskering_linearCoyoneda

@[simp]
theorem whiskering_linear_coyoneda₂ :
    linearCoyoneda R C ⋙
        (whiskeringRight _ _ _).obj (forget₂ (ModuleCat.{v} R) AddCommGroupCat.{v}) =
      preadditiveCoyoneda :=
  rfl
#align category_theory.whiskering_linear_coyoneda₂ CategoryTheory.whiskering_linear_coyoneda₂

instance linearYonedaFull : Full (linearYoneda R C) :=
  let yoneda_full :
    Full (linearYoneda R C ⋙ (whiskeringRight _ _ _).obj (forget (ModuleCat.{v} R))) :=
    Yoneda.yonedaFull
  full.of_comp_faithful (linear_yoneda R C)
    ((whiskering_right _ _ _).obj (forget (ModuleCat.{v} R)))
#align category_theory.linear_yoneda_full CategoryTheory.linearYonedaFull

instance linearCoyonedaFull : Full (linearCoyoneda R C) :=
  let coyoneda_full :
    Full (linearCoyoneda R C ⋙ (whiskeringRight _ _ _).obj (forget (ModuleCat.{v} R))) :=
    Coyoneda.coyonedaFull
  full.of_comp_faithful (linear_coyoneda R C)
    ((whiskering_right _ _ _).obj (forget (ModuleCat.{v} R)))
#align category_theory.linear_coyoneda_full CategoryTheory.linearCoyonedaFull

instance linearYoneda_faithful : Faithful (linearYoneda R C) :=
  Faithful.of_comp_eq (whiskering_linearYoneda R C)
#align category_theory.linear_yoneda_faithful CategoryTheory.linearYoneda_faithful

instance linearCoyoneda_faithful : Faithful (linearCoyoneda R C) :=
  Faithful.of_comp_eq (whiskering_linearCoyoneda R C)
#align category_theory.linear_coyoneda_faithful CategoryTheory.linearCoyoneda_faithful

end CategoryTheory