import data.rat.basic
import tactic

/-
In this file we will build "the algebraic hierarchy",
the classes of groups, rings, fields, modules etc.

To get the exercises, run:
```
leanproject get fpvandoorn/Harvard-tutoring
cp -r Harvard-tutoring/src/exercises/ Harvard-tutoring/src/my_exercises
code Harvard-tutoring
```
For this week, the exercises are in the files `my_exercises/algebraic_hierarchy.lean` and `my_exercises/order.lean`.

Also this week, think about a project you would like to formalize in Lean for the remaining weeks, and discuss it during your weekly meeting with your tutor.
-/

/-
## Notation typeclasses

We have notation typeclasses which are just there to provide notation.

If you write `[has_mul G]` then `G` will have a multiplication called `*` (satisfying no axioms).

Similarly `[has_one G]` gives you `(1 : G)`
and `[has_inv G]` gives you a map `(λ g, g⁻¹ : G → G)`
-/

#print has_mul

example {G : Type} [has_mul G] (g h : G) : g * h = h * g :=
sorry -- this is false

/-
## Groups
-/
-- `extends` is used to extend other classes
class my_group (G : Type) extends has_mul G, has_one G, has_inv G :=
(mul_assoc : ∀ (a b c : G), a * b * c = a * (b * c))
(one_mul : ∀ (a : G), 1 * a = a)
(mul_left_inv : ∀ (a : G), a⁻¹ * a = 1)

/-
Advantages of this approach: axioms look lovely.

Disadvantage: what if I want the group law to be `+`? I have embedded `has_mul`
in the definition.
-/

class my_add_group (G : Type) extends has_add G, has_zero G, has_neg G :=
(add_assoc : ∀ (a b c : G), a + b + c = a + (b + c))
(zero_add : ∀ (a : G), 0 + a = a)
(add_left_neg : ∀ (a : G), -a + a = 0)

attribute [to_additive] my_group

/-
Lean's solution: develop a `to_additive` metaprogram which translates all theorems about
`group`s (with group law `*`) to theorems about `add_group`s (with group law `+`).
-/

#print my_group.one_mul

namespace my_group

#print one_mul

/-
We can now prove more theorems about `my_group`, such as the following:

`mul_left_cancel : ∀ (a b c : G), a * b = a * c → b = c`
`mul_eq_of_eq_inv_mul {a x y : G} : x = a⁻¹ * y → a * x = y`
`mul_one (a : G) : a * 1 = a`
`mul_right_inv (a : G) : a * a⁻¹ = 1`
-/

@[to_additive] lemma mul_left_cancel {G : Type} [my_group G] (a b c : G)
  (Habac : a * b = a * c) : b = c :=
calc b = 1 * b         : by rw one_mul
   ... = (a⁻¹ * a) * b : by rw mul_left_inv
   ... = a⁻¹ * (a * b) : by rw mul_assoc
   ... = a⁻¹ * (a * c) : by sorry
   ... = (a⁻¹ * a) * c : by sorry
   ... = 1 * c         : by sorry
   ... = c             : by sorry

#check @my_group.mul_left_cancel
#check @my_add_group.add_left_cancel

variables (x y : ℚ)

example : x * y + y = y * (x + 1) :=
by { rw mul_comm, ring }

#check @mul_eq_of_eq_inv_mul

-- Abstract example of the power of classes: we can define products of groups with instances

attribute [simp] mul_assoc one_mul mul_left_inv

instance (G : Type) [my_group G] (H : Type) [my_group H] : my_group (G × H) :=
{ mul := λ gh gh', (gh.1 * gh'.1, gh.2 * gh'.2),
  one := (1, 1),
  inv := λ gh, (gh.1⁻¹, gh.2⁻¹),
  mul_assoc := by { intros a b c, cases c, cases b, cases a, dsimp at *, simp at * },
  one_mul := by tidy,
  mul_left_inv := by tidy }

-- the type class inference system now knows that products of groups are groups

example (G H K : Type) [my_group G] [my_group H] [my_group K] : my_group (G × H × K) :=
by { apply_instance }

end my_group

-- let's make a group of order two.

-- First the elements {+1, -1}
inductive mu2
| p1 : mu2
| m1 : mu2

namespace mu2

/- two preliminary facts -/
-- 1) give an algorithm to decide equality
attribute [derive decidable_eq] mu2

-- 2) prove it is finite
instance : fintype mu2 :=
⟨⟨[mu2.p1, mu2.m1], dec_trivial⟩, λ x, by { cases x; exact dec_trivial, }⟩

-- Define multiplication by doing all cases
def mul : mu2 → mu2 → mu2
| p1 p1 := p1
| p1 m1 := m1
| m1 p1 := m1
| m1 m1 := p1

-- now let's make it a group
instance : my_group mu2 :=
begin
  refine { mul := mu2.mul, one := p1, inv := id, .. },
  all_goals {exact dec_trivial},
end

end mu2

-- Now let's build rings and modules and stuff (via monoids and add_comm_groups)

-- a monoid is a group without inverses
class my_monoid (M : Type) extends has_mul M, has_one M :=
(mul_assoc : ∀ (a b c : M), a * b * c = a * (b * c))
(one_mul : ∀ (a : M), 1 * a = a)
(mul_one : ∀ (a : M), a * 1 = a)

#print monoid

-- rings are additive abelian groups and multiplicative monoids,
-- with distributivity
class my_ring (R : Type) extends my_monoid R, add_comm_group R :=
(mul_add : ∀ (a b c : R), a * (b + c) = a * b + a * c)
(add_mul : ∀ (a b c : R), (a + b) * c = a * c + b * c)

-- for commutative rings, add commutativity of multiplication
class my_comm_ring (R : Type) extends my_ring R :=
(mul_comm : ∀ a b : R, a * b = b * a)

/-- Typeclass for types with a scalar multiplication operation, denoted `•` (`\bu`) -/
class my_has_scalar (R : Type) (M : Type) := (smul : R → M → M)

infixr ` • `:73 := my_has_scalar.smul

-- modules over a ring
class my_module (R : Type) [my_ring R] (M : Type) [add_comm_group M]
extends my_has_scalar R M :=
(smul_add : ∀(r : R) (x y : M), r • (x + y) = r • x + r • y)
(add_smul : ∀(r s : R) (x : M), (r + s) • x = r • x + s • x)
(mul_smul : ∀ (r s : R) (x : M), (r * s) • x = r • s • x)
(one_smul : ∀ x : M, (1 : R) • x = x)

-- for fields we let ⁻¹ be defined on the entire field, and demand 0⁻¹ = 0
-- and that a⁻¹ * a = 1 for non-zero a. This is merely for convenience;
-- one can easily check that it's mathematically equivalent to the usual
-- definition of a field.
class my_field (K : Type) extends my_comm_ring K, has_inv K :=
(zero_ne_one : (0 : K) ≠ 1)
(mul_inv_cancel : ∀ {a : K}, a ≠ 0 → a * a⁻¹ = 1)
(inv_zero : (0 : K)⁻¹ = 0)

-- the type of vector spaces
def my_vector_space (K : Type) [my_field K] (V : Type) [add_comm_group V] := my_module K V

/-
Let's check that we can make the rational numbers into a field.
(easy because all the work is done in the import)
-/
instance : my_field ℚ :=
{ mul := (*),
  one := 1,
  mul_assoc := rat.mul_assoc,
  one_mul := rat.one_mul,
  mul_one := rat.mul_one,
  add := (+),
  zero := 0,
  neg := has_neg.neg,
  add_assoc := rat.add_assoc,
  zero_add := rat.zero_add,
  add_zero := rat.add_zero,
  add_left_neg := rat.add_left_neg,
  add_comm := rat.add_comm,
  mul_add := rat.mul_add,
  add_mul := rat.add_mul,
  mul_comm := rat.mul_comm,
  inv := has_inv.inv,
  zero_ne_one := rat.zero_ne_one,
  mul_inv_cancel := rat.mul_inv_cancel,
  inv_zero := inv_zero
  }

/- Some tactics that will help: -/

example {G : Type} [group G] (a b c d : G) :
 ((a * b)⁻¹ * a * 1⁻¹⁻¹⁻¹ * b * b⁻¹ * 1 * c)⁻¹ =
 (c⁻¹⁻¹ * (d * d⁻¹ * 1⁻¹⁻¹) * c⁻¹ * c⁻¹⁻¹⁻¹ * b)⁻¹⁻¹ :=
by simp

example {G : Type} [add_comm_group G] (a b : G) : (a + b) - ((b + a) + a) = -a :=
by abel -- rewriting in abelian groups

example {R : Type} [comm_ring R] (a b : R) : (a + b) * (a - b) = a ^ 2 - b ^ 2 :=
by ring -- rewriting in commutative rings

example {F : Type} [field F] (a b : F) (h : a ≠ 0) : (a + b) / a = 1 + b / a :=
by field_simp -- rewriting fractions in fields

/-
# Orders

We also have an "order hierarchy".

It starts with partially ordered sets, and then goes on to lattices.

We will motivate it using subgroups.
-/

open set

-- The type of subgroups of a group G is called `subgroup G` in Lean.
-- It already has a lattice structure in Lean.

-- So let's just redo the entire theory and call it `my_subgroup G`.

/-- The type of subgroups of a group `G`. -/
structure my_subgroup (G : Type) [group G] :=
-- A subgroup of G is a sub*set* of G, called `carrier`
(carrier : set G)
-- and then axioms saying it's closed under the group structure (i.e. *, 1, ⁻¹)
(mul_mem {a b : G} : a ∈ carrier → b ∈ carrier → a * b ∈ carrier)
(one_mem : (1 : G) ∈ carrier)
(inv_mem {a : G} : a ∈ carrier → a⁻¹ ∈ carrier)

namespace my_subgroup

/-
Note in particular that we have a function `my_subgroup.carrier : my_subgroup G → set G`,
sending a subgroup of `G` to the underlying subset (`set G` is the type of subsets of G).
-/

-- Let G be a group, let H,J,K be subgroups of G, and let a,b,c be elements of G.
variables {G : Type} [group G] (H J K : my_subgroup G) (a b c : G)

/- ## Extensionality -/

lemma carrier_injective (H J : my_subgroup G) (h : H.carrier = J.carrier) : H = J :=
begin
  cases H, cases J, simp * at *,
end

-- Now let's prove that two subgroups are equal iff they have the same elements.
-- This is the most useful "extensionality lemma" so we tag it `@[ext]`.
@[ext] theorem ext {H J : my_subgroup G} (h : ∀ (x : G), x ∈ H.carrier ↔ x ∈ J.carrier) :
  H = J :=
begin
  apply carrier_injective, ext, apply h,
end

-- We also want the `iff` version of this.
theorem ext_iff {H J : my_subgroup G} : H = J ↔ ∀ (x : G), x ∈ H.carrier ↔ x ∈ J.carrier :=
begin
  sorry
end

/-
## Partial orders

-/
-- We define `H ≤ J` to mean `H.carrier ⊆ J.carrier`
-- "tidy" is a one-size-fits-all tactic which solves certain kinds of "follow your nose" goals.
instance : partial_order (my_subgroup G) :=
{ le := λ H J, H.carrier ⊆ J.carrier,
  le_refl := by tidy,
  le_trans := by tidy,
  le_antisymm := by tidy }

-- We can give a second construction using `partial_order.lift`:
example : partial_order (my_subgroup G) :=
partial_order.lift my_subgroup.carrier carrier_injective

example {H J : my_subgroup G} (h : H < J) : H ≤ J := h.le

/-
## From partial orders to lattices.

We can now show that `my_subgroup G` is a semilattice with finite meets (binary meets and a top element).
-/
def top : my_subgroup G :=
{ carrier := set.univ,
  mul_mem := begin
    sorry
  end,
  one_mem := begin
    sorry
  end,
  inv_mem := begin
    sorry
  end }

-- Add the `⊤` notation (typed with `\top`) for this subgroup:
instance : has_top (my_subgroup G) := ⟨top⟩

#check (⊤ : my_subgroup G)

/-- "Theorem" : intersection of two my_subgroups is a my_subgroup -/
definition inf (H K : my_subgroup G) : my_subgroup G :=
{ carrier := H.carrier ∩ K.carrier,
  mul_mem := begin
    sorry
  end,
  one_mem := begin
    sorry
  end,
  inv_mem := begin
    sorry
  end }

-- Add the `⊓` notation (type with `\inf`) for the intersection (inf) of two subgroups:
instance : has_inf (my_subgroup G) := ⟨inf⟩

-- We now check the four axioms for a semilattice_inf_top.

lemma le_top (H : my_subgroup G) : H ≤ ⊤ :=
begin
  sorry
end

lemma inf_le_left (H K : my_subgroup G) : H ⊓ K ≤ H :=
begin
  sorry
end

lemma inf_le_right (H K : my_subgroup G) : H ⊓ K ≤ K :=
begin
  sorry
end

lemma le_inf (H J K : my_subgroup G) (h1 : H ≤ J) (h2 : H ≤ K) : H ≤ J ⊓ K :=
begin
  sorry
end

-- Now we're ready to make the instance.
instance : semilattice_inf_top (my_subgroup G) :=
{ top := top,
  le_top := le_top,
  inf := inf,
  inf_le_left := inf_le_left,
  inf_le_right := inf_le_right,
  le_inf := le_inf,
  .. my_subgroup.partial_order }

/-
## Complete lattices
Let's now show that subgroups form a complete lattice. This has arbitrary `Inf` and `Sup`s.
First we show we can form arbitrary intersections.
-/

def Inf (S : set (my_subgroup G)) : my_subgroup G :=
{ carrier := ⋂ K ∈ S, (K : my_subgroup G).carrier,
  mul_mem :=  begin
    intros a b ha hb, simp at *, intros K hK, apply my_subgroup.mul_mem, tidy
  end,
  one_mem := begin
    sorry
  end,
  inv_mem := begin
    sorry
  end }

-- We now equip `my_subgroup G` with an Inf. The notation is `⨅`, or `\Inf`.
instance : has_Inf (my_subgroup G) := ⟨Inf⟩

instance : complete_lattice (my_subgroup G) := complete_lattice_of_Inf _ begin
  sorry
end

/-
## Galois connections

