/-
Copyright (c) 2023 Chenyi Li, Ziyu Wang, Zaiwen Wen. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chenyi Li, Ziyu Wang, Penghao Yu, Zhipeng Cao, Shengyang Xu, Zaiwen Wen
-/

import Mathlib.Analysis.Convex.Function
import Mathlib.Analysis.Calculus.MeanValue
import Optlib.Differential.Calculation
import Optlib.Differential.Lemmas

set_option linter.unusedVariables false

/-!
# ConvexFunction

## Main results

This file mainly concentrates on the differentiable convex function and its properties.

The main theorem is given as:

Let f be a smooth function defined on a convex set s. Then the following statement is equivalent

  (a)  f is convex on s.

  (b)  (first order condition) f(y) ≥ f(x) + ∇ f(x)^T (y-x) ∀ x, y ∈ s.

  (c)  (monotonic of gradient) (∇ f(x) - ∇ f(y))^T (x-y) ≥ 0  ∀ x, y ∈ s.

  Besides we also proof the corresponding properties for strict convex function.

-/

open InnerProductSpace

noncomputable section

section FirstOrderCondition

variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E]

variable {f : E → ℝ} {f' : E → (E →L[ℝ] ℝ)} {x y x': E} {s : Set E}

private lemma point_proportion {a b: ℝ} (_ : 0 ≤ a) (_ : 0 ≤ b) (sumab: a + b = 1)
  (hpoint : x' = a • x + b • y) :  x - x' =  b • (x - y) := by
    calc
      x - x' = x - (a • x + b • y):= by rw [hpoint]
      _ = x - a • x - b • y:= sub_add_eq_sub_sub x (a • x) (b • y)
      _ = (1 : ℝ) • x - a • x - b • y:= by rw[one_smul]
      _ = (1 - a) • x - b • y := by rw[sub_smul 1 a]
      _ = b • x - b • y := by rw [← sumab]; ring_nf
      _ = b • (x - y) := Eq.symm (smul_sub b x y)

/-
  The first order condition for a convex function
-/
theorem Convex_first_order_condition {s : Set E}
    (h : HasFDerivAt f (f' x) x) (hf : ConvexOn ℝ s f) (xs : x ∈ s) :
    ∀ y ∈ s, f x + f' x (y - x) ≤ f y := by
  have h₁ : ∀ ε > (0 : ℝ), ∃ δ > (0 : ℝ), ∀ (x' : E), ‖x - x'‖ ≤ δ
      → ‖f x' - f x - (f' x) (x' - x)‖ ≤ ε * ‖x - x'‖:= by
    apply HasFDeriv_Convergence h
  intro y ys
  by_cases h₂ : y = x
  rw [h₂, sub_self, ContinuousLinearMap.map_zero (f' x), add_zero]
  have h₃ : 0 < ‖x - y‖ := by
    rw [norm_sub_pos_iff, Ne]
    exact Iff.mpr ne_comm h₂
  by_contra H
  push_neg at H
  rw [ConvexOn] at hf
  rcases hf with ⟨ _, cxf⟩
  specialize cxf xs ys
  let ε := f x + (f' x) (y - x) - f y
  have epos : 0 < ε := Iff.mpr sub_pos H
  have lnmp: ∀ c : ℝ , f' x (c • (y - x)) = c * (f' x (y - x)) := by
    intro c; rw [map_smul, smul_eq_mul]
  let e1 := ε / (2 * ‖x - y‖)
  have e1pos : 0 < e1 := div_pos (by linarith) (by linarith)
  specialize h₁ e1 e1pos
  rcases h₁ with ⟨δ , dpos, converge⟩
  let b1 := δ / ‖x - y‖
  have b1pos: 0 < b1 := div_pos dpos h₃
  let b := min b1 (1 : ℝ)
  let a := 1 - b
  have sum_a_b : a + b = 1:= sub_add_cancel 1 b
  have b_nonneg: 0 ≤ b := le_min (LT.lt.le b1pos) zero_le_one
  have a_nonneg : 0 ≤ a := by
    have h1: 0 + b ≤ a + b := by
      rw [zero_add, sum_a_b]
      exact min_le_right b1 1
    apply (add_le_add_iff_right b).mp h1
  specialize cxf a_nonneg b_nonneg sum_a_b
  let x' := a • x + b • y
  have x'rfl : x' = a • x + b • y := rfl
  have h1 : ‖x - x'‖ = ‖b • (x - y)‖ := by
    congr; apply point_proportion a_nonneg b_nonneg sum_a_b x'rfl
  have h2 : ‖b • (x - y)‖ = b * ‖x - y‖ := by
    rw [norm_smul, Real.norm_of_nonneg b_nonneg]
  have x1nbhd: ‖x - x'‖ ≤ δ := by
    rw[h1, h2]
    have h3: b * ‖x - y‖ ≤ b1 * ‖x - y‖:= by
      rw [mul_le_mul_right]
      apply min_le_left
      exact h₃
    have h4: b1 * ‖x - y‖ = δ := by
      simp [b1]
      rw [div_mul_cancel₀]
      apply ne_of_gt h₃
    rw[← h4]
    apply h3
  specialize converge x' x1nbhd
  have H1: f x + (f' x) (x' - x) - e1 * ‖x - x'‖ ≤ f x':= by
    have l1: f x + (f' x) (x' - x) - f x' ≤ ‖f x' - f x - (f' x) (x' - x)‖:= by
      rw [Real.norm_eq_abs]
      have : f x + (f' x) (x' - x) - f x' = -(f x' - f x - (f' x) (x' - x)) := by
        ring
      rw [this]
      apply neg_le_abs
    have _: f x + (f' x) (x' - x) - f x'≤ e1 * ‖x - x'‖ := by
      apply le_trans l1 converge
    linarith
  have H3: f y = f x + (f' x) (y - x) - ε := by simp only [ε, map_sub, sub_sub_cancel]
  have l4: e1 * ‖x - x'‖ = ε * b / 2 := by
    rw[h1, h2]
    calc
      e1 * (b * ‖x - y‖) = ε / (2 * ‖x - y‖) * (b * ‖x - y‖):= by rfl
      _ = ((ε / 2) / ‖x - y‖) *(b * ‖x - y‖):= by ring
      _ = ((ε / 2) / ‖x - y‖) * ‖x - y‖ * b := by rw[mul_comm b, mul_assoc]
      _ = (ε / 2) * b := by rw [div_mul_cancel₀]; apply ne_of_gt h₃
      _ = ε * b / 2 := by ring
  rw [l4] at H1; rw [smul_eq_mul, smul_eq_mul] at cxf
  have H4: a * f x + b * f y = f x + b * (f' x) (y - x) - b * ε := by rw [H3]; ring
  have l5: b * (f' x) (y - x) = (f' x) (x' - x) := by
    rw [← neg_sub x x',point_proportion a_nonneg b_nonneg sum_a_b x'rfl, smul_sub]
    rw [neg_sub, ← smul_sub, lnmp b]
  rw [l5] at H4; rw [H4] at cxf
  have _ : f x + (f' x) (x' - x) - ε * b / 2 ≤ f x + (f' x) (x' - x) - b * ε := le_trans H1 cxf
  have blt: 0 < b := by positivity
  have H11: ε ≤ 0 := nonpos_of_mul_nonpos_left (by linarith) blt
  linarith

theorem Convex_first_order_condition_inverse {f : E → ℝ} {f' : E → (E →L[ℝ] ℝ)}
    {s : Set E} (h : ∀ x ∈ s, HasFDerivAt f (f' x) x) (h₁: Convex ℝ s)
    (h₂ : ∀ (x : E), x ∈ s → ∀ (y : E), y ∈ s → f x + f' x (y - x) ≤ f y) : ConvexOn ℝ s f := by
  rw [ConvexOn]; constructor
  apply h₁; intro x xs y ys a b anonneg bnonneg sumab
  let x' := a • x + b • y
  have x'rfl : x' = a • x + b • y := rfl
  have x'rfl_comm : x' = b • y + a • x := by rw [add_comm]
  have sumba : b + a = 1 := by rw [add_comm]; exact sumab
  specialize h x'
  have x1s: x' ∈ s:= by
    rw [convex_iff_segment_subset] at h₁
    specialize h₁ xs ys
    rw [segment_subset_iff] at h₁
    exact h₁ a b anonneg bnonneg sumab
  have H1: f x' + f' x' (x - x') ≤ f x := h₂ x' x1s x xs
  have H2: f x' + f' x' (y - x') ≤ f y := h₂ x' x1s y ys
  have lnmp: ∀ c: ℝ , f' x' (c • (y - x)) = c * (f' x' (y - x)) := by
    intro c; rw [map_smul]; rfl
  have H: a • (f x' + (f' x') (x - x')) + b • (f x' + (f' x') (y - x')) = f x' := by
    have l1: a • (f x' + (f' x') (x - x')) + b • (f x' + (f' x') (y - x'))
        = (a + b) • f x' + a • (f' x') (x - x')+ b • (f' x') (y - x'):= by
      rw [smul_add, smul_add, ← add_assoc, add_assoc (a • f x'),
        add_comm (a • (f' x') (x - x')), ← add_assoc, add_smul]
    have l2: b • (f' x') (y - x') = (a * b) * (f' x') (y - x):= by
      rw [point_proportion bnonneg anonneg sumba x'rfl_comm, lnmp a]
      calc
        _ = b * (a * (f' x') (y - x)):= by rfl
        _ = b * a * (f' x') (y - x):= by rw [mul_assoc]
        _ = (a * b) * (f' x') (y - x):= by simp[mul_comm]
    have l3_1: x - x' = (- b) • (y - x):= by
      rw [point_proportion anonneg bnonneg sumab x'rfl]
      rw [smul_sub, neg_smul, smul_sub]; simp
    have l3: a • (f' x') (x - x') = - (a * b) * (f' x') (y - x):= by
      rw [l3_1, lnmp (- b)]
      simp [mul_neg, mul_assoc, map_sub, neg_mul]
    rw [l1, sumab, one_smul, l2, l3]
    simp only [map_sub, neg_mul, neg_add_cancel_right]
  have h1: a • (f x' + (f' x') (x - x')) ≤ a • f x := mul_le_mul_of_nonneg_left H1 anonneg
  have h2: b • (f x' + (f' x') (y - x')) ≤ b • f y := mul_le_mul_of_nonneg_left H2 bnonneg
  have H3: a • (f x' + (f' x') (x - x')) + b • (f x' + (f' x') (y - x')) ≤ a • f x + b • f y
      := add_le_add h1 h2
  rw [H] at H3
  apply H3

theorem Convex_first_order_condition_iff (h₁ : Convex ℝ s) (h : ∀ x ∈ s, HasFDerivAt f (f' x) x) :
    ConvexOn ℝ s f ↔ ∀ x ∈ s, ∀ y ∈ s, f x + f' x (y - x) ≤ f y :=
  ⟨fun h₂ x xs ↦ Convex_first_order_condition (h x xs) h₂ xs,
    Convex_first_order_condition_inverse h h₁⟩

theorem Convex_monotone_gradient (hfun: ConvexOn ℝ s f) (h : ∀ x ∈ s , HasFDerivAt f (f' x) x) :
    ∀ x ∈ s, ∀ y ∈ s, (f' x - f' y) (x - y) ≥ 0 := by
  intro x hx y hy
  have h₁ : f x + f' x (y - x) ≤ f y := Convex_first_order_condition (h x hx) hfun hx y hy
  have h₂ : f y + f' y (x - y) ≤ f x := Convex_first_order_condition (h y hy) hfun hy x hx
  have h₃ : f x + f' x (y - x) + (f y + f' y (x - y)) ≤ f y + f x := add_le_add h₁ h₂
  rw [add_assoc, ← le_sub_iff_add_le', ← add_sub, sub_self, add_zero] at h₃
  rw [add_comm, add_assoc, ← le_sub_iff_add_le', sub_self] at h₃
  simp only [map_sub] at h₃
  simp only [map_sub, ContinuousLinearMap.coe_sub', Pi.sub_apply]
  linarith

end FirstOrderCondition

/-
  The corresponding theorems for gradient.
-/
section FirstOrderCondition_Gradient

open Set InnerProductSpace

variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] [CompleteSpace E]

variable {f : E → ℝ} {f' : E → E} {s : Set E} {x : E}

theorem Convex_first_order_condition' (h : HasGradientAt f (f' x) x) (hf : ConvexOn ℝ s f)
    (xs : x ∈ s) : ∀ (y : E), y ∈ s → f x + inner (f' x) (y - x) ≤ f y := by
  show ∀ (y : E), y ∈ s → f x + (toDual ℝ E) (f' x) (y - x) ≤ f y
  apply Convex_first_order_condition _ hf xs
  apply h

theorem Convex_first_order_condition_inverse'  (h : ∀ x ∈ s , HasGradientAt f (f' x) x)
    (h₁ : Convex ℝ s) (h₂ : ∀ x : E, x ∈ s → ∀ y : E, y ∈ s → f x + inner (f' x) (y - x) ≤ f y) :
    ConvexOn ℝ s f := by
  apply Convex_first_order_condition_inverse
  intro x; specialize h x
  rw [hasGradientAt_iff_hasFDerivAt] at h
  apply h; apply h₁; apply h₂

theorem Convex_first_order_condition_iff' (h₁ : Convex ℝ s) (h : ∀ x ∈ s, HasGradientAt f (f' x) x) :
    ConvexOn ℝ s f ↔ ∀ x ∈ s, ∀ y ∈ s, f x + inner (f' x) (y - x) ≤ f y :=
  ⟨fun h₂ x xs ↦ Convex_first_order_condition' (h x xs) h₂ xs,
    Convex_first_order_condition_inverse' h h₁⟩

theorem Convex_monotone_gradient' (hfun: ConvexOn ℝ s f) (h : ∀ x ∈ s, HasGradientAt f (f' x) x) :
    ∀ x ∈ s, ∀ y ∈ s, inner (f' x - f' y) (x - y) ≥ (0 : ℝ) := by
  let g := fun x ↦ (toDual ℝ E) (f' x)
  have h' : ∀ x ∈ s, HasFDerivAt f (g x) x := h
  have equiv : ∀ x y : E, inner (f' x - f' y) (x - y) = (g x - g y) (x - y) := by
    intro x y
    rw [← InnerProductSpace.toDual_apply]
    simp only [map_sub, ContinuousLinearMap.coe_sub', Pi.sub_apply, toDual_apply, g]
  intro x hx y hy
  rw [equiv]
  exact Convex_monotone_gradient hfun h' x hx  y hy

theorem monotone_gradient_convex' (h₁ : Convex ℝ s) (hf : ∀ x ∈ s, HasGradientAt f (f' x) x)
    (mono: ∀ x ∈ s, ∀ y ∈ s, inner (f' x - f' y) (x - y) ≥ (0 : ℝ)) : ConvexOn ℝ s f := by
  apply Convex_first_order_condition_inverse' hf h₁
  intro x xs y ys
  let g := fun t : ℝ ↦ f (x + t • (y - x))
  let g' := fun t : ℝ ↦ (inner (f' (x + t • (y - x))) (y - x) : ℝ)
  have h1 : ∀ r ∈ Icc 0 1, HasDerivAt g (g' r) r := by
    let h := fun r : ℝ ↦ (x + r • (y - x))
    have : g = f ∘ h := rfl
    rw [this]; intro t ht
    have : inner (f' (x + t • (y - x))) (y - x) = toDual ℝ E (f' (x + t • (y - x))) (y - x) := rfl
    simp [g']; rw [this]; apply HasFDerivAt.comp_hasDerivAt
    · apply hasGradientAt_iff_hasFDerivAt.mp
      have : x + t • (y - x) ∈ s := by
        apply Convex.add_smul_sub_mem  h₁ xs ys; simp; simp at ht; rcases ht with ⟨ht1, ht2⟩
        constructor <;> linarith
      exact hf (x + t • (y - x)) this
    · have ht: HasDerivAt (fun r : ℝ ↦ r) 1 t := hasDerivAt_id' t
      have : HasDerivAt (fun r : ℝ ↦ r • (y - x)) ((1 : ℝ) • (y - x)) t := by
        exact HasDerivAt.smul_const ht (y - x)
      rw [one_smul] at this; exact HasDerivAt.const_add x this
  have e1 : f x = g 0 := by simp [g]
  have e2 : f y = g 1 := by simp [g]
  have e3 : inner (f' x) (y - x) = g' 0 := by simp [g']
  rw [e1, e2, e3]
  have mono' : ∀ t ∈ Ioo 0 1, g' t ≥ g' 0 := by
    intro t ht;
    simp [g']; rw [← sub_nonneg, ← inner_sub_left]
    rcases ht with ⟨ht1, ht2⟩
    have hh: inner (f' (x + t • (y - x)) - f' x) (x + t • (y - x) - x) ≥ (0 : ℝ) := by
      apply mono (x + t • (y - x)) _ x xs
      have e4 : x + t • (y - x) = (1 - t) • x + t • y := by
        rw [smul_sub, add_sub, sub_smul, one_smul, add_sub_right_comm]
      rw [e4]
      apply convex_iff_forall_pos.mp h₁ xs ys (by linarith) (by linarith) (by norm_num)
    rw [add_sub_cancel_left, inner_smul_right] at hh
    exact (mul_nonneg_iff_of_pos_left ht1).mp hh
  have h2 : ∃ c ∈ Set.Ioo 0 1, g' c = (g 1 - g 0) / (1 - 0) := by
    apply exists_hasDerivAt_eq_slope
    · linarith
    · have : ∀ x ∈ Icc 0 1, HasDerivAt g (g' x) x := by
        intro x hx
        exact (h1 x hx)
      exact HasDerivAt.continuousOn this
    · intro x hx
      have : x ∈ Icc 0 1 := by simp at hx; simp; constructor <;> linarith
      exact h1 x this
  rcases h2 with ⟨c, ⟨⟨hc1,hc2⟩,hc3⟩⟩
  rw [sub_zero, div_one] at hc3; rw [← le_sub_iff_add_le', ← hc3]
  apply mono' c
  simp; constructor; linarith; linarith

theorem monotone_gradient_iff_convex' (h₁ : Convex ℝ s) (hf : ∀ x ∈ s, HasGradientAt f (f' x) x):
    ConvexOn ℝ s f ↔ ∀ x ∈ s, ∀ y ∈ s, inner (f' x - f' y) (x - y) ≥ (0 : ℝ) :=
  ⟨fun h ↦ Convex_monotone_gradient' h hf, fun h ↦ monotone_gradient_convex' h₁ hf h⟩

theorem monotone_gradient_convex {f' : E → (E →L[ℝ] ℝ)} (h₁ : Convex ℝ s)
    (hf : ∀ x ∈ s, HasFDerivAt f (f' x) x)
    (mono : ∀ x ∈ s, ∀ y ∈ s, (f' x - f' y) (x - y) ≥ 0) : ConvexOn ℝ s f := by
  let g := fun x ↦ ((toDual ℝ E).symm (f' x))
  have h' : ∀ x ∈ s, HasGradientAt f (g x) x := by
    intro x' hx'
    exact HasFDerivAt.hasGradientAt (hf x' hx')
  have equiv : ∀ x y : E, inner (g x - g y) (x - y) = (f' x - f' y) (x - y) := by
    intro x y
    rw [← InnerProductSpace.toDual_apply]; simp [g]
  have mono' : ∀ x ∈ s, ∀ y ∈ s, inner (g x - g y) (x - y) ≥ (0 : ℝ) := by
    intro x hx y hy
    specialize mono x hx y hy
    rw [equiv]; exact mono
  exact monotone_gradient_convex' h₁ h' mono'

theorem montone_gradient_iff_convex {f' : E → (E →L[ℝ] ℝ)}
    (h₁ : Convex ℝ s) (hf : ∀ x ∈ s, HasFDerivAt f (f' x) x):
    ConvexOn ℝ s f ↔  ∀ x ∈ s, ∀ y ∈ s, (f' x - f' y) (x - y) ≥ (0 : ℝ) :=
  ⟨fun h ↦ Convex_monotone_gradient h hf, fun h ↦ monotone_gradient_convex h₁ hf h⟩

end FirstOrderCondition_Gradient

section strict

/-
  The corresponding theorems for a strict convex function, that is the
  monotonic gradient of a strict convex function ↔
  (monotonic of gradient) (∇ f(x) - ∇ f(y))^T (x-y) ≥ 0  ∀ x, y ∈ s.
-/
variable {E : Type*} [NormedAddCommGroup E] [InnerProductSpace ℝ E] [CompleteSpace E]

variable {f : E → ℝ} {f' : E → E} {s : Set E}

theorem monotone_gradient_strict_convex (hs : Convex ℝ s)
    (hf : ∀ x ∈ s, HasGradientAt f (f' x) x)
    (mono: ∀ x ∈ s, ∀ y ∈ s, x ≠ y → inner (f' x - f' y) (x - y) > (0 : ℝ)) :
    StrictConvexOn ℝ s f := by
  rw [StrictConvexOn]; use hs
  intro x xin y yin xney a b apos bpos absum1
  by_contra h₀; push_neg at h₀
  have anneg : 0 ≤ a := by linarith
  have bnneg : 0 ≤ b := by linarith
  have mono' : ∀ x ∈ s, ∀ y ∈ s, inner (f' x - f' y) (x - y) ≥ (0 : ℝ) := by
    intro x xin y yin
    by_cases h : x = y
    · rw [h]; simp
    · linarith [mono x xin y yin h]
  have convf : ConvexOn ℝ s f := by
    apply monotone_gradient_convex' hs hf mono'
  rw [ConvexOn] at convf
  rcases convf with ⟨_, convf⟩
  have eq : f (a • x + b • y) ≤ a • f x + b • f y := by apply convf xin yin anneg bnneg absum1
  have eq2 : f (a • x + b • y) = a • f x + b • f y := by linarith
  let z : E := a • x + b • y
  have zin : z ∈ s := by
    simp [z]
    have : a = 1 - b := by linarith
    rw [this, sub_smul, add_comm_sub, ← smul_sub]; simp
    apply Convex.add_smul_sub_mem hs xin yin; simp; use bnneg; linarith
  have eq1 : ∃ c : ℝ, c ∈ Set.Ioo 0 1 ∧ inner (f' (x + c • (z - x))) (z - x) = f z - f x := by
    apply lagrange hs hf x xin z zin
  have eq2 : ∃ c : ℝ, c ∈ Set.Ioo 0 1 ∧ inner (f' (z + c • (y - z))) (y - z) = f y - f z := by
    apply lagrange hs hf z zin y yin
  rcases eq1 with ⟨c, cin, e1⟩
  rcases eq2 with ⟨d, din, e2⟩
  have eq3 : b * inner (f' (z + d • (y - z))) (y - z) -
      a * inner (f' (x + c • (z - x))) (z - x) = 0 := by
    rw [e1, e2]; simp [z]; ring_nf; rw [add_comm, ← add_assoc]
    simp at eq2; rw [← eq2]; nth_rw 1 [← mul_one (f (a • x + b • y))]; rw [← absum1]; ring_nf
  rw [← inner_smul_right, ← inner_smul_right] at eq3
  have this1 : b • (y - z) = a • b • (y - x) := by
    simp [z]; nth_rw 1 [← one_smul ℝ y]; rw [← absum1, add_smul]; simp; rw [← smul_sub, smul_comm]
  have this2 : a • (z - x) = a • b • (y - x) := by
    simp [z]; nth_rw 2 [← one_smul ℝ x]; rw [← absum1, add_smul]; simp; rw [← smul_sub, smul_comm]
  rw [this1, this2, ← inner_sub_left, inner_smul_right, inner_smul_right, ← mul_assoc] at eq3
  have eq0 : inner (f' (z + d • (y - z)) - f' (x + c • (z - x))) (y - x) = (0 : ℝ) := by
    contrapose! eq3
    rw [mul_ne_zero_iff]
    constructor
    · rw [mul_ne_zero_iff]; constructor <;> linarith
    · exact eq3
  have zeq : z = x + b • (y - x) := by
    nth_rw 1 [← one_smul ℝ x]; rw [← absum1, add_smul, smul_sub]; simp
  let u : E := z + d • (y - z)
  let v : E := x + c • (z - x)
  have ueq : u = x + (b + d) • (y - x) - d • b • (y - x) := by
    show z + d • (y - z) = x + (b + d) • (y - x) - d • b • (y - x); rw [zeq]
    rw [add_assoc, ← add_sub, add_left_cancel_iff]
    rw [add_smul, ← add_sub, add_left_cancel_iff]
    rw [← sub_sub, smul_sub]
  have veq : v = x + c • b • (y - x) := by
    show x + c • (z - x) = x + c • b • (y - x)
    rw [zeq, add_left_cancel_iff]; simp
  have usubv : u - v = (b + d - d * b - c * b) • (y - x) := by
    rw [ueq, veq, ← smul_assoc, ← smul_assoc, ← sub_sub]; simp
    rw [← add_sub, ← sub_smul (b + d) (d * b)]; simp; rw [← sub_smul]
  have eeq0 : inner (f' u - f' v) (u - v) = (0 : ℝ) := by
    show inner (f' (z + d • (y - z)) - f' (x + c • (z - x))) (u - v) = (0 : ℝ)
    rw [usubv, inner_smul_right, eq0]; simp
  have coefne0 : b + d - d * b - c * b > 0 := by
    nth_rw 1 [← mul_one d]; rw [← absum1]; simp; ring_nf
    simp at cin; simp at din
    rcases cin with ⟨_, cl1⟩; rcases din with ⟨dpos, _⟩
    calc
      c * b < b := by rw [mul_lt_iff_lt_one_left]; apply cl1; linarith
      _ < b + d * a := by
        have : 0 < d * a := by apply mul_pos dpos apos
        linarith
  have neq0 : inner (f' u - f' v) (u - v) > (0 : ℝ) := by
    have uin : u ∈ s := by
      show z + d • (y - z) ∈ s
      apply Convex.add_smul_sub_mem hs zin yin; simp; simp at din
      rcases din with ⟨dpos, dl1⟩; constructor <;> linarith
    have vin : v ∈ s := by
      show x + c • (z - x) ∈ s
      apply Convex.add_smul_sub_mem hs xin zin; simp; simp at cin
      rcases cin with ⟨cpos, cl1⟩; constructor <;> linarith
    apply mono u uin v vin
    by_contra h
    have : u - v = 0 := by rw [h]; simp
    rw [usubv, smul_eq_zero] at this;
    contrapose! this
    constructor
    · linarith
    · rw [sub_ne_zero]; symm; exact xney
  linarith

theorem strict_convex_monotone_gradient (hf : ∀ x ∈ s, HasGradientAt f (f' x) x)
    (h₁ : StrictConvexOn ℝ s f ) :
    ∀ x ∈ s, ∀ y ∈ s, x ≠ y → inner (f' x - f' y) (x - y) > (0 : ℝ) := by
  intro x xin y yin xney
  have convf : ConvexOn ℝ s f := by apply StrictConvexOn.convexOn h₁
  rw [StrictConvexOn] at h₁
  rcases h₁ with ⟨hs, fsconv⟩
  have : inner (f' x - f' y) (x - y) ≥ (0 : ℝ) := by
    apply Convex_monotone_gradient' convf hf x xin y yin
  by_contra h0; push_neg at h0
  have eq : inner (f' x - f' y) (x - y) = (0 : ℝ) := by linarith
  have eq1 : f x + inner (f' x) (y - x) ≤ f y := by
    apply Convex_first_order_condition' (hf x xin) convf xin y yin
  have eq2 : f y + inner (f' y) (x - y) ≤ f x := by
    apply Convex_first_order_condition' (hf y yin) convf yin x xin
  have eq2' : f y ≤ f x + inner (f' x) (y - x) := by
    rw [← add_zero (inner (f' x) (y - x)), ← eq, inner_sub_left, add_sub, ← inner_add_right]
    simp; apply eq2
  have eq3 : f y - f x = inner (f' x) (y - x) := by linarith
  have extc : ∃ c : ℝ, c ∈ Set.Ioo 0 1 ∧ inner (f' (x + c • (y - x))) (y - x) = f y - f x := by
    apply lagrange hs hf x xin y yin
  rcases extc with ⟨c, cin, e1⟩
  let z : E := x + c • (y - x)
  have zin : z ∈ s := by
    apply Convex.add_smul_sub_mem hs xin yin; simp; simp at cin; rcases cin with ⟨cpos, cl1⟩
    constructor <;> linarith
  simp at cin; rcases cin with ⟨cpos, cl1⟩
  have eq0 : inner (f' z - f' x) (z - x) = (0 : ℝ) := by
    simp [z]; rw [inner_smul_right, inner_sub_left, ← eq3, e1]; simp
  have eq4 : f x + inner (f' x) (z - x) ≤ f z := by
    apply Convex_first_order_condition' (hf x xin) convf xin z zin
  have eq5 : f z + inner (f' z) (x - z) ≤ f x := by
    apply Convex_first_order_condition' (hf z zin) convf zin x xin
  have eq5' : f z ≤ f x + inner (f' x) (z - x) := by
    rw [← add_zero (inner (f' x) (z - x)), ← eq0, inner_sub_left]
    rw [add_sub, add_comm (inner (f' x) (z - x))]
    rw [← add_sub, ← inner_sub_right, sub_self, inner_zero_right, add_zero]
    rw [← sub_neg_eq_add, ← inner_neg_right, neg_sub]; linarith
  have eq6 : f z = inner (f' x) (z - x) + f x := by linarith
  have f1 : f z = (1 - c) • f x + c • f y := by
    rw [eq6]; simp [z]; rw [inner_smul_right, ← eq3]; ring_nf
  have f2 : f z < (1 - c) • f x + c • f y := by
    simp
    let d : ℝ := 1 - c
    have : x + c • (y - x) = d • x + c • y := by
      simp [d]; rw [smul_sub, sub_smul, one_smul, add_sub, add_sub_right_comm]
    have cdsum1 : d + c = 1 := by simp [d]
    have dpos : 0 < d := by linarith
    simp [z]; rw [this]
    apply fsconv xin yin xney dpos cpos cdsum1
  linarith

theorem strict_convex_iff_monotone_gradient
    (hs: Convex ℝ s) (h : ∀ x ∈ s, HasGradientAt f (f' x) x) :
    (∀ x ∈ s, ∀ y ∈ s, x ≠ y → inner (f' x - f' y) (x - y) > (0 : ℝ))
    ↔ StrictConvexOn ℝ s f := by
  constructor
  exact monotone_gradient_strict_convex hs h
  exact strict_convex_monotone_gradient h

end strict
