/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import algebra.hom.equiv
import data.part
import tactic.norm_num

/-!
# Natural numbers with infinity

The natural numbers and an extra `top` element `⊤`.

## Main definitions

The following instances are defined:

* `ordered_add_comm_monoid enat`
* `canonically_ordered_add_monoid enat`

There is no additive analogue of `monoid_with_zero`; if there were then `enat` could
be an `add_monoid_with_top`.

* `to_with_top` : the map from `enat` to `with_top ℕ`, with theorems that it plays well
with `+` and `≤`.

* `with_top_add_equiv : enat ≃+ with_top ℕ`
* `with_top_order_iso : enat ≃o with_top ℕ`

## Implementation details

`enat` is defined to be `part ℕ`.

`+` and `≤` are defined on `enat`, but there is an issue with `*` because it's not
clear what `0 * ⊤` should be. `mul` is hence left undefined. Similarly `⊤ - ⊤` is ambiguous
so there is no `-` defined on `enat`.

Before the `open_locale classical` line, various proofs are made with decidability assumptions.
This can cause issues -- see for example the non-simp lemma `to_with_top_zero` proved by `rfl`,
followed by `@[simp] lemma to_with_top_zero'` whose proof uses `convert`.

## Tags

enat, with_top ℕ
-/
open part (hiding some)

/-- Type of natural numbers with infinity (`⊤`) -/
def enat : Type := part ℕ

namespace enat

/-- The computable embedding `ℕ → enat`.

This coincides with the coercion `coe : ℕ → enat`, see `enat.some_eq_coe`.
However, `coe` is noncomputable so `some` is preferable when computability is a concern. -/
def some : ℕ → enat := part.some

instance : has_zero enat := ⟨some 0⟩
instance : inhabited enat := ⟨0⟩
instance : has_one enat := ⟨some 1⟩
instance : has_add enat := ⟨λ x y, ⟨x.dom ∧ y.dom, λ h, get x h.1 + get y h.2⟩⟩

instance (n : ℕ) : decidable (some n).dom := is_true trivial

@[simp] lemma dom_some (x : ℕ) : (some x).dom := trivial

