import prelim.set finsum.finsum algebra.ring.basic prelim.intervals

open_locale big_operators classical

universes u v w

section general

-- lemmas in this section maybe belong in the mathlib api

variables {α β : Type*} {M: Type*} [add_comm_monoid M] {f g : α → M} {s t : set α} {x y : α}

lemma finsum_eq_zero_of_infinite
  (hs : ¬ (function.support f).finite) : ∑ᶠ i, f i = 0 :=
by {rw [finsum_eq_finsum_in_univ, finsum_in_eq_zero_of_infinite], rwa set.univ_inter}

lemma finsupp_of_finsum_nonzero (h : ∑ᶠ i, f i ≠ 0) :
  (function.support f).finite :=
by_contra (λ hn, by {rw finsum_eq_zero_of_infinite hn at h, exact h rfl, })

lemma finsupp_of_finsum_in_nonzero (h : ∑ᶠ i in s, f i ≠ 0) :
  (s ∩ function.support f).finite :=
by_contra (λ hn, by {rw finsum_in_eq_zero_of_infinite hn at h, exact h rfl, })

@[simp] lemma finsum_empty_set_type:
  ∑ᶠ (i : ((∅ : set α) : Type*)), f i = 0 :=
begin
  rw [finsum_eq_finsum_in_univ, set.univ_eq_empty_iff.mpr, finsum_in_empty],
  by_contra, obtain ⟨x,hx⟩ := h,
  rwa set.mem_empty_eq at hx,
end

@[simp] lemma finsum_in_zero:
  ∑ᶠ i in s, (0 : M) = 0 :=
by {rw finsum_in_inter_support, convert finsum_in_empty, simp}

@[simp] lemma finsum_zero:
  ∑ᶠ (i : α), (0 : M) = 0 :=
by rw [finsum_eq_finsum_in_univ, finsum_in_zero]

@[simp] lemma finsum_fin_zero {f : fin 0 → M} :
  ∑ᶠ (i : fin 0), f i = 0 :=
by {convert finsum_zero}

@[simp] lemma finsum_fin_one {f : fin 1 → M} :
  ∑ᶠ (i : fin 1), f i = f (0 : fin 1) :=
by {convert finsum_singleton, simp,}

lemma finsum_in_eq_of_eq (h : ∀ i ∈ s, f i = g i) :
  ∑ᶠ i in s, f i = ∑ᶠ i in s, g i :=
by {apply @finsum_in_eq_of_bij_on α M _ α s s f g id, tidy}

lemma finsum_in_eq_zero_of_eq_zero (hs : ∀ x ∈ s, f x = 0):
  ∑ᶠ i in s, f i = 0 :=
by {convert finsum_in_eq_of_eq hs, rw finsum_in_zero}

lemma finsum_eq_of_eq (h : ∀ i, f i = g i) :
  ∑ᶠ i, f i = ∑ᶠ i, g i :=
by {repeat {rw finsum_eq_finsum_in_univ}, exact finsum_in_eq_of_eq (λ i _, h i)}

lemma finsum_in_eq_finset_sum_filter_union [add_comm_monoid M]
(hf: (s ∩ function.support f).finite) (hg: (s ∩ function.support g).finite) :
  ∑ᶠ (i : α) in s, f i = (finset.filter s (hf.to_finset ∪ hg.to_finset)).sum f :=
begin
  rw finsum_in_eq_finset_sum f hf,
  simp only,
  have h : hf.to_finset ⊆ finset.filter s (hf.to_finset ∪ hg.to_finset),
  { intro x, simp only [and_imp, set.mem_inter_eq, set.finite.mem_to_finset, ne.def,
      finset.mem_union, finset.mem_filter, function.mem_support],
    tauto,  },
  rw finset.sum_subset h,
  intros x hx hx',
  rw [set.finite.mem_to_finset, set.mem_inter_iff, not_and, function.mem_support, not_not] at hx',
  exact hx' (finset.mem_filter.mp hx).2,
end

lemma finsum_in_eq_finsum_in_subset
(h : s ⊆ t) (hf : ∀ x, x ∈ t → x ∉ s → f x = 0) :
  ∑ᶠ x in s, f x = ∑ᶠ x in t, f x :=
begin
  by_cases ht : (t ∩ function.support f).finite,
  { have hs : (s ∩ function.support f).finite, from
      set.finite.subset ht (set.inter_subset_inter_left _ h),
    rw [finsum_in_eq_finset_sum _ hs, finsum_in_eq_finset_sum _ ht],
    refine finset.sum_subset (λ x hxs, _) (λ x hxt hxs, _),
    { simp only [set.mem_inter_eq, set.finite.mem_to_finset, function.mem_support] at hxs ⊢,
      tauto, },
    rw [set.finite.mem_to_finset] at hxs, simp at hxs hxt, tauto},
  have hs : (s ∩ function.support f).infinite,
  { refine set.infinite_mono (λ x hx, _) ht,
    rw [set.mem_inter_iff, function.mem_support] at hx ⊢,
    exact ⟨by_contra (λ hn, hx.2 (hf _ hx.1 hn)),λ hn, hx.2 hn⟩},
  rw [finsum_in_eq_zero_of_infinite hs, finsum_in_eq_zero_of_infinite ht],
end

lemma finsum_eq_finsum_in_subset
(hf : ∀ x, x ∉ s → f x = 0) :
  ∑ᶠ x in s, f x = ∑ᶠ x, f x :=
begin
  rw finsum_eq_finsum_in_univ,
  exact finsum_in_eq_finsum_in_subset (set.subset_univ _) (λ x _ hx, hf x hx),
end

lemma finsum_ite_singleton_eq (a : α):
  ∑ᶠ (x : α), ite (x = a) (f x) 0 = f a :=
by convert @finsum_singleton _ _ _ f a

@[simp] lemma finsum_subtype_eq_finsum_in (f : α → M) (s : set α) :
 ∑ᶠ (x : s), f x = ∑ᶠ x in s, f x  :=
begin
  rw finsum_eq_finsum_in_univ,
  convert (finsum_in_eq_of_bij_on subtype.val _ (λ _ _, rfl)),
  convert set.inj_on.bij_on_image (set.inj_on_of_injective (subtype.val_injective) _),
  rw [set.image_univ, subtype.range_val],
end

