lean4-htt/src/Init/Data/List/MinMax.lean
Paul Reichert 1448493489
feat: improvements to Min/Max-related classes (#10024)
This PR adds useful declarations to the `LawfulOrderMin/Max` and
`LawfulOrderLeftLeaningMin/Max` API. In particular, it introduces
`.leftLeaningOfLE` factories for `Min` and `Max`. It also renames
`LawfulOrderMin/Max.of_le` to .of_le_min_iff` and `.of_max_le_iff` and
introduces a second variant with different arguments.
2025-08-22 07:08:00 +00:00

301 lines
10 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2014 Parikshit Khanna. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Mario Carneiro
-/
module
prelude
public import Init.Data.List.Lemmas
public import Init.Data.List.Pairwise
public import Init.Data.Order.Factories
public import Init.Data.Subtype.Order
import Init.Data.Order.Lemmas
public section
open Std
/-!
# Lemmas about `List.min?` and `List.max?.
-/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
open Nat
/-! ## Minima and maxima -/
/-! ### min? -/
@[simp] theorem min?_nil [Min α] : ([] : List α).min? = none := rfl
-- We don't put `@[simp]` on `min?_cons'`,
-- because the definition in terms of `foldl` is not useful for proofs.
theorem min?_cons' [Min α] {xs : List α} : (x :: xs).min? = some (foldl min x xs) := rfl
@[simp] theorem min?_cons [Min α] [Std.Associative (min : ααα)] {xs : List α} :
(x :: xs).min? = some (xs.min?.elim x (min x)) := by
cases xs <;> simp [min?_cons', foldl_assoc]
@[simp] theorem min?_eq_none_iff {xs : List α} [Min α] : xs.min? = none ↔ xs = [] := by
cases xs <;> simp [min?]
theorem isSome_min?_of_mem {l : List α} [Min α] {a : α} (h : a ∈ l) :
l.min?.isSome := by
cases l <;> simp_all [min?_cons']
theorem min?_eq_head? {α : Type u} [Min α] {l : List α}
(h : l.Pairwise (fun a b => min a b = a)) : l.min? = l.head? := by
cases l with
| nil => rfl
| cons x l =>
rw [head?_cons, min?_cons', Option.some.injEq]
induction l generalizing x with
| nil => simp
| cons y l ih =>
have hx : min x y = x := rel_of_pairwise_cons h mem_cons_self
rw [foldl_cons, ih _ (hx.symm ▸ h.sublist (by simp)), hx]
theorem min?_mem [Min α] [MinEqOr α] :
{xs : List α} → xs.min? = some a → a ∈ xs := by
intro xs
match xs with
| nil => simp
| x :: xs =>
simp only [min?_cons', Option.some.injEq, mem_cons]
intro eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons y xs ind =>
simp at eq
have p := ind _ eq
cases p with
| inl p =>
cases MinEqOr.min_eq_or x y with | _ q => simp [p, q]
| inr p => simp [p, mem_cons]
theorem le_min?_iff [Min α] [LE α] [LawfulOrderInf α] :
{xs : List α} → xs.min? = some a → ∀ {x}, x ≤ a ↔ ∀ b, b ∈ xs → x ≤ b
| nil => by simp
| cons x xs => by
rw [min?]
intro eq y
simp only [Option.some.injEq] at eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons z xs ih =>
simp at eq
simp [ih _ eq, le_min_iff, and_assoc]
theorem min?_eq_some_iff [Min α] [LE α] {xs : List α} [IsLinearOrder α] [LawfulOrderMin α] :
xs.min? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
refine ⟨fun h => ⟨min?_mem h, (le_min?_iff h).1 (le_refl _)⟩, ?_⟩
intro ⟨h₁, h₂⟩
cases xs with
| nil => simp at h₁
| cons x xs =>
rw [List.min?]
exact congrArg some <| le_antisymm
((le_min?_iff (xs := x :: xs) rfl).1 (le_refl _) _ h₁)
(h₂ _ (min?_mem (xs := x :: xs) rfl))
private theorem min?_attach [Min α] [MinEqOr α] {xs : List α} :
xs.attach.min? = (xs.min?.pmap (fun m hm => ⟨m, min?_mem hm⟩) (fun _ => id)) := by
cases xs with
| nil => simp
| cons x xs =>
simp only [min?, attach_cons, Option.some.injEq, Option.pmap_some]
rw [foldl_map]
simp only [Subtype.ext_iff]
rw [← foldl_attach (l := xs)]
apply Eq.trans (foldl_hom (f := Subtype.val) ?_).symm
· rfl
· intros; rfl
theorem min?_eq_min?_attach [Min α] [MinEqOr α] {xs : List α} :
xs.min? = (xs.attach.min?.map Subtype.val) := by
simp [min?_attach, Option.map_pmap]
theorem min?_eq_some_iff_subtype [Min α] [LE α] {xs : List α}
[MinEqOr α] [IsLinearOrder (Subtype (· ∈ xs))] [LawfulOrderMin (Subtype (· ∈ xs))] :
xs.min? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
have := fun a => min?_eq_some_iff (xs := xs.attach) (a := a)
rw [min?_eq_min?_attach]
simp [min?_eq_some_iff]
constructor
· rintro ⟨ha, h⟩
exact ⟨ha, h⟩
· rintro ⟨ha, h⟩
exact ⟨ha, h⟩
theorem min?_replicate [Min α] [Std.IdempotentOp (min : ααα)] {n : Nat} {a : α} :
(replicate n a).min? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, min?_cons', Std.IdempotentOp.idempotent]
@[simp] theorem min?_replicate_of_pos [Min α] [MinEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).min? = some a := by
simp [min?_replicate, Nat.ne_of_gt h]
/--
This lemma is also applicable given the following instances:
```
[LE α] [Min α] [IsLinearOrder α] [LawfulOrderMin α]
```
-/
theorem foldl_min [Min α] [Std.IdempotentOp (min : ααα)] [Std.Associative (min : ααα)]
{l : List α} {a : α} : l.foldl (init := a) min = min a (l.min?.getD a) := by
cases l <;> simp [min?, foldl_assoc, Std.IdempotentOp.idempotent]
/-! ### max? -/
@[simp] theorem max?_nil [Max α] : ([] : List α).max? = none := rfl
-- We don't put `@[simp]` on `max?_cons'`,
-- because the definition in terms of `foldl` is not useful for proofs.
theorem max?_cons' [Max α] {xs : List α} : (x :: xs).max? = some (foldl max x xs) := rfl
@[simp] theorem max?_cons [Max α] [Std.Associative (max : ααα)] {xs : List α} :
(x :: xs).max? = some (xs.max?.elim x (max x)) := by
cases xs <;> simp [max?_cons', foldl_assoc]
@[simp] theorem max?_eq_none_iff {xs : List α} [Max α] : xs.max? = none ↔ xs = [] := by
cases xs <;> simp [max?]
theorem isSome_max?_of_mem {l : List α} [Max α] {a : α} (h : a ∈ l) :
l.max?.isSome := by
cases l <;> simp_all [max?_cons']
theorem max?_eq_head? {α : Type u} [Max α] {l : List α}
(h : l.Pairwise (fun a b => max a b = a)) : l.max? = l.head? := by
cases l with
| nil => rfl
| cons x l =>
rw [head?_cons, max?_cons', Option.some.injEq]
induction l generalizing x with
| nil => simp
| cons y l ih =>
have hx : max x y = x := rel_of_pairwise_cons h mem_cons_self
rw [foldl_cons, ih _ (hx.symm ▸ h.sublist (by simp)), hx]
theorem max?_mem [Max α] [MaxEqOr α] :
{xs : List α} → xs.max? = some a → a ∈ xs := by
intro xs
match xs with
| nil => simp
| x :: xs =>
simp only [max?_cons', Option.some.injEq, mem_cons]
intro eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons y xs ind =>
simp at eq
have p := ind _ eq
cases p with
| inl p =>
cases MaxEqOr.max_eq_or x y with | _ q => simp [p, q]
| inr p => simp [p, mem_cons]
theorem max?_le_iff [Max α] [LE α] [LawfulOrderSup α] :
{xs : List α} → xs.max? = some a → ∀ {x}, a ≤ x ↔ ∀ b, b ∈ xs → b ≤ x
| nil => by simp
| cons x xs => by
rw [max?]
intro eq y
simp only [Option.some.injEq] at eq
induction xs generalizing x with
| nil =>
simp at eq
simp [eq]
| cons z xs ih =>
simp at eq
simp [ih _ eq, max_le_iff, and_assoc]
theorem max?_eq_some_iff [Max α] [LE α] {xs : List α} [IsLinearOrder (α)]
[LawfulOrderMax α] : xs.max? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → b ≤ a := by
refine ⟨fun h => ⟨max?_mem h, (max?_le_iff h).1 (le_refl _)⟩, ?_⟩
intro ⟨h₁, h₂⟩
cases xs with
| nil => simp at h₁
| cons x xs =>
rw [List.max?]
exact congrArg some <| le_antisymm
(h₂ _ (max?_mem (xs := x :: xs) rfl))
((max?_le_iff (xs := x :: xs) rfl).1 (le_refl _) _ h₁)
private theorem max?_attach [Max α] [MaxEqOr α] {xs : List α} :
xs.attach.max? = (xs.max?.pmap (fun m hm => ⟨m, max?_mem hm⟩) (fun _ => id)) := by
cases xs with
| nil => simp
| cons x xs =>
simp only [max?, attach_cons, Option.some.injEq, Option.pmap_some]
rw [foldl_map]
simp only [Subtype.ext_iff]
rw [← foldl_attach (l := xs)]
apply Eq.trans (foldl_hom (f := Subtype.val) ?_).symm
· rfl
· intros; rfl
theorem max?_eq_max?_attach [Max α] [MaxEqOr α] {xs : List α} :
xs.max? = (xs.attach.max?.map Subtype.val) := by
simp [max?_attach, Option.map_pmap]
theorem max?_eq_some_iff_subtype [Max α] [LE α] {xs : List α}
[MaxEqOr α] [IsLinearOrder (Subtype (· ∈ xs))]
[LawfulOrderMax (Subtype (· ∈ xs))] :
xs.max? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → b ≤ a := by
have := fun a => max?_eq_some_iff (xs := xs.attach) (a := a)
rw [max?_eq_max?_attach]
simp [max?_eq_some_iff]
constructor
· rintro ⟨ha, h⟩
exact ⟨ha, h⟩
· rintro ⟨ha, h⟩
exact ⟨ha, h⟩
@[deprecated max?_eq_some_iff (since := "2025-08-01")]
theorem max?_eq_some_iff_legacy [Max α] [LE α] [anti : Std.Antisymm (· ≤ · : αα → Prop)]
(le_refl : ∀ a : α, a ≤ a)
(max_eq_or : ∀ a b : α, max a b = a max a b = b)
(max_le_iff : ∀ a b c : α, max b c ≤ a ↔ b ≤ a ∧ c ≤ a) {xs : List α} :
xs.max? = some a ↔ a ∈ xs ∧ ∀ b ∈ xs, b ≤ a := by
haveI : MaxEqOr α := ⟨max_eq_or⟩
haveI : LawfulOrderMax α := .of_max_le_iff (fun _ _ _ => max_le_iff _ _ _) max_eq_or
haveI : Refl (α := α) (· ≤ ·) := ⟨le_refl⟩
haveI : IsLinearOrder α := .of_refl_of_antisymm_of_lawfulOrderMax
apply max?_eq_some_iff
theorem max?_replicate [Max α] [Std.IdempotentOp (max : ααα)] {n : Nat} {a : α} :
(replicate n a).max? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, max?_cons', Std.IdempotentOp.idempotent]
@[simp] theorem max?_replicate_of_pos [Max α] [MaxEqOr α] {n : Nat} {a : α} (h : 0 < n) :
(replicate n a).max? = some a := by
simp [max?_replicate, Nat.ne_of_gt h]
/--
This lemma is also applicable given the following instances:
```
[LE α] [Min α] [IsLinearOrder α] [LawfulOrderMax α]
```
-/
theorem foldl_max [Max α] [Std.IdempotentOp (max : ααα)] [Std.Associative (max : ααα)]
{l : List α} {a : α} : l.foldl (init := a) max = max a (l.max?.getD a) := by
cases l <;> simp [max?, foldl_assoc, Std.IdempotentOp.idempotent]
end List