instance : add_comm_monoid enat :=
{ add       := (+),
  zero      := (0),
  add_comm  := λ x y, part.ext' and.comm (λ _ _, add_comm _ _),
  zero_add  := λ x, part.ext' (true_and _) (λ _ _, zero_add _),
  add_zero  := λ x, part.ext' (and_true _) (λ _ _, add_zero _),
  add_assoc := λ x y z, part.ext' and.assoc (λ _ _, add_assoc _ _ _) }

instance : add_monoid_with_one enat :=
{ one := 1,
  nat_cast := some,
  nat_cast_zero := rfl,
  nat_cast_succ := λ _, part.ext' (true_and _).symm (λ _ _, rfl),
  .. enat.add_comm_monoid }

lemma some_eq_coe (n : ℕ) : some n = n := rfl

@[simp] lemma coe_inj {x y : ℕ} : (x : enat) = y ↔ x = y := part.some_inj

@[simp] lemma dom_coe (x : ℕ) : (x : enat).dom := trivial

instance : has_le enat := ⟨λ x y, ∃ h : y.dom → x.dom, ∀ hy : y.dom, x.get (h hy) ≤ y.get hy⟩
instance : has_top enat := ⟨none⟩
instance : has_bot enat := ⟨0⟩
instance : has_sup enat := ⟨λ x y, ⟨x.dom ∧ y.dom, λ h, x.get h.1 ⊔ y.get h.2⟩⟩

lemma le_def (x y : enat) : x ≤ y ↔ ∃ h : y.dom → x.dom, ∀ hy : y.dom, x.get (h hy) ≤ y.get hy :=
iff.rfl

@[elab_as_eliminator] protected lemma cases_on' {P : enat → Prop} :
  ∀ a : enat, P ⊤ → (∀ n : ℕ, P (some n)) → P a :=
part.induction_on

@[elab_as_eliminator] protected lemma cases_on {P : enat → Prop} :
  ∀ a : enat, P ⊤ → (∀ n : ℕ, P n) → P a :=
by { simp only [← some_eq_coe], exact enat.cases_on' }

@[simp] lemma top_add (x : enat) : ⊤ + x = ⊤ :=
part.ext' (false_and _) (λ h, h.left.elim)

@[simp] lemma add_top (x : enat) : x + ⊤ = ⊤ :=
by rw [add_comm, top_add]

@[simp] lemma coe_get {x : enat} (h : x.dom) : (x.get h : enat) = x :=
by { rw [← some_eq_coe], exact part.ext' (iff_of_true trivial h) (λ _ _, rfl) }

@[simp, norm_cast] lemma get_coe' (x : ℕ) (h : (x : enat).dom) : get (x : enat) h = x :=
by rw [← coe_inj, coe_get]

lemma get_coe {x : ℕ} : get (x : enat) (dom_coe x) = x := get_coe' _ _

lemma coe_add_get {x : ℕ} {y : enat} (h : ((x : enat) + y).dom) :
  get ((x : enat) + y) h = x + get y h.2 :=
by { simp only [← some_eq_coe] at h ⊢, refl }

@[simp] lemma get_add {x y : enat} (h : (x + y).dom) :
  get (x + y) h = x.get h.1 + y.get h.2 := rfl

@[simp] lemma get_zero (h : (0 : enat).dom) : (0 : enat).get h = 0 := rfl

@[simp] lemma get_one (h : (1 : enat).dom) : (1 : enat).get h = 1 := rfl

lemma get_eq_iff_eq_some {a : enat} {ha : a.dom} {b : ℕ} :
  a.get ha = b ↔ a = some b := get_eq_iff_eq_some

lemma get_eq_iff_eq_coe {a : enat} {ha : a.dom} {b : ℕ} :
  a.get ha = b ↔ a = b := by rw [get_eq_iff_eq_some, some_eq_coe]

lemma dom_of_le_of_dom {x y : enat} : x ≤ y → y.dom → x.dom := λ ⟨h, _⟩, h

lemma dom_of_le_some {x : enat} {y : ℕ} (h : x ≤ some y) : x.dom := dom_of_le_of_dom h trivial

lemma dom_of_le_coe {x : enat} {y : ℕ} (h : x ≤ y) : x.dom :=
by { rw [← some_eq_coe] at h, exact dom_of_le_some h }

instance decidable_le (x y : enat) [decidable x.dom] [decidable y.dom] : decidable (x ≤ y) :=
if hx : x.dom
then decidable_of_decidable_of_iff
  (show decidable (∀ (hy : (y : enat).dom), x.get hx ≤ (y : enat).get hy),
    from forall_prop_decidable _) $
  by { dsimp [(≤)], simp only [hx, exists_prop_of_true, forall_true_iff] }
else if hy : y.dom
then is_false $ λ h, hx $ dom_of_le_of_dom h hy
else is_true ⟨λ h, (hy h).elim, λ h, (hy h).elim⟩

/-- The coercion `ℕ → enat` preserves `0` and addition. -/
def coe_hom : ℕ →+ enat := ⟨coe, nat.cast_zero, nat.cast_add⟩

@[simp] lemma coe_coe_hom : ⇑coe_hom = coe := rfl

instance : partial_order enat :=
{ le          := (≤),
  le_refl     := λ x, ⟨id, λ _, le_rfl⟩,
  le_trans    := λ x y z ⟨hxy₁, hxy₂⟩ ⟨hyz₁, hyz₂⟩,
    ⟨hxy₁ ∘ hyz₁, λ _, le_trans (hxy₂ _) (hyz₂ _)⟩,
  le_antisymm := λ x y ⟨hxy₁, hxy₂⟩ ⟨hyx₁, hyx₂⟩, part.ext' ⟨hyx₁, hxy₁⟩
    (λ _ _, le_antisymm (hxy₂ _) (hyx₂ _)) }

lemma lt_def (x y : enat) : x < y ↔ ∃ (hx : x.dom), ∀ (hy : y.dom), x.get hx < y.get hy :=
begin
  rw [lt_iff_le_not_le, le_def, le_def, not_exists],
  split,
  { rintro ⟨⟨hyx, H⟩, h⟩,
    by_cases hx : x.dom,
    { use hx, intro hy,
      specialize H hy, specialize h (λ _, hy),
      rw not_forall at h, cases h with hx' h,
      rw not_le at h, exact h },
    { specialize h (λ hx', (hx hx').elim),
      rw not_forall at h, cases h with hx' h,
      exact (hx hx').elim } },
  { rintro ⟨hx, H⟩, exact ⟨⟨λ _, hx, λ hy, (H hy).le⟩, λ hxy h, not_lt_of_le (h _) (H _)⟩ }
end

@[simp, norm_cast] lemma coe_le_coe {x y : ℕ} : (x : enat) ≤ y ↔ x ≤ y :=
by { rw [← some_eq_coe, ← some_eq_coe], exact ⟨λ ⟨_, h⟩, h trivial, λ h, ⟨λ _, trivial, λ _, h⟩⟩ }

@[simp, norm_cast] lemma coe_lt_coe {x y : ℕ} : (x : enat) < y ↔ x < y :=
by rw [lt_iff_le_not_le, lt_iff_le_not_le, coe_le_coe, coe_le_coe]

@[simp] lemma get_le_get {x y : enat} {hx : x.dom} {hy : y.dom} :
  x.get hx ≤ y.get hy ↔ x ≤ y :=
by conv { to_lhs, rw [← coe_le_coe, coe_get, coe_get]}

lemma le_coe_iff (x : enat) (n : ℕ) : x ≤ n ↔ ∃ h : x.dom, x.get h ≤ n :=
begin
  rw [← some_eq_coe],
  show (∃ (h : true → x.dom), _) ↔ ∃ h : x.dom, x.get h ≤ n,
  simp only [forall_prop_of_true, some_eq_coe, dom_coe, get_coe']
end

lemma lt_coe_iff (x : enat) (n : ℕ) : x < n ↔ ∃ h : x.dom, x.get h < n :=
by simp only [lt_def, forall_prop_of_true, get_coe', dom_coe]

lemma coe_le_iff (n : ℕ) (x : enat) : (n : enat) ≤ x ↔ ∀ h : x.dom, n ≤ x.get h :=
begin
  rw [← some_eq_coe],
  simp only [le_def, exists_prop_of_true, dom_some, forall_true_iff],
  refl,
end

lemma coe_lt_iff (n : ℕ) (x : enat) : (n : enat) < x ↔ ∀ h : x.dom, n < x.get h :=
begin
  rw [← some_eq_coe],
  simp only [lt_def, exists_prop_of_true, dom_some, forall_true_iff],
  refl,
end

protected lemma zero_lt_one : (0 : enat) < 1 :=
by { norm_cast, norm_num }

instance semilattice_sup : semilattice_sup enat :=
{ sup := (⊔),
  le_sup_left := λ _ _, ⟨and.left, λ _, le_sup_left⟩,
  le_sup_right := λ _ _, ⟨and.right, λ _, le_sup_right⟩,
  sup_le := λ x y z ⟨hx₁, hx₂⟩ ⟨hy₁, hy₂⟩, ⟨λ hz, ⟨hx₁ hz, hy₁ hz⟩,
    λ _, sup_le (hx₂ _) (hy₂ _)⟩,
  ..enat.partial_order }

instance order_bot : order_bot enat :=
{ bot := (⊥),
  bot_le := λ _, ⟨λ _, trivial, λ _, nat.zero_le _⟩ }

instance order_top : order_top enat :=
{ top := (⊤),
  le_top := λ x, ⟨λ h, false.elim h, λ hy, false.elim hy⟩ }

lemma dom_of_lt {x y : enat} : x < y → x.dom :=
enat.cases_on x not_top_lt $ λ _ _, dom_coe _

lemma top_eq_none : (⊤ : enat) = none := rfl

@[simp] lemma coe_lt_top (x : ℕ) : (x : enat) < ⊤ :=
ne.lt_top (λ h, absurd (congr_arg dom h) $ by simpa only [dom_coe] using true_ne_false)

@[simp] lemma coe_ne_top (x : ℕ) : (x : enat) ≠ ⊤ := ne_of_lt (coe_lt_top x)

lemma ne_top_iff {x : enat} : x ≠ ⊤ ↔ ∃ (n : ℕ), x = n :=
by simpa only [← some_eq_coe] using part.ne_none_iff

lemma ne_top_iff_dom {x : enat} : x ≠ ⊤ ↔ x.dom :=
by classical; exact not_iff_comm.1 part.eq_none_iff'.symm

lemma ne_top_of_lt {x y : enat} (h : x < y) : x ≠ ⊤ :=
ne_of_lt $ lt_of_lt_of_le h le_top

lemma eq_top_iff_forall_lt (x : enat) : x = ⊤ ↔ ∀ n : ℕ, (n : enat) < x :=
begin
  split,
  { rintro rfl n, exact coe_lt_top _ },
  { contrapose!, rw ne_top_iff, rintro ⟨n, rfl⟩, exact ⟨n, irrefl _⟩ }
end

lemma eq_top_iff_forall_le (x : enat) : x = ⊤ ↔ ∀ n : ℕ, (n : enat) ≤ x :=
(eq_top_iff_forall_lt x).trans
⟨λ h n, (h n).le, λ h n, lt_of_lt_of_le (coe_lt_coe.mpr n.lt_succ_self) (h (n + 1))⟩

lemma pos_iff_one_le {x : enat} : 0 < x ↔ 1 ≤ x :=
enat.cases_on x (by simp only [iff_true, le_top, coe_lt_top, ← @nat.cast_zero enat]) $
  λ n, by { rw [← nat.cast_zero, ← nat.cast_one, enat.coe_lt_coe, enat.coe_le_coe], refl }

instance : is_total enat (≤) :=
{ total := λ x y, enat.cases_on x
    (or.inr le_top) (enat.cases_on y (λ _, or.inl le_top)
      (λ x y, (le_total x y).elim (or.inr ∘ coe_le_coe.2)
        (or.inl ∘ coe_le_coe.2))) }

noncomputable instance : linear_order enat :=
{ le_total := is_total.total,
  decidable_le := classical.dec_rel _,
  max := (⊔),
  max_def := @sup_eq_max_default _ _ (id _) _,
  ..enat.partial_order }

instance : bounded_order enat :=
{ ..enat.order_top,
  ..enat.order_bot }

noncomputable instance : lattice enat :=
{ inf := min,
  inf_le_left := min_le_left,
  inf_le_right := min_le_right,
  le_inf := λ _ _ _, le_min,
  ..enat.semilattice_sup }

instance : ordered_add_comm_monoid enat :=
{ add_le_add_left := λ a b ⟨h₁, h₂⟩ c,
    enat.cases_on c (by simp)
      (λ c, ⟨λ h, and.intro (dom_coe _) (h₁ h.2),
        λ h, by simpa only [coe_add_get] using add_le_add_left (h₂ _) c⟩),
  ..enat.linear_order,
  ..enat.add_comm_monoid }

instance : canonically_ordered_add_monoid enat :=
{ le_self_add := λ a b, enat.cases_on b (le_top.trans_eq (add_top _).symm) $ λ b, enat.cases_on a
    (top_add _).ge $ λ a, (coe_le_coe.2 le_self_add).trans_eq (nat.cast_add _ _),
  exists_add_of_le := λ a b, enat.cases_on b (λ _, ⟨⊤, (add_top _).symm⟩) $ λ b, enat.cases_on a
    (λ h, ((coe_lt_top _).not_le h).elim) $ λ a h, ⟨(b - a : ℕ),
        by rw [←nat.cast_add, coe_inj, add_comm, tsub_add_cancel_of_le (coe_le_coe.1 h)]⟩,
  ..enat.semilattice_sup,
  ..enat.order_bot,
  ..enat.ordered_add_comm_monoid }

protected lemma add_lt_add_right {x y z : enat} (h : x < y) (hz : z ≠ ⊤) : x + z < y + z :=
begin
  rcases ne_top_iff.mp (ne_top_of_lt h) with ⟨m, rfl⟩,
  rcases ne_top_iff.mp hz with ⟨k, rfl⟩,
  induction y using enat.cases_on with n,
  { rw [top_add], apply_mod_cast coe_lt_top },
  norm_cast at h, apply_mod_cast add_lt_add_right h
end

protected lemma add_lt_add_iff_right {x y z : enat} (hz : z ≠ ⊤) : x + z < y + z ↔ x < y :=
⟨lt_of_add_lt_add_right, λ h, enat.add_lt_add_right h hz⟩

protected lemma add_lt_add_iff_left {x y z : enat} (hz : z ≠ ⊤) : z + x < z + y ↔ x < y :=
by rw [add_comm z, add_comm z, enat.add_lt_add_iff_right hz]

protected lemma lt_add_iff_pos_right {x y : enat} (hx : x ≠ ⊤) : x < x + y ↔ 0 < y :=
by { conv_rhs { rw [← enat.add_lt_add_iff_left hx] }, rw [add_zero] }

lemma lt_add_one {x : enat} (hx : x ≠ ⊤) : x < x + 1 :=
by { rw [enat.lt_add_iff_pos_right hx], norm_cast, norm_num }

lemma le_of_lt_add_one {x y : enat} (h : x < y + 1) : x ≤ y :=
begin
  induction y using enat.cases_on with n, apply le_top,
  rcases ne_top_iff.mp (ne_top_of_lt h) with ⟨m, rfl⟩,
  apply_mod_cast nat.le_of_lt_succ, apply_mod_cast h
end

lemma add_one_le_of_lt {x y : enat} (h : x < y) : x + 1 ≤ y :=
begin
  induction y using enat.cases_on with n, apply le_top,
  rcases ne_top_iff.mp (ne_top_of_lt h) with ⟨m, rfl⟩,
  apply_mod_cast nat.succ_le_of_lt, apply_mod_cast h
end

lemma add_one_le_iff_lt {x y : enat} (hx : x ≠ ⊤) : x + 1 ≤ y ↔ x < y :=
begin
  split, swap, exact add_one_le_of_lt,
  intro h, rcases ne_top_iff.mp hx with ⟨m, rfl⟩,
  induction y using enat.cases_on with n, apply coe_lt_top,
  apply_mod_cast nat.lt_of_succ_le, apply_mod_cast h
end

lemma lt_add_one_iff_lt {x y : enat} (hx : x ≠ ⊤) : x < y + 1 ↔ x ≤ y :=
begin
  split, exact le_of_lt_add_one,
  intro h, rcases ne_top_iff.mp hx with ⟨m, rfl⟩,
  induction y using enat.cases_on with n, { rw [top_add], apply coe_lt_top },
  apply_mod_cast nat.lt_succ_of_le, apply_mod_cast h
end

lemma add_eq_top_iff {a b : enat} : a + b = ⊤ ↔ a = ⊤ ∨ b = ⊤ :=
by apply enat.cases_on a; apply enat.cases_on b;
  simp; simp only [(nat.cast_add _ _).symm, enat.coe_ne_top]; simp

protected lemma add_right_cancel_iff {a b c : enat} (hc : c ≠ ⊤) : a + c = b + c ↔ a = b :=
begin
  rcases ne_top_iff.1 hc with ⟨c, rfl⟩,
  apply enat.cases_on a; apply enat.cases_on b;
  simp [add_eq_top_iff, coe_ne_top, @eq_comm _ (⊤ : enat)];
  simp only [(nat.cast_add _ _).symm, add_left_cancel_iff, enat.coe_inj, add_comm];
  tauto
end

protected lemma add_left_cancel_iff {a b c : enat} (ha : a ≠ ⊤) : a + b = a + c ↔ b = c :=
by rw [add_comm a, add_comm a, enat.add_right_cancel_iff ha]

section with_top

/-- Computably converts an `enat` to a `with_top ℕ`. -/
def to_with_top (x : enat) [decidable x.dom] : with_top ℕ := x.to_option

lemma to_with_top_top : to_with_top ⊤ = ⊤ := rfl

@[simp] lemma to_with_top_top' {h : decidable (⊤ : enat).dom} : to_with_top ⊤ = ⊤ :=
by convert to_with_top_top

lemma to_with_top_zero : to_with_top 0 = 0 := rfl

@[simp] lemma to_with_top_zero' {h : decidable (0 : enat).dom} : to_with_top 0 = 0 :=
by convert to_with_top_zero

lemma to_with_top_some (n : ℕ) : to_with_top (some n) = n := rfl

lemma to_with_top_coe (n : ℕ) {_ : decidable (n : enat).dom} : to_with_top n = n :=
by simp only [← some_eq_coe, ← to_with_top_some]

@[simp] lemma to_with_top_coe' (n : ℕ) {h : decidable (n : enat).dom} :
  to_with_top (n : enat) = n :=
by convert to_with_top_coe n

@[simp] lemma to_with_top_le {x y : enat} : Π [decidable x.dom]
  [decidable y.dom], by exactI to_with_top x ≤ to_with_top y ↔ x ≤ y :=
enat.cases_on y (by simp) (enat.cases_on x (by simp) (by intros; simp))

@[simp] lemma to_with_top_lt {x y : enat} [decidable x.dom] [decidable y.dom] :
  to_with_top x < to_with_top y ↔ x < y :=
lt_iff_lt_of_le_iff_le to_with_top_le

end with_top

section with_top_equiv

open_locale classical

@[simp] lemma to_with_top_add {x y : enat} : to_with_top (x + y) = to_with_top x + to_with_top y :=
by apply enat.cases_on y; apply enat.cases_on x; simp [← nat.cast_add, ← with_top.coe_add]

/-- `equiv` between `enat` and `with_top ℕ` (for the order isomorphism see `with_top_order_iso`). -/
noncomputable def with_top_equiv : enat ≃ with_top ℕ :=
{ to_fun := λ x, to_with_top x,
  inv_fun := λ x, match x with (option.some n) := coe n | none := ⊤ end,
  left_inv := λ x, by apply enat.cases_on x; intros; simp; refl,
  right_inv := λ x, by cases x; simp [with_top_equiv._match_1]; refl }

@[simp] lemma with_top_equiv_top : with_top_equiv ⊤ = ⊤ :=
to_with_top_top'

@[simp] lemma with_top_equiv_coe (n : nat) : with_top_equiv n = n :=
to_with_top_coe' _

@[simp] lemma with_top_equiv_zero : with_top_equiv 0 = 0 :=
by simpa only [nat.cast_zero] using with_top_equiv_coe 0

@[simp] lemma with_top_equiv_le {x y : enat} : with_top_equiv x ≤ with_top_equiv y ↔ x ≤ y :=
to_with_top_le

@[simp] lemma with_top_equiv_lt {x y : enat} : with_top_equiv x < with_top_equiv y ↔ x < y :=
to_with_top_lt

/-- `to_with_top` induces an order isomorphism between `enat` and `with_top ℕ`. -/
noncomputable def with_top_order_iso : enat ≃o with_top ℕ :=
{ map_rel_iff' := λ _ _, with_top_equiv_le,
  .. with_top_equiv}

@[simp] lemma with_top_equiv_symm_top : with_top_equiv.symm ⊤ = ⊤ :=
rfl

@[simp] lemma with_top_equiv_symm_coe (n : nat) : with_top_equiv.symm n = n :=
rfl

@[simp] lemma with_top_equiv_symm_zero : with_top_equiv.symm 0 = 0 :=
rfl

@[simp] lemma with_top_equiv_symm_le {x y : with_top ℕ} :
  with_top_equiv.symm x ≤ with_top_equiv.symm y ↔ x ≤ y :=
by rw ← with_top_equiv_le; simp

@[simp] lemma with_top_equiv_symm_lt {x y : with_top ℕ} :
  with_top_equiv.symm x < with_top_equiv.symm y ↔ x < y :=
by rw ← with_top_equiv_lt; simp

/-- `to_with_top` induces an additive monoid isomorphism between `enat` and `with_top ℕ`. -/
noncomputable def with_top_add_equiv : enat ≃+ with_top ℕ :=
{ map_add' := λ x y, by simp only [with_top_equiv]; convert to_with_top_add,
  ..with_top_equiv}

end with_top_equiv

lemma lt_wf : @well_founded enat (<) :=
begin
  classical,
  change well_founded (λ a b : enat, a < b),
  simp_rw ←to_with_top_lt,
  exact inv_image.wf _ (with_top.well_founded_lt nat.lt_wf)
end

instance : is_well_order enat (<) := ⟨lt_wf⟩
instance : has_well_founded enat := ⟨(<), lt_wf⟩

section find

variables (P : ℕ → Prop) [decidable_pred P]

/-- The smallest `enat` satisfying a (decidable) predicate `P : ℕ → Prop` -/
def find : enat := ⟨∃ n, P n, nat.find⟩

@[simp] lemma find_get (h : (find P).dom) : (find P).get h = nat.find h := rfl

lemma find_dom (h : ∃ n, P n) : (find P).dom := h

lemma lt_find (n : ℕ) (h : ∀ m ≤ n, ¬P m) : (n : enat) < find P :=
begin
  rw coe_lt_iff, intro h', rw find_get,
  have := @nat.find_spec P _ h',
  contrapose! this,
  exact h _ this
end

lemma lt_find_iff (n : ℕ) : (n : enat) < find P ↔ (∀ m ≤ n, ¬P m) :=
begin
  refine ⟨_, lt_find P n⟩,
  intros h m hm,
  by_cases H : (find P).dom,
  { apply nat.find_min H, rw coe_lt_iff at h, specialize h H, exact lt_of_le_of_lt hm h },
  { exact not_exists.mp H m }
end

lemma find_le (n : ℕ) (h : P n) : find P ≤ n :=
by { rw le_coe_iff, refine ⟨⟨_, h⟩, @nat.find_min' P _ _ _ h⟩ }

lemma find_eq_top_iff : find P = ⊤ ↔ ∀ n, ¬P n :=
(eq_top_iff_forall_lt _).trans
⟨λ h n, (lt_find_iff P n).mp (h n) _ le_rfl, λ h n, lt_find P n $ λ _ _, h _⟩

end find

noncomputable instance : linear_ordered_add_comm_monoid_with_top enat :=
{ top_add' := top_add,
  .. enat.linear_order,
  .. enat.ordered_add_comm_monoid,
  .. enat.order_top }

end enat


/-
Copyright (c) 2020 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Joseph Myers.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.linear_algebra.affine_space.basic
import Mathlib.linear_algebra.tensor_product
import Mathlib.data.set.intervals.unordered_interval
import Mathlib.PostPort

universes u_1 u_2 u_3 l u_4

namespace Mathlib

/-!
# Affine spaces

This file defines affine subspaces (over modules) and the affine span of a set of points.

## Main definitions

* `affine_subspace k P` is the type of affine subspaces.  Unlike
  affine spaces, affine subspaces are allowed to be empty, and lemmas
  that do not apply to empty affine subspaces have `nonempty`
  hypotheses.  There is a `complete_lattice` structure on affine
  subspaces.
* `affine_subspace.direction` gives the `submodule` spanned by the
  pairwise differences of points in an `affine_subspace`.  There are
  various lemmas relating to the set of vectors in the `direction`,
  and relating the lattice structure on affine subspaces to that on
  their directions.
* `affine_span` gives the affine subspace spanned by a set of points,
  with `vector_span` giving its direction.  `affine_span` is defined
  in terms of `span_points`, which gives an explicit description of
  the points contained in the affine span; `span_points` itself should
  generally only be used when that description is required, with
  `affine_span` being the main definition for other purposes.  Two
  other descriptions of the affine span are proved equivalent: it is
  the `Inf` of affine subspaces containing the points, and (if
  `[nontrivial k]`) it contains exactly those points that are affine
  combinations of points in the given set.

## Implementation notes

`out_param` is used in the definiton of `add_torsor V P` to make `V` an implicit argument (deduced
from `P`) in most cases; `include V` is needed in many cases for `V`, and type classes using it, to
be added as implicit arguments to individual lemmas.  As for modules, `k` is an explicit argument
rather than implied by `P` or `V`.

This file only provides purely algebraic definitions and results.
Those depending on analysis or topology are defined elsewhere; see
`analysis.normed_space.add_torsor` and `topology.algebra.affine`.

## References

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

/-- The submodule spanning the differences of a (possibly empty) set
of points. -/
def vector_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : submodule k V :=
  submodule.span k (s -ᵥ s)

/-- The definition of `vector_span`, for rewriting. -/
theorem vector_span_def (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : vector_span k s = submodule.span k (s -ᵥ s) :=
  rfl

/-- `vector_span` is monotone. -/
theorem vector_span_mono (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s₁ : set P} {s₂ : set P} (h : s₁ ⊆ s₂) : vector_span k s₁ ≤ vector_span k s₂ :=
  submodule.span_mono (set.vsub_self_mono h)

/-- The `vector_span` of the empty set is `⊥`. -/
@[simp] theorem vector_span_empty (k : Type u_1) {V : Type u_2} (P : Type u_3) [ring k] [add_comm_group V] [module k V] [add_torsor V P] : vector_span k ∅ = ⊥ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (vector_span k ∅ = ⊥)) (vector_span_def k ∅)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (submodule.span k (∅ -ᵥ ∅) = ⊥)) (set.vsub_empty ∅)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (submodule.span k ∅ = ⊥)) submodule.span_empty)) (Eq.refl ⊥)))

/-- The `vector_span` of a single point is `⊥`. -/
@[simp] theorem vector_span_singleton (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (p : P) : vector_span k (singleton p) = ⊥ := sorry

/-- The `s -ᵥ s` lies within the `vector_span k s`. -/
theorem vsub_set_subset_vector_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : s -ᵥ s ⊆ ↑(vector_span k s) :=
  submodule.subset_span

/-- Each pairwise difference is in the `vector_span`. -/
theorem vsub_mem_vector_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} {p1 : P} {p2 : P} (hp1 : p1 ∈ s) (hp2 : p2 ∈ s) : p1 -ᵥ p2 ∈ vector_span k s :=
  vsub_set_subset_vector_span k s (set.vsub_mem_vsub hp1 hp2)

/-- The points in the affine span of a (possibly empty) set of
points. Use `affine_span` instead to get an `affine_subspace k P`. -/
def span_points (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : set P :=
  set_of fun (p : P) => ∃ (p1 : P), ∃ (H : p1 ∈ s), ∃ (v : V), ∃ (H : v ∈ vector_span k s), p = v +ᵥ p1

/-- A point in a set is in its affine span. -/
theorem mem_span_points (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (p : P) (s : set P) : p ∈ s → p ∈ span_points k s := sorry

/-- A set is contained in its `span_points`. -/
theorem subset_span_points (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : s ⊆ span_points k s :=
  fun (p : P) => mem_span_points k p s

/-- The `span_points` of a set is nonempty if and only if that set
is. -/
@[simp] theorem span_points_nonempty (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : set.nonempty (span_points k s) ↔ set.nonempty s := sorry

/-- Adding a point in the affine span and a vector in the spanning
submodule produces a point in the affine span. -/
theorem vadd_mem_span_points_of_mem_span_points_of_mem_vector_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} {p : P} {v : V} (hp : p ∈ span_points k s) (hv : v ∈ vector_span k s) : v +ᵥ p ∈ span_points k s := sorry

/-- Subtracting two points in the affine span produces a vector in the
spanning submodule. -/
theorem vsub_mem_vector_span_of_mem_span_points_of_mem_span_points (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} {p1 : P} {p2 : P} (hp1 : p1 ∈ span_points k s) (hp2 : p2 ∈ span_points k s) : p1 -ᵥ p2 ∈ vector_span k s := sorry

/-- An `affine_subspace k P` is a subset of an `affine_space V P`
that, if not empty, has an affine space structure induced by a
corresponding subspace of the `module k V`. -/
structure affine_subspace (k : Type u_1) {V : Type u_2} (P : Type u_3) [ring k] [add_comm_group V] [module k V] [add_torsor V P]
where
  carrier : set P
  smul_vsub_vadd_mem : ∀ (c : k) {p1 p2 p3 : P}, p1 ∈ carrier → p2 ∈ carrier → p3 ∈ carrier → c • (p1 -ᵥ p2) +ᵥ p3 ∈ carrier

namespace submodule

/-- Reinterpret `p : submodule k V` as an `affine_subspace k V`. -/
def to_affine_subspace {k : Type u_1} {V : Type u_2} [ring k] [add_comm_group V] [module k V] (p : submodule k V) : affine_subspace k V :=
  affine_subspace.mk ↑p sorry

end submodule

namespace affine_subspace

protected instance set.has_coe (k : Type u_1) {V : Type u_2} (P : Type u_3) [ring k] [add_comm_group V] [module k V] [add_torsor V P] : has_coe (affine_subspace k P) (set P) :=
  has_coe.mk carrier

protected instance has_mem (k : Type u_1) {V : Type u_2} (P : Type u_3) [ring k] [add_comm_group V] [module k V] [add_torsor V P] : has_mem P (affine_subspace k P) :=
  has_mem.mk fun (p : P) (s : affine_subspace k P) => p ∈ ↑s

/-- A point is in an affine subspace coerced to a set if and only if
it is in that affine subspace. -/
@[simp] theorem mem_coe (k : Type u_1) {V : Type u_2} (P : Type u_3) [ring k] [add_comm_group V] [module k V] [add_torsor V P] (p : P) (s : affine_subspace k P) : p ∈ ↑s ↔ p ∈ s :=
  iff.rfl

/-- The direction of an affine subspace is the submodule spanned by
the pairwise differences of points.  (Except in the case of an empty
affine subspace, where the direction is the zero submodule, every
vector in the direction is the difference of two points in the affine
subspace.) -/
def direction {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : affine_subspace k P) : submodule k V :=
  vector_span k ↑s

/-- The direction equals the `vector_span`. -/
theorem direction_eq_vector_span {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : affine_subspace k P) : direction s = vector_span k ↑s :=
  rfl

/-- Alternative definition of the direction when the affine subspace
is nonempty.  This is defined so that the order on submodules (as used
in the definition of `submodule.span`) can be used in the proof of
`coe_direction_eq_vsub_set`, and is not intended to be used beyond
that proof. -/
def direction_of_nonempty {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} (h : set.nonempty ↑s) : submodule k V :=
  submodule.mk (↑s -ᵥ ↑s) sorry sorry sorry

/-- `direction_of_nonempty` gives the same submodule as
`direction`. -/
theorem direction_of_nonempty_eq_direction {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} (h : set.nonempty ↑s) : direction_of_nonempty h = direction s :=
  le_antisymm (vsub_set_subset_vector_span k ↑s) (iff.mpr submodule.span_le set.subset.rfl)

/-- The set of vectors in the direction of a nonempty affine subspace
is given by `vsub_set`. -/
theorem coe_direction_eq_vsub_set {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} (h : set.nonempty ↑s) : ↑(direction s) = ↑s -ᵥ ↑s :=
  direction_of_nonempty_eq_direction h ▸ rfl

/-- A vector is in the direction of a nonempty affine subspace if and
only if it is the subtraction of two vectors in the subspace. -/
theorem mem_direction_iff_eq_vsub {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} (h : set.nonempty ↑s) (v : V) : v ∈ direction s ↔ ∃ (p1 : P), ∃ (H : p1 ∈ s), ∃ (p2 : P), ∃ (H : p2 ∈ s), v = p1 -ᵥ p2 := sorry

/-- Adding a vector in the direction to a point in the subspace
produces a point in the subspace. -/
theorem vadd_mem_of_mem_direction {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {v : V} (hv : v ∈ direction s) {p : P} (hp : p ∈ s) : v +ᵥ p ∈ s := sorry

/-- Subtracting two points in the subspace produces a vector in the
direction. -/
theorem vsub_mem_direction {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p1 : P} {p2 : P} (hp1 : p1 ∈ s) (hp2 : p2 ∈ s) : p1 -ᵥ p2 ∈ direction s :=
  vsub_mem_vector_span k hp1 hp2

/-- Adding a vector to a point in a subspace produces a point in the
subspace if and only if the vector is in the direction. -/
theorem vadd_mem_iff_mem_direction {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} (v : V) {p : P} (hp : p ∈ s) : v +ᵥ p ∈ s ↔ v ∈ direction s := sorry

/-- Given a point in an affine subspace, the set of vectors in its
direction equals the set of vectors subtracting that point on the
right. -/
theorem coe_direction_eq_vsub_set_right {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p : P} (hp : p ∈ s) : ↑(direction s) = (fun (_x : P) => _x -ᵥ p) '' ↑s := sorry

/-- Given a point in an affine subspace, the set of vectors in its
direction equals the set of vectors subtracting that point on the
left. -/
theorem coe_direction_eq_vsub_set_left {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p : P} (hp : p ∈ s) : ↑(direction s) = has_vsub.vsub p '' ↑s := sorry

/-- Given a point in an affine subspace, a vector is in its direction
if and only if it results from subtracting that point on the right. -/
theorem mem_direction_iff_eq_vsub_right {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p : P} (hp : p ∈ s) (v : V) : v ∈ direction s ↔ ∃ (p2 : P), ∃ (H : p2 ∈ s), v = p2 -ᵥ p := sorry

/-- Given a point in an affine subspace, a vector is in its direction
if and only if it results from subtracting that point on the left. -/
theorem mem_direction_iff_eq_vsub_left {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p : P} (hp : p ∈ s) (v : V) : v ∈ direction s ↔ ∃ (p2 : P), ∃ (H : p2 ∈ s), v = p -ᵥ p2 := sorry

/-- Given a point in an affine subspace, a result of subtracting that
point on the right is in the direction if and only if the other point
is in the subspace. -/
theorem vsub_right_mem_direction_iff_mem {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p : P} (hp : p ∈ s) (p2 : P) : p2 -ᵥ p ∈ direction s ↔ p2 ∈ s := sorry

/-- Given a point in an affine subspace, a result of subtracting that
point on the left is in the direction if and only if the other point
is in the subspace. -/
theorem vsub_left_mem_direction_iff_mem {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p : P} (hp : p ∈ s) (p2 : P) : p -ᵥ p2 ∈ direction s ↔ p2 ∈ s := sorry

/-- Two affine subspaces are equal if they have the same points. -/
theorem ext {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s1 : affine_subspace k P} {s2 : affine_subspace k P} (h : ↑s1 = ↑s2) : s1 = s2 := sorry

/-- Two affine subspaces with the same direction and nonempty
intersection are equal. -/
theorem ext_of_direction_eq {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s1 : affine_subspace k P} {s2 : affine_subspace k P} (hd : direction s1 = direction s2) (hn : set.nonempty (↑s1 ∩ ↑s2)) : s1 = s2 := sorry

protected instance to_add_torsor {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : affine_subspace k P) [Nonempty ↥s] : add_torsor ↥(direction s) ↥s :=
  add_torsor.mk (fun (a : ↥(direction s)) (b : ↥s) => { val := ↑a +ᵥ ↑b, property := sorry }) sorry sorry
    (fun (a b : ↥s) => { val := ↑a -ᵥ ↑b, property := sorry }) sorry sorry

@[simp] theorem coe_vsub {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : affine_subspace k P) [Nonempty ↥s] (a : ↥s) (b : ↥s) : ↑(a -ᵥ b) = ↑a -ᵥ ↑b :=
  rfl

@[simp] theorem coe_vadd {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : affine_subspace k P) [Nonempty ↥s] (a : ↥(direction s)) (b : ↥s) : ↑(a +ᵥ b) = ↑a +ᵥ ↑b :=
  rfl

/-- Two affine subspaces with nonempty intersection are equal if and
only if their directions are equal. -/
theorem eq_iff_direction_eq_of_mem {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s₁ : affine_subspace k P} {s₂ : affine_subspace k P} {p : P} (h₁ : p ∈ s₁) (h₂ : p ∈ s₂) : s₁ = s₂ ↔ direction s₁ = direction s₂ :=
  { mp := fun (h : s₁ = s₂) => h ▸ rfl,
    mpr := fun (h : direction s₁ = direction s₂) => ext_of_direction_eq h (Exists.intro p { left := h₁, right := h₂ }) }

/-- Construct an affine subspace from a point and a direction. -/
def mk' {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (p : P) (direction : submodule k V) : affine_subspace k P :=
  mk (set_of fun (q : P) => ∃ (v : V), ∃ (H : v ∈ direction), q = v +ᵥ p) sorry

/-- An affine subspace constructed from a point and a direction contains
that point. -/
theorem self_mem_mk' {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (p : P) (direction : submodule k V) : p ∈ mk' p direction :=
  Exists.intro 0 (Exists.intro (submodule.zero_mem direction) (Eq.symm (zero_vadd V p)))

/-- An affine subspace constructed from a point and a direction contains
the result of adding a vector in that direction to that point. -/
theorem vadd_mem_mk' {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {v : V} (p : P) {direction : submodule k V} (hv : v ∈ direction) : v +ᵥ p ∈ mk' p direction :=
  Exists.intro v (Exists.intro hv rfl)

/-- An affine subspace constructed from a point and a direction is
nonempty. -/
theorem mk'_nonempty {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (p : P) (direction : submodule k V) : set.nonempty ↑(mk' p direction) :=
  Exists.intro p (self_mem_mk' p direction)

/-- The direction of an affine subspace constructed from a point and a
direction. -/
@[simp] theorem direction_mk' {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (p : P) (direction : submodule k V) : direction (mk' p direction) = direction := sorry

/-- Constructing an affine subspace from a point in a subspace and
that subspace's direction yields the original subspace. -/
@[simp] theorem mk'_eq {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p : P} (hp : p ∈ s) : mk' p (direction s) = s :=
  ext_of_direction_eq (direction_mk' p (direction s)) (Exists.intro p (set.mem_inter (self_mem_mk' p (direction s)) hp))

/-- If an affine subspace contains a set of points, it contains the
`span_points` of that set. -/
theorem span_points_subset_coe_of_subset_coe {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} {s1 : affine_subspace k P} (h : s ⊆ ↑s1) : span_points k s ⊆ ↑s1 := sorry

end affine_subspace

/-- The affine span of a set of points is the smallest affine subspace
containing those points. (Actually defined here in terms of spans in
modules.) -/
def affine_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : affine_subspace k P :=
  affine_subspace.mk (span_points k s) sorry

/-- The affine span, converted to a set, is `span_points`. -/
@[simp] theorem coe_affine_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : ↑(affine_span k s) = span_points k s :=
  rfl

/-- A set is contained in its affine span. -/
theorem subset_affine_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : s ⊆ ↑(affine_span k s) :=
  subset_span_points k s

/-- The direction of the affine span is the `vector_span`. -/
theorem direction_affine_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : affine_subspace.direction (affine_span k s) = vector_span k s := sorry

/-- A point in a set is in its affine span. -/
theorem mem_affine_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {p : P} {s : set P} (hp : p ∈ s) : p ∈ affine_span k s :=
  mem_span_points k p s hp

namespace affine_subspace

protected instance complete_lattice {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] : complete_lattice (affine_subspace k P) :=
  complete_lattice.mk (fun (s1 s2 : affine_subspace k P) => affine_span k (↑s1 ∪ ↑s2)) partial_order.le partial_order.lt
    sorry sorry sorry sorry sorry sorry (fun (s1 s2 : affine_subspace k P) => mk (↑s1 ∩ ↑s2) sorry) sorry sorry sorry
    (mk set.univ sorry) sorry (mk ∅ sorry) sorry
    (fun (s : set (affine_subspace k P)) =>
      affine_span k (set.Union fun (s' : affine_subspace k P) => set.Union fun (H : s' ∈ s) => ↑s'))
    (fun (s : set (affine_subspace k P)) =>
      mk (set.Inter fun (s' : affine_subspace k P) => set.Inter fun (H : s' ∈ s) => ↑s') sorry)
    sorry sorry sorry sorry

protected instance inhabited {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] : Inhabited (affine_subspace k P) :=
  { default := ⊤ }

/-- The `≤` order on subspaces is the same as that on the corresponding
sets. -/
theorem le_def {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s1 : affine_subspace k P) (s2 : affine_subspace k P) : s1 ≤ s2 ↔ ↑s1 ⊆ ↑s2 :=
  iff.rfl

/-- One subspace is less than or equal to another if and only if all
its points are in the second subspace. -/
theorem le_def' {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s1 : affine_subspace k P) (s2 : affine_subspace k P) : s1 ≤ s2 ↔ ∀ (p : P), p ∈ s1 → p ∈ s2 :=
  iff.rfl

/-- The `<` order on subspaces is the same as that on the corresponding
sets. -/
theorem lt_def {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s1 : affine_subspace k P) (s2 : affine_subspace k P) : s1 < s2 ↔ ↑s1 ⊂ ↑s2 :=
  iff.rfl

/-- One subspace is not less than or equal to another if and only if
it has a point not in the second subspace. -/
theorem not_le_iff_exists {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s1 : affine_subspace k P) (s2 : affine_subspace k P) : ¬s1 ≤ s2 ↔ ∃ (p : P), ∃ (H : p ∈ s1), ¬p ∈ s2 :=
  set.not_subset

/-- If a subspace is less than another, there is a point only in the
second. -/
theorem exists_of_lt {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s1 : affine_subspace k P} {s2 : affine_subspace k P} (h : s1 < s2) : ∃ (p : P), ∃ (H : p ∈ s2), ¬p ∈ s1 :=
  set.exists_of_ssubset h

/-- A subspace is less than another if and only if it is less than or
equal to the second subspace and there is a point only in the
second. -/
theorem lt_iff_le_and_exists {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s1 : affine_subspace k P) (s2 : affine_subspace k P) : s1 < s2 ↔ s1 ≤ s2 ∧ ∃ (p : P), ∃ (H : p ∈ s2), ¬p ∈ s1 := sorry

/-- If an affine subspace is nonempty and contained in another with
the same direction, they are equal. -/
theorem eq_of_direction_eq_of_nonempty_of_le {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s₁ : affine_subspace k P} {s₂ : affine_subspace k P} (hd : direction s₁ = direction s₂) (hn : set.nonempty ↑s₁) (hle : s₁ ≤ s₂) : s₁ = s₂ := sorry

/-- The affine span is the `Inf` of subspaces containing the given
points. -/
theorem affine_span_eq_Inf (k : Type u_1) (V : Type u_2) {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s : set P) : affine_span k s = Inf (set_of fun (s' : affine_subspace k P) => s ⊆ ↑s') := sorry

/-- The Galois insertion formed by `affine_span` and coercion back to
a set. -/
protected def gi (k : Type u_1) (V : Type u_2) (P : Type u_3) [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] : galois_insertion (affine_span k) coe :=
  galois_insertion.mk (fun (s : set P) (_x : ↑(affine_span k s) ≤ s) => affine_span k s) sorry sorry sorry

/-- The span of the empty set is `⊥`. -/
@[simp] theorem span_empty (k : Type u_1) (V : Type u_2) (P : Type u_3) [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] : affine_span k ∅ = ⊥ :=
  galois_connection.l_bot (galois_insertion.gc (affine_subspace.gi k V P))

/-- The span of `univ` is `⊤`. -/
@[simp] theorem span_univ (k : Type u_1) (V : Type u_2) (P : Type u_3) [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] : affine_span k set.univ = ⊤ :=
  iff.mpr eq_top_iff (subset_span_points k ↑⊤)

/-- The affine span of a single point, coerced to a set, contains just
that point. -/
@[simp] theorem coe_affine_span_singleton (k : Type u_1) (V : Type u_2) {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (p : P) : ↑(affine_span k (singleton p)) = singleton p := sorry

/-- A point is in the affine span of a single point if and only if
they are equal. -/
@[simp] theorem mem_affine_span_singleton (k : Type u_1) (V : Type u_2) {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (p1 : P) (p2 : P) : p1 ∈ affine_span k (singleton p2) ↔ p1 = p2 := sorry

/-- The span of a union of sets is the sup of their spans. -/
theorem span_union (k : Type u_1) (V : Type u_2) {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s : set P) (t : set P) : affine_span k (s ∪ t) = affine_span k s ⊔ affine_span k t :=
  galois_connection.l_sup (galois_insertion.gc (affine_subspace.gi k V P))

/-- The span of a union of an indexed family of sets is the sup of
their spans. -/
theorem span_Union (k : Type u_1) (V : Type u_2) {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {ι : Type u_4} (s : ι → set P) : affine_span k (set.Union fun (i : ι) => s i) = supr fun (i : ι) => affine_span k (s i) :=
  galois_connection.l_supr (galois_insertion.gc (affine_subspace.gi k V P))

/-- `⊤`, coerced to a set, is the whole set of points. -/
@[simp] theorem top_coe (k : Type u_1) (V : Type u_2) (P : Type u_3) [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] : ↑⊤ = set.univ :=
  rfl

/-- All points are in `⊤`. -/
theorem mem_top (k : Type u_1) (V : Type u_2) {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (p : P) : p ∈ ⊤ :=
  set.mem_univ p

/-- The direction of `⊤` is the whole module as a submodule. -/
@[simp] theorem direction_top (k : Type u_1) (V : Type u_2) (P : Type u_3) [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] : direction ⊤ = ⊤ := sorry

/-- `⊥`, coerced to a set, is the empty set. -/
@[simp] theorem bot_coe (k : Type u_1) (V : Type u_2) (P : Type u_3) [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] : ↑⊥ = ∅ :=
  rfl

/-- No points are in `⊥`. -/
theorem not_mem_bot (k : Type u_1) (V : Type u_2) {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (p : P) : ¬p ∈ ⊥ :=
  set.not_mem_empty p

/-- The direction of `⊥` is the submodule `⊥`. -/
@[simp] theorem direction_bot (k : Type u_1) (V : Type u_2) (P : Type u_3) [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] : direction ⊥ = ⊥ := sorry

/-- A nonempty affine subspace is `⊤` if and only if its direction is
`⊤`. -/
@[simp] theorem direction_eq_top_iff_of_nonempty {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s : affine_subspace k P} (h : set.nonempty ↑s) : direction s = ⊤ ↔ s = ⊤ := sorry

/-- The inf of two affine subspaces, coerced to a set, is the
intersection of the two sets of points. -/
@[simp] theorem inf_coe {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s1 : affine_subspace k P) (s2 : affine_subspace k P) : ↑s1 ⊓ ↑s2 = ↑s1 ∩ ↑s2 :=
  rfl

/-- A point is in the inf of two affine subspaces if and only if it is
in both of them. -/
theorem mem_inf_iff {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (p : P) (s1 : affine_subspace k P) (s2 : affine_subspace k P) : p ∈ s1 ⊓ s2 ↔ p ∈ s1 ∧ p ∈ s2 :=
  iff.rfl

/-- The direction of the inf of two affine subspaces is less than or
equal to the inf of their directions. -/
theorem direction_inf {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s1 : affine_subspace k P) (s2 : affine_subspace k P) : direction (s1 ⊓ s2) ≤ direction s1 ⊓ direction s2 := sorry

/-- If two affine subspaces have a point in common, the direction of
their inf equals the inf of their directions. -/
theorem direction_inf_of_mem {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s₁ : affine_subspace k P} {s₂ : affine_subspace k P} {p : P} (h₁ : p ∈ s₁) (h₂ : p ∈ s₂) : direction (s₁ ⊓ s₂) = direction s₁ ⊓ direction s₂ := sorry

/-- If two affine subspaces have a point in their inf, the direction
of their inf equals the inf of their directions. -/
theorem direction_inf_of_mem_inf {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s₁ : affine_subspace k P} {s₂ : affine_subspace k P} {p : P} (h : p ∈ s₁ ⊓ s₂) : direction (s₁ ⊓ s₂) = direction s₁ ⊓ direction s₂ :=
  direction_inf_of_mem (and.left (iff.mp (mem_inf_iff p s₁ s₂) h)) (and.right (iff.mp (mem_inf_iff p s₁ s₂) h))

/-- If one affine subspace is less than or equal to another, the same
applies to their directions. -/
theorem direction_le {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s1 : affine_subspace k P} {s2 : affine_subspace k P} (h : s1 ≤ s2) : direction s1 ≤ direction s2 := sorry

/-- If one nonempty affine subspace is less than another, the same
applies to their directions -/
theorem direction_lt_of_nonempty {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s1 : affine_subspace k P} {s2 : affine_subspace k P} (h : s1 < s2) (hn : set.nonempty ↑s1) : direction s1 < direction s2 := sorry

/-- The sup of the directions of two affine subspaces is less than or
equal to the direction of their sup. -/
theorem sup_direction_le {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s1 : affine_subspace k P) (s2 : affine_subspace k P) : direction s1 ⊔ direction s2 ≤ direction (s1 ⊔ s2) := sorry

/-- The sup of the directions of two nonempty affine subspaces with
empty intersection is less than the direction of their sup. -/
theorem sup_direction_lt_of_nonempty_of_inter_empty {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s1 : affine_subspace k P} {s2 : affine_subspace k P} (h1 : set.nonempty ↑s1) (h2 : set.nonempty ↑s2) (he : ↑s1 ∩ ↑s2 = ∅) : direction s1 ⊔ direction s2 < direction (s1 ⊔ s2) := sorry

/-- If the directions of two nonempty affine subspaces span the whole
module, they have nonempty intersection. -/
theorem inter_nonempty_of_nonempty_of_sup_direction_eq_top {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s1 : affine_subspace k P} {s2 : affine_subspace k P} (h1 : set.nonempty ↑s1) (h2 : set.nonempty ↑s2) (hd : direction s1 ⊔ direction s2 = ⊤) : set.nonempty (↑s1 ∩ ↑s2) := sorry

/-- If the directions of two nonempty affine subspaces are complements
of each other, they intersect in exactly one point. -/
theorem inter_eq_singleton_of_nonempty_of_is_compl {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] {s1 : affine_subspace k P} {s2 : affine_subspace k P} (h1 : set.nonempty ↑s1) (h2 : set.nonempty ↑s2) (hd : is_compl (direction s1) (direction s2)) : ∃ (p : P), ↑s1 ∩ ↑s2 = singleton p := sorry

/-- Coercing a subspace to a set then taking the affine span produces
the original subspace. -/
@[simp] theorem affine_span_coe {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [S : add_torsor V P] (s : affine_subspace k P) : affine_span k ↑s = s := sorry

end affine_subspace

/-- The `vector_span` is the span of the pairwise subtractions with a
given point on the left. -/
theorem vector_span_eq_span_vsub_set_left (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} {p : P} (hp : p ∈ s) : vector_span k s = submodule.span k (has_vsub.vsub p '' s) := sorry

/-- The `vector_span` is the span of the pairwise subtractions with a
given point on the right. -/
theorem vector_span_eq_span_vsub_set_right (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} {p : P} (hp : p ∈ s) : vector_span k s = submodule.span k ((fun (_x : P) => _x -ᵥ p) '' s) := sorry

/-- The `vector_span` is the span of the pairwise subtractions with a
given point on the left, excluding the subtraction of that point from
itself. -/
theorem vector_span_eq_span_vsub_set_left_ne (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} {p : P} (hp : p ∈ s) : vector_span k s = submodule.span k (has_vsub.vsub p '' (s \ singleton p)) := sorry

/-- The `vector_span` is the span of the pairwise subtractions with a
given point on the right, excluding the subtraction of that point from
itself. -/
theorem vector_span_eq_span_vsub_set_right_ne (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} {p : P} (hp : p ∈ s) : vector_span k s = submodule.span k ((fun (_x : P) => _x -ᵥ p) '' (s \ singleton p)) := sorry

/-- The `vector_span` of the image of a function is the span of the
pairwise subtractions with a given point on the left, excluding the
subtraction of that point from itself. -/
theorem vector_span_image_eq_span_vsub_set_left_ne (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} (p : ι → P) {s : set ι} {i : ι} (hi : i ∈ s) : vector_span k (p '' s) = submodule.span k (has_vsub.vsub (p i) '' (p '' (s \ singleton i))) := sorry

/-- The `vector_span` of the image of a function is the span of the
pairwise subtractions with a given point on the right, excluding the
subtraction of that point from itself. -/
theorem vector_span_image_eq_span_vsub_set_right_ne (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} (p : ι → P) {s : set ι} {i : ι} (hi : i ∈ s) : vector_span k (p '' s) = submodule.span k ((fun (_x : P) => _x -ᵥ p i) '' (p '' (s \ singleton i))) := sorry

/-- The `vector_span` of an indexed family is the span of the pairwise
subtractions with a given point on the left. -/
theorem vector_span_range_eq_span_range_vsub_left (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} (p : ι → P) (i0 : ι) : vector_span k (set.range p) = submodule.span k (set.range fun (i : ι) => p i0 -ᵥ p i) := sorry

/-- The `vector_span` of an indexed family is the span of the pairwise
subtractions with a given point on the right. -/
theorem vector_span_range_eq_span_range_vsub_right (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} (p : ι → P) (i0 : ι) : vector_span k (set.range p) = submodule.span k (set.range fun (i : ι) => p i -ᵥ p i0) := sorry

/-- The `vector_span` of an indexed family is the span of the pairwise
subtractions with a given point on the left, excluding the subtraction
of that point from itself. -/
theorem vector_span_range_eq_span_range_vsub_left_ne (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} (p : ι → P) (i₀ : ι) : vector_span k (set.range p) = submodule.span k (set.range fun (i : Subtype fun (x : ι) => x ≠ i₀) => p i₀ -ᵥ p ↑i) := sorry

/-- The `vector_span` of an indexed family is the span of the pairwise
subtractions with a given point on the right, excluding the subtraction
of that point from itself. -/
theorem vector_span_range_eq_span_range_vsub_right_ne (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} (p : ι → P) (i₀ : ι) : vector_span k (set.range p) = submodule.span k (set.range fun (i : Subtype fun (x : ι) => x ≠ i₀) => p ↑i -ᵥ p i₀) := sorry

/-- The affine span of a set is nonempty if and only if that set
is. -/
theorem affine_span_nonempty (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) : set.nonempty ↑(affine_span k s) ↔ set.nonempty s :=
  span_points_nonempty k s

/-- The affine span of a nonempty set is nonempty. -/
protected instance affine_span.nonempty (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} [Nonempty ↥s] : Nonempty ↥(affine_span k s) :=
  set.nonempty.to_subtype (iff.mpr (affine_span_nonempty k s) (iff.mp nonempty_subtype _inst_5))

/-- Suppose a set of vectors spans `V`.  Then a point `p`, together
with those vectors added to `p`, spans `P`. -/
theorem affine_span_singleton_union_vadd_eq_top_of_span_eq_top {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : set V} (p : P) (h : submodule.span k (set.range coe) = ⊤) : affine_span k (singleton p ∪ (fun (v : V) => v +ᵥ p) '' s) = ⊤ := sorry

/-- `affine_span` is monotone. -/
theorem affine_span_mono (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s₁ : set P} {s₂ : set P} (h : s₁ ⊆ s₂) : affine_span k s₁ ≤ affine_span k s₂ :=
  affine_subspace.span_points_subset_coe_of_subset_coe (set.subset.trans h (subset_affine_span k s₂))

/-- Taking the affine span of a set, adding a point and taking the
span again produces the same results as adding the point to the set
and taking the span. -/
theorem affine_span_insert_affine_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] (p : P) (ps : set P) : affine_span k (insert p ↑(affine_span k ps)) = affine_span k (insert p ps) := sorry

/-- If a point is in the affine span of a set, adding it to that set
does not change the affine span. -/
theorem affine_span_insert_eq_affine_span (k : Type u_1) {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {p : P} {ps : set P} (h : p ∈ affine_span k ps) : affine_span k (insert p ps) = affine_span k ps := sorry

namespace affine_subspace

/-- The direction of the sup of two nonempty affine subspaces is the
sup of the two directions and of any one difference between points in
the two subspaces. -/
theorem direction_sup {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s1 : affine_subspace k P} {s2 : affine_subspace k P} {p1 : P} {p2 : P} (hp1 : p1 ∈ s1) (hp2 : p2 ∈ s2) : direction (s1 ⊔ s2) = direction s1 ⊔ direction s2 ⊔ submodule.span k (singleton (p2 -ᵥ p1)) := sorry

/-- The direction of the span of the result of adding a point to a
nonempty affine subspace is the sup of the direction of that subspace
and of any one difference between that point and a point in the
subspace. -/
theorem direction_affine_span_insert {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p1 : P} {p2 : P} (hp1 : p1 ∈ s) : direction (affine_span k (insert p2 ↑s)) = submodule.span k (singleton (p2 -ᵥ p1)) ⊔ direction s := sorry

/-- Given a point `p1` in an affine subspace `s`, and a point `p2`, a
point `p` is in the span of `s` with `p2` added if and only if it is a
multiple of `p2 -ᵥ p1` added to a point in `s`. -/
theorem mem_affine_span_insert_iff {k : Type u_1} {V : Type u_2} {P : Type u_3} [ring k] [add_comm_group V] [module k V] [add_torsor V P] {s : affine_subspace k P} {p1 : P} (hp1 : p1 ∈ s) (p2 : P) (p : P) : p ∈ affine_span k (insert p2 ↑s) ↔ ∃ (r : k), ∃ (p0 : P), ∃ (hp0 : p0 ∈ s), p = r • (p2 -ᵥ p1) +ᵥ p0 := sorry


/-
Copyright (c) 2019 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot
-/
import topology.algebra.uniform_ring
import topology.algebra.field

/-!
# Completion of topological fields

The goal of this file is to prove the main part of Proposition 7 of Bourbaki GT III 6.8 :

The completion `hat K` of a Hausdorff topological field is a field if the image under
the mapping `x ↦ x⁻¹` of every Cauchy filter (with respect to the additive uniform structure)
which does not have a cluster point at `0` is a Cauchy filter
(with respect to the additive uniform structure).

Bourbaki does not give any detail here, he refers to the general discussion of extending
functions defined on a dense subset with values in a complete Hausdorff space. In particular
the subtlety about clustering at zero is totally left to readers.

Note that the separated completion of a non-separated topological field is the zero ring, hence
the separation assumption is needed. Indeed the kernel of the completion map is the closure of
zero which is an ideal. Hence it's either zero (and the field is separated) or the full field,
which implies one is sent to zero and the completion ring is trivial.

The main definition is `completable_top_field` which packages the assumptions as a Prop-valued
type class and the main results are the instances `uniform_space.completion.field` and
`uniform_space.completion.topological_division_ring`.
-/

noncomputable theory

open_locale classical uniformity topological_space

open set uniform_space uniform_space.completion filter

variables (K : Type*) [field K]  [uniform_space K]

local notation `hat` := completion

/--
A topological field is completable if it is separated and the image under
the mapping x ↦ x⁻¹ of every Cauchy filter (with respect to the additive uniform structure)
which does not have a cluster point at 0 is a Cauchy filter
(with respect to the additive uniform structure). This ensures the completion is
a field.
-/
class completable_top_field extends separated_space K : Prop :=
(nice : ∀ F : filter K, cauchy F → 𝓝 0 ⊓ F = ⊥ → cauchy (map (λ x, x⁻¹) F))

namespace uniform_space
namespace completion

@[priority 100]
instance [separated_space K] : nontrivial (hat K) :=
⟨⟨0, 1, λ h, zero_ne_one $ (uniform_embedding_coe K).inj h⟩⟩

variables {K}

/-- extension of inversion to the completion of a field. -/
def hat_inv : hat K → hat K := dense_inducing_coe.extend (λ x : K, (coe x⁻¹ : hat K))

lemma continuous_hat_inv [completable_top_field K] {x : hat K} (h : x ≠ 0) :
  continuous_at hat_inv x :=
begin
  haveI : regular_space (hat K) := completion.regular_space K,
  refine dense_inducing_coe.continuous_at_extend _,
  apply mem_of_superset (compl_singleton_mem_nhds h),
  intros y y_ne,
  rw mem_compl_singleton_iff at y_ne,
  apply complete_space.complete,
  rw ← filter.map_map,
  apply cauchy.map _ (completion.uniform_continuous_coe K),
  apply completable_top_field.nice,
  { haveI := dense_inducing_coe.comap_nhds_ne_bot y,
    apply cauchy_nhds.comap,
    { rw completion.comap_coe_eq_uniformity,
      exact le_rfl } },
  { have eq_bot : 𝓝 (0 : hat K) ⊓ 𝓝 y = ⊥,
    { by_contradiction h,
      exact y_ne (eq_of_nhds_ne_bot $ ne_bot_iff.mpr h).symm },
    erw [dense_inducing_coe.nhds_eq_comap (0 : K), ← filter.comap_inf, eq_bot],
    exact comap_bot },
end

/-
The value of `hat_inv` at zero is not really specified, although it's probably zero.
Here we explicitly enforce the `inv_zero` axiom.
-/
instance : has_inv (hat K) := ⟨λ x, if x = 0 then 0 else hat_inv x⟩

variables [topological_division_ring K]

lemma hat_inv_extends {x : K} (h : x ≠ 0) : hat_inv (x : hat K) = coe (x⁻¹ : K) :=
dense_inducing_coe.extend_eq_at
    ((continuous_coe K).continuous_at.comp (continuous_at_inv₀ h))

variables [completable_top_field K]

@[norm_cast]
lemma coe_inv (x : K) : (x : hat K)⁻¹ = ((x⁻¹ : K) : hat K) :=
begin
  by_cases h : x = 0,
  { rw [h, inv_zero],
    dsimp [has_inv.inv],
    norm_cast,
    simp },
  { conv_lhs { dsimp [has_inv.inv] },
    rw if_neg,
    { exact hat_inv_extends h },
    { exact λ H, h (dense_embedding_coe.inj H) } }
end

variables [uniform_add_group K]

lemma mul_hat_inv_cancel {x : hat K} (x_ne : x ≠ 0) : x*hat_inv x = 1 :=
begin
  haveI : t1_space (hat K) := t2_space.t1_space,
  let f := λ x : hat K, x*hat_inv x,
  let c := (coe : K → hat K),
  change f x = 1,
  have cont : continuous_at f x,
  { letI : topological_space (hat K × hat K) := prod.topological_space,
    have : continuous_at (λ y : hat K, ((y, hat_inv y) : hat K × hat K)) x,
      from continuous_id.continuous_at.prod (continuous_hat_inv x_ne),
    exact (_root_.continuous_mul.continuous_at.comp this : _) },
  have clo : x ∈ closure (c '' {0}ᶜ),
  { have := dense_inducing_coe.dense x,
    rw [← image_univ, show (univ : set K) = {0} ∪ {0}ᶜ,
                      from (union_compl_self _).symm, image_union] at this,
    apply mem_closure_of_mem_closure_union this,
    rw image_singleton,
    exact compl_singleton_mem_nhds x_ne },
  have fxclo : f x ∈ closure (f '' (c '' {0}ᶜ)) := mem_closure_image cont clo,
  have : f '' (c '' {0}ᶜ) ⊆ {1},
  { rw image_image,
    rintros _ ⟨z, z_ne, rfl⟩,
    rw mem_singleton_iff,
    rw mem_compl_singleton_iff at z_ne,
    dsimp [c, f],
    rw hat_inv_extends z_ne,
    norm_cast,
    rw mul_inv_cancel z_ne,
    norm_cast },
  replace fxclo := closure_mono this fxclo,
  rwa [closure_singleton, mem_singleton_iff] at fxclo
end

instance : field (hat K) :=
{ exists_pair_ne := ⟨0, 1, λ h, zero_ne_one ((uniform_embedding_coe K).inj h)⟩,
  mul_inv_cancel := λ x x_ne, by { dsimp [has_inv.inv],
                                   simp [if_neg x_ne, mul_hat_inv_cancel x_ne], },
  inv_zero := show ((0 : K) : hat K)⁻¹ = ((0 : K) : hat K), by rw [coe_inv, inv_zero],
  ..completion.has_inv,
  ..(by apply_instance : comm_ring (hat K)) }

instance : topological_division_ring (hat K) :=
{ continuous_at_inv₀ := begin
    intros x x_ne,
    have : {y | hat_inv y = y⁻¹ } ∈ 𝓝 x,
    { have : {(0 : hat K)}ᶜ ⊆ {y : hat K | hat_inv y = y⁻¹ },
      { intros y y_ne,
        rw mem_compl_singleton_iff at y_ne,
        dsimp [has_inv.inv],
        rw if_neg y_ne },
      exact mem_of_superset (compl_singleton_mem_nhds x_ne) this },
    exact continuous_at.congr (continuous_hat_inv x_ne) this
  end,
  ..completion.top_ring_compl }

end completion
end uniform_space


/-
Copyright (c) 2015 Joe Hendrix. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Sebastian Ullrich
-/

import data.vector.basic
import data.nat.pow

/-!
# Basic operations on bitvectors

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

This is a work-in-progress, and contains additions to other theories.

This file was moved to mathlib from core Lean in the switch to Lean 3.20.0c.
It is not fully in compliance with mathlib style standards.
-/

/-- `bitvec n` is a `vector` of `bool` with length `n`. -/
@[reducible] def bitvec (n : ℕ) := vector bool n

namespace bitvec
open nat
open vector

local infix `++ₜ`:65 := vector.append

/-- Create a zero bitvector -/
@[reducible] protected def zero (n : ℕ) : bitvec n := replicate n ff

/-- Create a bitvector of length `n` whose `n-1`st entry is 1 and other entries are 0. -/
@[reducible] protected def one : Π (n : ℕ), bitvec n
| 0        := nil
| (succ n) := replicate n ff ++ₜ tt::ᵥnil

/-- Create a bitvector from another with a provably equal length. -/
protected def cong {a b : ℕ} (h : a = b) : bitvec a → bitvec b
| ⟨x, p⟩ := ⟨x, h ▸ p⟩

/-- `bitvec` specific version of `vector.append` -/
def append {m n} : bitvec m → bitvec n → bitvec (m + n) := vector.append

/-! ### Shift operations -/
section shift
variable {n : ℕ}

/-- `shl x i` is the bitvector obtained by left-shifting `x` `i` times and padding with `ff`.
If `x.length < i` then this will return the all-`ff`s bitvector. -/
def shl (x : bitvec n) (i : ℕ) : bitvec n :=
bitvec.cong (by simp) $
  drop i x ++ₜ replicate (min n i) ff

/-- `fill_shr x i fill` is the bitvector obtained by right-shifting `x` `i` times and then
padding with `fill : bool`. If `x.length < i` then this will return the constant `fill`
bitvector. -/
def fill_shr (x : bitvec n) (i : ℕ) (fill : bool) : bitvec n :=
bitvec.cong
  begin
    by_cases (i ≤ n),
    { have h₁ := nat.sub_le n i,
      rw [min_eq_right h],
      rw [min_eq_left h₁, ← add_tsub_assoc_of_le h, nat.add_comm, add_tsub_cancel_right] },
    { have h₁ := le_of_not_ge h,
      rw [min_eq_left h₁, tsub_eq_zero_iff_le.mpr h₁, zero_min, nat.add_zero] }
  end $
  replicate (min n i) fill ++ₜ take (n-i) x

/-- unsigned shift right -/
def ushr (x : bitvec n) (i : ℕ) : bitvec n :=
fill_shr x i ff

/-- signed shift right -/
def sshr : Π {m : ℕ}, bitvec m → ℕ → bitvec m
| 0        _ _ := nil
| (succ m) x i := head x ::ᵥ fill_shr (tail x) i (head x)

end shift

/-! ### Bitwise operations -/
section bitwise
variable {n : ℕ}

/-- bitwise not -/
def not : bitvec n → bitvec n := map bnot
/-- bitwise and -/
def and : bitvec n → bitvec n → bitvec n := map₂ band
/-- bitwise or -/
def or  : bitvec n → bitvec n → bitvec n := map₂ bor
/-- bitwise xor -/
def xor : bitvec n → bitvec n → bitvec n := map₂ bxor

end bitwise

/-! ### Arithmetic operators -/
section arith
variable {n : ℕ}

/-- `xor3 x y c` is `((x XOR y) XOR c)`. -/
protected def xor3 (x y c : bool) := bxor (bxor x y) c
/-- `carry x y c` is `x && y || x && c || y && c`. -/
protected def carry (x y c : bool) :=
x && y || x && c || y && c

/-- `neg x` is the two's complement of `x`. -/
protected def neg (x : bitvec n) : bitvec n :=
let f := λ y c, (y || c, bxor y c) in
prod.snd (map_accumr f x ff)

/-- Add with carry (no overflow) -/
def adc (x y : bitvec n) (c : bool) : bitvec (n+1) :=
let f := λ x y c, (bitvec.carry x y c, bitvec.xor3 x y c) in
let ⟨c, z⟩ := vector.map_accumr₂ f x y c in
c ::ᵥ z

/-- The sum of two bitvectors -/
protected def add (x y : bitvec n) : bitvec n := tail (adc x y ff)

/-- Subtract with borrow -/
def sbb (x y : bitvec n) (b : bool) : bool × bitvec n :=
let f := λ x y c, (bitvec.carry (bnot x) y c, bitvec.xor3 x y c) in
vector.map_accumr₂ f x y b

/-- The difference of two bitvectors -/
protected def sub (x y : bitvec n) : bitvec n := prod.snd (sbb x y ff)

instance : has_zero (bitvec n) := ⟨bitvec.zero n⟩
instance : has_one (bitvec n)  := ⟨bitvec.one n⟩
instance : has_add (bitvec n)  := ⟨bitvec.add⟩
instance : has_sub (bitvec n)  := ⟨bitvec.sub⟩
instance : has_neg (bitvec n)  := ⟨bitvec.neg⟩

/-- The product of two bitvectors -/
protected def mul (x y : bitvec n) : bitvec n :=
let f := λ r b, cond b (r + r + y) (r + r) in
(to_list x).foldl f 0

instance : has_mul (bitvec n)  := ⟨bitvec.mul⟩
end arith

/-! ### Comparison operators -/
section comparison
variable {n : ℕ}

/-- `uborrow x y` returns `tt` iff the "subtract with borrow" operation on `x`, `y` and `ff`
required a borrow. -/
def uborrow (x y : bitvec n) : bool := prod.fst (sbb x y ff)

/-- unsigned less-than proposition -/
def ult (x y : bitvec n) : Prop := uborrow x y
/-- unsigned greater-than proposition -/
def ugt (x y : bitvec n) : Prop := ult y x

/-- unsigned less-than-or-equal-to proposition -/
def ule (x y : bitvec n) : Prop := ¬ (ult y x)
/-- unsigned greater-than-or-equal-to proposition -/
def uge (x y : bitvec n) : Prop := ule y x

/-- `sborrow x y` returns `tt` iff `x < y` as two's complement integers -/
def sborrow : Π {n : ℕ}, bitvec n → bitvec n → bool
| 0        _ _ := ff
| (succ n) x y :=
  match (head x, head y) with
  | (tt, ff) := tt
  | (ff, tt) := ff
  | _        := uborrow (tail x) (tail y)
  end

/-- signed less-than proposition -/
def slt (x y : bitvec n) : Prop := sborrow x y
/-- signed greater-than proposition -/
def sgt (x y : bitvec n) : Prop := slt y x
/-- signed less-than-or-equal-to proposition -/
def sle (x y : bitvec n) : Prop := ¬ (slt y x)
/-- signed greater-than-or-equal-to proposition -/
def sge (x y : bitvec n) : Prop := sle y x

end comparison

/-! ### Conversion to `nat` and `int` -/
section conversion
variable {α : Type}

/-- Create a bitvector from a `nat` -/
protected def of_nat : Π (n : ℕ), nat → bitvec n
| 0        x := nil
| (succ n) x := of_nat n (x / 2) ++ₜ to_bool (x % 2 = 1) ::ᵥ nil

/-- Create a bitvector in the two's complement representation from an `int` -/
protected def of_int : Π (n : ℕ), int → bitvec (succ n)
| n (int.of_nat m)          := ff ::ᵥ bitvec.of_nat n m
| n (int.neg_succ_of_nat m) := tt ::ᵥ not (bitvec.of_nat n m)

/-- `add_lsb r b` is `r + r + 1` if `b` is `tt` and `r + r` otherwise. -/
def add_lsb (r : ℕ) (b : bool) := r + r + cond b 1 0

/-- Given a `list` of `bool`s, return the `nat` they represent as a list of binary digits. -/
def bits_to_nat (v : list bool) : nat :=
v.foldl add_lsb 0

/-- Return the natural number encoded by the input bitvector -/
protected def to_nat {n : nat} (v : bitvec n) : nat :=
bits_to_nat (to_list v)

theorem bits_to_nat_to_list {n : ℕ} (x : bitvec n) :
bitvec.to_nat x = bits_to_nat (vector.to_list x)  := rfl

local attribute [simp] nat.add_comm nat.add_assoc nat.add_left_comm nat.mul_comm nat.mul_assoc
local attribute [simp] nat.zero_add nat.add_zero nat.one_mul nat.mul_one nat.zero_mul nat.mul_zero
-- mul_left_comm

theorem to_nat_append {m : ℕ} (xs : bitvec m) (b : bool) :
bitvec.to_nat (xs ++ₜ b::ᵥnil) = bitvec.to_nat xs * 2 + bitvec.to_nat (b::ᵥnil) :=
begin
  cases xs with xs P,
  simp [bits_to_nat_to_list], clear P,
  unfold bits_to_nat list.foldl,
  -- generalize the accumulator of foldl
  generalize h : 0 = x, conv in (add_lsb x b) { rw ←h }, clear h,
  simp,
  induction xs with x xs generalizing x,
  { simp, unfold list.foldl add_lsb, simp [nat.mul_succ] },
  { simp, apply xs_ih }
end

theorem bits_to_nat_to_bool (n : ℕ)
: bitvec.to_nat (to_bool (n % 2 = 1) ::ᵥ nil) = n % 2 :=
begin
  simp [bits_to_nat_to_list],
  unfold bits_to_nat add_lsb list.foldl cond,
  simp [cond_to_bool_mod_two],
end

theorem of_nat_succ {k n : ℕ}
:  bitvec.of_nat (succ k) n = bitvec.of_nat k (n / 2) ++ₜ to_bool (n % 2 = 1) ::ᵥ nil :=
rfl

theorem to_nat_of_nat {k n : ℕ}
: bitvec.to_nat (bitvec.of_nat k n) = n % 2 ^ k :=
begin
  induction k with k ih generalizing n,
  { simp [nat.mod_one], refl },
  { rw [of_nat_succ, to_nat_append, ih, bits_to_nat_to_bool, mod_pow_succ, nat.mul_comm] }
end

/-- Return the integer encoded by the input bitvector -/
protected def to_int : Π {n : nat}, bitvec n → int
| 0        _ := 0
| (succ n) v :=
  cond (head v)
    (int.neg_succ_of_nat $ bitvec.to_nat $ not $ tail v)
    (int.of_nat $ bitvec.to_nat $ tail v)

end conversion

/-! ### Miscellaneous instances -/
private def repr {n : nat} : bitvec n → string
| ⟨bs, p⟩ :=
  "0b" ++ (bs.map (λ b : bool, if b then '1' else '0')).as_string

instance (n : nat) : has_repr (bitvec n) :=
⟨repr⟩
end bitvec

instance {n} {x y : bitvec n} : decidable (bitvec.ult x y) := bool.decidable_eq _ _
instance {n} {x y : bitvec n} : decidable (bitvec.ugt x y) := bool.decidable_eq _ _