/-
Copyright (c) 2022 Haruhisa Enomoto. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Haruhisa Enomoto.
-/
import ring_theory.ideal.basic
import ring_theory.ideal.local_ring
import tactic.noncomm_ring

/-!
# Noncommutative Jacobson radical and local ring

In this file, we define the Jacobson radical of a ring and local rings
in the noncommutative setting, and prove their basic properties.

Let `R` be a (possibly noncommutative) ring. The *Jacobson radical* of `R` is defined
to be the intersection of all maximal *left* ideals of `R`, which coincides with that of
all maximal *right* ideals, so it is a two-sided ideal.

`R` is called an *nc-local ring* if it has a unique maximal *left* ideal.
This conditions is equivalent to that it has a unique maximal *right* ideal.
(Note that this is simply called a *local ring* in the noncommutative setting,
but we call it *nc-local* to avoid confusion.)

## Main definitions

* `nc_jacobson R : ideal R`: the Jacobson radical of a ring `R`, that is,
  the intersection of all maximal *left* ideals.

* `nc_local R : Prop`: the proposition that `R` is an nc-local ring, that is,
  it has a unique maximal *left* ideal.

## Main statements

* `mem_nc_jacobson_tfae`: several equivalent conditions for an element in a ring
  to be contained in the Jacobson radical.

* `nc_jacobson_symm`: the definition of the Jacobson radical is left-right symmetric.

* `nc_local_tfae`: several equivalent conditions for a ring to be nc-local.

* `nc_local_symm`: the definition of an nc-local ring is left-right symmetric.

## Implementation notes

We already have the typeclass `ring_theory.ideal.local_ring.local_ring`
for an nc-local (semi)ring. We use a different definition for a ring to be nc-local
for the convenience of the proof. `local_iff_nc_local` proves that `nc_local R`
if and only if `R` is an instance of `local_ring` (if `R` is a ring).
-/

universe u
open mul_opposite

section monoid
variables {M : Type u} {a : M} {b : M} [monoid M]
/-! ### Some lemmas on inverses in monoids -/

lemma is_unit_op_of_is_unit : is_unit a → is_unit (op a) :=
λ ⟨⟨_, _, hab, hba⟩, rfl⟩, ⟨⟨_, _, by rw [←op_mul, ←op_one, hba],
  by rw [←op_mul, ←op_one, hab]⟩, rfl⟩

lemma is_unit_unop_of_is_unit {x : Mᵐᵒᵖ} : is_unit x → is_unit (unop x) :=
λ ⟨⟨_, _, hxa, hax⟩, rfl⟩, ⟨⟨_, _, by rw [←unop_mul, ←unop_one, hax],
  by rw [←unop_mul, ←unop_one, hxa]⟩, rfl⟩

lemma is_unit_op_iff_is_unit {a : M} : is_unit (op a) ↔ is_unit a :=
⟨λ h, unop_op a ▸ is_unit_unop_of_is_unit h, is_unit_op_of_is_unit⟩

lemma is_unit_unop_iff_is_unit {x : Mᵐᵒᵖ} : is_unit (unop x) ↔ is_unit x :=
⟨λ h, op_unop x ▸ is_unit_op_of_is_unit h, is_unit_unop_of_is_unit⟩

lemma is_unit_iff_has_two_sided_inv :
  is_unit a ↔ ∃ x : M, a * x = 1 ∧ x * a = 1 :=
⟨λ ⟨⟨a, x, hax, hxa⟩, rfl⟩, ⟨x, hax, hxa⟩, λ ⟨x, hax, hxa⟩, ⟨⟨a, x, hax, hxa⟩, rfl⟩⟩

/-- An element `a : M` of a monoid has a left inverse
if there is some `x : M` satisfying `x * a = 1`. -/
def has_left_inv (a : M) : Prop := ∃ x : M, x * a = 1

/-- An element `a : M` of a monoid has a right inverse
if there is some `x : M` satisfying `a * x = 1`. -/
def has_right_inv (a : M) : Prop := ∃ x : M, a * x = 1

/-- An element of a monoid is a unit
if and only if it has both a left inverse and a right inverse. -/
theorem is_unit_iff_has_left_inv_right_inv :
  is_unit a ↔ has_left_inv a ∧ has_right_inv a :=
⟨λ h, ⟨h.exists_left_inv, h.exists_right_inv⟩,
  λ ⟨⟨x, hxa⟩, ⟨y, hay⟩⟩, ⟨⟨a, x, by { convert hay,
  rw [←mul_one x, ←hay, ←mul_assoc, hxa, one_mul] }, hxa⟩, rfl⟩⟩

/-- An element of a monoid is a unit
if it has a left inverse which also has a left inverse. -/
theorem is_unit_of_has_left_inv_of_has_left_inv :
  b * a = 1 → has_left_inv b → is_unit a :=
λ hba ⟨c, hcb⟩, ⟨⟨a, b, by { convert hcb,
  rw [←one_mul a, ←hcb, mul_assoc, hba, mul_one] }, hba⟩, rfl⟩

end monoid

section ring
variables {R : Type u} {a : R} {b : R} [ring R]
/-! ### Some lemmas on inverses in rings -/

lemma has_left_inv_one_mul_swap :
  has_left_inv (1 + a * b) → has_left_inv (1 + b * a) :=
begin
  rintro ⟨u, hu⟩,
  existsi 1 - b * u * a,
  calc (1 - b * u * a) * (1 + b * a)
        = 1 + b * a - b * (u * (1 + a * b)) * a : by noncomm_ring
    ... = 1 : by rw [hu, mul_one, add_sub_cancel],
end

lemma has_right_inv_one_add_mul_swap :
  has_right_inv (1 + a * b) → has_right_inv (1 + b * a) :=
begin
  rintro ⟨u, hu⟩,
  existsi 1 - b * u * a,
  calc (1 + b * a) * (1 - b * u * a)
      = 1 + b * a - b * ((1 + a * b ) * u) * a : by noncomm_ring
  ... = 1 : by rw [hu, mul_one, add_sub_cancel],
end

lemma is_unit_one_add_mul_swap :
  is_unit (1 + a * b) → is_unit (1 + b * a) :=
begin
  repeat {rw is_unit_iff_has_left_inv_right_inv},
  rintro ⟨h₁, h₂⟩,
  exact ⟨has_left_inv_one_mul_swap h₁, has_right_inv_one_add_mul_swap h₂⟩,
end

end ring

namespace ideal
section nc_jacobson_radical
variables {R : Type u} [ring R]
/-! ### Jacobson radical of a ring -/

/--
For a semiring `R`, `nc_jacobson R` is the Jacobson radical of `R`, that is,
the intersection of all maximal left ideals of of `R`. Note that we use left ideals.
-/
def nc_jacobson (R : Type u) [semiring R] : ideal R :=
Inf {I : ideal R | I.is_maximal }

lemma has_left_inv_iff_span_top {x : R} :
  has_left_inv x ↔ span ({x} : set R) = ⊤ :=