A Galois conection is a pair of adjoint functors between two
partially ordered sets, considered as categories whose hom sets Hom(H,J) have
size 1 if H ≤ J and size 0 otherwise. In other words, a Galois
connection between two partial orders α and β is a pair of monotone functions
`l : α → β` and `u : β → α` such that `∀ (a : α) (b : β), l a ≤ b ↔ a ≤ u b`.

There is an example coming from Galois theory (between subfields and subgroups),
and an example coming from classical algebraic geometry (between affine
varieties and ideals); note that in both cases you have to use the opposite
partial order on one side to make everything covariant.

The examples we want to keep in mind here are:
1) α = subsets of G, β = subgroups of G, l = "subgroup generated by", u = `carrier`
2) X : Type, α := set (set X), β := topologies on X,
  l = topology generated by a collection of open sets, u = the open sets regarded as subsets.

As you can imagine, there are a bunch of abstract theorems with simple proofs
proved for Galois connections. You can see them by `#check galois_connection`,
jumping to the definition, and reading the next 150 lines of the mathlib file
after the definition. Examples of theorems you might recognise from contexts
where you have seen this before:

lemma le_u_l (a : α) : a ≤ u (l a) := ...

lemma l_u_le (b : β) : l (u b) ≤ b := ...

lemma u_l_u_eq_u : u ∘ l ∘ u = u := ...

lemma l_u_l_eq_l : l ∘ u ∘ l = l := ...

# Galois insertions

A particularly cool kind of Galois connection is a Galois insertion, which
is a Galois connection such that `l ∘ u = id`. This is true for both
the examples we're keeping in mind (the subgroup of `G` generated
by a subgroup is the same subgroup; the topology on `X` generated by a
topology is the same topology).

Our new goal: let's make subgroups of a group into a complete lattice,
using the fact that `carrier` is part of a Galois insertion.

-/

-- The adjoint functor to the `carrier` functor is the `span` functor
-- from subsets to my_subgroups. Here we will CHEAT by using `Inf` to
-- define `span`. We could have built `span` directly with
-- an inductive definition.
def span (S : set G) : my_subgroup G := Inf {H : my_subgroup G | S ⊆ H.carrier}

-- Here are some theorems about it.
lemma monotone_carrier : monotone (my_subgroup.carrier : my_subgroup G → set G) :=
begin
  sorry
end

lemma monotone_span : monotone (span : set G → my_subgroup G) :=
begin
  sorry
end

lemma subset_span (S : set G) : S ≤ (span S).carrier :=
begin
  sorry
end

lemma span_my_subgroup (H : my_subgroup G) : span H.carrier = H :=
begin
  sorry
end

-- We have proved all the things we need to show that `span` and `carrier`
-- form a Galois insertion, using `galois_insertion.monotone_intro`.
def gi_my_subgroup : galois_insertion (span : set G → my_subgroup G) (my_subgroup.carrier : my_subgroup G → set G) :=
galois_insertion.monotone_intro monotone_carrier monotone_span subset_span span_my_subgroup

-- Note that `set G` is already a complete lattice:
example : complete_lattice (set G) := by apply_instance

-- and now `my_subgroup G` can also be made into a complete lattice, by
-- a theorem about Galois insertions. Again, I don't use `instance`
-- because we already made the instance above.

example : complete_lattice (my_subgroup G) :=
galois_insertion.lift_complete_lattice gi_my_subgroup

end my_subgroup


/-
Copyright (c) 2019 Seul Baek. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Seul Baek

! This file was ported from Lean 3 source module tactic.omega.nat.sub_elim
! leanprover-community/mathlib commit 312db88b2d1e9f9cfc44004370be5d5f3f780cff
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Tactic.Omega.Nat.Form

/-
Subtraction elimination for linear natural number arithmetic.
Works by repeatedly rewriting goals of the preform `P[t-s]` into
`P[x] ∧ (t = s + x ∨ (t ≤ s ∧ x = 0))`, where `x` is fresh.
-/
namespace Omega

namespace Nat

open Omega.Nat

namespace Preterm

/-- Find subtraction inside preterm and return its operands -/
def subTerms : Preterm → Option (Preterm × Preterm)
  | &i => none
  | i ** n => none
  | t +* s => t.subTerms <|> s.subTerms
  | t -* s => t.subTerms <|> s.subTerms <|> some (t, s)
#align omega.nat.preterm.sub_terms Omega.Nat.Preterm.subTerms

/-- Find (t - s) inside a preterm and replace it with variable k -/
def subSubst (t s : Preterm) (k : Nat) : Preterm → Preterm
  | t@(&m) => t
  | t@(m ** n) => t
  | x +* y => x.subSubst +* y.subSubst
  | x -* y => if x = t ∧ y = s then 1 ** k else x.subSubst -* y.subSubst
#align omega.nat.preterm.sub_subst Omega.Nat.Preterm.subSubst

theorem val_subSubst {k : Nat} {x y : Preterm} {v : Nat → Nat} :
    ∀ {t : Preterm},
      t.freshIndex ≤ k → (subSubst x y k t).val (update k (x.val v - y.val v) v) = t.val v
  | &m, h1 => rfl
  | m ** n, h1 => by
    have h2 : n ≠ k := ne_of_lt h1
    simp only [sub_subst, preterm.val]
    rw [update_eq_of_ne _ h2]
  | t +* s, h1 => by
    simp only [sub_subst, val_add]; apply fun_mono_2 <;> apply val_sub_subst (le_trans _ h1)
    apply le_max_left; apply le_max_right
  | t -* s, h1 => by
    simp only [sub_subst, val_sub]
    by_cases h2 : t = x ∧ s = y
    · rw [if_pos h2]
      simp only [val_var, one_mul]
      rw [update_eq, h2.left, h2.right]
    · rw [if_neg h2]
      simp only [val_sub, sub_subst]
      apply fun_mono_2 <;> apply val_sub_subst (le_trans _ h1)
      apply le_max_left
      apply le_max_right
#align omega.nat.preterm.val_sub_subst Omega.Nat.Preterm.val_subSubst

end Preterm

namespace Preform

/-- Find subtraction inside preform and return its operands -/
def subTerms : Preform → Option (Preterm × Preterm)
  | t =* s => t.subTerms <|> s.subTerms
  | t ≤* s => t.subTerms <|> s.subTerms
  | ¬* p => p.subTerms
  | p ∨* q => p.subTerms <|> q.subTerms
  | p ∧* q => p.subTerms <|> q.subTerms
#align omega.nat.preform.sub_terms Omega.Nat.Preform.subTerms

/-- Find (t - s) inside a preform and replace it with variable k -/
@[simp]
def subSubst (x y : Preterm) (k : Nat) : Preform → Preform
  | t =* s => Preterm.subSubst x y k t =* Preterm.subSubst x y k s
  | t ≤* s => Preterm.subSubst x y k t ≤* Preterm.subSubst x y k s
  | ¬* p => ¬* p.subSubst
  | p ∨* q => p.subSubst ∨* q.subSubst
  | p ∧* q => p.subSubst ∧* q.subSubst
#align omega.nat.preform.sub_subst Omega.Nat.Preform.subSubst

end Preform

/-- Preform which asserts that the value of variable k is
    the truncated difference between preterms t and s -/
def isDiff (t s : Preterm) (k : Nat) : Preform :=
  (t =* s +* 1 ** k) ∨* (t ≤* s) ∧* (1 ** k) =* &0
#align omega.nat.is_diff Omega.Nat.isDiff

theorem holds_isDiff {t s : Preterm} {k : Nat} {v : Nat → Nat} :
    v k = t.val v - s.val v → (isDiff t s k).Holds v :=
  by
  intro h1
  simp only [preform.holds, is_diff, if_pos (Eq.refl 1), preterm.val_add, preterm.val_var,
    preterm.val_const]
  cases' le_total (t.val v) (s.val v) with h2 h2
  · right
    refine' ⟨h2, _⟩
    rw [h1, one_mul, tsub_eq_zero_iff_le]
    exact h2
  · left
    rw [h1, one_mul, add_comm, tsub_add_cancel_of_le h2]
#align omega.nat.holds_is_diff Omega.Nat.holds_isDiff

/-- Helper function for sub_elim -/
def subElimCore (t s : Preterm) (k : Nat) (p : Preform) : Preform :=
  Preform.subSubst t s k p ∧* isDiff t s k
#align omega.nat.sub_elim_core Omega.Nat.subElimCore

/-- Return de Brujin index of fresh variable that does not occur
    in any of the arguments -/
def subFreshIndex (t s : Preterm) (p : Preform) : Nat :=
  max p.freshIndex (max t.freshIndex s.freshIndex)
#align omega.nat.sub_fresh_index Omega.Nat.subFreshIndex

/-- Return a new preform with all subtractions eliminated -/
def subElim (t s : Preterm) (p : Preform) : Preform :=
  subElimCore t s (subFreshIndex t s p) p
#align omega.nat.sub_elim Omega.Nat.subElim

theorem subSubst_equiv {k : Nat} {x y : Preterm} {v : Nat → Nat} :
    ∀ p : Preform,
      p.freshIndex ≤ k →
        ((Preform.subSubst x y k p).Holds (update k (x.val v - y.val v) v) ↔ p.Holds v)
  | t =* s, h1 => by
    simp only [preform.holds, preform.sub_subst]
    apply pred_mono_2 <;> apply preterm.val_sub_subst (le_trans _ h1)
    apply le_max_left; apply le_max_right
  | t ≤* s, h1 => by
    simp only [preform.holds, preform.sub_subst]
    apply pred_mono_2 <;> apply preterm.val_sub_subst (le_trans _ h1)
    apply le_max_left; apply le_max_right
  | ¬* p, h1 => by
    apply not_congr
    apply sub_subst_equiv p h1
  | p ∨* q, h1 => by
    simp only [preform.holds, preform.sub_subst]
    apply pred_mono_2 <;> apply propext <;> apply sub_subst_equiv _ (le_trans _ h1)
    apply le_max_left; apply le_max_right
  | p ∧* q, h1 => by
    simp only [preform.holds, preform.sub_subst]
    apply pred_mono_2 <;> apply propext <;> apply sub_subst_equiv _ (le_trans _ h1)
    apply le_max_left; apply le_max_right
#align omega.nat.sub_subst_equiv Omega.Nat.subSubst_equiv

theorem sat_subElim {t s : Preterm} {p : Preform} : p.Sat → (subElim t s p).Sat :=
  by
  intro h1; simp only [sub_elim, sub_elim_core]
  cases' h1 with v h1
  refine' ⟨update (sub_fresh_index t s p) (t.val v - s.val v) v, _⟩
  constructor
  · apply (sub_subst_equiv p _).right h1
    apply le_max_left
  · apply holds_is_diff
    rw [update_eq]
    apply fun_mono_2 <;> apply preterm.val_constant <;> intro x h2 <;>
          rw [update_eq_of_ne _ (Ne.symm (ne_of_gt _))] <;>
        apply lt_of_lt_of_le h2 <;>
      apply le_trans _ (le_max_right _ _)
    apply le_max_left
    apply le_max_right
#align omega.nat.sat_sub_elim Omega.Nat.sat_subElim

theorem unsat_of_unsat_subElim (t s : Preterm) (p : Preform) : (subElim t s p).Unsat → p.Unsat :=
  mt sat_subElim
#align omega.nat.unsat_of_unsat_sub_elim Omega.Nat.unsat_of_unsat_subElim

end Nat

end Omega


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

import algebra.ordered_group
import algebra.group_with_zero
import algebra.group_with_zero.power
import tactic.abel

/-!
# Linearly ordered commutative groups and monoids with a zero element adjoined

This file sets up a special class of linearly ordered commutative monoids
that show up as the target of so-called “valuations” in algebraic number theory.

Usually, in the informal literature, these objects are constructed
by taking a linearly ordered commutative group Γ and formally adjoining a zero element: Γ ∪ {0}.

The disadvantage is that a type such as `nnreal` is not of that form,
whereas it is a very common target for valuations.
The solutions is to use a typeclass, and that is exactly what we do in this file.

Note that to avoid issues with import cycles, `linear_ordered_comm_monoid_with_zero` is defined
in another file. However, the lemmas about it are stated here.
-/

set_option old_structure_cmd true

/-- A linearly ordered commutative group with a zero element. -/
class linear_ordered_comm_group_with_zero (α : Type*)
  extends linear_ordered_comm_monoid_with_zero α, comm_group_with_zero α

variables {α : Type*}
variables {a b c d x y z : α}

instance [linear_ordered_add_comm_monoid_with_top α] :
  linear_ordered_comm_monoid_with_zero (multiplicative (order_dual α)) :=
{ zero := multiplicative.of_add (⊤ : α),
  zero_mul := top_add,
  mul_zero := add_top,
  zero_le_one := (le_top : (0 : α) ≤ ⊤),
  ..multiplicative.ordered_comm_monoid,
  ..multiplicative.linear_order }

section linear_ordered_comm_monoid

variables [linear_ordered_comm_monoid_with_zero α]
/-
The following facts are true more generally in a (linearly) ordered commutative monoid.
-/

/-- Pullback a `linear_ordered_comm_monoid_with_zero` under an injective map. -/
def function.injective.linear_ordered_comm_monoid_with_zero {β : Type*}
  [has_zero β] [has_one β] [has_mul β]
  (f : β → α) (hf : function.injective f) (zero : f 0 = 0) (one : f 1 = 1)
  (mul : ∀ x y, f (x * y) = f x * f y) :
  linear_ordered_comm_monoid_with_zero β :=
{ zero_le_one := show f 0 ≤ f 1, by simp only [zero, one,
    linear_ordered_comm_monoid_with_zero.zero_le_one],
  ..linear_order.lift f hf,
  ..hf.ordered_comm_monoid f one mul,
  ..hf.comm_monoid_with_zero f zero one mul }

lemma one_le_pow_of_one_le' {n : ℕ} (H : 1 ≤ x) : 1 ≤ x^n :=
begin
  induction n with n ih,
  { rw pow_zero },
  { rw pow_succ, exact one_le_mul H ih }
end

