/-
Copyright (c) 2019 Minchao Wu. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Minchao Wu, Chris Hughes, Mantas Bakšys

! This file was ported from Lean 3 source module data.list.min_max
! leanprover-community/mathlib commit 00f4ab49e7d5139216e0b3daad15fffa504897ab
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.List.Basic

/-!
# Minimum and maximum of lists

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

## Main definitions

The main definitions are `argmax`, `argmin`, `minimum` and `maximum` for lists.

`argmax f l` returns `some a`, where `a` of `l` that maximises `f a`. If there are `a b` such that
  `f a = f b`, it returns whichever of `a` or `b` comes first in the list.
  `argmax f []` = none`

`minimum l` returns an `with_top α`, the smallest element of `l` for nonempty lists, and `⊤` for
`[]`
-/

namespace List

variable {α β : Type _}

section ArgAux

variable (r : α → α → Prop) [DecidableRel r] {l : List α} {o : Option α} {a m : α}

#print List.argAux /-
/-- Auxiliary definition for `argmax` and `argmin`. -/
def argAux (a : Option α) (b : α) : Option α :=
  Option.casesOn a (some b) fun c => if r b c then some b else some c
#align list.arg_aux List.argAux
-/

#print List.foldl_argAux_eq_none /-
@[simp]
theorem foldl_argAux_eq_none : l.foldl (argAux r) o = none ↔ l = [] ∧ o = none :=
  List.reverseRecOn l (by simp) fun tl hd => by
    simp [arg_aux] <;> cases foldl (arg_aux r) o tl <;> simp <;> try split_ifs <;> simp
#align list.foldl_arg_aux_eq_none List.foldl_argAux_eq_none
-/

private theorem foldl_arg_aux_mem (l) : ∀ a m : α, m ∈ foldl (argAux r) (some a) l → m ∈ a :: l :=
  List.reverseRecOn l (by simp [eq_comm])
    (by
      intro tl hd ih a m
      simp only [foldl_append, foldl_cons, foldl_nil, arg_aux]
      cases hf : foldl (arg_aux r) (some a) tl
      · simp (config := { contextual := true })
      · dsimp only
        split_ifs
        · simp (config := { contextual := true })
        · -- `finish [ih _ _ hf]` closes this goal
          rcases ih _ _ hf with (rfl | H)
          · simp only [mem_cons_iff, mem_append, mem_singleton, Option.mem_def]
            tauto
          · apply fun hm => Or.inr (list.mem_append.mpr <| Or.inl _)
            exact option.mem_some_iff.mp hm ▸ H)
#align list.foldl_arg_aux_mem list.foldl_arg_aux_mem

#print List.argAux_self /-
@[simp]
theorem argAux_self (hr₀ : Irreflexive r) (a : α) : argAux r (some a) a = a :=
  if_neg <| hr₀ _
#align list.arg_aux_self List.argAux_self
-/

#print List.not_of_mem_foldl_argAux /-
theorem not_of_mem_foldl_argAux (hr₀ : Irreflexive r) (hr₁ : Transitive r) :
    ∀ {a m : α} {o : Option α}, a ∈ l → m ∈ foldl (argAux r) o l → ¬r a m :=
  by
  induction' l using List.reverseRecOn with tl a ih
  · exact fun _ _ _ h => absurd h <| not_mem_nil _
  intro b m o hb ho
  rw [foldl_append, foldl_cons, foldl_nil, arg_aux] at ho
  cases' hf : foldl (arg_aux r) o tl with c
  · rw [hf] at ho
    rw [foldl_arg_aux_eq_none] at hf
    simp_all [hf.1, hf.2, hr₀ _]
  rw [hf, Option.mem_def] at ho
  dsimp only at ho
  split_ifs  at ho with hac hac <;> cases' mem_append.1 hb with h h <;> subst ho
  · exact fun hba => ih h hf (hr₁ hba hac)
  · simp_all [hr₀ _]
  · exact ih h hf
  · simp_all
#align list.not_of_mem_foldl_arg_aux List.not_of_mem_foldl_argAux
-/

end ArgAux

section Preorder

variable [Preorder β] [@DecidableRel β (· < ·)] {f : α → β} {l : List α} {o : Option α} {a m : α}

#print List.argmax /-
/-- `argmax f l` returns `some a`, where `f a` is maximal among the elements of `l`, in the sense
that there is no `b ∈ l` with `f a < f b`. If `a`, `b` are such that `f a = f b`, it returns
whichever of `a` or `b` comes first in the list. `argmax f []` = none`. -/
def argmax (f : α → β) (l : List α) : Option α :=
  l.foldl (argAux fun b c => f c < f b) none
#align list.argmax List.argmax
-/