begin
  split,
  { rintro ⟨a, hax⟩,
    apply eq_top_of_unit_mem _ x a _ hax,
    apply submodule.mem_span_singleton_self },
  { intro h,
    have : (1 : R) ∈ span ({x} : set R) := by { rw h, exact submodule.mem_top },
    exact (mem_span_singleton').mp this },
end

lemma not_has_left_inv_iff_mem_maximal {x : R} :
  ¬has_left_inv x ↔ ∃ I : ideal R, I.is_maximal ∧ x ∈ I :=
begin
  rw has_left_inv_iff_span_top,
  split,
  { intro hx,
    obtain ⟨I, hImax, hxI⟩ := exists_le_maximal _ hx,
    exact ⟨I, hImax, by {apply hxI, apply submodule.mem_span_singleton_self}⟩ },
  { rintro ⟨I, hImax, hxI⟩ hcontra,
    refine hImax.ne_top _,
    rwa [eq_top_iff, ←hcontra, span_le, set.singleton_subset_iff] },
end

lemma has_left_inv_one_add_of_mem_jacobson {x : R} :
  x ∈ nc_jacobson R → has_left_inv (1 + x):=
begin
  contrapose,
  rw not_has_left_inv_iff_mem_maximal,
  rintro ⟨I, hImax, hxxI⟩ hx,
  refine hImax.ne_top _,
  rw eq_top_iff_one,
  have hxI : x ∈ I := by { rw [nc_jacobson, mem_Inf] at hx, apply hx hImax },
  exact (add_mem_cancel_right hxI).mp hxxI,
end

lemma one_add_mul_self_mem_maximal_of_not_mem_maximal {x : R} {I : ideal R} :
  I.is_maximal → x ∉ I → ∃ a : R, 1 + a * x ∈ I :=
begin
  intros hImax hxI,
  have : (1 : R) ∈ I ⊔ span {x},
  { rw is_maximal_iff at hImax,
    apply hImax.2 _ _ le_sup_left hxI,
    apply mem_sup_right,
    apply submodule.mem_span_singleton_self },
  rw submodule.mem_sup at this,
  obtain ⟨m, hmI, y, hy, hmy⟩ := this,
  rw mem_span_singleton' at hy,
  obtain ⟨a, rfl⟩ := hy,
  existsi -a,
  rwa [←hmy, neg_mul, add_neg_cancel_right],
end

/--
The following are equivalent for an element `x` in a ring `R`.
0. `x` is in the Jacobson radical of `R`, that is, contained in every mximal left ideal.
1. `1 + a * x` has a left inverse for any `a : R`.
2. `1 + a * x` is a unit for any `a : R`.
3. `1 + x * b` is a unit for any `b : R`.
4. `1 + a * x * b` is a unit for any `a b : R`.
-/
theorem mem_nc_jacobson_tfae {R : Type u} [ring R] (x : R) : tfae [
  x ∈ nc_jacobson R,
  ∀ a : R, has_left_inv (1 + a * x),
  ∀ a : R, is_unit (1 + a * x),
  ∀ b : R, is_unit (1 + x * b),
  ∀ a b : R, is_unit (1 + a * x * b)] :=
begin
  tfae_have : 1 → 2,
  { exact λ hx a, has_left_inv_one_add_of_mem_jacobson $
    (nc_jacobson R).smul_mem' a hx },
  tfae_have : 2 → 3,
  { intros hx a,
    obtain ⟨c, hc⟩ := hx a,
    apply is_unit_of_has_left_inv_of_has_left_inv hc,
    suffices : c = 1 + ( -c * a * x),
    { rw this, apply hx },
    calc c = c * (1 + a * x) + ( -c * a * x) : by noncomm_ring
      ...  = 1 + ( -c * a * x) : by rw hc },
  tfae_have : 3 → 5,
  { intros hx _ _,
    apply is_unit_one_add_mul_swap,
    rw ←mul_assoc,
    apply hx },
  tfae_have : 5 → 1,
  { intro h,
    by_contra hx,
    rw [nc_jacobson, submodule.mem_Inf] at hx,
    simp only [not_forall] at hx,
    rcases hx with ⟨I, hImax, hxI⟩,
    refine hImax.ne_top _,
    obtain ⟨a, ha⟩ := one_add_mul_self_mem_maximal_of_not_mem_maximal hImax hxI,
    apply eq_top_of_is_unit_mem _ ha,
    specialize h a 1,
    rwa [mul_assoc, mul_one] at h },
  tfae_have : 3 ↔ 4,
  { split; exact λ h b, is_unit_one_add_mul_swap (h b) },
  tfae_finish,
end

/--
The definition of the Jacobson radical of a ring is left-right symmetric, that is,
the intersection of all maximal left ideals coincides with that of all maximal right ideals.
We express this by using the opposite ring `Rᵐᵒᵖ`.
-/
theorem nc_jacobson_symm {x : R} :
  x ∈ nc_jacobson R ↔ op x ∈ nc_jacobson Rᵐᵒᵖ :=
begin
  split,
  { intro hx,
    rw (mem_nc_jacobson_tfae $ op x).out 0 3,
    intro a,
    rw [←is_unit_unop_iff_is_unit, unop_add, unop_one, unop_mul, unop_op],
    apply ((mem_nc_jacobson_tfae x).out 0 2).mp hx },
  { intro hx,
    rw (mem_nc_jacobson_tfae x).out 0 3,
    intro a,
    rw [←is_unit_op_iff_is_unit, op_add, op_one, op_mul],
    apply ((mem_nc_jacobson_tfae $ op x).out 0 2).mp hx },
end

end nc_jacobson_radical

section nc_local_ring
variables {R : Type u} [ring R]
/-! ### Noncommutative local ring -/

/-- A ring is an nc-local ring if it has a unique maximal left ideal.
Note that we use left ideals. -/
def nc_local (R : Type u) [ring R] : Prop :=
  (∃! I : ideal R, I.is_maximal)

lemma nc_local_iff_jacobson_is_maximal :
  nc_local R ↔ (nc_jacobson R).is_maximal :=
begin
  split,
  { rintro ⟨I, hImax, hIuniq⟩,
    convert hImax,
    have : {J : ideal R | J.is_maximal} = {I},
    { rw set.eq_singleton_iff_unique_mem, exact ⟨hImax, hIuniq⟩ },
    rw [nc_jacobson, this],
    exact Inf_singleton },
  { intro h,
    refine ⟨nc_jacobson R, h, _⟩,
    intros I hImax,
    exact (eq_top_or_eq_of_coatom_le h.1 (Inf_le hImax)).resolve_left hImax.ne_top },
end

lemma is_jacobson_of_nc_local_of_is_maximal {J : ideal R} :
  nc_local R → J.is_maximal → J = nc_jacobson R :=
λ ⟨I, hIuniq⟩ hJmax, (hIuniq.2 _ $ (nc_local_iff_jacobson_is_maximal).mp
  ⟨I, hIuniq⟩).symm ▸ hIuniq.2 _ hJmax

lemma nontrivial_of_nc_local : nc_local R → nontrivial R :=
λ ⟨I, hI⟩, nontrivial_of_ne 0 1 $ λ h, (ne_top_iff_one I).mp hI.1.ne_top (h ▸ zero_mem I)

/-- In an nc-local ring, an element with a left inverse is automatically a unit. -/
lemma is_unit_of_nc_local_of_has_left_inv :
  nc_local R → ∀ a : R, has_left_inv a → is_unit a :=
begin
  rintro h a ⟨x, hxa⟩,
  apply is_unit_of_has_left_inv_of_has_left_inv hxa,
  by_contra hx,
  suffices : is_unit (0 : R),
  { haveI := nontrivial_of_nc_local h,
    exact not_is_unit_zero this },
  obtain ⟨I, hImax, hxI⟩ := (not_has_left_inv_iff_mem_maximal).mp hx,
  replace hxI : x ∈ nc_jacobson R :=
    (is_jacobson_of_nc_local_of_is_maximal h hImax) ▸ hxI,
  replace hxI := ((mem_nc_jacobson_tfae x).out 0 3).mp hxI,
  specialize hxI (-a),
  rwa [mul_neg, hxa, add_right_neg] at hxI,
end

lemma nonunits_add_iff_is_unit_or_is_unit_one_sub_self :
  (∀ {a b : R}, a ∈ nonunits R → b ∈ nonunits R → a + b ∈ nonunits R) ↔
  ∀ a : R, is_unit a ∨ is_unit (1 - a) :=
begin
  split,
  { intros h a,
    by_contra hcont,
    rw not_or_distrib at hcont,
    specialize h hcont.1 hcont.2,
    rw add_sub_cancel'_right at h,
    exact one_not_mem_nonunits h },
  { intros h a b,
    suffices : is_unit (a + b) → ¬is_unit a → is_unit b,
    { exact λ h₁ h₂ h₃, h₂ $ (this h₃) h₁ },
    rintro ⟨u, hucoe⟩ ha, -- u = a + b
    rw ←units.is_unit_units_mul u⁻¹,
    have : ↑u⁻¹ * b = 1 - ↑u⁻¹ * a,
    { rw [eq_sub_iff_add_eq, add_comm, ← mul_add],
      exact units.inv_mul_of_eq hucoe },
    rw this,
    apply (h (↑u⁻¹ * a)).resolve_left,
    rwa units.is_unit_units_mul u⁻¹ },
end

/--
The following are equivalent for a non-zero ring `R`.
0. `R` is an nc-local ring, that is, it has a unique maximal left ideal.
1. The set of nonunits coincides with the Jacobson radical.
2. For any `a : R`, either `a` or `1 - a` is unit.
3. The set of nonunits are closed under additions.
-/
theorem nc_local_tfae (R : Type u) [ring R] [nontrivial R] : tfae [
    nc_local R,
    nonunits R = nc_jacobson R,
    ∀ a : R, is_unit a ∨ is_unit (1 - a),
    ∀ {a b : R}, a ∈ nonunits R → b ∈ nonunits R → a + b ∈ nonunits R] :=
begin
  tfae_have : 1 → 3,
  { intros h a,
    rw or_iff_not_imp_left,
    intro ha,
    replace ha : ¬has_left_inv a := ha ∘ (is_unit_of_nc_local_of_has_left_inv h a),
    rw not_has_left_inv_iff_mem_maximal at ha,
    obtain ⟨I, hImax, haI⟩ := ha,
    replace haI : a ∈ nc_jacobson R :=
      (is_jacobson_of_nc_local_of_is_maximal h hImax) ▸ haI,
    rw (mem_nc_jacobson_tfae a).out 0 2 at haI,
    specialize haI (-1),
    rwa [neg_mul, one_mul, ←sub_eq_add_neg] at haI },
  tfae_have : 3 → 2,
  { intro h,
    apply subset_antisymm, swap,
    { obtain ⟨_, hmax⟩ := exists_maximal R,
      exact subset_trans (Inf_le hmax) (coe_subset_nonunits hmax.ne_top) },
    intros x hx,
    change x ∈ nc_jacobson R,
    rw [mem_nonunits_iff, is_unit_iff_has_left_inv_right_inv, not_and_distrib] at hx,
    cases hx with hleft hright,
    { rw (mem_nc_jacobson_tfae x).out 0 2,
      intro a,
      specialize h (- a * x),
      rw [neg_mul, sub_neg_eq_add] at h,
      apply h.resolve_left,
      intro hax,
      apply hleft,
      obtain ⟨b, hbax⟩ := hax.exists_left_inv,
      existsi -b * a,
      rwa [mul_assoc, neg_mul, ←mul_neg] },
    { rw (mem_nc_jacobson_tfae x).out 0 3,
      intro b,
      specialize h (- x * b),
      rw [neg_mul, sub_neg_eq_add] at h,
      apply h.resolve_left _,
      intro hxb,
      apply hright,
      obtain ⟨c, hxbc⟩ := hxb.exists_right_inv,
      existsi -b * c,
      rwa [neg_mul, mul_neg, ←mul_assoc, ←neg_mul] } },
  tfae_have : 2 → 1,
  { intro h,
    existsi nc_jacobson R,
    dsimp only,
    split,
    { rw [is_maximal_def, is_coatom],
      split,
      { rw [ne_top_iff_one, ←set_like.mem_coe, ←h], exact one_not_mem_nonunits },
      intro I,
      contrapose,
      intro hI,
      refine not_lt_of_le _,
      rw [←set_like.coe_subset_coe, ←h],
      exact coe_subset_nonunits hI },
    { intros I hImax,
      apply le_antisymm,
      { rw [←set_like.coe_subset_coe, ←h],
        exact coe_subset_nonunits hImax.ne_top },
      { exact Inf_le hImax } } },
  tfae_have : 4 ↔ 3,
  { exact nonunits_add_iff_is_unit_or_is_unit_one_sub_self },
  tfae_finish,
end

/--
The definition of a local ring is left-right symmetric, that is,
a ring has a unique maximal left ideal if and only if it has a unique maximal right ideal.
We express this by using the opposite ring `Rᵐᵒᵖ`.
-/
theorem nc_local_symm [nontrivial R] : nc_local R ↔ nc_local Rᵐᵒᵖ :=
begin
  split,
  { intro h,
    rw (nc_local_tfae Rᵐᵒᵖ).out 0 2,
    rw (nc_local_tfae R).out 0 2 at h,
    intro a,
    rw [←@is_unit_unop_iff_is_unit _ _ a, ←@is_unit_unop_iff_is_unit _ _ (1 - a)],
    apply h },
  { intro h,
    rw (nc_local_tfae R).out 0 2,
    rw (nc_local_tfae Rᵐᵒᵖ).out 0 2 at h,
    intro a,
    rw [←@is_unit_op_iff_is_unit _ _ a, ←@is_unit_op_iff_is_unit _ _ (1 - a)],
    apply h },
end

/--
A ring `R` is an instance of `ring_theory.ideal.local_ring.local_ring`
if and only if `nc_local R` holds.
-/
theorem local_iff_nc_local : (local_ring R) ↔ (nc_local R) :=
begin
  split,
  { rintro ⟨_, hlocal⟩, resetI,
    rwa (nc_local_tfae R).out 0 3 },
  { intro h,
    haveI := nontrivial_of_nc_local h,
    have := ((nc_local_tfae R).out 0 3).mp h,
    exact local_ring.mk (λ _ _ ha hb, this ha hb) },
end

end nc_local_ring
end ideal


/-
Copyright (c) 2022 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser

! This file was ported from Lean 3 source module data.fin.tuple.nat_antidiagonal
! leanprover-community/mathlib commit 98e83c3d541c77cdb7da20d79611a780ff8e7d90
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.BigOperators.Fin
import Mathlib.Data.Finset.NatAntidiagonal
import Mathlib.Data.Fin.VecNotation
import Mathlib.Logic.Equiv.Fin

/-!
# Collections of tuples of naturals with the same sum

This file generalizes `List.Nat.Antidiagonal n`, `Multiset.Nat.Antidiagonal n`, and
`Finset.Nat.Antidiagonal n` from the pair of elements `x : ℕ × ℕ` such that `n = x.1 + x.2`, to
the sequence of elements `x : Fin k → ℕ` such that `n = ∑ i, x i`.

## Main definitions

* `List.Nat.antidiagonalTuple`
* `Multiset.Nat.antidiagonalTuple`
* `Finset.Nat.antidiagonalTuple`

## Main results

* `antidiagonalTuple 2 n` is analogous to `antidiagonal n`:

  * `List.Nat.antidiagonalTuple_two`
  * `Multiset.Nat.antidiagonalTuple_two`
  * `Finset.Nat.antidiagonalTuple_two`

## Implementation notes

While we could implement this by filtering `(Fintype.PiFinset $ λ _, range (n + 1))` or similar,
this implementation would be much slower.

In the future, we could consider generalizing `Finset.Nat.antidiagonalTuple` further to
support finitely-supported functions, as is done with `cut` in
`archive/100-theorems-list/45_partition.lean`.
-/

open BigOperators

/-! ### Lists -/

namespace List.Nat

/-- `List.antidiagonalTuple k n` is a list of all `k`-tuples which sum to `n`.

This list contains no duplicates (`List.Nat.nodup_antidiagonalTuple`), and is sorted
lexicographically (`List.Nat.antidiagonalTuple_pairwise_pi_lex`), starting with `![0, ..., n]`
and ending with `![n, ..., 0]`.

```
#eval antidiagonalTuple 3 2
-- [![0, 0, 2], ![0, 1, 1], ![0, 2, 0], ![1, 0, 1], ![1, 1, 0], ![2, 0, 0]]
```
-/
def antidiagonalTuple : ∀ k, ℕ → List (Fin k → ℕ)
  | 0, 0 => [![]]
  | 0, _ + 1 => []
  | k + 1, n =>
    (List.Nat.antidiagonal n).bind fun ni =>
      (antidiagonalTuple k ni.2).map fun x => Fin.cons ni.1 x
#align list.nat.antidiagonal_tuple List.Nat.antidiagonalTuple

@[simp]
theorem antidiagonalTuple_zero_zero : antidiagonalTuple 0 0 = [![]] :=
  rfl
#align list.nat.antidiagonal_tuple_zero_zero List.Nat.antidiagonalTuple_zero_zero

@[simp]
theorem antidiagonalTuple_zero_succ (n : ℕ) : antidiagonalTuple 0 n.succ = [] :=
  rfl
#align list.nat.antidiagonal_tuple_zero_succ List.Nat.antidiagonalTuple_zero_succ

theorem mem_antidiagonalTuple {n : ℕ} {k : ℕ} {x : Fin k → ℕ} :
    x ∈ antidiagonalTuple k n ↔ (∑ i, x i) = n := by
  induction x using Fin.consInduction generalizing n with
  | h0 =>
    cases n
    · simp
    · simp [eq_comm]
  | h x₀ x ih =>
    simp_rw [Fin.sum_cons]
    rw [antidiagonalTuple]  -- porting note: simp_rw doesn't use the equation lemma properly
    simp_rw [List.mem_bind, List.mem_map,
      List.Nat.mem_antidiagonal, Fin.cons_eq_cons, exists_eq_right_right, ih,
      @eq_comm _ _ (Prod.snd _), and_comm (a := Prod.snd _ = _),
      ←Prod.mk.inj_iff (a₁ := Prod.fst _), Prod.mk.eta, exists_eq_right]
#align list.nat.mem_antidiagonal_tuple List.Nat.mem_antidiagonalTuple

/-- The antidiagonal of `n` does not contain duplicate entries. -/
theorem nodup_antidiagonalTuple (k n : ℕ) : List.Nodup (antidiagonalTuple k n) := by
  induction' k with k ih generalizing n
  · cases n
    · simp
    · simp [eq_comm]
  simp_rw [antidiagonalTuple, List.nodup_bind]
  constructor
  · intro i _
    exact (ih i.snd).map (Fin.cons_right_injective (α := fun _ => ℕ) i.fst)
  induction' n with n n_ih
  · exact List.pairwise_singleton _ _
  · rw [List.Nat.antidiagonal_succ]
    refine' List.Pairwise.cons (fun a ha x hx₁ hx₂ => _) (n_ih.map _ fun a b h x hx₁ hx₂ => _)
    · rw [List.mem_map] at hx₁ hx₂ ha
      obtain ⟨⟨a, -, rfl⟩, ⟨x₁, -, rfl⟩, ⟨x₂, -, h⟩⟩ := ha, hx₁, hx₂
      rw [Fin.cons_eq_cons] at h
      injection h.1
    · rw [List.mem_map] at hx₁ hx₂
      obtain ⟨⟨x₁, hx₁, rfl⟩, ⟨x₂, hx₂, h₁₂⟩⟩ := hx₁, hx₂
      dsimp at h₁₂
      rw [Fin.cons_eq_cons, Nat.succ_inj'] at h₁₂
      obtain ⟨h₁₂, rfl⟩ := h₁₂
      rw [h₁₂] at h
      exact h (List.mem_map_of_mem _ hx₁) (List.mem_map_of_mem _ hx₂)
#align list.nat.nodup_antidiagonal_tuple List.Nat.nodup_antidiagonalTuple

theorem antidiagonalTuple_zero_right : ∀ k, antidiagonalTuple k 0 = [0]
  | 0 => (congr_arg fun x => [x]) <| Subsingleton.elim _ _
  | k + 1 =>
    by
    rw [antidiagonalTuple, antidiagonal_zero, List.bind_singleton, antidiagonalTuple_zero_right k,
      List.map_singleton]
    exact congr_arg (fun x => [x]) Matrix.cons_zero_zero
#align list.nat.antidiagonal_tuple_zero_right List.Nat.antidiagonalTuple_zero_right

@[simp]
theorem antidiagonalTuple_one (n : ℕ) : antidiagonalTuple 1 n = [![n]] := by
  simp_rw [antidiagonalTuple, antidiagonal, List.range_succ, List.map_append, List.map_singleton,
    tsub_self, List.bind_append, List.bind_singleton, antidiagonalTuple_zero_zero,
    List.map_singleton, List.map_bind]
  conv_rhs => rw [← List.nil_append [![n]]]
  congr 1
  simp_rw [List.bind_eq_nil, List.mem_range, List.map_eq_nil]
  intro x hx
  obtain ⟨m, rfl⟩ := Nat.exists_eq_add_of_lt hx
  rw [add_assoc, add_tsub_cancel_left, antidiagonalTuple_zero_succ]
#align list.nat.antidiagonal_tuple_one List.Nat.antidiagonalTuple_one

theorem antidiagonalTuple_two (n : ℕ) :
    antidiagonalTuple 2 n = (antidiagonal n).map fun i => ![i.1, i.2] := by
  rw [antidiagonalTuple]
  simp_rw [antidiagonalTuple_one, List.map_singleton]
  rw [List.map_eq_bind]
  rfl
#align list.nat.antidiagonal_tuple_two List.Nat.antidiagonalTuple_two

theorem antidiagonalTuple_pairwise_pi_lex :
    ∀ k n, (antidiagonalTuple k n).Pairwise (Pi.Lex (· < ·) @fun _ => (· < ·))
  | 0, 0 => List.pairwise_singleton _ _
  | 0, _ + 1 => List.Pairwise.nil
  | k + 1, n =>
    by
    simp_rw [antidiagonalTuple, List.pairwise_bind, List.pairwise_map, List.mem_map,
      forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]
    simp only [mem_antidiagonal, Prod.forall, and_imp, forall_apply_eq_imp_iff₂]
    simp only [Fin.pi_lex_lt_cons_cons, eq_self_iff_true, true_and_iff, lt_self_iff_false,
      false_or_iff]
    refine' ⟨fun _ _ _ => antidiagonalTuple_pairwise_pi_lex k _, _⟩
    induction' n with n n_ih
    · rw [antidiagonal_zero]
      exact List.pairwise_singleton _ _
    · rw [antidiagonal_succ, List.pairwise_cons, List.pairwise_map]
      refine' ⟨fun p hp x hx y hy => _, _⟩
      · rw [List.mem_map, Prod.exists] at hp
        obtain ⟨a, b, _, rfl : (Nat.succ a, b) = p⟩ := hp
        exact Or.inl (Nat.zero_lt_succ _)
      dsimp
      simp_rw [Nat.succ_inj', Nat.succ_lt_succ_iff]
      exact n_ih
#align list.nat.antidiagonal_tuple_pairwise_pi_lex List.Nat.antidiagonalTuple_pairwise_pi_lex

end List.Nat

/-! ### Multisets -/

namespace Multiset.Nat

/-- `Multiset.Nat.antidiagonalTuple k n` is a multiset of `k`-tuples summing to `n` -/
def antidiagonalTuple (k n : ℕ) : Multiset (Fin k → ℕ) :=
  List.Nat.antidiagonalTuple k n
#align multiset.nat.antidiagonal_tuple Multiset.Nat.antidiagonalTuple

@[simp]
theorem antidiagonalTuple_zero_zero : antidiagonalTuple 0 0 = {![]} :=
  rfl
#align multiset.nat.antidiagonal_tuple_zero_zero Multiset.Nat.antidiagonalTuple_zero_zero

@[simp]
theorem antidiagonalTuple_zero_succ (n : ℕ) : antidiagonalTuple 0 n.succ = 0 :=
  rfl
#align multiset.nat.antidiagonal_tuple_zero_succ Multiset.Nat.antidiagonalTuple_zero_succ

theorem mem_antidiagonalTuple {n : ℕ} {k : ℕ} {x : Fin k → ℕ} :
    x ∈ antidiagonalTuple k n ↔ (∑ i, x i) = n :=
  List.Nat.mem_antidiagonalTuple
#align multiset.nat.mem_antidiagonal_tuple Multiset.Nat.mem_antidiagonalTuple

theorem nodup_antidiagonalTuple (k n : ℕ) : (antidiagonalTuple k n).Nodup :=
  List.Nat.nodup_antidiagonalTuple _ _
#align multiset.nat.nodup_antidiagonal_tuple Multiset.Nat.nodup_antidiagonalTuple

theorem antidiagonalTuple_zero_right (k : ℕ) : antidiagonalTuple k 0 = {0} :=
  congr_arg _ (List.Nat.antidiagonalTuple_zero_right k)
#align multiset.nat.antidiagonal_tuple_zero_right Multiset.Nat.antidiagonalTuple_zero_right

@[simp]
theorem antidiagonalTuple_one (n : ℕ) : antidiagonalTuple 1 n = {![n]} :=
  congr_arg _ (List.Nat.antidiagonalTuple_one n)
#align multiset.nat.antidiagonal_tuple_one Multiset.Nat.antidiagonalTuple_one

theorem antidiagonalTuple_two (n : ℕ) :
    antidiagonalTuple 2 n = (antidiagonal n).map fun i => ![i.1, i.2] :=
  congr_arg _ (List.Nat.antidiagonalTuple_two n)
#align multiset.nat.antidiagonal_tuple_two Multiset.Nat.antidiagonalTuple_two

end Multiset.Nat

/-! ### Finsets -/

namespace Finset.Nat

/-- `Finset.Nat.antidiagonalTuple k n` is a finset of `k`-tuples summing to `n` -/
def antidiagonalTuple (k n : ℕ) : Finset (Fin k → ℕ) :=
  ⟨Multiset.Nat.antidiagonalTuple k n, Multiset.Nat.nodup_antidiagonalTuple k n⟩
#align finset.nat.antidiagonal_tuple Finset.Nat.antidiagonalTuple

@[simp]
theorem antidiagonalTuple_zero_zero : antidiagonalTuple 0 0 = {![]} :=
  rfl
#align finset.nat.antidiagonal_tuple_zero_zero Finset.Nat.antidiagonalTuple_zero_zero

@[simp]
theorem antidiagonalTuple_zero_succ (n : ℕ) : antidiagonalTuple 0 n.succ = ∅ :=
  rfl
#align finset.nat.antidiagonal_tuple_zero_succ Finset.Nat.antidiagonalTuple_zero_succ

theorem mem_antidiagonalTuple {n : ℕ} {k : ℕ} {x : Fin k → ℕ} :
    x ∈ antidiagonalTuple k n ↔ (∑ i, x i) = n :=
  List.Nat.mem_antidiagonalTuple
#align finset.nat.mem_antidiagonal_tuple Finset.Nat.mem_antidiagonalTuple

theorem antidiagonalTuple_zero_right (k : ℕ) : antidiagonalTuple k 0 = {0} :=
  Finset.eq_of_veq (Multiset.Nat.antidiagonalTuple_zero_right k)
#align finset.nat.antidiagonal_tuple_zero_right Finset.Nat.antidiagonalTuple_zero_right

@[simp]
theorem antidiagonalTuple_one (n : ℕ) : antidiagonalTuple 1 n = {![n]} :=
  Finset.eq_of_veq (Multiset.Nat.antidiagonalTuple_one n)
#align finset.nat.antidiagonal_tuple_one Finset.Nat.antidiagonalTuple_one

theorem antidiagonalTuple_two (n : ℕ) :
    antidiagonalTuple 2 n = (antidiagonal n).map (piFinTwoEquiv fun _ => ℕ).symm.toEmbedding :=
  Finset.eq_of_veq (Multiset.Nat.antidiagonalTuple_two n)
#align finset.nat.antidiagonal_tuple_two Finset.Nat.antidiagonalTuple_two

section EquivProd

/-- The disjoint union of antidiagonal tuples `Σ n, antidiagonalTuple k n` is equivalent to the
`k`-tuple `Fin k → ℕ`. This is such an equivalence, obtained by mapping `(n, x)` to `x`.

This is the tuple version of `Finset.Nat.sigmaAntidiagonalEquivProd`. -/
@[simps]
def sigmaAntidiagonalTupleEquivTuple (k : ℕ) : (Σ n, antidiagonalTuple k n) ≃ (Fin k → ℕ)
    where
  toFun x := x.2
  invFun x := ⟨∑ i, x i, x, mem_antidiagonalTuple.mpr rfl⟩
  left_inv := fun ⟨_, _, h⟩ => Sigma.subtype_ext (mem_antidiagonalTuple.mp h) rfl
  right_inv _ := rfl
#align finset.nat.sigma_antidiagonal_tuple_equiv_tuple Finset.Nat.sigmaAntidiagonalTupleEquivTuple

end EquivProd

end Finset.Nat


/-
Copyright (c) 2020 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Myers
-/
import algebra.invertible
import algebra.indicator_function
import algebra.module.big_operators
import data.fintype.big_operators
import linear_algebra.affine_space.affine_map
import linear_algebra.affine_space.affine_subspace
import linear_algebra.finsupp
import tactic.fin_cases

/-!
# Affine combinations of points

This file defines affine combinations of points.

## Main definitions

* `weighted_vsub_of_point` is a general weighted combination of
  subtractions with an explicit base point, yielding a vector.

* `weighted_vsub` uses an arbitrary choice of base point and is intended
  to be used when the sum of weights is 0, in which case the result is
  independent of the choice of base point.

* `affine_combination` adds the weighted combination to the arbitrary
  base point, yielding a point rather than a vector, and is intended
  to be used when the sum of weights is 1, in which case the result is
  independent of the choice of base point.

These definitions are for sums over a `finset`; versions for a
`fintype` may be obtained using `finset.univ`, while versions for a
`finsupp` may be obtained using `finsupp.support`.

## References

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

-/

noncomputable theory
open_locale big_operators affine

namespace finset

lemma univ_fin2 : (univ : finset (fin 2)) = {0, 1} :=
by { ext x, fin_cases x; simp }

variables {k : Type*} {V : Type*} {P : Type*} [ring k] [add_comm_group V] [module k V]
variables [S : affine_space V P]
include S

variables {ι : Type*} (s : finset ι)
variables {ι₂ : Type*} (s₂ : finset ι₂)

/-- A weighted sum of the results of subtracting a base point from the
given points, as a linear map on the weights.  The main cases of
interest are where the sum of the weights is 0, in which case the sum
is independent of the choice of base point, and where the sum of the
weights is 1, in which case the sum added to the base point is
independent of the choice of base point. -/
def weighted_vsub_of_point (p : ι → P) (b : P) : (ι → k) →ₗ[k] V :=
∑ i in s, (linear_map.proj i : (ι → k) →ₗ[k] k).smul_right (p i -ᵥ b)

@[simp] lemma weighted_vsub_of_point_apply (w : ι → k) (p : ι → P) (b : P) :
  s.weighted_vsub_of_point p b w = ∑ i in s, w i • (p i -ᵥ b) :=
by simp [weighted_vsub_of_point, linear_map.sum_apply]

/-- The value of `weighted_vsub_of_point`, where the given points are equal. -/
@[simp] lemma weighted_vsub_of_point_apply_const (w : ι → k) (p : P) (b : P) :
  s.weighted_vsub_of_point (λ _, p) b w = (∑ i in s, w i) • (p -ᵥ b) :=
by rw [weighted_vsub_of_point_apply, sum_smul]

/-- `weighted_vsub_of_point` gives equal results for two families of weights and two families of
points that are equal on `s`. -/
lemma weighted_vsub_of_point_congr {w₁ w₂ : ι → k} (hw : ∀ i ∈ s, w₁ i = w₂ i) {p₁ p₂ : ι → P}
  (hp : ∀ i ∈ s, p₁ i = p₂ i) (b : P) :
  s.weighted_vsub_of_point p₁ b w₁ = s.weighted_vsub_of_point p₂ b w₂ :=
begin
  simp_rw weighted_vsub_of_point_apply,
  convert sum_congr rfl (λ i hi, _),
  rw [hw i hi, hp i hi]
end

/-- Given a family of points, if we use a member of the family as a base point, the
`weighted_vsub_of_point` does not depend on the value of the weights at this point. -/
lemma weighted_vsub_of_point_eq_of_weights_eq
  (p : ι → P) (j : ι) (w₁ w₂ : ι → k) (hw : ∀ i, i ≠ j → w₁ i = w₂ i) :
  s.weighted_vsub_of_point p (p j) w₁ = s.weighted_vsub_of_point p (p j) w₂ :=
begin
  simp only [finset.weighted_vsub_of_point_apply],
  congr,
  ext i,
  cases eq_or_ne i j with h h,
  { simp [h], },
  { simp [hw i h], },
end

/-- The weighted sum is independent of the base point when the sum of
the weights is 0. -/
lemma weighted_vsub_of_point_eq_of_sum_eq_zero (w : ι → k) (p : ι → P) (h : ∑ i in s, w i = 0)
    (b₁ b₂ : P) : s.weighted_vsub_of_point p b₁ w = s.weighted_vsub_of_point p b₂ w :=
begin
  apply eq_of_sub_eq_zero,
  rw [weighted_vsub_of_point_apply, weighted_vsub_of_point_apply, ←sum_sub_distrib],
  conv_lhs
  { congr,
    skip,
    funext,
    rw [←smul_sub, vsub_sub_vsub_cancel_left] },
  rw [←sum_smul, h, zero_smul]
end

/-- The weighted sum, added to the base point, is independent of the
base point when the sum of the weights is 1. -/
lemma weighted_vsub_of_point_vadd_eq_of_sum_eq_one (w : ι → k) (p : ι → P) (h : ∑ i in s, w i = 1)
    (b₁ b₂ : P) :
  s.weighted_vsub_of_point p b₁ w +ᵥ b₁ = s.weighted_vsub_of_point p b₂ w +ᵥ b₂ :=
begin
  erw [weighted_vsub_of_point_apply, weighted_vsub_of_point_apply, ←@vsub_eq_zero_iff_eq V,
       vadd_vsub_assoc, vsub_vadd_eq_vsub_sub, ←add_sub_assoc, add_comm, add_sub_assoc,
       ←sum_sub_distrib],
  conv_lhs
  { congr,
    skip,
    congr,
    skip,
    funext,
    rw [←smul_sub, vsub_sub_vsub_cancel_left] },
  rw [←sum_smul, h, one_smul, vsub_add_vsub_cancel, vsub_self]
end

/-- The weighted sum is unaffected by removing the base point, if
present, from the set of points. -/
@[simp] lemma weighted_vsub_of_point_erase [decidable_eq ι] (w : ι → k) (p : ι → P) (i : ι) :
  (s.erase i).weighted_vsub_of_point p (p i) w = s.weighted_vsub_of_point p (p i) w :=
begin
  rw [weighted_vsub_of_point_apply, weighted_vsub_of_point_apply],
  apply sum_erase,
  rw [vsub_self, smul_zero]
end

/-- The weighted sum is unaffected by adding the base point, whether
or not present, to the set of points. -/
@[simp] lemma weighted_vsub_of_point_insert [decidable_eq ι] (w : ι → k) (p : ι → P) (i : ι) :
  (insert i s).weighted_vsub_of_point p (p i) w = s.weighted_vsub_of_point p (p i) w :=
begin
  rw [weighted_vsub_of_point_apply, weighted_vsub_of_point_apply],
  apply sum_insert_zero,
  rw [vsub_self, smul_zero]
end

/-- The weighted sum is unaffected by changing the weights to the
corresponding indicator function and adding points to the set. -/
lemma weighted_vsub_of_point_indicator_subset (w : ι → k) (p : ι → P) (b : P) {s₁ s₂ : finset ι}
    (h : s₁ ⊆ s₂) :
  s₁.weighted_vsub_of_point p b w = s₂.weighted_vsub_of_point p b (set.indicator ↑s₁ w) :=
begin
  rw [weighted_vsub_of_point_apply, weighted_vsub_of_point_apply],
  exact set.sum_indicator_subset_of_eq_zero w (λ i wi, wi • (p i -ᵥ b : V)) h (λ i, zero_smul k _)
end

/-- A weighted sum, over the image of an embedding, equals a weighted
sum with the same points and weights over the original
`finset`. -/
lemma weighted_vsub_of_point_map (e : ι₂ ↪ ι) (w : ι → k) (p : ι → P) (b : P) :
  (s₂.map e).weighted_vsub_of_point p b w = s₂.weighted_vsub_of_point (p ∘ e) b (w ∘ e) :=
begin
  simp_rw [weighted_vsub_of_point_apply],
  exact finset.sum_map _ _ _
end

/-- A weighted sum of pairwise subtractions, expressed as a subtraction of two
`weighted_vsub_of_point` expressions. -/
lemma sum_smul_vsub_eq_weighted_vsub_of_point_sub (w : ι → k) (p₁ p₂ : ι → P) (b : P) :
  ∑ i in s, w i • (p₁ i -ᵥ p₂ i) =
    s.weighted_vsub_of_point p₁ b w - s.weighted_vsub_of_point p₂ b w :=
by simp_rw [weighted_vsub_of_point_apply, ←sum_sub_distrib, ←smul_sub, vsub_sub_vsub_cancel_right]

/-- A weighted sum of pairwise subtractions, where the point on the right is constant,
expressed as a subtraction involving a `weighted_vsub_of_point` expression. -/
lemma sum_smul_vsub_const_eq_weighted_vsub_of_point_sub (w : ι → k) (p₁ : ι → P) (p₂ b : P) :
  ∑ i in s, w i • (p₁ i -ᵥ p₂) = s.weighted_vsub_of_point p₁ b w - (∑ i in s, w i) • (p₂ -ᵥ b) :=
by rw [sum_smul_vsub_eq_weighted_vsub_of_point_sub, weighted_vsub_of_point_apply_const]

/-- A weighted sum of pairwise subtractions, where the point on the left is constant,
expressed as a subtraction involving a `weighted_vsub_of_point` expression. -/
lemma sum_smul_const_vsub_eq_sub_weighted_vsub_of_point (w : ι → k) (p₂ : ι → P) (p₁ b : P) :
  ∑ i in s, w i • (p₁ -ᵥ p₂ i) = (∑ i in s, w i) • (p₁ -ᵥ b) - s.weighted_vsub_of_point p₂ b w :=
by rw [sum_smul_vsub_eq_weighted_vsub_of_point_sub, weighted_vsub_of_point_apply_const]

/-- A weighted sum may be split into such sums over two subsets. -/
lemma weighted_vsub_of_point_sdiff [decidable_eq ι] {s₂ : finset ι} (h : s₂ ⊆ s) (w : ι → k)
  (p : ι → P) (b : P) : (s \ s₂).weighted_vsub_of_point p b w + s₂.weighted_vsub_of_point p b w =
    s.weighted_vsub_of_point p b w :=
by simp_rw [weighted_vsub_of_point_apply, sum_sdiff h]

/-- A weighted sum may be split into a subtraction of such sums over two subsets. -/
lemma weighted_vsub_of_point_sdiff_sub [decidable_eq ι] {s₂ : finset ι} (h : s₂ ⊆ s) (w : ι → k)
  (p : ι → P) (b : P) : (s \ s₂).weighted_vsub_of_point p b w - s₂.weighted_vsub_of_point p b (-w) =
    s.weighted_vsub_of_point p b w :=
by rw [map_neg, sub_neg_eq_add, s.weighted_vsub_of_point_sdiff h]

/-- A weighted sum over `s.subtype pred` equals one over `s.filter pred`. -/
lemma weighted_vsub_of_point_subtype_eq_filter (w : ι → k) (p : ι → P) (b : P)
  (pred : ι → Prop) [decidable_pred pred] :
  (s.subtype pred).weighted_vsub_of_point (λ i, p i) b (λ i, w i) =
    (s.filter pred).weighted_vsub_of_point p b w :=
by rw [weighted_vsub_of_point_apply, weighted_vsub_of_point_apply, ←sum_subtype_eq_sum_filter]

/-- A weighted sum over `s.filter pred` equals one over `s` if all the weights at indices in `s`
not satisfying `pred` are zero. -/
lemma weighted_vsub_of_point_filter_of_ne (w : ι → k) (p : ι → P) (b : P) {pred : ι → Prop}
  [decidable_pred pred] (h : ∀ i ∈ s, w i ≠ 0 → pred i) :
  (s.filter pred).weighted_vsub_of_point p b w = s.weighted_vsub_of_point p b w :=
begin
  rw [weighted_vsub_of_point_apply, weighted_vsub_of_point_apply, sum_filter_of_ne],
  intros i hi hne,
  refine h i hi _,
  intro hw,
  simpa [hw] using hne,
end

/-- A constant multiplier of the weights in `weighted_vsub_of_point` may be moved outside the
sum. -/
lemma weighted_vsub_of_point_const_smul (w : ι → k) (p : ι → P) (b : P) (c : k) :
  s.weighted_vsub_of_point p b (c • w) = c • s.weighted_vsub_of_point p b w :=
by simp_rw [weighted_vsub_of_point_apply, smul_sum, pi.smul_apply, smul_smul, smul_eq_mul]

/-- A weighted sum of the results of subtracting a default base point
from the given points, as a linear map on the weights.  This is
intended to be used when the sum of the weights is 0; that condition
is specified as a hypothesis on those lemmas that require it. -/
def weighted_vsub (p : ι → P) : (ι → k) →ₗ[k] V :=
s.weighted_vsub_of_point p (classical.choice S.nonempty)

/-- Applying `weighted_vsub` with given weights.  This is for the case
where a result involving a default base point is OK (for example, when
that base point will cancel out later); a more typical use case for
`weighted_vsub` would involve selecting a preferred base point with
`weighted_vsub_eq_weighted_vsub_of_point_of_sum_eq_zero` and then
using `weighted_vsub_of_point_apply`. -/
lemma weighted_vsub_apply (w : ι → k) (p : ι → P) :
  s.weighted_vsub p w = ∑ i in s, w i • (p i -ᵥ (classical.choice S.nonempty)) :=
by simp [weighted_vsub, linear_map.sum_apply]

/-- `weighted_vsub` gives the sum of the results of subtracting any
base point, when the sum of the weights is 0. -/
lemma weighted_vsub_eq_weighted_vsub_of_point_of_sum_eq_zero (w : ι → k) (p : ι → P)
    (h : ∑ i in s, w i = 0) (b : P) : s.weighted_vsub p w = s.weighted_vsub_of_point p b w :=
s.weighted_vsub_of_point_eq_of_sum_eq_zero w p h _ _

/-- The value of `weighted_vsub`, where the given points are equal and the sum of the weights
is 0. -/
@[simp] lemma weighted_vsub_apply_const (w : ι → k) (p : P) (h : ∑ i in s, w i = 0) :
  s.weighted_vsub (λ _, p) w = 0 :=
by rw [weighted_vsub, weighted_vsub_of_point_apply_const, h, zero_smul]

/-- The `weighted_vsub` for an empty set is 0. -/
@[simp] lemma weighted_vsub_empty (w : ι → k) (p : ι → P) :
  (∅ : finset ι).weighted_vsub p w = (0:V) :=
by simp [weighted_vsub_apply]

/-- `weighted_vsub` gives equal results for two families of weights and two families of points
that are equal on `s`. -/
lemma weighted_vsub_congr {w₁ w₂ : ι → k} (hw : ∀ i ∈ s, w₁ i = w₂ i) {p₁ p₂ : ι → P}
  (hp : ∀ i ∈ s, p₁ i = p₂ i) : s.weighted_vsub p₁ w₁ = s.weighted_vsub p₂ w₂ :=
s.weighted_vsub_of_point_congr hw hp _

/-- The weighted sum is unaffected by changing the weights to the
corresponding indicator function and adding points to the set. -/
lemma weighted_vsub_indicator_subset (w : ι → k) (p : ι → P) {s₁ s₂ : finset ι} (h : s₁ ⊆ s₂) :
  s₁.weighted_vsub p w = s₂.weighted_vsub p (set.indicator ↑s₁ w) :=
weighted_vsub_of_point_indicator_subset _ _ _ h

/-- A weighted subtraction, over the image of an embedding, equals a
weighted subtraction with the same points and weights over the
original `finset`. -/
lemma weighted_vsub_map (e : ι₂ ↪ ι) (w : ι → k) (p : ι → P) :
  (s₂.map e).weighted_vsub p w = s₂.weighted_vsub (p ∘ e) (w ∘ e) :=
s₂.weighted_vsub_of_point_map _ _ _ _

/-- A weighted sum of pairwise subtractions, expressed as a subtraction of two `weighted_vsub`
expressions. -/
lemma sum_smul_vsub_eq_weighted_vsub_sub (w : ι → k) (p₁ p₂ : ι → P) :
  ∑ i in s, w i • (p₁ i -ᵥ p₂ i) = s.weighted_vsub p₁ w - s.weighted_vsub p₂ w :=
s.sum_smul_vsub_eq_weighted_vsub_of_point_sub _ _ _ _

/-- A weighted sum of pairwise subtractions, where the point on the right is constant and the
sum of the weights is 0. -/
lemma sum_smul_vsub_const_eq_weighted_vsub (w : ι → k) (p₁ : ι → P) (p₂ : P)
  (h : ∑ i in s, w i = 0) :
  ∑ i in s, w i • (p₁ i -ᵥ p₂) = s.weighted_vsub p₁ w :=
by rw [sum_smul_vsub_eq_weighted_vsub_sub, s.weighted_vsub_apply_const _ _ h, sub_zero]

/-- A weighted sum of pairwise subtractions, where the point on the left is constant and the
sum of the weights is 0. -/
lemma sum_smul_const_vsub_eq_neg_weighted_vsub (w : ι → k) (p₂ : ι → P) (p₁ : P)
  (h : ∑ i in s, w i = 0) :
  ∑ i in s, w i • (p₁ -ᵥ p₂ i) = -s.weighted_vsub p₂ w :=
by rw [sum_smul_vsub_eq_weighted_vsub_sub, s.weighted_vsub_apply_const _ _ h, zero_sub]

/-- A weighted sum may be split into such sums over two subsets. -/
lemma weighted_vsub_sdiff [decidable_eq ι] {s₂ : finset ι} (h : s₂ ⊆ s) (w : ι → k)
  (p : ι → P) : (s \ s₂).weighted_vsub p w + s₂.weighted_vsub p w = s.weighted_vsub p w :=
s.weighted_vsub_of_point_sdiff h _ _ _

/-- A weighted sum may be split into a subtraction of such sums over two subsets. -/
lemma weighted_vsub_sdiff_sub [decidable_eq ι] {s₂ : finset ι} (h : s₂ ⊆ s) (w : ι → k)
  (p : ι → P) : (s \ s₂).weighted_vsub p w - s₂.weighted_vsub p (-w) = s.weighted_vsub p w :=
s.weighted_vsub_of_point_sdiff_sub h _ _ _

/-- A weighted sum over `s.subtype pred` equals one over `s.filter pred`. -/
lemma weighted_vsub_subtype_eq_filter (w : ι → k) (p : ι → P) (pred : ι → Prop)
  [decidable_pred pred] :
  (s.subtype pred).weighted_vsub (λ i, p i) (λ i, w i) = (s.filter pred).weighted_vsub p w :=
s.weighted_vsub_of_point_subtype_eq_filter _ _ _ _

/-- A weighted sum over `s.filter pred` equals one over `s` if all the weights at indices in `s`
not satisfying `pred` are zero. -/
lemma weighted_vsub_filter_of_ne (w : ι → k) (p : ι → P) {pred : ι → Prop}
  [decidable_pred pred] (h : ∀ i ∈ s, w i ≠ 0 → pred i) :
  (s.filter pred).weighted_vsub p w = s.weighted_vsub p w :=
s.weighted_vsub_of_point_filter_of_ne _ _ _ h

/-- A constant multiplier of the weights in `weighted_vsub_of` may be moved outside the sum. -/
lemma weighted_vsub_const_smul (w : ι → k) (p : ι → P) (c : k) :
  s.weighted_vsub p (c • w) = c • s.weighted_vsub p w :=
s.weighted_vsub_of_point_const_smul _ _ _ _

variables (k)

/-- A weighted sum of the results of subtracting a default base point
from the given points, added to that base point, as an affine map on
the weights.  This is intended to be used when the sum of the weights
is 1, in which case it is an affine combination (barycenter) of the
points with the given weights; that condition is specified as a
hypothesis on those lemmas that require it. -/
def affine_combination (p : ι → P) : (ι → k) →ᵃ[k] P :=
{ to_fun := λ w,
    s.weighted_vsub_of_point p (classical.choice S.nonempty) w +ᵥ (classical.choice S.nonempty),
  linear := s.weighted_vsub p,
  map_vadd' := λ w₁ w₂, by simp_rw [vadd_vadd, weighted_vsub, vadd_eq_add, linear_map.map_add] }

/-- The linear map corresponding to `affine_combination` is
`weighted_vsub`. -/
@[simp] lemma affine_combination_linear (p : ι → P) :
  (s.affine_combination k p).linear = s.weighted_vsub p :=
rfl

variables {k}

/-- Applying `affine_combination` with given weights.  This is for the
case where a result involving a default base point is OK (for example,
when that base point will cancel out later); a more typical use case
for `affine_combination` would involve selecting a preferred base
point with
`affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one` and
then using `weighted_vsub_of_point_apply`. -/
lemma affine_combination_apply (w : ι → k) (p : ι → P) :
  s.affine_combination k p w =
    s.weighted_vsub_of_point p (classical.choice S.nonempty) w +ᵥ (classical.choice S.nonempty) :=
rfl

/-- The value of `affine_combination`, where the given points are equal. -/
@[simp] lemma affine_combination_apply_const (w : ι → k) (p : P) (h : ∑ i in s, w i = 1) :
  s.affine_combination k (λ _, p) w = p :=
by rw [affine_combination_apply, s.weighted_vsub_of_point_apply_const, h, one_smul, vsub_vadd]

/-- `affine_combination` gives equal results for two families of weights and two families of
points that are equal on `s`. -/
lemma affine_combination_congr {w₁ w₂ : ι → k} (hw : ∀ i ∈ s, w₁ i = w₂ i) {p₁ p₂ : ι → P}
  (hp : ∀ i ∈ s, p₁ i = p₂ i) : s.affine_combination k p₁ w₁ = s.affine_combination k p₂ w₂ :=
by simp_rw [affine_combination_apply, s.weighted_vsub_of_point_congr hw hp]

/-- `affine_combination` gives the sum with any base point, when the
sum of the weights is 1. -/
lemma affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one (w : ι → k) (p : ι → P)
    (h : ∑ i in s, w i = 1) (b : P) :
  s.affine_combination k p w = s.weighted_vsub_of_point p b w +ᵥ b :=
s.weighted_vsub_of_point_vadd_eq_of_sum_eq_one w p h _ _

/-- Adding a `weighted_vsub` to an `affine_combination`. -/
lemma weighted_vsub_vadd_affine_combination (w₁ w₂ : ι → k) (p : ι → P) :
  s.weighted_vsub p w₁ +ᵥ s.affine_combination k p w₂ = s.affine_combination k p (w₁ + w₂) :=
by rw [←vadd_eq_add, affine_map.map_vadd, affine_combination_linear]

/-- Subtracting two `affine_combination`s. -/
lemma affine_combination_vsub (w₁ w₂ : ι → k) (p : ι → P) :
  s.affine_combination k p w₁ -ᵥ s.affine_combination k p w₂ = s.weighted_vsub p (w₁ - w₂) :=
by rw [←affine_map.linear_map_vsub, affine_combination_linear, vsub_eq_sub]

lemma attach_affine_combination_of_injective [decidable_eq P]
  (s : finset P) (w : P → k) (f : s → P) (hf : function.injective f) :
  s.attach.affine_combination k f (w ∘ f) = (image f univ).affine_combination k id w :=
begin
  simp only [affine_combination, weighted_vsub_of_point_apply, id.def, vadd_right_cancel_iff,
    function.comp_app, affine_map.coe_mk],
  let g₁ : s → V := λ i, w (f i) • (f i -ᵥ classical.choice S.nonempty),
  let g₂ : P → V := λ i, w i • (i -ᵥ classical.choice S.nonempty),
  change univ.sum g₁ = (image f univ).sum g₂,
  have hgf : g₁ = g₂ ∘ f, { ext, simp, },
  rw [hgf, sum_image],
  exact λ _ _ _ _ hxy, hf hxy,
end

lemma attach_affine_combination_coe (s : finset P) (w : P → k) :
  s.attach.affine_combination k (coe : s → P) (w ∘ coe) = s.affine_combination k id w :=
by classical; rw [attach_affine_combination_of_injective s w (coe : s → P) subtype.coe_injective,
  univ_eq_attach, attach_image_coe]

omit S

/-- Viewing a module as an affine space modelled on itself, a `weighted_vsub` is just a linear
combination. -/
@[simp] lemma weighted_vsub_eq_linear_combination
  {ι} (s : finset ι) {w : ι → k} {p : ι → V} (hw : s.sum w = 0) :
  s.weighted_vsub p w = ∑ i in s, w i • p i :=
by simp [s.weighted_vsub_apply, vsub_eq_sub, smul_sub, ← finset.sum_smul, hw]

/-- Viewing a module as an affine space modelled on itself, affine combinations are just linear
combinations. -/
@[simp] lemma affine_combination_eq_linear_combination (s : finset ι) (p : ι → V) (w : ι → k)
  (hw : ∑ i in s, w i = 1) :
  s.affine_combination k p w = ∑ i in s, w i • p i :=
by simp [s.affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one w p hw 0]

include S

/-- An `affine_combination` equals a point if that point is in the set
and has weight 1 and the other points in the set have weight 0. -/
@[simp]

/-- An affine combination is unaffected by changing the weights to the
corresponding indicator function and adding points to the set. -/
lemma affine_combination_indicator_subset (w : ι → k) (p : ι → P) {s₁ s₂ : finset ι}
    (h : s₁ ⊆ s₂) :
  s₁.affine_combination k p w = s₂.affine_combination k p (set.indicator ↑s₁ w) :=
by rw [affine_combination_apply, affine_combination_apply,
       weighted_vsub_of_point_indicator_subset _ _ _ h]

/-- An affine combination, over the image of an embedding, equals an
affine combination with the same points and weights over the original
`finset`. -/
lemma affine_combination_map (e : ι₂ ↪ ι) (w : ι → k) (p : ι → P) :
  (s₂.map e).affine_combination k p w = s₂.affine_combination k (p ∘ e) (w ∘ e) :=
by simp_rw [affine_combination_apply, weighted_vsub_of_point_map]

/-- A weighted sum of pairwise subtractions, expressed as a subtraction of two `affine_combination`
expressions. -/
lemma sum_smul_vsub_eq_affine_combination_vsub (w : ι → k) (p₁ p₂ : ι → P) :
  ∑ i in s, w i • (p₁ i -ᵥ p₂ i) = s.affine_combination k p₁ w -ᵥ s.affine_combination k p₂ w :=
begin
  simp_rw [affine_combination_apply, vadd_vsub_vadd_cancel_right],
  exact s.sum_smul_vsub_eq_weighted_vsub_of_point_sub _ _ _ _
end

/-- A weighted sum of pairwise subtractions, where the point on the right is constant and the
sum of the weights is 1. -/
lemma sum_smul_vsub_const_eq_affine_combination_vsub (w : ι → k) (p₁ : ι → P) (p₂ : P)
  (h : ∑ i in s, w i = 1) :
  ∑ i in s, w i • (p₁ i -ᵥ p₂) = s.affine_combination k p₁ w -ᵥ p₂ :=
by rw [sum_smul_vsub_eq_affine_combination_vsub, affine_combination_apply_const _ _ _ h]

/-- A weighted sum of pairwise subtractions, where the point on the left is constant and the
sum of the weights is 1. -/
lemma sum_smul_const_vsub_eq_vsub_affine_combination (w : ι → k) (p₂ : ι → P) (p₁ : P)
  (h : ∑ i in s, w i = 1) :
  ∑ i in s, w i • (p₁ -ᵥ p₂ i) = p₁ -ᵥ s.affine_combination k p₂ w :=
by rw [sum_smul_vsub_eq_affine_combination_vsub, affine_combination_apply_const _ _ _ h]

/-- A weighted sum may be split into a subtraction of affine combinations over two subsets. -/
lemma affine_combination_sdiff_sub [decidable_eq ι] {s₂ : finset ι} (h : s₂ ⊆ s) (w : ι → k)
  (p : ι → P) :
  (s \ s₂).affine_combination k p w -ᵥ s₂.affine_combination k p (-w) = s.weighted_vsub p w :=
begin
  simp_rw [affine_combination_apply, vadd_vsub_vadd_cancel_right],
  exact s.weighted_vsub_sdiff_sub h _ _
end

/-- If a weighted sum is zero and one of the weights is `-1`, the corresponding point is
the affine combination of the other points with the given weights. -/
lemma affine_combination_eq_of_weighted_vsub_eq_zero_of_eq_neg_one {w : ι → k} {p : ι → P}
  (hw : s.weighted_vsub p w = (0 : V)) {i : ι} [decidable_pred (≠ i)] (his : i ∈ s)
  (hwi : w i = -1) : (s.filter (≠ i)).affine_combination k p w = p i :=
begin
  classical,
  rw [←@vsub_eq_zero_iff_eq V, ←hw,
      ←s.affine_combination_sdiff_sub (singleton_subset_iff.2 his), sdiff_singleton_eq_erase,
      ←filter_ne'],
  congr,
  refine (affine_combination_of_eq_one_of_eq_zero _ _ _ (mem_singleton_self _) _ _).symm,
  { simp [hwi] },
  { simp }
end

/-- An affine combination over `s.subtype pred` equals one over `s.filter pred`. -/
lemma affine_combination_subtype_eq_filter (w : ι → k) (p : ι → P) (pred : ι → Prop)
  [decidable_pred pred] :
  (s.subtype pred).affine_combination k (λ i, p i) (λ i, w i) =
  (s.filter pred).affine_combination k p w :=
by rw [affine_combination_apply, affine_combination_apply, weighted_vsub_of_point_subtype_eq_filter]

/-- An affine combination over `s.filter pred` equals one over `s` if all the weights at indices
in `s` not satisfying `pred` are zero. -/
lemma affine_combination_filter_of_ne (w : ι → k) (p : ι → P) {pred : ι → Prop}
  [decidable_pred pred] (h : ∀ i ∈ s, w i ≠ 0 → pred i) :
  (s.filter pred).affine_combination k p w = s.affine_combination k p w :=
by rw [affine_combination_apply, affine_combination_apply,
       s.weighted_vsub_of_point_filter_of_ne _ _ _ h]

variables {V}

/-- Suppose an indexed family of points is given, along with a subset
of the index type.  A vector can be expressed as
`weighted_vsub_of_point` using a `finset` lying within that subset and
with a given sum of weights if and only if it can be expressed as
`weighted_vsub_of_point` with that sum of weights for the
corresponding indexed family whose index type is the subtype
corresponding to that subset. -/
lemma eq_weighted_vsub_of_point_subset_iff_eq_weighted_vsub_of_point_subtype {v : V} {x : k}
    {s : set ι} {p : ι → P} {b : P} :
  (∃ (fs : finset ι) (hfs : ↑fs ⊆ s) (w : ι → k) (hw : ∑ i in fs, w i = x),
    v = fs.weighted_vsub_of_point p b w) ↔
  ∃ (fs : finset s) (w : s → k) (hw : ∑ i in fs, w i = x),
    v = fs.weighted_vsub_of_point (λ (i : s), p i) b w :=
begin
  classical,
  simp_rw weighted_vsub_of_point_apply,
  split,
  { rintros ⟨fs, hfs, w, rfl, rfl⟩,
    use [fs.subtype s, λ i, w i, sum_subtype_of_mem _ hfs, (sum_subtype_of_mem _ hfs).symm] },
  { rintros ⟨fs, w, rfl, rfl⟩,
    refine ⟨fs.map (function.embedding.subtype _), map_subtype_subset _,
         λ i, if h : i ∈ s then w ⟨i, h⟩ else 0, _, _⟩;
      simp }
end

variables (k)

/-- Suppose an indexed family of points is given, along with a subset
of the index type.  A vector can be expressed as `weighted_vsub` using
a `finset` lying within that subset and with sum of weights 0 if and
only if it can be expressed as `weighted_vsub` with sum of weights 0
for the corresponding indexed family whose index type is the subtype
corresponding to that subset. -/
lemma eq_weighted_vsub_subset_iff_eq_weighted_vsub_subtype {v : V} {s : set ι} {p : ι → P} :
  (∃ (fs : finset ι) (hfs : ↑fs ⊆ s) (w : ι → k) (hw : ∑ i in fs, w i = 0),
    v = fs.weighted_vsub p w) ↔
  ∃ (fs : finset s) (w : s → k) (hw : ∑ i in fs, w i = 0),
    v = fs.weighted_vsub (λ (i : s), p i) w :=
eq_weighted_vsub_of_point_subset_iff_eq_weighted_vsub_of_point_subtype

variables (V)

/-- Suppose an indexed family of points is given, along with a subset
of the index type.  A point can be expressed as an
`affine_combination` using a `finset` lying within that subset and
with sum of weights 1 if and only if it can be expressed an
`affine_combination` with sum of weights 1 for the corresponding
indexed family whose index type is the subtype corresponding to that
subset. -/
lemma eq_affine_combination_subset_iff_eq_affine_combination_subtype {p0 : P} {s : set ι}
    {p : ι → P} :
  (∃ (fs : finset ι) (hfs : ↑fs ⊆ s) (w : ι → k) (hw : ∑ i in fs, w i = 1),
    p0 = fs.affine_combination k p w) ↔
  ∃ (fs : finset s) (w : s → k) (hw : ∑ i in fs, w i = 1),
    p0 = fs.affine_combination k (λ (i : s), p i) w :=
begin
  simp_rw [affine_combination_apply, eq_vadd_iff_vsub_eq],
  exact eq_weighted_vsub_of_point_subset_iff_eq_weighted_vsub_of_point_subtype
end

variables {k V}

/-- Affine maps commute with affine combinations. -/
lemma map_affine_combination {V₂ P₂ : Type*} [add_comm_group V₂] [module k V₂] [affine_space V₂ P₂]
  (p : ι → P) (w : ι → k) (hw : s.sum w = 1) (f : P →ᵃ[k] P₂) :
  f (s.affine_combination k p w) = s.affine_combination k (f ∘ p) w :=
begin
  have b := classical.choice (infer_instance : affine_space V P).nonempty,
  have b₂ := classical.choice (infer_instance : affine_space V₂ P₂).nonempty,
  rw [s.affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one w p hw b,
      s.affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one w (f ∘ p) hw b₂,
      ← s.weighted_vsub_of_point_vadd_eq_of_sum_eq_one w (f ∘ p) hw (f b) b₂],
  simp only [weighted_vsub_of_point_apply, ring_hom.id_apply, affine_map.map_vadd,
    linear_map.map_smulₛₗ, affine_map.linear_map_vsub, linear_map.map_sum],
end

variables (k)

omit S

/-- Weights for expressing a single point as an affine combination. -/
def affine_combination_single_weights [decidable_eq ι] (i : ι) : ι → k :=
function.update (function.const ι 0) i 1

@[simp] lemma affine_combination_single_weights_apply_self [decidable_eq ι] (i : ι) :
  affine_combination_single_weights k i i = 1 :=
by simp [affine_combination_single_weights]

@[simp] lemma affine_combination_single_weights_apply_of_ne [decidable_eq ι] {i j : ι} (h : j ≠ i) :
  affine_combination_single_weights k i j = 0 :=
by simp [affine_combination_single_weights, h]

@[simp] lemma sum_affine_combination_single_weights [decidable_eq ι] {i : ι} (h : i ∈ s) :
  ∑ j in s, affine_combination_single_weights k i j = 1 :=
begin
  rw ←affine_combination_single_weights_apply_self k i,
  exact sum_eq_single_of_mem i h (λ j _ hj, affine_combination_single_weights_apply_of_ne k hj)
end

/-- Weights for expressing the subtraction of two points as a `weighted_vsub`. -/
def weighted_vsub_vsub_weights [decidable_eq ι] (i j : ι) : ι → k :=
affine_combination_single_weights k i - affine_combination_single_weights k j

@[simp] lemma weighted_vsub_vsub_weights_self [decidable_eq ι] (i : ι) :
  weighted_vsub_vsub_weights k i i = 0 :=
by simp [weighted_vsub_vsub_weights]

@[simp] lemma weighted_vsub_vsub_weights_apply_left [decidable_eq ι] {i j : ι} (h : i ≠ j) :
  weighted_vsub_vsub_weights k i j i = 1 :=
by simp [weighted_vsub_vsub_weights, h]

@[simp] lemma weighted_vsub_vsub_weights_apply_right [decidable_eq ι] {i j : ι} (h : i ≠ j) :
  weighted_vsub_vsub_weights k i j j = -1 :=
by simp [weighted_vsub_vsub_weights, h.symm]

@[simp] lemma weighted_vsub_vsub_weights_apply_of_ne [decidable_eq ι] {i j t : ι} (hi : t ≠ i)
  (hj : t ≠ j) : weighted_vsub_vsub_weights k i j t = 0 :=
by simp [weighted_vsub_vsub_weights, hi, hj]

@[simp] lemma sum_weighted_vsub_vsub_weights [decidable_eq ι] {i j : ι} (hi : i ∈ s) (hj : j ∈ s) :
  ∑ t in s, weighted_vsub_vsub_weights k i j t = 0 :=
begin
  simp_rw [weighted_vsub_vsub_weights, pi.sub_apply, sum_sub_distrib],
  simp [hi, hj]
end

variables {k}

/-- Weights for expressing `line_map` as an affine combination. -/
def affine_combination_line_map_weights [decidable_eq ι] (i j : ι) (c : k) : ι → k :=
c • weighted_vsub_vsub_weights k j i + affine_combination_single_weights k i

@[simp] lemma affine_combination_line_map_weights_self [decidable_eq ι] (i : ι) (c : k) :
  affine_combination_line_map_weights i i c = affine_combination_single_weights k i :=
by simp [affine_combination_line_map_weights]

@[simp] lemma affine_combination_line_map_weights_apply_left [decidable_eq ι] {i j : ι}
  (h : i ≠ j) (c : k) : affine_combination_line_map_weights i j c i = 1 - c :=
by simp [affine_combination_line_map_weights, h.symm, sub_eq_neg_add]

@[simp] lemma affine_combination_line_map_weights_apply_right [decidable_eq ι] {i j : ι}
  (h : i ≠ j) (c : k) : affine_combination_line_map_weights i j c j = c :=
by simp [affine_combination_line_map_weights, h.symm]

@[simp] lemma affine_combination_line_map_weights_apply_of_ne [decidable_eq ι] {i j t : ι}
  (hi : t ≠ i) (hj : t ≠ j) (c : k) : affine_combination_line_map_weights i j c t = 0 :=
by simp [affine_combination_line_map_weights, hi, hj]

@[simp] lemma sum_affine_combination_line_map_weights [decidable_eq ι] {i j : ι} (hi : i ∈ s)
  (hj : j ∈ s) (c : k) : ∑ t in s, affine_combination_line_map_weights i j c t = 1 :=
begin
  simp_rw [affine_combination_line_map_weights, pi.add_apply, sum_add_distrib],
  simp [hi, hj, ←mul_sum]
end

include S

variables (k)

/-- An affine combination with `affine_combination_single_weights` gives the specified point. -/
@[simp] lemma affine_combination_affine_combination_single_weights [decidable_eq ι] (p : ι → P)
  {i : ι} (hi : i ∈ s) : s.affine_combination k p (affine_combination_single_weights k i) = p i :=
begin
  refine s.affine_combination_of_eq_one_of_eq_zero _ _ hi (by simp) _,
  rintro j - hj,
  simp [hj]
end

/-- A weighted subtraction with `weighted_vsub_vsub_weights` gives the result of subtracting the
specified points. -/
@[simp] lemma weighted_vsub_weighted_vsub_vsub_weights [decidable_eq ι] (p : ι → P) {i j : ι}
  (hi : i ∈ s) (hj : j ∈ s) : s.weighted_vsub p (weighted_vsub_vsub_weights k i j) = p i -ᵥ p j :=
begin
  rw [weighted_vsub_vsub_weights, ←affine_combination_vsub,
      s.affine_combination_affine_combination_single_weights k p hi,
      s.affine_combination_affine_combination_single_weights k p hj]
end

variables {k}

/-- An affine combination with `affine_combination_line_map_weights` gives the result of
`line_map`. -/
@[simp] lemma affine_combination_affine_combination_line_map_weights [decidable_eq ι] (p : ι → P)
  {i j : ι} (hi : i ∈ s) (hj : j ∈ s) (c : k) :
  s.affine_combination k p (affine_combination_line_map_weights i j c) =
    affine_map.line_map (p i) (p j) c :=
by rw [affine_combination_line_map_weights, ←weighted_vsub_vadd_affine_combination,
       weighted_vsub_const_smul, s.affine_combination_affine_combination_single_weights k p hi,
       s.weighted_vsub_weighted_vsub_vsub_weights k p hj hi, affine_map.line_map_apply]

end finset

namespace finset

variables (k : Type*) {V : Type*} {P : Type*} [division_ring k] [add_comm_group V] [module k V]
variables [affine_space V P] {ι : Type*} (s : finset ι) {ι₂ : Type*} (s₂ : finset ι₂)

/-- The weights for the centroid of some points. -/
def centroid_weights : ι → k := function.const ι (card s : k) ⁻¹

/-- `centroid_weights` at any point. -/
@[simp] lemma centroid_weights_apply (i : ι) : s.centroid_weights k i = (card s : k) ⁻¹ :=
rfl

/-- `centroid_weights` equals a constant function. -/
lemma centroid_weights_eq_const :
  s.centroid_weights k = function.const ι ((card s : k) ⁻¹) :=
rfl

variables {k}

/-- The weights in the centroid sum to 1, if the number of points,
converted to `k`, is not zero. -/
lemma sum_centroid_weights_eq_one_of_cast_card_ne_zero (h : (card s : k) ≠ 0) :
  ∑ i in s, s.centroid_weights k i = 1 :=
by simp [h]

variables (k)

/-- In the characteristic zero case, the weights in the centroid sum
to 1 if the number of points is not zero. -/
lemma sum_centroid_weights_eq_one_of_card_ne_zero [char_zero k] (h : card s ≠ 0) :
  ∑ i in s, s.centroid_weights k i = 1 :=
by simp [h]

/-- In the characteristic zero case, the weights in the centroid sum
to 1 if the set is nonempty. -/
lemma sum_centroid_weights_eq_one_of_nonempty [char_zero k] (h : s.nonempty) :
  ∑ i in s, s.centroid_weights k i = 1 :=
s.sum_centroid_weights_eq_one_of_card_ne_zero k (ne_of_gt (card_pos.2 h))

/-- In the characteristic zero case, the weights in the centroid sum
to 1 if the number of points is `n + 1`. -/
lemma sum_centroid_weights_eq_one_of_card_eq_add_one [char_zero k] {n : ℕ}
  (h : card s = n + 1) : ∑ i in s, s.centroid_weights k i = 1 :=
s.sum_centroid_weights_eq_one_of_card_ne_zero k (h.symm ▸ nat.succ_ne_zero n)

include V

/-- The centroid of some points.  Although defined for any `s`, this
is intended to be used in the case where the number of points,
converted to `k`, is not zero. -/
def centroid (p : ι → P) : P :=
s.affine_combination k p (s.centroid_weights k)

/-- The definition of the centroid. -/
lemma centroid_def (p : ι → P) :
  s.centroid k p = s.affine_combination k p (s.centroid_weights k) :=
rfl

lemma centroid_univ (s : finset P) :
  univ.centroid k (coe : s → P) = s.centroid k id :=
by { rw [centroid, centroid, ← s.attach_affine_combination_coe], congr, ext, simp, }

/-- The centroid of a single point. -/
@[simp] lemma centroid_singleton (p : ι → P) (i : ι) :
  ({i} : finset ι).centroid k p = p i :=
by simp [centroid_def, affine_combination_apply]

/-- The centroid of two points, expressed directly as adding a vector
to a point. -/
lemma centroid_pair [decidable_eq ι] [invertible (2 : k)] (p : ι → P) (i₁ i₂ : ι) :
  ({i₁, i₂} : finset ι).centroid k p = (2 ⁻¹ : k) • (p i₂ -ᵥ p i₁) +ᵥ p i₁ :=
begin
  by_cases h : i₁ = i₂,
  { simp [h] },
  { have hc : (card ({i₁, i₂} : finset ι) : k) ≠ 0,
    { rw [card_insert_of_not_mem (not_mem_singleton.2 h), card_singleton],
      norm_num,
      exact nonzero_of_invertible _ },
    rw [centroid_def,
        affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one _ _ _
          (sum_centroid_weights_eq_one_of_cast_card_ne_zero _ hc) (p i₁)],
    simp [h] }
end

/-- The centroid of two points indexed by `fin 2`, expressed directly
as adding a vector to the first point. -/
lemma centroid_pair_fin [invertible (2 : k)] (p : fin 2 → P) :
  univ.centroid k p = (2 ⁻¹ : k) • (p 1 -ᵥ p 0) +ᵥ p 0 :=
begin
  rw univ_fin2,
  convert centroid_pair k p 0 1
end

/-- A centroid, over the image of an embedding, equals a centroid with
the same points and weights over the original `finset`. -/
lemma centroid_map (e : ι₂ ↪ ι) (p : ι → P) : (s₂.map e).centroid k p = s₂.centroid k (p ∘ e) :=
by simp [centroid_def, affine_combination_map, centroid_weights]

omit V

/-- `centroid_weights` gives the weights for the centroid as a
constant function, which is suitable when summing over the points
whose centroid is being taken.  This function gives the weights in a
form suitable for summing over a larger set of points, as an indicator
function that is zero outside the set whose centroid is being taken.
In the case of a `fintype`, the sum may be over `univ`. -/
def centroid_weights_indicator : ι → k := set.indicator ↑s (s.centroid_weights k)

/-- The definition of `centroid_weights_indicator`. -/
lemma centroid_weights_indicator_def :
  s.centroid_weights_indicator k = set.indicator ↑s (s.centroid_weights k) :=
rfl

/-- The sum of the weights for the centroid indexed by a `fintype`. -/
lemma sum_centroid_weights_indicator [fintype ι] :
  ∑ i, s.centroid_weights_indicator k i = ∑ i in s, s.centroid_weights k i :=
(set.sum_indicator_subset _ (subset_univ _)).symm

/-- In the characteristic zero case, the weights in the centroid
indexed by a `fintype` sum to 1 if the number of points is not
zero. -/
lemma sum_centroid_weights_indicator_eq_one_of_card_ne_zero [char_zero k] [fintype ι]
  (h : card s ≠ 0) : ∑ i, s.centroid_weights_indicator k i = 1 :=
begin
  rw sum_centroid_weights_indicator,
  exact s.sum_centroid_weights_eq_one_of_card_ne_zero k h
end

/-- In the characteristic zero case, the weights in the centroid
indexed by a `fintype` sum to 1 if the set is nonempty. -/
lemma sum_centroid_weights_indicator_eq_one_of_nonempty [char_zero k] [fintype ι]
  (h : s.nonempty) : ∑ i, s.centroid_weights_indicator k i = 1 :=
begin
  rw sum_centroid_weights_indicator,
  exact s.sum_centroid_weights_eq_one_of_nonempty k h
end

/-- In the characteristic zero case, the weights in the centroid
indexed by a `fintype` sum to 1 if the number of points is `n + 1`. -/
lemma sum_centroid_weights_indicator_eq_one_of_card_eq_add_one [char_zero k] [fintype ι] {n : ℕ}
  (h : card s = n + 1) : ∑ i, s.centroid_weights_indicator k i = 1 :=
begin
  rw sum_centroid_weights_indicator,
  exact s.sum_centroid_weights_eq_one_of_card_eq_add_one k h
end

include V

/-- The centroid as an affine combination over a `fintype`. -/
lemma centroid_eq_affine_combination_fintype [fintype ι] (p : ι → P) :
  s.centroid k p = univ.affine_combination k p (s.centroid_weights_indicator k) :=
affine_combination_indicator_subset _ _ (subset_univ _)

/-- An indexed family of points that is injective on the given
`finset` has the same centroid as the image of that `finset`.  This is
stated in terms of a set equal to the image to provide control of
definitional equality for the index type used for the centroid of the
image. -/
lemma centroid_eq_centroid_image_of_inj_on {p : ι → P} (hi : ∀ i j ∈ s, p i = p j → i = j)
  {ps : set P} [fintype ps] (hps : ps = p '' ↑s) :
  s.centroid k p = (univ : finset ps).centroid k (λ x, x) :=
begin
  let f : p '' ↑s → ι := λ x, x.property.some,
  have hf : ∀ x, f x ∈ s ∧ p (f x) = x := λ x, x.property.some_spec,
  let f' : ps → ι := λ x, f ⟨x, hps ▸ x.property⟩,
  have hf' : ∀ x, f' x ∈ s ∧ p (f' x) = x := λ x, hf ⟨x, hps ▸ x.property⟩,
  have hf'i : function.injective f',
  { intros x y h,
    rw [subtype.ext_iff, ←(hf' x).2, ←(hf' y).2, h] },
  let f'e : ps ↪ ι := ⟨f', hf'i⟩,
  have hu : finset.univ.map f'e = s,
  { ext x,
    rw mem_map,
    split,
    { rintros ⟨i, _, rfl⟩,
      exact (hf' i).1 },
    { intro hx,
      use [⟨p x, hps.symm ▸ set.mem_image_of_mem _ hx⟩, mem_univ _],
      refine hi _ (hf' _).1 _ hx _,
      rw (hf' _).2,
      refl } },
  rw [←hu, centroid_map],
  congr' with x,
  change p (f' x) = ↑x,
  rw (hf' x).2
end

/-- Two indexed families of points that are injective on the given
`finset`s and with the same points in the image of those `finset`s
have the same centroid. -/
lemma centroid_eq_of_inj_on_of_image_eq {p : ι → P} (hi : ∀ i j ∈ s, p i = p j → i = j)
  {p₂ : ι₂ → P} (hi₂ : ∀ i j ∈ s₂, p₂ i = p₂ j → i = j) (he : p '' ↑s = p₂ '' ↑s₂) :
  s.centroid k p = s₂.centroid k p₂ :=
by classical; rw [s.centroid_eq_centroid_image_of_inj_on k hi rfl,
                  s₂.centroid_eq_centroid_image_of_inj_on k hi₂ he]

end finset

section affine_space'

variables {k : Type*} {V : Type*} {P : Type*} [ring k] [add_comm_group V] [module k V]
          [affine_space V P]
variables {ι : Type*}
include V

/-- A `weighted_vsub` with sum of weights 0 is in the `vector_span` of
an indexed family. -/
lemma weighted_vsub_mem_vector_span {s : finset ι} {w : ι → k}
    (h : ∑ i in s, w i = 0) (p : ι → P) :
    s.weighted_vsub p w ∈ vector_span k (set.range p) :=
begin
  classical,
  rcases is_empty_or_nonempty ι with hι|⟨⟨i0⟩⟩,
  { resetI, simp [finset.eq_empty_of_is_empty s] },
  { rw [vector_span_range_eq_span_range_vsub_right k p i0, ←set.image_univ,
        finsupp.mem_span_image_iff_total,
        finset.weighted_vsub_eq_weighted_vsub_of_point_of_sum_eq_zero s w p h (p i0),
        finset.weighted_vsub_of_point_apply],
    let w' := set.indicator ↑s w,
    have hwx : ∀ i, w' i ≠ 0 → i ∈ s := λ i, set.mem_of_indicator_ne_zero,
    use [finsupp.on_finset s w' hwx, set.subset_univ _],
    rw [finsupp.total_apply, finsupp.on_finset_sum hwx],
    { apply finset.sum_congr rfl,
      intros i hi,
      simp [w', set.indicator_apply, if_pos hi] },
    { exact λ _, zero_smul k _ } },
end

/-- An `affine_combination` with sum of weights 1 is in the
`affine_span` of an indexed family, if the underlying ring is
nontrivial. -/
lemma affine_combination_mem_affine_span [nontrivial k] {s : finset ι} {w : ι → k}
    (h : ∑ i in s, w i = 1) (p : ι → P) :
  s.affine_combination k p w ∈ affine_span k (set.range p) :=
begin
  classical,
  have hnz : ∑ i in s, w i ≠ 0 := h.symm ▸ one_ne_zero,
  have hn : s.nonempty := finset.nonempty_of_sum_ne_zero hnz,
  cases hn with i1 hi1,
  let w1 : ι → k := function.update (function.const ι 0) i1 1,
  have hw1 : ∑ i in s, w1 i = 1,
  { rw [finset.sum_update_of_mem hi1, finset.sum_const_zero, add_zero] },
  have hw1s : s.affine_combination k p w1 = p i1 :=
    s.affine_combination_of_eq_one_of_eq_zero w1 p hi1 (function.update_same _ _ _)
                                              (λ _ _ hne, function.update_noteq hne _ _),
  have hv : s.affine_combination k p w -ᵥ p i1 ∈ (affine_span k (set.range p)).direction,
  { rw [direction_affine_span, ←hw1s, finset.affine_combination_vsub],
    apply weighted_vsub_mem_vector_span,
    simp [pi.sub_apply, h, hw1] },
  rw ←vsub_vadd (s.affine_combination k p w) (p i1),
  exact affine_subspace.vadd_mem_of_mem_direction hv (mem_affine_span k (set.mem_range_self _))
end

variables (k) {V}

/-- A vector is in the `vector_span` of an indexed family if and only
if it is a `weighted_vsub` with sum of weights 0. -/
lemma mem_vector_span_iff_eq_weighted_vsub {v : V} {p : ι → P} :
  v ∈ vector_span k (set.range p) ↔
    ∃ (s : finset ι) (w : ι → k) (h : ∑ i in s, w i = 0), v = s.weighted_vsub p w :=
begin
  classical,
  split,
  { rcases is_empty_or_nonempty ι with hι|⟨⟨i0⟩⟩, swap,
    { rw [vector_span_range_eq_span_range_vsub_right k p i0, ←set.image_univ,
          finsupp.mem_span_image_iff_total],
      rintros ⟨l, hl, hv⟩,
      use insert i0 l.support,
      set w := (l : ι → k) -
        function.update (function.const ι 0 : ι → k) i0 (∑ i in l.support, l i) with hwdef,
      use w,
      have hw : ∑ i in insert i0 l.support, w i = 0,
      { rw hwdef,
        simp_rw [pi.sub_apply, finset.sum_sub_distrib,
                 finset.sum_update_of_mem (finset.mem_insert_self _ _), finset.sum_const_zero,
                 finset.sum_insert_of_eq_zero_if_not_mem finsupp.not_mem_support_iff.1,
                 add_zero, sub_self] },
      use hw,
      have hz : w i0 • (p i0 -ᵥ p i0 : V) = 0 := (vsub_self (p i0)).symm ▸ smul_zero _,
      change (λ i, w i • (p i -ᵥ p i0 : V)) i0 = 0 at hz,
      rw [finset.weighted_vsub_eq_weighted_vsub_of_point_of_sum_eq_zero _ w p hw (p i0),
          finset.weighted_vsub_of_point_apply, ←hv, finsupp.total_apply,
          finset.sum_insert_zero hz],
      change ∑ i in l.support, l i • _ = _,
      congr' with i,
      by_cases h : i = i0,
      { simp [h] },
      { simp [hwdef, h] } },
    { resetI,
      rw [set.range_eq_empty, vector_span_empty, submodule.mem_bot],
      rintro rfl,
      use [∅],
      simp } },
  { rintros ⟨s, w, hw, rfl⟩,
    exact weighted_vsub_mem_vector_span hw p }
end

variables {k}

/-- A point in the `affine_span` of an indexed family is an
`affine_combination` with sum of weights 1. See also
`eq_affine_combination_of_mem_affine_span_of_fintype`. -/
lemma eq_affine_combination_of_mem_affine_span {p1 : P} {p : ι → P}
    (h : p1 ∈ affine_span k (set.range p)) :
  ∃ (s : finset ι) (w : ι → k) (hw : ∑ i in s, w i = 1), p1 = s.affine_combination k p w :=
begin
  classical,
  have hn : ((affine_span k (set.range p)) : set P).nonempty := ⟨p1, h⟩,
  rw [affine_span_nonempty, set.range_nonempty_iff_nonempty] at hn,
  cases hn with i0,
  have h0 : p i0 ∈ affine_span k (set.range p) := mem_affine_span k (set.mem_range_self i0),
  have hd : p1 -ᵥ p i0 ∈ (affine_span k (set.range p)).direction :=
    affine_subspace.vsub_mem_direction h h0,
  rw [direction_affine_span, mem_vector_span_iff_eq_weighted_vsub] at hd,
  rcases hd with ⟨s, w, h, hs⟩,
  let s' := insert i0 s,
  let w' := set.indicator ↑s w,
  have h' : ∑ i in s', w' i = 0,
  { rw [←h, set.sum_indicator_subset _ (finset.subset_insert i0 s)] },
  have hs' : s'.weighted_vsub p w' = p1 -ᵥ p i0,
  { rw hs,
    exact (finset.weighted_vsub_indicator_subset _ _ (finset.subset_insert i0 s)).symm },
  let w0 : ι → k := function.update (function.const ι 0) i0 1,
  have hw0 : ∑ i in s', w0 i = 1,
  { rw [finset.sum_update_of_mem (finset.mem_insert_self _ _), finset.sum_const_zero, add_zero] },
  have hw0s : s'.affine_combination k p w0 = p i0 :=
    s'.affine_combination_of_eq_one_of_eq_zero w0 p
                                               (finset.mem_insert_self _ _)
                                               (function.update_same _ _ _)
                                               (λ _ _ hne, function.update_noteq hne _ _),
  use [s', w0 + w'],
  split,
  { simp [pi.add_apply, finset.sum_add_distrib, hw0, h'] },
  { rw [add_comm, ←finset.weighted_vsub_vadd_affine_combination, hw0s, hs', vsub_vadd] }
end

lemma eq_affine_combination_of_mem_affine_span_of_fintype [fintype ι] {p1 : P} {p : ι → P}
  (h : p1 ∈ affine_span k (set.range p)) :
  ∃ (w : ι → k) (hw : ∑ i, w i = 1), p1 = finset.univ.affine_combination k p w :=
begin
  classical,
  obtain ⟨s, w, hw, rfl⟩ := eq_affine_combination_of_mem_affine_span h,
  refine ⟨(s : set ι).indicator w, _, finset.affine_combination_indicator_subset w p s.subset_univ⟩,
  simp only [finset.mem_coe, set.indicator_apply, ← hw],
  rw fintype.sum_extend_by_zero s w,
end

variables (k V)

/-- A point is in the `affine_span` of an indexed family if and only
if it is an `affine_combination` with sum of weights 1, provided the
underlying ring is nontrivial. -/
lemma mem_affine_span_iff_eq_affine_combination [nontrivial k] {p1 : P} {p : ι → P} :
  p1 ∈ affine_span k (set.range p) ↔
    ∃ (s : finset ι) (w : ι → k) (hw : ∑ i in s, w i = 1), p1 = s.affine_combination k p w :=
begin
  split,
  { exact eq_affine_combination_of_mem_affine_span },
  { rintros ⟨s, w, hw, rfl⟩,
    exact affine_combination_mem_affine_span hw p }
end

/-- Given a family of points together with a chosen base point in that family, membership of the
affine span of this family corresponds to an identity in terms of `weighted_vsub_of_point`, with
weights that are not required to sum to 1. -/
lemma mem_affine_span_iff_eq_weighted_vsub_of_point_vadd
  [nontrivial k] (p : ι → P) (j : ι) (q : P) :
  q ∈ affine_span k (set.range p) ↔
  ∃ (s : finset ι) (w : ι → k), q = s.weighted_vsub_of_point p (p j) w +ᵥ (p j) :=
begin
  split,
  { intros hq,
    obtain ⟨s, w, hw, rfl⟩ := eq_affine_combination_of_mem_affine_span hq,
    exact ⟨s, w, s.affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one w p hw (p j)⟩, },
  { rintros ⟨s, w, rfl⟩,
    classical,
    let w' : ι → k := function.update w j (1 - (s \ {j}).sum w),
    have h₁ : (insert j s).sum w' = 1,
    { by_cases hj : j ∈ s,
      { simp [finset.sum_update_of_mem hj, finset.insert_eq_of_mem hj], },
      { simp [w', finset.sum_insert hj, finset.sum_update_of_not_mem hj, hj], }, },
    have hww : ∀ i, i ≠ j → w i = w' i, { intros i hij, simp [w', hij], },
    rw [s.weighted_vsub_of_point_eq_of_weights_eq p j w w' hww,
      ← s.weighted_vsub_of_point_insert w' p j,
      ← (insert j s).affine_combination_eq_weighted_vsub_of_point_vadd_of_sum_eq_one w' p h₁ (p j)],
    exact affine_combination_mem_affine_span h₁ p, },
end

variables {k V}

/-- Given a set of points, together with a chosen base point in this set, if we affinely transport
all other members of the set along the line joining them to this base point, the affine span is
unchanged. -/
lemma affine_span_eq_affine_span_line_map_units [nontrivial k]
  {s : set P} {p : P} (hp : p ∈ s) (w : s → units k) :
  affine_span k (set.range (λ (q : s), affine_map.line_map p ↑q (w q : k))) = affine_span k s :=
begin
  have : s = set.range (coe : s → P), { simp, },
  conv_rhs { rw this, },
  apply le_antisymm;
  intros q hq;
  erw mem_affine_span_iff_eq_weighted_vsub_of_point_vadd k V _ (⟨p, hp⟩ : s) q at hq ⊢;
  obtain ⟨t, μ, rfl⟩ := hq;
  use t;
  [use λ x, (μ x) * ↑(w x), use λ x, (μ x) * ↑(w x)⁻¹];
  simp [smul_smul],
end

end affine_space'

section division_ring

variables {k : Type*} {V : Type*} {P : Type*} [division_ring k] [add_comm_group V] [module k V]
variables [affine_space V P] {ι : Type*}
include V

open set finset

/-- The centroid lies in the affine span if the number of points,
converted to `k`, is not zero. -/
lemma centroid_mem_affine_span_of_cast_card_ne_zero {s : finset ι} (p : ι → P)
  (h : (card s : k) ≠ 0) : s.centroid k p ∈ affine_span k (range p) :=
affine_combination_mem_affine_span (s.sum_centroid_weights_eq_one_of_cast_card_ne_zero h) p

variables (k)

/-- In the characteristic zero case, the centroid lies in the affine
span if the number of points is not zero. -/
lemma centroid_mem_affine_span_of_card_ne_zero [char_zero k] {s : finset ι} (p : ι → P)
  (h : card s ≠ 0) : s.centroid k p ∈ affine_span k (range p) :=
affine_combination_mem_affine_span (s.sum_centroid_weights_eq_one_of_card_ne_zero k h) p

/-- In the characteristic zero case, the centroid lies in the affine
span if the set is nonempty. -/
lemma centroid_mem_affine_span_of_nonempty [char_zero k] {s : finset ι} (p : ι → P)
  (h : s.nonempty) : s.centroid k p ∈ affine_span k (range p) :=
affine_combination_mem_affine_span (s.sum_centroid_weights_eq_one_of_nonempty k h) p

/-- In the characteristic zero case, the centroid lies in the affine
span if the number of points is `n + 1`. -/
lemma centroid_mem_affine_span_of_card_eq_add_one [char_zero k] {s : finset ι} (p : ι → P)
  {n : ℕ} (h : card s = n + 1) : s.centroid k p ∈ affine_span k (range p) :=
affine_combination_mem_affine_span (s.sum_centroid_weights_eq_one_of_card_eq_add_one k h) p

end division_ring

namespace affine_map
variables {k : Type*} {V : Type*} (P : Type*) [comm_ring k] [add_comm_group V] [module k V]
variables [affine_space V P] {ι : Type*} (s : finset ι)
include V

-- TODO: define `affine_map.proj`, `affine_map.fst`, `affine_map.snd`
/-- A weighted sum, as an affine map on the points involved. -/
def weighted_vsub_of_point (w : ι → k) : ((ι → P) × P) →ᵃ[k] V :=
{ to_fun := λ p, s.weighted_vsub_of_point p.fst p.snd w,
  linear := ∑ i in s,
    w i • ((linear_map.proj i).comp (linear_map.fst _ _ _) - linear_map.snd _ _ _),
  map_vadd' := begin
    rintros ⟨p, b⟩ ⟨v, b'⟩,
    simp [linear_map.sum_apply, finset.weighted_vsub_of_point, vsub_vadd_eq_vsub_sub,
          vadd_vsub_assoc, add_sub, ← sub_add_eq_add_sub, smul_add, finset.sum_add_distrib]
  end }

end affine_map


import data.real.basic
import tactic.linarith.frontend

section
universe u
variables {α : Type u} [ordered_add_comm_monoid α]

lemma nonneg_of_nonneg_le_smul {a b : α} {n : ℕ} :
  0 < a → a ≤ n • b → 0 < n :=
begin
  intros ha h,
  have hnb : 0 < n • b, by exact lt_of_lt_of_le ha h,
  have hn0 : 0 = n ∨ 0 < n, by exact eq_or_lt_of_le (zero_le n),
  cases hn0 with n0 n1,
    exfalso,
    rw [← n0, zero_smul] at hnb,
    exact (lt_irrefl 0) hnb,
  exact n1,
end

lemma nonneg_of_nonneg_lt_smul {a b : α} {n : ℕ} :
  0 < a → a < n • b → 0 < n :=
begin
  intros ha h,
  have h_le : a ≤ n • b, by exact le_of_lt h,
  exact nonneg_of_nonneg_le_smul ha h_le,
end

lemma arch_gt [archimedean α] [covariant_class α α (+) (<)] :
  ∀ (x : α) {y}, 0 < y → ∃ n : ℕ, x < n • y :=
begin
  introv ypos,
  obtain ⟨m, hm⟩ := archimedean.arch x ypos,
  use m + 1,
  rw [add_smul, one_smul],
  -- this should be easier...
  have hmy1 : m • y + 0 < m • y + y,
    apply (add_lt_add_left ypos (m • y)),
  simp at hmy1,
  exact lt_of_le_of_lt hm hmy1,
end

end section

lemma nat_pred_lt_of_pos { n : ℕ } (h : 0 < n) : nat.pred n < n :=
begin
  apply nat.pred_lt,
  intro h_n0,
  rw h_n0 at h,
  apply lt_irrefl 0,
  exact h,
end

lemma sub_one_lt_of_pos { n : ℕ } (h : 0 < n) : n - 1 < n :=
begin
  rw nat.sub_one n,
  exact nat_pred_lt_of_pos h,
end

lemma inv_nat_lt_pos_real (x : ℝ) (xpos : 0 < x) :
  ∃ n : ℕ, 0 < n ∧ 1 / (n : ℝ) < x :=
begin
  -- by archimedean property of ℝ, there is some natural number n
  -- with n * x > 1, so 1 / n < x.
  obtain ⟨n, hn⟩ := arch_gt (1 : ℝ) xpos,
  have hn_gt_zero : 0 < n,
    exact nonneg_of_nonneg_lt_smul (@zero_lt_one ℝ _ _) hn,
  have hn_inv_lt_x : 1 / (n : ℝ) < x,
    simp at hn,
    have hn_gt_zero' : 0 < (n : ℝ),
      exact (@nat.cast_pos ℝ _ _ n).2 hn_gt_zero,
    apply (div_lt_iff' hn_gt_zero').2,
    assumption,
  use n,
  exact ⟨hn_gt_zero, hn_inv_lt_x⟩,
end

lemma rational_between_nonneg_reals (x y : ℝ) (h : x < y) (h_xnonneg: 0 ≤ x):
  ∃ r : ℚ, (x < r) ∧ (↑r < y) :=
begin
  -- By archimedean property of ℝ, there is some (positive)
  -- natural number n with n(y-x) > 1, so 1/n < y-x.
  have h_yx_positive : 0 < y - x, by simpa,
  cases (inv_nat_lt_pos_real (y - x) h_yx_positive) with n hn,

  -- Next, consider K = { k ∈ ℕ : k / n > x }.  By the archimedean
  -- property again, this set is non-empty.
  let p := λ k : ℕ, x < (k : ℝ) * (n : ℝ)⁻¹,
  let K := { k : ℕ // p k },
  have h_1n_positive : 0 < 1 / (n : ℝ), by { simp, exact hn.left },
  obtain ⟨n0, hn0⟩ := arch_gt x h_1n_positive,
  simp at hn0,
  have hp : ∃ (m : ℕ), p m,
    use n0,
    exact hn0,
  -- Since K is non-empty, we can take its smallest element, k₀.
  -- have k0_x := nat.find_x hp,
  let k0 := nat.find hp, -- k0_x.val,
  -- there must be a better way to do the next two lines; surely
  -- we shouldn't need an explicit lemma to substitute for k0?
  have k0_def : k0 = nat.find hp,
    simp,
  have k0_spec := nat.find_spec hp,
  -- k₀/n is the rational we want
  use (k0 : ℚ) * (n : ℚ)⁻¹,

  split,
    -- the first main goal, x < k₀ / n, is true because k₀ satisfies p
    simp,
    rw k0_def,
    exact k0_spec,

  -- We show that k₀ / n < y by contradiction.  Assume that k₀ / n ≥ y.
  -- Then (k₀-1) / n ≥ y - 1/n > y - (y-x) = x, contradicting the
  -- minimality of k₀.

  -- We must first show that k₀ > 0, so that k₀ - 1 ∈ ℕ.
  simp,
  have k0_positive : k0 > 0,
    apply nat.pos_of_ne_zero,
    intro k0_zero,
    rw k0_def at k0_zero,
    rw k0_zero at k0_spec,
    have not_p_0 : ¬ p 0,
      have not_k_0 : ¬ x < 0 * (↑n)⁻¹,
        simpa,
      exact not_k_0,
    simpa,

  -- The minimality of k₀ shows that k₀ - 1 does not satisfy
  -- the condition.
  have k_1_lt_k : k0 - 1 < k0,
    exact sub_one_lt_of_pos k0_positive,
  have x_lt_k0_1 : ¬ x < ↑(k0 - 1) * (↑n)⁻¹,
    exact nat.find_min hp k_1_lt_k,

  by_contradiction h_y_le_k0_div_n,
  simp at h_y_le_k0_div_n,
  -- We have now assumed that k₀/n ≥ y.  The rest seems more
  -- painful than it perhaps should be?
  apply x_lt_k0_1,
  have h_y_sub_1n : y - (n : ℝ)⁻¹ ≤ ((k0 : ℝ) - 1) * (n : ℝ)⁻¹,
    rw [sub_mul, one_mul],
    simpa,
  have x_lt_y_sub_1n : x < y - (n : ℝ)⁻¹,
    have n1_lt_y_sub_x : (n : ℝ)⁻¹ < y - x,
      rw ← one_div,
      exact hn.right,
    linarith,
  norm_cast at h_y_sub_1n,
  exact lt_of_lt_of_le x_lt_y_sub_1n h_y_sub_1n,
end

lemma rational_between_reals (x y : ℝ) (h : x < y): ∃ r : ℚ, (x < r) ∧ (↑r < y) :=
begin
  have h_x_le_or_lt : 0 ≤ x ∨ x < 0,
    exact le_or_lt _ _ ,
  cases h_x_le_or_lt,
    -- 0 ≤ x < y is our previous result
    exact rational_between_nonneg_reals x y h h_x_le_or_lt,

  have h_y_le_or_lt : y ≤ 0 ∨ 0 < y,
    exact le_or_lt _ _ ,
  cases h_y_le_or_lt,
    -- x < y ≤ 0, so 0 ≤ -y < -x and we can apply the previous result
    -- to -y and -x
    have h' : -y < -x,
      exact neg_lt_neg h,
    have h_negy_nonneg: 0 ≤ -y,
      exact neg_nonneg_of_nonpos h_y_le_or_lt,
    cases rational_between_nonneg_reals (-y) (-x) h' h_negy_nonneg with rneg hrneg,
      use -rneg,
    simp,
    split,
      rw ← neg_neg x,
      exact neg_lt_neg hrneg.right,
      rw ← neg_neg y,
      exact neg_lt_neg hrneg.left,

  -- x < 0 < y, so we can just use 0
  use 0,
  split,
  repeat { simpa },
end

end


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import analysis.analytic.basic
import analysis.special_functions.pow

/-!
# Representation of `formal_multilinear_series.radius` as a `liminf`

In this file we prove that the radius of convergence of a `formal_multilinear_series` is equal to
$\liminf_{n\to\infty} \frac{1}{\sqrt[n]{‖p n‖}}$. This lemma can't go to `basic.lean` because this
would create a circular dependency once we redefine `exp` using `formal_multilinear_series`.
-/
variables {𝕜 : Type*} [nontrivially_normed_field 𝕜]
{E : Type*} [normed_add_comm_group E] [normed_space 𝕜 E]
{F : Type*} [normed_add_comm_group F] [normed_space 𝕜 F]

open_locale topology classical big_operators nnreal ennreal
open filter asymptotics

namespace formal_multilinear_series

variables (p : formal_multilinear_series 𝕜 E F)

/-- The radius of a formal multilinear series is equal to
$\liminf_{n\to\infty} \frac{1}{\sqrt[n]{‖p n‖}}$. The actual statement uses `ℝ≥0` and some
coercions. -/
lemma radius_eq_liminf : p.radius = liminf (λ n, 1/((‖p n‖₊) ^ (1 / (n : ℝ)) : ℝ≥0)) at_top :=
begin
  have : ∀ (r : ℝ≥0) {n : ℕ}, 0 < n →
    ((r : ℝ≥0∞) ≤ 1 / ↑(‖p n‖₊ ^ (1 / (n : ℝ))) ↔ ‖p n‖₊ * r ^ n ≤ 1),
  { intros r n hn,
    have : 0 < (n : ℝ) := nat.cast_pos.2 hn,
    conv_lhs {rw [one_div, ennreal.le_inv_iff_mul_le, ← ennreal.coe_mul,
      ennreal.coe_le_one_iff, one_div, ← nnreal.rpow_one r, ← mul_inv_cancel this.ne',
      nnreal.rpow_mul, ← nnreal.mul_rpow, ← nnreal.one_rpow (n⁻¹),
      nnreal.rpow_le_rpow_iff (inv_pos.2 this), mul_comm, nnreal.rpow_nat_cast] } },
  apply le_antisymm; refine ennreal.le_of_forall_nnreal_lt (λ r hr, _),
  { rcases ((tfae_exists_lt_is_o_pow (λ n, ‖p n‖ * r ^ n) 1).out 1 7).1 (p.is_o_of_lt_radius hr)
      with ⟨a, ha, H⟩,
    refine le_Liminf_of_le (by apply_auto_param) (eventually_map.2 $ _),
    refine H.mp ((eventually_gt_at_top 0).mono $ λ n hn₀ hn, (this _ hn₀).2
      (nnreal.coe_le_coe.1 _)),
    push_cast,
    exact (le_abs_self _).trans (hn.trans (pow_le_one _ ha.1.le ha.2.le)) },
  { refine p.le_radius_of_is_O (is_O.of_bound 1 _),
    refine (eventually_lt_of_lt_liminf hr).mp ((eventually_gt_at_top 0).mono (λ n hn₀ hn, _)),
    simpa using nnreal.coe_le_coe.2 ((this _ hn₀).1 hn.le) }
end

end formal_multilinear_series