This PR deprecates `extract_eq_drop_take` in favor of the more correct name `extract_eq_take_drop`, so that we'll be able to use the old name for a lemma `xs.extract start stop = (xs.take stop).drop start`. Until the deprecation deadline has passed, this new lemma will be called `extract_eq_drop_take'`.
403 lines
14 KiB
Text
403 lines
14 KiB
Text
/-
|
||
Copyright (c) 2026 Lean FRO, LLC. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Paul Reichert
|
||
-/
|
||
module
|
||
|
||
prelude
|
||
public import Init.Data.Array.Lemmas
|
||
import Init.Data.List.MinMax
|
||
public import Init.Data.Order.Classes
|
||
import Init.Data.Array.Bootstrap
|
||
import Init.Data.Array.DecidableEq
|
||
import Init.Data.List.TakeDrop
|
||
import Init.Data.Order.Lemmas
|
||
|
||
namespace Array
|
||
|
||
/-! ## Minima and maxima -/
|
||
|
||
/-! ### min -/
|
||
|
||
/--
|
||
Returns the smallest element of a non-empty array.
|
||
|
||
Examples:
|
||
* `#[4].min (by decide) = 4`
|
||
* `#[1, 4, 2, 10, 6].min (by decide) = 1`
|
||
-/
|
||
public protected def min [Min α] (arr : Array α) (h : arr ≠ #[]) : α :=
|
||
haveI : arr.size > 0 := by simp [Array.size_pos_iff, h]
|
||
arr.foldl min arr[0] (start := 1)
|
||
|
||
/-! ### min? -/
|
||
|
||
/--
|
||
Returns the smallest element of the array if it is not empty, or `none` if it is empty.
|
||
|
||
Examples:
|
||
* `#[].min? = none`
|
||
* `#[4].min? = some 4`
|
||
* `#[1, 4, 2, 10, 6].min? = some 1`
|
||
-/
|
||
public protected def min? [Min α] (arr : Array α) : Option α :=
|
||
if h : arr ≠ #[] then
|
||
some (arr.min h)
|
||
else
|
||
none
|
||
|
||
/-! ### max -/
|
||
|
||
/--
|
||
Returns the largest element of a non-empty array.
|
||
|
||
Examples:
|
||
* `#[4].max (by decide) = 4`
|
||
* `#[1, 4, 2, 10, 6].max (by decide) = 10`
|
||
-/
|
||
public protected def max [Max α] (arr : Array α) (h : arr ≠ #[]) : α :=
|
||
haveI : arr.size > 0 := by simp [Array.size_pos_iff, h]
|
||
arr.foldl max arr[0] (start := 1)
|
||
|
||
/-! ### max? -/
|
||
|
||
/--
|
||
Returns the largest element of the array if it is not empty, or `none` if it is empty.
|
||
|
||
Examples:
|
||
* `#[].max? = none`
|
||
* `#[4].max? = some 4`
|
||
* `#[1, 4, 2, 10, 6].max? = some 10`
|
||
-/
|
||
public protected def max? [Max α] (arr : Array α) : Option α :=
|
||
if h : arr ≠ #[] then
|
||
some (arr.max h)
|
||
else
|
||
none
|
||
|
||
/-! ### Compatibility with `List` -/
|
||
|
||
@[simp, grind =]
|
||
public theorem _root_.List.min_toArray [Min α] {l : List α} {h} :
|
||
l.toArray.min h = l.min (by simpa [List.ne_nil_iff_length_pos] using h) := by
|
||
let h' : l ≠ [] := by simpa [List.ne_nil_iff_length_pos] using h
|
||
change l.toArray.min h = l.min h'
|
||
rw [Array.min]
|
||
· induction l
|
||
· contradiction
|
||
· rename_i x xs
|
||
simp only [List.getElem_toArray, List.getElem_cons_zero, List.size_toArray, List.length_cons]
|
||
rw [List.toArray_cons, foldl_eq_foldl_extract]
|
||
rw [← Array.foldl_toList, Array.toList_extract, List.extract_eq_take_drop]
|
||
simp [List.min]
|
||
|
||
public theorem _root_.List.min_eq_min_toArray [Min α] {l : List α} {h} :
|
||
l.min h = l.toArray.min (by simpa [List.ne_nil_iff_length_pos] using h) := by
|
||
simp
|
||
|
||
@[simp, grind =]
|
||
public theorem min_toList [Min α] {xs : Array α} {h} :
|
||
xs.toList.min h = xs.min (by simpa [List.ne_nil_iff_length_pos] using h) := by
|
||
cases xs; simp
|
||
|
||
public theorem min_eq_min_toList [Min α] {xs : Array α} {h} :
|
||
xs.min h = xs.toList.min (by simpa [List.ne_nil_iff_length_pos] using h) := by
|
||
simp
|
||
|
||
@[simp, grind =]
|
||
public theorem _root_.List.min?_toArray [Min α] {l : List α} :
|
||
l.toArray.min? = l.min? := by
|
||
rw [Array.min?]
|
||
split
|
||
· simp [List.min_toArray, List.min_eq_get_min?, - List.get_min?]
|
||
· simp_all
|
||
|
||
@[simp, grind =]
|
||
public theorem min?_toList [Min α] {xs : Array α} :
|
||
xs.toList.min? = xs.min? := by
|
||
cases xs; simp
|
||
|
||
@[simp, grind =]
|
||
public theorem _root_.List.max_toArray [Max α] {l : List α} {h} :
|
||
l.toArray.max h = l.max (by simpa [List.ne_nil_iff_length_pos] using h) := by
|
||
let h' : l ≠ [] := by simpa [List.ne_nil_iff_length_pos] using h
|
||
change l.toArray.max h = l.max h'
|
||
rw [Array.max]
|
||
· induction l
|
||
· contradiction
|
||
· rename_i x xs
|
||
simp only [List.getElem_toArray, List.getElem_cons_zero, List.size_toArray, List.length_cons]
|
||
rw [List.toArray_cons, foldl_eq_foldl_extract]
|
||
rw [← Array.foldl_toList, Array.toList_extract, List.extract_eq_take_drop]
|
||
simp [List.max]
|
||
|
||
public theorem _root_.List.max_eq_max_toArray [Max α] {l : List α} {h} :
|
||
l.max h = l.toArray.max (by simpa [List.ne_nil_iff_length_pos] using h) := by
|
||
simp
|
||
|
||
@[simp, grind =]
|
||
public theorem max_toList [Max α] {xs : Array α} {h} :
|
||
xs.toList.max h = xs.max (by simpa [List.ne_nil_iff_length_pos] using h) := by
|
||
cases xs; simp
|
||
|
||
public theorem max_eq_max_toList [Max α] {xs : Array α} {h} :
|
||
xs.max h = xs.toList.max (by simpa [List.ne_nil_iff_length_pos] using h) := by
|
||
simp
|
||
|
||
@[simp, grind =]
|
||
public theorem _root_.List.max?_toArray [Max α] {l : List α} :
|
||
l.toArray.max? = l.max? := by
|
||
rw [Array.max?]
|
||
split
|
||
· simp [List.max_toArray, List.max_eq_get_max?, - List.get_max?]
|
||
· simp_all
|
||
|
||
@[simp, grind =]
|
||
public theorem max?_toList [Max α] {xs : Array α} :
|
||
xs.toList.max? = xs.max? := by
|
||
cases xs; simp
|
||
|
||
/-! ### Lemmas about `min?` -/
|
||
|
||
@[simp, grind =]
|
||
public theorem min?_empty [Min α] : (#[] : Array α).min? = none :=
|
||
(rfl)
|
||
|
||
@[simp, grind =]
|
||
public theorem min?_singleton [Min α] {x : α} : #[x].min? = some x :=
|
||
(rfl)
|
||
|
||
-- We don't put `@[simp]` on `min?_singleton_append'`,
|
||
-- because the definition in terms of `foldl` is not useful for proofs.
|
||
public theorem min?_singleton_append' [Min α] {xs : Array α} :
|
||
(#[x] ++ xs).min? = some (xs.foldl min x) := by
|
||
simp [← min?_toList, toList_append, List.min?]
|
||
|
||
@[simp]
|
||
public theorem min?_singleton_append [Min α] [Std.Associative (min : α → α → α)] {xs : Array α} :
|
||
(#[x] ++ xs).min? = some (xs.min?.elim x (min x)) := by
|
||
simp [← min?_toList, toList_append, List.min?_cons]
|
||
|
||
@[simp, grind =]
|
||
public theorem min?_eq_none_iff {xs : Array α} [Min α] : xs.min? = none ↔ xs = #[] := by
|
||
rcases xs with ⟨l⟩
|
||
simp
|
||
|
||
@[simp, grind =]
|
||
public theorem isSome_min?_iff {xs : Array α} [Min α] : xs.min?.isSome ↔ xs ≠ #[] := by
|
||
rcases xs with ⟨l⟩
|
||
simp
|
||
|
||
@[grind .]
|
||
public theorem isSome_min?_of_mem {xs : Array α} [Min α] {a : α} (h : a ∈ xs) :
|
||
xs.min?.isSome := by
|
||
rw [← min?_toList]
|
||
apply List.isSome_min?_of_mem (a := a)
|
||
simpa
|
||
|
||
public theorem isSome_min?_of_ne_empty [Min α] (xs : Array α) (h : xs ≠ #[]) : xs.min?.isSome := by
|
||
rw [← min?_toList]
|
||
apply List.isSome_min?_of_ne_nil
|
||
simpa
|
||
|
||
public theorem min?_mem [Min α] [Std.MinEqOr α] (xs : Array α) (h : xs.min? = some a) : a ∈ xs := by
|
||
rw [← min?_toList] at h
|
||
simpa using List.min?_mem h
|
||
|
||
public theorem le_min?_iff [Min α] [LE α] [Std.LawfulOrderInf α] :
|
||
{xs : Array α} → xs.min? = some a → ∀ {x}, x ≤ a ↔ ∀ b, b ∈ xs → x ≤ b := by
|
||
intro xs h x
|
||
simp only [← min?_toList] at h
|
||
simpa using List.le_min?_iff h
|
||
|
||
public theorem min?_eq_some_iff [Min α] [LE α] {xs : Array α} [Std.IsLinearOrder α]
|
||
[Std.LawfulOrderMin α] : xs.min? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
|
||
rcases xs with ⟨l⟩
|
||
simpa using List.min?_eq_some_iff
|
||
|
||
public theorem min?_replicate [Min α] [Std.IdempotentOp (min : α → α → α)] {n : Nat} {a : α} :
|
||
(replicate n a).min? = if n = 0 then none else some a := by
|
||
rw [← List.toArray_replicate, List.min?_toArray, List.min?_replicate]
|
||
|
||
@[simp, grind =]
|
||
public theorem min?_replicate_of_pos [Min α] [Std.MinEqOr α] {n : Nat} {a : α} (h : 0 < n) :
|
||
(replicate n a).min? = some a := by
|
||
simp [min?_replicate, Nat.ne_of_gt h]
|
||
|
||
public theorem foldl_min [Min α] [Std.IdempotentOp (min : α → α → α)]
|
||
[Std.Associative (min : α → α → α)] {xs : Array α} {a : α} :
|
||
xs.foldl (init := a) min = min a (xs.min?.getD a) := by
|
||
rcases xs with ⟨l⟩
|
||
simp [List.foldl_min]
|
||
|
||
/-! ### Lemmas about `max?` -/
|
||
|
||
@[simp, grind =]
|
||
public theorem max?_empty [Max α] : (#[] : Array α).max? = none :=
|
||
(rfl)
|
||
|
||
@[simp, grind =]
|
||
public theorem max?_singleton [Max α] {x : α} : #[x].max? = some x :=
|
||
(rfl)
|
||
|
||
-- We don't put `@[simp]` on `max?_singleton_append'`,
|
||
-- because the definition in terms of `foldl` is not useful for proofs.
|
||
public theorem max?_singleton_append' [Max α] {xs : Array α} : (#[x] ++ xs).max? = some (xs.foldl max x) := by
|
||
simp [← max?_toList, toList_append, List.max?]
|
||
|
||
@[simp]
|
||
public theorem max?_singleton_append [Max α] [Std.Associative (max : α → α → α)] {xs : Array α} :
|
||
(#[x] ++ xs).max? = some (xs.max?.elim x (max x)) := by
|
||
simp [← max?_toList, toList_append, List.max?_cons]
|
||
|
||
@[simp, grind =]
|
||
public theorem max?_eq_none_iff {xs : Array α} [Max α] : xs.max? = none ↔ xs = #[] := by
|
||
rcases xs with ⟨l⟩
|
||
simp
|
||
|
||
@[simp, grind =]
|
||
public theorem isSome_max?_iff {xs : Array α} [Max α] : xs.max?.isSome ↔ xs ≠ #[] := by
|
||
rcases xs with ⟨l⟩
|
||
simp
|
||
|
||
@[grind .]
|
||
public theorem isSome_max?_of_mem {xs : Array α} [Max α] {a : α} (h : a ∈ xs) :
|
||
xs.max?.isSome := by
|
||
rw [← max?_toList]
|
||
apply List.isSome_max?_of_mem (a := a)
|
||
simpa
|
||
|
||
public theorem isSome_max?_of_ne_empty [Max α] (xs : Array α) (h : xs ≠ #[]) : xs.max?.isSome := by
|
||
rw [← max?_toList]
|
||
apply List.isSome_max?_of_ne_nil
|
||
simpa
|
||
|
||
public theorem max?_mem [Max α] [Std.MaxEqOr α] (xs : Array α) (h : xs.max? = some a) : a ∈ xs := by
|
||
rw [← max?_toList] at h
|
||
simpa using List.max?_mem h
|
||
|
||
public theorem max?_le_iff [Max α] [LE α] [Std.LawfulOrderSup α] :
|
||
{xs : Array α} → xs.max? = some a → ∀ {x}, a ≤ x ↔ ∀ b, b ∈ xs → b ≤ x := by
|
||
intro xs h x
|
||
simp only [← max?_toList] at h
|
||
simpa using List.max?_le_iff h
|
||
|
||
public theorem max?_eq_some_iff [Max α] [LE α] {xs : Array α} [Std.IsLinearOrder α]
|
||
[Std.LawfulOrderMax α] : xs.max? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → b ≤ a := by
|
||
rcases xs with ⟨l⟩
|
||
simpa using List.max?_eq_some_iff
|
||
|
||
public theorem max?_replicate [Max α] [Std.IdempotentOp (max : α → α → α)] {n : Nat} {a : α} :
|
||
(replicate n a).max? = if n = 0 then none else some a := by
|
||
rw [← List.toArray_replicate, List.max?_toArray, List.max?_replicate]
|
||
|
||
@[simp, grind =]
|
||
public theorem max?_replicate_of_pos [Max α] [Std.MaxEqOr α] {n : Nat} {a : α} (h : 0 < n) :
|
||
(replicate n a).max? = some a := by
|
||
simp [max?_replicate, Nat.ne_of_gt h]
|
||
|
||
public theorem foldl_max [Max α] [Std.IdempotentOp (max : α → α → α)] [Std.Associative (max : α → α → α)]
|
||
{xs : Array α} {a : α} : xs.foldl (init := a) max = max a (xs.max?.getD a) := by
|
||
rcases xs with ⟨l⟩
|
||
simp [List.foldl_max]
|
||
|
||
/-! ### Lemmas about `min` -/
|
||
|
||
@[simp, grind =]
|
||
theorem min_singleton [Min α] {x : α} :
|
||
#[x].min (ne_empty_of_size_eq_add_one rfl) = x := by
|
||
(rfl)
|
||
|
||
public theorem min?_eq_some_min [Min α] : {xs : Array α} → (h : xs ≠ #[]) →
|
||
xs.min? = some (xs.min h)
|
||
| ⟨a::as⟩, _ => by simp [Array.min, Array.min?]
|
||
|
||
public theorem min_eq_get_min? [Min α] : (xs : Array α) → (h : xs ≠ #[]) →
|
||
xs.min h = xs.min?.get (xs.isSome_min?_of_ne_empty h)
|
||
| ⟨a::as⟩, _ => by simp [Array.min, Array.min?]
|
||
|
||
@[simp, grind =]
|
||
public theorem get_min? [Min α] {xs : Array α} {h : xs.min?.isSome} :
|
||
xs.min?.get h = xs.min (isSome_min?_iff.mp h) := by
|
||
simp [min?_eq_some_min (isSome_min?_iff.mp h)]
|
||
|
||
@[grind .]
|
||
public theorem min_mem [Min α] [Std.MinEqOr α] {xs : Array α} (h : xs ≠ #[]) : xs.min h ∈ xs :=
|
||
xs.min?_mem (min?_eq_some_min h)
|
||
|
||
@[grind .]
|
||
public theorem min_le_of_mem [Min α] [LE α] [Std.IsLinearOrder α] [Std.LawfulOrderMin α]
|
||
{xs : Array α} {a : α} (ha : a ∈ xs) :
|
||
xs.min (ne_empty_of_mem ha) ≤ a :=
|
||
(Array.min?_eq_some_iff.mp (min?_eq_some_min (ne_empty_of_mem ha))).right a ha
|
||
|
||
public protected theorem le_min_iff [Min α] [LE α] [Std.LawfulOrderInf α]
|
||
{xs : Array α} (h : xs ≠ #[]) : ∀ {x}, x ≤ xs.min h ↔ ∀ b, b ∈ xs → x ≤ b :=
|
||
le_min?_iff (min?_eq_some_min h)
|
||
|
||
public theorem min_eq_iff [Min α] [LE α] {xs : Array α} [Std.IsLinearOrder α] [Std.LawfulOrderMin α]
|
||
(h : xs ≠ #[]) : xs.min h = a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
|
||
simpa [min?_eq_some_min h] using (min?_eq_some_iff (xs := xs))
|
||
|
||
@[simp, grind =]
|
||
public theorem min_replicate [Min α] [Std.MinEqOr α] {n : Nat} {a : α} (h : (replicate n a) ≠ #[]) :
|
||
(replicate n a).min h = a := by
|
||
have n_pos : 0 < n := by simpa [Nat.ne_zero_iff_zero_lt] using h
|
||
simpa [min?_eq_some_min h] using (min?_replicate_of_pos (a := a) n_pos)
|
||
|
||
public theorem foldl_min_eq_min [Min α] [Std.IdempotentOp (min : α → α → α)]
|
||
[Std.Associative (min : α → α → α)] {xs : Array α} (h : xs ≠ #[]) {a : α} :
|
||
xs.foldl min a = min a (xs.min h) := by
|
||
simpa [min?_eq_some_min h] using foldl_min (xs := xs)
|
||
|
||
/-! ### Lemmas about `max` -/
|
||
|
||
@[simp, grind =]
|
||
theorem max_singleton [Max α] {x : α} :
|
||
#[x].max (ne_empty_of_size_eq_add_one rfl) = x := by
|
||
(rfl)
|
||
|
||
public theorem max?_eq_some_max [Max α] : {xs : Array α} → (h : xs ≠ #[]) →
|
||
xs.max? = some (xs.max h)
|
||
| ⟨a::as⟩, _ => by simp [Array.max, Array.max?]
|
||
|
||
public theorem max_eq_get_max? [Max α] : (xs : Array α) → (h : xs ≠ #[]) →
|
||
xs.max h = xs.max?.get (xs.isSome_max?_of_ne_empty h)
|
||
| ⟨a::as⟩, _ => by simp [Array.max, Array.max?]
|
||
|
||
@[simp, grind =]
|
||
public theorem get_max? [Max α] {xs : Array α} {h : xs.max?.isSome} :
|
||
xs.max?.get h = xs.max (isSome_max?_iff.mp h) := by
|
||
simp [max?_eq_some_max (isSome_max?_iff.mp h)]
|
||
|
||
@[grind .]
|
||
public theorem max_mem [Max α] [Std.MaxEqOr α] {xs : Array α} (h : xs ≠ #[]) : xs.max h ∈ xs :=
|
||
xs.max?_mem (max?_eq_some_max h)
|
||
|
||
public protected theorem max_le_iff [Max α] [LE α] [Std.LawfulOrderSup α]
|
||
{xs : Array α} (h : xs ≠ #[]) : ∀ {x}, xs.max h ≤ x ↔ ∀ b, b ∈ xs → b ≤ x :=
|
||
max?_le_iff (max?_eq_some_max h)
|
||
|
||
public theorem max_eq_iff [Max α] [LE α] {xs : Array α} [Std.IsLinearOrder α] [Std.LawfulOrderMax α]
|
||
(h : xs ≠ #[]) : xs.max h = a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → b ≤ a := by
|
||
simpa [max?_eq_some_max h] using (max?_eq_some_iff (xs := xs))
|
||
|
||
@[grind .]
|
||
public theorem le_max_of_mem [Max α] [LE α] [Std.IsLinearOrder α] [Std.LawfulOrderMax α]
|
||
{xs : Array α} {a : α} (ha : a ∈ xs) :
|
||
a ≤ xs.max (ne_empty_of_mem ha) :=
|
||
(Array.max?_eq_some_iff.mp (max?_eq_some_max (ne_empty_of_mem ha))).right a ha
|
||
|
||
@[simp, grind =]
|
||
public theorem max_replicate [Max α] [Std.MaxEqOr α] {n : Nat} {a : α} (h : (replicate n a) ≠ #[]) :
|
||
(replicate n a).max h = a := by
|
||
have n_pos : 0 < n := by simpa [Nat.ne_zero_iff_zero_lt] using h
|
||
simpa [max?_eq_some_max h] using (max?_replicate_of_pos (a := a) n_pos)
|
||
|
||
public theorem foldl_max_eq_max [Max α] [Std.IdempotentOp (max : α → α → α)]
|
||
[Std.Associative (max : α → α → α)] {xs : Array α} (h : xs ≠ #[]) {a : α} :
|
||
xs.foldl max a = max a (xs.max h) := by
|
||
simpa [max?_eq_some_max h] using foldl_max (xs := xs)
|
||
|
||
end Array
|