#print List.argmin /-
/-- `argmin f l` returns `some a`, where `f a` is minimal among the elements of `l`, in the sense
that there is no `b ∈ l` with `f b < f a`. If `a`, `b` are such that `f a = f b`, it returns
whichever of `a` or `b` comes first in the list. `argmin f []` = none`. -/
def argmin (f : α → β) (l : List α) :=
  l.foldl (argAux fun b c => f b < f c) none
#align list.argmin List.argmin
-/

/- warning: list.argmax_nil -> List.argmax_nil is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] (f : α -> β), Eq.{succ u1} (Option.{u1} α) (List.argmax.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (List.nil.{u1} α)) (Option.none.{u1} α)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.927 : β) (x._@.Mathlib.Data.List.MinMax._hyg.929 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.927 x._@.Mathlib.Data.List.MinMax._hyg.929)] (f : α -> β), Eq.{succ u2} (Option.{u2} α) (List.argmax.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (List.nil.{u2} α)) (Option.none.{u2} α)
Case conversion may be inaccurate. Consider using '#align list.argmax_nil List.argmax_nilₓ'. -/
@[simp]
theorem argmax_nil (f : α → β) : argmax f [] = none :=
  rfl
#align list.argmax_nil List.argmax_nil

/- warning: list.argmin_nil -> List.argmin_nil is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] (f : α -> β), Eq.{succ u1} (Option.{u1} α) (List.argmin.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (List.nil.{u1} α)) (Option.none.{u1} α)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.974 : β) (x._@.Mathlib.Data.List.MinMax._hyg.976 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.974 x._@.Mathlib.Data.List.MinMax._hyg.976)] (f : α -> β), Eq.{succ u2} (Option.{u2} α) (List.argmin.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (List.nil.{u2} α)) (Option.none.{u2} α)
Case conversion may be inaccurate. Consider using '#align list.argmin_nil List.argmin_nilₓ'. -/
@[simp]
theorem argmin_nil (f : α → β) : argmin f [] = none :=
  rfl
#align list.argmin_nil List.argmin_nil

/- warning: list.argmax_singleton -> List.argmax_singleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] {f : α -> β} {a : α}, Eq.{succ u1} (Option.{u1} α) (List.argmax.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (List.cons.{u1} α a (List.nil.{u1} α))) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (Option.{u1} α) (HasLiftT.mk.{succ u1, succ u1} α (Option.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} α (Option.{u1} α) (coeOption.{u1} α))) a)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1021 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1023 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1021 x._@.Mathlib.Data.List.MinMax._hyg.1023)] {f : α -> β} {a : α}, Eq.{succ u2} (Option.{u2} α) (List.argmax.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (List.cons.{u2} α a (List.nil.{u2} α))) (Option.some.{u2} α a)
Case conversion may be inaccurate. Consider using '#align list.argmax_singleton List.argmax_singletonₓ'. -/
@[simp]
theorem argmax_singleton {f : α → β} {a : α} : argmax f [a] = a :=
  rfl
#align list.argmax_singleton List.argmax_singleton

/- warning: list.argmin_singleton -> List.argmin_singleton is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] {f : α -> β} {a : α}, Eq.{succ u1} (Option.{u1} α) (List.argmin.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (List.cons.{u1} α a (List.nil.{u1} α))) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (Option.{u1} α) (HasLiftT.mk.{succ u1, succ u1} α (Option.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} α (Option.{u1} α) (coeOption.{u1} α))) a)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1071 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1073 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1071 x._@.Mathlib.Data.List.MinMax._hyg.1073)] {f : α -> β} {a : α}, Eq.{succ u2} (Option.{u2} α) (List.argmin.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (List.cons.{u2} α a (List.nil.{u2} α))) (Option.some.{u2} α a)
Case conversion may be inaccurate. Consider using '#align list.argmin_singleton List.argmin_singletonₓ'. -/
@[simp]
theorem argmin_singleton {f : α → β} {a : α} : argmin f [a] = a :=
  rfl
#align list.argmin_singleton List.argmin_singleton

/- warning: list.not_lt_of_mem_argmax -> List.not_lt_of_mem_argmax is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] {f : α -> β} {l : List.{u1} α} {a : α} {m : α}, (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmax.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l)) -> (Not (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1) (f m) (f a)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1121 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1123 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1121 x._@.Mathlib.Data.List.MinMax._hyg.1123)] {f : α -> β} {l : List.{u2} α} {a : α} {m : α}, (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmax.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l)) -> (Not (LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) (f m) (f a)))
Case conversion may be inaccurate. Consider using '#align list.not_lt_of_mem_argmax List.not_lt_of_mem_argmaxₓ'. -/
theorem not_lt_of_mem_argmax : a ∈ l → m ∈ argmax f l → ¬f m < f a :=
  not_of_mem_foldl_argAux _ (fun _ => lt_irrefl _) fun _ _ _ => gt_trans
#align list.not_lt_of_mem_argmax List.not_lt_of_mem_argmax

/- warning: list.not_lt_of_mem_argmin -> List.not_lt_of_mem_argmin is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] {f : α -> β} {l : List.{u1} α} {a : α} {m : α}, (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmin.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l)) -> (Not (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1) (f a) (f m)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1209 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1211 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1209 x._@.Mathlib.Data.List.MinMax._hyg.1211)] {f : α -> β} {l : List.{u2} α} {a : α} {m : α}, (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmin.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l)) -> (Not (LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) (f a) (f m)))
Case conversion may be inaccurate. Consider using '#align list.not_lt_of_mem_argmin List.not_lt_of_mem_argminₓ'. -/
theorem not_lt_of_mem_argmin : a ∈ l → m ∈ argmin f l → ¬f a < f m :=
  not_of_mem_foldl_argAux _ (fun _ => lt_irrefl _) fun _ _ _ => lt_trans
#align list.not_lt_of_mem_argmin List.not_lt_of_mem_argmin

/- warning: list.argmax_concat -> List.argmax_concat is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] (f : α -> β) (a : α) (l : List.{u1} α), Eq.{succ u1} (Option.{u1} α) (List.argmax.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (Append.append.{u1} (List.{u1} α) (List.hasAppend.{u1} α) l (List.cons.{u1} α a (List.nil.{u1} α)))) (Option.casesOn.{succ u1, u1} α (fun (_x : Option.{u1} α) => Option.{u1} α) (List.argmax.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l) (Option.some.{u1} α a) (fun (c : α) => ite.{succ u1} (Option.{u1} α) (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1) (f c) (f a)) (_inst_2 (f c) (f a)) (Option.some.{u1} α a) (Option.some.{u1} α c)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1297 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1299 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1297 x._@.Mathlib.Data.List.MinMax._hyg.1299)] (f : α -> β) (a : α) (l : List.{u2} α), Eq.{succ u2} (Option.{u2} α) (List.argmax.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (HAppend.hAppend.{u2, u2, u2} (List.{u2} α) (List.{u2} α) (List.{u2} α) (instHAppend.{u2} (List.{u2} α) (List.instAppendList.{u2} α)) l (List.cons.{u2} α a (List.nil.{u2} α)))) (Option.casesOn.{succ u2, u2} α (fun (_x : Option.{u2} α) => Option.{u2} α) (List.argmax.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l) (Option.some.{u2} α a) (fun (c : α) => ite.{succ u2} (Option.{u2} α) (LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) (f c) (f a)) (_inst_2 (f c) (f a)) (Option.some.{u2} α a) (Option.some.{u2} α c)))
Case conversion may be inaccurate. Consider using '#align list.argmax_concat List.argmax_concatₓ'. -/
theorem argmax_concat (f : α → β) (a : α) (l : List α) :
    argmax f (l ++ [a]) =
      Option.casesOn (argmax f l) (some a) fun c => if f c < f a then some a else some c :=
  by rw [argmax, argmax] <;> simp [arg_aux]
#align list.argmax_concat List.argmax_concat

/- warning: list.argmin_concat -> List.argmin_concat is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] (f : α -> β) (a : α) (l : List.{u1} α), Eq.{succ u1} (Option.{u1} α) (List.argmin.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (Append.append.{u1} (List.{u1} α) (List.hasAppend.{u1} α) l (List.cons.{u1} α a (List.nil.{u1} α)))) (Option.casesOn.{succ u1, u1} α (fun (_x : Option.{u1} α) => Option.{u1} α) (List.argmin.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l) (Option.some.{u1} α a) (fun (c : α) => ite.{succ u1} (Option.{u1} α) (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1) (f a) (f c)) (_inst_2 (f a) (f c)) (Option.some.{u1} α a) (Option.some.{u1} α c)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1419 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1421 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1419 x._@.Mathlib.Data.List.MinMax._hyg.1421)] (f : α -> β) (a : α) (l : List.{u2} α), Eq.{succ u2} (Option.{u2} α) (List.argmin.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f (HAppend.hAppend.{u2, u2, u2} (List.{u2} α) (List.{u2} α) (List.{u2} α) (instHAppend.{u2} (List.{u2} α) (List.instAppendList.{u2} α)) l (List.cons.{u2} α a (List.nil.{u2} α)))) (Option.casesOn.{succ u2, u2} α (fun (_x : Option.{u2} α) => Option.{u2} α) (List.argmin.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l) (Option.some.{u2} α a) (fun (c : α) => ite.{succ u2} (Option.{u2} α) (LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) (f a) (f c)) (_inst_2 (f a) (f c)) (Option.some.{u2} α a) (Option.some.{u2} α c)))
Case conversion may be inaccurate. Consider using '#align list.argmin_concat List.argmin_concatₓ'. -/
theorem argmin_concat (f : α → β) (a : α) (l : List α) :
    argmin f (l ++ [a]) =
      Option.casesOn (argmin f l) (some a) fun c => if f a < f c then some a else some c :=
  @argmax_concat _ βᵒᵈ _ _ _ _ _
#align list.argmin_concat List.argmin_concat

/- warning: list.argmax_mem -> List.argmax_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] {f : α -> β} {l : List.{u1} α} {m : α}, (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmax.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l)) -> (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) m l)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1516 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1518 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1516 x._@.Mathlib.Data.List.MinMax._hyg.1518)] {f : α -> β} {l : List.{u2} α} {m : α}, (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmax.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l)) -> (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) m l)
Case conversion may be inaccurate. Consider using '#align list.argmax_mem List.argmax_memₓ'. -/
theorem argmax_mem : ∀ {l : List α} {m : α}, m ∈ argmax f l → m ∈ l
  | [], m => by simp
  | hd :: tl, m => by simpa [argmax, arg_aux] using foldl_arg_aux_mem _ tl hd m
#align list.argmax_mem List.argmax_mem

/- warning: list.argmin_mem -> List.argmin_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] {f : α -> β} {l : List.{u1} α} {m : α}, (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmin.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l)) -> (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) m l)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1629 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1631 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1629 x._@.Mathlib.Data.List.MinMax._hyg.1631)] {f : α -> β} {l : List.{u2} α} {m : α}, (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmin.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l)) -> (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) m l)
Case conversion may be inaccurate. Consider using '#align list.argmin_mem List.argmin_memₓ'. -/
theorem argmin_mem : ∀ {l : List α} {m : α}, m ∈ argmin f l → m ∈ l :=
  @argmax_mem _ βᵒᵈ _ _ _
#align list.argmin_mem List.argmin_mem

/- warning: list.argmax_eq_none -> List.argmax_eq_none is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] {f : α -> β} {l : List.{u1} α}, Iff (Eq.{succ u1} (Option.{u1} α) (List.argmax.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l) (Option.none.{u1} α)) (Eq.{succ u1} (List.{u1} α) l (List.nil.{u1} α))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1688 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1690 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1688 x._@.Mathlib.Data.List.MinMax._hyg.1690)] {f : α -> β} {l : List.{u2} α}, Iff (Eq.{succ u2} (Option.{u2} α) (List.argmax.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l) (Option.none.{u2} α)) (Eq.{succ u2} (List.{u2} α) l (List.nil.{u2} α))
Case conversion may be inaccurate. Consider using '#align list.argmax_eq_none List.argmax_eq_noneₓ'. -/
@[simp]
theorem argmax_eq_none : l.argmax f = none ↔ l = [] := by simp [argmax]
#align list.argmax_eq_none List.argmax_eq_none

/- warning: list.argmin_eq_none -> List.argmin_eq_none is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Preorder.{u2} β] [_inst_2 : DecidableRel.{succ u2} β (LT.lt.{u2} β (Preorder.toLT.{u2} β _inst_1))] {f : α -> β} {l : List.{u1} α}, Iff (Eq.{succ u1} (Option.{u1} α) (List.argmin.{u1, u2} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l) (Option.none.{u1} α)) (Eq.{succ u1} (List.{u1} α) l (List.nil.{u1} α))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Preorder.{u1} β] [_inst_2 : DecidableRel.{succ u1} β (fun (x._@.Mathlib.Data.List.MinMax._hyg.1741 : β) (x._@.Mathlib.Data.List.MinMax._hyg.1743 : β) => LT.lt.{u1} β (Preorder.toLT.{u1} β _inst_1) x._@.Mathlib.Data.List.MinMax._hyg.1741 x._@.Mathlib.Data.List.MinMax._hyg.1743)] {f : α -> β} {l : List.{u2} α}, Iff (Eq.{succ u2} (Option.{u2} α) (List.argmin.{u2, u1} α β _inst_1 (fun (a : β) (b : β) => _inst_2 a b) f l) (Option.none.{u2} α)) (Eq.{succ u2} (List.{u2} α) l (List.nil.{u2} α))
Case conversion may be inaccurate. Consider using '#align list.argmin_eq_none List.argmin_eq_noneₓ'. -/
@[simp]
theorem argmin_eq_none : l.argmin f = none ↔ l = [] :=
  @argmax_eq_none _ βᵒᵈ _ _ _ _
#align list.argmin_eq_none List.argmin_eq_none

end Preorder

section LinearOrder

variable [LinearOrder β] {f : α → β} {l : List α} {o : Option α} {a m : α}

/- warning: list.le_of_mem_argmax -> List.le_of_mem_argmax is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {f : α -> β} {l : List.{u1} α} {a : α} {m : α}, (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmax.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l)) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a) (f m))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {f : α -> β} {l : List.{u2} α} {a : α} {m : α}, (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmax.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l)) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f a) (f m))
Case conversion may be inaccurate. Consider using '#align list.le_of_mem_argmax List.le_of_mem_argmaxₓ'. -/
theorem le_of_mem_argmax : a ∈ l → m ∈ argmax f l → f a ≤ f m := fun ha hm =>
  le_of_not_lt <| not_lt_of_mem_argmax ha hm
#align list.le_of_mem_argmax List.le_of_mem_argmax

/- warning: list.le_of_mem_argmin -> List.le_of_mem_argmin is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {f : α -> β} {l : List.{u1} α} {a : α} {m : α}, (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmin.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l)) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f m) (f a))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {f : α -> β} {l : List.{u2} α} {a : α} {m : α}, (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmin.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l)) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f m) (f a))
Case conversion may be inaccurate. Consider using '#align list.le_of_mem_argmin List.le_of_mem_argminₓ'. -/
theorem le_of_mem_argmin : a ∈ l → m ∈ argmin f l → f m ≤ f a :=
  @le_of_mem_argmax _ βᵒᵈ _ _ _ _ _
#align list.le_of_mem_argmin List.le_of_mem_argmin

/- warning: list.argmax_cons -> List.argmax_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] (f : α -> β) (a : α) (l : List.{u1} α), Eq.{succ u1} (Option.{u1} α) (List.argmax.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f (List.cons.{u1} α a l)) (Option.casesOn.{succ u1, u1} α (fun (_x : Option.{u1} α) => Option.{u1} α) (List.argmax.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l) (Option.some.{u1} α a) (fun (c : α) => ite.{succ u1} (Option.{u1} α) (LT.lt.{u2} β (Preorder.toLT.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a) (f c)) (LT.lt.decidable.{u2} β _inst_1 (f a) (f c)) (Option.some.{u1} α c) (Option.some.{u1} α a)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] (f : α -> β) (a : α) (l : List.{u2} α), Eq.{succ u2} (Option.{u2} α) (List.argmax.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f (List.cons.{u2} α a l)) (Option.casesOn.{succ u2, u2} α (fun (_x : Option.{u2} α) => Option.{u2} α) (List.argmax.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l) (Option.some.{u2} α a) (fun (c : α) => ite.{succ u2} (Option.{u2} α) (LT.lt.{u1} β (Preorder.toLT.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f a) (f c)) (instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 (f a) (f c)) (Option.some.{u2} α c) (Option.some.{u2} α a)))
Case conversion may be inaccurate. Consider using '#align list.argmax_cons List.argmax_consₓ'. -/
theorem argmax_cons (f : α → β) (a : α) (l : List α) :
    argmax f (a :: l) =
      Option.casesOn (argmax f l) (some a) fun c => if f a < f c then some c else some a :=
  List.reverseRecOn l rfl fun hd tl ih =>
    by
    rw [← cons_append, argmax_concat, ih, argmax_concat]
    cases' h : argmax f hd with m
    · simp [h]
    dsimp
    rw [← apply_ite, ← apply_ite]
    dsimp
    split_ifs <;> try rfl
    · exact absurd (lt_trans ‹f a < f m› ‹_›) ‹_›
    · cases (‹f a < f tl›.lt_or_lt _).elim ‹_› ‹_›
#align list.argmax_cons List.argmax_cons

/- warning: list.argmin_cons -> List.argmin_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] (f : α -> β) (a : α) (l : List.{u1} α), Eq.{succ u1} (Option.{u1} α) (List.argmin.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f (List.cons.{u1} α a l)) (Option.casesOn.{succ u1, u1} α (fun (_x : Option.{u1} α) => Option.{u1} α) (List.argmin.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l) (Option.some.{u1} α a) (fun (c : α) => ite.{succ u1} (Option.{u1} α) (LT.lt.{u2} β (Preorder.toLT.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f c) (f a)) (LT.lt.decidable.{u2} β _inst_1 (f c) (f a)) (Option.some.{u1} α c) (Option.some.{u1} α a)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] (f : α -> β) (a : α) (l : List.{u2} α), Eq.{succ u2} (Option.{u2} α) (List.argmin.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f (List.cons.{u2} α a l)) (Option.casesOn.{succ u2, u2} α (fun (_x : Option.{u2} α) => Option.{u2} α) (List.argmin.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l) (Option.some.{u2} α a) (fun (c : α) => ite.{succ u2} (Option.{u2} α) (LT.lt.{u1} β (Preorder.toLT.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f c) (f a)) (instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 (f c) (f a)) (Option.some.{u2} α c) (Option.some.{u2} α a)))
Case conversion may be inaccurate. Consider using '#align list.argmin_cons List.argmin_consₓ'. -/
theorem argmin_cons (f : α → β) (a : α) (l : List α) :
    argmin f (a :: l) =
      Option.casesOn (argmin f l) (some a) fun c => if f c < f a then some c else some a :=
  by convert@argmax_cons _ βᵒᵈ _ _ _ _
#align list.argmin_cons List.argmin_cons

variable [DecidableEq α]

/- warning: list.index_of_argmax -> List.index_of_argmax is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {f : α -> β} [_inst_2 : DecidableEq.{succ u1} α] {l : List.{u1} α} {m : α}, (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmax.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l)) -> (forall {a : α}, (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f m) (f a)) -> (LE.le.{0} Nat Nat.hasLe (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) m l) (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) a l)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {f : α -> β} [_inst_2 : DecidableEq.{succ u2} α] {l : List.{u2} α} {m : α}, (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmax.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l)) -> (forall {a : α}, (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f m) (f a)) -> (LE.le.{0} Nat instLENat (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) m l) (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) a l)))
Case conversion may be inaccurate. Consider using '#align list.index_of_argmax List.index_of_argmaxₓ'. -/
theorem index_of_argmax :
    ∀ {l : List α} {m : α}, m ∈ argmax f l → ∀ {a}, a ∈ l → f m ≤ f a → l.indexOfₓ m ≤ l.indexOfₓ a
  | [], m, _, _, _, _ => by simp
  | hd :: tl, m, hm, a, ha, ham =>
    by
    simp only [index_of_cons, argmax_cons, Option.mem_def] at hm⊢
    cases h : argmax f tl
    · rw [h] at hm
      simp_all
    rw [h] at hm
    dsimp only at hm
    obtain rfl | ha := ha <;> split_ifs  at hm <;> subst hm
    · cases not_le_of_lt ‹_› ‹_›
    · rw [if_neg, if_neg]
      exact Nat.succ_le_succ (index_of_argmax h ha ham)
      · exact ne_of_apply_ne f (lt_of_lt_of_le ‹_› ‹_›).ne'
      · exact ne_of_apply_ne _ ‹f hd < f val›.ne'
    · rw [if_pos rfl]
      exact bot_le
#align list.index_of_argmax List.index_of_argmax

/- warning: list.index_of_argmin -> List.index_of_argmin is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {f : α -> β} [_inst_2 : DecidableEq.{succ u1} α] {l : List.{u1} α} {m : α}, (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmin.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l)) -> (forall {a : α}, (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a) (f m)) -> (LE.le.{0} Nat Nat.hasLe (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) m l) (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) a l)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {f : α -> β} [_inst_2 : DecidableEq.{succ u2} α] {l : List.{u2} α} {m : α}, (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmin.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l)) -> (forall {a : α}, (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f a) (f m)) -> (LE.le.{0} Nat instLENat (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) m l) (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) a l)))
Case conversion may be inaccurate. Consider using '#align list.index_of_argmin List.index_of_argminₓ'. -/
theorem index_of_argmin :
    ∀ {l : List α} {m : α},
      m ∈ argmin f l → ∀ {a}, a ∈ l → f a ≤ f m → l.indexOfₓ m ≤ l.indexOfₓ a :=
  @index_of_argmax _ βᵒᵈ _ _ _
#align list.index_of_argmin List.index_of_argmin

/- warning: list.mem_argmax_iff -> List.mem_argmax_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {f : α -> β} {l : List.{u1} α} {m : α} [_inst_2 : DecidableEq.{succ u1} α], Iff (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmax.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l)) (And (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) m l) (And (forall (a : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a) (f m))) (forall (a : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f m) (f a)) -> (LE.le.{0} Nat Nat.hasLe (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) m l) (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) a l)))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {f : α -> β} {l : List.{u2} α} {m : α} [_inst_2 : DecidableEq.{succ u2} α], Iff (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmax.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l)) (And (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) m l) (And (forall (a : α), (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f a) (f m))) (forall (a : α), (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f m) (f a)) -> (LE.le.{0} Nat instLENat (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) m l) (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) a l)))))
Case conversion may be inaccurate. Consider using '#align list.mem_argmax_iff List.mem_argmax_iffₓ'. -/
theorem mem_argmax_iff :
    m ∈ argmax f l ↔
      m ∈ l ∧ (∀ a ∈ l, f a ≤ f m) ∧ ∀ a ∈ l, f m ≤ f a → l.indexOfₓ m ≤ l.indexOfₓ a :=
  ⟨fun hm => ⟨argmax_mem hm, fun a ha => le_of_mem_argmax ha hm, fun _ => index_of_argmax hm⟩,
    by
    rintro ⟨hml, ham, hma⟩
    cases' harg : argmax f l with n
    · simp_all
    · have :=
        le_antisymm (hma n (argmax_mem harg) (le_of_mem_argmax hml harg))
          (index_of_argmax harg hml (ham _ (argmax_mem harg)))
      rw [(index_of_inj hml (argmax_mem harg)).1 this, Option.mem_def]⟩
#align list.mem_argmax_iff List.mem_argmax_iff

/- warning: list.argmax_eq_some_iff -> List.argmax_eq_some_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {f : α -> β} {l : List.{u1} α} {m : α} [_inst_2 : DecidableEq.{succ u1} α], Iff (Eq.{succ u1} (Option.{u1} α) (List.argmax.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l) (Option.some.{u1} α m)) (And (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) m l) (And (forall (a : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a) (f m))) (forall (a : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f m) (f a)) -> (LE.le.{0} Nat Nat.hasLe (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) m l) (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) a l)))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {f : α -> β} {l : List.{u2} α} {m : α} [_inst_2 : DecidableEq.{succ u2} α], Iff (Eq.{succ u2} (Option.{u2} α) (List.argmax.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l) (Option.some.{u2} α m)) (And (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) m l) (And (forall (a : α), (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f a) (f m))) (forall (a : α), (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f m) (f a)) -> (LE.le.{0} Nat instLENat (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) m l) (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) a l)))))
Case conversion may be inaccurate. Consider using '#align list.argmax_eq_some_iff List.argmax_eq_some_iffₓ'. -/
theorem argmax_eq_some_iff :
    argmax f l = some m ↔
      m ∈ l ∧ (∀ a ∈ l, f a ≤ f m) ∧ ∀ a ∈ l, f m ≤ f a → l.indexOfₓ m ≤ l.indexOfₓ a :=
  mem_argmax_iff
#align list.argmax_eq_some_iff List.argmax_eq_some_iff

/- warning: list.mem_argmin_iff -> List.mem_argmin_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {f : α -> β} {l : List.{u1} α} {m : α} [_inst_2 : DecidableEq.{succ u1} α], Iff (Membership.Mem.{u1, u1} α (Option.{u1} α) (Option.hasMem.{u1} α) m (List.argmin.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l)) (And (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) m l) (And (forall (a : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f m) (f a))) (forall (a : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a) (f m)) -> (LE.le.{0} Nat Nat.hasLe (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) m l) (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) a l)))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {f : α -> β} {l : List.{u2} α} {m : α} [_inst_2 : DecidableEq.{succ u2} α], Iff (Membership.mem.{u2, u2} α (Option.{u2} α) (Option.instMembershipOption.{u2} α) m (List.argmin.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l)) (And (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) m l) (And (forall (a : α), (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f m) (f a))) (forall (a : α), (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f a) (f m)) -> (LE.le.{0} Nat instLENat (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) m l) (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) a l)))))
Case conversion may be inaccurate. Consider using '#align list.mem_argmin_iff List.mem_argmin_iffₓ'. -/
theorem mem_argmin_iff :
    m ∈ argmin f l ↔
      m ∈ l ∧ (∀ a ∈ l, f m ≤ f a) ∧ ∀ a ∈ l, f a ≤ f m → l.indexOfₓ m ≤ l.indexOfₓ a :=
  @mem_argmax_iff _ βᵒᵈ _ _ _ _ _
#align list.mem_argmin_iff List.mem_argmin_iff

/- warning: list.argmin_eq_some_iff -> List.argmin_eq_some_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {f : α -> β} {l : List.{u1} α} {m : α} [_inst_2 : DecidableEq.{succ u1} α], Iff (Eq.{succ u1} (Option.{u1} α) (List.argmin.{u1, u2} α β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))) (fun (a : β) (b : β) => LT.lt.decidable.{u2} β _inst_1 a b) f l) (Option.some.{u1} α m)) (And (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) m l) (And (forall (a : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f m) (f a))) (forall (a : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) a l) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a) (f m)) -> (LE.le.{0} Nat Nat.hasLe (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) m l) (List.indexOfₓ.{u1} α (fun (a : α) (b : α) => _inst_2 a b) a l)))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {f : α -> β} {l : List.{u2} α} {m : α} [_inst_2 : DecidableEq.{succ u2} α], Iff (Eq.{succ u2} (Option.{u2} α) (List.argmin.{u2, u1} α β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))) (fun (a : β) (b : β) => instDecidableLtToLTToPreorderToPartialOrder.{u1} β _inst_1 a b) f l) (Option.some.{u2} α m)) (And (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) m l) (And (forall (a : α), (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f m) (f a))) (forall (a : α), (Membership.mem.{u2, u2} α (List.{u2} α) (List.instMembershipList.{u2} α) a l) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f a) (f m)) -> (LE.le.{0} Nat instLENat (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) m l) (List.indexOf.{u2} α (instBEq.{u2} α (fun (a : α) (b : α) => _inst_2 a b)) a l)))))
Case conversion may be inaccurate. Consider using '#align list.argmin_eq_some_iff List.argmin_eq_some_iffₓ'. -/
theorem argmin_eq_some_iff :
    argmin f l = some m ↔
      m ∈ l ∧ (∀ a ∈ l, f m ≤ f a) ∧ ∀ a ∈ l, f a ≤ f m → l.indexOfₓ m ≤ l.indexOfₓ a :=
  mem_argmin_iff
#align list.argmin_eq_some_iff List.argmin_eq_some_iff

end LinearOrder

section MaximumMinimum

section Preorder

variable [Preorder α] [@DecidableRel α (· < ·)] {l : List α} {a m : α}

#print List.maximum /-
/-- `maximum l` returns an `with_bot α`, the largest element of `l` for nonempty lists, and `⊥` for
`[]`  -/
def maximum (l : List α) : WithBot α :=
  argmax id l
#align list.maximum List.maximum
-/

#print List.minimum /-
/-- `minimum l` returns an `with_top α`, the smallest element of `l` for nonempty lists, and `⊤` for
`[]`  -/
def minimum (l : List α) : WithTop α :=
  argmin id l
#align list.minimum List.minimum
-/

#print List.maximum_nil /-
@[simp]
theorem maximum_nil : maximum ([] : List α) = ⊥ :=
  rfl
#align list.maximum_nil List.maximum_nil
-/

#print List.minimum_nil /-
@[simp]
theorem minimum_nil : minimum ([] : List α) = ⊤ :=
  rfl
#align list.minimum_nil List.minimum_nil
-/

#print List.maximum_singleton /-
@[simp]
theorem maximum_singleton (a : α) : maximum [a] = a :=
  rfl
#align list.maximum_singleton List.maximum_singleton
-/

#print List.minimum_singleton /-
@[simp]
theorem minimum_singleton (a : α) : minimum [a] = a :=
  rfl
#align list.minimum_singleton List.minimum_singleton
-/

#print List.maximum_mem /-
theorem maximum_mem {l : List α} {m : α} : (maximum l : WithTop α) = m → m ∈ l :=
  argmax_mem
#align list.maximum_mem List.maximum_mem
-/

#print List.minimum_mem /-
theorem minimum_mem {l : List α} {m : α} : (minimum l : WithBot α) = m → m ∈ l :=
  argmin_mem
#align list.minimum_mem List.minimum_mem
-/

#print List.maximum_eq_none /-
@[simp]
theorem maximum_eq_none {l : List α} : l.maximum = none ↔ l = [] :=
  argmax_eq_none
#align list.maximum_eq_none List.maximum_eq_none
-/

#print List.minimum_eq_none /-
@[simp]
theorem minimum_eq_none {l : List α} : l.minimum = none ↔ l = [] :=
  argmin_eq_none
#align list.minimum_eq_none List.minimum_eq_none
-/

#print List.not_lt_maximum_of_mem /-
theorem not_lt_maximum_of_mem : a ∈ l → (maximum l : WithBot α) = m → ¬m < a :=
  not_lt_of_mem_argmax
#align list.not_lt_maximum_of_mem List.not_lt_maximum_of_mem
-/

#print List.minimum_not_lt_of_mem /-
theorem minimum_not_lt_of_mem : a ∈ l → (minimum l : WithTop α) = m → ¬a < m :=
  not_lt_of_mem_argmin
#align list.minimum_not_lt_of_mem List.minimum_not_lt_of_mem
-/

#print List.not_lt_maximum_of_mem' /-
theorem not_lt_maximum_of_mem' (ha : a ∈ l) : ¬maximum l < (a : WithBot α) :=
  by
  cases h : l.maximum
  · simp_all
  · simp_rw [WithBot.some_eq_coe, WithBot.coe_lt_coe, not_lt_maximum_of_mem ha h, not_false_iff]
#align list.not_lt_maximum_of_mem' List.not_lt_maximum_of_mem'
-/

#print List.not_lt_minimum_of_mem' /-
theorem not_lt_minimum_of_mem' (ha : a ∈ l) : ¬(a : WithTop α) < minimum l :=
  @not_lt_maximum_of_mem' αᵒᵈ _ _ _ _ ha
#align list.not_lt_minimum_of_mem' List.not_lt_minimum_of_mem'
-/

end Preorder

section LinearOrder

variable [LinearOrder α] {l : List α} {a m : α}

#print List.maximum_concat /-
theorem maximum_concat (a : α) (l : List α) : maximum (l ++ [a]) = max (maximum l) a :=
  by
  simp only [maximum, argmax_concat, id]
  cases h : argmax id l
  · exact (max_eq_right bot_le).symm
  · simp [Option.coe_def, max_def_lt]
#align list.maximum_concat List.maximum_concat
-/

#print List.le_maximum_of_mem /-
theorem le_maximum_of_mem : a ∈ l → (maximum l : WithBot α) = m → a ≤ m :=
  le_of_mem_argmax
#align list.le_maximum_of_mem List.le_maximum_of_mem
-/

#print List.minimum_le_of_mem /-
theorem minimum_le_of_mem : a ∈ l → (minimum l : WithTop α) = m → m ≤ a :=
  le_of_mem_argmin
#align list.minimum_le_of_mem List.minimum_le_of_mem
-/

#print List.le_maximum_of_mem' /-
theorem le_maximum_of_mem' (ha : a ∈ l) : (a : WithBot α) ≤ maximum l :=
  le_of_not_lt <| not_lt_maximum_of_mem' ha
#align list.le_maximum_of_mem' List.le_maximum_of_mem'
-/

#print List.le_minimum_of_mem' /-
theorem le_minimum_of_mem' (ha : a ∈ l) : minimum l ≤ (a : WithTop α) :=
  @le_maximum_of_mem' αᵒᵈ _ _ _ ha
#align list.le_minimum_of_mem' List.le_minimum_of_mem'
-/

#print List.minimum_concat /-
theorem minimum_concat (a : α) (l : List α) : minimum (l ++ [a]) = min (minimum l) a :=
  @maximum_concat αᵒᵈ _ _ _
#align list.minimum_concat List.minimum_concat
-/

#print List.maximum_cons /-
theorem maximum_cons (a : α) (l : List α) : maximum (a :: l) = max a (maximum l) :=
  List.reverseRecOn l (by simp [@max_eq_left (WithBot α) _ _ _ bot_le]) fun tl hd ih => by
    rw [← cons_append, maximum_concat, ih, maximum_concat, max_assoc]
#align list.maximum_cons List.maximum_cons
-/

#print List.minimum_cons /-
theorem minimum_cons (a : α) (l : List α) : minimum (a :: l) = min a (minimum l) :=
  @maximum_cons αᵒᵈ _ _ _
#align list.minimum_cons List.minimum_cons
-/

#print List.maximum_eq_coe_iff /-
theorem maximum_eq_coe_iff : maximum l = m ↔ m ∈ l ∧ ∀ a ∈ l, a ≤ m :=
  by
  unfold_coes
  simp only [maximum, argmax_eq_some_iff, id]
  constructor
  · simp (config := { contextual := true }) only [true_and_iff, forall_true_iff]
  · simp (config := { contextual := true }) only [true_and_iff, forall_true_iff]
    intro h a hal hma
    rw [le_antisymm hma (h.2 a hal)]
#align list.maximum_eq_coe_iff List.maximum_eq_coe_iff
-/

#print List.minimum_eq_coe_iff /-
theorem minimum_eq_coe_iff : minimum l = m ↔ m ∈ l ∧ ∀ a ∈ l, m ≤ a :=
  @maximum_eq_coe_iff αᵒᵈ _ _ _
#align list.minimum_eq_coe_iff List.minimum_eq_coe_iff
-/

end LinearOrder

end MaximumMinimum

section Fold

variable [LinearOrder α]

section OrderBot

variable [OrderBot α] {l : List α}

/- warning: list.foldr_max_of_ne_nil -> List.foldr_max_of_ne_nil is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))] {l : List.{u1} α}, (Ne.{succ u1} (List.{u1} α) l (List.nil.{u1} α)) -> (Eq.{succ u1} (WithBot.{u1} α) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (WithBot.{u1} α) (HasLiftT.mk.{succ u1, succ u1} α (WithBot.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} α (WithBot.{u1} α) (WithBot.hasCoeT.{u1} α))) (List.foldr.{u1, u1} α α (LinearOrder.max.{u1} α _inst_1) (Bot.bot.{u1} α (OrderBot.toHasBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) _inst_2)) l)) (List.maximum.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) (fun (a : α) (b : α) => LT.lt.decidable.{u1} α _inst_1 a b) l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))))] {l : List.{u1} α}, (Ne.{succ u1} (List.{u1} α) l (List.nil.{u1} α)) -> (Eq.{succ u1} (WithBot.{u1} α) (WithBot.some.{u1} α (List.foldr.{u1, u1} α α (Max.max.{u1} α (LinearOrder.toMax.{u1} α _inst_1)) (Bot.bot.{u1} α (OrderBot.toBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) _inst_2)) l)) (List.maximum.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) (fun (a : α) (b : α) => instDecidableLtToLTToPreorderToPartialOrder.{u1} α _inst_1 a b) l))
Case conversion may be inaccurate. Consider using '#align list.foldr_max_of_ne_nil List.foldr_max_of_ne_nilₓ'. -/
@[simp]
theorem foldr_max_of_ne_nil (h : l ≠ []) : ↑(l.foldr max ⊥) = l.maximum :=
  by
  induction' l with hd tl IH
  · contradiction
  · rw [maximum_cons, foldr, WithBot.coe_max]
    by_cases h : tl = []
    · simp [h]
    · simp [IH h]
#align list.foldr_max_of_ne_nil List.foldr_max_of_ne_nil

/- warning: list.max_le_of_forall_le -> List.max_le_of_forall_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))] (l : List.{u1} α) (a : α), (forall (x : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) x l) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) x a)) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) (List.foldr.{u1, u1} α α (LinearOrder.max.{u1} α _inst_1) (Bot.bot.{u1} α (OrderBot.toHasBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) _inst_2)) l) a)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))))] (l : List.{u1} α) (a : α), (forall (x : α), (Membership.mem.{u1, u1} α (List.{u1} α) (List.instMembershipList.{u1} α) x l) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) x a)) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) (List.foldr.{u1, u1} α α (Max.max.{u1} α (LinearOrder.toMax.{u1} α _inst_1)) (Bot.bot.{u1} α (OrderBot.toBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) _inst_2)) l) a)
Case conversion may be inaccurate. Consider using '#align list.max_le_of_forall_le List.max_le_of_forall_leₓ'. -/
theorem max_le_of_forall_le (l : List α) (a : α) (h : ∀ x ∈ l, x ≤ a) : l.foldr max ⊥ ≤ a :=
  by
  induction' l with y l IH
  · simp
  · simpa [h y (mem_cons_self _ _)] using IH fun x hx => h x <| mem_cons_of_mem _ hx
#align list.max_le_of_forall_le List.max_le_of_forall_le

/- warning: list.le_max_of_le -> List.le_max_of_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))] {l : List.{u1} α} {a : α} {x : α}, (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) x l) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a x) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a (List.foldr.{u1, u1} α α (LinearOrder.max.{u1} α _inst_1) (Bot.bot.{u1} α (OrderBot.toHasBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) _inst_2)) l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))))] {l : List.{u1} α} {a : α} {x : α}, (Membership.mem.{u1, u1} α (List.{u1} α) (List.instMembershipList.{u1} α) x l) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) a x) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) a (List.foldr.{u1, u1} α α (Max.max.{u1} α (LinearOrder.toMax.{u1} α _inst_1)) (Bot.bot.{u1} α (OrderBot.toBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) _inst_2)) l))
Case conversion may be inaccurate. Consider using '#align list.le_max_of_le List.le_max_of_leₓ'. -/
theorem le_max_of_le {l : List α} {a x : α} (hx : x ∈ l) (h : a ≤ x) : a ≤ l.foldr max ⊥ :=
  by
  induction' l with y l IH
  · exact absurd hx (not_mem_nil _)
  · obtain rfl | hl := hx
    simp only [foldr, foldr_cons]
    · exact le_max_of_le_left h
    · exact le_max_of_le_right (IH hl)
#align list.le_max_of_le List.le_max_of_le

end OrderBot

section OrderTop

variable [OrderTop α] {l : List α}

/- warning: list.foldr_min_of_ne_nil -> List.foldr_min_of_ne_nil is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))] {l : List.{u1} α}, (Ne.{succ u1} (List.{u1} α) l (List.nil.{u1} α)) -> (Eq.{succ u1} (WithTop.{u1} α) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) α (WithTop.{u1} α) (HasLiftT.mk.{succ u1, succ u1} α (WithTop.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} α (WithTop.{u1} α) (WithTop.hasCoeT.{u1} α))) (List.foldr.{u1, u1} α α (LinearOrder.min.{u1} α _inst_1) (Top.top.{u1} α (OrderTop.toHasTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) _inst_2)) l)) (List.minimum.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))) (fun (a : α) (b : α) => LT.lt.decidable.{u1} α _inst_1 a b) l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))))] {l : List.{u1} α}, (Ne.{succ u1} (List.{u1} α) l (List.nil.{u1} α)) -> (Eq.{succ u1} (WithTop.{u1} α) (WithTop.some.{u1} α (List.foldr.{u1, u1} α α (Min.min.{u1} α (LinearOrder.toMin.{u1} α _inst_1)) (Top.top.{u1} α (OrderTop.toTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) _inst_2)) l)) (List.minimum.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))) (fun (a : α) (b : α) => instDecidableLtToLTToPreorderToPartialOrder.{u1} α _inst_1 a b) l))
Case conversion may be inaccurate. Consider using '#align list.foldr_min_of_ne_nil List.foldr_min_of_ne_nilₓ'. -/
@[simp]
theorem foldr_min_of_ne_nil (h : l ≠ []) : ↑(l.foldr min ⊤) = l.minimum :=
  @foldr_max_of_ne_nil αᵒᵈ _ _ _ h
#align list.foldr_min_of_ne_nil List.foldr_min_of_ne_nil

/- warning: list.le_min_of_forall_le -> List.le_min_of_forall_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))] (l : List.{u1} α) (a : α), (forall (x : α), (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) x l) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a x)) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) a (List.foldr.{u1, u1} α α (LinearOrder.min.{u1} α _inst_1) (Top.top.{u1} α (OrderTop.toHasTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) _inst_2)) l))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))))] (l : List.{u1} α) (a : α), (forall (x : α), (Membership.mem.{u1, u1} α (List.{u1} α) (List.instMembershipList.{u1} α) x l) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) a x)) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) a (List.foldr.{u1, u1} α α (Min.min.{u1} α (LinearOrder.toMin.{u1} α _inst_1)) (Top.top.{u1} α (OrderTop.toTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) _inst_2)) l))
Case conversion may be inaccurate. Consider using '#align list.le_min_of_forall_le List.le_min_of_forall_leₓ'. -/
theorem le_min_of_forall_le (l : List α) (a : α) (h : ∀ x ∈ l, a ≤ x) : a ≤ l.foldr min ⊤ :=
  @max_le_of_forall_le αᵒᵈ _ _ _ _ h
#align list.le_min_of_forall_le List.le_min_of_forall_le

/- warning: list.min_le_of_le -> List.min_le_of_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1)))))] (l : List.{u1} α) (a : α) {x : α}, (Membership.Mem.{u1, u1} α (List.{u1} α) (List.hasMem.{u1} α) x l) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) x a) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) (List.foldr.{u1, u1} α α (LinearOrder.min.{u1} α _inst_1) (Top.top.{u1} α (OrderTop.toHasTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (LinearOrder.toLattice.{u1} α _inst_1))))) _inst_2)) l) a)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : LinearOrder.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1))))))] (l : List.{u1} α) (a : α) {x : α}, (Membership.mem.{u1, u1} α (List.{u1} α) (List.instMembershipList.{u1} α) x l) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) x a) -> (LE.le.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) (List.foldr.{u1, u1} α α (Min.min.{u1} α (LinearOrder.toMin.{u1} α _inst_1)) (Top.top.{u1} α (OrderTop.toTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α (Lattice.toSemilatticeInf.{u1} α (DistribLattice.toLattice.{u1} α (instDistribLattice.{u1} α _inst_1)))))) _inst_2)) l) a)
Case conversion may be inaccurate. Consider using '#align list.min_le_of_le List.min_le_of_leₓ'. -/
theorem min_le_of_le (l : List α) (a : α) {x : α} (hx : x ∈ l) (h : x ≤ a) : l.foldr min ⊤ ≤ a :=
  @le_max_of_le αᵒᵈ _ _ _ _ _ hx h
#align list.min_le_of_le List.min_le_of_le

end OrderTop

end Fold

end List


/-
We saw a bit of Lean's type theory last time. One of the core
functions of Lean is a type-checker.

To get a sense of how tha works, we will discuss type checking in
in the context of simple type theory.

We have typing conclusions called _judgements_ or _inferences_ and
rules for building those judgements.

A typing judgement always takes place in the presence of a
_local context_ C. You can think of this as the current scope in
the program. The local context keeps track of the current
constants, variables and their corresponding types.

Definitions and variables add new terms to the local context.
-/

variable (x : Nat) -- now we have a variable of type Nat in context
variable (f' : Nat → Float) -- now a function in the context

/-
We write C ⊢ x : τ to mean that in the (local) context, the term x has
type τ.

The rules for deriving typing judgement are

Typing rules:

  —————————— Cst   if c is declared with type σ
  C ⊢ c : σ

  —————————— Var   if x : σ is the last occurrence of x in C
  C ⊢ x : σ

  C ⊢ t : σ → τ    C ⊢ u : σ
  ——————————————————————————— App
  C ⊢ t u : τ

  C, x : σ ⊢ t : τ
  ———————————————————————— Lam
  C ⊢ (λx : σ, t) : σ → τ

The Cst rule allows us to type check constants and the Var rule does
the same for variables.

The final two rules describe how function application and
abstraction interact with typing.
-/

def sadFace : String := sorry

#check sadFace -- type checks correctly

variable (s : String)

#check s -- type checks correctly

-- use a "general" type α
variable { α : Type }

def sumin : α := sorry

#check sumin

variable (a₀ : α)

#check a₀

variable { β : Type }
variable  (f : α → β)

#check f a₀  -- good

def myTerm (a : α) : β := sorry

def myFun : α → β := fun a => myTerm a

#check @myFun α β

/-
Types have terms themselves in Lean
-/
variable (α β γ δ ε : Type)

/-

## Type inhabitation

Not every type has a term in general. You are even more
constrained if you case try to construct a term of a type
given a fixed local context.

We say a type in _inhabited_ if it there is a term of that type.

For iterated function types, there is a search process to
try to build a term. Constructing a term of τ → σ using function
abstraction is equivalent to positing a variable x : τ in the
context and then attempting to build a term of σ using x and
other terms in C.

Let's do an example. We mark it with sorry to tone down
the error to a warning as we proceed.
-/

-- Common types are inhabited
example : String := ""
example : Nat := 0
example : Int := 0
example : Float := 0.0
example : Bool := true
example : Char := ⟨0,(by simp)⟩

/-
A generic type build from base types by making function may
not have a term.
-/
-- Can you fill this in?
example : α → β := sorry

-- How about
example : α → α := fun a => a

-- Or
example : α → (β → α) := fun a _ => a

/-
Let's look a more general algorithm in a more complex
examples.
-/

-- We want a term of this type
example : (α → β → γ) → (α → β) → (α → γ) := sorry

/-
We can introduce a bound variable of type f : α → β → γ and
sorry what is left.
-/
example : (α → β → γ) → (α → β) → (α → γ) := fun f => sorry
/-
We see we need to fill in sorry with a term of type (α → β) →
α → γ. We give a bound variable g : α → β to simplify the goal.
-/
example : (α → β → γ) → (α → β) → (α → γ) := fun f g => sorry
/-
We continue again introducing a : α
-/
example : (α → β → γ) → (α → β) → (α → γ) := fun f g a => sorry
/-
Our goal now is to make a term of type γ. This is not a function
type so we cannot continue. But we now have f g a and can use
function application to make new types. In particular, f a : β → γ
and g a : β. With these results, we can use application once
more to get f a (g a) : γ - the desired term.
-/
def done : (α → β → γ) → (α → β) → α → γ :=
  fun (f : α → β → γ) (g : α → β) (a : α) => f a (g a)

-- Some more examples

example : (α → γ) → (δ → β) → (γ → ε) → (β → γ) → δ → ε :=
  fun _ g h k d => h (k (g d))

example : (((α → β) → β) → β) → (α → β) := fun f a => f (fun g => g a)

example : (((α → β) → β) → β) → (α → β) := by
  intro f a
  exact f (fun g => g a)

example : ((β → α) → α) → (β → γ) → (γ → α) → α := sorry


-- Las equivalencias son preórdenes simétricos
-- ===========================================

-- ----------------------------------------------------
-- Ej. 1. Un preorden es una relación reflexiva y
-- transitiva.
--
-- Demostrar que las relaciones de equivalencias son
-- los prórdenes simétricos.
-- ----------------------------------------------------

import tactic

variable {A : Type}
variable R : A → A → Prop

def preorden (R : A → A → Prop) : Prop :=
  reflexive R ∧ transitive R

-- #print equivalence
-- #print symmetric

-- 1ª demostración
example :
  equivalence R ↔ preorden R ∧ symmetric R :=
begin
  split,
  { rintros ⟨h1, h2, h3⟩,
    exact ⟨⟨h1, h3⟩, h2⟩, },
  { rintros ⟨⟨h1, h3⟩, h2⟩,
    exact ⟨h1, h2, h3⟩, },
end

-- 2ª demostración
example :
  equivalence R ↔ preorden R ∧ symmetric R :=
⟨λ ⟨h1, h2, h3⟩, ⟨⟨h1, h3⟩, h2⟩,
 λ ⟨⟨h1, h3⟩, h2⟩, ⟨h1, h2, h3⟩⟩

-- 3ª demostración
example :
  equivalence R ↔ preorden R ∧ symmetric R :=
iff.intro
  ( assume h1 : equivalence R,
    have h2 : reflexive R, from and.left h1,
    have h3 : symmetric R, from and.left (and.right h1),
    have h4 : transitive R, from and.right (and.right h1),
    show preorden R ∧ symmetric R,
      from and.intro (and.intro h2 h4) h3)
  ( assume h1 : preorden R ∧ symmetric R,
    have h2 : preorden R, from and.left h1,
    show equivalence R,
      from and.intro (and.left h2)
             (and.intro (and.right h1) (and.right h2)))

-- 4ª demostración
example :
  equivalence R ↔ preorden R ∧ symmetric R :=
begin
  unfold equivalence preorden,
  tauto,
end

-- 5ª demostración
example :
  equivalence R ↔ preorden R ∧ symmetric R :=
by finish [preorden]


/-
Copyright (c) 2018 Mitchell Rowett. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mitchell Rowett, Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.group_theory.subgroup
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-- The left coset `a*s` corresponding to an element `a : α` and a subset `s : set α` -/
def left_coset {α : Type u_1} [Mul α] (a : α) (s : set α) : set α := (fun (x : α) => a * x) '' s

/-- The right coset `s*a` corresponding to an element `a : α` and a subset `s : set α` -/
def right_coset {α : Type u_1} [Mul α] (s : set α) (a : α) : set α := (fun (x : α) => x * a) '' s

theorem mem_left_coset {α : Type u_1} [Mul α] {s : set α} {x : α} (a : α) (hxS : x ∈ s) :
    a * x ∈ left_coset a s :=
  set.mem_image_of_mem (fun (b : α) => a * b) hxS

theorem mem_right_coset {α : Type u_1} [Mul α] {s : set α} {x : α} (a : α) (hxS : x ∈ s) :
    x * a ∈ right_coset s a :=
  set.mem_image_of_mem (fun (b : α) => b * a) hxS

/-- Equality of two left cosets `a*s` and `b*s` -/
def left_coset_equiv {α : Type u_1} [Mul α] (s : set α) (a : α) (b : α) :=
  left_coset a s = left_coset b s

theorem left_add_coset_equiv_rel {α : Type u_1} [Add α] (s : set α) :
    equivalence (left_add_coset_equiv s) :=
  mk_equivalence (left_add_coset_equiv s) (fun (a : α) => rfl) (fun (a b : α) => Eq.symm)
    fun (a b c : α) => Eq.trans

@[simp] theorem left_coset_assoc {α : Type u_1} [semigroup α] (s : set α) (a : α) (b : α) :
    left_coset a (left_coset b s) = left_coset (a * b) s :=
  sorry

@[simp] theorem left_add_coset_assoc {α : Type u_1} [add_semigroup α] (s : set α) (a : α) (b : α) :
    left_add_coset a (left_add_coset b s) = left_add_coset (a + b) s :=
  sorry

@[simp] theorem right_coset_assoc {α : Type u_1} [semigroup α] (s : set α) (a : α) (b : α) :
    right_coset (right_coset s a) b = right_coset s (a * b) :=
  sorry

@[simp] theorem right_add_coset_assoc {α : Type u_1} [add_semigroup α] (s : set α) (a : α) (b : α) :
    right_add_coset (right_add_coset s a) b = right_add_coset s (a + b) :=
  sorry

theorem left_add_coset_right_add_coset {α : Type u_1} [add_semigroup α] (s : set α) (a : α)
    (b : α) : right_add_coset (left_add_coset a s) b = left_add_coset a (right_add_coset s b) :=
  sorry

@[simp] theorem one_left_coset {α : Type u_1} [monoid α] (s : set α) : left_coset 1 s = s := sorry

@[simp] theorem zero_left_add_coset {α : Type u_1} [add_monoid α] (s : set α) :
    left_add_coset 0 s = s :=
  sorry

@[simp] theorem right_coset_one {α : Type u_1} [monoid α] (s : set α) : right_coset s 1 = s := sorry

@[simp] theorem right_add_coset_zero {α : Type u_1} [add_monoid α] (s : set α) :
    right_add_coset s 0 = s :=
  sorry

theorem mem_own_left_coset {α : Type u_1} [monoid α] (s : submonoid α) (a : α) :
    a ∈ left_coset a ↑s :=
  sorry

theorem mem_own_right_add_coset {α : Type u_1} [add_monoid α] (s : add_submonoid α) (a : α) :
    a ∈ right_add_coset (↑s) a :=
  sorry

theorem mem_left_coset_left_coset {α : Type u_1} [monoid α] (s : submonoid α) {a : α}
    (ha : left_coset a ↑s = ↑s) : a ∈ s :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ∈ s)) (Eq.symm (propext submonoid.mem_coe))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ∈ ↑s)) (Eq.symm ha))) (mem_own_left_coset s a))

theorem mem_right_add_coset_right_add_coset {α : Type u_1} [add_monoid α] (s : add_submonoid α)
    {a : α} (ha : right_add_coset (↑s) a = ↑s) : a ∈ s :=
  eq.mpr (id (Eq._oldrec (Eq.refl (a ∈ s)) (Eq.symm (propext add_submonoid.mem_coe))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (a ∈ ↑s)) (Eq.symm ha))) (mem_own_right_add_coset s a))

theorem mem_left_coset_iff {α : Type u_1} [group α] {s : set α} {x : α} (a : α) :
    x ∈ left_coset a s ↔ a⁻¹ * x ∈ s :=
  sorry

theorem mem_right_coset_iff {α : Type u_1} [group α] {s : set α} {x : α} (a : α) :
    x ∈ right_coset s a ↔ x * (a⁻¹) ∈ s :=
  sorry

theorem left_coset_mem_left_coset {α : Type u_1} [group α] (s : subgroup α) {a : α} (ha : a ∈ s) :
    left_coset a ↑s = ↑s :=
  sorry

theorem right_add_coset_mem_right_add_coset {α : Type u_1} [add_group α] (s : add_subgroup α)
    {a : α} (ha : a ∈ s) : right_add_coset (↑s) a = ↑s :=
  sorry

theorem normal_of_eq_cosets {α : Type u_1} [group α] (s : subgroup α) (N : subgroup.normal s)
    (g : α) : left_coset g ↑s = right_coset (↑s) g :=
  sorry

theorem eq_cosets_of_normal {α : Type u_1} [group α] (s : subgroup α)
    (h : ∀ (g : α), left_coset g ↑s = right_coset (↑s) g) : subgroup.normal s :=
  sorry

theorem normal_iff_eq_add_cosets {α : Type u_1} [add_group α] (s : add_subgroup α) :
    add_subgroup.normal s ↔ ∀ (g : α), left_add_coset g ↑s = right_add_coset (↑s) g :=
  { mp := normal_of_eq_add_cosets s, mpr := eq_add_cosets_of_normal s }

namespace quotient_group

/-- The equivalence relation corresponding to the partition of a group by left cosets
of a subgroup.-/
def Mathlib.quotient_add_group.left_rel {α : Type u_1} [add_group α] (s : add_subgroup α) :
    setoid α :=
  setoid.mk (fun (x y : α) => -x + y ∈ s) sorry

protected instance left_rel_decidable {α : Type u_1} [group α] (s : subgroup α)
    [d : decidable_pred fun (a : α) => a ∈ s] : DecidableRel setoid.r :=
  fun (_x _x_1 : α) => d (_x⁻¹ * _x_1)

/-- `quotient s` is the quotient type representing the left cosets of `s`.
  If `s` is a normal subgroup, `quotient s` is a group -/
def quotient {α : Type u_1} [group α] (s : subgroup α) := quotient (left_rel s)

end quotient_group

namespace quotient_add_group

/-- `quotient s` is the quotient type representing the left cosets of `s`.
  If `s` is a normal subgroup, `quotient s` is a group -/
def quotient {α : Type u_1} [add_group α] (s : add_subgroup α) := quotient (left_rel s)

end quotient_add_group

namespace quotient_group

protected instance Mathlib.quotient_add_group.fintype {α : Type u_1} [add_group α] [fintype α]
    (s : add_subgroup α) [DecidableRel setoid.r] : fintype (quotient_add_group.quotient s) :=
  quotient.fintype (quotient_add_group.left_rel s)

/-- The canonical map from a group `α` to the quotient `α/s`. -/
def Mathlib.quotient_add_group.mk {α : Type u_1} [add_group α] {s : add_subgroup α} (a : α) :
    quotient_add_group.quotient s :=
  quotient.mk' a

theorem induction_on {α : Type u_1} [group α] {s : subgroup α} {C : quotient s → Prop}
    (x : quotient s) (H : ∀ (z : α), C (mk z)) : C x :=
  quotient.induction_on' x H

protected instance quotient.has_coe_t {α : Type u_1} [group α] {s : subgroup α} :
    has_coe_t α (quotient s) :=
  has_coe_t.mk mk

theorem induction_on' {α : Type u_1} [group α] {s : subgroup α} {C : quotient s → Prop}
    (x : quotient s) (H : ∀ (z : α), C ↑z) : C x :=
  quotient.induction_on' x H

protected instance quotient.inhabited {α : Type u_1} [group α] (s : subgroup α) :
    Inhabited (quotient s) :=
  { default := ↑1 }

protected theorem eq {α : Type u_1} [group α] {s : subgroup α} {a : α} {b : α} :
    ↑a = ↑b ↔ a⁻¹ * b ∈ s :=
  quotient.eq'

theorem Mathlib.quotient_add_group.eq_class_eq_left_coset {α : Type u_1} [add_group α]
    (s : add_subgroup α) (g : α) : (set_of fun (x : α) => ↑x = ↑g) = left_add_coset g ↑s :=
  sorry

theorem preimage_image_coe {α : Type u_1} [group α] (N : subgroup α) (s : set α) :
    coe ⁻¹' (coe '' s) = set.Union fun (x : ↥N) => (fun (y : α) => y * ↑x) '' s :=
  sorry

end quotient_group

namespace subgroup

/-- The natural bijection between the cosets `g*s` and `s` -/
def Mathlib.add_subgroup.left_coset_equiv_add_subgroup {α : Type u_1} [add_group α]
    {s : add_subgroup α} (g : α) : ↥(left_add_coset g ↑s) ≃ ↥s :=
  equiv.mk (fun (x : ↥(left_add_coset g ↑s)) => { val := -g + subtype.val x, property := sorry })
    (fun (x : ↥s) => { val := g + subtype.val x, property := sorry }) sorry sorry

/-- A (non-canonical) bijection between a group `α` and the product `(α/s) × s` -/
def Mathlib.add_subgroup.add_group_equiv_quotient_times_add_subgroup {α : Type u_1} [add_group α]
    {s : add_subgroup α} : α ≃ quotient_add_group.quotient s × ↥s :=
  equiv.trans
    (equiv.trans
      (equiv.trans (equiv.symm (equiv.sigma_preimage_equiv quotient_add_group.mk))
        (equiv.sigma_congr_right
          fun (L : quotient_add_group.quotient s) =>
            eq.mpr sorry
              (id (eq.mpr sorry (equiv.refl (Subtype fun (x : α) => quotient.mk' x = L))))))
      (equiv.sigma_congr_right
        fun (L : quotient_add_group.quotient s) =>
          add_subgroup.left_coset_equiv_add_subgroup (quotient.out' L)))
    (equiv.sigma_equiv_prod (quotient_add_group.quotient s) ↥s)

end subgroup

namespace quotient_group

-- FIXME -- why is there no `to_additive`?

/-- If `s` is a subgroup of the group `α`, and `t` is a subset of `α/s`, then
there is a (typically non-canonical) bijection between the preimage of `t` in
`α` and the product `s × t`. -/
def preimage_mk_equiv_subgroup_times_set {α : Type u_1} [group α] (s : subgroup α)
    (t : set (quotient s)) : ↥(mk ⁻¹' t) ≃ ↥s × ↥t :=
  (fun
      (h :
      ∀ {x : quotient s} {a : α},
        x ∈ t → a ∈ s → quotient.mk' (quotient.out' x * a) = quotient.mk' (quotient.out' x)) =>
      equiv.mk (fun (_x : ↥(mk ⁻¹' t)) => sorry) (fun (_x : ↥s × ↥t) => sorry) sorry sorry)
    sorry

end quotient_group

/--
We use the class `has_coe_t` instead of `has_coe` if the first argument is a variable,
end Mathlib


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

Intervals

Naming conventions:
  `i`: infinite
  `o`: open
  `c`: closed

Each interval has the name `I` + letter for left side + letter for right side

TODO: This is just the beginning; a lot of intervals and rules are missing
-/
import data.set.lattice algebra.order algebra.order_functions

namespace set

open set

section intervals
variables {α : Type*} [preorder α] {a a₁ a₂ b b₁ b₂ x : α}

/-- Left-open right-open interval -/
def Ioo (a b : α) := {x | a < x ∧ x < b}

/-- Left-closed right-open interval -/
def Ico (a b : α) := {x | a ≤ x ∧ x < b}

/-- Left-closed right-closed interval -/
def Icc (a b : α) := {x | a ≤ x ∧ x ≤ b}

/-- Left-infinite right-open interval -/
def Iio (a : α) := {x | x < a}

@[simp] lemma mem_Ioo : x ∈ Ioo a b ↔ a < x ∧ x < b := iff.rfl
@[simp] lemma mem_Ico : x ∈ Ico a b ↔ a ≤ x ∧ x < b := iff.rfl
@[simp] lemma mem_Icc : x ∈ Icc a b ↔ a ≤ x ∧ x ≤ b := iff.rfl
@[simp] lemma mem_Iio : x ∈ Iio b ↔ x < b := iff.rfl

@[simp] lemma Ioo_eq_empty (h : b ≤ a) : Ioo a b = ∅ :=
eq_empty_iff_forall_not_mem.2 $ λ x ⟨h₁, h₂⟩, not_le_of_lt (lt_trans h₁ h₂) h

@[simp] lemma Ico_eq_empty (h : b ≤ a) : Ico a b = ∅ :=
eq_empty_iff_forall_not_mem.2 $ λ x ⟨h₁, h₂⟩, not_le_of_lt (lt_of_le_of_lt h₁ h₂) h

@[simp] lemma Icc_eq_empty (h : b < a) : Icc a b = ∅ :=
eq_empty_iff_forall_not_mem.2 $ λ x ⟨h₁, h₂⟩, not_lt_of_le (le_trans h₁ h₂) h

@[simp] lemma Ioo_self (a : α) : Ioo a a = ∅ := Ioo_eq_empty $ le_refl _
@[simp] lemma Ico_self (a : α) : Ico a a = ∅ := Ico_eq_empty $ le_refl _

lemma Iio_ne_empty [no_bot_order α] (a : α) : Iio a ≠ ∅ :=
ne_empty_iff_exists_mem.2 (no_bot a)

lemma Ioo_subset_Ioo (h₁ : a₂ ≤ a₁) (h₂ : b₁ ≤ b₂) :
  Ioo a₁ b₁ ⊆ Ioo a₂ b₂ :=
λ x ⟨hx₁, hx₂⟩, ⟨lt_of_le_of_lt h₁ hx₁, lt_of_lt_of_le hx₂ h₂⟩

lemma Ioo_subset_Ioo_left (h : a₁ ≤ a₂) : Ioo a₂ b ⊆ Ioo a₁ b :=
Ioo_subset_Ioo h (le_refl _)

lemma Ioo_subset_Ioo_right (h : b₁ ≤ b₂) : Ioo a b₁ ⊆ Ioo a b₂ :=
Ioo_subset_Ioo (le_refl _) h

lemma Ico_subset_Ico (h₁ : a₂ ≤ a₁) (h₂ : b₁ ≤ b₂) :
  Ico a₁ b₁ ⊆ Ico a₂ b₂ :=
λ x ⟨hx₁, hx₂⟩, ⟨le_trans h₁ hx₁, lt_of_lt_of_le hx₂ h₂⟩

lemma Ico_subset_Ico_left (h : a₁ ≤ a₂) : Ico a₂ b ⊆ Ico a₁ b :=
Ico_subset_Ico h (le_refl _)

lemma Ico_subset_Ico_right (h : b₁ ≤ b₂) : Ico a b₁ ⊆ Ico a b₂ :=
Ico_subset_Ico (le_refl _) h

lemma Icc_subset_Icc (h₁ : a₂ ≤ a₁) (h₂ : b₁ ≤ b₂) :
  Icc a₁ b₁ ⊆ Icc a₂ b₂ :=
λ x ⟨hx₁, hx₂⟩, ⟨le_trans h₁ hx₁, le_trans hx₂ h₂⟩

lemma Icc_subset_Icc_left (h : a₁ ≤ a₂) : Icc a₂ b ⊆ Icc a₁ b :=
Icc_subset_Icc h (le_refl _)

lemma Icc_subset_Icc_right (h : b₁ ≤ b₂) : Icc a b₁ ⊆ Icc a b₂ :=
Icc_subset_Icc (le_refl _) h

lemma Ico_subset_Ioo_left (h₁ : a₁ < a₂) : Ico a₂ b ⊆ Ioo a₁ b :=
λ x, and.imp_left $ lt_of_lt_of_le h₁

lemma Icc_subset_Ico_right (h₁ : b₁ < b₂) : Icc a b₁ ⊆ Ico a b₂ :=
λ x, and.imp_right $ λ h₂, lt_of_le_of_lt h₂ h₁

lemma Ioo_subset_Ico_self : Ioo a b ⊆ Ico a b := λ x, and.imp_left le_of_lt

lemma Ico_subset_Icc_self : Ico a b ⊆ Icc a b := λ x, and.imp_right le_of_lt

lemma Ioo_subset_Icc_self : Ioo a b ⊆ Icc a b :=
subset.trans Ioo_subset_Ico_self Ico_subset_Icc_self

lemma Ico_subset_Iio_self : Ioo a b ⊆ Iio b := λ x, and.right

end intervals

section partial_order
variables {α : Type*} [partial_order α] {a b : α}

@[simp] lemma Icc_self (a : α) : Icc a a = {a} :=
set.ext $ by simp [Icc, le_antisymm_iff, and_comm]

lemma Ico_diff_Ioo_eq_singleton (h : a < b) : Ico a b \ Ioo a b = {a} :=
set.ext $ λ x, begin
  simp, split,
  { rintro ⟨⟨ax, xb⟩, h⟩,
    exact eq.symm (classical.by_contradiction
      (λ ne, h (lt_of_le_of_ne ax ne) xb)) },
  { rintro rfl, exact ⟨⟨le_refl _, h⟩, (lt_irrefl x).elim⟩ }
end

lemma Icc_diff_Ico_eq_singleton (h : a ≤ b) : Icc a b \ Ico a b = {b} :=
set.ext $ λ x, begin
  simp, split,
  { rintro ⟨⟨ax, xb⟩, h⟩,
    exact classical.by_contradiction
      (λ ne, h ax (lt_of_le_of_ne xb ne)) },
  { rintro rfl, exact ⟨⟨h, le_refl _⟩, λ _, lt_irrefl x⟩ }
end

end partial_order

section linear_order
variables {α : Type*} [linear_order α] {a a₁ a₂ b b₁ b₂ : α}

lemma Ioo_eq_empty_iff [densely_ordered α] : Ioo a b = ∅ ↔ b ≤ a :=
⟨λ eq, le_of_not_lt $ λ h,
  let ⟨x, h₁, h₂⟩ := dense h in
  eq_empty_iff_forall_not_mem.1 eq x ⟨h₁, h₂⟩,
Ioo_eq_empty⟩

lemma Ico_eq_empty_iff : Ico a b = ∅ ↔ b ≤ a :=
⟨λ eq, le_of_not_lt $ λ h, eq_empty_iff_forall_not_mem.1 eq a ⟨le_refl _, h⟩,
 Ico_eq_empty⟩

lemma Icc_eq_empty_iff : Icc a b = ∅ ↔ b < a :=
⟨λ eq, lt_of_not_ge $ λ h, eq_empty_iff_forall_not_mem.1 eq a ⟨le_refl _, h⟩,
 Icc_eq_empty⟩

lemma Ico_subset_Ico_iff (h₁ : a₁ < b₁) :
  Ico a₁ b₁ ⊆ Ico a₂ b₂ ↔ a₂ ≤ a₁ ∧ b₁ ≤ b₂ :=
⟨λ h,
  have a₂ ≤ a₁ ∧ a₁ < b₂ := h ⟨le_refl _, h₁⟩,
  ⟨this.1, le_of_not_lt $ λ h', lt_irrefl b₂ (h ⟨le_of_lt this.2, h'⟩).2⟩,
 λ ⟨h₁, h₂⟩, Ico_subset_Ico h₁ h₂⟩

lemma Ioo_subset_Ioo_iff [densely_ordered α] (h₁ : a₁ < b₁) :
  Ioo a₁ b₁ ⊆ Ioo a₂ b₂ ↔ a₂ ≤ a₁ ∧ b₁ ≤ b₂ :=
⟨λ h, begin
  rcases dense h₁ with ⟨x, xa, xb⟩,
  split; refine le_of_not_lt (λ h', _),
  { have ab := lt_trans (h ⟨xa, xb⟩).1 xb,
    exact lt_irrefl _ (h ⟨h', ab⟩).1 },
  { have ab := lt_trans xa (h ⟨xa, xb⟩).2,
    exact lt_irrefl _ (h ⟨ab, h'⟩).2 }
end, λ ⟨h₁, h₂⟩, Ioo_subset_Ioo h₁ h₂⟩

lemma Ico_eq_Ico_iff (h : a₁ < b₁ ∨ a₂ < b₂) : Ico a₁ b₁ = Ico a₂ b₂ ↔ a₁ = a₂ ∧ b₁ = b₂ :=
⟨λ e, begin
  simp [subset.antisymm_iff] at e, simp [le_antisymm_iff],
  cases h; simp [Ico_subset_Ico_iff h] at e;
    [ rcases e with ⟨⟨h₁, h₂⟩, e'⟩, rcases e with ⟨e', ⟨h₁, h₂⟩⟩ ];
    have := (Ico_subset_Ico_iff (lt_of_le_of_lt h₁ $ lt_of_lt_of_le h h₂)).1 e';
    tauto
end, λ ⟨h₁, h₂⟩, by rw [h₁, h₂]⟩

end linear_order

section decidable_linear_order
variables {α : Type*} [decidable_linear_order α] {a a₁ a₂ b b₁ b₂ : α}

@[simp] lemma Ico_diff_Iio {a b c : α} : Ico a b \ Iio c = Ico (max a c) b :=
set.ext $ by simp [Ico, Iio, iff_def, max_le_iff] {contextual:=tt}

@[simp] lemma Ico_inter_Iio {a b c : α} : Ico a b ∩ Iio c = Ico a (min b c) :=
set.ext $ by simp [Ico, Iio, iff_def, lt_min_iff] {contextual:=tt}

lemma Ioo_inter_Ioo {a b c d : α} : Ioo a b ∩ Ioo c d = Ioo (max a c) (min b d) :=
set.ext $ by simp [iff_def, Ioo, lt_min_iff, max_lt_iff] {contextual := tt}

end decidable_linear_order

end set