lemma pow_le_one_of_le_one {n : ℕ} (H : x ≤ 1) : x^n ≤ 1 :=
begin
  induction n with n ih,
  { rw pow_zero },
  { rw pow_succ, exact mul_le_one' H ih }
end

lemma eq_one_of_pow_eq_one {n : ℕ} (hn : n ≠ 0) (H : x ^ n = 1) : x = 1 :=
begin
  rcases nat.exists_eq_succ_of_ne_zero hn with ⟨n, rfl⟩, clear hn,
  induction n with n ih,
  { simpa using H },
  { cases le_total x 1 with h,
    all_goals
    { have h1 := mul_le_mul_right' h (x ^ (n + 1)),
      rw pow_succ at H,
      rw [H, one_mul] at h1 },
    { have h2 := pow_le_one_of_le_one h,
      exact ih (le_antisymm h2 h1) },
    { have h2 := one_le_pow_of_one_le' h,
      exact ih (le_antisymm h1 h2) } }
end

lemma pow_eq_one_iff {n : ℕ} (hn : n ≠ 0) : x ^ n = 1 ↔ x = 1 :=
⟨eq_one_of_pow_eq_one hn, by { rintro rfl, exact one_pow _ }⟩

lemma one_le_pow_iff {n : ℕ} (hn : n ≠ 0) : 1 ≤ x^n ↔ 1 ≤ x :=
begin
  refine ⟨_, one_le_pow_of_one_le'⟩,
  contrapose!,
  intro h, apply lt_of_le_of_ne (pow_le_one_of_le_one (le_of_lt h)),
  rw [ne.def, pow_eq_one_iff hn],
  exact ne_of_lt h,
end

lemma pow_le_one_iff {n : ℕ} (hn : n ≠ 0) : x^n ≤ 1 ↔ x ≤ 1 :=
begin
  refine ⟨_, pow_le_one_of_le_one⟩,
  contrapose!,
  intro h, apply lt_of_le_of_ne (one_le_pow_of_one_le' (le_of_lt h)),
  rw [ne.def, eq_comm, pow_eq_one_iff hn],
  exact ne_of_gt h,
end

lemma zero_le_one' : (0 : α) ≤ 1 :=
linear_ordered_comm_monoid_with_zero.zero_le_one

@[simp] lemma zero_le' : 0 ≤ a :=
by simpa only [mul_zero, mul_one] using mul_le_mul_left' (@zero_le_one' α _) a

@[simp] lemma not_lt_zero' : ¬a < 0 :=
not_lt_of_le zero_le'

@[simp] lemma le_zero_iff : a ≤ 0 ↔ a = 0 :=
⟨λ h, le_antisymm h zero_le', λ h, h ▸ le_refl _⟩

lemma zero_lt_iff : 0 < a ↔ a ≠ 0 :=
⟨ne_of_gt, λ h, lt_of_le_of_ne zero_le' h.symm⟩

lemma ne_zero_of_lt (h : b < a) : a ≠ 0 :=
λ h1, not_lt_zero' $ show b < 0, from h1 ▸ h

lemma pow_pos_iff [no_zero_divisors α] {n : ℕ} (hn : 0 < n) : 0 < a ^ n ↔ 0 < a :=
by simp_rw [zero_lt_iff, pow_ne_zero_iff hn]

instance : linear_ordered_add_comm_monoid_with_top (additive (order_dual α)) :=
{ top := (0 : α),
  top_add' := λ a, (zero_mul a : (0 : α) * a = 0),
  le_top := λ _, zero_le',
  ..additive.ordered_add_comm_monoid,
  ..additive.linear_order }

end linear_ordered_comm_monoid

variables [linear_ordered_comm_group_with_zero α]

lemma zero_lt_one'' : (0 : α) < 1 :=
lt_of_le_of_ne zero_le_one' zero_ne_one

lemma le_of_le_mul_right (h : c ≠ 0) (hab : a * c ≤ b * c) : a ≤ b :=
by simpa only [mul_inv_cancel_right' h] using (mul_le_mul_right' hab c⁻¹)

lemma le_mul_inv_of_mul_le (h : c ≠ 0) (hab : a * c ≤ b) : a ≤ b * c⁻¹ :=
le_of_le_mul_right h (by simpa [h] using hab)

lemma mul_inv_le_of_le_mul (h : c ≠ 0) (hab : a ≤ b * c) : a * c⁻¹ ≤ b :=
le_of_le_mul_right h (by simpa [h] using hab)

lemma div_le_div' (a b c d : α) (hb : b ≠ 0) (hd : d ≠ 0) :
  a * b⁻¹ ≤ c * d⁻¹ ↔ a * d ≤ c * b :=
begin
  by_cases ha : a = 0, { simp [ha] },
  by_cases hc : c = 0, { simp [inv_ne_zero hb, hc, hd], },
  exact @div_le_div_iff' _ _ (units.mk0 a ha) (units.mk0 b hb) (units.mk0 c hc) (units.mk0 d hd)
end

@[simp] lemma units.zero_lt (u : units α) : (0 : α) < u :=
zero_lt_iff.2 $ u.ne_zero

lemma mul_lt_mul'''' (hab : a < b) (hcd : c < d) : a * c < b * d :=
have hb : b ≠ 0 := ne_zero_of_lt hab,
have hd : d ≠ 0 := ne_zero_of_lt hcd,
if ha : a = 0 then by { rw [ha, zero_mul, zero_lt_iff], exact mul_ne_zero hb hd } else
if hc : c = 0 then by { rw [hc, mul_zero, zero_lt_iff], exact mul_ne_zero hb hd } else
@mul_lt_mul''' _ _ (units.mk0 a ha) (units.mk0 b hb) (units.mk0 c hc) (units.mk0 d hd) hab hcd

lemma mul_inv_lt_of_lt_mul' (h : x < y * z) : x * z⁻¹ < y :=
have hz : z ≠ 0 := (mul_ne_zero_iff.1 $ ne_zero_of_lt h).2,
by { contrapose! h, simpa only [inv_inv'] using mul_inv_le_of_le_mul (inv_ne_zero hz) h }

lemma mul_lt_right' (c : α) (h : a < b) (hc : c ≠ 0) : a * c < b * c :=
by { contrapose! h, exact le_of_le_mul_right hc h }

lemma pow_lt_pow_succ {x : α} {n : ℕ} (hx : 1 < x) : x ^ n < x ^ n.succ :=
by { rw [← one_mul (x ^ n), pow_succ],
exact mul_lt_right' _ hx (pow_ne_zero _ $ ne_of_gt (lt_trans zero_lt_one'' hx)) }

lemma pow_lt_pow' {x : α} {m n : ℕ} (hx : 1 < x) (hmn : m < n) : x ^ m < x ^ n :=
by { induction hmn with n hmn ih, exacts [pow_lt_pow_succ hx, lt_trans ih (pow_lt_pow_succ hx)] }

lemma inv_lt_inv'' (ha : a ≠ 0) (hb : b ≠ 0) : a⁻¹ < b⁻¹ ↔ b < a :=
@inv_lt_inv_iff _ _ (units.mk0 a ha) (units.mk0 b hb)

lemma inv_le_inv'' (ha : a ≠ 0) (hb : b ≠ 0) : a⁻¹ ≤ b⁻¹ ↔ b ≤ a :=
@inv_le_inv_iff _ _ (units.mk0 a ha) (units.mk0 b hb)

namespace monoid_hom

variables {R : Type*} [ring R] (f : R →* α)

theorem map_neg_one : f (-1) = 1 :=
begin
  apply eq_one_of_pow_eq_one (nat.succ_ne_zero 1) (_ : _ ^ 2 = _),
  rw [sq, ← f.map_mul, neg_one_mul, neg_neg, f.map_one],
end

@[simp] lemma map_neg (x : R) : f (-x) = f x :=
calc f (-x) = f (-1 * x)   : by rw [neg_one_mul]
        ... = f (-1) * f x : map_mul _ _ _
        ... = f x          : by rw [f.map_neg_one, one_mul]

lemma map_sub_swap (x y : R) : f (x - y) = f (y - x) :=
calc f (x - y) = f (-(y - x)) : by rw show x - y = -(y-x), by abel
           ... = _ : map_neg _ _

end monoid_hom


/-
Copyright (c) 2018 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Johannes Hölzl

Theory of topological rings.
-/
import topology.algebra.group
import ring_theory.ideal.basic
import ring_theory.subring
import algebra.ring.prod

/-!

# Topological (semi)rings

A topological (semi)ring is a (semi)ring equipped with a topology such that all operations are
continuous. Besides this definition, this file proves that the topological closure of a subring
(resp. an ideal) is a subring (resp. an ideal) and defines products and quotients
of topological (semi)rings.

## Main Results

- `subring.topological_closure`/`subsemiring.topological_closure`: the topological closure of a
  `subring`/`subsemiring` is itself a `sub(semi)ring`.
- `prod.topological_semiring`/`prod.topological_ring`: The product of two topological (semi)rings.
- `pi.topological_semiring`/`pi.topological_ring`: The arbitrary product of topological (semi)rings.
- `ideal.closure`: The closure of an ideal is an ideal.
- `topological_ring_quotient`: The quotient of a topological ring by an ideal is a topological ring.

-/

open classical set filter topological_space
open_locale classical

section topological_ring
variables (α : Type*)

/-- A topological semiring is a semiring where addition and multiplication are continuous. -/
class topological_semiring [topological_space α] [semiring α]
  extends has_continuous_add α, has_continuous_mul α : Prop

section
variables {α} [topological_space α] [semiring α] [topological_semiring α]

/-- The (topological-space) closure of a subsemiring of a topological semiring is
itself a subsemiring. -/
def subsemiring.topological_closure (s : subsemiring α) : subsemiring α :=
{ carrier := closure (s : set α),
  ..(s.to_submonoid.topological_closure),
  ..(s.to_add_submonoid.topological_closure ) }

@[simp] lemma subsemiring.topological_closure_coe (s : subsemiring α) :
  (s.topological_closure : set α) = closure (s : set α) :=
rfl

instance subsemiring.topological_closure_topological_semiring (s : subsemiring α) :
  topological_semiring (s.topological_closure) :=
{ ..s.to_add_submonoid.topological_closure_has_continuous_add,
  ..s.to_submonoid.topological_closure_has_continuous_mul }

lemma subsemiring.subring_topological_closure (s : subsemiring α) :
  s ≤ s.topological_closure :=
subset_closure

lemma subsemiring.is_closed_topological_closure (s : subsemiring α) :
  is_closed (s.topological_closure : set α) :=
by convert is_closed_closure

lemma subsemiring.topological_closure_minimal
  (s : subsemiring α) {t : subsemiring α} (h : s ≤ t) (ht : is_closed (t : set α)) :
  s.topological_closure ≤ t :=
closure_minimal h ht

/-- The product topology on the cartesian product of two topological semirings
  makes the product into a topological semiring. -/
instance {β : Type*}
  [semiring β] [topological_space β] [topological_semiring β] : topological_semiring (α × β) :=
{}

instance {β : Type*} {C : β → Type*} [∀ b, topological_space (C b)]
  [Π b, semiring (C b)] [Π b, topological_semiring (C b)] : topological_semiring (Π b, C b) :=
{}

end

/-- A topological ring is a ring where the ring operations are continuous. -/
class topological_ring [topological_space α] [ring α]
  extends topological_add_group α, has_continuous_mul α : Prop

variables {α} [ring α] [topological_space α]

section
variables [t : topological_ring α]
@[priority 100] -- see Note [lower instance priority]
instance topological_ring.to_topological_semiring : topological_semiring α := {..t}
end

variables [topological_ring α]

/-- The product topology on the cartesian product of two topological rings
  makes the product into a topological ring. -/
instance {β : Type*}
  [ring β] [topological_space β] [topological_ring β] : topological_ring (α × β) :=
{ }

instance {β : Type*} {C : β → Type*} [Π b, topological_space (C b)]
  [Π b, ring (C b)] [∀ b, topological_ring (C b)] : topological_ring (Π b, C b) :=
{ continuous_neg := continuous_neg }

/-- In a topological ring, the left-multiplication `add_monoid_hom` is continuous. -/
lemma mul_left_continuous (x : α) : continuous (add_monoid_hom.mul_left x) :=
continuous_const.mul continuous_id

/-- In a topological ring, the right-multiplication `add_monoid_hom` is continuous. -/
lemma mul_right_continuous (x : α) : continuous (add_monoid_hom.mul_right x) :=
continuous_id.mul continuous_const

/-- The (topological-space) closure of a subring of a topological semiring is
itself a subring. -/
def subring.topological_closure (S : subring α) : subring α :=
{ carrier := closure (S : set α),
  ..S.to_submonoid.topological_closure,
  ..S.to_add_subgroup.topological_closure }

instance subring.topological_closure_topological_ring (s : subring α) :
  topological_ring (s.topological_closure) :=
{ ..s.to_add_subgroup.topological_closure_topological_add_group,
  ..s.to_submonoid.topological_closure_has_continuous_mul }

lemma subring.subring_topological_closure (s : subring α) :
  s ≤ s.topological_closure := subset_closure

lemma subring.is_closed_topological_closure (s : subring α) :
  is_closed (s.topological_closure : set α) := by convert is_closed_closure

lemma subring.topological_closure_minimal
  (s : subring α) {t : subring α} (h : s ≤ t) (ht : is_closed (t : set α)) :
  s.topological_closure ≤ t := closure_minimal h ht

end topological_ring

section topological_comm_ring
variables {α : Type*} [topological_space α] [comm_ring α] [topological_ring α]

/-- The closure of an ideal in a topological ring as an ideal. -/
def ideal.closure (S : ideal α) : ideal α :=
{ carrier   := closure S,
  smul_mem' := λ c x hx, map_mem_closure (mul_left_continuous _) hx $ λ a, S.mul_mem_left c,
  ..(add_submonoid.topological_closure S.to_add_submonoid) }

@[simp] lemma ideal.coe_closure (S : ideal α) : (S.closure : set α) = closure S := rfl

end topological_comm_ring

section topological_ring
variables {α : Type*} [topological_space α] [comm_ring α] (N : ideal α)
open ideal.quotient

instance topological_ring_quotient_topology : topological_space N.quotient :=
by dunfold ideal.quotient submodule.quotient; apply_instance

-- note for the reader: in the following, `mk` is `ideal.quotient.mk`, the canonical map `R → R/I`.

variable [topological_ring α]

lemma quotient_ring.is_open_map_coe : is_open_map (mk N) :=
begin
  intros s s_op,
  change is_open (mk N ⁻¹' (mk N '' s)),
  rw quotient_ring_saturate,
  exact is_open_Union (λ ⟨n, _⟩, is_open_map_add_left n s s_op)
end

lemma quotient_ring.quotient_map_coe_coe : quotient_map (λ p : α × α, (mk N p.1, mk N p.2)) :=
is_open_map.to_quotient_map
((quotient_ring.is_open_map_coe N).prod (quotient_ring.is_open_map_coe N))
((continuous_quot_mk.comp continuous_fst).prod_mk (continuous_quot_mk.comp continuous_snd))
(by rintro ⟨⟨x⟩, ⟨y⟩⟩; exact ⟨(x, y), rfl⟩)

instance topological_ring_quotient : topological_ring N.quotient :=
{ continuous_add :=
    have cont : continuous (mk N ∘ (λ (p : α × α), p.fst + p.snd)) :=
      continuous_quot_mk.comp continuous_add,
    (quotient_map.continuous_iff (quotient_ring.quotient_map_coe_coe N)).mpr cont,
  continuous_neg :=
    by convert continuous_quotient_lift _ (continuous_quot_mk.comp continuous_neg); apply_instance,
  continuous_mul :=
    have cont : continuous (mk N ∘ (λ (p : α × α), p.fst * p.snd)) :=
      continuous_quot_mk.comp continuous_mul,
    (quotient_map.continuous_iff (quotient_ring.quotient_map_coe_coe N)).mpr cont }

end topological_ring

section

-- namespace foo

-- universe u v

variables {α: Type*}[ring α]

@[ext]
class ring_topology (α : Type u)[ring α]
  extends topological_space α,
  topological_ring α : Type u

@[ext]
lemma ring_topology_eq {f g : ring_topology α}: f.is_open = g.is_open → f = g
:= by {intros h, ext, rw h}

end


/-
Copyright (c) 2019 Alexander Bentkamp. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alexander Bentkamp, Yury Kudriashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.set.intervals.ord_connected
import Mathlib.data.set.intervals.image_preimage
import Mathlib.data.complex.module
import Mathlib.linear_algebra.affine_space.affine_map
import Mathlib.algebra.module.ordered
import Mathlib.PostPort

universes u u_1 v v' w x u'

namespace Mathlib

/-!
# Convex sets and functions on real vector spaces

In a real vector space, we define the following objects and properties.

* `segment x y` is the closed segment joining `x` and `y`.
* A set `s` is `convex` if for any two points `x y ∈ s` it includes `segment x y`;
* A function `f : E → β` is `convex_on` a set `s` if `s` is itself a convex set, and for any two
  points `x y ∈ s` the segment joining `(x, f x)` to `(y, f y)` is (non-strictly) above the graph
  of `f`; equivalently, `convex_on f s` means that the epigraph
  `{p : E × β | p.1 ∈ s ∧ f p.1 ≤ p.2}` is a convex set;
* Center mass of a finite set of points with prescribed weights.
* Convex hull of a set `s` is the minimal convex set that includes `s`.
* Standard simplex `std_simplex ι [fintype ι]` is the intersection of the positive quadrant with
  the hyperplane `s.sum = 1` in the space `ι → ℝ`.

We also provide various equivalent versions of the definitions above, prove that some specific sets
are convex, and prove Jensen's inequality.

Note: To define convexity for functions `f : E → β`, we need `β` to be an ordered vector space,
defined using the instance `ordered_semimodule ℝ β`.

## Notations

We use the following local notations:

* `I = Icc (0:ℝ) 1`;
* `[x, y] = segment x y`.

They are defined using `local notation`, so they are not available outside of this file.
-/

/-! ### Segment -/

/-- Segments in a vector space. -/
def segment {E : Type u} [add_comm_group E] [vector_space ℝ E] (x : E) (y : E) : set E :=
  set_of
    fun (z : E) =>
      ∃ (a : ℝ), ∃ (b : ℝ), ∃ (ha : 0 ≤ a), ∃ (hb : 0 ≤ b), ∃ (hab : a + b = 1), a • x + b • y = z

theorem segment_symm {E : Type u} [add_comm_group E] [vector_space ℝ E] (x : E) (y : E) :
    segment x y = segment y x :=
  sorry

theorem left_mem_segment {E : Type u} [add_comm_group E] [vector_space ℝ E] (x : E) (y : E) :
    x ∈ segment x y :=
  sorry

theorem right_mem_segment {E : Type u} [add_comm_group E] [vector_space ℝ E] (x : E) (y : E) :
    y ∈ segment x y :=
  segment_symm y x ▸ left_mem_segment y x

theorem segment_same {E : Type u} [add_comm_group E] [vector_space ℝ E] (x : E) :
    segment x x = singleton x :=
  sorry

theorem segment_eq_image {E : Type u} [add_comm_group E] [vector_space ℝ E] (x : E) (y : E) :
    segment x y = (fun (θ : ℝ) => (1 - θ) • x + θ • y) '' set.Icc 0 1 :=
  sorry

theorem segment_eq_image' {E : Type u} [add_comm_group E] [vector_space ℝ E] (x : E) (y : E) :
    segment x y = (fun (θ : ℝ) => x + θ • (y - x)) '' set.Icc 0 1 :=
  sorry

theorem segment_eq_image₂ {E : Type u} [add_comm_group E] [vector_space ℝ E] (x : E) (y : E) :
    segment x y =
        (fun (p : ℝ × ℝ) => prod.fst p • x + prod.snd p • y) ''
          set_of fun (p : ℝ × ℝ) => 0 ≤ prod.fst p ∧ 0 ≤ prod.snd p ∧ prod.fst p + prod.snd p = 1 :=
  sorry

theorem segment_eq_Icc {a : ℝ} {b : ℝ} (h : a ≤ b) : segment a b = set.Icc a b := sorry

theorem segment_eq_Icc' (a : ℝ) (b : ℝ) : segment a b = set.Icc (min a b) (max a b) := sorry

theorem segment_eq_interval (a : ℝ) (b : ℝ) : segment a b = set.interval a b := segment_eq_Icc' a b

theorem mem_segment_translate {E : Type u} [add_comm_group E] [vector_space ℝ E] (a : E) {x : E}
    {b : E} {c : E} : a + x ∈ segment (a + b) (a + c) ↔ x ∈ segment b c :=
  sorry

theorem segment_translate_preimage {E : Type u} [add_comm_group E] [vector_space ℝ E] (a : E)
    (b : E) (c : E) : (fun (x : E) => a + x) ⁻¹' segment (a + b) (a + c) = segment b c :=
  set.ext fun (x : E) => mem_segment_translate a

theorem segment_translate_image {E : Type u} [add_comm_group E] [vector_space ℝ E] (a : E) (b : E)
    (c : E) : (fun (x : E) => a + x) '' segment b c = segment (a + b) (a + c) :=
  Eq.subst (segment_translate_preimage a b c) (set.image_preimage_eq (segment (a + b) (a + c)))
    (add_left_surjective a)

/-! ### Convexity of sets -/

/-- Convexity of sets. -/
def convex {E : Type u} [add_comm_group E] [vector_space ℝ E] (s : set E) :=
  ∀ {x y : E}, x ∈ s → y ∈ s → ∀ {a b : ℝ}, 0 ≤ a → 0 ≤ b → a + b = 1 → a • x + b • y ∈ s

theorem convex_iff_forall_pos {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} :
    convex s ↔
        ∀ {x y : E}, x ∈ s → y ∈ s → ∀ {a b : ℝ}, 0 < a → 0 < b → a + b = 1 → a • x + b • y ∈ s :=
  sorry

theorem convex_iff_segment_subset {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} :
    convex s ↔ ∀ {x y : E}, x ∈ s → y ∈ s → segment x y ⊆ s :=
  sorry

theorem convex.segment_subset {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    (h : convex s) {x : E} {y : E} (hx : x ∈ s) (hy : y ∈ s) : segment x y ⊆ s :=
  iff.mp convex_iff_segment_subset h x y hx hy

/-- Alternative definition of set convexity, in terms of pointwise set operations. -/
theorem convex_iff_pointwise_add_subset {E : Type u} [add_comm_group E] [vector_space ℝ E]
    {s : set E} : convex s ↔ ∀ {a b : ℝ}, 0 ≤ a → 0 ≤ b → a + b = 1 → a • s + b • s ⊆ s :=
  sorry

/-- Alternative definition of set convexity, using division. -/
theorem convex_iff_div {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} :
    convex s ↔
        ∀ {x y : E},
          x ∈ s →
            y ∈ s →
              ∀ {a b : ℝ}, 0 ≤ a → 0 ≤ b → 0 < a + b → (a / (a + b)) • x + (b / (a + b)) • y ∈ s :=
  sorry

/-! ### Examples of convex sets -/

theorem convex_empty {E : Type u} [add_comm_group E] [vector_space ℝ E] : convex ∅ := sorry

theorem convex_singleton {E : Type u} [add_comm_group E] [vector_space ℝ E] (c : E) :
    convex (singleton c) :=
  sorry

theorem convex_univ {E : Type u} [add_comm_group E] [vector_space ℝ E] : convex set.univ :=
  fun (_x _x_1 : E) (_x : _x ∈ set.univ) (_x : _x_1 ∈ set.univ) (_x _x_2 : ℝ) (_x_3 : 0 ≤ _x)
    (_x_4 : 0 ≤ _x_2) (_x : _x + _x_2 = 1) => trivial

theorem convex.inter {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} {t : set E}
    (hs : convex s) (ht : convex t) : convex (s ∩ t) :=
  fun (x y : E) (hx : x ∈ s ∩ t) (hy : y ∈ s ∩ t) (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b)
    (hab : a + b = 1) =>
    { left := hs (and.left hx) (and.left hy) ha hb hab,
      right := ht (and.right hx) (and.right hy) ha hb hab }

theorem convex_sInter {E : Type u} [add_comm_group E] [vector_space ℝ E] {S : set (set E)}
    (h : ∀ (s : set E), s ∈ S → convex s) : convex (⋂₀S) :=
  fun (x y : E) (hx : x ∈ ⋂₀S) (hy : y ∈ ⋂₀S) (a b : ℝ) (ha : 0 ≤ a) (hb : 0 ≤ b) (hab : a + b = 1)
    (s : set E) (hs : s ∈ S) => h s hs (hx s hs) (hy s hs) ha hb hab

theorem convex_Inter {E : Type u} [add_comm_group E] [vector_space ℝ E] {ι : Sort u_1}
    {s : ι → set E} (h : ∀ (i : ι), convex (s i)) : convex (set.Inter fun (i : ι) => s i) :=
  Eq.subst (set.sInter_range s) convex_sInter (iff.mpr set.forall_range_iff h)

theorem convex.prod {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {s : set E} {t : set F} (hs : convex s) (ht : convex t) :
    convex (set.prod s t) :=
  sorry

theorem convex.combo_to_vadd {E : Type u} [add_comm_group E] [vector_space ℝ E] {a : ℝ} {b : ℝ}
    {x : E} {y : E} (h : a + b = 1) : a • x + b • y = b • (y - x) + x :=
  sorry

/--
Applying an affine map to an affine combination of two points yields
an affine combination of the images.
-/
theorem convex.combo_affine_apply {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {a : ℝ} {b : ℝ} {x : E} {y : E} {f : affine_map ℝ E F}
    (h : a + b = 1) : coe_fn f (a • x + b • y) = a • coe_fn f x + b • coe_fn f y :=
  sorry

/-- The preimage of a convex set under an affine map is convex. -/
theorem convex.affine_preimage {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] (f : affine_map ℝ E F) {s : set F} (hs : convex s) :
    convex (⇑f ⁻¹' s) :=
  sorry

/-- The image of a convex set under an affine map is convex. -/
theorem convex.affine_image {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] (f : affine_map ℝ E F) {s : set E} (hs : convex s) :
    convex (⇑f '' s) :=
  sorry

theorem convex.linear_image {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {s : set E} (hs : convex s) (f : linear_map ℝ E F) :
    convex (⇑f '' s) :=
  convex.affine_image (linear_map.to_affine_map f) hs

theorem convex.is_linear_image {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {s : set E} (hs : convex s) {f : E → F}
    (hf : is_linear_map ℝ f) : convex (f '' s) :=
  convex.linear_image hs (is_linear_map.mk' f hf)

theorem convex.linear_preimage {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {s : set F} (hs : convex s) (f : linear_map ℝ E F) :
    convex (⇑f ⁻¹' s) :=
  convex.affine_preimage (linear_map.to_affine_map f) hs

theorem convex.is_linear_preimage {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {s : set F} (hs : convex s) {f : E → F}
    (hf : is_linear_map ℝ f) : convex (f ⁻¹' s) :=
  convex.linear_preimage hs (is_linear_map.mk' f hf)

theorem convex.neg {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} (hs : convex s) :
    convex ((fun (z : E) => -z) '' s) :=
  convex.is_linear_image hs is_linear_map.is_linear_map_neg

theorem convex.neg_preimage {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    (hs : convex s) : convex ((fun (z : E) => -z) ⁻¹' s) :=
  convex.is_linear_preimage hs is_linear_map.is_linear_map_neg

theorem convex.smul {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} (c : ℝ)
    (hs : convex s) : convex (c • s) :=
  convex.linear_image hs (coe_fn (linear_map.lsmul ℝ E) c)

theorem convex.smul_preimage {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} (c : ℝ)
    (hs : convex s) : convex ((fun (z : E) => c • z) ⁻¹' s) :=
  convex.linear_preimage hs (coe_fn (linear_map.lsmul ℝ E) c)

theorem convex.add {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} {t : set E}
    (hs : convex s) (ht : convex t) : convex (s + t) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (convex (s + t))) (Eq.symm set.add_image_prod)))
    (convex.is_linear_image (convex.prod hs ht) is_linear_map.is_linear_map_add)

theorem convex.sub {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} {t : set E}
    (hs : convex s) (ht : convex t) :
    convex ((fun (x : E × E) => prod.fst x - prod.snd x) '' set.prod s t) :=
  convex.is_linear_image (convex.prod hs ht) is_linear_map.is_linear_map_sub

theorem convex.translate {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    (hs : convex s) (z : E) : convex ((fun (x : E) => z + x) '' s) :=
  convex.affine_image (affine_map.const ℝ E z +ᵥ affine_map.id ℝ E) hs

/-- The translation of a convex set is also convex. -/
theorem convex.translate_preimage_right {E : Type u} [add_comm_group E] [vector_space ℝ E]
    {s : set E} (hs : convex s) (a : E) : convex ((fun (z : E) => a + z) ⁻¹' s) :=
  convex.affine_preimage (affine_map.const ℝ E a +ᵥ affine_map.id ℝ E) hs

/-- The translation of a convex set is also convex. -/
theorem convex.translate_preimage_left {E : Type u} [add_comm_group E] [vector_space ℝ E]
    {s : set E} (hs : convex s) (a : E) : convex ((fun (z : E) => z + a) ⁻¹' s) :=
  sorry

theorem convex.affinity {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    (hs : convex s) (z : E) (c : ℝ) : convex ((fun (x : E) => z + c • x) '' s) :=
  convex.affine_image (affine_map.const ℝ E z +ᵥ c • affine_map.id ℝ E) hs

theorem real.convex_iff_ord_connected {s : set ℝ} : convex s ↔ set.ord_connected s := sorry

theorem convex.ord_connected {s : set ℝ} : convex s → set.ord_connected s :=
  iff.mp real.convex_iff_ord_connected

theorem convex_Iio (r : ℝ) : convex (set.Iio r) := set.ord_connected.convex set.ord_connected_Iio

theorem convex_Ioi (r : ℝ) : convex (set.Ioi r) := set.ord_connected.convex set.ord_connected_Ioi

theorem convex_Iic (r : ℝ) : convex (set.Iic r) := set.ord_connected.convex set.ord_connected_Iic

theorem convex_Ici (r : ℝ) : convex (set.Ici r) := set.ord_connected.convex set.ord_connected_Ici

theorem convex_Ioo (r : ℝ) (s : ℝ) : convex (set.Ioo r s) :=
  set.ord_connected.convex set.ord_connected_Ioo

theorem convex_Ico (r : ℝ) (s : ℝ) : convex (set.Ico r s) :=
  set.ord_connected.convex set.ord_connected_Ico

theorem convex_Ioc (r : ℝ) (s : ℝ) : convex (set.Ioc r s) :=
  set.ord_connected.convex set.ord_connected_Ioc

theorem convex_Icc (r : ℝ) (s : ℝ) : convex (set.Icc r s) :=
  set.ord_connected.convex set.ord_connected_Icc

theorem convex_interval (r : ℝ) (s : ℝ) : convex (set.interval r s) :=
  set.ord_connected.convex set.ord_connected_interval

theorem convex_segment {E : Type u} [add_comm_group E] [vector_space ℝ E] (a : E) (b : E) :
    convex (segment a b) :=
  sorry

theorem convex_halfspace_lt {E : Type u} [add_comm_group E] [vector_space ℝ E] {f : E → ℝ}
    (h : is_linear_map ℝ f) (r : ℝ) : convex (set_of fun (w : E) => f w < r) :=
  convex.is_linear_preimage (convex_Iio r) h

theorem convex_halfspace_le {E : Type u} [add_comm_group E] [vector_space ℝ E] {f : E → ℝ}
    (h : is_linear_map ℝ f) (r : ℝ) : convex (set_of fun (w : E) => f w ≤ r) :=
  convex.is_linear_preimage (convex_Iic r) h

theorem convex_halfspace_gt {E : Type u} [add_comm_group E] [vector_space ℝ E] {f : E → ℝ}
    (h : is_linear_map ℝ f) (r : ℝ) : convex (set_of fun (w : E) => r < f w) :=
  convex.is_linear_preimage (convex_Ioi r) h

theorem convex_halfspace_ge {E : Type u} [add_comm_group E] [vector_space ℝ E] {f : E → ℝ}
    (h : is_linear_map ℝ f) (r : ℝ) : convex (set_of fun (w : E) => r ≤ f w) :=
  convex.is_linear_preimage (convex_Ici r) h

theorem convex_hyperplane {E : Type u} [add_comm_group E] [vector_space ℝ E] {f : E → ℝ}
    (h : is_linear_map ℝ f) (r : ℝ) : convex (set_of fun (w : E) => f w = r) :=
  id
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (convex (f ⁻¹' set_of fun (p : ℝ) => p = r)))
          set.set_of_eq_eq_singleton))
      (convex.is_linear_preimage (convex_singleton r) h))

theorem convex_halfspace_re_lt (r : ℝ) : convex (set_of fun (c : ℂ) => complex.re c < r) :=
  convex_halfspace_lt (is_linear_map.mk complex.add_re complex.smul_re) r

theorem convex_halfspace_re_le (r : ℝ) : convex (set_of fun (c : ℂ) => complex.re c ≤ r) :=
  convex_halfspace_le (is_linear_map.mk complex.add_re complex.smul_re) r

theorem convex_halfspace_re_gt (r : ℝ) : convex (set_of fun (c : ℂ) => r < complex.re c) :=
  convex_halfspace_gt (is_linear_map.mk complex.add_re complex.smul_re) r

theorem convex_halfspace_re_lge (r : ℝ) : convex (set_of fun (c : ℂ) => r ≤ complex.re c) :=
  convex_halfspace_ge (is_linear_map.mk complex.add_re complex.smul_re) r

theorem convex_halfspace_im_lt (r : ℝ) : convex (set_of fun (c : ℂ) => complex.im c < r) :=
  convex_halfspace_lt (is_linear_map.mk complex.add_im complex.smul_im) r

theorem convex_halfspace_im_le (r : ℝ) : convex (set_of fun (c : ℂ) => complex.im c ≤ r) :=
  convex_halfspace_le (is_linear_map.mk complex.add_im complex.smul_im) r

theorem convex_halfspace_im_gt (r : ℝ) : convex (set_of fun (c : ℂ) => r < complex.im c) :=
  convex_halfspace_gt (is_linear_map.mk complex.add_im complex.smul_im) r

theorem convex_halfspace_im_lge (r : ℝ) : convex (set_of fun (c : ℂ) => r ≤ complex.im c) :=
  convex_halfspace_ge (is_linear_map.mk complex.add_im complex.smul_im) r

/-! ### Convex combinations in intervals -/

theorem convex.combo_self {α : Type v'} [linear_ordered_field α] (a : α) {x : α} {y : α}
    (h : x + y = 1) : a = x * a + y * a :=
  sorry

/--
If `x` is in an `Ioo`, it can be expressed as a convex combination of the endpoints.
-/
theorem convex.mem_Ioo {α : Type v'} [linear_ordered_field α] {a : α} {b : α} {x : α} (h : a < b) :
    x ∈ set.Ioo a b ↔
        ∃ (x_a : α), ∃ (x_b : α), 0 < x_a ∧ 0 < x_b ∧ x_a + x_b = 1 ∧ x_a * a + x_b * b = x :=
  sorry

/-- If `x` is in an `Ioc`, it can be expressed as a convex combination of the endpoints. -/
theorem convex.mem_Ioc {α : Type v'} [linear_ordered_field α] {a : α} {b : α} {x : α} (h : a < b) :
    x ∈ set.Ioc a b ↔
        ∃ (x_a : α), ∃ (x_b : α), 0 ≤ x_a ∧ 0 < x_b ∧ x_a + x_b = 1 ∧ x_a * a + x_b * b = x :=
  sorry

/-- If `x` is in an `Ico`, it can be expressed as a convex combination of the endpoints. -/
theorem convex.mem_Ico {α : Type v'} [linear_ordered_field α] {a : α} {b : α} {x : α} (h : a < b) :
    x ∈ set.Ico a b ↔
        ∃ (x_a : α), ∃ (x_b : α), 0 < x_a ∧ 0 ≤ x_b ∧ x_a + x_b = 1 ∧ x_a * a + x_b * b = x :=
  sorry

/-- If `x` is in an `Icc`, it can be expressed as a convex combination of the endpoints. -/
theorem convex.mem_Icc {α : Type v'} [linear_ordered_field α] {a : α} {b : α} {x : α} (h : a ≤ b) :
    x ∈ set.Icc a b ↔
        ∃ (x_a : α), ∃ (x_b : α), 0 ≤ x_a ∧ 0 ≤ x_b ∧ x_a + x_b = 1 ∧ x_a * a + x_b * b = x :=
  sorry

theorem submodule.convex {E : Type u} [add_comm_group E] [vector_space ℝ E] (K : submodule ℝ E) :
    convex ↑K :=
  id
    fun (x y : E) (ᾰ : x ∈ ↑K) (ᾰ_1 : y ∈ ↑K) (a b : ℝ) (ᾰ_2 : 0 ≤ a) (ᾰ_3 : 0 ≤ b)
      (ᾰ_4 : a + b = 1) =>
      submodule.add_mem K (submodule.smul_mem K a ᾰ) (submodule.smul_mem K b ᾰ_1)

theorem subspace.convex {E : Type u} [add_comm_group E] [vector_space ℝ E] (K : subspace ℝ E) :
    convex ↑K :=
  submodule.convex K

/-! ### Convex and concave functions -/

/-- Convexity of functions -/
def convex_on {E : Type u} [add_comm_group E] [vector_space ℝ E] {β : Type u_1}
    [ordered_add_comm_monoid β] [semimodule ℝ β] (s : set E) (f : E → β) :=
  convex s ∧
    ∀ {x y : E},
      x ∈ s → y ∈ s → ∀ {a b : ℝ}, 0 ≤ a → 0 ≤ b → a + b = 1 → f (a • x + b • y) ≤ a • f x + b • f y

/-- Concavity of functions -/
def concave_on {E : Type u} [add_comm_group E] [vector_space ℝ E] {β : Type u_1}
    [ordered_add_comm_monoid β] [semimodule ℝ β] (s : set E) (f : E → β) :=
  convex s ∧
    ∀ {x y : E},
      x ∈ s → y ∈ s → ∀ {a b : ℝ}, 0 ≤ a → 0 ≤ b → a + b = 1 → a • f x + b • f y ≤ f (a • x + b • y)

/-- A function `f` is concave iff `-f` is convex. -/
@[simp] theorem neg_convex_on_iff {E : Type u} [add_comm_group E] [vector_space ℝ E] {γ : Type u_1}
    [ordered_add_comm_group γ] [semimodule ℝ γ] (s : set E) (f : E → γ) :
    convex_on s (-f) ↔ concave_on s f :=
  sorry

/-- A function `f` is concave iff `-f` is convex. -/
@[simp] theorem neg_concave_on_iff {E : Type u} [add_comm_group E] [vector_space ℝ E] {γ : Type u_1}
    [ordered_add_comm_group γ] [semimodule ℝ γ] (s : set E) (f : E → γ) :
    concave_on s (-f) ↔ convex_on s f :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (concave_on s (-f) ↔ convex_on s f))
        (Eq.symm (propext (neg_convex_on_iff s (-f))))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (convex_on s ( --f) ↔ convex_on s f)) (neg_neg f)))
      (iff.refl (convex_on s f)))

theorem convex_on_id {s : set ℝ} (hs : convex s) : convex_on s id := sorry

theorem concave_on_id {s : set ℝ} (hs : convex s) : concave_on s id := sorry

theorem convex_on_const {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] (c : β) (hs : convex s) :
    convex_on s fun (x : E) => c :=
  sorry

theorem concave_on_const {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] (c : β) (hs : convex s) :
    concave_on s fun (x : E) => c :=
  convex_on_const c hs

theorem convex_on_iff_div {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} :
    convex_on s f ↔
        convex s ∧
          ∀ {x y : E},
            x ∈ s →
              y ∈ s →
                ∀ {a b : ℝ},
                  0 ≤ a →
                    0 ≤ b →
                      0 < a + b →
                        f ((a / (a + b)) • x + (b / (a + b)) • y) ≤
                          (a / (a + b)) • f x + (b / (a + b)) • f y :=
  sorry

theorem concave_on_iff_div {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} :
    concave_on s f ↔
        convex s ∧
          ∀ {x y : E},
            x ∈ s →
              y ∈ s →
                ∀ {a b : ℝ},
                  0 ≤ a →
                    0 ≤ b →
                      0 < a + b →
                        (a / (a + b)) • f x + (b / (a + b)) • f y ≤
                          f ((a / (a + b)) • x + (b / (a + b)) • y) :=
  convex_on_iff_div

/-- For a function on a convex set in a linear ordered space, in order to prove that it is convex
it suffices to verify the inequality `f (a • x + b • y) ≤ a • f x + b • f y` only for `x < y`
and positive `a`, `b`. The main use case is `E = ℝ` however one can apply it, e.g., to `ℝ^n` with
lexicographic order. -/
theorem linear_order.convex_on_of_lt {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} [linear_order E]
    (hs : convex s)
    (hf :
      ∀ {x y : E},
        x ∈ s →
          y ∈ s →
            x < y →
              ∀ {a b : ℝ}, 0 < a → 0 < b → a + b = 1 → f (a • x + b • y) ≤ a • f x + b • f y) :
    convex_on s f :=
  sorry

/-- For a function on a convex set in a linear ordered space, in order to prove that it is concave
it suffices to verify the inequality `a • f x + b • f y ≤ f (a • x + b • y)` only for `x < y`
and positive `a`, `b`. The main use case is `E = ℝ` however one can apply it, e.g., to `ℝ^n` with
lexicographic order. -/
theorem linear_order.concave_on_of_lt {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} [linear_order E]
    (hs : convex s)
    (hf :
      ∀ {x y : E},
        x ∈ s →
          y ∈ s →
            x < y →
              ∀ {a b : ℝ}, 0 < a → 0 < b → a + b = 1 → a • f x + b • f y ≤ f (a • x + b • y)) :
    concave_on s f :=
  linear_order.convex_on_of_lt hs hf

/-- For a function `f` defined on a convex subset `D` of `ℝ`, if for any three points `x<y<z`
the slope of the secant line of `f` on `[x, y]` is less than or equal to the slope
of the secant line of `f` on `[x, z]`, then `f` is convex on `D`. This way of proving convexity
of a function is used in the proof of convexity of a function with a monotone derivative. -/
theorem convex_on_real_of_slope_mono_adjacent {s : set ℝ} (hs : convex s) {f : ℝ → ℝ}
    (hf :
      ∀ {x y z : ℝ},
        x ∈ s → z ∈ s → x < y → y < z → (f y - f x) / (y - x) ≤ (f z - f y) / (z - y)) :
    convex_on s f :=
  sorry

/-- For a function `f` defined on a subset `D` of `ℝ`, if `f` is convex on `D`, then for any three
points `x<y<z`, the slope of the secant line of `f` on `[x, y]` is less than or equal to the slope
of the secant line of `f` on `[x, z]`. -/
theorem convex_on.slope_mono_adjacent {s : set ℝ} {f : ℝ → ℝ} (hf : convex_on s f) {x : ℝ} {y : ℝ}
    {z : ℝ} (hx : x ∈ s) (hz : z ∈ s) (hxy : x < y) (hyz : y < z) :
    (f y - f x) / (y - x) ≤ (f z - f y) / (z - y) :=
  sorry

/-- For a function `f` defined on a convex subset `D` of `ℝ`, `f` is convex on `D` iff for any three
points `x<y<z` the slope of the secant line of `f` on `[x, y]` is less than or equal to the slope
of the secant line of `f` on `[x, z]`. -/
theorem convex_on_real_iff_slope_mono_adjacent {s : set ℝ} (hs : convex s) {f : ℝ → ℝ} :
    convex_on s f ↔
        ∀ {x y z : ℝ},
          x ∈ s → z ∈ s → x < y → y < z → (f y - f x) / (y - x) ≤ (f z - f y) / (z - y) :=
  { mp := convex_on.slope_mono_adjacent, mpr := convex_on_real_of_slope_mono_adjacent hs }

/-- For a function `f` defined on a convex subset `D` of `ℝ`, if for any three points `x<y<z`
the slope of the secant line of `f` on `[x, y]` is greater than or equal to the slope
of the secant line of `f` on `[x, z]`, then `f` is concave on `D`. -/
theorem concave_on_real_of_slope_mono_adjacent {s : set ℝ} (hs : convex s) {f : ℝ → ℝ}
    (hf :
      ∀ {x y z : ℝ},
        x ∈ s → z ∈ s → x < y → y < z → (f z - f y) / (z - y) ≤ (f y - f x) / (y - x)) :
    concave_on s f :=
  sorry

/-- For a function `f` defined on a subset `D` of `ℝ`, if `f` is concave on `D`, then for any three
points `x<y<z`, the slope of the secant line of `f` on `[x, y]` is greater than or equal to the
slope of the secant line of `f` on `[x, z]`. -/
theorem concave_on.slope_mono_adjacent {s : set ℝ} {f : ℝ → ℝ} (hf : concave_on s f) {x : ℝ} {y : ℝ}
    {z : ℝ} (hx : x ∈ s) (hz : z ∈ s) (hxy : x < y) (hyz : y < z) :
    (f z - f y) / (z - y) ≤ (f y - f x) / (y - x) :=
  sorry

/-- For a function `f` defined on a convex subset `D` of `ℝ`, `f` is concave on `D` iff for any
three points `x<y<z` the slope of the secant line of `f` on `[x, y]` is greater than or equal to
the slope of the secant line of `f` on `[x, z]`. -/
theorem concave_on_real_iff_slope_mono_adjacent {s : set ℝ} (hs : convex s) {f : ℝ → ℝ} :
    concave_on s f ↔
        ∀ {x y z : ℝ},
          x ∈ s → z ∈ s → x < y → y < z → (f z - f y) / (z - y) ≤ (f y - f x) / (y - x) :=
  { mp := concave_on.slope_mono_adjacent, mpr := concave_on_real_of_slope_mono_adjacent hs }

theorem convex_on.subset {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] {t : set E} {f : E → β}
    (h_convex_on : convex_on t f) (h_subset : s ⊆ t) (h_convex : convex s) : convex_on s f :=
  { left := h_convex,
    right :=
      fun (x y : E) (hx : x ∈ s) (hy : y ∈ s) =>
        and.right h_convex_on x y (h_subset hx) (h_subset hy) }

theorem concave_on.subset {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] {t : set E} {f : E → β}
    (h_concave_on : concave_on t f) (h_subset : s ⊆ t) (h_convex : convex s) : concave_on s f :=
  convex_on.subset h_concave_on h_subset h_convex

theorem convex_on.add {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} {β : Type u_1}
    [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} {g : E → β} (hf : convex_on s f)
    (hg : convex_on s g) : convex_on s fun (x : E) => f x + g x :=
  sorry

theorem concave_on.add {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} {β : Type u_1}
    [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} {g : E → β} (hf : concave_on s f)
    (hg : concave_on s g) : concave_on s fun (x : E) => f x + g x :=
  convex_on.add hf hg

theorem convex_on.smul {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} {β : Type u_1}
    [ordered_add_comm_monoid β] [semimodule ℝ β] [ordered_semimodule ℝ β] {f : E → β} {c : ℝ}
    (hc : 0 ≤ c) (hf : convex_on s f) : convex_on s fun (x : E) => c • f x :=
  sorry

theorem concave_on.smul {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] [ordered_semimodule ℝ β] {f : E → β}
    {c : ℝ} (hc : 0 ≤ c) (hf : concave_on s f) : concave_on s fun (x : E) => c • f x :=
  convex_on.smul hc hf

/-- A convex function on a segment is upper-bounded by the max of its endpoints. -/
theorem convex_on.le_on_segment' {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {γ : Type u_1} [linear_ordered_add_comm_group γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ]
    {f : E → γ} {x : E} {y : E} {a : ℝ} {b : ℝ} (hf : convex_on s f) (hx : x ∈ s) (hy : y ∈ s)
    (ha : 0 ≤ a) (hb : 0 ≤ b) (hab : a + b = 1) : f (a • x + b • y) ≤ max (f x) (f y) :=
  sorry

/-- A concave function on a segment is lower-bounded by the min of its endpoints. -/
theorem concave_on.le_on_segment' {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {γ : Type u_1} [linear_ordered_add_comm_group γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ]
    {f : E → γ} {x : E} {y : E} {a : ℝ} {b : ℝ} (hf : concave_on s f) (hx : x ∈ s) (hy : y ∈ s)
    (ha : 0 ≤ a) (hb : 0 ≤ b) (hab : a + b = 1) : min (f x) (f y) ≤ f (a • x + b • y) :=
  convex_on.le_on_segment' hf hx hy ha hb hab

/-- A convex function on a segment is upper-bounded by the max of its endpoints. -/
theorem convex_on.le_on_segment {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {γ : Type u_1} [linear_ordered_add_comm_group γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ]
    {f : E → γ} (hf : convex_on s f) {x : E} {y : E} {z : E} (hx : x ∈ s) (hy : y ∈ s)
    (hz : z ∈ segment x y) : f z ≤ max (f x) (f y) :=
  sorry

/-- A concave function on a segment is lower-bounded by the min of its endpoints. -/
theorem concave_on.le_on_segment {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {γ : Type u_1} [linear_ordered_add_comm_group γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ]
    {f : E → γ} (hf : concave_on s f) {x : E} {y : E} {z : E} (hx : x ∈ s) (hy : y ∈ s)
    (hz : z ∈ segment x y) : min (f x) (f y) ≤ f z :=
  convex_on.le_on_segment hf hx hy hz

theorem convex_on.convex_le {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] [ordered_semimodule ℝ β] {f : E → β}
    (hf : convex_on s f) (r : β) : convex (has_sep.sep (fun (x : E) => f x ≤ r) s) :=
  sorry

theorem concave_on.concave_le {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {β : Type u_1} [ordered_add_comm_monoid β] [semimodule ℝ β] [ordered_semimodule ℝ β] {f : E → β}
    (hf : concave_on s f) (r : β) : convex (has_sep.sep (fun (x : E) => r ≤ f x) s) :=
  convex_on.convex_le hf r

theorem convex_on.convex_lt {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {γ : Type u_1} [ordered_cancel_add_comm_monoid γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ]
    {f : E → γ} (hf : convex_on s f) (r : γ) : convex (has_sep.sep (fun (x : E) => f x < r) s) :=
  sorry

theorem concave_on.convex_lt {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {γ : Type u_1} [ordered_cancel_add_comm_monoid γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ]
    {f : E → γ} (hf : concave_on s f) (r : γ) : convex (has_sep.sep (fun (x : E) => r < f x) s) :=
  convex_on.convex_lt hf r

theorem convex_on.convex_epigraph {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {γ : Type u_1} [ordered_add_comm_group γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ] {f : E → γ}
    (hf : convex_on s f) :
    convex (set_of fun (p : E × γ) => prod.fst p ∈ s ∧ f (prod.fst p) ≤ prod.snd p) :=
  sorry

theorem concave_on.convex_hypograph {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {γ : Type u_1} [ordered_add_comm_group γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ] {f : E → γ}
    (hf : concave_on s f) :
    convex (set_of fun (p : E × γ) => prod.fst p ∈ s ∧ prod.snd p ≤ f (prod.fst p)) :=
  convex_on.convex_epigraph hf

theorem convex_on_iff_convex_epigraph {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {γ : Type u_1} [ordered_add_comm_group γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ]
    {f : E → γ} :
    convex_on s f ↔
        convex (set_of fun (p : E × γ) => prod.fst p ∈ s ∧ f (prod.fst p) ≤ prod.snd p) :=
  sorry

theorem concave_on_iff_convex_hypograph {E : Type u} [add_comm_group E] [vector_space ℝ E]
    {s : set E} {γ : Type u_1} [ordered_add_comm_group γ] [semimodule ℝ γ] [ordered_semimodule ℝ γ]
    {f : E → γ} :
    concave_on s f ↔
        convex (set_of fun (p : E × γ) => prod.fst p ∈ s ∧ prod.snd p ≤ f (prod.fst p)) :=
  convex_on_iff_convex_epigraph

/-- If a function is convex on `s`, it remains convex when precomposed by an affine map. -/
theorem convex_on.comp_affine_map {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {β : Type u_1} [ordered_add_comm_monoid β]
    [semimodule ℝ β] {f : F → β} (g : affine_map ℝ E F) {s : set F} (hf : convex_on s f) :
    convex_on (⇑g ⁻¹' s) (f ∘ ⇑g) :=
  sorry

/-- If a function is concave on `s`, it remains concave when precomposed by an affine map. -/
theorem concave_on.comp_affine_map {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {β : Type u_1} [ordered_add_comm_monoid β]
    [semimodule ℝ β] {f : F → β} (g : affine_map ℝ E F) {s : set F} (hf : concave_on s f) :
    concave_on (⇑g ⁻¹' s) (f ∘ ⇑g) :=
  convex_on.comp_affine_map g hf

/-- If `g` is convex on `s`, so is `(g ∘ f)` on `f ⁻¹' s` for a linear `f`. -/
theorem convex_on.comp_linear_map {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {β : Type u_1} [ordered_add_comm_monoid β]
    [semimodule ℝ β] {g : F → β} {s : set F} (hg : convex_on s g) (f : linear_map ℝ E F) :
    convex_on (⇑f ⁻¹' s) (g ∘ ⇑f) :=
  convex_on.comp_affine_map (linear_map.to_affine_map f) hg

/-- If `g` is concave on `s`, so is `(g ∘ f)` on `f ⁻¹' s` for a linear `f`. -/
theorem concave_on.comp_linear_map {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {β : Type u_1} [ordered_add_comm_monoid β]
    [semimodule ℝ β] {g : F → β} {s : set F} (hg : concave_on s g) (f : linear_map ℝ E F) :
    concave_on (⇑f ⁻¹' s) (g ∘ ⇑f) :=
  concave_on.comp_affine_map (linear_map.to_affine_map f) hg

/-- If a function is convex on `s`, it remains convex after a translation. -/
theorem convex_on.translate_right {E : Type u} [add_comm_group E] [vector_space ℝ E] {β : Type u_1}
    [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} {s : set E} {a : E}
    (hf : convex_on s f) : convex_on ((fun (z : E) => a + z) ⁻¹' s) (f ∘ fun (z : E) => a + z) :=
  convex_on.comp_affine_map (affine_map.const ℝ E a +ᵥ affine_map.id ℝ E) hf

/-- If a function is concave on `s`, it remains concave after a translation. -/
theorem concave_on.translate_right {E : Type u} [add_comm_group E] [vector_space ℝ E] {β : Type u_1}
    [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} {s : set E} {a : E}
    (hf : concave_on s f) : concave_on ((fun (z : E) => a + z) ⁻¹' s) (f ∘ fun (z : E) => a + z) :=
  concave_on.comp_affine_map (affine_map.const ℝ E a +ᵥ affine_map.id ℝ E) hf

/-- If a function is convex on `s`, it remains convex after a translation. -/
theorem convex_on.translate_left {E : Type u} [add_comm_group E] [vector_space ℝ E] {β : Type u_1}
    [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} {s : set E} {a : E}
    (hf : convex_on s f) : convex_on ((fun (z : E) => a + z) ⁻¹' s) (f ∘ fun (z : E) => z + a) :=
  sorry

/-- If a function is concave on `s`, it remains concave after a translation. -/
theorem concave_on.translate_left {E : Type u} [add_comm_group E] [vector_space ℝ E] {β : Type u_1}
    [ordered_add_comm_monoid β] [semimodule ℝ β] {f : E → β} {s : set E} {a : E}
    (hf : concave_on s f) : concave_on ((fun (z : E) => a + z) ⁻¹' s) (f ∘ fun (z : E) => z + a) :=
  sorry

/-! ### Center of mass -/

/-- Center of mass of a finite collection of points with prescribed weights.
Note that we require neither `0 ≤ w i` nor `∑ w = 1`. -/
def finset.center_mass {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    (t : finset ι) (w : ι → ℝ) (z : ι → E) : E :=
  (finset.sum t fun (i : ι) => w i)⁻¹ • finset.sum t fun (i : ι) => w i • z i

theorem finset.center_mass_empty {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    (w : ι → ℝ) (z : ι → E) : finset.center_mass ∅ w z = 0 :=
  sorry

theorem finset.center_mass_pair {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    (i : ι) (j : ι) (w : ι → ℝ) (z : ι → E) (hne : i ≠ j) :
    finset.center_mass (insert i (singleton j)) w z =
        (w i / (w i + w j)) • z i + (w j / (w i + w j)) • z j :=
  sorry

theorem finset.center_mass_insert {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    (i : ι) (t : finset ι) {w : ι → ℝ} (z : ι → E) (ha : ¬i ∈ t)
    (hw : (finset.sum t fun (j : ι) => w j) ≠ 0) :
    finset.center_mass (insert i t) w z =
        (w i / (w i + finset.sum t fun (j : ι) => w j)) • z i +
          ((finset.sum t fun (j : ι) => w j) / (w i + finset.sum t fun (j : ι) => w j)) •
            finset.center_mass t w z :=
  sorry

theorem finset.center_mass_singleton {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    (i : ι) {w : ι → ℝ} (z : ι → E) (hw : w i ≠ 0) : finset.center_mass (singleton i) w z = z i :=
  sorry

theorem finset.center_mass_eq_of_sum_1 {E : Type u} {ι : Type w} [add_comm_group E]
    [vector_space ℝ E] (t : finset ι) {w : ι → ℝ} (z : ι → E)
    (hw : (finset.sum t fun (i : ι) => w i) = 1) :
    finset.center_mass t w z = finset.sum t fun (i : ι) => w i • z i :=
  sorry

theorem finset.center_mass_smul {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    (c : ℝ) (t : finset ι) {w : ι → ℝ} (z : ι → E) :
    (finset.center_mass t w fun (i : ι) => c • z i) = c • finset.center_mass t w z :=
  sorry

/-- A convex combination of two centers of mass is a center of mass as well. This version
deals with two different index types. -/
theorem finset.center_mass_segment' {E : Type u} {ι : Type w} {ι' : Type x} [add_comm_group E]
    [vector_space ℝ E] (s : finset ι) (t : finset ι') (ws : ι → ℝ) (zs : ι → E) (wt : ι' → ℝ)
    (zt : ι' → E) (hws : (finset.sum s fun (i : ι) => ws i) = 1)
    (hwt : (finset.sum t fun (i : ι') => wt i) = 1) (a : ℝ) (b : ℝ) (hab : a + b = 1) :
    a • finset.center_mass s ws zs + b • finset.center_mass t wt zt =
        finset.center_mass
          (finset.map function.embedding.inl s ∪ finset.map function.embedding.inr t)
          (sum.elim (fun (i : ι) => a * ws i) fun (j : ι') => b * wt j) (sum.elim zs zt) :=
  sorry

/-- A convex combination of two centers of mass is a center of mass as well. This version
works if two centers of mass share the set of original points. -/
theorem finset.center_mass_segment {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    (s : finset ι) (w₁ : ι → ℝ) (w₂ : ι → ℝ) (z : ι → E)
    (hw₁ : (finset.sum s fun (i : ι) => w₁ i) = 1) (hw₂ : (finset.sum s fun (i : ι) => w₂ i) = 1)
    (a : ℝ) (b : ℝ) (hab : a + b = 1) :
    a • finset.center_mass s w₁ z + b • finset.center_mass s w₂ z =
        finset.center_mass s (fun (i : ι) => a * w₁ i + b * w₂ i) z :=
  sorry

theorem finset.center_mass_ite_eq {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    (i : ι) (t : finset ι) (z : ι → E) (hi : i ∈ t) :
    finset.center_mass t (fun (j : ι) => ite (i = j) 1 0) z = z i :=
  sorry

theorem finset.center_mass_subset {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    {t : finset ι} {w : ι → ℝ} (z : ι → E) {t' : finset ι} (ht : t ⊆ t')
    (h : ∀ (i : ι), i ∈ t' → ¬i ∈ t → w i = 0) :
    finset.center_mass t w z = finset.center_mass t' w z :=
  sorry

theorem finset.center_mass_filter_ne_zero {E : Type u} {ι : Type w} [add_comm_group E]
    [vector_space ℝ E] {t : finset ι} {w : ι → ℝ} (z : ι → E) :
    finset.center_mass (finset.filter (fun (i : ι) => w i ≠ 0) t) w z = finset.center_mass t w z :=
  sorry

/-- The center of mass of a finite subset of a convex set belongs to the set
provided that all weights are non-negative, and the total weight is positive. -/
theorem convex.center_mass_mem {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    {s : set E} {t : finset ι} {w : ι → ℝ} {z : ι → E} (hs : convex s) :
    (∀ (i : ι), i ∈ t → 0 ≤ w i) →
        (0 < finset.sum t fun (i : ι) => w i) →
          (∀ (i : ι), i ∈ t → z i ∈ s) → finset.center_mass t w z ∈ s :=
  sorry

theorem convex.sum_mem {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E] {s : set E}
    {t : finset ι} {w : ι → ℝ} {z : ι → E} (hs : convex s) (h₀ : ∀ (i : ι), i ∈ t → 0 ≤ w i)
    (h₁ : (finset.sum t fun (i : ι) => w i) = 1) (hz : ∀ (i : ι), i ∈ t → z i ∈ s) :
    (finset.sum t fun (i : ι) => w i • z i) ∈ s :=
  sorry

theorem convex_iff_sum_mem {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} :
    convex s ↔
        ∀ (t : finset E) (w : E → ℝ),
          (∀ (i : E), i ∈ t → 0 ≤ w i) →
            (finset.sum t fun (i : E) => w i) = 1 →
              (∀ (x : E), x ∈ t → x ∈ s) → (finset.sum t fun (x : E) => w x • x) ∈ s :=
  sorry

/-- Jensen's inequality, `finset.center_mass` version. -/
theorem convex_on.map_center_mass_le {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    {s : set E} {t : finset ι} {w : ι → ℝ} {z : ι → E} {f : E → ℝ} (hf : convex_on s f)
    (h₀ : ∀ (i : ι), i ∈ t → 0 ≤ w i) (hpos : 0 < finset.sum t fun (i : ι) => w i)
    (hmem : ∀ (i : ι), i ∈ t → z i ∈ s) :
    f (finset.center_mass t w z) ≤ finset.center_mass t w (f ∘ z) :=
  sorry

/-- Jensen's inequality, `finset.sum` version. -/
theorem convex_on.map_sum_le {E : Type u} {ι : Type w} [add_comm_group E] [vector_space ℝ E]
    {s : set E} {t : finset ι} {w : ι → ℝ} {z : ι → E} {f : E → ℝ} (hf : convex_on s f)
    (h₀ : ∀ (i : ι), i ∈ t → 0 ≤ w i) (h₁ : (finset.sum t fun (i : ι) => w i) = 1)
    (hmem : ∀ (i : ι), i ∈ t → z i ∈ s) :
    f (finset.sum t fun (i : ι) => w i • z i) ≤ finset.sum t fun (i : ι) => w i * f (z i) :=
  sorry

/-- If a function `f` is convex on `s` takes value `y` at the center of mass of some points
`z i ∈ s`, then for some `i` we have `y ≤ f (z i)`. -/
theorem convex_on.exists_ge_of_center_mass {E : Type u} {ι : Type w} [add_comm_group E]
    [vector_space ℝ E] {s : set E} {t : finset ι} {w : ι → ℝ} {z : ι → E} {f : E → ℝ}
    (h : convex_on s f) (hw₀ : ∀ (i : ι), i ∈ t → 0 ≤ w i)
    (hws : 0 < finset.sum t fun (i : ι) => w i) (hz : ∀ (i : ι), i ∈ t → z i ∈ s) :
    ∃ (i : ι), ∃ (H : i ∈ t), f (finset.center_mass t w z) ≤ f (z i) :=
  sorry

/-! ### Convex hull -/

/-- The convex hull of a set `s` is the minimal convex set that includes `s`. -/
def convex_hull {E : Type u} [add_comm_group E] [vector_space ℝ E] (s : set E) : set E :=
  set.Inter fun (t : set E) => set.Inter fun (hst : s ⊆ t) => set.Inter fun (ht : convex t) => t

theorem subset_convex_hull {E : Type u} [add_comm_group E] [vector_space ℝ E] (s : set E) :
    s ⊆ convex_hull s :=
  set.subset_Inter
    fun (t : set E) =>
      set.subset_Inter fun (hst : s ⊆ t) => set.subset_Inter fun (ht : convex t) => hst

theorem convex_convex_hull {E : Type u} [add_comm_group E] [vector_space ℝ E] (s : set E) :
    convex (convex_hull s) :=
  convex_Inter fun (t : set E) => convex_Inter fun (ht : s ⊆ t) => convex_Inter id

theorem convex_hull_min {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} {t : set E}
    (hst : s ⊆ t) (ht : convex t) : convex_hull s ⊆ t :=
  set.Inter_subset_of_subset t
    (set.Inter_subset_of_subset hst (set.Inter_subset (fun (ht : convex t) => t) ht))

theorem convex_hull_mono {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E} {t : set E}
    (hst : s ⊆ t) : convex_hull s ⊆ convex_hull t :=
  convex_hull_min (set.subset.trans hst (subset_convex_hull t)) (convex_convex_hull t)

theorem convex.convex_hull_eq {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    (hs : convex s) : convex_hull s = s :=
  set.subset.antisymm (convex_hull_min (set.subset.refl s) hs) (subset_convex_hull s)

@[simp] theorem convex_hull_singleton {E : Type u} [add_comm_group E] [vector_space ℝ E] {x : E} :
    convex_hull (singleton x) = singleton x :=
  convex.convex_hull_eq (convex_singleton x)

theorem is_linear_map.image_convex_hull {E : Type u} {F : Type v} [add_comm_group E]
    [vector_space ℝ E] [add_comm_group F] [vector_space ℝ F] {s : set E} {f : E → F}
    (hf : is_linear_map ℝ f) : f '' convex_hull s = convex_hull (f '' s) :=
  sorry

theorem linear_map.image_convex_hull {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] {s : set E} (f : linear_map ℝ E F) :
    ⇑f '' convex_hull s = convex_hull (⇑f '' s) :=
  is_linear_map.image_convex_hull (linear_map.is_linear f)

theorem finset.center_mass_mem_convex_hull {E : Type u} {ι : Type w} [add_comm_group E]
    [vector_space ℝ E] {s : set E} (t : finset ι) {w : ι → ℝ} (hw₀ : ∀ (i : ι), i ∈ t → 0 ≤ w i)
    (hws : 0 < finset.sum t fun (i : ι) => w i) {z : ι → E} (hz : ∀ (i : ι), i ∈ t → z i ∈ s) :
    finset.center_mass t w z ∈ convex_hull s :=
  convex.center_mass_mem (convex_convex_hull s) hw₀ hws
    fun (i : ι) (hi : i ∈ t) => subset_convex_hull s (hz i hi)

-- TODO : Do we need other versions of the next lemma?

/-- Convex hull of `s` is equal to the set of all centers of masses of `finset`s `t`, `z '' t ⊆ s`.
This version allows finsets in any type in any universe. -/
theorem convex_hull_eq {E : Type u} [add_comm_group E] [vector_space ℝ E] (s : set E) :
    convex_hull s =
        set_of
          fun (x : E) =>
            ∃ (ι : Type u'),
              ∃ (t : finset ι),
                ∃ (w : ι → ℝ),
                  ∃ (z : ι → E),
                    ∃ (hw₀ : ∀ (i : ι), i ∈ t → 0 ≤ w i),
                      ∃ (hw₁ : (finset.sum t fun (i : ι) => w i) = 1),
                        ∃ (hz : ∀ (i : ι), i ∈ t → z i ∈ s), finset.center_mass t w z = x :=
  sorry

/-- Maximum principle for convex functions. If a function `f` is convex on the convex hull of `s`,
then `f` can't have a maximum on `convex_hull s` outside of `s`. -/
theorem convex_on.exists_ge_of_mem_convex_hull {E : Type u} [add_comm_group E] [vector_space ℝ E]
    {s : set E} {f : E → ℝ} (hf : convex_on (convex_hull s) f) {x : E} (hx : x ∈ convex_hull s) :
    ∃ (y : E), ∃ (H : y ∈ s), f x ≤ f y :=
  sorry

theorem finset.convex_hull_eq {E : Type u} [add_comm_group E] [vector_space ℝ E] (s : finset E) :
    convex_hull ↑s =
        set_of
          fun (x : E) =>
            ∃ (w : E → ℝ),
              ∃ (hw₀ : ∀ (y : E), y ∈ s → 0 ≤ w y),
                ∃ (hw₁ : (finset.sum s fun (y : E) => w y) = 1), finset.center_mass s w id = x :=
  sorry

theorem set.finite.convex_hull_eq {E : Type u} [add_comm_group E] [vector_space ℝ E] {s : set E}
    (hs : set.finite s) :
    convex_hull s =
        set_of
          fun (x : E) =>
            ∃ (w : E → ℝ),
              ∃ (hw₀ : ∀ (y : E), y ∈ s → 0 ≤ w y),
                ∃ (hw₁ : (finset.sum (set.finite.to_finset hs) fun (y : E) => w y) = 1),
                  finset.center_mass (set.finite.to_finset hs) w id = x :=
  sorry

theorem convex_hull_eq_union_convex_hull_finite_subsets {E : Type u} [add_comm_group E]
    [vector_space ℝ E] (s : set E) :
    convex_hull s = set.Union fun (t : finset E) => set.Union fun (w : ↑t ⊆ s) => convex_hull ↑t :=
  sorry

theorem is_linear_map.convex_hull_image {E : Type u} {F : Type v} [add_comm_group E]
    [vector_space ℝ E] [add_comm_group F] [vector_space ℝ F] {f : E → F} (hf : is_linear_map ℝ f)
    (s : set E) : convex_hull (f '' s) = f '' convex_hull s :=
  sorry

theorem linear_map.convex_hull_image {E : Type u} {F : Type v} [add_comm_group E] [vector_space ℝ E]
    [add_comm_group F] [vector_space ℝ F] (f : linear_map ℝ E F) (s : set E) :
    convex_hull (⇑f '' s) = ⇑f '' convex_hull s :=
  is_linear_map.convex_hull_image (linear_map.is_linear f) s

/-! ### Simplex -/

/-- The standard simplex in the space of functions `ι → ℝ` is the set
of vectors with non-negative coordinates with total sum `1`. -/
def std_simplex (ι : Type u_1) [fintype ι] : set (ι → ℝ) :=
  set_of fun (f : ι → ℝ) => (∀ (x : ι), 0 ≤ f x) ∧ (finset.sum finset.univ fun (x : ι) => f x) = 1

theorem std_simplex_eq_inter (ι : Type w) [fintype ι] :
    std_simplex ι =
        (set.Inter fun (x : ι) => set_of fun (f : ι → ℝ) => 0 ≤ f x) ∩
          set_of fun (f : ι → ℝ) => (finset.sum finset.univ fun (x : ι) => f x) = 1 :=
  sorry

theorem convex_std_simplex (ι : Type w) [fintype ι] : convex (std_simplex ι) := sorry

theorem ite_eq_mem_std_simplex {ι : Type w} [fintype ι] (i : ι) :
    (fun (j : ι) => ite (i = j) 1 0) ∈ std_simplex ι :=
  sorry

/-- `std_simplex ι` is the convex hull of the canonical basis in `ι → ℝ`. -/
theorem convex_hull_basis_eq_std_simplex {ι : Type w} [fintype ι] :
    convex_hull (set.range fun (i j : ι) => ite (i = j) 1 0) = std_simplex ι :=
  sorry

/-- The convex hull of a finite set is the image of the standard simplex in `s → ℝ`
under the linear map sending each function `w` to `∑ x in s, w x • x`.

Since we have no sums over finite sets, we use sum over `@finset.univ _ hs.fintype`.
The map is defined in terms of operations on `(s → ℝ) →ₗ[ℝ] ℝ` so that later we will not need
to prove that this map is linear. -/
theorem set.finite.convex_hull_eq_image {E : Type u} [add_comm_group E] [vector_space ℝ E]
    {s : set E} (hs : set.finite s) :
    convex_hull s =
        ⇑(finset.sum finset.univ
              fun (x : ↥s) => linear_map.smul_right (linear_map.proj x) (subtype.val x)) ''
          std_simplex ↥s :=
  sorry

/-- All values of a function `f ∈ std_simplex ι` belong to `[0, 1]`. -/
theorem mem_Icc_of_mem_std_simplex {ι : Type w} [fintype ι] {f : ι → ℝ} (hf : f ∈ std_simplex ι)
    (x : ι) : f x ∈ set.Icc 0 1 :=
  { left := and.left hf x,
    right :=
      and.right hf ▸
        finset.single_le_sum (fun (y : ι) (hy : y ∈ finset.univ) => and.left hf y)
          (finset.mem_univ x) }

end Mathlib


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes
-/
import group_theory.order_of_element data.nat.totient data.polynomial data.equiv.algebra

universes u v
variables {α : Type u} {β : Type v}

open function finset polynomial nat

section

variables [integral_domain α] [decidable_eq α] (S : set (units α)) [is_subgroup S] [fintype S]

lemma card_nth_roots_subgroup_units {n : ℕ} (hn : 0 < n) (a : S) :
  (univ.filter (λ b : S, b ^ n = a)).card ≤ (nth_roots n ((a : units α) : α)).card :=
card_le_card_of_inj_on (λ a, ((a : units α) : α))
  (by simp [mem_nth_roots hn, (units.coe_pow _ _).symm, -units.coe_pow, units.ext_iff.symm, subtype.coe_ext])
  (by simp [units.ext_iff.symm, subtype.coe_ext.symm])

instance subgroup_units_cyclic : is_cyclic S :=
by haveI := classical.dec_eq α; exact
is_cyclic_of_card_pow_eq_one_le
  (λ n hn, le_trans (card_nth_roots_subgroup_units S hn 1) (card_nth_roots _ _))

end

namespace finite_field

def field_of_integral_domain [fintype α] [decidable_eq α] [integral_domain α] :
  discrete_field α :=
{ has_decidable_eq := by apply_instance,
  inv := λ a, if h : a = 0 then 0
    else fintype.bij_inv (show function.bijective (* a),
      from fintype.injective_iff_bijective.1 $ λ _ _, (domain.mul_right_inj h).1) 1,
  inv_mul_cancel := λ a ha, show dite _ _ _ * a = _, by rw dif_neg ha;
    exact fintype.right_inverse_bij_inv (show function.bijective (* a), from _) 1,
  mul_inv_cancel :=  λ a ha, show a * dite _ _ _ = _, by rw [dif_neg ha, mul_comm];
    exact fintype.right_inverse_bij_inv (show function.bijective (* a), from _) 1,
  inv_zero := dif_pos rfl,
  ..show integral_domain α, by apply_instance }

section
variables [field α] [fintype α]

instance [decidable_eq α] : fintype (units α) :=
by haveI := set_fintype {a : α | a ≠ 0}; exact
fintype.of_equiv _ (equiv.units_equiv_ne_zero α).symm

lemma card_units [decidable_eq α] : fintype.card (units α) = fintype.card α - 1 :=
begin
  rw [eq_comm, nat.sub_eq_iff_eq_add (fintype.card_pos_iff.2 ⟨(0 : α)⟩)],
  haveI := set_fintype {a : α | a ≠ 0},
  haveI := set_fintype (@set.univ α),
  rw [fintype.card_congr (equiv.units_equiv_ne_zero _),
    ← @set.card_insert _ _ {a : α | a ≠ 0} _ (not_not.2 (eq.refl (0 : α)))
    (set.fintype_insert _ _), fintype.card_congr (equiv.set.univ α).symm],
  congr; simp [set.ext_iff, classical.em]
end

instance : is_cyclic (units α) :=
by haveI := classical.dec_eq α;
haveI := set_fintype (@set.univ (units α)); exact
let ⟨g, hg⟩ := is_cyclic.exists_generator (@set.univ (units α)) in
⟨⟨g, λ x, let ⟨n, hn⟩ := hg ⟨x, trivial⟩ in ⟨n, by rw [← is_subgroup.coe_gpow, hn]; refl⟩⟩⟩

lemma prod_univ_units_id_eq_neg_one [decidable_eq α] :
  univ.prod (λ x, x) = (-1 : units α) :=
have ((@univ (units α) _).erase (-1)).prod (λ x, x) = 1,
from prod_involution (λ x _, x⁻¹) (by simp)
  (λ a, by simp [units.inv_eq_self_iff] {contextual := tt})
  (λ a, by simp [@inv_eq_iff_inv_eq _ _ a, eq_comm] {contextual := tt})
  (by simp),
by rw [← insert_erase (mem_univ (-1 : units α)), prod_insert (not_mem_erase _ _),
    this, mul_one]

end

lemma pow_card_sub_one_eq_one [discrete_field α] [fintype α] (a : α) (ha : a ≠ 0) :
  a ^ (fintype.card α - 1) = 1 :=
calc a ^ (fintype.card α - 1) = (units.mk0 a ha ^ (fintype.card α - 1) : units α) :
    by rw [units.coe_pow, units.mk0_val]
  ... = 1 : by rw [← card_units, pow_card_eq_one]; refl

end finite_field


/-
Copyright (c) 2018 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel

! This file was ported from Lean 3 source module topology.metric_space.kuratowski
! leanprover-community/mathlib commit 95d4f6586d313c8c28e00f36621d2a6a66893aa6
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.NormedSpace.LpSpace
import Mathbin.Topology.Sets.Compacts

/-!
# The Kuratowski embedding

Any separable metric space can be embedded isometrically in `ℓ^∞(ℝ)`.
-/

noncomputable section

open Set Metric TopologicalSpace

open ENNReal

-- mathport name: exprℓ_infty_ℝ
local notation "ℓ_infty_ℝ" => lp (fun n : ℕ => ℝ) ∞

universe u v w

variable {α : Type u} {β : Type v} {γ : Type w}

namespace kuratowskiEmbedding

/-! ### Any separable metric space can be embedded isometrically in ℓ^∞(ℝ) -/

variable {f g : ℓ_infty_ℝ} {n : ℕ} {C : ℝ} [MetricSpace α] (x : ℕ → α) (a b : α)

/-- A metric space can be embedded in `l^∞(ℝ)` via the distances to points in
a fixed countable set, if this set is dense. This map is given in `Kuratowski_embedding`,
without density assumptions. -/
def embeddingOfSubset : ℓ_infty_ℝ :=
  ⟨fun n => dist a (x n) - dist (x 0) (x n),
    by
    apply memℓp_infty
    use dist a (x 0)
    rintro - ⟨n, rfl⟩
    exact abs_dist_sub_le _ _ _⟩
#align Kuratowski_embedding.embedding_of_subset KuratowskiEmbedding.embeddingOfSubset

theorem embeddingOfSubset_coe : embeddingOfSubset x a n = dist a (x n) - dist (x 0) (x n) :=
  rfl
#align Kuratowski_embedding.embedding_of_subset_coe KuratowskiEmbedding.embeddingOfSubset_coe

/-- The embedding map is always a semi-contraction. -/
theorem embeddingOfSubset_dist_le (a b : α) :
    dist (embeddingOfSubset x a) (embeddingOfSubset x b) ≤ dist a b :=
  by
  refine' lp.norm_le_of_forall_le dist_nonneg fun n => _
  simp only [lp.coeFn_sub, Pi.sub_apply, embedding_of_subset_coe, Real.dist_eq]
  convert abs_dist_sub_le a b (x n) using 2
  ring
#align Kuratowski_embedding.embedding_of_subset_dist_le KuratowskiEmbedding.embeddingOfSubset_dist_le

/-- When the reference set is dense, the embedding map is an isometry on its image. -/
theorem embeddingOfSubset_isometry (H : DenseRange x) : Isometry (embeddingOfSubset x) :=
  by
  refine' Isometry.of_dist_eq fun a b => _
  refine' (embedding_of_subset_dist_le x a b).antisymm (le_of_forall_pos_le_add fun e epos => _)
  -- First step: find n with dist a (x n) < e
  rcases Metric.mem_closure_range_iff.1 (H a) (e / 2) (half_pos epos) with ⟨n, hn⟩
  -- Second step: use the norm control at index n to conclude
  have C : dist b (x n) - dist a (x n) = embedding_of_subset x b n - embedding_of_subset x a n := by
    simp only [embedding_of_subset_coe, sub_sub_sub_cancel_right]
  have :=
    calc
      dist a b ≤ dist a (x n) + dist (x n) b := dist_triangle _ _ _
      _ = 2 * dist a (x n) + (dist b (x n) - dist a (x n)) :=
        by
        simp [dist_comm]
        ring
      _ ≤ 2 * dist a (x n) + |dist b (x n) - dist a (x n)| := by
        apply_rules [add_le_add_left, le_abs_self]
      _ ≤ 2 * (e / 2) + |embedding_of_subset x b n - embedding_of_subset x a n| := by rw [C];
        apply_rules [add_le_add, mul_le_mul_of_nonneg_left, hn.le, le_refl] ; norm_num
      _ ≤ 2 * (e / 2) + dist (embedding_of_subset x b) (embedding_of_subset x a) :=
        by
        have :
          |embedding_of_subset x b n - embedding_of_subset x a n| ≤
            dist (embedding_of_subset x b) (embedding_of_subset x a) :=
          by
          simpa [dist_eq_norm] using
            lp.norm_apply_le_norm ENNReal.top_ne_zero
              (embedding_of_subset x b - embedding_of_subset x a) n
        nlinarith
      _ = dist (embedding_of_subset x b) (embedding_of_subset x a) + e := by ring

  simpa [dist_comm] using this
#align Kuratowski_embedding.embedding_of_subset_isometry KuratowskiEmbedding.embeddingOfSubset_isometry

/-- Every separable metric space embeds isometrically in `ℓ_infty_ℝ`. -/
theorem exists_isometric_embedding (α : Type u) [MetricSpace α] [SeparableSpace α] :
    ∃ f : α → ℓ_infty_ℝ, Isometry f :=
  by
  cases' (univ : Set α).eq_empty_or_nonempty with h h
  · use fun _ => 0
    intro x
    exact absurd h (nonempty.ne_empty ⟨x, mem_univ x⟩)
  · -- We construct a map x : ℕ → α with dense image
    rcases h with ⟨basepoint⟩
    haveI : Inhabited α := ⟨basepoint⟩
    have : ∃ s : Set α, s.Countable ∧ Dense s := exists_countable_dense α
    rcases this with ⟨S, ⟨S_countable, S_dense⟩⟩
    rcases Set.countable_iff_exists_subset_range.1 S_countable with ⟨x, x_range⟩
    -- Use embedding_of_subset to construct the desired isometry
    exact ⟨embedding_of_subset x, embedding_of_subset_isometry x (S_dense.mono x_range)⟩
#align Kuratowski_embedding.exists_isometric_embedding KuratowskiEmbedding.exists_isometric_embedding

end kuratowskiEmbedding

open TopologicalSpace kuratowskiEmbedding

/-- The Kuratowski embedding is an isometric embedding of a separable metric space in `ℓ^∞(ℝ)`. -/
def kuratowskiEmbedding (α : Type u) [MetricSpace α] [SeparableSpace α] : α → ℓ_infty_ℝ :=
  Classical.choose (KuratowskiEmbedding.exists_isometric_embedding α)
#align Kuratowski_embedding kuratowskiEmbedding

/-- The Kuratowski embedding is an isometry. -/
protected theorem kuratowskiEmbedding.isometry (α : Type u) [MetricSpace α] [SeparableSpace α] :
    Isometry (kuratowskiEmbedding α) :=
  Classical.choose_spec (exists_isometric_embedding α)
#align Kuratowski_embedding.isometry kuratowskiEmbedding.isometry

/-- Version of the Kuratowski embedding for nonempty compacts -/
def NonemptyCompacts.kuratowskiEmbedding (α : Type u) [MetricSpace α] [CompactSpace α]
    [Nonempty α] : NonemptyCompacts ℓ_infty_ℝ
    where
  carrier := range (kuratowskiEmbedding α)
  is_compact' := isCompact_range (kuratowskiEmbedding.isometry α).Continuous
  nonempty' := range_nonempty _
#align nonempty_compacts.Kuratowski_embedding NonemptyCompacts.kuratowskiEmbedding