lemma finsum_subtype_eq_finsum_in_set_of (f : α → M)(P : α → Prop) :
  ∑ᶠ (i : {x // P x}), f i = ∑ᶠ i in {x | P x}, f i :=
finsum_subtype_eq_finsum_in f _

lemma finsum_set_subtype_eq_finsum_set (f : α → M) (P Q : α → Prop) :
  ∑ᶠ (x : {y // P y}) in {x : {y // P y} | Q (coe x)}, f x = ∑ᶠ x in { x | P x ∧ Q x }, f x :=
by {convert (finsum_in_image _).symm, tidy,}

/-- A fixed version of `finsum_in_bUnion` in which `t` is a finite set of `set α`s. -/
lemma finsum_in_sUnion' (f : α → M) {t : set (set α)} (ht₀ : t.finite)
  (ht₁ : ∀ x ∈ t, set.finite x) (h : set.pairwise_disjoint t):
  ∑ᶠ i in (⋃₀ t), f i = ∑ᶠ s in t, (∑ᶠ i in s, f i) :=
begin
  rw [set.sUnion_eq_bUnion],
  have h' := @finsum_in_bUnion _ _ _ _ f _ _
    (set.univ_finite_iff_nonempty_fintype.mpr ⟨ht₀.fintype⟩)
    (λ s, ht₁ s.1 s.2)
    (λ x hx y hy hxy, h x.1 x.2 y.1 y.2 (λ hn, hxy (subtype.val_injective hn))),
  rw [← finsum_eq_finsum_in_univ] at h',
  nth_rewrite 1 ← finsum_subtype_eq_finsum_in,
  erw ← h',
  congr' 1,
  ext, simp,
end

lemma finsum_in_powerset_insert (f : set α → M) {s : set α} {a : α} (h : a ∉ s)
(hsupp : ((insert a s).powerset ∩ function.support f).finite ):
  ∑ᶠ x in (insert a s).powerset, f x =
   ∑ᶠ t in s.powerset, f t + ∑ᶠ t in s.powerset, f (insert a t):=
begin
  set p₀ := s.powerset with hp₀,
  set p₁ := (λ (t : set α), insert a t) '' s.powerset with hp₁,
  have hI : disjoint p₀ p₁,
  { simp_rw [set.disjoint_left, hp₀, hp₁, set.mem_image, set.mem_powerset_iff],
    rintros a ha ⟨x, hxs, rfl⟩,
    exact h (set.mem_of_mem_of_subset (set.mem_insert a x) ha)},

  have hU : p₀ ∪ p₁ = (insert a s).powerset,
  { ext t,
    simp_rw [hp₀, hp₁, set.mem_union, set.mem_image, set.mem_powerset_iff,
    set.subset_insert_iff_subset_or_eq_insert],
    convert iff.rfl,
    ext s',
    rw [exists_prop, eq_comm]},

  have hbij : set.bij_on (λ (t : set α), insert a t) p₀ p₁,
  { refine ⟨λ t ht, _, λ t ht t' ht' htt',_ ,λ t ht, _⟩,
    { simp_rw [hp₁, hp₀, set.mem_image, set.mem_powerset_iff] at ⊢ ht,
      exact ⟨t, by assumption, rfl⟩},
    { rw [hp₀, set.mem_powerset_iff] at ht ht',
      exact (set.insert_inj_subset_iff ht ht' h).mp htt'},
    rw set.mem_image,
    rw [hp₁, set.mem_image] at ht,
    exact ht},

  rw ← hU,
  convert finsum_in_union' _ _ hI using 2,
  { exact finsum_in_eq_of_bij_on _ hbij (λ x hx, (by simp))},
  all_goals
  { apply set.finite.subset hsupp,
    apply set.inter_subset_inter_left,
    simp only [← hU, set.subset_union_right, set.subset_union_left], },
end

/-
lemma finsum_in_bUnion {s : set β} {t : β → set α} (hs : s.finite)
  (ht : ∀ b, (t b).finite) (h : ∀ x ∈ s, ∀ y ∈ s, x ≠ y → disjoint (t x) (t y)) :
  ∑ᶠ i in (⋃ x ∈ s, t x), f i = ∑ᶠ i in s, (∑ᶠ j in (t i), f j) :=
-/

lemma finsum_in_sigma {α : Type*} {β : α → Type*} (s : set α) (t : Π (a : α), set (β a))
(f : sigma β → M) (hs : (s.sigma t ∩ function.support f).finite ):
  ∑ᶠ x in s.sigma t, f x = ∑ᶠ a in s, ∑ᶠ b in t a, f ⟨a, b⟩ :=
begin
  set t' : Π (a : α), set (β a) := λ a, ite (a ∈ s) {b ∈ t a | f ⟨a,b⟩ ≠ 0} ∅ with ht'_def,

  set s' := {a ∈ s | (t' a).nonempty} with hs'_def,
  have hs'' : s' = {a ∈ s' | (t' a).nonempty}, by {ext a, simp [hs'_def, ht'_def]},

  have ht' : s.sigma t ∩ function.support f = s'.sigma t',
  { ext x,
    simp only [ht'_def, hs'_def, set.sigma, set.mem_sep_eq, set.mem_inter_eq, sigma.eta,
    set.mem_set_of_eq, function.mem_support, set.nonempty_def],
    split_ifs,
    { cases x, simp only [set.mem_sep_eq, sigma.eta, ne.def], tauto},
    cases x, simp only at ⊢ h,  tauto, },

  rw finsum_in_inter_support,
  conv in (_ ∩ _) {dsimp only, rw ht'},
  rw [ht'] at hs,

  rw [finsum_in_eq_finset_sum''' _ hs, set.sigma_to_finset hs, finset.sum_sigma],

  have hs' := hs,
  rw [set.sigma_finite_iff, ← hs''] at hs',

  have hat' : ∀ a ∈ s, ∑ᶠ (b : β a) in t a, f ⟨a, b⟩ = ∑ᶠ (b : β a) in t' a, f ⟨a, b⟩,
  { intros a ha, rw [finsum_in_inter_support, ht'_def], congr', ext, simp [if_pos ha], },

  rw [← finsum_in_eq_finsum_in_of_subset
    (by {intros x, simp only [and_imp, set.mem_sep_eq], tauto}: s' ⊆ s)],
  swap,
  { simp only [ht'_def, and_imp, set.mem_sep_eq, not_and, set.mem_diff, ne.def],
    intros x hx hx', specialize hx' hx,
    rw if_pos hx at hx',
    exact finsum_in_eq_zero_of_eq_zero (λ b hb, by_contra (λ hb', hx' ⟨b, ⟨hb, hb'⟩⟩ ))},

  rw finsum_in_eq_finset_sum''' _ hs'.1,
  apply finset.sum_congr, congr' 1, rw ←hs'',

  intros a ha,
  rw [set.finite.mem_to_finset] at ha,
  rw [hat', dif_pos ha, finsum_in_eq_finset_sum'''],
  exact ha.1,
end

/-- for rewriting in reverse direction-/
lemma finsum_in_sigma' {α : Type*} {β : α → Type*} (s : set α) (t : Π (a : α), set (β a))
(f : sigma β → M)
(hs : {a ∈ s | ((t a) ∩ function.support (λ b, f ⟨a,b ⟩)).nonempty}.finite)
(hst : ∀ a ∈ s, (t a ∩ (function.support (λ b, f ⟨a, b⟩ ))).finite ):
  ∑ᶠ x in s.sigma t, f x = ∑ᶠ a in s, ∑ᶠ b in t a, f ⟨a, b⟩ :=
begin
  rw [finsum_in_sigma], rw [set.sigma_inter, set.sigma_finite_iff],
  refine ⟨ by {convert hs, ext, simp [set.nonempty_def]}, λ a ha, _⟩,
  exact set.finite.subset (hst a ha) (λ x hx, by simpa using hx),
end

/-- given nested sums in which the inner summation is over a set that depends on the outer summand,
 reverses the order of summation. -/
lemma finsum_comm_dep {α : Type*} (s : set α) (t : α → set β) (f : α → β → M)
  (hs : {a ∈ s | t a ≠ ∅}.finite) (hst : ∀ a ∈ s, (t a ∩ function.support (f a)).finite):
  ∑ᶠ x in s, (∑ᶠ y in t x, f x y) = ∑ᶠ y, ∑ᶠ x in {i ∈ s | y ∈ t i}, f x y :=
begin
  set f' := λ (p : Σ (a : α), β), f p.1 p.2 with hf',

  have hfin : ((s.sigma t) ∩ function.support f').finite,
  { rw [set.sigma_inter, set.sigma_finite_iff],
    refine ⟨_, λ a ha, _⟩,
    { refine set.finite.subset hs (λ x hx, (by_contra (λ hx', _))),
      replace hx': ¬ x ∈ {a ∈ s | t a ≠ ∅} := hx',
      simp only [set.mem_sep_eq, not_and, not_not, ne.def, function.mem_support] at hx hx',
      simpa [hx' hx.1] using hx},
    convert hst a ha using 1, ext x, simp},

  have hfin' : (((set.univ : set β).sigma (λ y, {i ∈ s | y ∈ t i}))
                  ∩ function.support (f' ∘ set.sigma_swap)).finite,
  { rw [set.sigma_inter, set.finite.iff_of_bij_on (set.sigma_invert _ _)] at hfin,
    rw set.sigma_inter,
    convert hfin,
    ext b a,
    congr' 1,
    simp only [set.sigma_swap, set.mem_sep_eq, set.mem_inter_eq, and_self_left,
    function.comp_app, eq_iff_iff, ne.def, function.mem_support],
    rw and_assoc},

  rw ← finsum_in_sigma s t f' hfin,
  nth_rewrite_rhs 0 finsum_eq_finsum_in_univ,
  convert finsum_in_sigma set.univ (λ y, {i ∈ s | y ∈ t i}) (f' ∘ set.sigma_swap) hfin' using 1,

  rw set.sigma_eq_univ_sigma,
  have := @finsum_in_eq_of_bij_on _ _ _ _ _ _ (f' ∘ set.sigma_swap) f' set.sigma_swap
    (set.sigma_univ_invert (λ y, {i ∈ s | y ∈ t i}))
    (by simp),
  convert this.symm using 2,

  rw ← set.sigma_eq_univ_sigma,
  ext,
  simp [set.sigma],
end

lemma finsum_in_involution {s : set α} (g : α → α)
(h_neg : ∀ a ∈ s, f a + f (g a) = 0)
(h_mem : ∀ a ∈ s, g a ∈ s)
(h_inv : ∀ a ∈ s, g (g a) = a)
(h_ne : ∀ a ∈ s, g a = a → f a = 0):
  ∑ᶠ a in s, f a = 0 :=
begin
  by_cases hs : (s ∩ function.support f).finite, swap, rw finsum_in_eq_zero_of_infinite hs,
  rw [finsum_in_eq_finset_sum _ hs],
  refine finset.sum_involution (λ a _, g a) _ _ _ _,
  any_goals {simp_rw [set.finite.mem_to_finset, set.mem_inter_iff, function.mem_support]},
  any_goals {tauto},
    exact λ a h hne hg, hne (h_ne a h.1 hg),
  refine λ a h, ⟨h_mem a h.1, (λ hg, _)⟩,
  specialize h_neg a h.1,
  rw [hg, add_zero] at h_neg,
  exact h.2 h_neg,
end

end general

section nat

lemma nat.coe_int_distrib_finsum_in {α : Type*} (f : α → ℕ) (s : set α) :
  (((∑ᶠ i in s, f i) : ℕ ) : ℤ) = ∑ᶠ i in s, (f i : ℤ) :=
begin
  by_cases hs : (s ∩ function.support f).finite,
  { simpa using (finsum_in_hom''' (nat.cast_add_monoid_hom ℤ) hs).symm,  },
  repeat {rw finsum_in_eq_zero_of_infinite},
    exact int.coe_nat_zero,
    swap, assumption,
  convert hs using 2, ext, simp,
end

lemma nat.coe_int_distrib_finsum {α : Type*} (f : α → ℕ) :
  (((∑ᶠ (i : α), f i) : ℕ ) : ℤ) = ∑ᶠ (i : α), (f i : ℤ) :=
by rw [finsum_eq_finsum_in_univ, nat.coe_int_distrib_finsum_in, ← finsum_eq_finsum_in_univ]

end nat

section ring

variables {α : Type*} {M: Type*} [comm_semiring M] {f g : α → M} {s : set α}

lemma mul_distrib_finsum_in'
(hs : (s ∩ function.support f).finite) (c : M) :
  c * (∑ᶠ x in s, f x) = ∑ᶠ x in s, c * f x :=
(finsum_in_hom' (add_monoid_hom.mul_left c) hs).symm

lemma mul_distrib_finsum_in [no_zero_divisors M] (c : M) :
  c * (∑ᶠ x in s, f x) = ∑ᶠ x in s, c * f x :=
begin
  by_cases hs : (s ∩ function.support f).finite,
    apply mul_distrib_finsum_in' hs,
  by_cases hc : c = 0, simp [hc],
  rw [finsum_in_eq_zero_of_infinite hs, finsum_in_eq_zero_of_infinite, mul_zero],
  convert hs using 3,
  ext, simp [hc],
end

lemma mul_distrib_finsum'
(h : (function.support f).finite) (c : M) :
  c * (∑ᶠ x, f x) = ∑ᶠ x, c * f x :=
begin
  rw [finsum_eq_finsum_in_univ, finsum_eq_finsum_in_univ],
  apply mul_distrib_finsum_in',
  rwa set.univ_inter,
end

lemma mul_distrib_finsum [no_zero_divisors M] (c : M) :
  c * (∑ᶠ x, f x) = ∑ᶠ x, c * f x :=
by {rw [finsum_eq_finsum_in_univ, finsum_eq_finsum_in_univ], apply mul_distrib_finsum_in}

end ring

section group

variables {α : Type*} {M: Type*} [add_comm_group M] {f g : α → M} {s : set α}

def gsmul_as_hom (n : ℤ) : M →+ M :=
{ to_fun := λ g, n •ℤ g,
  map_zero' := gsmul_zero _,
  map_add' := λ x y, gsmul_add _ _ _}

lemma gsmul_distrib_finsum_in (c : ℤ) (hs : (s ∩ function.support f).finite):
  c •ℤ (∑ᶠ x in s, f x) = ∑ᶠ x in s, c •ℤ (f x) :=
(finsum_in_hom' (gsmul_as_hom c : M →+ M) hs).symm

lemma gsmul_distrib_finsum (c : ℤ) (h : (function.support f).finite):
  c •ℤ (∑ᶠ x, f x) = ∑ᶠ x, c •ℤ (f x) :=
begin
  rw [finsum_eq_finsum_in_univ, gsmul_distrib_finsum_in, ←finsum_eq_finsum_in_univ],
  apply set.finite.inter_right h,
end

lemma finsum_in_neg_distrib (f : α → M) (s : set α) :
  ∑ᶠ i in s, - f i = - ∑ᶠ i in s, f i :=
begin
  by_cases hs : (s ∩ function.support f).finite,
    exact finsum_in_hom' (- (add_monoid_hom.id M)) hs,
  repeat {rw [finsum_in_eq_zero_of_infinite]}; simp [hs],
end

lemma finsum_neg_distrib (f : α → M) :
  ∑ᶠ i, - f i = - ∑ᶠ i, f i :=
by {repeat {rw finsum_eq_finsum_in_univ}, exact finsum_in_neg_distrib _ _}

--lemma finsum_in_sub_distrib {M : Type*} [add_comm_group M] {f g : α → M} {s : set α} :

lemma finsum_in_sub_distrib'
  (hf : (s ∩ function.support f).finite) (hg : (s ∩ function.support g).finite) :
  ∑ᶠ i in s, (f - g) i = ∑ᶠ i in s, f i - ∑ᶠ i in s, g i :=
begin
  simp_rw [sub_eq_add_neg, ← finsum_in_neg_distrib],
  convert finsum_in_add_distrib' _ _, assumption,
  rwa ← function.support_neg at hg,
end

lemma finsum_in_sub_distrib (hs : s.finite) :
  ∑ᶠ i in s, (f - g) i = ∑ᶠ i in s, f i - ∑ᶠ i in s, g i :=
by {apply finsum_in_sub_distrib'; exact set.finite.subset hs (set.inter_subset_left _ _), }

lemma finsum_sub_distrib
(hf : (function.support f).finite) (hg : (function.support g).finite) :
  ∑ᶠ (i : α), (f - g) i = ∑ᶠ (i : α), f i - ∑ᶠ (i : α), g i :=
by {repeat {rw finsum_eq_finsum_in_univ}, apply finsum_in_sub_distrib';
    exact set.finite.subset (by assumption) (set.inter_subset_right _ _), }

end group

section order

open_locale big_operators

variables {α : Type*} {M: Type*} {f g : α → M} {s t : set α}

lemma nonneg_of_finsum_in_nonneg [ordered_add_comm_monoid M] (hf : ∀ i ∈ s, 0 ≤ f i) :
  0 ≤ ∑ᶠ i in s, f i :=
finsum_in_induction _ (le_refl _) (λ _ _ ha hb, add_nonneg ha hb) hf

lemma nonneg_of_finsum_nonneg [ordered_add_comm_monoid M] (hf : ∀ i, 0 ≤ f i) :
  0 ≤ ∑ᶠ (i : α), f i :=
by {rw finsum_eq_finsum_in_univ, exact nonneg_of_finsum_in_nonneg (λ i _, hf i)}

lemma finsum_in_le_finsum_in' [ordered_add_comm_monoid M ] (hfg : ∀ x ∈ s, f x ≤ g x)
(hf : (s ∩ function.support f).finite) (hg : (s ∩ function.support g).finite) :
  ∑ᶠ i in s, f i ≤ ∑ᶠ i in s, g i :=
begin
  convert @finset.sum_le_sum _ _ ((hf.to_finset ∪ hg.to_finset).filter s) f g _ _,
  { apply finsum_in_eq_finset_sum_filter_union hf hg},
  { rw finset.union_comm, apply finsum_in_eq_finset_sum_filter_union hg hf},
  exact λ x hx, hfg _ (finset.mem_filter.mp hx).2,
end

lemma finsum_in_le_finsum_in [ordered_add_comm_monoid M] (hs : s.finite) (hfg : ∀ x ∈ s, f x ≤ g x):
  ∑ᶠ i in s, f i ≤ ∑ᶠ i in s, g i :=
by {apply finsum_in_le_finsum_in' hfg;  exact set.finite.subset hs (set.inter_subset_left _ _)}

lemma finsum_in_le_finsum_in_of_pos [ordered_add_comm_monoid M]
(hfg : ∀ x ∈ s, f x ≤ g x) (hg : 0 < ∑ᶠ i in s, g i) :
  ∑ᶠ i in s, f i ≤ ∑ᶠ i in s, g i :=
begin
  have hg' : (s ∩ function.support g).finite, from
    by_contra (λ hn, by {rw finsum_in_eq_zero_of_infinite hn at hg, exact lt_irrefl _ hg, }),
  by_cases hf : (s ∩ function.support f).finite,
    exact finsum_in_le_finsum_in' hfg hf hg',
  rw finsum_in_eq_zero_of_infinite hf,
  exact le_of_lt hg,
end

lemma finsum_le_finsum [ordered_add_comm_monoid M] (hfg : ∀ x, f x ≤ g x)
(hf : (function.support f).finite) (hg : (function.support g).finite) :
  ∑ᶠ (i : α), f i ≤ ∑ᶠ (i : α), g i :=
begin
  repeat {rw finsum_eq_finsum_in_univ},
  apply finsum_in_le_finsum_in' (λ x hx, hfg x);
  exact set.finite.subset (by assumption) (set.inter_subset_right _ _),
end

lemma finsum_le_finsum_of_pos [ordered_add_comm_monoid M] (hfg : ∀ x, f x ≤ g x)
(hg : 0 < ∑ᶠ i, g i) :
  ∑ᶠ (i : α), f i ≤ ∑ᶠ (i : α), g i :=
begin
  simp_rw finsum_eq_finsum_in_univ at *,
  apply finsum_in_le_finsum_in_of_pos (λ x hx, hfg x) hg,
end

lemma finsum_in_eq_zero_iff_of_nonneg_supp [ordered_add_comm_monoid M]
(hs : (s ∩ function.support f).finite) (hf : ∀ x ∈ s, 0 ≤ f x) :
  ∑ᶠ x in s, f x = 0 ↔ ∀ x ∈ s, f x = 0 :=
begin
  rw [finsum_in_eq_finset_sum _ hs, finset.sum_eq_zero_iff_of_nonneg], simp,
  simp_rw [set.finite.mem_to_finset, set.mem_inter_iff], tauto,
end

lemma finsum_in_eq_zero_iff_of_nonneg [ordered_add_comm_monoid M]
(hs : s.finite) (hf : ∀ x ∈ s, 0 ≤ f x) :
  ∑ᶠ x in s, f x = 0 ↔ ∀ x ∈ s, f x = 0 :=
finsum_in_eq_zero_iff_of_nonneg_supp (hs.inter_left _) hf

lemma finsum_in_le_zero_iff_of_nonneg_supp [ordered_add_comm_monoid M]
(hs : (s ∩ function.support f).finite) (hf : ∀ x ∈ s, 0 ≤ f x) :
  ∑ᶠ x in s, f x ≤ 0 ↔ ∀ x ∈ s, f x = 0 :=
begin
  convert finsum_in_eq_zero_iff_of_nonneg_supp hs hf,
  exact iff_iff_eq.mp ⟨λ h, le_antisymm h (nonneg_of_finsum_in_nonneg hf), λ h, by rw h⟩ ,
end

lemma finsum_in_le_zero_iff_of_nonneg [ordered_add_comm_monoid M]
(hs : s.finite) (hf : ∀ x ∈ s, 0 ≤ f x) :
  ∑ᶠ x in s, f x ≤ 0 ↔ ∀ x ∈ s, f x = 0 :=
finsum_in_le_zero_iff_of_nonneg_supp (hs.inter_left _) hf

lemma finsum_eq_zero_iff_of_nonneg [ordered_add_comm_monoid M]
(h : (function.support f).finite) (hf : ∀ x, 0 ≤ f x) :
  ∑ᶠ x, f x = 0 ↔ ∀ x, f x = 0 :=
begin
  rw [finsum_eq_finsum_in_univ, finsum_in_eq_zero_iff_of_nonneg_supp],
  tauto, rwa [set.univ_inter], tauto,
end

lemma finsum_le_zero_iff_of_nonneg [ordered_add_comm_monoid M]
(h : (function.support f).finite) (hf : ∀ x, 0 ≤ f x) :
  ∑ᶠ x, f x ≤ 0 ↔ ∀ x, f x = 0 :=
begin
  convert finsum_eq_zero_iff_of_nonneg h hf,
  exact iff_iff_eq.mp ⟨λ h, le_antisymm h (nonneg_of_finsum_nonneg hf), λ h, by rw h⟩ ,
end

@[simp] lemma finsum_in_eq_zero_iff [canonically_ordered_add_monoid M]
(hs : (s ∩ function.support f).finite) :
  ∑ᶠ x in s, f x = 0 ↔ ∀ x ∈ s, f x = 0 :=
finsum_in_eq_zero_iff_of_nonneg_supp hs (by simp)

@[simp] lemma finsum_eq_zero_iff [canonically_ordered_add_monoid M]
(h : (function.support f).finite) :
  ∑ᶠ x, f x = 0 ↔ ∀ x, f x = 0 :=
finsum_eq_zero_iff_of_nonneg h (by simp)

lemma finsum_in_exists_lt_of_finsum_lt_supp [linear_ordered_cancel_add_comm_monoid M]
(hf : (s ∩ function.support f).finite) (hg : (s ∩ function.support g).finite)
(hlt : ∑ᶠ x in s, f x < ∑ᶠ x in s, g x) :
  ∃ i ∈ s, f i < g i :=
by_contra (λ hn, by {push_neg at hn, exact not_lt_of_le (finsum_in_le_finsum_in' hn hg hf) hlt})

lemma finsum_in_exists_lt_of_finsum_lt [linear_ordered_cancel_add_comm_monoid M]
(hs : s.finite) (hlt : ∑ᶠ x in s, f x < ∑ᶠ x in s, g x) :
  ∃ i ∈ s, f i < g i :=
finsum_in_exists_lt_of_finsum_lt_supp (hs.inter_left _) (hs.inter_left _) hlt

lemma finsum_exists_lt_of_finsum_lt [linear_ordered_cancel_add_comm_monoid M]
(hf : (function.support f).finite) (hg : (function.support g).finite)
(hlt : ∑ᶠ x, f x < ∑ᶠ x, g x) :
  ∃ i, f i < g i :=
begin
  repeat {rw finsum_eq_finsum_in_univ at hlt},
  obtain ⟨i,-,hi⟩ := finsum_in_exists_lt_of_finsum_lt_supp
    (hf.inter_right _) (hg.inter_right _) hlt,
  exact ⟨i,hi⟩,
end

lemma finsum_in_lt_finsum_in_supp [ordered_cancel_add_comm_monoid M]
(hf : (s ∩ function.support f).finite) (hg : (s ∩ function.support g).finite)
(hle : ∀ i ∈ s, f i ≤ g i) (hlt : ∃ i ∈ s, f i < g i) :
  ∑ᶠ i in s, f i < ∑ᶠ i in s, g i :=
begin
  convert @finset.sum_lt_sum α M ((hf.to_finset ∪ hg.to_finset).filter s) f g _ _ _,
  { apply finsum_in_eq_finset_sum_filter_union hf hg},
  { rw finset.union_comm, apply finsum_in_eq_finset_sum_filter_union hg hf},
  { exact λ x hx, hle _ (finset.mem_filter.mp hx).2},
  obtain ⟨i,his, hi⟩ := hlt,
  refine ⟨i, _, hi⟩,
  simp only [set.mem_inter_eq, set.finite.mem_to_finset, finset.mem_union,
  finset.mem_filter, function.mem_support, ← and_or_distrib_left],
  refine ⟨⟨his, by_contra (λ hn, _)⟩, his⟩,
  push_neg at hn, rw [hn.1, hn.2] at hi,
  exact lt_irrefl _ hi,
end

lemma finsum_in_lt_finsum_in [ordered_cancel_add_comm_monoid M]
(hs : s.finite) (hle : ∀ i ∈ s, f i ≤ g i) (hlt : ∃ i ∈ s, f i < g i) :
  ∑ᶠ i in s, f i < ∑ᶠ i in s, g i :=
finsum_in_lt_finsum_in_supp (hs.inter_left _) (hs.inter_left _) hle hlt

lemma finsum_lt_finsum [ordered_cancel_add_comm_monoid M]
(hf : (function.support f).finite) (hg : (function.support g).finite)
(hle : ∀ i, f i ≤ g i) (hlt : ∃ i, f i < g i) :
  ∑ᶠ i, f i < ∑ᶠ i, g i :=
begin
  repeat {rw finsum_eq_finsum_in_univ}, apply finsum_in_lt_finsum_in_supp,
  { apply set.finite.subset hf _, exact set.inter_subset_right _ _, },
  { apply set.finite.subset hg _, exact set.inter_subset_right _ _, },
  { exact λ i hi, hle i, },
  exact (let ⟨i, hi⟩ := hlt in ⟨i, set.mem_univ _, hi⟩),
end

lemma finsum_in_eq_finsum_in_iff_of_le_supp [ordered_cancel_add_comm_monoid M]
(hf : (s ∩ function.support f).finite) (hg : (s ∩ function.support g).finite)
(hfg : ∀ x ∈ s, f x ≤ g x) :
  ∑ᶠ i in s, f i = ∑ᶠ i in s, g i ↔ ∀ i ∈ s, f i = g i :=
begin
  refine ⟨λ h, λ i hi, by_contra (λ hn, _), finsum_in_eq_of_eq⟩,
  exact (finsum_in_lt_finsum_in_supp hf hg hfg ⟨i, hi, (ne.le_iff_lt hn).mp (hfg i hi)⟩).ne h,
end

lemma finsum_in_eq_finsum_in_iff_of_le [ordered_cancel_add_comm_monoid M]
(hs : s.finite) (hfg : ∀ x ∈ s, f x ≤ g x) :
  ∑ᶠ i in s, f i = ∑ᶠ i in s, g i ↔ ∀ i ∈ s, f i = g i :=
finsum_in_eq_finsum_in_iff_of_le_supp (hs.inter_left _) (hs.inter_left _) hfg

lemma finsum_in_ge_finsum_in_iff_of_le_supp [ordered_cancel_add_comm_monoid M]
(hf : (s ∩ function.support f).finite) (hg : (s ∩ function.support g).finite)
(hfg : ∀ x ∈ s, f x ≤ g x) :
  ∑ᶠ i in s, g i ≤ ∑ᶠ i in s, f i ↔ ∀ i ∈ s, f i = g i :=
begin
  convert finsum_in_eq_finsum_in_iff_of_le_supp hf hg hfg,
  refine iff_iff_eq.mp ⟨λ h, le_antisymm (finsum_in_le_finsum_in' hfg hf hg) h, λ h, by rw h⟩,
end

lemma finsum_in_ge_finsum_in_iff_of_le [ordered_cancel_add_comm_monoid M]
(hs : s.finite) (hfg : ∀ x ∈ s, f x ≤ g x) :
  ∑ᶠ i in s, g i ≤ ∑ᶠ i in s, f i ↔ ∀ i ∈ s, f i = g i :=
finsum_in_ge_finsum_in_iff_of_le_supp (hs.inter_left _) (hs.inter_left _) hfg

lemma finsum_eq_finsum_iff_of_le [ordered_cancel_add_comm_monoid M]
(hf : (function.support f).finite) (hg : (function.support g).finite)
(hfg : ∀ x, f x ≤ g x) :
  ∑ᶠ i, f i = ∑ᶠ i, g i ↔ ∀ i, f i = g i :=
begin
  repeat {rw finsum_eq_finsum_in_univ}, rw finsum_in_eq_finsum_in_iff_of_le_supp, tauto,
  { apply set.finite.subset hf _, exact set.inter_subset_right _ _, },
  { apply set.finite.subset hg _, exact set.inter_subset_right _ _, },
  exact λ x _, hfg x,
end

lemma finsum_in_subset_le_finsum_in_of_nonneg_supp [ordered_add_comm_monoid M]
(ht : (t ∩ (function.support f)).finite)
(hst : s ⊆ t) (hf : ∀ x ∈ t, 0 ≤ f x) :
  ∑ᶠ x in s, f x ≤ ∑ᶠ x in t, f x :=
begin
  have hs := set.finite.subset ht ((function.support f).inter_subset_inter_left hst),
  have hs' := set.finite.subset ht
    ((function.support f).inter_subset_inter_left (set.diff_subset t s)),
  have h' := finsum_in_union' hs hs' (set.disjoint_diff),
  rw (set.union_diff_cancel hst) at h',
  rw h',
  exact le_add_of_nonneg_right (nonneg_of_finsum_in_nonneg (λ i hi, hf i (set.mem_of_mem_diff hi)))
end

lemma finsum_in_subset_le_finsum_in_of_nonneg [ordered_add_comm_monoid M]
(ht : t.finite)
(hst : s ⊆ t) (hf : ∀ x ∈ t, 0 ≤ f x) :
  ∑ᶠ x in s, f x ≤ ∑ᶠ x in t, f x :=
finsum_in_subset_le_finsum_in_of_nonneg_supp (ht.inter_left _) hst hf

lemma eq_zero_of_finsum_in_subset_le_finsum_in_of_nonneg_supp [ordered_cancel_add_comm_monoid M]
(ht : (t ∩ (function.support f)).finite)
(hst : s ⊆ t) (hf : ∀ x ∈ t, 0 ≤ f x) (h : ∑ᶠ x in t, f x ≤ ∑ᶠ x in s, f x) :
  ∀ x ∈ t \ s, f x = 0 :=
begin
  have hs := set.finite.subset ht ((function.support f).inter_subset_inter_left hst),
  have hs' := set.finite.subset ht
    ((function.support f).inter_subset_inter_left (set.diff_subset t s)),
  have h' := finsum_in_union' hs hs' (set.disjoint_diff),
  rw (set.union_diff_cancel hst) at h',
  rwa [h', add_le_iff_nonpos_right, finsum_in_le_zero_iff_of_nonneg_supp hs'] at h,
  exact λ x hx, hf x (set.mem_of_mem_diff hx),
end

lemma eq_zero_of_finsum_in_subset_eq_finsum_in_of_nonneg [ordered_cancel_add_comm_monoid M]
(ht : t.finite)
(hst : s ⊆ t) (hf : ∀ x ∈ t, 0 ≤ f x) (h : ∑ᶠ x in t, f x ≤ ∑ᶠ x in s, f x) :
  ∀ x ∈ t \ s, f x = 0 :=
eq_zero_of_finsum_in_subset_le_finsum_in_of_nonneg_supp (ht.inter_left _) hst hf h

end order

section intervals

open set

variables {α : Type*} [ordered_cancel_add_comm_monoid α] [has_exists_add_of_le α]

section shifts
variables {M : Type*} [add_comm_monoid M]

lemma finsum_Icc_shift (a b d : α) (f : α → M) :
  ∑ᶠ i in set.Icc a b, f (i + d) = ∑ᶠ i in set.Icc (a + d) (b + d), f i :=
finsum_in_eq_of_bij_on _ (Icc_add_bij _ _ _) (λ _ _, rfl)

lemma finsum_Ico_shift (a b d : α) (f : α → M) :
  ∑ᶠ i in set.Ico a b, f (i + d) = ∑ᶠ i in set.Ico (a + d) (b + d), f i :=
finsum_in_eq_of_bij_on _ (Ico_add_bij _ _ _) (λ _ _, rfl)

lemma finsum_Ioc_shift (a b d : α) (f : α → M) :
  ∑ᶠ i in set.Ioc a b, f (i + d) = ∑ᶠ i in set.Ioc (a + d) (b + d), f i :=
finsum_in_eq_of_bij_on _ (Ioc_add_bij _ _ _) (λ _ _, rfl)

lemma finsum_Ioo_shift (a b d : α) (f : α → M) :
  ∑ᶠ i in set.Ioo a b, f (i + d) = ∑ᶠ i in set.Ioo (a + d) (b + d), f i :=
finsum_in_eq_of_bij_on _ (Ioo_add_bij _ _ _) (λ _ _, rfl)

end shifts

end intervals


-- 5.5 Tactic Combinators

example (p q : Prop) (hp : p) : p ∨ q :=
begin left, assumption end

example (p q : Prop) (hp : p) : p ∨ q :=
by { left, assumption }

example (p q : Prop) (hp : p) (hq : q) : p ∧ q :=
by split; assumption

example (p q : Prop) (hp : p) : p ∨ q :=
by {left, assumption} <|> {right, assumption}

example (p q : Prop) (hq : q) : p ∨ q :=
by {left, assumption} <|> {right, assumption}

example (p q r : Prop) (hp : p) : p ∨ q ∨ r :=
by repeat {{left, assumption} <|> right <|> assumption}

example (p q r : Prop) (hq : q) : p ∨ q ∨ r :=
by repeat {{left, assumption} <|> right <|> assumption}

example (p q r : Prop) (hr : r) : p ∨ q ∨ r :=
by repeat {{left, assumption} <|> right <|> assumption}

meta def my_tac : tactic unit :=
`[ repeat {{left, assumption} <|> right <|> assumption}]

example (p q r : Prop) (hp : p) : p ∨ q ∨ r :=
by my_tac

example (p q r : Prop) (hq : q) : p ∨ q ∨ r :=
by my_tac

example (p q r : Prop) (hr : r) : p ∨ q ∨ r :=
by my_tac

example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ q ∧ r :=
by split; try {split}; assumption

example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ q ∧ r :=
begin
  split,
  all_goals {try {split}},
  all_goals {assumption}
end

example (p q r : Prop) (hp : p) (hq : q) (hr : r) : p ∧ q ∧ r :=
begin
  split,
  any_goals {split},
  any_goals {assumption}
end

example (p q r : Prop) (hp : p) (hq : q) (hr : r) :
  p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) :=
begin
  repeat { any_goals { split }},
  all_goals { assumption }
end

example (p q r : Prop) (hp : p) (hq : q) (hr : r) :
  p ∧ ((p ∧ q) ∧ r) ∧ (q ∧ r ∧ p) :=
by repeat { any_goals { split <|> assumption} }


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

! This file was ported from Lean 3 source module analysis.convex.side
! leanprover-community/mathlib commit a63928c34ec358b5edcda2bf7513c50052a5230f
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Convex.Between
import Mathbin.Analysis.Convex.Normed
import Mathbin.Analysis.Normed.Group.AddTorsor

/-!
# Sides of affine subspaces

This file defines notions of two points being on the same or opposite sides of an affine subspace.

## Main definitions

* `s.w_same_side x y`: The points `x` and `y` are weakly on the same side of the affine
  subspace `s`.
* `s.s_same_side x y`: The points `x` and `y` are strictly on the same side of the affine
  subspace `s`.
* `s.w_opp_side x y`: The points `x` and `y` are weakly on opposite sides of the affine
  subspace `s`.
* `s.s_opp_side x y`: The points `x` and `y` are strictly on opposite sides of the affine
  subspace `s`.

-/

variable {R V V' P P' : Type _}

open AffineEquiv AffineMap

namespace AffineSubspace

section StrictOrderedCommRing

variable [StrictOrderedCommRing R] [AddCommGroup V] [Module R V] [AddTorsor V P]

variable [AddCommGroup V'] [Module R V'] [AddTorsor V' P']

include V

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (p₁ p₂ «expr ∈ » s) -/
/-- The points `x` and `y` are weakly on the same side of `s`. -/
def WSameSide (s : AffineSubspace R P) (x y : P) : Prop :=
  ∃ (p₁ : _)(_ : p₁ ∈ s)(p₂ : _)(_ : p₂ ∈ s), SameRay R (x -ᵥ p₁) (y -ᵥ p₂)
#align affine_subspace.w_same_side AffineSubspace.WSameSide

/-- The points `x` and `y` are strictly on the same side of `s`. -/
def SSameSide (s : AffineSubspace R P) (x y : P) : Prop :=
  s.WSameSide x y ∧ x ∉ s ∧ y ∉ s
#align affine_subspace.s_same_side AffineSubspace.SSameSide

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (p₁ p₂ «expr ∈ » s) -/
/-- The points `x` and `y` are weakly on opposite sides of `s`. -/
def WOppSide (s : AffineSubspace R P) (x y : P) : Prop :=
  ∃ (p₁ : _)(_ : p₁ ∈ s)(p₂ : _)(_ : p₂ ∈ s), SameRay R (x -ᵥ p₁) (p₂ -ᵥ y)
#align affine_subspace.w_opp_side AffineSubspace.WOppSide

/-- The points `x` and `y` are strictly on opposite sides of `s`. -/
def SOppSide (s : AffineSubspace R P) (x y : P) : Prop :=
  s.WOppSide x y ∧ x ∉ s ∧ y ∉ s
#align affine_subspace.s_opp_side AffineSubspace.SOppSide

include V'

theorem WSameSide.map {s : AffineSubspace R P} {x y : P} (h : s.WSameSide x y) (f : P →ᵃ[R] P') :
    (s.map f).WSameSide (f x) (f y) :=
  by
  rcases h with ⟨p₁, hp₁, p₂, hp₂, h⟩
  refine' ⟨f p₁, mem_map_of_mem f hp₁, f p₂, mem_map_of_mem f hp₂, _⟩
  simp_rw [← linear_map_vsub]
  exact h.map f.linear
#align affine_subspace.w_same_side.map AffineSubspace.WSameSide.map

theorem Function.Injective.wSameSide_map_iff {s : AffineSubspace R P} {x y : P} {f : P →ᵃ[R] P'}
    (hf : Function.Injective f) : (s.map f).WSameSide (f x) (f y) ↔ s.WSameSide x y :=
  by
  refine' ⟨fun h => _, fun h => h.map _⟩
  rcases h with ⟨fp₁, hfp₁, fp₂, hfp₂, h⟩
  rw [mem_map] at hfp₁ hfp₂
  rcases hfp₁ with ⟨p₁, hp₁, rfl⟩
  rcases hfp₂ with ⟨p₂, hp₂, rfl⟩
  refine' ⟨p₁, hp₁, p₂, hp₂, _⟩
  simp_rw [← linear_map_vsub, (f.linear_injective_iff.2 hf).sameRay_map_iff] at h
  exact h
#align function.injective.w_same_side_map_iff Function.Injective.wSameSide_map_iff

theorem Function.Injective.sSameSide_map_iff {s : AffineSubspace R P} {x y : P} {f : P →ᵃ[R] P'}
    (hf : Function.Injective f) : (s.map f).SSameSide (f x) (f y) ↔ s.SSameSide x y := by
  simp_rw [s_same_side, hf.w_same_side_map_iff, mem_map_iff_mem_of_injective hf]
#align function.injective.s_same_side_map_iff Function.Injective.sSameSide_map_iff

@[simp]
theorem AffineEquiv.wSameSide_map_iff {s : AffineSubspace R P} {x y : P} (f : P ≃ᵃ[R] P') :
    (s.map ↑f).WSameSide (f x) (f y) ↔ s.WSameSide x y :=
  (show Function.Injective f.toAffineMap from f.Injective).wSameSide_map_iff
#align affine_equiv.w_same_side_map_iff AffineEquiv.wSameSide_map_iff

@[simp]
theorem AffineEquiv.sSameSide_map_iff {s : AffineSubspace R P} {x y : P} (f : P ≃ᵃ[R] P') :
    (s.map ↑f).SSameSide (f x) (f y) ↔ s.SSameSide x y :=
  (show Function.Injective f.toAffineMap from f.Injective).sSameSide_map_iff
#align affine_equiv.s_same_side_map_iff AffineEquiv.sSameSide_map_iff

theorem WOppSide.map {s : AffineSubspace R P} {x y : P} (h : s.WOppSide x y) (f : P →ᵃ[R] P') :
    (s.map f).WOppSide (f x) (f y) :=
  by
  rcases h with ⟨p₁, hp₁, p₂, hp₂, h⟩
  refine' ⟨f p₁, mem_map_of_mem f hp₁, f p₂, mem_map_of_mem f hp₂, _⟩
  simp_rw [← linear_map_vsub]
  exact h.map f.linear
#align affine_subspace.w_opp_side.map AffineSubspace.WOppSide.map

theorem Function.Injective.wOppSide_map_iff {s : AffineSubspace R P} {x y : P} {f : P →ᵃ[R] P'}
    (hf : Function.Injective f) : (s.map f).WOppSide (f x) (f y) ↔ s.WOppSide x y :=
  by
  refine' ⟨fun h => _, fun h => h.map _⟩
  rcases h with ⟨fp₁, hfp₁, fp₂, hfp₂, h⟩
  rw [mem_map] at hfp₁ hfp₂
  rcases hfp₁ with ⟨p₁, hp₁, rfl⟩
  rcases hfp₂ with ⟨p₂, hp₂, rfl⟩
  refine' ⟨p₁, hp₁, p₂, hp₂, _⟩
  simp_rw [← linear_map_vsub, (f.linear_injective_iff.2 hf).sameRay_map_iff] at h
  exact h
#align function.injective.w_opp_side_map_iff Function.Injective.wOppSide_map_iff

theorem Function.Injective.sOppSide_map_iff {s : AffineSubspace R P} {x y : P} {f : P →ᵃ[R] P'}
    (hf : Function.Injective f) : (s.map f).SOppSide (f x) (f y) ↔ s.SOppSide x y := by
  simp_rw [s_opp_side, hf.w_opp_side_map_iff, mem_map_iff_mem_of_injective hf]
#align function.injective.s_opp_side_map_iff Function.Injective.sOppSide_map_iff

@[simp]
theorem AffineEquiv.wOppSide_map_iff {s : AffineSubspace R P} {x y : P} (f : P ≃ᵃ[R] P') :
    (s.map ↑f).WOppSide (f x) (f y) ↔ s.WOppSide x y :=
  (show Function.Injective f.toAffineMap from f.Injective).wOppSide_map_iff
#align affine_equiv.w_opp_side_map_iff AffineEquiv.wOppSide_map_iff

@[simp]
theorem AffineEquiv.sOppSide_map_iff {s : AffineSubspace R P} {x y : P} (f : P ≃ᵃ[R] P') :
    (s.map ↑f).SOppSide (f x) (f y) ↔ s.SOppSide x y :=
  (show Function.Injective f.toAffineMap from f.Injective).sOppSide_map_iff
#align affine_equiv.s_opp_side_map_iff AffineEquiv.sOppSide_map_iff

omit V'

theorem WSameSide.nonempty {s : AffineSubspace R P} {x y : P} (h : s.WSameSide x y) :
    (s : Set P).Nonempty :=
  ⟨h.some, h.choose_spec.some⟩
#align affine_subspace.w_same_side.nonempty AffineSubspace.WSameSide.nonempty

theorem SSameSide.nonempty {s : AffineSubspace R P} {x y : P} (h : s.SSameSide x y) :
    (s : Set P).Nonempty :=
  ⟨h.1.some, h.1.choose_spec.some⟩
#align affine_subspace.s_same_side.nonempty AffineSubspace.SSameSide.nonempty

theorem WOppSide.nonempty {s : AffineSubspace R P} {x y : P} (h : s.WOppSide x y) :
    (s : Set P).Nonempty :=
  ⟨h.some, h.choose_spec.some⟩
#align affine_subspace.w_opp_side.nonempty AffineSubspace.WOppSide.nonempty

theorem SOppSide.nonempty {s : AffineSubspace R P} {x y : P} (h : s.SOppSide x y) :
    (s : Set P).Nonempty :=
  ⟨h.1.some, h.1.choose_spec.some⟩
#align affine_subspace.s_opp_side.nonempty AffineSubspace.SOppSide.nonempty

theorem SSameSide.wSameSide {s : AffineSubspace R P} {x y : P} (h : s.SSameSide x y) :
    s.WSameSide x y :=
  h.1
#align affine_subspace.s_same_side.w_same_side AffineSubspace.SSameSide.wSameSide

theorem SSameSide.left_not_mem {s : AffineSubspace R P} {x y : P} (h : s.SSameSide x y) : x ∉ s :=
  h.2.1
#align affine_subspace.s_same_side.left_not_mem AffineSubspace.SSameSide.left_not_mem

theorem SSameSide.right_not_mem {s : AffineSubspace R P} {x y : P} (h : s.SSameSide x y) : y ∉ s :=
  h.2.2
#align affine_subspace.s_same_side.right_not_mem AffineSubspace.SSameSide.right_not_mem

theorem SOppSide.wOppSide {s : AffineSubspace R P} {x y : P} (h : s.SOppSide x y) :
    s.WOppSide x y :=
  h.1
#align affine_subspace.s_opp_side.w_opp_side AffineSubspace.SOppSide.wOppSide

theorem SOppSide.left_not_mem {s : AffineSubspace R P} {x y : P} (h : s.SOppSide x y) : x ∉ s :=
  h.2.1
#align affine_subspace.s_opp_side.left_not_mem AffineSubspace.SOppSide.left_not_mem

theorem SOppSide.right_not_mem {s : AffineSubspace R P} {x y : P} (h : s.SOppSide x y) : y ∉ s :=
  h.2.2
#align affine_subspace.s_opp_side.right_not_mem AffineSubspace.SOppSide.right_not_mem

theorem wSameSide_comm {s : AffineSubspace R P} {x y : P} : s.WSameSide x y ↔ s.WSameSide y x :=
  ⟨fun ⟨p₁, hp₁, p₂, hp₂, h⟩ => ⟨p₂, hp₂, p₁, hp₁, h.symm⟩, fun ⟨p₁, hp₁, p₂, hp₂, h⟩ =>
    ⟨p₂, hp₂, p₁, hp₁, h.symm⟩⟩
#align affine_subspace.w_same_side_comm AffineSubspace.wSameSide_comm

alias w_same_side_comm ↔ w_same_side.symm _
#align affine_subspace.w_same_side.symm AffineSubspace.WSameSide.symm

theorem sSameSide_comm {s : AffineSubspace R P} {x y : P} : s.SSameSide x y ↔ s.SSameSide y x := by
  rw [s_same_side, s_same_side, w_same_side_comm, and_comm' (x ∉ s)]
#align affine_subspace.s_same_side_comm AffineSubspace.sSameSide_comm

alias s_same_side_comm ↔ s_same_side.symm _
#align affine_subspace.s_same_side.symm AffineSubspace.SSameSide.symm

theorem wOppSide_comm {s : AffineSubspace R P} {x y : P} : s.WOppSide x y ↔ s.WOppSide y x :=
  by
  constructor
  · rintro ⟨p₁, hp₁, p₂, hp₂, h⟩
    refine' ⟨p₂, hp₂, p₁, hp₁, _⟩
    rwa [SameRay.sameRay_comm, ← sameRay_neg_iff, neg_vsub_eq_vsub_rev, neg_vsub_eq_vsub_rev]
  · rintro ⟨p₁, hp₁, p₂, hp₂, h⟩
    refine' ⟨p₂, hp₂, p₁, hp₁, _⟩
    rwa [SameRay.sameRay_comm, ← sameRay_neg_iff, neg_vsub_eq_vsub_rev, neg_vsub_eq_vsub_rev]
#align affine_subspace.w_opp_side_comm AffineSubspace.wOppSide_comm

alias w_opp_side_comm ↔ w_opp_side.symm _
#align affine_subspace.w_opp_side.symm AffineSubspace.WOppSide.symm

theorem sOppSide_comm {s : AffineSubspace R P} {x y : P} : s.SOppSide x y ↔ s.SOppSide y x := by
  rw [s_opp_side, s_opp_side, w_opp_side_comm, and_comm' (x ∉ s)]
#align affine_subspace.s_opp_side_comm AffineSubspace.sOppSide_comm

alias s_opp_side_comm ↔ s_opp_side.symm _
#align affine_subspace.s_opp_side.symm AffineSubspace.SOppSide.symm

theorem not_wSameSide_bot (x y : P) : ¬(⊥ : AffineSubspace R P).WSameSide x y := by
  simp [w_same_side, not_mem_bot]
#align affine_subspace.not_w_same_side_bot AffineSubspace.not_wSameSide_bot

theorem not_sSameSide_bot (x y : P) : ¬(⊥ : AffineSubspace R P).SSameSide x y := fun h =>
  not_wSameSide_bot x y h.WSameSide
#align affine_subspace.not_s_same_side_bot AffineSubspace.not_sSameSide_bot

theorem not_wOppSide_bot (x y : P) : ¬(⊥ : AffineSubspace R P).WOppSide x y := by
  simp [w_opp_side, not_mem_bot]
#align affine_subspace.not_w_opp_side_bot AffineSubspace.not_wOppSide_bot

theorem not_sOppSide_bot (x y : P) : ¬(⊥ : AffineSubspace R P).SOppSide x y := fun h =>
  not_wOppSide_bot x y h.WOppSide
#align affine_subspace.not_s_opp_side_bot AffineSubspace.not_sOppSide_bot

@[simp]
theorem wSameSide_self_iff {s : AffineSubspace R P} {x : P} :
    s.WSameSide x x ↔ (s : Set P).Nonempty :=
  ⟨fun h => h.Nonempty, fun ⟨p, hp⟩ => ⟨p, hp, p, hp, SameRay.rfl⟩⟩
#align affine_subspace.w_same_side_self_iff AffineSubspace.wSameSide_self_iff

theorem sSameSide_self_iff {s : AffineSubspace R P} {x : P} :
    s.SSameSide x x ↔ (s : Set P).Nonempty ∧ x ∉ s :=
  ⟨fun ⟨h, hx, _⟩ => ⟨wSameSide_self_iff.1 h, hx⟩, fun ⟨h, hx⟩ => ⟨wSameSide_self_iff.2 h, hx, hx⟩⟩
#align affine_subspace.s_same_side_self_iff AffineSubspace.sSameSide_self_iff

theorem wSameSide_of_left_mem {s : AffineSubspace R P} {x : P} (y : P) (hx : x ∈ s) :
    s.WSameSide x y := by
  refine' ⟨x, hx, x, hx, _⟩
  simp
#align affine_subspace.w_same_side_of_left_mem AffineSubspace.wSameSide_of_left_mem

theorem wSameSide_of_right_mem {s : AffineSubspace R P} (x : P) {y : P} (hy : y ∈ s) :
    s.WSameSide x y :=
  (wSameSide_of_left_mem x hy).symm
#align affine_subspace.w_same_side_of_right_mem AffineSubspace.wSameSide_of_right_mem

theorem wOppSide_of_left_mem {s : AffineSubspace R P} {x : P} (y : P) (hx : x ∈ s) :
    s.WOppSide x y := by
  refine' ⟨x, hx, x, hx, _⟩
  simp
#align affine_subspace.w_opp_side_of_left_mem AffineSubspace.wOppSide_of_left_mem

theorem wOppSide_of_right_mem {s : AffineSubspace R P} (x : P) {y : P} (hy : y ∈ s) :
    s.WOppSide x y :=
  (wOppSide_of_left_mem x hy).symm
#align affine_subspace.w_opp_side_of_right_mem AffineSubspace.wOppSide_of_right_mem

theorem wSameSide_vadd_left_iff {s : AffineSubspace R P} {x y : P} {v : V} (hv : v ∈ s.direction) :
    s.WSameSide (v +ᵥ x) y ↔ s.WSameSide x y :=
  by
  constructor
  · rintro ⟨p₁, hp₁, p₂, hp₂, h⟩
    refine'
      ⟨-v +ᵥ p₁, AffineSubspace.vadd_mem_of_mem_direction (Submodule.neg_mem _ hv) hp₁, p₂, hp₂, _⟩
    rwa [vsub_vadd_eq_vsub_sub, sub_neg_eq_add, add_comm, ← vadd_vsub_assoc]
  · rintro ⟨p₁, hp₁, p₂, hp₂, h⟩
    refine' ⟨v +ᵥ p₁, AffineSubspace.vadd_mem_of_mem_direction hv hp₁, p₂, hp₂, _⟩
    rwa [vadd_vsub_vadd_cancel_left]
#align affine_subspace.w_same_side_vadd_left_iff AffineSubspace.wSameSide_vadd_left_iff

theorem wSameSide_vadd_right_iff {s : AffineSubspace R P} {x y : P} {v : V} (hv : v ∈ s.direction) :
    s.WSameSide x (v +ᵥ y) ↔ s.WSameSide x y := by
  rw [w_same_side_comm, w_same_side_vadd_left_iff hv, w_same_side_comm]
#align affine_subspace.w_same_side_vadd_right_iff AffineSubspace.wSameSide_vadd_right_iff

theorem sSameSide_vadd_left_iff {s : AffineSubspace R P} {x y : P} {v : V} (hv : v ∈ s.direction) :
    s.SSameSide (v +ᵥ x) y ↔ s.SSameSide x y := by
  rw [s_same_side, s_same_side, w_same_side_vadd_left_iff hv, vadd_mem_iff_mem_of_mem_direction hv]
#align affine_subspace.s_same_side_vadd_left_iff AffineSubspace.sSameSide_vadd_left_iff

theorem sSameSide_vadd_right_iff {s : AffineSubspace R P} {x y : P} {v : V} (hv : v ∈ s.direction) :
    s.SSameSide x (v +ᵥ y) ↔ s.SSameSide x y := by
  rw [s_same_side_comm, s_same_side_vadd_left_iff hv, s_same_side_comm]
#align affine_subspace.s_same_side_vadd_right_iff AffineSubspace.sSameSide_vadd_right_iff

theorem wOppSide_vadd_left_iff {s : AffineSubspace R P} {x y : P} {v : V} (hv : v ∈ s.direction) :
    s.WOppSide (v +ᵥ x) y ↔ s.WOppSide x y :=
  by
  constructor
  · rintro ⟨p₁, hp₁, p₂, hp₂, h⟩
    refine'
      ⟨-v +ᵥ p₁, AffineSubspace.vadd_mem_of_mem_direction (Submodule.neg_mem _ hv) hp₁, p₂, hp₂, _⟩
    rwa [vsub_vadd_eq_vsub_sub, sub_neg_eq_add, add_comm, ← vadd_vsub_assoc]
  · rintro ⟨p₁, hp₁, p₂, hp₂, h⟩
    refine' ⟨v +ᵥ p₁, AffineSubspace.vadd_mem_of_mem_direction hv hp₁, p₂, hp₂, _⟩
    rwa [vadd_vsub_vadd_cancel_left]
#align affine_subspace.w_opp_side_vadd_left_iff AffineSubspace.wOppSide_vadd_left_iff

theorem wOppSide_vadd_right_iff {s : AffineSubspace R P} {x y : P} {v : V} (hv : v ∈ s.direction) :
    s.WOppSide x (v +ᵥ y) ↔ s.WOppSide x y := by
  rw [w_opp_side_comm, w_opp_side_vadd_left_iff hv, w_opp_side_comm]
#align affine_subspace.w_opp_side_vadd_right_iff AffineSubspace.wOppSide_vadd_right_iff

theorem sOppSide_vadd_left_iff {s : AffineSubspace R P} {x y : P} {v : V} (hv : v ∈ s.direction) :
    s.SOppSide (v +ᵥ x) y ↔ s.SOppSide x y := by
  rw [s_opp_side, s_opp_side, w_opp_side_vadd_left_iff hv, vadd_mem_iff_mem_of_mem_direction hv]
#align affine_subspace.s_opp_side_vadd_left_iff AffineSubspace.sOppSide_vadd_left_iff

theorem sOppSide_vadd_right_iff {s : AffineSubspace R P} {x y : P} {v : V} (hv : v ∈ s.direction) :
    s.SOppSide x (v +ᵥ y) ↔ s.SOppSide x y := by
  rw [s_opp_side_comm, s_opp_side_vadd_left_iff hv, s_opp_side_comm]
#align affine_subspace.s_opp_side_vadd_right_iff AffineSubspace.sOppSide_vadd_right_iff

theorem wSameSide_smul_vsub_vadd_left {s : AffineSubspace R P} {p₁ p₂ : P} (x : P) (hp₁ : p₁ ∈ s)
    (hp₂ : p₂ ∈ s) {t : R} (ht : 0 ≤ t) : s.WSameSide (t • (x -ᵥ p₁) +ᵥ p₂) x :=
  by
  refine' ⟨p₂, hp₂, p₁, hp₁, _⟩
  rw [vadd_vsub]
  exact SameRay.sameRay_nonneg_smul_left _ ht
#align affine_subspace.w_same_side_smul_vsub_vadd_left AffineSubspace.wSameSide_smul_vsub_vadd_left

theorem wSameSide_smul_vsub_vadd_right {s : AffineSubspace R P} {p₁ p₂ : P} (x : P) (hp₁ : p₁ ∈ s)
    (hp₂ : p₂ ∈ s) {t : R} (ht : 0 ≤ t) : s.WSameSide x (t • (x -ᵥ p₁) +ᵥ p₂) :=
  (wSameSide_smul_vsub_vadd_left x hp₁ hp₂ ht).symm
#align affine_subspace.w_same_side_smul_vsub_vadd_right AffineSubspace.wSameSide_smul_vsub_vadd_right

theorem wSameSide_lineMap_left {s : AffineSubspace R P} {x : P} (y : P) (h : x ∈ s) {t : R}
    (ht : 0 ≤ t) : s.WSameSide (lineMap x y t) y :=
  wSameSide_smul_vsub_vadd_left y h h ht
#align affine_subspace.w_same_side_line_map_left AffineSubspace.wSameSide_lineMap_left

theorem wSameSide_lineMap_right {s : AffineSubspace R P} {x : P} (y : P) (h : x ∈ s) {t : R}
    (ht : 0 ≤ t) : s.WSameSide y (lineMap x y t) :=
  (wSameSide_lineMap_left y h ht).symm
#align affine_subspace.w_same_side_line_map_right AffineSubspace.wSameSide_lineMap_right

theorem wOppSide_smul_vsub_vadd_left {s : AffineSubspace R P} {p₁ p₂ : P} (x : P) (hp₁ : p₁ ∈ s)
    (hp₂ : p₂ ∈ s) {t : R} (ht : t ≤ 0) : s.WOppSide (t • (x -ᵥ p₁) +ᵥ p₂) x :=
  by
  refine' ⟨p₂, hp₂, p₁, hp₁, _⟩
  rw [vadd_vsub, ← neg_neg t, neg_smul, ← smul_neg, neg_vsub_eq_vsub_rev]
  exact SameRay.sameRay_nonneg_smul_left _ (neg_nonneg.2 ht)
#align affine_subspace.w_opp_side_smul_vsub_vadd_left AffineSubspace.wOppSide_smul_vsub_vadd_left

theorem wOppSide_smul_vsub_vadd_right {s : AffineSubspace R P} {p₁ p₂ : P} (x : P) (hp₁ : p₁ ∈ s)
    (hp₂ : p₂ ∈ s) {t : R} (ht : t ≤ 0) : s.WOppSide x (t • (x -ᵥ p₁) +ᵥ p₂) :=
  (wOppSide_smul_vsub_vadd_left x hp₁ hp₂ ht).symm
#align affine_subspace.w_opp_side_smul_vsub_vadd_right AffineSubspace.wOppSide_smul_vsub_vadd_right

theorem wOppSide_lineMap_left {s : AffineSubspace R P} {x : P} (y : P) (h : x ∈ s) {t : R}
    (ht : t ≤ 0) : s.WOppSide (lineMap x y t) y :=
  wOppSide_smul_vsub_vadd_left y h h ht
#align affine_subspace.w_opp_side_line_map_left AffineSubspace.wOppSide_lineMap_left

theorem wOppSide_lineMap_right {s : AffineSubspace R P} {x : P} (y : P) (h : x ∈ s) {t : R}
    (ht : t ≤ 0) : s.WOppSide y (lineMap x y t) :=
  (wOppSide_lineMap_left y h ht).symm
#align affine_subspace.w_opp_side_line_map_right AffineSubspace.wOppSide_lineMap_right

theorem Wbtw.w_same_side₂₃ {s : AffineSubspace R P} {x y z : P} (h : Wbtw R x y z) (hx : x ∈ s) :
    s.WSameSide y z := by
  rcases h with ⟨t, ⟨ht0, -⟩, rfl⟩
  exact w_same_side_line_map_left z hx ht0
#align wbtw.w_same_side₂₃ Wbtw.w_same_side₂₃

theorem Wbtw.w_same_side₃₂ {s : AffineSubspace R P} {x y z : P} (h : Wbtw R x y z) (hx : x ∈ s) :
    s.WSameSide z y :=
  (h.w_same_side₂₃ hx).symm
#align wbtw.w_same_side₃₂ Wbtw.w_same_side₃₂

theorem Wbtw.w_same_side₁₂ {s : AffineSubspace R P} {x y z : P} (h : Wbtw R x y z) (hz : z ∈ s) :
    s.WSameSide x y :=
  h.symm.w_same_side₃₂ hz
#align wbtw.w_same_side₁₂ Wbtw.w_same_side₁₂

theorem Wbtw.w_same_side₂₁ {s : AffineSubspace R P} {x y z : P} (h : Wbtw R x y z) (hz : z ∈ s) :
    s.WSameSide y x :=
  h.symm.w_same_side₂₃ hz
#align wbtw.w_same_side₂₁ Wbtw.w_same_side₂₁

theorem Wbtw.w_opp_side₁₃ {s : AffineSubspace R P} {x y z : P} (h : Wbtw R x y z) (hy : y ∈ s) :
    s.WOppSide x z := by
  rcases h with ⟨t, ⟨ht0, ht1⟩, rfl⟩
  refine' ⟨_, hy, _, hy, _⟩
  rcases ht1.lt_or_eq with (ht1' | rfl); swap; · simp
  rcases ht0.lt_or_eq with (ht0' | rfl); swap; · simp
  refine' Or.inr (Or.inr ⟨1 - t, t, sub_pos.2 ht1', ht0', _⟩)
  simp_rw [line_map_apply, vadd_vsub_assoc, vsub_vadd_eq_vsub_sub, ← neg_vsub_eq_vsub_rev z x,
    vsub_self, zero_sub, ← neg_one_smul R (z -ᵥ x), ← add_smul, smul_neg, ← neg_smul, smul_smul]
  ring_nf
#align wbtw.w_opp_side₁₃ Wbtw.w_opp_side₁₃

theorem Wbtw.w_opp_side₃₁ {s : AffineSubspace R P} {x y z : P} (h : Wbtw R x y z) (hy : y ∈ s) :
    s.WOppSide z x :=
  h.symm.w_opp_side₁₃ hy
#align wbtw.w_opp_side₃₁ Wbtw.w_opp_side₃₁

end StrictOrderedCommRing

section LinearOrderedField

variable [LinearOrderedField R] [AddCommGroup V] [Module R V] [AddTorsor V P]

variable [AddCommGroup V'] [Module R V'] [AddTorsor V' P']

include V

variable {R}

@[simp]
theorem wOppSide_self_iff {s : AffineSubspace R P} {x : P} : s.WOppSide x x ↔ x ∈ s :=
  by
  constructor
  · rintro ⟨p₁, hp₁, p₂, hp₂, h⟩
    obtain ⟨a, -, -, -, -, h₁, -⟩ := h.exists_eq_smul_add
    rw [add_comm, vsub_add_vsub_cancel, ← eq_vadd_iff_vsub_eq] at h₁
    rw [h₁]
    exact s.smul_vsub_vadd_mem a hp₂ hp₁ hp₁
  · exact fun h => ⟨x, h, x, h, SameRay.rfl⟩
#align affine_subspace.w_opp_side_self_iff AffineSubspace.wOppSide_self_iff

theorem not_sOppSide_self (s : AffineSubspace R P) (x : P) : ¬s.SOppSide x x := by simp [s_opp_side]
#align affine_subspace.not_s_opp_side_self AffineSubspace.not_sOppSide_self

theorem wSameSide_iff_exists_left {s : AffineSubspace R P} {x y p₁ : P} (h : p₁ ∈ s) :
    s.WSameSide x y ↔ x ∈ s ∨ ∃ p₂ ∈ s, SameRay R (x -ᵥ p₁) (y -ᵥ p₂) :=
  by
  constructor
  · rintro ⟨p₁', hp₁', p₂', hp₂', h0 | h0 | ⟨r₁, r₂, hr₁, hr₂, hr⟩⟩
    · rw [vsub_eq_zero_iff_eq] at h0
      rw [h0]
      exact Or.inl hp₁'
    · refine' Or.inr ⟨p₂', hp₂', _⟩
      rw [h0]
      exact SameRay.zero_right _
    · refine'
        Or.inr
          ⟨(r₁ / r₂) • (p₁ -ᵥ p₁') +ᵥ p₂', s.smul_vsub_vadd_mem _ h hp₁' hp₂',
            Or.inr (Or.inr ⟨r₁, r₂, hr₁, hr₂, _⟩)⟩
      rw [vsub_vadd_eq_vsub_sub, smul_sub, ← hr, smul_smul, mul_div_cancel' _ hr₂.ne.symm, ←
        smul_sub, vsub_sub_vsub_cancel_right]
  · rintro (h' | h')
    · exact w_same_side_of_left_mem y h'
    · exact ⟨p₁, h, h'⟩
#align affine_subspace.w_same_side_iff_exists_left AffineSubspace.wSameSide_iff_exists_left

theorem wSameSide_iff_exists_right {s : AffineSubspace R P} {x y p₂ : P} (h : p₂ ∈ s) :
    s.WSameSide x y ↔ y ∈ s ∨ ∃ p₁ ∈ s, SameRay R (x -ᵥ p₁) (y -ᵥ p₂) :=
  by
  rw [w_same_side_comm, w_same_side_iff_exists_left h]
  simp_rw [SameRay.sameRay_comm]
#align affine_subspace.w_same_side_iff_exists_right AffineSubspace.wSameSide_iff_exists_right

theorem sSameSide_iff_exists_left {s : AffineSubspace R P} {x y p₁ : P} (h : p₁ ∈ s) :
    s.SSameSide x y ↔ x ∉ s ∧ y ∉ s ∧ ∃ p₂ ∈ s, SameRay R (x -ᵥ p₁) (y -ᵥ p₂) :=
  by
  rw [s_same_side, and_comm', w_same_side_iff_exists_left h, and_assoc', and_congr_right_iff]
  intro hx
  rw [or_iff_right hx]
#align affine_subspace.s_same_side_iff_exists_left AffineSubspace.sSameSide_iff_exists_left

theorem sSameSide_iff_exists_right {s : AffineSubspace R P} {x y p₂ : P} (h : p₂ ∈ s) :
    s.SSameSide x y ↔ x ∉ s ∧ y ∉ s ∧ ∃ p₁ ∈ s, SameRay R (x -ᵥ p₁) (y -ᵥ p₂) :=
  by
  rw [s_same_side_comm, s_same_side_iff_exists_left h, ← and_assoc', and_comm' (y ∉ s), and_assoc']
  simp_rw [SameRay.sameRay_comm]
#align affine_subspace.s_same_side_iff_exists_right AffineSubspace.sSameSide_iff_exists_right

theorem wOppSide_iff_exists_left {s : AffineSubspace R P} {x y p₁ : P} (h : p₁ ∈ s) :
    s.WOppSide x y ↔ x ∈ s ∨ ∃ p₂ ∈ s, SameRay R (x -ᵥ p₁) (p₂ -ᵥ y) :=
  by
  constructor
  · rintro ⟨p₁', hp₁', p₂', hp₂', h0 | h0 | ⟨r₁, r₂, hr₁, hr₂, hr⟩⟩
    · rw [vsub_eq_zero_iff_eq] at h0
      rw [h0]
      exact Or.inl hp₁'
    · refine' Or.inr ⟨p₂', hp₂', _⟩
      rw [h0]
      exact SameRay.zero_right _
    · refine'
        Or.inr
          ⟨(-r₁ / r₂) • (p₁ -ᵥ p₁') +ᵥ p₂', s.smul_vsub_vadd_mem _ h hp₁' hp₂',
            Or.inr (Or.inr ⟨r₁, r₂, hr₁, hr₂, _⟩)⟩
      rw [vadd_vsub_assoc, smul_add, ← hr, smul_smul, neg_div, mul_neg,
        mul_div_cancel' _ hr₂.ne.symm, neg_smul, neg_add_eq_sub, ← smul_sub,
        vsub_sub_vsub_cancel_right]
  · rintro (h' | h')
    · exact w_opp_side_of_left_mem y h'
    · exact ⟨p₁, h, h'⟩
#align affine_subspace.w_opp_side_iff_exists_left AffineSubspace.wOppSide_iff_exists_left

theorem wOppSide_iff_exists_right {s : AffineSubspace R P} {x y p₂ : P} (h : p₂ ∈ s) :
    s.WOppSide x y ↔ y ∈ s ∨ ∃ p₁ ∈ s, SameRay R (x -ᵥ p₁) (p₂ -ᵥ y) :=
  by
  rw [w_opp_side_comm, w_opp_side_iff_exists_left h]
  constructor
  · rintro (hy | ⟨p, hp, hr⟩)
    · exact Or.inl hy
    refine' Or.inr ⟨p, hp, _⟩
    rwa [SameRay.sameRay_comm, ← sameRay_neg_iff, neg_vsub_eq_vsub_rev, neg_vsub_eq_vsub_rev]
  · rintro (hy | ⟨p, hp, hr⟩)
    · exact Or.inl hy
    refine' Or.inr ⟨p, hp, _⟩
    rwa [SameRay.sameRay_comm, ← sameRay_neg_iff, neg_vsub_eq_vsub_rev, neg_vsub_eq_vsub_rev]
#align affine_subspace.w_opp_side_iff_exists_right AffineSubspace.wOppSide_iff_exists_right

theorem sOppSide_iff_exists_left {s : AffineSubspace R P} {x y p₁ : P} (h : p₁ ∈ s) :
    s.SOppSide x y ↔ x ∉ s ∧ y ∉ s ∧ ∃ p₂ ∈ s, SameRay R (x -ᵥ p₁) (p₂ -ᵥ y) :=
  by
  rw [s_opp_side, and_comm', w_opp_side_iff_exists_left h, and_assoc', and_congr_right_iff]
  intro hx
  rw [or_iff_right hx]
#align affine_subspace.s_opp_side_iff_exists_left AffineSubspace.sOppSide_iff_exists_left

theorem sOppSide_iff_exists_right {s : AffineSubspace R P} {x y p₂ : P} (h : p₂ ∈ s) :
    s.SOppSide x y ↔ x ∉ s ∧ y ∉ s ∧ ∃ p₁ ∈ s, SameRay R (x -ᵥ p₁) (p₂ -ᵥ y) :=
  by
  rw [s_opp_side, and_comm', w_opp_side_iff_exists_right h, and_assoc', and_congr_right_iff,
    and_congr_right_iff]
  rintro hx hy
  rw [or_iff_right hy]
#align affine_subspace.s_opp_side_iff_exists_right AffineSubspace.sOppSide_iff_exists_right

theorem WSameSide.trans {s : AffineSubspace R P} {x y z : P} (hxy : s.WSameSide x y)
    (hyz : s.WSameSide y z) (hy : y ∉ s) : s.WSameSide x z :=
  by
  rcases hxy with ⟨p₁, hp₁, p₂, hp₂, hxy⟩
  rw [w_same_side_iff_exists_left hp₂, or_iff_right hy] at hyz
  rcases hyz with ⟨p₃, hp₃, hyz⟩
  refine' ⟨p₁, hp₁, p₃, hp₃, hxy.trans hyz _⟩
  refine' fun h => False.elim _
  rw [vsub_eq_zero_iff_eq] at h
  exact hy (h.symm ▸ hp₂)
#align affine_subspace.w_same_side.trans AffineSubspace.WSameSide.trans

theorem WSameSide.trans_sSameSide {s : AffineSubspace R P} {x y z : P} (hxy : s.WSameSide x y)
    (hyz : s.SSameSide y z) : s.WSameSide x z :=
  hxy.trans hyz.1 hyz.2.1
#align affine_subspace.w_same_side.trans_s_same_side AffineSubspace.WSameSide.trans_sSameSide

theorem WSameSide.trans_wOppSide {s : AffineSubspace R P} {x y z : P} (hxy : s.WSameSide x y)
    (hyz : s.WOppSide y z) (hy : y ∉ s) : s.WOppSide x z :=
  by
  rcases hxy with ⟨p₁, hp₁, p₂, hp₂, hxy⟩
  rw [w_opp_side_iff_exists_left hp₂, or_iff_right hy] at hyz
  rcases hyz with ⟨p₃, hp₃, hyz⟩
  refine' ⟨p₁, hp₁, p₃, hp₃, hxy.trans hyz _⟩
  refine' fun h => False.elim _
  rw [vsub_eq_zero_iff_eq] at h
  exact hy (h.symm ▸ hp₂)
#align affine_subspace.w_same_side.trans_w_opp_side AffineSubspace.WSameSide.trans_wOppSide

theorem WSameSide.trans_sOppSide {s : AffineSubspace R P} {x y z : P} (hxy : s.WSameSide x y)
    (hyz : s.SOppSide y z) : s.WOppSide x z :=
  hxy.trans_wOppSide hyz.1 hyz.2.1
#align affine_subspace.w_same_side.trans_s_opp_side AffineSubspace.WSameSide.trans_sOppSide

theorem SSameSide.trans_wSameSide {s : AffineSubspace R P} {x y z : P} (hxy : s.SSameSide x y)
    (hyz : s.WSameSide y z) : s.WSameSide x z :=
  (hyz.symm.trans_sSameSide hxy.symm).symm
#align affine_subspace.s_same_side.trans_w_same_side AffineSubspace.SSameSide.trans_wSameSide

theorem SSameSide.trans {s : AffineSubspace R P} {x y z : P} (hxy : s.SSameSide x y)
    (hyz : s.SSameSide y z) : s.SSameSide x z :=
  ⟨hxy.WSameSide.trans_sSameSide hyz, hxy.2.1, hyz.2.2⟩
#align affine_subspace.s_same_side.trans AffineSubspace.SSameSide.trans

theorem SSameSide.trans_wOppSide {s : AffineSubspace R P} {x y z : P} (hxy : s.SSameSide x y)
    (hyz : s.WOppSide y z) : s.WOppSide x z :=
  hxy.WSameSide.trans_wOppSide hyz hxy.2.2
#align affine_subspace.s_same_side.trans_w_opp_side AffineSubspace.SSameSide.trans_wOppSide

theorem SSameSide.trans_sOppSide {s : AffineSubspace R P} {x y z : P} (hxy : s.SSameSide x y)
    (hyz : s.SOppSide y z) : s.SOppSide x z :=
  ⟨hxy.trans_wOppSide hyz.1, hxy.2.1, hyz.2.2⟩
#align affine_subspace.s_same_side.trans_s_opp_side AffineSubspace.SSameSide.trans_sOppSide

theorem WOppSide.trans_wSameSide {s : AffineSubspace R P} {x y z : P} (hxy : s.WOppSide x y)
    (hyz : s.WSameSide y z) (hy : y ∉ s) : s.WOppSide x z :=
  (hyz.symm.trans_wOppSide hxy.symm hy).symm
#align affine_subspace.w_opp_side.trans_w_same_side AffineSubspace.WOppSide.trans_wSameSide

theorem WOppSide.trans_sSameSide {s : AffineSubspace R P} {x y z : P} (hxy : s.WOppSide x y)
    (hyz : s.SSameSide y z) : s.WOppSide x z :=
  hxy.trans_wSameSide hyz.1 hyz.2.1
#align affine_subspace.w_opp_side.trans_s_same_side AffineSubspace.WOppSide.trans_sSameSide

theorem WOppSide.trans {s : AffineSubspace R P} {x y z : P} (hxy : s.WOppSide x y)
    (hyz : s.WOppSide y z) (hy : y ∉ s) : s.WSameSide x z :=
  by
  rcases hxy with ⟨p₁, hp₁, p₂, hp₂, hxy⟩
  rw [w_opp_side_iff_exists_left hp₂, or_iff_right hy] at hyz
  rcases hyz with ⟨p₃, hp₃, hyz⟩
  rw [← sameRay_neg_iff, neg_vsub_eq_vsub_rev, neg_vsub_eq_vsub_rev] at hyz
  refine' ⟨p₁, hp₁, p₃, hp₃, hxy.trans hyz _⟩
  refine' fun h => False.elim _
  rw [vsub_eq_zero_iff_eq] at h
  exact hy (h ▸ hp₂)
#align affine_subspace.w_opp_side.trans AffineSubspace.WOppSide.trans

theorem WOppSide.trans_sOppSide {s : AffineSubspace R P} {x y z : P} (hxy : s.WOppSide x y)
    (hyz : s.SOppSide y z) : s.WSameSide x z :=
  hxy.trans hyz.1 hyz.2.1
#align affine_subspace.w_opp_side.trans_s_opp_side AffineSubspace.WOppSide.trans_sOppSide

theorem SOppSide.trans_wSameSide {s : AffineSubspace R P} {x y z : P} (hxy : s.SOppSide x y)
    (hyz : s.WSameSide y z) : s.WOppSide x z :=
  (hyz.symm.trans_sOppSide hxy.symm).symm
#align affine_subspace.s_opp_side.trans_w_same_side AffineSubspace.SOppSide.trans_wSameSide

theorem SOppSide.trans_sSameSide {s : AffineSubspace R P} {x y z : P} (hxy : s.SOppSide x y)
    (hyz : s.SSameSide y z) : s.SOppSide x z :=
  (hyz.symm.trans_sOppSide hxy.symm).symm
#align affine_subspace.s_opp_side.trans_s_same_side AffineSubspace.SOppSide.trans_sSameSide

theorem SOppSide.trans_wOppSide {s : AffineSubspace R P} {x y z : P} (hxy : s.SOppSide x y)
    (hyz : s.WOppSide y z) : s.WSameSide x z :=
  (hyz.symm.trans_sOppSide hxy.symm).symm
#align affine_subspace.s_opp_side.trans_w_opp_side AffineSubspace.SOppSide.trans_wOppSide

theorem SOppSide.trans {s : AffineSubspace R P} {x y z : P} (hxy : s.SOppSide x y)
    (hyz : s.SOppSide y z) : s.SSameSide x z :=
  ⟨hxy.trans_wOppSide hyz.1, hxy.2.1, hyz.2.2⟩
#align affine_subspace.s_opp_side.trans AffineSubspace.SOppSide.trans

theorem wSameSide_and_wOppSide_iff {s : AffineSubspace R P} {x y : P} :
    s.WSameSide x y ∧ s.WOppSide x y ↔ x ∈ s ∨ y ∈ s :=
  by
  constructor
  · rintro ⟨hs, ho⟩
    rw [w_opp_side_comm] at ho
    by_contra h
    rw [not_or] at h
    exact h.1 (w_opp_side_self_iff.1 (hs.trans_w_opp_side ho h.2))
  · rintro (h | h)
    · exact ⟨w_same_side_of_left_mem y h, w_opp_side_of_left_mem y h⟩
    · exact ⟨w_same_side_of_right_mem x h, w_opp_side_of_right_mem x h⟩
#align affine_subspace.w_same_side_and_w_opp_side_iff AffineSubspace.wSameSide_and_wOppSide_iff

theorem WSameSide.not_sOppSide {s : AffineSubspace R P} {x y : P} (h : s.WSameSide x y) :
    ¬s.SOppSide x y := by
  intro ho
  have hxy := w_same_side_and_w_opp_side_iff.1 ⟨h, ho.1⟩
  rcases hxy with (hx | hy)
  · exact ho.2.1 hx
  · exact ho.2.2 hy
#align affine_subspace.w_same_side.not_s_opp_side AffineSubspace.WSameSide.not_sOppSide

theorem SSameSide.not_wOppSide {s : AffineSubspace R P} {x y : P} (h : s.SSameSide x y) :
    ¬s.WOppSide x y := by
  intro ho
  have hxy := w_same_side_and_w_opp_side_iff.1 ⟨h.1, ho⟩
  rcases hxy with (hx | hy)
  · exact h.2.1 hx
  · exact h.2.2 hy
#align affine_subspace.s_same_side.not_w_opp_side AffineSubspace.SSameSide.not_wOppSide

theorem SSameSide.not_sOppSide {s : AffineSubspace R P} {x y : P} (h : s.SSameSide x y) :
    ¬s.SOppSide x y := fun ho => h.not_wOppSide ho.1
#align affine_subspace.s_same_side.not_s_opp_side AffineSubspace.SSameSide.not_sOppSide

theorem WOppSide.not_sSameSide {s : AffineSubspace R P} {x y : P} (h : s.WOppSide x y) :
    ¬s.SSameSide x y := fun hs => hs.not_wOppSide h
#align affine_subspace.w_opp_side.not_s_same_side AffineSubspace.WOppSide.not_sSameSide

theorem SOppSide.not_wSameSide {s : AffineSubspace R P} {x y : P} (h : s.SOppSide x y) :
    ¬s.WSameSide x y := fun hs => hs.not_sOppSide h
#align affine_subspace.s_opp_side.not_w_same_side AffineSubspace.SOppSide.not_wSameSide

theorem SOppSide.not_sSameSide {s : AffineSubspace R P} {x y : P} (h : s.SOppSide x y) :
    ¬s.SSameSide x y := fun hs => h.not_wSameSide hs.1
#align affine_subspace.s_opp_side.not_s_same_side AffineSubspace.SOppSide.not_sSameSide

theorem wOppSide_iff_exists_wbtw {s : AffineSubspace R P} {x y : P} :
    s.WOppSide x y ↔ ∃ p ∈ s, Wbtw R x p y :=
  by
  refine' ⟨fun h => _, fun ⟨p, hp, h⟩ => h.w_opp_side₁₃ hp⟩
  rcases h with ⟨p₁, hp₁, p₂, hp₂, h | h | ⟨r₁, r₂, hr₁, hr₂, h⟩⟩
  · rw [vsub_eq_zero_iff_eq] at h
    rw [h]
    exact ⟨p₁, hp₁, wbtw_self_left _ _ _⟩
  · rw [vsub_eq_zero_iff_eq] at h
    rw [← h]
    exact ⟨p₂, hp₂, wbtw_self_right _ _ _⟩
  · refine' ⟨line_map x y (r₂ / (r₁ + r₂)), _, _⟩
    · rw [line_map_apply, ← vsub_vadd x p₁, ← vsub_vadd y p₂, vsub_vadd_eq_vsub_sub,
        vadd_vsub_assoc, ← vadd_assoc, vadd_eq_add]
      convert s.smul_vsub_vadd_mem (r₂ / (r₁ + r₂)) hp₂ hp₁ hp₁
      rw [add_comm (y -ᵥ p₂), smul_sub, smul_add, add_sub_assoc, add_assoc, add_right_eq_self,
        div_eq_inv_mul, ← neg_vsub_eq_vsub_rev, smul_neg, ← smul_smul, ← h, smul_smul, ← neg_smul, ←
        sub_smul, ← div_eq_inv_mul, ← div_eq_inv_mul, ← neg_div, ← sub_div, sub_eq_add_neg, ←
        neg_add, neg_div, div_self (Left.add_pos hr₁ hr₂).Ne.symm, neg_one_smul, neg_add_self]
    ·
      exact
        Set.mem_image_of_mem _
          ⟨div_nonneg hr₂.le (Left.add_pos hr₁ hr₂).le,
            div_le_one_of_le (le_add_of_nonneg_left hr₁.le) (Left.add_pos hr₁ hr₂).le⟩
#align affine_subspace.w_opp_side_iff_exists_wbtw AffineSubspace.wOppSide_iff_exists_wbtw

theorem SOppSide.exists_sbtw {s : AffineSubspace R P} {x y : P} (h : s.SOppSide x y) :
    ∃ p ∈ s, Sbtw R x p y :=
  by
  obtain ⟨p, hp, hw⟩ := w_opp_side_iff_exists_wbtw.1 h.w_opp_side
  refine' ⟨p, hp, hw, _, _⟩
  · rintro rfl
    exact h.2.1 hp
  · rintro rfl
    exact h.2.2 hp
#align affine_subspace.s_opp_side.exists_sbtw AffineSubspace.SOppSide.exists_sbtw

theorem Sbtw.sOppSide_of_not_mem_of_mem {s : AffineSubspace R P} {x y z : P} (h : Sbtw R x y z)
    (hx : x ∉ s) (hy : y ∈ s) : s.SOppSide x z :=
  by
  refine' ⟨h.wbtw.w_opp_side₁₃ hy, hx, fun hz => hx _⟩
  rcases h with ⟨⟨t, ⟨ht0, ht1⟩, rfl⟩, hyx, hyz⟩
  rw [line_map_apply] at hy
  have ht : t ≠ 1 := by
    rintro rfl
    simpa [line_map_apply] using hyz
  have hy' := vsub_mem_direction hy hz
  rw [vadd_vsub_assoc, ← neg_vsub_eq_vsub_rev z, ← neg_one_smul R (z -ᵥ x), ← add_smul, ←
    sub_eq_add_neg, s.direction.smul_mem_iff (sub_ne_zero_of_ne ht)] at hy'
  rwa [vadd_mem_iff_mem_of_mem_direction (Submodule.smul_mem _ _ hy')] at hy
#align sbtw.s_opp_side_of_not_mem_of_mem Sbtw.sOppSide_of_not_mem_of_mem

theorem sSameSide_smul_vsub_vadd_left {s : AffineSubspace R P} {x p₁ p₂ : P} (hx : x ∉ s)
    (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) {t : R} (ht : 0 < t) : s.SSameSide (t • (x -ᵥ p₁) +ᵥ p₂) x :=
  by
  refine' ⟨w_same_side_smul_vsub_vadd_left x hp₁ hp₂ ht.le, fun h => hx _, hx⟩
  rwa [vadd_mem_iff_mem_direction _ hp₂, s.direction.smul_mem_iff ht.ne.symm,
    vsub_right_mem_direction_iff_mem hp₁] at h
#align affine_subspace.s_same_side_smul_vsub_vadd_left AffineSubspace.sSameSide_smul_vsub_vadd_left

theorem sSameSide_smul_vsub_vadd_right {s : AffineSubspace R P} {x p₁ p₂ : P} (hx : x ∉ s)
    (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) {t : R} (ht : 0 < t) : s.SSameSide x (t • (x -ᵥ p₁) +ᵥ p₂) :=
  (sSameSide_smul_vsub_vadd_left hx hp₁ hp₂ ht).symm
#align affine_subspace.s_same_side_smul_vsub_vadd_right AffineSubspace.sSameSide_smul_vsub_vadd_right

theorem sSameSide_lineMap_left {s : AffineSubspace R P} {x y : P} (hx : x ∈ s) (hy : y ∉ s) {t : R}
    (ht : 0 < t) : s.SSameSide (lineMap x y t) y :=
  sSameSide_smul_vsub_vadd_left hy hx hx ht
#align affine_subspace.s_same_side_line_map_left AffineSubspace.sSameSide_lineMap_left

theorem sSameSide_lineMap_right {s : AffineSubspace R P} {x y : P} (hx : x ∈ s) (hy : y ∉ s) {t : R}
    (ht : 0 < t) : s.SSameSide y (lineMap x y t) :=
  (sSameSide_lineMap_left hx hy ht).symm
#align affine_subspace.s_same_side_line_map_right AffineSubspace.sSameSide_lineMap_right

theorem sOppSide_smul_vsub_vadd_left {s : AffineSubspace R P} {x p₁ p₂ : P} (hx : x ∉ s)
    (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) {t : R} (ht : t < 0) : s.SOppSide (t • (x -ᵥ p₁) +ᵥ p₂) x :=
  by
  refine' ⟨w_opp_side_smul_vsub_vadd_left x hp₁ hp₂ ht.le, fun h => hx _, hx⟩
  rwa [vadd_mem_iff_mem_direction _ hp₂, s.direction.smul_mem_iff ht.ne,
    vsub_right_mem_direction_iff_mem hp₁] at h
#align affine_subspace.s_opp_side_smul_vsub_vadd_left AffineSubspace.sOppSide_smul_vsub_vadd_left

theorem sOppSide_smul_vsub_vadd_right {s : AffineSubspace R P} {x p₁ p₂ : P} (hx : x ∉ s)
    (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) {t : R} (ht : t < 0) : s.SOppSide x (t • (x -ᵥ p₁) +ᵥ p₂) :=
  (sOppSide_smul_vsub_vadd_left hx hp₁ hp₂ ht).symm
#align affine_subspace.s_opp_side_smul_vsub_vadd_right AffineSubspace.sOppSide_smul_vsub_vadd_right

theorem sOppSide_lineMap_left {s : AffineSubspace R P} {x y : P} (hx : x ∈ s) (hy : y ∉ s) {t : R}
    (ht : t < 0) : s.SOppSide (lineMap x y t) y :=
  sOppSide_smul_vsub_vadd_left hy hx hx ht
#align affine_subspace.s_opp_side_line_map_left AffineSubspace.sOppSide_lineMap_left

theorem sOppSide_lineMap_right {s : AffineSubspace R P} {x y : P} (hx : x ∈ s) (hy : y ∉ s) {t : R}
    (ht : t < 0) : s.SOppSide y (lineMap x y t) :=
  (sOppSide_lineMap_left hx hy ht).symm
#align affine_subspace.s_opp_side_line_map_right AffineSubspace.sOppSide_lineMap_right

theorem setOf_wSameSide_eq_image2 {s : AffineSubspace R P} {x p : P} (hx : x ∉ s) (hp : p ∈ s) :
    { y | s.WSameSide x y } = Set.image2 (fun (t : R) q => t • (x -ᵥ p) +ᵥ q) (Set.Ici 0) s :=
  by
  ext y
  simp_rw [Set.mem_setOf, Set.mem_image2, Set.mem_Ici, mem_coe]
  constructor
  · rw [w_same_side_iff_exists_left hp, or_iff_right hx]
    rintro ⟨p₂, hp₂, h | h | ⟨r₁, r₂, hr₁, hr₂, h⟩⟩
    · rw [vsub_eq_zero_iff_eq] at h
      exact False.elim (hx (h.symm ▸ hp))
    · rw [vsub_eq_zero_iff_eq] at h
      refine' ⟨0, p₂, le_refl _, hp₂, _⟩
      simp [h]
    · refine' ⟨r₁ / r₂, p₂, (div_pos hr₁ hr₂).le, hp₂, _⟩
      rw [div_eq_inv_mul, ← smul_smul, h, smul_smul, inv_mul_cancel hr₂.ne.symm, one_smul,
        vsub_vadd]
  · rintro ⟨t, p', ht, hp', rfl⟩
    exact w_same_side_smul_vsub_vadd_right x hp hp' ht
#align affine_subspace.set_of_w_same_side_eq_image2 AffineSubspace.setOf_wSameSide_eq_image2

theorem setOf_sSameSide_eq_image2 {s : AffineSubspace R P} {x p : P} (hx : x ∉ s) (hp : p ∈ s) :
    { y | s.SSameSide x y } = Set.image2 (fun (t : R) q => t • (x -ᵥ p) +ᵥ q) (Set.Ioi 0) s :=
  by
  ext y
  simp_rw [Set.mem_setOf, Set.mem_image2, Set.mem_Ioi, mem_coe]
  constructor
  · rw [s_same_side_iff_exists_left hp]
    rintro ⟨-, hy, p₂, hp₂, h | h | ⟨r₁, r₂, hr₁, hr₂, h⟩⟩
    · rw [vsub_eq_zero_iff_eq] at h
      exact False.elim (hx (h.symm ▸ hp))
    · rw [vsub_eq_zero_iff_eq] at h
      exact False.elim (hy (h.symm ▸ hp₂))
    · refine' ⟨r₁ / r₂, p₂, div_pos hr₁ hr₂, hp₂, _⟩
      rw [div_eq_inv_mul, ← smul_smul, h, smul_smul, inv_mul_cancel hr₂.ne.symm, one_smul,
        vsub_vadd]
  · rintro ⟨t, p', ht, hp', rfl⟩
    exact s_same_side_smul_vsub_vadd_right hx hp hp' ht
#align affine_subspace.set_of_s_same_side_eq_image2 AffineSubspace.setOf_sSameSide_eq_image2

theorem setOf_wOppSide_eq_image2 {s : AffineSubspace R P} {x p : P} (hx : x ∉ s) (hp : p ∈ s) :
    { y | s.WOppSide x y } = Set.image2 (fun (t : R) q => t • (x -ᵥ p) +ᵥ q) (Set.Iic 0) s :=
  by
  ext y
  simp_rw [Set.mem_setOf, Set.mem_image2, Set.mem_Iic, mem_coe]
  constructor
  · rw [w_opp_side_iff_exists_left hp, or_iff_right hx]
    rintro ⟨p₂, hp₂, h | h | ⟨r₁, r₂, hr₁, hr₂, h⟩⟩
    · rw [vsub_eq_zero_iff_eq] at h
      exact False.elim (hx (h.symm ▸ hp))
    · rw [vsub_eq_zero_iff_eq] at h
      refine' ⟨0, p₂, le_refl _, hp₂, _⟩
      simp [h]
    · refine' ⟨-r₁ / r₂, p₂, (div_neg_of_neg_of_pos (Left.neg_neg_iff.2 hr₁) hr₂).le, hp₂, _⟩
      rw [div_eq_inv_mul, ← smul_smul, neg_smul, h, smul_neg, smul_smul, inv_mul_cancel hr₂.ne.symm,
        one_smul, neg_vsub_eq_vsub_rev, vsub_vadd]
  · rintro ⟨t, p', ht, hp', rfl⟩
    exact w_opp_side_smul_vsub_vadd_right x hp hp' ht
#align affine_subspace.set_of_w_opp_side_eq_image2 AffineSubspace.setOf_wOppSide_eq_image2

theorem setOf_sOppSide_eq_image2 {s : AffineSubspace R P} {x p : P} (hx : x ∉ s) (hp : p ∈ s) :
    { y | s.SOppSide x y } = Set.image2 (fun (t : R) q => t • (x -ᵥ p) +ᵥ q) (Set.Iio 0) s :=
  by
  ext y
  simp_rw [Set.mem_setOf, Set.mem_image2, Set.mem_Iio, mem_coe]
  constructor
  · rw [s_opp_side_iff_exists_left hp]
    rintro ⟨-, hy, p₂, hp₂, h | h | ⟨r₁, r₂, hr₁, hr₂, h⟩⟩
    · rw [vsub_eq_zero_iff_eq] at h
      exact False.elim (hx (h.symm ▸ hp))
    · rw [vsub_eq_zero_iff_eq] at h
      exact False.elim (hy (h ▸ hp₂))
    · refine' ⟨-r₁ / r₂, p₂, div_neg_of_neg_of_pos (Left.neg_neg_iff.2 hr₁) hr₂, hp₂, _⟩
      rw [div_eq_inv_mul, ← smul_smul, neg_smul, h, smul_neg, smul_smul, inv_mul_cancel hr₂.ne.symm,
        one_smul, neg_vsub_eq_vsub_rev, vsub_vadd]
  · rintro ⟨t, p', ht, hp', rfl⟩
    exact s_opp_side_smul_vsub_vadd_right hx hp hp' ht
#align affine_subspace.set_of_s_opp_side_eq_image2 AffineSubspace.setOf_sOppSide_eq_image2

theorem wOppSide_pointReflection {s : AffineSubspace R P} {x : P} (y : P) (hx : x ∈ s) :
    s.WOppSide y (pointReflection R x y) :=
  (wbtw_pointReflection R _ _).w_opp_side₁₃ hx
#align affine_subspace.w_opp_side_point_reflection AffineSubspace.wOppSide_pointReflection

theorem sOppSide_pointReflection {s : AffineSubspace R P} {x y : P} (hx : x ∈ s) (hy : y ∉ s) :
    s.SOppSide y (pointReflection R x y) :=
  by
  refine' (sbtw_pointReflection_of_ne R fun h => hy _).sOppSide_of_not_mem_of_mem hy hx
  rwa [← h]
#align affine_subspace.s_opp_side_point_reflection AffineSubspace.sOppSide_pointReflection

end LinearOrderedField

section Normed

variable [SeminormedAddCommGroup V] [NormedSpace ℝ V] [PseudoMetricSpace P]

variable [NormedAddTorsor V P]

include V

theorem isConnected_setOf_wSameSide {s : AffineSubspace ℝ P} (x : P) (h : (s : Set P).Nonempty) :
    IsConnected { y | s.WSameSide x y } :=
  by
  obtain ⟨p, hp⟩ := h
  haveI : Nonempty s := ⟨⟨p, hp⟩⟩
  by_cases hx : x ∈ s
  · convert isConnected_univ
    · simp [w_same_side_of_left_mem, hx]
    · exact AddTorsor.connectedSpace V P
  · rw [set_of_w_same_side_eq_image2 hx hp, ← Set.image_prod]
    refine'
      (is_connected_Ici.prod (isConnected_iff_connectedSpace.2 _)).image _
        ((continuous_fst.smul continuous_const).vadd continuous_snd).ContinuousOn
    convert AddTorsor.connectedSpace s.direction s
#align affine_subspace.is_connected_set_of_w_same_side AffineSubspace.isConnected_setOf_wSameSide

theorem isPreconnected_setOf_wSameSide (s : AffineSubspace ℝ P) (x : P) :
    IsPreconnected { y | s.WSameSide x y } :=
  by
  rcases Set.eq_empty_or_nonempty (s : Set P) with (h | h)
  · convert isPreconnected_empty
    rw [coe_eq_bot_iff] at h
    simp only [h, not_w_same_side_bot]
    rfl
  · exact (is_connected_set_of_w_same_side x h).IsPreconnected
#align affine_subspace.is_preconnected_set_of_w_same_side AffineSubspace.isPreconnected_setOf_wSameSide

theorem isConnected_setOf_sSameSide {s : AffineSubspace ℝ P} {x : P} (hx : x ∉ s)
    (h : (s : Set P).Nonempty) : IsConnected { y | s.SSameSide x y } :=
  by
  obtain ⟨p, hp⟩ := h
  haveI : Nonempty s := ⟨⟨p, hp⟩⟩
  rw [set_of_s_same_side_eq_image2 hx hp, ← Set.image_prod]
  refine'
    (is_connected_Ioi.prod (isConnected_iff_connectedSpace.2 _)).image _
      ((continuous_fst.smul continuous_const).vadd continuous_snd).ContinuousOn
  convert AddTorsor.connectedSpace s.direction s
#align affine_subspace.is_connected_set_of_s_same_side AffineSubspace.isConnected_setOf_sSameSide

theorem isPreconnected_setOf_sSameSide (s : AffineSubspace ℝ P) (x : P) :
    IsPreconnected { y | s.SSameSide x y } :=
  by
  rcases Set.eq_empty_or_nonempty (s : Set P) with (h | h)
  · convert isPreconnected_empty
    rw [coe_eq_bot_iff] at h
    simp only [h, not_s_same_side_bot]
    rfl
  · by_cases hx : x ∈ s
    · convert isPreconnected_empty
      simp only [hx, s_same_side, not_true, false_and_iff, and_false_iff]
      rfl
    · exact (is_connected_set_of_s_same_side hx h).IsPreconnected
#align affine_subspace.is_preconnected_set_of_s_same_side AffineSubspace.isPreconnected_setOf_sSameSide

theorem isConnected_setOf_wOppSide {s : AffineSubspace ℝ P} (x : P) (h : (s : Set P).Nonempty) :
    IsConnected { y | s.WOppSide x y } :=
  by
  obtain ⟨p, hp⟩ := h
  haveI : Nonempty s := ⟨⟨p, hp⟩⟩
  by_cases hx : x ∈ s
  · convert isConnected_univ
    · simp [w_opp_side_of_left_mem, hx]
    · exact AddTorsor.connectedSpace V P
  · rw [set_of_w_opp_side_eq_image2 hx hp, ← Set.image_prod]
    refine'
      (is_connected_Iic.prod (isConnected_iff_connectedSpace.2 _)).image _
        ((continuous_fst.smul continuous_const).vadd continuous_snd).ContinuousOn
    convert AddTorsor.connectedSpace s.direction s
#align affine_subspace.is_connected_set_of_w_opp_side AffineSubspace.isConnected_setOf_wOppSide

theorem isPreconnected_setOf_wOppSide (s : AffineSubspace ℝ P) (x : P) :
    IsPreconnected { y | s.WOppSide x y } :=
  by
  rcases Set.eq_empty_or_nonempty (s : Set P) with (h | h)
  · convert isPreconnected_empty
    rw [coe_eq_bot_iff] at h
    simp only [h, not_w_opp_side_bot]
    rfl
  · exact (is_connected_set_of_w_opp_side x h).IsPreconnected
#align affine_subspace.is_preconnected_set_of_w_opp_side AffineSubspace.isPreconnected_setOf_wOppSide

theorem isConnected_setOf_sOppSide {s : AffineSubspace ℝ P} {x : P} (hx : x ∉ s)
    (h : (s : Set P).Nonempty) : IsConnected { y | s.SOppSide x y } :=
  by
  obtain ⟨p, hp⟩ := h
  haveI : Nonempty s := ⟨⟨p, hp⟩⟩
  rw [set_of_s_opp_side_eq_image2 hx hp, ← Set.image_prod]
  refine'
    (is_connected_Iio.prod (isConnected_iff_connectedSpace.2 _)).image _
      ((continuous_fst.smul continuous_const).vadd continuous_snd).ContinuousOn
  convert AddTorsor.connectedSpace s.direction s
#align affine_subspace.is_connected_set_of_s_opp_side AffineSubspace.isConnected_setOf_sOppSide

theorem isPreconnected_setOf_sOppSide (s : AffineSubspace ℝ P) (x : P) :
    IsPreconnected { y | s.SOppSide x y } :=
  by
  rcases Set.eq_empty_or_nonempty (s : Set P) with (h | h)
  · convert isPreconnected_empty
    rw [coe_eq_bot_iff] at h
    simp only [h, not_s_opp_side_bot]
    rfl
  · by_cases hx : x ∈ s
    · convert isPreconnected_empty
      simp only [hx, s_opp_side, not_true, false_and_iff, and_false_iff]
      rfl
    · exact (is_connected_set_of_s_opp_side hx h).IsPreconnected
#align affine_subspace.is_preconnected_set_of_s_opp_side AffineSubspace.isPreconnected_setOf_sOppSide

end Normed

end AffineSubspace


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import algebra.add_torsor
import topology.algebra.constructions
import group_theory.group_action.prod
import topology.algebra.const_mul_action

/-!
# Continuous monoid action

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

In this file we define class `has_continuous_smul`. We say `has_continuous_smul M X` if `M` acts on
`X` and the map `(c, x) ↦ c • x` is continuous on `M × X`. We reuse this class for topological
(semi)modules, vector spaces and algebras.

## Main definitions

* `has_continuous_smul M X` : typeclass saying that the map `(c, x) ↦ c • x` is continuous
  on `M × X`;
* `homeomorph.smul_of_ne_zero`: if a group with zero `G₀` (e.g., a field) acts on `X` and `c : G₀`
  is a nonzero element of `G₀`, then scalar multiplication by `c` is a homeomorphism of `X`;
* `homeomorph.smul`: scalar multiplication by an element of a group `G` acting on `X`
  is a homeomorphism of `X`.
* `units.has_continuous_smul`: scalar multiplication by `Mˣ` is continuous when scalar
  multiplication by `M` is continuous. This allows `homeomorph.smul` to be used with on monoids
  with `G = Mˣ`.

## Main results

Besides homeomorphisms mentioned above, in this file we provide lemmas like `continuous.smul`
or `filter.tendsto.smul` that provide dot-syntax access to `continuous_smul`.
-/

open_locale topology pointwise
open filter

/-- Class `has_continuous_smul M X` says that the scalar multiplication `(•) : M → X → X`
is continuous in both arguments. We use the same class for all kinds of multiplicative actions,
including (semi)modules and algebras. -/
class has_continuous_smul (M X : Type*) [has_smul M X]
  [topological_space M] [topological_space X] : Prop :=
(continuous_smul : continuous (λp : M × X, p.1 • p.2))

export has_continuous_smul (continuous_smul)

/-- Class `has_continuous_vadd M X` says that the additive action `(+ᵥ) : M → X → X`
is continuous in both arguments. We use the same class for all kinds of additive actions,
including (semi)modules and algebras. -/
class has_continuous_vadd (M X : Type*) [has_vadd M X]
  [topological_space M] [topological_space X] : Prop :=
(continuous_vadd : continuous (λp : M × X, p.1 +ᵥ p.2))

export has_continuous_vadd (continuous_vadd)

attribute [to_additive] has_continuous_smul

section main

variables {M X Y α : Type*} [topological_space M] [topological_space X] [topological_space Y]

section has_smul

variables [has_smul M X] [has_continuous_smul M X]

@[priority 100, to_additive] instance has_continuous_smul.has_continuous_const_smul :
  has_continuous_const_smul M X :=
{ continuous_const_smul := λ _, continuous_smul.comp (continuous_const.prod_mk continuous_id) }

@[to_additive]
lemma filter.tendsto.smul {f : α → M} {g : α → X} {l : filter α} {c : M} {a : X}
  (hf : tendsto f l (𝓝 c)) (hg : tendsto g l (𝓝 a)) :
  tendsto (λ x, f x • g x) l (𝓝 $ c • a) :=
(continuous_smul.tendsto _).comp (hf.prod_mk_nhds hg)

@[to_additive]
lemma filter.tendsto.smul_const {f : α → M} {l : filter α} {c : M}
  (hf : tendsto f l (𝓝 c)) (a : X) :
  tendsto (λ x, (f x) • a) l (𝓝 (c • a)) :=
hf.smul tendsto_const_nhds

variables {f : Y → M} {g : Y → X} {b : Y} {s : set Y}

@[to_additive]
lemma continuous_within_at.smul (hf : continuous_within_at f s b)
  (hg : continuous_within_at g s b) :
  continuous_within_at (λ x, f x • g x) s b :=
hf.smul hg

@[to_additive]
lemma continuous_at.smul (hf : continuous_at f b) (hg : continuous_at g b) :
  continuous_at (λ x, f x • g x) b :=
hf.smul hg

@[to_additive]
lemma continuous_on.smul (hf : continuous_on f s) (hg : continuous_on g s) :
  continuous_on (λ x, f x • g x) s :=
λ x hx, (hf x hx).smul (hg x hx)

@[continuity, to_additive]
lemma continuous.smul (hf : continuous f) (hg : continuous g) :
  continuous (λ x, f x • g x) :=
continuous_smul.comp (hf.prod_mk hg)

/-- If a scalar action is central, then its right action is continuous when its left action is. -/
@[to_additive "If an additive action is central, then its right action is continuous when its left
action is."]
instance has_continuous_smul.op [has_smul Mᵐᵒᵖ X] [is_central_scalar M X] :
  has_continuous_smul Mᵐᵒᵖ X :=
⟨ suffices continuous (λ p : M × X, mul_opposite.op p.fst • p.snd),
  from this.comp (mul_opposite.continuous_unop.prod_map continuous_id),
  by simpa only [op_smul_eq_smul] using (continuous_smul : continuous (λ p : M × X, _)) ⟩

@[to_additive] instance mul_opposite.has_continuous_smul : has_continuous_smul M Xᵐᵒᵖ :=
⟨mul_opposite.continuous_op.comp $ continuous_smul.comp $
  continuous_id.prod_map mul_opposite.continuous_unop⟩

end has_smul

section monoid

variables [monoid M] [mul_action M X] [has_continuous_smul M X]

@[to_additive] instance units.has_continuous_smul : has_continuous_smul Mˣ X :=
{ continuous_smul :=
    show continuous ((λ p : M × X, p.fst • p.snd) ∘ (λ p : Mˣ × X, (p.1, p.2))),
    from continuous_smul.comp ((units.continuous_coe.comp continuous_fst).prod_mk continuous_snd) }

end monoid

@[to_additive]
instance [has_smul M X] [has_smul M Y] [has_continuous_smul M X]
  [has_continuous_smul M Y] :
  has_continuous_smul M (X × Y) :=
⟨(continuous_fst.smul (continuous_fst.comp continuous_snd)).prod_mk
  (continuous_fst.smul (continuous_snd.comp continuous_snd))⟩

@[to_additive]
instance {ι : Type*} {γ : ι → Type*}
  [∀ i, topological_space (γ i)] [Π i, has_smul M (γ i)] [∀ i, has_continuous_smul M (γ i)] :
  has_continuous_smul M (Π i, γ i) :=
⟨continuous_pi $ λ i,
  (continuous_fst.smul continuous_snd).comp $
    continuous_fst.prod_mk ((continuous_apply i).comp continuous_snd)⟩

end main

section lattice_ops

variables {ι : Sort*} {M X : Type*} [topological_space M] [has_smul M X]

@[to_additive] lemma has_continuous_smul_Inf {ts : set (topological_space X)}
  (h : Π t ∈ ts, @has_continuous_smul M X _ _ t) :
  @has_continuous_smul M X _ _ (Inf ts) :=
{ continuous_smul :=
  begin
    rw ← @Inf_singleton _ _ ‹topological_space M›,
    exact continuous_Inf_rng.2 (λ t ht, continuous_Inf_dom₂ (eq.refl _) ht
      (@has_continuous_smul.continuous_smul _ _ _ _ t (h t ht)))
  end }

@[to_additive] lemma has_continuous_smul_infi {ts' : ι → topological_space X}
  (h : Π i, @has_continuous_smul M X _ _ (ts' i)) :
  @has_continuous_smul M X _ _ (⨅ i, ts' i) :=
has_continuous_smul_Inf $ set.forall_range_iff.mpr h

@[to_additive] lemma has_continuous_smul_inf {t₁ t₂ : topological_space X}
  [@has_continuous_smul M X _ _ t₁] [@has_continuous_smul M X _ _ t₂] :
  @has_continuous_smul M X _ _ (t₁ ⊓ t₂) :=
by { rw inf_eq_infi, refine has_continuous_smul_infi (λ b, _), cases b; assumption }

end lattice_ops

section add_torsor

variables (G : Type*) (P : Type*) [add_group G] [add_torsor G P] [topological_space G]
variables [preconnected_space G] [topological_space P] [has_continuous_vadd G P]
include G

/-- An `add_torsor` for a connected space is a connected space. This is not an instance because
it loops for a group as a torsor over itself. -/
protected lemma add_torsor.connected_space : connected_space P :=
{ is_preconnected_univ :=
    begin
      convert is_preconnected_univ.image ((equiv.vadd_const (classical.arbitrary P)) : G → P)
                                         (continuous_id.vadd continuous_const).continuous_on,
      rw [set.image_univ, equiv.range_eq_univ]
    end,
  to_nonempty := infer_instance }

end add_torsor


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

! This file was ported from Lean 3 source module topology.bornology.hom
! leanprover-community/mathlib commit e3d9ab8faa9dea8f78155c6c27d62a621f4c152d
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Topology.Bornology.Basic

/-!
# Locally bounded maps

This file defines locally bounded maps between bornologies.

We use the `FunLike` design, so each type of morphisms has a companion typeclass which is meant to
be satisfied by itself and all stricter types.

## Types of morphisms

* `LocallyBoundedMap`: Locally bounded maps. Maps which preserve boundedness.

## Typeclasses

* `LocallyBoundedMapClass`
-/

open Bornology Filter Function Set

variable {F α β γ δ : Type _}

/-- The type of bounded maps from `α` to `β`, the maps which send a bounded set to a bounded set. -/
structure LocallyBoundedMap (α β : Type _) [Bornology α] [Bornology β] where
  /-- The function underlying a locally bounded map -/
  toFun : α → β
  /-- The pullback of the `Bornology.cobounded` filter under the function is contained in the
  cobounded filter. Equivalently, the function maps bounded sets to bounded sets. -/
  comap_cobounded_le' : (cobounded β).comap toFun ≤ cobounded α
#align locally_bounded_map LocallyBoundedMap

section

/-- `LocallyBoundedMapClass F α β` states that `F` is a type of bounded maps.

You should extend this class when you extend `LocallyBoundedMap`. -/
class LocallyBoundedMapClass (F : Type _) (α β : outParam <| Type _) [Bornology α]
    [Bornology β] extends FunLike F α fun _ => β where
  /-- The pullback of the `Bornology.cobounded` filter under the function is contained in the
  cobounded filter. Equivalently, the function maps bounded sets to bounded sets. -/
  comap_cobounded_le (f : F) : (cobounded β).comap f ≤ cobounded α
#align locally_bounded_map_class LocallyBoundedMapClass

end

export LocallyBoundedMapClass (comap_cobounded_le)

theorem IsBounded.image [Bornology α] [Bornology β] [LocallyBoundedMapClass F α β] {f : F}
    {s : Set α} (hs : IsBounded s) : IsBounded (f '' s) :=
  comap_cobounded_le_iff.1 (comap_cobounded_le f) hs
#align is_bounded.image IsBounded.image

/-- Turn an element of a type `F` satisfying `LocallyBoundedMapClass F α β` into an actual
`LocallyBoundedMap`. This is declared as the default coercion from `F` to
`LocallyBoundedMap α β`. -/
@[coe]
def LocallyBoundedMapClass.toLocallyBoundedMap [Bornology α] [Bornology β]
    [LocallyBoundedMapClass F α β] (f : F) : LocallyBoundedMap α β where
  toFun := f
  comap_cobounded_le' := comap_cobounded_le f

instance [Bornology α] [Bornology β] [LocallyBoundedMapClass F α β] :
    CoeTC F (LocallyBoundedMap α β) :=
  ⟨fun f => ⟨f, comap_cobounded_le f⟩⟩

namespace LocallyBoundedMap

variable [Bornology α] [Bornology β] [Bornology γ] [Bornology δ]

instance : LocallyBoundedMapClass (LocallyBoundedMap α β) α β where
  coe f := f.toFun
  coe_injective' f g h := by
    cases f
    cases g
    congr
  comap_cobounded_le f := f.comap_cobounded_le'

/- omitting helper instance because it is not needed in Lean 4.
/-- Helper instance for when there's too many metavariables to apply the coercion via `FunLike`
directly.
instance : CoeFun (LocallyBoundedMap α β) fun _ => α → β where
  coe := LocallyBoundedMap.toFun -/ -/

-- porting note: syntactic tautology because of the way coercions work
#noalign locally_bounded_map.to_fun_eq_coe

@[ext]
theorem ext {f g : LocallyBoundedMap α β} (h : ∀ a, f a = g a) : f = g :=
  FunLike.ext f g h
#align locally_bounded_map.ext LocallyBoundedMap.ext

/-- Copy of a `LocallyBoundedMap` with a new `toFun` equal to the old one. Useful to fix
definitional equalities. -/
protected def copy (f : LocallyBoundedMap α β) (f' : α → β) (h : f' = f) : LocallyBoundedMap α β :=
  ⟨f', h.symm ▸ f.comap_cobounded_le'⟩
#align locally_bounded_map.copy LocallyBoundedMap.copy

@[simp]
theorem coe_copy (f : LocallyBoundedMap α β) (f' : α → β) (h : f' = f) : ⇑(f.copy f' h) = f' :=
  rfl
#align locally_bounded_map.coe_copy LocallyBoundedMap.coe_copy

theorem copy_eq (f : LocallyBoundedMap α β) (f' : α → β) (h : f' = f) : f.copy f' h = f :=
  FunLike.ext' h
#align locally_bounded_map.copy_eq LocallyBoundedMap.copy_eq

/-- Construct a `LocallyBoundedMap` from the fact that the function maps bounded sets to bounded
sets. -/
def ofMapBounded (f : α → β) (h : ∀ ⦃s : Set α⦄, IsBounded s → IsBounded (f '' s)) :
    LocallyBoundedMap α β :=
  ⟨f, comap_cobounded_le_iff.2 h⟩
-- porting note: I had to provide the type of `h` explicitly.
#align locally_bounded_map.of_map_bounded LocallyBoundedMap.ofMapBounded

@[simp]
theorem coe_ofMapBounded (f : α → β) {h} : ⇑(ofMapBounded f h) = f :=
  rfl
#align locally_bounded_map.coe_of_map_bounded LocallyBoundedMap.coe_ofMapBounded

@[simp]
theorem ofMapBounded_apply (f : α → β) {h} (a : α) : ofMapBounded f h a = f a :=
  rfl
#align locally_bounded_map.of_map_bounded_apply LocallyBoundedMap.ofMapBounded_apply

variable (α)

/-- `id` as a `LocallyBoundedMap`. -/
protected def id : LocallyBoundedMap α α :=
  ⟨id, comap_id.le⟩
#align locally_bounded_map.id LocallyBoundedMap.id

instance : Inhabited (LocallyBoundedMap α α) :=
  ⟨LocallyBoundedMap.id α⟩

@[simp]
theorem coe_id : ⇑(LocallyBoundedMap.id α) = id :=
  rfl
#align locally_bounded_map.coe_id LocallyBoundedMap.coe_id

variable {α}

@[simp]
theorem id_apply (a : α) : LocallyBoundedMap.id α a = a :=
  rfl
#align locally_bounded_map.id_apply LocallyBoundedMap.id_apply

/-- Composition of `LocallyBoundedMap`s as a `LocallyBoundedMap`. -/
def comp (f : LocallyBoundedMap β γ) (g : LocallyBoundedMap α β) : LocallyBoundedMap α γ
    where
  toFun := f ∘ g
  comap_cobounded_le' :=
    comap_comap.ge.trans <| (comap_mono f.comap_cobounded_le').trans g.comap_cobounded_le'
#align locally_bounded_map.comp LocallyBoundedMap.comp

@[simp]
theorem coe_comp (f : LocallyBoundedMap β γ) (g : LocallyBoundedMap α β) : ⇑(f.comp g) = f ∘ g :=
  rfl
#align locally_bounded_map.coe_comp LocallyBoundedMap.coe_comp

@[simp]
theorem comp_apply (f : LocallyBoundedMap β γ) (g : LocallyBoundedMap α β) (a : α) :
    f.comp g a = f (g a) :=
  rfl
#align locally_bounded_map.comp_apply LocallyBoundedMap.comp_apply

@[simp]
theorem comp_assoc (f : LocallyBoundedMap γ δ) (g : LocallyBoundedMap β γ)
    (h : LocallyBoundedMap α β) : (f.comp g).comp h = f.comp (g.comp h) :=
  rfl
#align locally_bounded_map.comp_assoc LocallyBoundedMap.comp_assoc

@[simp]
theorem comp_id (f : LocallyBoundedMap α β) : f.comp (LocallyBoundedMap.id α) = f :=
  ext fun _ => rfl
#align locally_bounded_map.comp_id LocallyBoundedMap.comp_id

@[simp]
theorem id_comp (f : LocallyBoundedMap α β) : (LocallyBoundedMap.id β).comp f = f :=
  ext fun _ => rfl
#align locally_bounded_map.id_comp LocallyBoundedMap.id_comp

theorem cancel_right {g₁ g₂ : LocallyBoundedMap β γ} {f : LocallyBoundedMap α β}
    (hf : Surjective f) : g₁.comp f = g₂.comp f ↔ g₁ = g₂ :=
  ⟨fun h => ext <| hf.forall.2 <| FunLike.ext_iff.1 h, congrArg (fun x => comp x f)⟩
-- porting note: unification was not strong enough to do `congrArg _`.
#align locally_bounded_map.cancel_right LocallyBoundedMap.cancel_right

theorem cancel_left {g : LocallyBoundedMap β γ} {f₁ f₂ : LocallyBoundedMap α β} (hg : Injective g) :
    g.comp f₁ = g.comp f₂ ↔ f₁ = f₂ :=
  ⟨fun h => ext fun a => hg <| by rw [← comp_apply, h, comp_apply], congr_arg _⟩
#align locally_bounded_map.cancel_left LocallyBoundedMap.cancel_left

end LocallyBoundedMap


import data.real.basic

noncomputable theory

open real finset

def arithmetic_mean (S : list ℝ) : ℝ :=
(list.sum S) / (list.length S)

-- No instance for has_pow ℝ ℝ
-- def geometric_mean (S : finset ℝ) {h : ∀ (s : ℝ), s ∈ S → s > 0} : ℝ :=
-- (finset.prod S id) ^ (1 / (finset.card S : ℝ))

theorem AM_geq_GM (S : list ℝ) (h : ∀ (s : ℝ), s ∈ S → s > 0) :
(arithmetic_mean S)^(list.length S) ≥ list.prod S :=
begin
    dsimp [arithmetic_mean],
    induction (S),

    {simp,
     exact le_refl 1,
    },

    {simp,
     rw [add_comm 1, pow_succ],
     sorry,
    }

end


import galois.tactic
       galois.fin
       data.vector

universes u

structure vector_ind_succ {A : Type u} {n : ℕ} (xs : vector A n.succ)
  : Type u :=
  (head : A)
  (tail : vector A n)
  (equiv : xs = head :: tail)

namespace vector

def generate {A : Type _} : ∀ (n : ℕ) (f : ℕ → A),
  vector A n
| 0 _ := nil
| (nat.succ n) f := f 0 :: generate n (f ∘ nat.succ)

def invert_succ {A : Type _} {n : ℕ} (xs : vector A n.succ)
  : vector_ind_succ xs
  := ⟨ xs.head, xs.tail, eq.symm (cons_head_tail _) ⟩

lemma map₂_nil {A B C} (f : A → B → C)
  : map₂ f nil nil = nil
  := rfl

lemma map₂_cons {A B C} (f : A → B → C)
  (a : A) (b : B) {n : ℕ} (as : vector A n) (bs : vector B n)
  : map₂ f (a :: as) (b :: bs) = f a b :: map₂ f as bs
:= begin
induction as, induction bs, reflexivity,
end

lemma repeat_unfold {A} (n : ℕ) (x : A)
  : repeat x n.succ = x :: repeat x n
:= rfl

lemma nth_cons_nz {A} {n : ℕ} (i : fin n.succ)
  (H : i ≠ 0)
  (x : A) (xs : vector A n)
  : nth (x :: xs) i = nth xs (fin.pred i H)
:= begin
induction xs, induction i,
dsimp [cons, nth, fin.pred],
have H : val_1 ≠ 0, intros contra,
apply H, unfold has_zero.zero, subst val_1,
cases val_1, contradiction,
dsimp [list.nth_le], reflexivity,
end

lemma nth_0 {A : Type _} {n : ℕ} (x : A) (xs : vector A n)
  {i : fin n.succ} (Hi : i.val = 0)
  : nth (x :: xs) i = x
:= begin
induction i, dsimp at Hi,
subst val, induction xs,
dsimp [nth, cons], reflexivity
end

lemma nth_cons_zero {A} {n : ℕ}
  (x : A) (xs : vector A n)
  : nth (x :: xs) 0 = x
:= begin
apply nth_0, reflexivity,
end

lemma map₂_comm {A B} (f : A → A → B)
  (f_comm : ∀ x y, f x y = f y x)
  {n : ℕ} (xs ys : vector A n)
  : map₂ f xs ys = map₂ f ys xs
:= begin
induction n,
{ rw (vector.eq_nil xs), rw (vector.eq_nil ys), },
{ have Hx := xs.invert_succ, induction Hx with x xs' Hx, subst xs,
  have Hy := ys.invert_succ, induction Hy with y ys' Hy, subst ys,
  repeat { rw map₂_cons }, rw f_comm,
  f_equal, apply ih_1,
}
end

lemma generate_const_repeat {A} (n : ℕ) (c : A)
  : generate n (λ _, c)
  = repeat c n
:= begin
induction n; dsimp [repeat, generate],
reflexivity,
rw ih_1, reflexivity,
end

lemma nth_of_fn {A} {n : ℕ} (f : fin n → A)
  (i : fin n)
  : nth (of_fn f) i = f i
:= begin
revert f i,
induction n; intros; dsimp [of_fn],
{ exfalso, apply fin.fin_0_empty, assumption, },
{ apply (if H : i = 0 then _ else _),
  { subst i, rw vector.nth_cons_zero, },
  { rw vector.nth_cons_nz, tactic.swap, assumption,
    rw ih_1, rw fin.succ_pred_id, }
}
end

lemma nil_of_fn {A} : @vector.nil A = vector.of_fn (begin
  intros i, exfalso, apply fin.fin_0_empty, assumption,
  end)
:= eq.symm (vector.eq_nil _)

end vector


/-
Copyright (c) 2018 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Reid Barton, Bhavik Mehta
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.category_theory.over
import Mathlib.category_theory.limits.connected
import Mathlib.category_theory.limits.creates
import Mathlib.PostPort

universes u v

namespace Mathlib

/-!
# Connected limits in the over category

Shows that the forgetful functor `over B ⥤ C` creates connected limits, in particular `over B` has
any connected limit which `C` has.
-/

namespace category_theory.over

namespace creates_connected

/--
(Impl) Given a diagram in the over category, produce a natural transformation from the
diagram legs to the specific object.
-/
def nat_trans_in_over {J : Type v} [small_category J] {C : Type u} [category C] {B : C}
    (F : J ⥤ over B) : F ⋙ forget B ⟶ functor.obj (functor.const J) B :=
  nat_trans.mk fun (j : J) => comma.hom (functor.obj F j)

/--
(Impl) Given a cone in the base category, raise it to a cone in the over category. Note this is
where the connected assumption is used.
-/
@[simp] theorem raise_cone_π_app {J : Type v} [small_category J] {C : Type u} [category C]
    [is_connected J] {B : C} {F : J ⥤ over B} (c : limits.cone (F ⋙ forget B)) (j : J) :
    nat_trans.app (limits.cone.π (raise_cone c)) j = hom_mk (nat_trans.app (limits.cone.π c) j) :=
  Eq.refl (nat_trans.app (limits.cone.π (raise_cone c)) j)

theorem raised_cone_lowers_to_original {J : Type v} [small_category J] {C : Type u} [category C]
    [is_connected J] {B : C} {F : J ⥤ over B} (c : limits.cone (F ⋙ forget B))
    (t : limits.is_limit c) : functor.map_cone (forget B) (raise_cone c) = c :=
  sorry

/-- (Impl) Show that the raised cone is a limit. -/
def raised_cone_is_limit {J : Type v} [small_category J] {C : Type u} [category C] [is_connected J]
    {B : C} {F : J ⥤ over B} {c : limits.cone (F ⋙ forget B)} (t : limits.is_limit c) :
    limits.is_limit (raise_cone c) :=
  limits.is_limit.mk
    fun (s : limits.cone F) => hom_mk (limits.is_limit.lift t (functor.map_cone (forget B) s))

end creates_connected

/-- The forgetful functor from the over category creates any connected limit. -/
protected instance forget_creates_connected_limits {J : Type v} [small_category J] {C : Type u}
    [category C] [is_connected J] {B : C} : creates_limits_of_shape J (forget B) :=
  creates_limits_of_shape.mk
    fun (K : J ⥤ over B) =>
      creates_limit_of_reflects_iso
        fun (c : limits.cone (K ⋙ forget B)) (t : limits.is_limit c) =>
          lifts_to_limit.mk
            (liftable_cone.mk (creates_connected.raise_cone c)
              (eq_to_iso (creates_connected.raised_cone_lowers_to_original c t)))
            (creates_connected.raised_cone_is_limit t)

/-- The over category has any connected limit which the original category has. -/
protected instance has_connected_limits {J : Type v} [small_category J] {C : Type u} [category C]
    {B : C} [is_connected J] [limits.has_limits_of_shape J C] :
    limits.has_limits_of_shape J (over B) :=
  limits.has_limits_of_shape.mk fun (F : J ⥤ over B) => has_limit_of_created F (forget B)

end Mathlib