lean4-htt/src/Init/Data/List/Lemmas.lean
2024-06-21 06:40:30 +00:00

2563 lines
101 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
-/
prelude
import Init.Data.Bool
import Init.Data.Option.Lemmas
import Init.Data.List.BasicAux
import Init.Data.List.Control
import Init.PropLemmas
import Init.Control.Lawful.Basic
import Init.Hints
/-! # Theorems about `List` operations.
For each `List` operation, we would like theorems describing the following, when relevant:
* if it is a "convenience" function, a `@[simp]` lemma reducing it to more basic operations
(e.g. `List.partition_eq_filter_filter`), and otherwise:
* any special cases of equational lemmas that require additional hypotheses
* lemmas for special cases of the arguments (e.g. `List.map_id`)
* the length of the result `(f L).length`
* the `i`-th element, described via `(f L)[i]` and/or `(f L)[i]?` (these should typically be `@[simp]`)
* consequences for `f L` of the fact `x ∈ L` or `x ∉ L`
* conditions characterising `x ∈ f L` (often but not always `@[simp]`)
* injectivity statements, or congruence statements of the form `p L M → f L = f M`.
* conditions characterising the result, i.e. of the form `f L = M ↔ p M` for some predicate `p`,
along with special cases of `M` (e.g. `List.append_eq_nil : L ++ M = [] ↔ L = [] ∧ M = []`)
* negative characterisations are also useful, e.g. `List.cons_ne_nil`
* interactions with all previously described `List` operations where possible
(some of these should be `@[simp]`, particularly if the result can be described by a single operation)
* characterising `(∀ (i) (_ : i ∈ f L), P i)`, for some predicate `P`
Of course for any individual operation, not all of these will be relevant or helpful, so some judgement is required.
General principles for `simp` normal forms for `List` operations:
* Conversion operations (e.g. `toArray`, or `length`) should be moved inwards aggressively,
to make the conversion effective.
* Similarly, operation which work on elements should be moved inwards in preference to
"structural" operations on the list, e.g. we prefer to simplify
`List.map f (L ++ M) ~> (List.map f L) ++ (List.map f M)`,
`List.map f L.reverse ~> (List.map f L).reverse`, and
`List.map f (L.take n) ~> (List.map f L).take n`.
* Arithmetic operations are "light", so e.g. we prefer to simplify `drop i (drop j L)` to `drop (i + j) L`,
rather than the other way round.
* Function compositions are "light", so we prefer to simplify `(L.map f).map g` to `L.map (g ∘ f)`.
* We try to avoid non-linear left hand sides (i.e. with subexpressions appearing multiple times),
but this is only a weak preference.
* Generally, we prefer that the right hand side does not introduce duplication,
however generally duplication of higher order arguments (functions, predicates, etc) is allowed,
as we expect to be able to compute these once they reach ground terms.
-/
namespace List
open Nat
/-! ## Preliminaries -/
-- We may want to replace these `simp` attributes with explicit equational lemmas,
-- as we already have for all the non-monadic functions.
attribute [simp] mapA forA filterAuxM firstM anyM allM findM? findSomeM?
-- Previously `range.loop`, `mapM.loop`, `filterMapM.loop`, `forIn.loop`, `forIn'.loop`
-- had attribute `@[simp]`.
-- We don't currently provide simp lemmas,
-- as this is an internal implementation and they don't seem to be needed.
/-! ### cons -/
theorem cons_ne_nil (a : α) (l : List α) : a :: l ≠ [] := nofun
@[simp]
theorem cons_ne_self (a : α) (l : List α) : a :: l ≠ l := mt (congrArg length) (Nat.succ_ne_self _)
theorem head_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : h₁ = h₂ := (cons.inj H).1
theorem tail_eq_of_cons_eq (H : h₁ :: t₁ = h₂ :: t₂) : t₁ = t₂ := (cons.inj H).2
theorem cons_inj_right (a : α) {l l' : List α} : a :: l = a :: l' ↔ l = l' :=
⟨tail_eq_of_cons_eq, congrArg _⟩
@[deprecated (since := "2024-06-15")] abbrev cons_inj := @cons_inj_right
theorem cons_eq_cons {a b : α} {l l' : List α} : a :: l = b :: l' ↔ a = b ∧ l = l' :=
List.cons.injEq .. ▸ .rfl
theorem exists_cons_of_ne_nil : ∀ {l : List α}, l ≠ [] → ∃ b L, l = b :: L
| c :: l', _ => ⟨c, l', rfl⟩
/-! ### length -/
theorem eq_nil_of_length_eq_zero (_ : length l = 0) : l = [] := match l with | [] => rfl
theorem ne_nil_of_length_eq_add_one (_ : length l = n + 1) : l ≠ [] := fun _ => nomatch l
@[deprecated ne_nil_of_length_eq_add_one (since := "2024-06-16")]
abbrev ne_nil_of_length_eq_succ := @ne_nil_of_length_eq_add_one
@[simp] theorem length_eq_zero : length l = 0 ↔ l = [] :=
⟨eq_nil_of_length_eq_zero, fun h => h ▸ rfl⟩
theorem length_pos_of_mem {a : α} : ∀ {l : List α}, a ∈ l → 0 < length l
| _::_, _ => Nat.zero_lt_succ _
theorem exists_mem_of_length_pos : ∀ {l : List α}, 0 < length l → ∃ a, a ∈ l
| _::_, _ => ⟨_, .head ..⟩
theorem length_pos_iff_exists_mem {l : List α} : 0 < length l ↔ ∃ a, a ∈ l :=
⟨exists_mem_of_length_pos, fun ⟨_, h⟩ => length_pos_of_mem h⟩
theorem exists_cons_of_length_pos : ∀ {l : List α}, 0 < l.length → ∃ h t, l = h :: t
| _::_, _ => ⟨_, _, rfl⟩
theorem length_pos_iff_exists_cons :
∀ {l : List α}, 0 < l.length ↔ ∃ h t, l = h :: t :=
⟨exists_cons_of_length_pos, fun ⟨_, _, eq⟩ => eq ▸ Nat.succ_pos _⟩
theorem exists_cons_of_length_eq_add_one :
∀ {l : List α}, l.length = n + 1 → ∃ h t, l = h :: t
| _::_, _ => ⟨_, _, rfl⟩
theorem length_pos {l : List α} : 0 < length l ↔ l ≠ [] :=
Nat.pos_iff_ne_zero.trans (not_congr length_eq_zero)
theorem length_eq_one {l : List α} : length l = 1 ↔ ∃ a, l = [a] :=
⟨fun h => match l, h with | [_], _ => ⟨_, rfl⟩, fun ⟨_, h⟩ => by simp [h]⟩
/-! ### L[i] and L[i]? -/
@[simp] theorem get_cons_zero : get (a::l) (0 : Fin (l.length + 1)) = a := rfl
@[simp] theorem get_cons_succ {as : List α} {h : i + 1 < (a :: as).length} :
(a :: as).get ⟨i+1, h⟩ = as.get ⟨i, Nat.lt_of_succ_lt_succ h⟩ := rfl
@[simp] theorem get_cons_succ' {as : List α} {i : Fin as.length} :
(a :: as).get i.succ = as.get i := rfl
theorem get?_len_le : ∀ {l : List α} {n}, length l ≤ n → l.get? n = none
| [], _, _ => rfl
| _ :: l, _+1, h => get?_len_le (l := l) <| Nat.le_of_succ_le_succ h
theorem get?_eq_get : ∀ {l : List α} {n} (h : n < l.length), l.get? n = some (get l ⟨n, h⟩)
| _ :: _, 0, _ => rfl
| _ :: l, _+1, _ => get?_eq_get (l := l) _
theorem get?_eq_some : l.get? n = some a ↔ ∃ h, get l ⟨n, h⟩ = a :=
⟨fun e =>
have : n < length l := Nat.gt_of_not_le fun hn => by cases get?_len_le hn ▸ e
⟨this, by rwa [get?_eq_get this, Option.some.injEq] at e⟩,
fun ⟨h, e⟩ => e ▸ get?_eq_get _⟩
theorem get?_eq_none : l.get? n = none ↔ length l ≤ n :=
⟨fun e => Nat.ge_of_not_lt (fun h' => by cases e ▸ get?_eq_some.2 ⟨h', rfl⟩), get?_len_le⟩
@[simp] theorem get?_eq_getElem? (l : List α) (i : Nat) : l.get? i = l[i]? := by
simp only [getElem?]; split
· exact (get?_eq_get _)
· exact (get?_eq_none.2 <| Nat.not_lt.1 _)
@[simp] theorem get_eq_getElem (l : List α) (i : Fin l.length) : l.get i = l[i.1]'i.2 := rfl
@[simp] theorem getElem?_nil {n : Nat} : ([] : List α)[n]? = none := rfl
@[simp] theorem getElem?_cons_zero {l : List α} : (a::l)[0]? = some a := by
simp only [← get?_eq_getElem?]
rfl
@[simp] theorem getElem?_cons_succ {l : List α} : (a::l)[n+1]? = l[n]? := by
simp only [← get?_eq_getElem?]
rfl
theorem getElem?_len_le : ∀ {l : List α} {n}, length l ≤ n → l[n]? = none
| [], _, _ => rfl
| _ :: l, _+1, h => by
rw [getElem?_cons_succ, getElem?_len_le (l := l) <| Nat.le_of_succ_le_succ h]
@[simp] theorem getElem?_eq_getElem {l : List α} {n} (h : n < l.length) : l[n]? = some l[n] := by
simp only [← get?_eq_getElem?, get?_eq_get, h, get_eq_getElem]
theorem getElem?_eq_some {l : List α} : l[n]? = some a ↔ ∃ h : n < l.length, l[n] = a := by
simp only [← get?_eq_getElem?, get?_eq_some, get_eq_getElem]
@[simp] theorem getElem?_eq_none_iff : l[n]? = none ↔ length l ≤ n := by
simp only [← get?_eq_getElem?, get?_eq_none]
theorem getElem?_eq_none (h : length l ≤ n) : l[n]? = none := getElem?_eq_none_iff.mpr h
@[simp] theorem getElem!_nil [Inhabited α] {n : Nat} : ([] : List α)[n]! = default := rfl
@[simp] theorem getElem!_cons_zero [Inhabited α] {l : List α} : (a::l)[0]! = a := by
rw [getElem!_pos] <;> simp
@[simp] theorem getElem!_cons_succ [Inhabited α] {l : List α} : (a::l)[n+1]! = l[n]! := by
by_cases h : n < l.length
· rw [getElem!_pos, getElem!_pos] <;> simp_all [Nat.succ_lt_succ_iff]
· rw [getElem!_neg, getElem!_neg] <;> simp_all [Nat.succ_lt_succ_iff]
@[simp] theorem get_cons_cons_one : (a₁ :: a₂ :: as).get (1 : Fin (as.length + 2)) = a₂ := rfl
theorem get!_len_le [Inhabited α] : ∀ {l : List α} {n}, length l ≤ n → l.get! n = (default : α)
| [], _, _ => rfl
| _ :: l, _+1, h => get!_len_le (l := l) <| Nat.le_of_succ_le_succ h
theorem get?_zero (l : List α) : l.get? 0 = l.head? := by cases l <;> rfl
/--
If one has `l[i]` in an expression and `h : l = l'`,
`rw [h]` will give a "motive it not type correct" error, as it cannot rewrite the
implicit `i < l.length` to `i < l'.length` directly. The theorem `getElem_of_eq` can be used to make
such a rewrite, with `rw [getElem_of_eq h]`.
-/
theorem getElem_of_eq {l l' : List α} (h : l = l') {i : Nat} (w : i < l.length) :
l[i] = l'[i]'(h ▸ w) := by cases h; rfl
/--
If one has `l.get i` in an expression (with `i : Fin l.length`) and `h : l = l'`,
`rw [h]` will give a "motive it not type correct" error, as it cannot rewrite the
`i : Fin l.length` to `Fin l'.length` directly. The theorem `get_of_eq` can be used to make
such a rewrite, with `rw [get_of_eq h]`.
-/
theorem get_of_eq {l l' : List α} (h : l = l') (i : Fin l.length) :
get l i = get l' ⟨i, h ▸ i.2⟩ := by cases h; rfl
@[simp] theorem getElem_singleton (a : α) (h : i < 1) : [a][i] = a :=
match i, h with
| 0, _ => rfl
@[deprecated getElem_singleton (since := "2024-06-12")]
theorem get_singleton (a : α) (n : Fin 1) : get [a] n = a := by simp
theorem getElem_zero {l : List α} (h : 0 < l.length) : l[0] = l.head (length_pos.mp h) :=
match l, h with
| _ :: _, _ => rfl
theorem get_mk_zero : ∀ {l : List α} (h : 0 < l.length), l.get ⟨0, h⟩ = l.head (length_pos.mp h)
| _::_, _ => rfl
theorem getElem!_of_getElem? [Inhabited α] : ∀ {l : List α} {n : Nat}, l[n]? = some a → l[n]! = a
| _a::_, 0, _ => by
rw [getElem!_pos] <;> simp_all
| _::l, _+1, e => by
simp at e
simp_all [getElem!_of_getElem? (l := l) e]
theorem get!_of_get? [Inhabited α] : ∀ {l : List α} {n}, get? l n = some a → get! l n = a
| _a::_, 0, rfl => rfl
| _::l, _+1, e => get!_of_get? (l := l) e
@[simp] theorem get!_eq_getD [Inhabited α] : ∀ (l : List α) n, l.get! n = l.getD n default
| [], _ => rfl
| _a::_, 0 => rfl
| _a::l, n+1 => get!_eq_getD l n
@[ext] theorem ext_getElem? {l₁ l₂ : List α} (h : ∀ n : Nat, l₁[n]? = l₂[n]?) : l₁ = l₂ :=
ext_get? fun n => by simp_all
theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
(h : ∀ (n : Nat) (h₁ : n < l₁.length) (h₂ : n < l₂.length), l₁[n]'h₁ = l₂[n]'h₂) : l₁ = l₂ :=
ext_getElem? fun n =>
if h₁ : n < length l₁ then by
simp_all [getElem?_eq_getElem]
else by
have h₁ := Nat.le_of_not_lt h₁
rw [getElem?_len_le h₁, getElem?_len_le]; rwa [← hl]
theorem ext_get {l₁ l₂ : List α} (hl : length l₁ = length l₂)
(h : ∀ n h₁ h₂, get l₁ ⟨n, h₁⟩ = get l₂ ⟨n, h₂⟩) : l₁ = l₂ :=
ext_getElem hl (by simp_all)
@[simp] theorem getElem_concat_length : ∀ (l : List α) (a : α) (i) (_ : i = l.length) (w), (l ++ [a])[i]'w = a
| [], a, _, h, _ => by subst h; simp
| _ :: l, a, _, h, _ => by simp [getElem_concat_length, h]
theorem getElem?_concat_length (l : List α) (a : α) : (l ++ [a])[l.length]? = some a := by
simp
@[deprecated getElem?_concat_length (since := "2024-06-12")]
theorem get?_concat_length (l : List α) (a : α) : (l ++ [a]).get? l.length = some a := by simp
/-! ### mem -/
@[simp] theorem not_mem_nil (a : α) : ¬ a ∈ [] := nofun
@[simp] theorem mem_cons : a ∈ (b :: l) ↔ a = b a ∈ l :=
⟨fun h => by cases h <;> simp [Membership.mem, *],
fun | Or.inl rfl => by constructor | Or.inr h => by constructor; assumption⟩
theorem mem_cons_self (a : α) (l : List α) : a ∈ a :: l := .head ..
theorem mem_cons_of_mem (y : α) {a : α} {l : List α} : a ∈ l → a ∈ y :: l := .tail _
theorem exists_mem_of_ne_nil (l : List α) (h : l ≠ []) : ∃ x, x ∈ l :=
exists_mem_of_length_pos (length_pos.2 h)
theorem eq_nil_iff_forall_not_mem {l : List α} : l = [] ↔ ∀ a, a ∉ l := by
cases l <;> simp [-not_or]
theorem eq_of_mem_singleton : a ∈ [b] → a = b
| .head .. => rfl
@[simp 1100] theorem mem_singleton {a b : α} : a ∈ [b] ↔ a = b :=
⟨eq_of_mem_singleton, (by simp [·])⟩
theorem forall_mem_cons {p : α → Prop} {a : α} {l : List α} :
(∀ x, x ∈ a :: l → p x) ↔ p a ∧ ∀ x, x ∈ l → p x :=
⟨fun H => ⟨H _ (.head ..), fun _ h => H _ (.tail _ h)⟩,
fun ⟨H₁, H₂⟩ _ => fun | .head .. => H₁ | .tail _ h => H₂ _ h⟩
theorem exists_mem_nil (p : α → Prop) : ¬ (∃ x, ∃ _ : x ∈ @nil α, p x) := nofun
theorem forall_mem_nil (p : α → Prop) : ∀ (x) (_ : x ∈ @nil α), p x := nofun
theorem exists_mem_cons {p : α → Prop} {a : α} {l : List α} :
(∃ x, ∃ _ : x ∈ a :: l, p x) ↔ p a ∃ x, ∃ _ : x ∈ l, p x := by simp
theorem forall_mem_singleton {p : α → Prop} {a : α} : (∀ (x) (_ : x ∈ [a]), p x) ↔ p a := by
simp only [mem_singleton, forall_eq]
theorem mem_nil_iff (a : α) : a ∈ ([] : List α) ↔ False := by simp
theorem mem_singleton_self (a : α) : a ∈ [a] := mem_cons_self _ _
theorem mem_of_mem_cons_of_mem : ∀ {a b : α} {l : List α}, a ∈ b :: l → b ∈ l → a ∈ l
| _, _, _, .head .., h | _, _, _, .tail _ h, _ => h
theorem eq_or_ne_mem_of_mem {a b : α} {l : List α} (h' : a ∈ b :: l) : a = b (a ≠ b ∧ a ∈ l) :=
(Classical.em _).imp_right fun h => ⟨h, (mem_cons.1 h').resolve_left h⟩
theorem ne_nil_of_mem {a : α} {l : List α} (h : a ∈ l) : l ≠ [] := by cases h <;> nofun
theorem elem_iff [BEq α] [LawfulBEq α] {a : α} {as : List α} :
elem a as = true ↔ a ∈ as := ⟨mem_of_elem_eq_true, elem_eq_true_of_mem⟩
@[simp] theorem elem_eq_mem [BEq α] [LawfulBEq α] (a : α) (as : List α) :
elem a as = decide (a ∈ as) := by rw [Bool.eq_iff_iff, elem_iff, decide_eq_true_iff]
theorem mem_of_ne_of_mem {a y : α} {l : List α} (h₁ : a ≠ y) (h₂ : a ∈ y :: l) : a ∈ l :=
Or.elim (mem_cons.mp h₂) (absurd · h₁) (·)
theorem ne_of_not_mem_cons {a b : α} {l : List α} : a ∉ b::l → a ≠ b := mt (· ▸ .head _)
theorem not_mem_of_not_mem_cons {a b : α} {l : List α} : a ∉ b::l → a ∉ l := mt (.tail _)
theorem not_mem_cons_of_ne_of_not_mem {a y : α} {l : List α} : a ≠ y → a ∉ l → a ∉ y::l :=
mt ∘ mem_of_ne_of_mem
theorem ne_and_not_mem_of_not_mem_cons {a y : α} {l : List α} : a ∉ y::l → a ≠ y ∧ a ∉ l :=
fun p => ⟨ne_of_not_mem_cons p, not_mem_of_not_mem_cons p⟩
theorem getElem_of_mem : ∀ {a} {l : List α}, a ∈ l → ∃ (n : Nat) (h : n < l.length), l[n]'h = a
| _, _ :: _, .head .. => ⟨0, Nat.succ_pos _, rfl⟩
| _, _ :: _, .tail _ m => let ⟨n, h, e⟩ := getElem_of_mem m; ⟨n+1, Nat.succ_lt_succ h, e⟩
theorem get_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, get l n = a := by
obtain ⟨n, h, e⟩ := getElem_of_mem h
exact ⟨⟨n, h⟩, e⟩
theorem getElem_mem : ∀ (l : List α) n (h : n < l.length), l[n]'h ∈ l
| _ :: _, 0, _ => .head ..
| _ :: l, _+1, _ => .tail _ (getElem_mem l ..)
theorem get_mem : ∀ (l : List α) n h, get l ⟨n, h⟩ ∈ l
| _ :: _, 0, _ => .head ..
| _ :: l, _+1, _ => .tail _ (get_mem l ..)
theorem mem_iff_getElem {a} {l : List α} : a ∈ l ↔ ∃ (n : Nat) (h : n < l.length), l[n]'h = a :=
⟨getElem_of_mem, fun ⟨_, _, e⟩ => e ▸ getElem_mem ..⟩
theorem mem_iff_get {a} {l : List α} : a ∈ l ↔ ∃ n, get l n = a :=
⟨get_of_mem, fun ⟨_, e⟩ => e ▸ get_mem ..⟩
theorem getElem?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n : Nat, l[n]? = some a :=
let ⟨n, _, e⟩ := getElem_of_mem h; ⟨n, e ▸ getElem?_eq_getElem _⟩
theorem get?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ n, l.get? n = some a :=
let ⟨⟨n, _⟩, e⟩ := get_of_mem h; ⟨n, e ▸ get?_eq_get _⟩
theorem getElem?_mem {l : List α} {n : Nat} {a : α} (e : l[n]? = some a) : a ∈ l :=
let ⟨_, e⟩ := getElem?_eq_some.1 e; e ▸ getElem_mem ..
theorem get?_mem {l : List α} {n a} (e : l.get? n = some a) : a ∈ l :=
let ⟨_, e⟩ := get?_eq_some.1 e; e ▸ get_mem ..
theorem mem_iff_getElem? {a} {l : List α} : a ∈ l ↔ ∃ n : Nat, l[n]? = some a := by
simp [getElem?_eq_some, mem_iff_getElem]
theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a := by
simp [getElem?_eq_some, Fin.exists_iff, mem_iff_get]
@[simp] theorem decide_mem_cons [BEq α] [LawfulBEq α] {l : List α} :
decide (y ∈ a :: l) = (y == a || decide (y ∈ l)) := by
cases h : y == a <;> simp_all
/-! ### any / all -/
theorem any_eq {l : List α} : l.any p = decide (∃ x, x ∈ l ∧ p x) := by induction l <;> simp [*]
theorem all_eq {l : List α} : l.all p = decide (∀ x, x ∈ l → p x) := by induction l <;> simp [*]
@[simp] theorem any_eq_true {l : List α} : l.any p ↔ ∃ x, x ∈ l ∧ p x := by simp [any_eq]
@[simp] theorem all_eq_true {l : List α} : l.all p ↔ ∀ x, x ∈ l → p x := by simp [all_eq]
/-! ### set -/
-- As `List.set` is defined in `Init.Prelude`, we write the basic simplification lemmas here.
@[simp] theorem set_nil (n : Nat) (a : α) : [].set n a = [] := rfl
@[simp] theorem set_cons_zero (x : α) (xs : List α) (a : α) :
(x :: xs).set 0 a = a :: xs := rfl
@[simp] theorem set_cons_succ (x : α) (xs : List α) (n : Nat) (a : α) :
(x :: xs).set (n + 1) a = x :: xs.set n a := rfl
@[simp] theorem getElem_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length) :
(l.set i a)[i] = a :=
match l, i with
| [], _ => by
simp at h
contradiction
| _ :: _, 0 => by simp
| _ :: l, i + 1 => by simp [getElem_set_eq]
@[deprecated getElem_set_eq (since := "2024-06-12")]
theorem get_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length) :
(l.set i a).get ⟨i, h⟩ = a := by
simp
@[simp] theorem getElem?_set_eq {l : List α} {i : Nat} {a : α} (h : i < (l.set i a).length) :
(l.set i a)[i]? = some a := by
simp_all [getElem?_eq_some]
@[simp] theorem getElem_set_ne {l : List α} {i j : Nat} (h : i ≠ j) {a : α}
(hj : j < (l.set i a).length) :
(l.set i a)[j] = l[j]'(by simp at hj; exact hj) :=
match l, i, j with
| [], _, _ => by simp
| _ :: _, 0, 0 => by contradiction
| _ :: _, 0, _ + 1 => by simp
| _ :: _, _ + 1, 0 => by simp
| _ :: l, i + 1, j + 1 => by
have g : i ≠ j := h ∘ congrArg (· + 1)
simp [getElem_set_ne g]
@[deprecated getElem_set_ne (since := "2024-06-12")]
theorem get_set_ne {l : List α} {i j : Nat} (h : i ≠ j) {a : α}
(hj : j < (l.set i a).length) :
(l.set i a).get ⟨j, hj⟩ = l.get ⟨j, by simp at hj; exact hj⟩ := by
simp [h]
@[simp] theorem getElem?_set_ne {l : List α} {i j : Nat} (h : i ≠ j) {a : α} :
(l.set i a)[j]? = l[j]? := by
by_cases hj : j < (l.set i a).length
· rw [getElem?_eq_getElem hj, getElem?_eq_getElem (by simp_all)]
simp_all
· rw [getElem?_eq_none (by simp_all), getElem?_eq_none (by simp_all)]
theorem getElem_set {l : List α} {m n} {a} (h) :
(set l m a)[n]'h = if m = n then a else l[n]'(length_set .. ▸ h) := by
if h : m = n then
subst m; simp only [getElem_set_eq, ↓reduceIte]
else
simp [h]
@[deprecated getElem_set (since := "2024-06-12")]
theorem get_set {l : List α} {m n} {a : α} (h) :
(set l m a).get ⟨n, h⟩ = if m = n then a else l.get ⟨n, length_set .. ▸ h⟩ := by
simp [getElem_set]
theorem getElem?_set {l : List α} {i j : Nat} {a : α} :
(l.set i a)[j]? = if i = j then if i < l.length then some a else none else l[j]? := by
if h : i = j then
subst h
rw [if_pos rfl]
split <;> rename_i h
· simp only [getElem?_set_eq (by simpa), h]
· simp_all
else
simp [h]
theorem set_eq_of_length_le {l : List α} {n : Nat} (h : l.length ≤ n) {a : α} :
l.set n a = l := by
induction l generalizing n with
| nil => simp_all
| cons a l ih =>
induction n
· simp_all
· simp only [set_cons_succ, cons.injEq, true_and]
rw [ih]
exact Nat.succ_le_succ_iff.mp h
@[simp] theorem set_eq_nil (l : List α) (n : Nat) (a : α) : l.set n a = [] ↔ l = [] := by
cases l <;> cases n <;> simp only [set]
theorem set_comm (a b : α) : ∀ {n m : Nat} (l : List α), n ≠ m →
(l.set n a).set m b = (l.set m b).set n a
| _, _, [], _ => by simp
| n+1, 0, _ :: _, _ => by simp [set]
| 0, m+1, _ :: _, _ => by simp [set]
| n+1, m+1, x :: t, h =>
congrArg _ <| set_comm a b t fun h' => h <| Nat.succ_inj'.mpr h'
@[simp]
theorem set_set (a b : α) : ∀ (l : List α) (n : Nat), (l.set n a).set n b = l.set n b
| [], _ => by simp
| _ :: _, 0 => by simp [set]
| _ :: _, _+1 => by simp [set, set_set]
theorem mem_set (l : List α) (n : Nat) (h : n < l.length) (a : α) :
a ∈ l.set n a := by
simp [mem_iff_getElem]
exact ⟨n, (by simpa using h), by simp⟩
theorem mem_or_eq_of_mem_set : ∀ {l : List α} {n : Nat} {a b : α}, a ∈ l.set n b → a ∈ l a = b
| _ :: _, 0, _, _, h => ((mem_cons ..).1 h).symm.imp_left (.tail _)
| _ :: _, _+1, _, _, .head .. => .inl (.head ..)
| _ :: _, _+1, _, _, .tail _ h => (mem_or_eq_of_mem_set h).imp_left (.tail _)
-- See also `set_eq_take_append_cons_drop` in `Init.Data.List.TakeDrop`.
/-! ### Lexicographic ordering -/
theorem lt_irrefl' [LT α] (lt_irrefl : ∀ x : α, ¬x < x) (l : List α) : ¬l < l := by
induction l with
| nil => nofun
| cons a l ih => intro
| .head _ _ h => exact lt_irrefl _ h
| .tail _ _ h => exact ih h
theorem lt_trans' [LT α] [DecidableRel (@LT.lt α _)]
(lt_trans : ∀ {x y z : α}, x < y → y < z → x < z)
(le_trans : ∀ {x y z : α}, ¬x < y → ¬y < z → ¬x < z)
{l₁ l₂ l₃ : List α} (h₁ : l₁ < l₂) (h₂ : l₂ < l₃) : l₁ < l₃ := by
induction h₁ generalizing l₃ with
| nil => let _::_ := l₃; exact List.lt.nil ..
| @head a l₁ b l₂ ab =>
match h₂ with
| .head l₂ l₃ bc => exact List.lt.head _ _ (lt_trans ab bc)
| .tail _ cb ih =>
exact List.lt.head _ _ <| Decidable.by_contra (le_trans · cb ab)
| @tail a l₁ b l₂ ab ba h₁ ih2 =>
match h₂ with
| .head l₂ l₃ bc =>
exact List.lt.head _ _ <| Decidable.by_contra (le_trans ba · bc)
| .tail bc cb ih =>
exact List.lt.tail (le_trans ab bc) (le_trans cb ba) (ih2 ih)
theorem lt_antisymm' [LT α]
(lt_antisymm : ∀ {x y : α}, ¬x < y → ¬y < x → x = y)
{l₁ l₂ : List α} (h₁ : ¬l₁ < l₂) (h₂ : ¬l₂ < l₁) : l₁ = l₂ := by
induction l₁ generalizing l₂ with
| nil =>
cases l₂ with
| nil => rfl
| cons b l₂ => cases h₁ (.nil ..)
| cons a l₁ ih =>
cases l₂ with
| nil => cases h₂ (.nil ..)
| cons b l₂ =>
have ab : ¬a < b := fun ab => h₁ (.head _ _ ab)
cases lt_antisymm ab (fun ba => h₂ (.head _ _ ba))
rw [ih (fun ll => h₁ (.tail ab ab ll)) (fun ll => h₂ (.tail ab ab ll))]
/-! ### foldlM and foldrM -/
@[simp] theorem foldlM_reverse [Monad m] (l : List α) (f : β → α → m β) (b) :
l.reverse.foldlM f b = l.foldrM (fun x y => f y x) b := rfl
@[simp] theorem foldlM_append [Monad m] [LawfulMonad m] (f : β → α → m β) (b) (l l' : List α) :
(l ++ l').foldlM f b = l.foldlM f b >>= l'.foldlM f := by
induction l generalizing b <;> simp [*]
@[simp] theorem foldrM_cons [Monad m] [LawfulMonad m] (a : α) (l) (f : α → β → m β) (b) :
(a :: l).foldrM f b = l.foldrM f b >>= f a := by
simp only [foldrM]
induction l <;> simp_all
theorem foldl_eq_foldlM (f : β → α → β) (b) (l : List α) :
l.foldl f b = l.foldlM (m := Id) f b := by
induction l generalizing b <;> simp [*, foldl]
theorem foldr_eq_foldrM (f : α → β → β) (b) (l : List α) :
l.foldr f b = l.foldrM (m := Id) f b := by
induction l <;> simp [*, foldr]
/-! ### foldl and foldr -/
@[simp] theorem foldrM_append [Monad m] [LawfulMonad m] (f : α → β → m β) (b) (l l' : List α) :
(l ++ l').foldrM f b = l'.foldrM f b >>= l.foldrM f := by
induction l <;> simp [*]
@[simp] theorem foldl_append {β : Type _} (f : β → α → β) (b) (l l' : List α) :
(l ++ l').foldl f b = l'.foldl f (l.foldl f b) := by simp [foldl_eq_foldlM]
@[simp] theorem foldr_append (f : α → β → β) (b) (l l' : List α) :
(l ++ l').foldr f b = l.foldr f (l'.foldr f b) := by simp [foldr_eq_foldrM]
@[simp] theorem foldr_self_append (l : List α) : l.foldr cons l' = l ++ l' := by
induction l <;> simp [*]
theorem foldr_self (l : List α) : l.foldr cons [] = l := by simp
theorem foldl_map (f : β₁ → β₂) (g : α → β₂ → α) (l : List β₁) (init : α) :
(l.map f).foldl g init = l.foldl (fun x y => g x (f y)) init := by
induction l generalizing init <;> simp [*]
theorem foldr_map (f : α₁ → α₂) (g : α₂ → β → β) (l : List α₁) (init : β) :
(l.map f).foldr g init = l.foldr (fun x y => g (f x) y) init := by
induction l generalizing init <;> simp [*]
theorem foldl_map' {α β : Type u} (g : α → β) (f : ααα) (f' : β → β → β) (a : α) (l : List α)
(h : ∀ x y, f' (g x) (g y) = g (f x y)) :
(l.map g).foldl f' (g a) = g (l.foldl f a) := by
induction l generalizing a
· simp
· simp [*, h]
theorem foldr_map' {α β : Type u} (g : α → β) (f : ααα) (f' : β → β → β) (a : α) (l : List α)
(h : ∀ x y, f' (g x) (g y) = g (f x y)) :
(l.map g).foldr f' (g a) = g (l.foldr f a) := by
induction l generalizing a
· simp
· simp [*, h]
/-! ### getD -/
@[simp] theorem getD_eq_getElem? (l) (n) (a : α) : getD l n a = (l[n]?).getD a := by
simp [getD]
@[deprecated getD_eq_getElem? (since := "2024-06-12")]
theorem getD_eq_get? : ∀ l n (a : α), getD l n a = (get? l n).getD a := by simp
/-! ### getLast -/
theorem getLast_eq_get : ∀ (l : List α) (h : l ≠ []),
getLast l h = l.get ⟨l.length - 1, by
match l with
| [] => contradiction
| a :: l => exact Nat.le_refl _⟩
| [a], h => rfl
| a :: b :: l, h => by
simp [getLast, get, Nat.succ_sub_succ, getLast_eq_get]
theorem getLast_cons' {a : α} {l : List α} : ∀ (h₁ : a :: l ≠ nil) (h₂ : l ≠ nil),
getLast (a :: l) h₁ = getLast l h₂ := by
induction l <;> intros; {contradiction}; rfl
theorem getLast_eq_getLastD (a l h) : @getLast α (a::l) h = getLastD l a := by
cases l <;> rfl
theorem getLastD_eq_getLast? (a l) : @getLastD α l a = (getLast? l).getD a := by
cases l <;> rfl
@[simp] theorem getLast_singleton (a h) : @getLast α [a] h = a := rfl
theorem getLast!_cons [Inhabited α] : @getLast! α _ (a::l) = getLastD l a := by
simp [getLast!, getLast_eq_getLastD]
theorem getLast_mem : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ l
| [], h => absurd rfl h
| [_], _ => .head ..
| _::a::l, _ => .tail _ <| getLast_mem (cons_ne_nil a l)
theorem getLastD_mem_cons : ∀ (l : List α) (a : α), getLastD l a ∈ a::l
| [], _ => .head ..
| _::_, _ => .tail _ <| getLast_mem _
theorem getElem_cons_length (x : α) (xs : List α) (n : Nat) (h : n = xs.length) :
(x :: xs)[n]'(by simp [h]) = (x :: xs).getLast (cons_ne_nil x xs) := by
rw [getLast_eq_get]; cases h; rfl
@[deprecated getElem_cons_length (since := "2024-06-12")]
theorem get_cons_length (x : α) (xs : List α) (n : Nat) (h : n = xs.length) :
(x :: xs).get ⟨n, by simp [h]⟩ = (x :: xs).getLast (cons_ne_nil x xs) := by
simp [getElem_cons_length, h]
/-! ### getLast? -/
theorem getLast?_cons : @getLast? α (a::l) = getLastD l a := by
simp [getLast?, getLast_eq_getLastD]
@[simp] theorem getLast?_singleton (a : α) : getLast? [a] = a := rfl
theorem getLast?_eq_getLast : ∀ l h, @getLast? α l = some (getLast l h)
| [], h => nomatch h rfl
| _::_, _ => rfl
theorem getLast?_eq_get? : ∀ (l : List α), getLast? l = l.get? (l.length - 1)
| [] => rfl
| a::l => by rw [getLast?_eq_getLast (a::l) nofun, getLast_eq_get, get?_eq_get]
@[simp] theorem getLast?_concat (l : List α) : getLast? (l ++ [a]) = some a := by
simp [getLast?_eq_get?, Nat.succ_sub_succ]
@[simp] theorem getLastD_concat (a b l) : @getLastD α (l ++ [b]) a = b := by
rw [getLastD_eq_getLast?, getLast?_concat]; rfl
/-! ## Head and tail -/
/-! ### head -/
theorem head!_of_head? [Inhabited α] : ∀ {l : List α}, head? l = some a → head! l = a
| _a::_l, rfl => rfl
theorem head?_eq_head : ∀ l h, @head? α l = some (head l h)
| _::_, _ => rfl
/-! ### tail -/
@[simp] theorem tailD_eq_tail? (l l' : List α) : tailD l l' = (tail? l).getD l' := by
cases l <;> rfl
/-! ## Basic operations -/
/-! ### map -/
@[simp] theorem map_id (l : List α) : map id l = l := by induction l <;> simp_all
@[simp] theorem map_id' (l : List α) : map (fun a => a) l = l := by induction l <;> simp_all
theorem map_id'' {f : αα} (h : ∀ x, f x = x) (l : List α) : map f l = l := by
simp [show f = id from funext h]
theorem map_singleton (f : α → β) (a : α) : map f [a] = [f a] := rfl
@[simp] theorem length_map (as : List α) (f : α → β) : (as.map f).length = as.length := by
induction as with
| nil => simp [List.map]
| cons _ as ih => simp [List.map, ih]
@[simp] theorem getElem?_map (f : α → β) : ∀ (l : List α) (n : Nat), (map f l)[n]? = Option.map f l[n]?
| [], _ => rfl
| _ :: _, 0 => by simp
| _ :: l, n+1 => by simp [getElem?_map f l n]
@[deprecated getElem?_map (since := "2024-06-12")]
theorem get?_map (f : α → β) : ∀ l n, (map f l).get? n = (l.get? n).map f
| [], _ => rfl
| _ :: _, 0 => rfl
| _ :: l, n+1 => get?_map f l n
@[simp] theorem getElem_map (f : α → β) {l} {n : Nat} {h : n < (map f l).length} :
(map f l)[n] = f (l[n]'(length_map l f ▸ h)) :=
Option.some.inj <| by rw [← getElem?_eq_getElem, getElem?_map, getElem?_eq_getElem]; rfl
@[deprecated getElem_map (since := "2024-06-12")]
theorem get_map (f : α → β) {l n} :
get (map f l) n = f (get l ⟨n, length_map l f ▸ n.2⟩) := by
simp
@[simp] theorem mem_map {f : α → β} : ∀ {l : List α}, b ∈ l.map f ↔ ∃ a, a ∈ l ∧ f a = b
| [] => by simp
| _ :: l => by simp [mem_map (l := l), eq_comm (a := b)]
theorem exists_of_mem_map (h : b ∈ map f l) : ∃ a, a ∈ l ∧ f a = b := mem_map.1 h
theorem mem_map_of_mem (f : α → β) (h : a ∈ l) : f a ∈ map f l := mem_map.2 ⟨_, h, rfl⟩
@[simp] theorem map_inj_left {f g : α → β} : map f l = map g l ↔ ∀ a ∈ l, f a = g a := by
induction l <;> simp_all
theorem map_congr_left (h : ∀ a ∈ l, f a = g a) : map f l = map g l :=
map_inj_left.2 h
theorem map_inj : map f = map g ↔ f = g := by
constructor
· intro h; ext a; replace h := congrFun h [a]; simpa using h
· intro h; subst h; rfl
@[simp] theorem map_eq_nil {f : α → β} {l : List α} : map f l = [] ↔ l = [] := by
constructor <;> exact fun _ => match l with | [] => rfl
theorem eq_nil_of_map_eq_nil {f : α → β} {l : List α} (h : map f l = []) : l = [] :=
map_eq_nil.mp h
theorem map_eq_cons {f : α → β} {l : List α} :
map f l = b :: l₂ ↔ l.head?.map f = some b ∧ l.tail?.map (map f) = some l₂ := by
induction l <;> simp_all
theorem map_eq_cons' {f : α → β} {l : List α} :
map f l = b :: l₂ ↔ ∃ a l₁, l = a :: l₁ ∧ f a = b ∧ map f l₁ = l₂ := by
cases l
case nil => simp
case cons a l₁ =>
simp only [map_cons, cons.injEq]
constructor
· rintro ⟨rfl, rfl⟩
exact ⟨a, l₁, ⟨rfl, rfl⟩, ⟨rfl, rfl⟩⟩
· rintro ⟨a, l₁, ⟨rfl, rfl⟩, ⟨rfl, rfl⟩⟩
constructor <;> rfl
theorem map_eq_foldr (f : α → β) (l : List α) : map f l = foldr (fun a bs => f a :: bs) [] l := by
induction l <;> simp [*]
@[simp] theorem set_map {f : α → β} {l : List α} {n : Nat} {a : α} :
(map f l).set n (f a) = map f (l.set n a) := by
induction l generalizing n with
| nil => simp
| cons b l ih => cases n <;> simp_all
@[simp] theorem head_map (f : α → β) (l : List α) (w) :
head (map f l) w = f (head l (by simpa using w)) := by
cases l
· simp at w
· simp_all
@[simp] theorem head?_map (f : α → β) (l : List α) : head? (map f l) = (head? l).map f := by
cases l <;> rfl
@[simp] theorem headD_map (f : α → β) (l : List α) (a : α) : headD (map f l) (f a) = f (headD l a) := by
cases l <;> rfl
@[simp] theorem tail?_map (f : α → β) (l : List α) : tail? (map f l) = (tail? l).map (map f) := by
cases l <;> rfl
@[simp] theorem tailD_map (f : α → β) (l : List α) (l' : List α) :
tailD (map f l) (map f l') = map f (tailD l l') := by
cases l <;> rfl
@[simp] theorem getLast_map (f : α → β) (l : List α) (h) :
getLast (map f l) h = f (getLast l (by simpa using h)) := by
cases l
· simp at h
· simp only [← getElem_cons_length _ _ _ rfl]
simp only [map_cons]
simp only [← getElem_cons_length _ _ _ rfl]
simp only [← map_cons, getElem_map]
simp
@[simp] theorem getLast?_map (f : α → β) (l : List α) : getLast? (map f l) = (getLast? l).map f := by
cases l
· simp
· rw [getLast?_eq_getLast, getLast?_eq_getLast, getLast_map] <;> simp
@[simp] theorem getLastD_map (f : α → β) (l : List α) (a : α) : getLastD (map f l) (f a) = f (getLastD l a) := by
cases l
· simp
· rw [getLastD_eq_getLast?, getLastD_eq_getLast?, getLast?_map] <;> simp
@[simp] theorem map_append (f : α → β) : ∀ l₁ l₂, map f (l₁ ++ l₂) = map f l₁ ++ map f l₂ := by
intro l₁; induction l₁ <;> intros <;> simp_all
@[simp] theorem map_map (g : β → γ) (f : α → β) (l : List α) :
map g (map f l) = map (g ∘ f) l := by induction l <;> simp_all
theorem forall_mem_map_iff {f : α → β} {l : List α} {P : β → Prop} :
(∀ (i) (_ : i ∈ l.map f), P i) ↔ ∀ (j) (_ : j ∈ l), P (f j) := by
simp
/-! ### filter -/
@[simp] theorem filter_cons_of_pos {p : α → Bool} {a : α} (l) (pa : p a) :
filter p (a :: l) = a :: filter p l := by rw [filter, pa]
@[simp] theorem filter_cons_of_neg {p : α → Bool} {a : α} (l) (pa : ¬ p a) :
filter p (a :: l) = filter p l := by rw [filter, eq_false_of_ne_true pa]
theorem filter_cons :
(x :: xs : List α).filter p = if p x then x :: (xs.filter p) else xs.filter p := by
split <;> simp [*]
theorem length_filter_le (p : α → Bool) (l : List α) :
(l.filter p).length ≤ l.length := by
induction l with
| nil => simp
| cons a l ih =>
simp only [filter_cons, length_cons, succ_eq_add_one]
split
· simp only [length_cons, succ_eq_add_one]
exact Nat.succ_le_succ ih
· exact Nat.le_trans ih (Nat.le_add_right _ _)
@[simp]
theorem filter_eq_self {l} : filter p l = l ↔ ∀ a ∈ l, p a := by
induction l with simp
| cons a l ih =>
cases h : p a <;> simp [*]
intro h; exact Nat.lt_irrefl _ (h ▸ length_filter_le p l)
@[simp]
theorem filter_length_eq_length {l} : (filter p l).length = l.length ↔ ∀ a ∈ l, p a := by
induction l with
| nil => simp
| cons a l ih =>
simp only [filter_cons, length_cons, succ_eq_add_one, mem_cons, forall_eq_or_imp]
split <;> rename_i h
· simp_all [Nat.add_one_inj] -- Why does the simproc not fire here?
· have := Nat.ne_of_lt (Nat.lt_succ.mpr (length_filter_le p l))
simp_all
theorem mem_filter : x ∈ filter p as ↔ x ∈ as ∧ p x := by
induction as with
| nil => simp [filter]
| cons a as ih =>
by_cases h : p a
· simp_all [or_and_left]
· simp_all [or_and_right]
theorem filter_eq_nil {l} : filter p l = [] ↔ ∀ a, a ∈ l → ¬p a := by
simp only [eq_nil_iff_forall_not_mem, mem_filter, not_and]
@[simp] theorem filter_filter (q) : ∀ l, filter p (filter q l) = filter (fun a => p a ∧ q a) l
| [] => rfl
| a :: l => by by_cases hp : p a <;> by_cases hq : q a <;> simp [hp, hq, filter_filter _ l]
theorem filter_map (f : β → α) (l : List β) : filter p (map f l) = map f (filter (p ∘ f) l) := by
induction l with
| nil => rfl
| cons a l IH => by_cases h : p (f a) <;> simp [*]
@[deprecated filter_map (since := "2024-06-15")] abbrev map_filter := @filter_map
theorem map_filter_eq_foldr (f : α → β) (p : α → Bool) (as : List α) :
map f (filter p as) = foldr (fun a bs => bif p a then f a :: bs else bs) [] as := by
induction as with
| nil => rfl
| cons head _ ih =>
simp only [foldr]
cases hp : p head <;> simp [filter, *]
@[simp] theorem filter_append {p : α → Bool} :
∀ (l₁ l₂ : List α), filter p (l₁ ++ l₂) = filter p l₁ ++ filter p l₂
| [], l₂ => rfl
| a :: l₁, l₂ => by simp [filter]; split <;> simp [filter_append l₁]
theorem filter_congr {p q : α → Bool} :
∀ {l : List α}, (∀ x ∈ l, p x ↔ q x) → filter p l = filter q l
| [], _ => rfl
| a :: l, h => by
rw [forall_mem_cons] at h; by_cases pa : p a
· simp [pa, h.1.1 pa, filter_congr h.2]
· simp [pa, mt h.1.2 pa, filter_congr h.2]
@[deprecated filter_congr (since := "2024-06-20")] abbrev filter_congr' := @filter_congr
/-! ### filterMap -/
@[simp] theorem filterMap_cons_none {f : α → Option β} (a : α) (l : List α) (h : f a = none) :
filterMap f (a :: l) = filterMap f l := by simp only [filterMap, h]
@[simp] theorem filterMap_cons_some (f : α → Option β) (a : α) (l : List α) {b : β} (h : f a = some b) :
filterMap f (a :: l) = b :: filterMap f l := by simp only [filterMap, h]
@[simp]
theorem filterMap_eq_map (f : α → β) : filterMap (some ∘ f) = map f := by
funext l; induction l <;> simp [*, filterMap_cons]
@[simp] theorem filterMap_some (l : List α) : filterMap some l = l := by
erw [filterMap_eq_map, map_id]
theorem map_filterMap_some_eq_filter_map_is_some (f : α → Option β) (l : List α) :
(l.filterMap f).map some = (l.map f).filter fun b => b.isSome := by
induction l <;> simp [filterMap_cons]; split <;> simp [*]
theorem length_filterMap_le (f : α → Option β) (l : List α) :
(filterMap f l).length ≤ l.length := by
rw [← length_map _ some, map_filterMap_some_eq_filter_map_is_some, ← length_map _ f]
apply length_filter_le
@[simp]
theorem filterMap_length_eq_length {l} :
(filterMap f l).length = l.length ↔ ∀ a ∈ l, (f a).isSome := by
induction l with
| nil => simp
| cons a l ih =>
simp only [filterMap_cons, length_cons, succ_eq_add_one, mem_cons, forall_eq_or_imp]
split <;> rename_i h
· have := Nat.ne_of_lt (Nat.lt_succ.mpr (length_filterMap_le f l))
simp_all
· simp_all [Nat.add_one_inj] -- Why does the simproc not fire here?
@[simp]
theorem filterMap_eq_filter (p : α → Bool) :
filterMap (Option.guard (p ·)) = filter p := by
funext l
induction l with
| nil => rfl
| cons a l IH => by_cases pa : p a <;> simp [filterMap_cons, Option.guard, pa, ← IH]
theorem filterMap_filterMap (f : α → Option β) (g : β → Option γ) (l : List α) :
filterMap g (filterMap f l) = filterMap (fun x => (f x).bind g) l := by
induction l with
| nil => rfl
| cons a l IH => cases h : f a <;> simp [filterMap_cons, *]
theorem map_filterMap (f : α → Option β) (g : β → γ) (l : List α) :
map g (filterMap f l) = filterMap (fun x => (f x).map g) l := by
simp only [← filterMap_eq_map, filterMap_filterMap, Option.map_eq_bind]
@[simp]
theorem filterMap_map (f : α → β) (g : β → Option γ) (l : List α) :
filterMap g (map f l) = filterMap (g ∘ f) l := by
rw [← filterMap_eq_map, filterMap_filterMap]; rfl
theorem filter_filterMap (f : α → Option β) (p : β → Bool) (l : List α) :
filter p (filterMap f l) = filterMap (fun x => (f x).filter p) l := by
rw [← filterMap_eq_filter, filterMap_filterMap]
congr; funext x; cases f x <;> simp [Option.filter, Option.guard]
theorem filterMap_filter (p : α → Bool) (f : α → Option β) (l : List α) :
filterMap f (filter p l) = filterMap (fun x => if p x then f x else none) l := by
rw [← filterMap_eq_filter, filterMap_filterMap]
congr; funext x; by_cases h : p x <;> simp [Option.guard, h]
@[simp] theorem mem_filterMap (f : α → Option β) (l : List α) {b : β} :
b ∈ filterMap f l ↔ ∃ a, a ∈ l ∧ f a = some b := by
induction l <;> simp [filterMap_cons]; split <;> simp [*, eq_comm]
theorem filterMap_append {α β : Type _} (l l' : List α) (f : α → Option β) :
filterMap f (l ++ l') = filterMap f l ++ filterMap f l' := by
induction l <;> simp [filterMap_cons]; split <;> simp [*]
@[simp] theorem filterMap_join (f : α → Option β) (L : List (List α)) :
filterMap f (join L) = join (map (filterMap f) L) := by
induction L <;> simp [*, filterMap_append]
theorem map_filterMap_of_inv (f : α → Option β) (g : β → α) (H : ∀ x : α, (f x).map g = some x)
(l : List α) : map g (filterMap f l) = l := by simp only [map_filterMap, H, filterMap_some]
/-! ### append -/
theorem getElem?_append_right : ∀ {l₁ l₂ : List α} {n : Nat}, l₁.length ≤ n →
(l₁ ++ l₂)[n]? = l₂[n - l₁.length]?
| [], _, n, _ => rfl
| a :: l, _, n+1, h₁ => by
rw [cons_append]
simp [Nat.succ_sub_succ_eq_sub, getElem?_append_right (Nat.lt_succ.1 h₁)]
@[deprecated getElem?_append_right (since := "2024-06-12")]
theorem get?_append_right {l₁ l₂ : List α} {n : Nat} (h : l₁.length ≤ n) :
(l₁ ++ l₂).get? n = l₂.get? (n - l₁.length) := by
simp [getElem?_append_right, h]
theorem getElem_append_right' {l₁ l₂ : List α} {n : Nat} (h₁ : l₁.length ≤ n) (h₂) :
(l₁ ++ l₂)[n]'h₂ =
l₂[n - l₁.length]'(by rw [length_append] at h₂; exact Nat.sub_lt_left_of_lt_add h₁ h₂) :=
Option.some.inj <| by rw [← getElem?_eq_getElem, ← getElem?_eq_getElem, getElem?_append_right h₁]
@[deprecated (since := "2024-06-12")]
theorem get_append_right_aux {l₁ l₂ : List α} {n : Nat}
(h₁ : l₁.length ≤ n) (h₂ : n < (l₁ ++ l₂).length) : n - l₁.length < l₂.length := by
rw [length_append] at h₂
exact Nat.sub_lt_left_of_lt_add h₁ h₂
set_option linter.deprecated false in
@[deprecated getElem_append_right' (since := "2024-06-12")]
theorem get_append_right' {l₁ l₂ : List α} {n : Nat} (h₁ : l₁.length ≤ n) (h₂) :
(l₁ ++ l₂).get ⟨n, h₂⟩ = l₂.get ⟨n - l₁.length, get_append_right_aux h₁ h₂⟩ :=
Option.some.inj <| by rw [← get?_eq_get, ← get?_eq_get, get?_append_right h₁]
theorem getElem_of_append {l : List α} (eq : l = l₁ ++ a :: l₂) (h : l₁.length = n) :
l[n]'(eq ▸ h ▸ by simp_arith) = a := Option.some.inj <| by
rw [← getElem?_eq_getElem, eq, getElem?_append_right (h ▸ Nat.le_refl _), h]
simp
@[deprecated (since := "2024-06-12")]
theorem get_of_append_proof {l : List α}
(eq : l = l₁ ++ a :: l₂) (h : l₁.length = n) : n < length l := eq ▸ h ▸ by simp_arith
set_option linter.deprecated false in
@[deprecated getElem_of_append (since := "2024-06-12")]
theorem get_of_append {l : List α} (eq : l = l₁ ++ a :: l₂) (h : l₁.length = n) :
l.get ⟨n, get_of_append_proof eq h⟩ = a := Option.some.inj <| by
rw [← get?_eq_get, eq, get?_append_right (h ▸ Nat.le_refl _), h, Nat.sub_self]; rfl
theorem append_of_mem {a : α} {l : List α} : a ∈ l → ∃ s t : List α, l = s ++ a :: t
| .head l => ⟨[], l, rfl⟩
| .tail b h => let ⟨s, t, h'⟩ := append_of_mem h; ⟨b::s, t, by rw [h', cons_append]⟩
@[simp 1100] theorem singleton_append : [x] ++ l = x :: l := rfl
theorem append_inj :
∀ {s₁ s₂ t₁ t₂ : List α}, s₁ ++ t₁ = s₂ ++ t₂ → length s₁ = length s₂ → s₁ = s₂ ∧ t₁ = t₂
| [], [], t₁, t₂, h, _ => ⟨rfl, h⟩
| a :: s₁, b :: s₂, t₁, t₂, h, hl => by
simp [append_inj (cons.inj h).2 (Nat.succ.inj hl)] at h ⊢; exact h
theorem append_inj_right (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length s₁ = length s₂) : t₁ = t₂ :=
(append_inj h hl).right
theorem append_inj_left (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length s₁ = length s₂) : s₁ = s₂ :=
(append_inj h hl).left
theorem append_inj' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : s₁ = s₂ ∧ t₁ = t₂ :=
append_inj h <| @Nat.add_right_cancel _ (length t₁) _ <| by
let hap := congrArg length h; simp only [length_append, ← hl] at hap; exact hap
theorem append_inj_right' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : t₁ = t₂ :=
(append_inj' h hl).right
theorem append_inj_left' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : s₁ = s₂ :=
(append_inj' h hl).left
theorem append_right_inj {t₁ t₂ : List α} (s) : s ++ t₁ = s ++ t₂ ↔ t₁ = t₂ :=
⟨fun h => append_inj_right h rfl, congrArg _⟩
theorem append_left_inj {s₁ s₂ : List α} (t) : s₁ ++ t = s₂ ++ t ↔ s₁ = s₂ :=
⟨fun h => append_inj_left' h rfl, congrArg (· ++ _)⟩
@[simp] theorem append_eq_nil : p ++ q = [] ↔ p = [] ∧ q = [] := by
cases p <;> simp
@[simp] theorem getLast_append {a : α} : ∀ (l : List α) h, getLast (l ++ [a]) h = a
| [], _ => rfl
| a::t, h => by
simp [getLast_cons' _ fun H => cons_ne_nil _ _ (append_eq_nil.1 H).2, getLast_append t]
theorem getLast_concat : getLast (concat l a) h = a := by
simp [concat_eq_append, getLast_append]
theorem getElem_append : ∀ {l₁ l₂ : List α} (n : Nat) (h : n < l₁.length),
(l₁ ++ l₂)[n]'(length_append .. ▸ Nat.lt_add_right _ h) = l₁[n]
| a :: l, _, 0, h => rfl
| a :: l, _, n+1, h => by simp only [get, cons_append]; apply getElem_append
@[deprecated getElem_append (since := "2024-06-12")]
theorem get_append {l₁ l₂ : List α} (n : Nat) (h : n < l₁.length) :
(l₁ ++ l₂).get ⟨n, length_append .. ▸ Nat.lt_add_right _ h⟩ = l₁.get ⟨n, h⟩ := by
simp [getElem_append, h]
@[deprecated getElem_append_left (since := "2024-06-12")]
theorem get_append_left (as bs : List α) (h : i < as.length) {h'} : (as ++ bs).get ⟨i, h'⟩ = as.get ⟨i, h⟩ := by
simp [getElem_append_left, h, h']
@[deprecated getElem_append_right (since := "2024-06-12")]
theorem get_append_right (as bs : List α) (h : ¬ i < as.length) {h' h''} : (as ++ bs).get ⟨i, h'⟩ = bs.get ⟨i - as.length, h''⟩ := by
simp [getElem_append_right, h, h', h'']
theorem getElem?_append {l₁ l₂ : List α} {n : Nat} (hn : n < l₁.length) :
(l₁ ++ l₂)[n]? = l₁[n]? := by
have hn' : n < (l₁ ++ l₂).length := Nat.lt_of_lt_of_le hn <|
length_append .. ▸ Nat.le_add_right ..
simp_all [getElem?_eq_getElem, getElem_append]
@[deprecated (since := "2024-06-12")]
theorem get?_append {l₁ l₂ : List α} {n : Nat} (hn : n < l₁.length) :
(l₁ ++ l₂).get? n = l₁.get? n := by
simp [getElem?_append hn]
theorem append_eq_append : List.append l₁ l₂ = l₁ ++ l₂ := rfl
theorem append_ne_nil_of_ne_nil_left (s t : List α) : s ≠ [] → s ++ t ≠ [] := by simp_all
theorem append_ne_nil_of_ne_nil_right (s t : List α) : t ≠ [] → s ++ t ≠ [] := by simp_all
@[simp] theorem nil_eq_append : [] = a ++ b ↔ a = [] ∧ b = [] := by
rw [eq_comm, append_eq_nil]
theorem append_ne_nil_of_left_ne_nil (a b : List α) (h0 : a ≠ []) : a ++ b ≠ [] := by simp [*]
theorem append_eq_cons :
a ++ b = x :: c ↔ (a = [] ∧ b = x :: c) (∃ a', a = x :: a' ∧ c = a' ++ b) := by
cases a with simp | cons a as => ?_
exact ⟨fun h => ⟨as, by simp [h]⟩, fun ⟨a', ⟨aeq, aseq⟩, h⟩ => ⟨aeq, by rw [aseq, h]⟩⟩
theorem cons_eq_append :
x :: c = a ++ b ↔ (a = [] ∧ b = x :: c) (∃ a', a = x :: a' ∧ c = a' ++ b) := by
rw [eq_comm, append_eq_cons]
theorem append_eq_append_iff {a b c d : List α} :
a ++ b = c ++ d ↔ (∃ a', c = a ++ a' ∧ b = a' ++ d) ∃ c', a = c ++ c' ∧ d = c' ++ b := by
induction a generalizing c with
| nil => simp_all
| cons a as ih => cases c <;> simp [eq_comm, and_assoc, ih, and_or_left]
@[simp] theorem mem_append {a : α} {s t : List α} : a ∈ s ++ t ↔ a ∈ s a ∈ t := by
induction s <;> simp_all [or_assoc]
theorem not_mem_append {a : α} {s t : List α} (h₁ : a ∉ s) (h₂ : a ∉ t) : a ∉ s ++ t :=
mt mem_append.1 $ not_or.mpr ⟨h₁, h₂⟩
theorem mem_append_eq (a : α) (s t : List α) : (a ∈ s ++ t) = (a ∈ s a ∈ t) :=
propext mem_append
theorem mem_append_left {a : α} {l₁ : List α} (l₂ : List α) (h : a ∈ l₁) : a ∈ l₁ ++ l₂ :=
mem_append.2 (Or.inl h)
theorem mem_append_right {a : α} (l₁ : List α) {l₂ : List α} (h : a ∈ l₂) : a ∈ l₁ ++ l₂ :=
mem_append.2 (Or.inr h)
theorem mem_iff_append {a : α} {l : List α} : a ∈ l ↔ ∃ s t : List α, l = s ++ a :: t :=
⟨append_of_mem, fun ⟨s, t, e⟩ => e ▸ by simp⟩
theorem forall_mem_append {p : α → Prop} {l₁ l₂ : List α} :
(∀ (x) (_ : x ∈ l₁ ++ l₂), p x) ↔ (∀ (x) (_ : x ∈ l₁), p x) ∧ (∀ (x) (_ : x ∈ l₂), p x) := by
simp only [mem_append, or_imp, forall_and]
/-! ### concat
Note that `concat_eq_append` is a `@[simp]` lemma, so `concat` should usually not appear in goals.
As such there's no need for a thorough set of lemmas describing `concat`.
-/
-- As `List.concat` is defined in `Init.Prelude`, we write the basic simplification lemmas here.
theorem concat_nil (a : α) : concat [] a = [a] :=
rfl
theorem concat_cons (a b : α) (l : List α) : concat (a :: l) b = a :: concat l b :=
rfl
theorem init_eq_of_concat_eq {a b : α} {l₁ l₂ : List α} : concat l₁ a = concat l₂ b → l₁ = l₂ := by
simp only [concat_eq_append]
intro h
apply append_inj_left' h (by simp)
theorem last_eq_of_concat_eq {a b : α} {l₁ l₂ : List α} : concat l₁ a = concat l₂ b → a = b := by
simp only [concat_eq_append]
intro h
simpa using append_inj_right' h (by simp)
theorem concat_inj_left {l l' : List α} (a : α) : concat l a = concat l' a ↔ l = l' :=
⟨init_eq_of_concat_eq, by simp⟩
theorem concat_eq_concat {l l' : List α} {a b : α} : concat l a = concat l' b ↔ l = l' ∧ a = b :=
⟨fun h => ⟨init_eq_of_concat_eq h, last_eq_of_concat_eq h⟩, by rintro ⟨rfl, rfl⟩; rfl⟩
theorem concat_ne_nil (a : α) (l : List α) : concat l a ≠ [] := by cases l <;> simp
theorem concat_append (a : α) (l₁ l₂ : List α) : concat l₁ a ++ l₂ = l₁ ++ a :: l₂ := by simp
theorem append_concat (a : α) (l₁ l₂ : List α) : l₁ ++ concat l₂ a = concat (l₁ ++ l₂) a := by simp
theorem map_concat (f : α → β) (a : α) (l : List α) : map f (concat l a) = concat (map f l) (f a) := by
induction l with
| nil => rfl
| cons x xs ih => simp [ih]
theorem eq_nil_or_concat : ∀ l : List α, l = [] ∃ L b, l = concat L b
| [] => .inl rfl
| a::l => match l, eq_nil_or_concat l with
| _, .inl rfl => .inr ⟨[], a, rfl⟩
| _, .inr ⟨L, b, rfl⟩ => .inr ⟨a::L, b, rfl⟩
/-! ### join -/
@[simp] theorem mem_join : ∀ {L : List (List α)}, a ∈ L.join ↔ ∃ l, l ∈ L ∧ a ∈ l
| [] => by simp
| b :: l => by simp [mem_join, or_and_right, exists_or]
theorem exists_of_mem_join : a ∈ join L → ∃ l, l ∈ L ∧ a ∈ l := mem_join.1
theorem mem_join_of_mem (lL : l ∈ L) (al : a ∈ l) : a ∈ join L := mem_join.2 ⟨l, lL, al⟩
@[simp] theorem map_join (f : α → β) (L : List (List α)) : map f (join L) = join (map (map f) L) := by
induction L <;> simp_all
/-! ### bind -/
@[simp] theorem append_bind xs ys (f : α → List β) :
List.bind (xs ++ ys) f = List.bind xs f ++ List.bind ys f := by
induction xs; {rfl}; simp_all [bind_cons, append_assoc]
@[simp] theorem bind_id (l : List (List α)) : List.bind l id = l.join := by simp [List.bind]
@[simp] theorem mem_bind {f : α → List β} {b} {l : List α} : b ∈ l.bind f ↔ ∃ a, a ∈ l ∧ b ∈ f a := by
simp [List.bind, mem_join]
exact ⟨fun ⟨_, ⟨a, h₁, rfl⟩, h₂⟩ => ⟨a, h₁, h₂⟩, fun ⟨a, h₁, h₂⟩ => ⟨_, ⟨a, h₁, rfl⟩, h₂⟩⟩
theorem exists_of_mem_bind {b : β} {l : List α} {f : α → List β} :
b ∈ List.bind l f → ∃ a, a ∈ l ∧ b ∈ f a := mem_bind.1
theorem mem_bind_of_mem {b : β} {l : List α} {f : α → List β} {a} (al : a ∈ l) (h : b ∈ f a) :
b ∈ List.bind l f := mem_bind.2 ⟨a, al, h⟩
theorem bind_singleton (f : α → List β) (x : α) : [x].bind f = f x :=
append_nil (f x)
@[simp] theorem bind_singleton' (l : List α) : (l.bind fun x => [x]) = l := by
induction l <;> simp [*]
theorem bind_assoc {α β} (l : List α) (f : α → List β) (g : β → List γ) :
(l.bind f).bind g = l.bind fun x => (f x).bind g := by
induction l <;> simp [*]
theorem map_bind (f : β → γ) (g : α → List β) :
∀ l : List α, (l.bind g).map f = l.bind fun a => (g a).map f
| [] => rfl
| a::l => by simp only [bind_cons, map_append, map_bind _ _ l]
theorem bind_map {f : α → β} {g : β → List γ} (l : List α) : (map f l).bind g = l.bind (fun a => g (f a)) := by
induction l <;> simp [bind_cons, append_bind, *]
theorem map_eq_bind {α β} (f : α → β) (l : List α) : map f l = l.bind fun x => [f x] := by
simp only [← map_singleton]
rw [← bind_singleton' l, map_bind, bind_singleton']
/-! ### replicate -/
@[simp] theorem replicate_one : replicate 1 a = [a] := rfl
@[simp] theorem contains_replicate [BEq α] {n : Nat} {a b : α} :
(replicate n b).contains a = (a == b && !n == 0) := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, elem_cons]
split <;> simp_all
@[simp] theorem decide_mem_replicate [BEq α] [LawfulBEq α] {a b : α} :
∀ {n}, decide (b ∈ replicate n a) = ((¬ n == 0) && b == a)
| 0 => by simp
| n+1 => by simp [replicate_succ, decide_mem_replicate, Nat.succ_ne_zero]
@[simp] theorem mem_replicate {a b : α} : ∀ {n}, b ∈ replicate n a ↔ n ≠ 0 ∧ b = a
| 0 => by simp
| n+1 => by simp [replicate_succ, mem_replicate, Nat.succ_ne_zero]
theorem eq_of_mem_replicate {a b : α} {n} (h : b ∈ replicate n a) : b = a := (mem_replicate.1 h).2
@[simp] theorem replicate_succ_ne_nil (n : Nat) (a : α) : replicate (n+1) a ≠ [] := by
simp [replicate_succ]
@[simp] theorem getElem_replicate (a : α) {n : Nat} {m} (h : m < (replicate n a).length) :
(replicate n a)[m] = a :=
eq_of_mem_replicate (get_mem _ _ _)
@[deprecated getElem_replicate (since := "2024-06-12")]
theorem get_replicate (a : α) {n : Nat} (m : Fin _) : (replicate n a).get m = a := by
simp
theorem getElem?_replicate : (replicate n a)[m]? = if m < n then some a else none := by
by_cases h : m < n
· rw [getElem?_eq_getElem (by simpa), getElem_replicate, if_pos h]
· rw [getElem?_eq_none (by simpa using h), if_neg h]
@[simp] theorem getElem?_replicate_of_lt {n : Nat} {m : Nat} (h : m < n) : (replicate n a)[m]? = some a := by
simp [getElem?_replicate, h]
@[simp] theorem replicate_inj : replicate n a = replicate m b ↔ n = m ∧ (n = 0 a = b) :=
⟨fun h => have eq : n = m := by simpa using congrArg length h
⟨eq, by
subst eq
by_cases w : n = 0
· simp_all
· right
have p := congrArg (·[0]?) h
replace w : 0 < n := by exact zero_lt_of_ne_zero w
simp only [getElem?_replicate, if_pos w] at p
simp_all⟩,
by rintro ⟨rfl, rfl | rfl⟩ <;> rfl⟩
theorem eq_replicate_of_mem {a : α} :
∀ {l : List α}, (∀ (b) (_ : b ∈ l), b = a) → l = replicate l.length a
| [], _ => rfl
| b :: l, H => by
let ⟨rfl, H₂⟩ := forall_mem_cons (l := l).1 H
rw [length_cons, replicate, ← eq_replicate_of_mem H₂]
theorem eq_replicate {a : α} {n} {l : List α} :
l = replicate n a ↔ length l = n ∧ ∀ (b) (_ : b ∈ l), b = a :=
⟨fun h => h ▸ ⟨length_replicate .., fun _ => eq_of_mem_replicate⟩,
fun ⟨e, al⟩ => e ▸ eq_replicate_of_mem al⟩
theorem map_eq_replicate_iff {l : List α} {f : α → β} {b : β} :
l.map f = replicate l.length b ↔ ∀ x ∈ l, f x = b := by
simp [eq_replicate]
@[simp] theorem map_const (l : List α) (b : β) : map (Function.const α b) l = replicate l.length b :=
map_eq_replicate_iff.mpr fun _ _ => rfl
-- This can not be a `@[simp]` lemma because it would fire on every `List.map`.
theorem map_const' (l : List α) (b : β) : map (fun _ => b) l = replicate l.length b :=
map_const l b
@[simp] theorem append_replicate_replicate : replicate n a ++ replicate m a = replicate (n + m) a := by
rw [eq_replicate]
constructor
· simp
· intro b
simp only [mem_append, mem_replicate, ne_eq]
rintro (⟨-, rfl⟩ | ⟨_, rfl⟩) <;> rfl
@[simp] theorem map_replicate : (replicate n a).map f = replicate n (f a) := by
ext1 n
simp only [getElem?_map, getElem?_replicate]
split <;> simp
theorem filter_replicate : (replicate n a).filter p = if p a then replicate n a else [] := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, filter_cons]
split <;> simp_all
@[simp] theorem filter_replicate_of_pos (h : p a) : (replicate n a).filter p = replicate n a := by
simp [filter_replicate, h]
@[simp] theorem filter_replicate_of_neg (h : ¬ p a) : (replicate n a).filter p = [] := by
simp [filter_replicate, h]
theorem filterMap_replicate {f : α → Option β} :
(replicate n a).filterMap f = match f a with | none => [] | .some b => replicate n b := by
induction n with
| zero => split <;> simp
| succ n ih =>
simp only [replicate_succ, filterMap_cons]
split <;> simp_all
-- This is not a useful `simp` lemma because `b` is unknown.
theorem filterMap_replicate_of_some {f : α → Option β} (h : f a = some b) :
(replicate n a).filterMap f = replicate n b := by
simp [filterMap_replicate, h]
@[simp] theorem filterMap_replicate_of_isSome {f : α → Option β} (h : (f a).isSome) :
(replicate n a).filterMap f = replicate n (Option.get _ h) := by
rw [Option.isSome_iff_exists] at h
obtain ⟨b, h⟩ := h
simp [filterMap_replicate, h]
@[simp] theorem filterMap_replicate_of_none {f : α → Option β} (h : f a = none) :
(replicate n a).filterMap f = [] := by
simp [filterMap_replicate, h]
@[simp] theorem join_replicate_nil : (replicate n ([] : List α)).join = [] := by
induction n <;> simp_all [replicate_succ]
@[simp] theorem join_replicate_singleton : (replicate n [a]).join = replicate n a := by
induction n <;> simp_all [replicate_succ]
@[simp] theorem join_replicate_replicate : (replicate n (replicate m a)).join = replicate (n * m) a := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, join_cons, ih, append_replicate_replicate, replicate_inj, or_true,
and_true, add_one_mul, Nat.add_comm]
theorem bind_replicate {β} (f : α → List β) : (replicate n a).bind f = (replicate n (f a)).join := by
induction n with
| zero => simp
| succ n ih => simp only [replicate_succ, bind_cons, ih, join_cons]
@[simp] theorem isEmpty_replicate : (replicate n a).isEmpty = decide (n = 0) := by
cases n <;> simp [replicate_succ]
/-! ### reverse -/
@[simp] theorem length_reverse (as : List α) : (as.reverse).length = as.length := by
induction as with
| nil => rfl
| cons a as ih => simp [ih]
theorem getElem?_reverse' : ∀ {l : List α} (i j), i + j + 1 = length l →
l.reverse[i]? = l[j]?
| [], _, _, _ => rfl
| a::l, i, 0, h => by simp [Nat.succ.injEq] at h; simp [h, getElem?_append_right, Nat.succ.injEq]
| a::l, i, j+1, h => by
have := Nat.succ.inj h; simp at this ⊢
rw [getElem?_append, getElem?_reverse' _ _ this]
rw [length_reverse, ← this]; apply Nat.lt_add_of_pos_right (Nat.succ_pos _)
@[deprecated getElem?_reverse' (since := "2024-06-12")]
theorem get?_reverse' {l : List α} (i j) (h : i + j + 1 = length l) : get? l.reverse i = get? l j := by
simp [getElem?_reverse' _ _ h]
theorem getElem?_reverse {l : List α} {i} (h : i < length l) :
l.reverse[i]? = l[l.length - 1 - i]? :=
getElem?_reverse' _ _ <| by
rw [Nat.add_sub_of_le (Nat.le_sub_one_of_lt h),
Nat.sub_add_cancel (Nat.lt_of_le_of_lt (Nat.zero_le _) h)]
@[deprecated getElem?_reverse (since := "2024-06-12")]
theorem get?_reverse {l : List α} {i} (h : i < length l) :
get? l.reverse i = get? l (l.length - 1 - i) := by
simp [getElem?_reverse h]
theorem reverseAux_reverseAux_nil (as bs : List α) : reverseAux (reverseAux as bs) [] = reverseAux bs as := by
induction as generalizing bs with
| nil => rfl
| cons a as ih => simp [reverseAux, ih]
@[simp] theorem reverse_reverse (as : List α) : as.reverse.reverse = as := by
simp only [reverse]; rw [reverseAux_reverseAux_nil]; rfl
@[simp] theorem getLast?_reverse (l : List α) : l.reverse.getLast? = l.head? := by cases l <;> simp
@[simp] theorem head?_reverse (l : List α) : l.reverse.head? = l.getLast? := by
rw [← getLast?_reverse, reverse_reverse]
@[simp] theorem reverse_append (as bs : List α) : (as ++ bs).reverse = bs.reverse ++ as.reverse := by
induction as <;> simp_all
@[simp] theorem map_reverse (f : α → β) (l : List α) : l.reverse.map f = (l.map f).reverse := by
induction l <;> simp [*]
@[deprecated map_reverse (since := "2024-06-20")]
theorem reverse_map (f : α → β) (l : List α) : (l.map f).reverse = l.reverse.map f := by
simp
@[simp] theorem reverse_eq_nil_iff {xs : List α} : xs.reverse = [] ↔ xs = [] := by
match xs with
| [] => simp
| x :: xs => simp
@[simp] theorem mem_reverseAux {x : α} : ∀ {as bs}, x ∈ reverseAux as bs ↔ x ∈ as x ∈ bs
| [], _ => ⟨.inr, fun | .inr h => h⟩
| a :: _, _ => by rw [reverseAux, mem_cons, or_assoc, or_left_comm, mem_reverseAux, mem_cons]
@[simp] theorem mem_reverse {x : α} {as : List α} : x ∈ reverse as ↔ x ∈ as := by simp [reverse]
theorem reverseAux_eq (as bs : List α) : reverseAux as bs = reverse as ++ bs :=
reverseAux_eq_append ..
@[simp] theorem foldrM_reverse [Monad m] (l : List α) (f : α → β → m β) (b) :
l.reverse.foldrM f b = l.foldlM (fun x y => f y x) b :=
(foldlM_reverse ..).symm.trans <| by simp
@[simp] theorem foldl_reverse (l : List α) (f : β → α → β) (b) :
l.reverse.foldl f b = l.foldr (fun x y => f y x) b := by simp [foldl_eq_foldlM, foldr_eq_foldrM]
@[simp] theorem foldr_reverse (l : List α) (f : α → β → β) (b) :
l.reverse.foldr f b = l.foldl (fun x y => f y x) b :=
(foldl_reverse ..).symm.trans <| by simp
@[simp] theorem reverse_replicate (n) (a : α) : reverse (replicate n a) = replicate n a :=
eq_replicate.2
⟨by rw [length_reverse, length_replicate],
fun b h => eq_of_mem_replicate (mem_reverse.1 h)⟩
/-! ## List membership -/
/-! ### elem / contains -/
@[simp] theorem elem_cons_self [BEq α] [LawfulBEq α] {a : α} : (a::as).elem a = true := by
simp [elem_cons]
@[simp] theorem contains_cons [BEq α] :
(a :: as : List α).contains x = (x == a || as.contains x) := by
simp only [contains, elem]
split <;> simp_all
theorem contains_eq_any_beq [BEq α] (l : List α) (a : α) : l.contains a = l.any (a == ·) := by
induction l with simp | cons b l => cases b == a <;> simp [*]
/-! ## Sublists -/
/-! ### take and drop
Further results on `List.take` and `List.drop`, which rely on stronger automation in `Nat`,
are given in `Init.Data.List.TakeDrop`.
-/
@[simp] theorem take_append_drop : ∀ (n : Nat) (l : List α), take n l ++ drop n l = l
| 0, _ => rfl
| _+1, [] => rfl
| n+1, x :: xs => congrArg (cons x) <| take_append_drop n xs
@[simp] theorem length_drop : ∀ (i : Nat) (l : List α), length (drop i l) = length l - i
| 0, _ => rfl
| succ i, [] => Eq.symm (Nat.zero_sub (succ i))
| succ i, x :: l => calc
length (drop (succ i) (x :: l)) = length l - i := length_drop i l
_ = succ (length l) - succ i := (Nat.succ_sub_succ_eq_sub (length l) i).symm
theorem drop_length_le {l : List α} (h : l.length ≤ i) : drop i l = [] :=
length_eq_zero.1 (length_drop .. ▸ Nat.sub_eq_zero_of_le h)
theorem take_length_le {l : List α} (h : l.length ≤ i) : take i l = l := by
have := take_append_drop i l
rw [drop_length_le h, append_nil] at this; exact this
@[simp] theorem drop_length (l : List α) : drop l.length l = [] := drop_length_le (Nat.le_refl _)
@[simp] theorem take_length (l : List α) : take l.length l = l := take_length_le (Nat.le_refl _)
theorem take_concat_get (l : List α) (i : Nat) (h : i < l.length) :
(l.take i).concat l[i] = l.take (i+1) :=
Eq.symm <| (append_left_inj _).1 <| (take_append_drop (i+1) l).trans <| by
rw [concat_eq_append, append_assoc, singleton_append, get_drop_eq_drop, take_append_drop]
theorem reverse_concat (l : List α) (a : α) : (l.concat a).reverse = a :: l.reverse := by
rw [concat_eq_append, reverse_append]; rfl
abbrev take_succ_cons := @take_cons_succ
theorem take_all_of_le {n} {l : List α} (h : length l ≤ n) : take n l = l :=
take_length_le h
@[simp]
theorem take_left : ∀ l₁ l₂ : List α, take (length l₁) (l₁ ++ l₂) = l₁
| [], _ => rfl
| a :: l₁, l₂ => congrArg (cons a) (take_left l₁ l₂)
theorem take_left' {l₁ l₂ : List α} {n} (h : length l₁ = n) : take n (l₁ ++ l₂) = l₁ := by
rw [← h]; apply take_left
@[simp] theorem map_take (f : α → β) :
∀ (L : List α) (i : Nat), (L.take i).map f = (L.map f).take i
| [], i => by simp
| _, 0 => by simp
| h :: t, n + 1 => by dsimp; rw [map_take f t n]
theorem getElem?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n)[m]? = l[m]? := by
induction n generalizing l m with
| zero =>
exact absurd h (Nat.not_lt_of_le m.zero_le)
| succ _ hn =>
cases l with
| nil => simp only [take_nil]
| cons hd tl =>
cases m
· simp
· simpa using hn (Nat.lt_of_succ_lt_succ h)
@[deprecated getElem?_take (since := "2024-06-12")]
theorem get?_take {l : List α} {n m : Nat} (h : m < n) : (l.take n).get? m = l.get? m := by
simp [getElem?_take, h]
@[simp]
theorem get?_take_of_succ {l : List α} {n : Nat} : (l.take (n + 1))[n]? = l[n]? :=
getElem?_take (Nat.lt_succ_self n)
theorem take_succ {l : List α} {n : Nat} : l.take (n + 1) = l.take n ++ l[n]?.toList := by
induction l generalizing n with
| nil =>
simp only [take_nil, Option.toList, getElem?_nil, append_nil]
| cons hd tl hl =>
cases n
· simp only [take, Option.toList, getElem?_cons_zero, nil_append]
· simp only [take, hl, getElem?_cons_succ, cons_append]
@[simp]
theorem take_eq_nil_iff {l : List α} {k : Nat} : l.take k = [] ↔ l = [] k = 0 := by
cases l <;> cases k <;> simp [Nat.succ_ne_zero]
theorem take_eq_nil_of_eq_nil : ∀ {as : List α} {i}, as = [] → as.take i = []
| _, _, rfl => take_nil
theorem ne_nil_of_take_ne_nil {as : List α} {i : Nat} (h: as.take i ≠ []) : as ≠ [] :=
mt take_eq_nil_of_eq_nil h
theorem dropLast_eq_take (l : List α) : l.dropLast = l.take l.length.pred := by
cases l with
| nil => simp [dropLast]
| cons x l =>
induction l generalizing x with
| nil => simp [dropLast]
| cons hd tl hl => simp [dropLast, hl]
@[simp]
theorem drop_eq_nil_iff_le {l : List α} {k : Nat} : l.drop k = [] ↔ l.length ≤ k := by
refine' ⟨fun h => _, drop_eq_nil_of_le⟩
induction k generalizing l with
| zero =>
simp only [drop] at h
simp [h]
| succ k hk =>
cases l
· simp
· simp only [drop] at h
simpa [Nat.succ_le_succ_iff] using hk h
theorem drop_sizeOf_le [SizeOf α] (l : List α) (n : Nat) : sizeOf (l.drop n) ≤ sizeOf l := by
induction l generalizing n with
| nil => rw [drop_nil]; apply Nat.le_refl
| cons _ _ lih =>
induction n with
| zero => apply Nat.le_refl
| succ n =>
exact Trans.trans (lih _) (Nat.le_add_left _ _)
@[simp] theorem drop_drop (n : Nat) : ∀ (m) (l : List α), drop n (drop m l) = drop (n + m) l
| m, [] => by simp
| 0, l => by simp
| m + 1, a :: l =>
calc
drop n (drop (m + 1) (a :: l)) = drop n (drop m l) := rfl
_ = drop (n + m) l := drop_drop n m l
_ = drop (n + (m + 1)) (a :: l) := rfl
theorem take_drop : ∀ (m n : Nat) (l : List α), take n (drop m l) = drop m (take (m + n) l)
| 0, _, _ => by simp
| _, _, [] => by simp
| _+1, _, _ :: _ => by simpa [Nat.succ_add, take_succ_cons, drop_succ_cons] using take_drop ..
@[simp] theorem map_drop (f : α → β) :
∀ (L : List α) (i : Nat), (L.drop i).map f = (L.map f).drop i
| [], i => by simp
| L, 0 => by simp
| h :: t, n + 1 => by
dsimp
rw [map_drop f t]
@[simp]
theorem getElem_cons_drop : ∀ (l : List α) (i : Nat) (h : i < l.length),
l[i] :: drop (i + 1) l = drop i l
| _::_, 0, _ => rfl
| _::_, i+1, _ => getElem_cons_drop _ i _
@[deprecated getElem_cons_drop (since := "2024-06-12")]
theorem get_cons_drop (l : List α) (i) : get l i :: drop (i + 1) l = drop i l := by
simp
theorem drop_eq_getElem_cons {n} {l : List α} (h) : drop n l = l[n] :: drop (n + 1) l :=
(getElem_cons_drop _ n h).symm
@[deprecated drop_eq_getElem_cons (since := "2024-06-12")]
theorem drop_eq_get_cons {n} {l : List α} (h) : drop n l = get l ⟨n, h⟩ :: drop (n + 1) l := by
simp [drop_eq_getElem_cons]
theorem drop_eq_nil_of_eq_nil : ∀ {as : List α} {i}, as = [] → as.drop i = []
| _, _, rfl => drop_nil
theorem ne_nil_of_drop_ne_nil {as : List α} {i : Nat} (h: as.drop i ≠ []) : as ≠ [] :=
mt drop_eq_nil_of_eq_nil h
@[deprecated drop_drop (since := "2024-06-15")]
theorem drop_add (m n) (l : List α) : drop (m + n) l = drop m (drop n l) := by
simp [drop_drop]
@[simp]
theorem drop_left : ∀ l₁ l₂ : List α, drop (length l₁) (l₁ ++ l₂) = l₂
| [], _ => rfl
| _ :: l₁, l₂ => drop_left l₁ l₂
theorem drop_left' {l₁ l₂ : List α} {n} (h : length l₁ = n) : drop n (l₁ ++ l₂) = l₂ := by
rw [← h]; apply drop_left
/-! ### takeWhile and dropWhile -/
theorem takeWhile_cons (p : α → Bool) (a : α) (l : List α) :
(a :: l).takeWhile p = if p a then a :: l.takeWhile p else [] := by
simp only [takeWhile]
by_cases h: p a <;> simp [h]
theorem dropWhile_cons :
(x :: xs : List α).dropWhile p = if p x then xs.dropWhile p else x :: xs := by
split <;> simp_all [dropWhile]
theorem takeWhile_map (f : α → β) (p : β → Bool) (l : List α) :
(l.map f).takeWhile p = (l.takeWhile (p ∘ f)).map f := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [map_cons, takeWhile_cons]
split <;> simp_all
theorem dropWhile_map (f : α → β) (p : β → Bool) (l : List α) :
(l.map f).dropWhile p = (l.dropWhile (p ∘ f)).map f := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [map_cons, dropWhile_cons]
split <;> simp_all
@[simp] theorem takeWhile_append_dropWhile (p : α → Bool) :
∀ (l : List α), takeWhile p l ++ dropWhile p l = l
| [] => rfl
| x :: xs => by simp [takeWhile, dropWhile]; cases p x <;> simp [takeWhile_append_dropWhile p xs]
theorem dropWhile_append {xs ys : List α} :
(xs ++ ys).dropWhile p =
if (xs.dropWhile p).isEmpty then ys.dropWhile p else xs.dropWhile p ++ ys := by
induction xs with
| nil => simp
| cons h t ih =>
simp only [cons_append, dropWhile_cons]
split <;> simp_all
@[simp] theorem takeWhile_replicate_eq_filter (p : α → Bool) :
(replicate n a).takeWhile p = (replicate n a).filter p := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, takeWhile_cons]
split <;> simp_all
theorem takeWhile_replicate (p : α → Bool) :
(replicate n a).takeWhile p = if p a then replicate n a else [] := by
rw [takeWhile_replicate_eq_filter, filter_replicate]
@[simp] theorem dropWhile_replicate_eq_filter_not (p : α → Bool) :
(replicate n a).dropWhile p = (replicate n a).filter (fun a => !p a) := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, dropWhile_cons]
split <;> simp_all
theorem dropWhile_replicate (p : α → Bool) :
(replicate n a).dropWhile p = if p a then [] else replicate n a := by
simp only [dropWhile_replicate_eq_filter_not, filter_replicate]
split <;> simp_all
/-! ### partition -/
@[simp] theorem partition_eq_filter_filter (p : α → Bool) (l : List α) :
partition p l = (filter p l, filter (not ∘ p) l) := by simp [partition, aux] where
aux : ∀ l {as bs}, partition.loop p l (as, bs) =
(as.reverse ++ filter p l, bs.reverse ++ filter (not ∘ p) l)
| [] => by simp [partition.loop, filter]
| a :: l => by cases pa : p a <;> simp [partition.loop, pa, aux, filter, append_assoc]
/-! ### dropLast
`dropLast` is the specification for `Array.pop`, so theorems about `List.dropLast`
are often used for theorems about `Array.pop`.
-/
@[simp] theorem length_dropLast : ∀ (xs : List α), xs.dropLast.length = xs.length - 1
| [] => rfl
| x::xs => by simp
@[simp] theorem getElem_dropLast : ∀ (xs : List α) (i : Nat) (h : i < xs.dropLast.length),
xs.dropLast[i] = xs[i]'(Nat.lt_of_lt_of_le h (length_dropLast .. ▸ Nat.pred_le _))
| _::_::_, 0, _ => rfl
| _::_::_, i+1, _ => getElem_dropLast _ i _
@[deprecated getElem_dropLast (since := "2024-06-12")]
theorem get_dropLast (xs : List α) (i : Fin xs.dropLast.length) :
xs.dropLast.get i = xs.get ⟨i, Nat.lt_of_lt_of_le i.isLt (length_dropLast .. ▸ Nat.pred_le _)⟩ := by
simp
theorem dropLast_cons_of_ne_nil {α : Type u} {x : α}
{l : List α} (h : l ≠ []) : (x :: l).dropLast = x :: l.dropLast := by
simp [dropLast, h]
@[simp] theorem dropLast_append_of_ne_nil {α : Type u} {l : List α} :
∀ (l' : List α) (_ : l ≠ []), (l' ++ l).dropLast = l' ++ l.dropLast
| [], _ => by simp only [nil_append]
| a :: l', h => by
rw [cons_append, dropLast, dropLast_append_of_ne_nil l' h, cons_append]
simp [h]
theorem dropLast_append_cons : dropLast (l₁ ++ b::l₂) = l₁ ++ dropLast (b::l₂) := by
simp only [ne_eq, not_false_eq_true, dropLast_append_of_ne_nil]
@[simp 1100] theorem dropLast_concat : dropLast (l₁ ++ [b]) = l₁ := by simp
@[simp] theorem map_dropLast (f : α → β) (l : List α) : l.dropLast.map f = (l.map f).dropLast := by
induction l with
| nil => rfl
| cons x xs ih => cases xs <;> simp [ih]
@[simp] theorem dropLast_replicate (n) (a : α) : dropLast (replicate n a) = replicate (n - 1) a := by
match n with
| 0 => simp
| 1 => simp [replicate_succ]
| n+2 =>
rw [replicate_succ, dropLast_cons_of_ne_nil, dropLast_replicate]
· simp [replicate_succ]
· simp
@[simp] theorem dropLast_cons_self_replicate (n) (a : α) :
dropLast (a :: replicate n a) = replicate n a := by
rw [← replicate_succ, dropLast_replicate, Nat.add_sub_cancel]
/-! ### isPrefixOf -/
section isPrefixOf
variable [BEq α]
@[simp] theorem isPrefixOf_cons₂_self [LawfulBEq α] {a : α} :
isPrefixOf (a::as) (a::bs) = isPrefixOf as bs := by simp [isPrefixOf_cons₂]
@[simp] theorem isPrefixOf_length_pos_nil {L : List α} (h : 0 < L.length) : isPrefixOf L [] = false := by
cases L <;> simp_all [isPrefixOf]
@[simp] theorem isPrefixOf_replicate {a : α} :
isPrefixOf l (replicate n a) = (decide (l.length ≤ n) && l.all (· == a)) := by
induction l generalizing n with
| nil => simp
| cons h t ih =>
cases n
· simp
· simp [replicate_succ, isPrefixOf_cons₂, ih, Nat.succ_le_succ_iff, Bool.and_left_comm]
end isPrefixOf
/-! ### isSuffixOf -/
section isSuffixOf
variable [BEq α]
@[simp] theorem isSuffixOf_cons_nil : isSuffixOf (a::as) ([] : List α) = false := by
simp [isSuffixOf]
@[simp] theorem isSuffixOf_replicate {a : α} :
isSuffixOf l (replicate n a) = (decide (l.length ≤ n) && l.all (· == a)) := by
simp [isSuffixOf, all_eq]
end isSuffixOf
/-! ### rotateLeft -/
@[simp] theorem rotateLeft_zero (l : List α) : rotateLeft l 0 = l := by
simp [rotateLeft]
-- TODO Batteries defines its own `getElem?_rotate`, which we need to adapt.
-- TODO Prove `map_rotateLeft`, using `ext` and `getElem?_rotateLeft`.
/-! ### rotateRight -/
@[simp] theorem rotateRight_zero (l : List α) : rotateRight l 0 = l := by
simp [rotateRight]
-- TODO Batteries defines its own `getElem?_rotate`, which we need to adapt.
-- TODO Prove `map_rotateRight`, using `ext` and `getElem?_rotateRight`.
/-! ## Manipulating elements -/
/-! ### replace -/
section replace
variable [BEq α]
@[simp] theorem replace_cons_self [LawfulBEq α] {a : α} : (a::as).replace a b = b::as := by
simp [replace_cons]
@[simp] theorem replace_of_not_mem {l : List α} (h : !l.elem a) : l.replace a b = l := by
induction l <;> simp_all [replace_cons]
@[simp] theorem replace_replicate_self [LawfulBEq α] {a : α} (h : 0 < n) :
(replicate n a).replace a b = b :: replicate (n - 1) a := by
cases n <;> simp_all [replicate_succ, replace_cons]
@[simp] theorem replace_replicate_ne {a b c : α} (h : !b == a) :
(replicate n a).replace b c = replicate n a := by
rw [replace_of_not_mem]
simp_all
end replace
/-! ### insert -/
section insert
variable [BEq α] [LawfulBEq α]
@[simp] theorem insert_nil (a : α) : [].insert a = [a] := by
simp [List.insert]
@[simp] theorem insert_of_mem {l : List α} (h : a ∈ l) : l.insert a = l := by
simp [List.insert, h]
@[simp] theorem insert_of_not_mem {l : List α} (h : a ∉ l) : l.insert a = a :: l := by
simp [List.insert, h]
@[simp] theorem mem_insert_iff {l : List α} : a ∈ l.insert b ↔ a = b a ∈ l := by
if h : b ∈ l then
rw [insert_of_mem h]
constructor; {apply Or.inr}
intro
| Or.inl h' => rw [h']; exact h
| Or.inr h' => exact h'
else rw [insert_of_not_mem h, mem_cons]
@[simp 1100] theorem mem_insert_self (a : α) (l : List α) : a ∈ l.insert a :=
mem_insert_iff.2 (Or.inl rfl)
theorem mem_insert_of_mem {l : List α} (h : a ∈ l) : a ∈ l.insert b :=
mem_insert_iff.2 (Or.inr h)
theorem eq_or_mem_of_mem_insert {l : List α} (h : a ∈ l.insert b) : a = b a ∈ l :=
mem_insert_iff.1 h
@[simp] theorem length_insert_of_mem {l : List α} (h : a ∈ l) :
length (l.insert a) = length l := by rw [insert_of_mem h]
@[simp] theorem length_insert_of_not_mem {l : List α} (h : a ∉ l) :
length (l.insert a) = length l + 1 := by rw [insert_of_not_mem h]; rfl
@[simp] theorem insert_replicate_self {a : α} (h : 0 < n) : (replicate n a).insert a = replicate n a := by
cases n <;> simp_all
@[simp] theorem insert_replicate_ne {a b : α} (h : !b == a) :
(replicate n a).insert b = b :: replicate n a := by
rw [insert_of_not_mem]
simp_all
end insert
/-! ### erase -/
section erase
variable [BEq α]
@[simp] theorem erase_cons_head [LawfulBEq α] (a : α) (l : List α) : (a :: l).erase a = l := by
simp [erase_cons]
@[simp] theorem erase_cons_tail {a b : α} (l : List α) (h : ¬(b == a)) :
(b :: l).erase a = b :: l.erase a := by simp only [erase_cons, if_neg h]
theorem erase_of_not_mem [LawfulBEq α] {a : α} : ∀ {l : List α}, a ∉ l → l.erase a = l
| [], _ => rfl
| b :: l, h => by
rw [mem_cons, not_or] at h
simp only [erase_cons, if_neg, erase_of_not_mem h.2, beq_iff_eq, Ne.symm h.1, not_false_eq_true]
@[simp] theorem erase_replicate_self [LawfulBEq α] {a : α} :
(replicate n a).erase a = replicate (n - 1) a := by
cases n <;> simp [replicate_succ]
@[simp] theorem erase_replicate_ne [LawfulBEq α] {a b : α} (h : !b == a) :
(replicate n a).erase b = replicate n a := by
rw [erase_of_not_mem]
simp_all
end erase
/-! ### find? -/
@[simp] theorem find?_cons_of_pos (l) (h : p a) : find? p (a :: l) = some a := by
simp [find?, h]
@[simp] theorem find?_cons_of_neg (l) (h : ¬p a) : find? p (a :: l) = find? p l := by
simp [find?, h]
@[simp] theorem find?_eq_none : find? p l = none ↔ ∀ x ∈ l, ¬ p x := by
induction l <;> simp [find?_cons]; split <;> simp [*]
theorem find?_some : ∀ {l}, find? p l = some a → p a
| b :: l, H => by
by_cases h : p b <;> simp [find?, h] at H
· exact H ▸ h
· exact find?_some H
@[simp] theorem mem_of_find?_eq_some : ∀ {l}, find? p l = some a → a ∈ l
| b :: l, H => by
by_cases h : p b <;> simp [find?, h] at H
· exact H ▸ .head _
· exact .tail _ (mem_of_find?_eq_some H)
@[simp] theorem find?_map (f : β → α) (l : List β) : find? p (l.map f) = (l.find? (p ∘ f)).map f := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, find?]
by_cases h : p (f x) <;> simp [h, ih]
theorem find?_replicate : find? p (replicate n a) = if n = 0 then none else if p a then some a else none := by
cases n
· simp
· by_cases p a <;> simp_all [replicate_succ]
@[simp] theorem find?_replicate_of_length_pos (h : 0 < n) : find? p (replicate n a) = if p a then some a else none := by
simp [find?_replicate, Nat.ne_of_gt h]
@[simp] theorem find?_replicate_of_pos (h : p a) : find? p (replicate n a) = if n = 0 then none else some a := by
simp [find?_replicate, h]
@[simp] theorem find?_replicate_of_neg (h : ¬ p a) : find? p (replicate n a) = none := by
simp [find?_replicate, h]
/-! ### findSome? -/
@[simp] theorem findSome?_cons_of_isSome (l) (h : (f a).isSome) : findSome? f (a :: l) = f a := by
simp only [findSome?]
split <;> simp_all
@[simp] theorem findSome?_cons_of_isNone (l) (h : (f a).isNone) : findSome? f (a :: l) = findSome? f l := by
simp only [findSome?]
split <;> simp_all
theorem exists_of_findSome?_eq_some {l : List α} {f : α → Option β} (w : l.findSome? f = some b) :
∃ a, a ∈ l ∧ f a = b := by
induction l with
| nil => simp_all
| cons h l ih =>
simp_all only [findSome?_cons, mem_cons, exists_eq_or_imp]
split at w <;> simp_all
@[simp] theorem findSome?_map (f : β → γ) (l : List β) : findSome? p (l.map f) = l.findSome? (p ∘ f) := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, findSome?]
split <;> simp_all
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, findSome?_cons]
split <;> simp_all
@[simp] theorem findSome?_replicate_of_pos (h : 0 < n) : findSome? f (replicate n a) = f a := by
simp [findSome?_replicate, Nat.ne_of_gt h]
@[simp] theorem find?_replicate_of_isSome (h : (f a).isSome) : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [findSome?_replicate, h]
@[simp] theorem find?_replicate_of_isNone (h : (f a).isNone) : findSome? f (replicate n a) = none := by
rw [Option.isNone_iff_eq_none] at h
simp [findSome?_replicate, h]
/-! ### lookup -/
section lookup
variable [BEq α] [LawfulBEq α]
@[simp] theorem lookup_cons_self {k : α} : ((k,b)::es).lookup k = some b := by
simp [lookup_cons]
theorem lookup_replicate {k : α} :
(replicate n (a,b)).lookup k = if n = 0 then none else if k == a then some b else none := by
induction n with
| zero => simp
| succ n ih =>
simp only [replicate_succ, lookup_cons]
split <;> simp_all
theorem lookup_replicate_of_pos {k : α} (h : 0 < n) :
(replicate n (a,b)).lookup k = if k == a then some b else none := by
simp [lookup_replicate, Nat.ne_of_gt h]
theorem lookup_replicate_self {a : α} :
(replicate n (a, b)).lookup a = if n = 0 then none else some b := by
simp [lookup_replicate]
@[simp] theorem lookup_replicate_self_of_pos {a : α} (h : 0 < n) :
(replicate n (a, b)).lookup a = some b := by
simp [lookup_replicate_self, Nat.ne_of_gt h]
@[simp] theorem lookup_replicate_ne {k : α} (h : !k == a) :
(replicate n (a,b)).lookup k = none := by
simp_all [lookup_replicate]
end lookup
/-! ## Logic -/
/-! ### any / all -/
theorem not_any_eq_all_not (l : List α) (p : α → Bool) : (!l.any p) = l.all fun a => !p a := by
induction l with simp | cons _ _ ih => rw [ih]
theorem not_all_eq_any_not (l : List α) (p : α → Bool) : (!l.all p) = l.any fun a => !p a := by
induction l with simp | cons _ _ ih => rw [ih]
theorem and_any_distrib_left (l : List α) (p : α → Bool) (q : Bool) :
(q && l.any p) = l.any fun a => q && p a := by
induction l with simp | cons _ _ ih => rw [Bool.and_or_distrib_left, ih]
theorem and_any_distrib_right (l : List α) (p : α → Bool) (q : Bool) :
(l.any p && q) = l.any fun a => p a && q := by
induction l with simp | cons _ _ ih => rw [Bool.and_or_distrib_right, ih]
theorem or_all_distrib_left (l : List α) (p : α → Bool) (q : Bool) :
(q || l.all p) = l.all fun a => q || p a := by
induction l with simp | cons _ _ ih => rw [Bool.or_and_distrib_left, ih]
theorem or_all_distrib_right (l : List α) (p : α → Bool) (q : Bool) :
(l.all p || q) = l.all fun a => p a || q := by
induction l with simp | cons _ _ ih => rw [Bool.or_and_distrib_right, ih]
theorem any_eq_not_all_not (l : List α) (p : α → Bool) : l.any p = !l.all (!p .) := by
simp only [not_all_eq_any_not, Bool.not_not]
theorem all_eq_not_any_not (l : List α) (p : α → Bool) : l.all p = !l.any (!p .) := by
simp only [not_any_eq_all_not, Bool.not_not]
theorem any_map (f : α → β) (l : List α) (p : β → Bool) : (l.map f).any p = l.any (p ∘ f) := by
induction l with simp | cons _ _ ih => rw [ih]
theorem all_map (f : α → β) (l : List α) (p : β → Bool) : (l.map f).all p = l.all (p ∘ f) := by
induction l with simp | cons _ _ ih => rw [ih]
/-! ## Zippers -/
/-! ### zip -/
theorem zip_map (f : αγ) (g : β → δ) :
∀ (l₁ : List α) (l₂ : List β), zip (l₁.map f) (l₂.map g) = (zip l₁ l₂).map (Prod.map f g)
| [], l₂ => rfl
| l₁, [] => by simp only [map, zip_nil_right]
| a :: l₁, b :: l₂ => by
simp only [map, zip_cons_cons, zip_map, Prod.map]; constructor
theorem zip_map_left (f : αγ) (l₁ : List α) (l₂ : List β) :
zip (l₁.map f) l₂ = (zip l₁ l₂).map (Prod.map f id) := by rw [← zip_map, map_id]
theorem zip_map_right (f : β → γ) (l₁ : List α) (l₂ : List β) :
zip l₁ (l₂.map f) = (zip l₁ l₂).map (Prod.map id f) := by rw [← zip_map, map_id]
theorem zip_append :
∀ {l₁ r₁ : List α} {l₂ r₂ : List β} (_h : length l₁ = length l₂),
zip (l₁ ++ r₁) (l₂ ++ r₂) = zip l₁ l₂ ++ zip r₁ r₂
| [], r₁, l₂, r₂, h => by simp only [eq_nil_of_length_eq_zero h.symm]; rfl
| l₁, r₁, [], r₂, h => by simp only [eq_nil_of_length_eq_zero h]; rfl
| a :: l₁, r₁, b :: l₂, r₂, h => by
simp only [cons_append, zip_cons_cons, zip_append (Nat.succ.inj h)]
theorem zip_map' (f : α → β) (g : αγ) :
∀ l : List α, zip (l.map f) (l.map g) = l.map fun a => (f a, g a)
| [] => rfl
| a :: l => by simp only [map, zip_cons_cons, zip_map']
theorem of_mem_zip {a b} : ∀ {l₁ : List α} {l₂ : List β}, (a, b) ∈ zip l₁ l₂ → a ∈ l₁ ∧ b ∈ l₂
| _ :: l₁, _ :: l₂, h => by
cases h
case head => simp
case tail h =>
· have := of_mem_zip h
exact ⟨Mem.tail _ this.1, Mem.tail _ this.2⟩
theorem map_fst_zip :
∀ (l₁ : List α) (l₂ : List β), l₁.length ≤ l₂.length → map Prod.fst (zip l₁ l₂) = l₁
| [], bs, _ => rfl
| _ :: as, _ :: bs, h => by
simp [Nat.succ_le_succ_iff] at h
show _ :: map Prod.fst (zip as bs) = _ :: as
rw [map_fst_zip as bs h]
| a :: as, [], h => by simp at h
theorem map_snd_zip :
∀ (l₁ : List α) (l₂ : List β), l₂.length ≤ l₁.length → map Prod.snd (zip l₁ l₂) = l₂
| _, [], _ => by
rw [zip_nil_right]
rfl
| [], b :: bs, h => by simp at h
| a :: as, b :: bs, h => by
simp [Nat.succ_le_succ_iff] at h
show _ :: map Prod.snd (zip as bs) = _ :: bs
rw [map_snd_zip as bs h]
/-- See also `List.zip_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
@[simp] theorem zip_replicate' {a : α} {b : β} {n : Nat} :
zip (replicate n a) (replicate n b) = replicate n (a, b) := by
induction n with
| zero => rfl
| succ n ih => simp [replicate_succ, ih]
/-! ### zipWith -/
theorem getElem?_zipWith {f : α → β → γ} {i : Nat} :
(List.zipWith f as bs)[i]? = match as[i]?, bs[i]? with
| some a, some b => some (f a b) | _, _ => none := by
induction as generalizing bs i with
| nil => cases bs with
| nil => simp
| cons b bs => simp
| cons a as aih => cases bs with
| nil => simp
| cons b bs => cases i <;> simp_all
@[deprecated getElem?_zipWith (since := "2024-06-12")]
theorem get?_zipWith {f : α → β → γ} :
(List.zipWith f as bs).get? i = match as.get? i, bs.get? i with
| some a, some b => some (f a b) | _, _ => none := by
simp [getElem?_zipWith]
set_option linter.deprecated false in
@[deprecated getElem?_zipWith (since := "2024-06-07")] abbrev zipWith_get? := @get?_zipWith
@[simp]
theorem zipWith_map {μ} (f : γ → δ → μ) (g : αγ) (h : β → δ) (l₁ : List α) (l₂ : List β) :
zipWith f (l₁.map g) (l₂.map h) = zipWith (fun a b => f (g a) (h b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWith_map_left (l₁ : List α) (l₂ : List β) (f : αα') (g : α' → β → γ) :
zipWith g (l₁.map f) l₂ = zipWith (fun a b => g (f a) b) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWith_map_right (l₁ : List α) (l₂ : List β) (f : β → β') (g : α → β' → γ) :
zipWith g l₁ (l₂.map f) = zipWith (fun a b => g a (f b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWith_foldr_eq_zip_foldr {f : α → β → γ} (i : δ):
(zipWith f l₁ l₂).foldr g i = (zip l₁ l₂).foldr (fun p r => g (f p.1 p.2) r) i := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWith_foldl_eq_zip_foldl {f : α → β → γ} (i : δ):
(zipWith f l₁ l₂).foldl g i = (zip l₁ l₂).foldl (fun r p => g r (f p.1 p.2)) i := by
induction l₁ generalizing i l₂ <;> cases l₂ <;> simp_all
@[simp]
theorem zipWith_eq_nil_iff {f : α → β → γ} {l l'} : zipWith f l l' = [] ↔ l = [] l' = [] := by
cases l <;> cases l' <;> simp
theorem map_zipWith {δ : Type _} (f : α → β) (g : γ → δ → α) (l : List γ) (l' : List δ) :
map f (zipWith g l l') = zipWith (fun x y => f (g x y)) l l' := by
induction l generalizing l' with
| nil => simp
| cons hd tl hl =>
· cases l'
· simp
· simp [hl]
theorem zipWith_distrib_take : (zipWith f l l').take n = zipWith f (l.take n) (l'.take n) := by
induction l generalizing l' n with
| nil => simp
| cons hd tl hl =>
cases l'
· simp
· cases n
· simp
· simp [hl]
theorem zipWith_distrib_drop : (zipWith f l l').drop n = zipWith f (l.drop n) (l'.drop n) := by
induction l generalizing l' n with
| nil => simp
| cons hd tl hl =>
· cases l'
· simp
· cases n
· simp
· simp [hl]
theorem zipWith_append (f : α → β → γ) (l la : List α) (l' lb : List β)
(h : l.length = l'.length) :
zipWith f (l ++ la) (l' ++ lb) = zipWith f l l' ++ zipWith f la lb := by
induction l generalizing l' with
| nil =>
have : l' = [] := eq_nil_of_length_eq_zero (by simpa using h.symm)
simp [this]
| cons hl tl ih =>
cases l' with
| nil => simp at h
| cons head tail =>
simp only [length_cons, Nat.succ.injEq] at h
simp [ih _ h]
/-- See also `List.zipWith_replicate` in `Init.Data.List.TakeDrop` for a generalization with different lengths. -/
@[simp] theorem zipWith_replicate' {a : α} {b : β} {n : Nat} :
zipWith f (replicate n a) (replicate n b) = replicate n (f a b) := by
induction n with
| zero => rfl
| succ n ih => simp [replicate_succ, ih]
/-! ### zipWithAll -/
theorem getElem?_zipWithAll {f : Option α → Option β → γ} {i : Nat} :
(zipWithAll f as bs)[i]? = match as[i]?, bs[i]? with
| none, none => .none | a?, b? => some (f a? b?) := by
induction as generalizing bs i with
| nil => induction bs generalizing i with
| nil => simp
| cons b bs bih => cases i <;> simp_all
| cons a as aih => cases bs with
| nil =>
specialize @aih []
cases i <;> simp_all
| cons b bs => cases i <;> simp_all
@[deprecated getElem?_zipWithAll (since := "2024-06-12")]
theorem get?_zipWithAll {f : Option α → Option β → γ} :
(zipWithAll f as bs).get? i = match as.get? i, bs.get? i with
| none, none => .none | a?, b? => some (f a? b?) := by
simp [getElem?_zipWithAll]
set_option linter.deprecated false in
@[deprecated getElem?_zipWithAll (since := "2024-06-07")] abbrev zipWithAll_get? := @get?_zipWithAll
theorem zipWithAll_map {μ} (f : Option γ → Option δ → μ) (g : αγ) (h : β → δ) (l₁ : List α) (l₂ : List β) :
zipWithAll f (l₁.map g) (l₂.map h) = zipWithAll (fun a b => f (g <$> a) (h <$> b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWithAll_map_left (l₁ : List α) (l₂ : List β) (f : αα') (g : Option α' → Option β → γ) :
zipWithAll g (l₁.map f) l₂ = zipWithAll (fun a b => g (f <$> a) b) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem zipWithAll_map_right (l₁ : List α) (l₂ : List β) (f : β → β') (g : Option α → Option β' → γ) :
zipWithAll g l₁ (l₂.map f) = zipWithAll (fun a b => g a (f <$> b)) l₁ l₂ := by
induction l₁ generalizing l₂ <;> cases l₂ <;> simp_all
theorem map_zipWithAll {δ : Type _} (f : α → β) (g : Option γ → Option δ → α) (l : List γ) (l' : List δ) :
map f (zipWithAll g l l') = zipWithAll (fun x y => f (g x y)) l l' := by
induction l generalizing l' with
| nil => simp
| cons hd tl hl =>
cases l' <;> simp_all
@[simp] theorem zipWithAll_replicate {a : α} {b : β} {n : Nat} :
zipWithAll f (replicate n a) (replicate n b) = replicate n (f a b) := by
induction n with
| zero => rfl
| succ n ih => simp [replicate_succ, ih]
/-! ### unzip -/
@[simp] theorem unzip_replicate {n : Nat} {a : α} {b : β} :
unzip (replicate n (a, b)) = (replicate n a, replicate n b) := by
induction n with
| zero => rfl
| succ n ih => simp [replicate_succ, ih]
/-! ## Ranges and enumeration -/
/-! ### enumFrom -/
@[simp] theorem enumFrom_length : ∀ {n} {l : List α}, (enumFrom n l).length = l.length
| _, [] => rfl
| _, _ :: _ => congrArg Nat.succ enumFrom_length
theorem map_enumFrom (f : α → β) (n : Nat) (l : List α) :
map (Prod.map id f) (enumFrom n l) = enumFrom n (map f l) := by
induction l generalizing n <;> simp_all
/-! ### enum -/
@[simp] theorem enum_length : (enum l).length = l.length :=
enumFrom_length
theorem enum_cons : (a::as).enum = (0, a) :: as.enumFrom 1 := rfl
theorem map_enum (f : α → β) (l : List α) : map (Prod.map id f) (enum l) = enum (map f l) :=
map_enumFrom f 0 l
/-! ## Minima and maxima -/
/-! ### minimum? -/
@[simp] theorem minimum?_nil [Min α] : ([] : List α).minimum? = none := rfl
-- We don't put `@[simp]` on `minimum?_cons`,
-- because the definition in terms of `foldl` is not useful for proofs.
theorem minimum?_cons [Min α] {xs : List α} : (x :: xs).minimum? = foldl min x xs := rfl
@[simp] theorem minimum?_eq_none_iff {xs : List α} [Min α] : xs.minimum? = none ↔ xs = [] := by
cases xs <;> simp [minimum?]
theorem minimum?_mem [Min α] (min_eq_or : ∀ a b : α, min a b = a min a b = b) :
{xs : List α} → xs.minimum? = some a → a ∈ xs := by
intro xs
match xs with
| nil => simp
| x :: xs =>
simp only [minimum?_cons, Option.some.injEq, List.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 min_eq_or x y with | _ q => simp [p, q]
| inr p => simp [p, mem_cons]
theorem le_minimum?_iff [Min α] [LE α]
(le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) :
{xs : List α} → xs.minimum? = some a → ∀ x, x ≤ a ↔ ∀ b, b ∈ xs → x ≤ b
| nil => by simp
| cons x xs => by
rw [minimum?]
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]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `min_eq_or`,
-- and `le_min_iff`.
theorem minimum?_eq_some_iff [Min α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
(le_refl : ∀ a : α, a ≤ a)
(min_eq_or : ∀ a b : α, min a b = a min a b = b)
(le_min_iff : ∀ a b c : α, a ≤ min b c ↔ a ≤ b ∧ a ≤ c) {xs : List α} :
xs.minimum? = some a ↔ a ∈ xs ∧ ∀ b, b ∈ xs → a ≤ b := by
refine ⟨fun h => ⟨minimum?_mem min_eq_or h, (le_minimum?_iff le_min_iff h _).1 (le_refl _)⟩, ?_⟩
intro ⟨h₁, h₂⟩
cases xs with
| nil => simp at h₁
| cons x xs =>
exact congrArg some <| anti.1
((le_minimum?_iff le_min_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
(h₂ _ (minimum?_mem min_eq_or (xs := x::xs) rfl))
theorem minimum?_replicate [Min α] {n : Nat} {a : α} (w : min a a = a) :
(replicate n a).minimum? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, minimum?_cons]
@[simp] theorem minimum?_replicate_of_pos [Min α] {n : Nat} {a : α} (w : min a a = a) (h : 0 < n) :
(replicate n a).minimum? = some a := by
simp [minimum?_replicate, Nat.ne_of_gt h, w]
/-! ### maximum? -/
@[simp] theorem maximum?_nil [Max α] : ([] : List α).maximum? = none := rfl
-- We don't put `@[simp]` on `maximum?_cons`,
-- because the definition in terms of `foldl` is not useful for proofs.
theorem maximum?_cons [Max α] {xs : List α} : (x :: xs).maximum? = foldl max x xs := rfl
@[simp] theorem maximum?_eq_none_iff {xs : List α} [Max α] : xs.maximum? = none ↔ xs = [] := by
cases xs <;> simp [maximum?]
theorem maximum?_mem [Max α] (min_eq_or : ∀ a b : α, max a b = a max a b = b) :
{xs : List α} → xs.maximum? = some a → a ∈ xs
| nil => by simp
| cons x xs => by
rw [maximum?]; rintro ⟨⟩
induction xs generalizing x with simp at *
| cons y xs ih =>
rcases ih (max x y) with h | h <;> simp [h]
simp [← or_assoc, min_eq_or x y]
theorem maximum?_le_iff [Max α] [LE α]
(max_le_iff : ∀ a b c : α, max b c ≤ a ↔ b ≤ a ∧ c ≤ a) :
{xs : List α} → xs.maximum? = some a → ∀ x, a ≤ x ↔ ∀ b ∈ xs, b ≤ x
| nil => by simp
| cons x xs => by
rw [maximum?]; rintro ⟨⟩ y
induction xs generalizing x with
| nil => simp
| cons y xs ih => simp [ih, max_le_iff, and_assoc]
-- This could be refactored by designing appropriate typeclasses to replace `le_refl`, `max_eq_or`,
-- and `le_min_iff`.
theorem maximum?_eq_some_iff [Max α] [LE α] [anti : Antisymm ((· : α) ≤ ·)]
(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.maximum? = some a ↔ a ∈ xs ∧ ∀ b ∈ xs, b ≤ a := by
refine ⟨fun h => ⟨maximum?_mem max_eq_or h, (maximum?_le_iff max_le_iff h _).1 (le_refl _)⟩, ?_⟩
intro ⟨h₁, h₂⟩
cases xs with
| nil => simp at h₁
| cons x xs =>
exact congrArg some <| anti.1
(h₂ _ (maximum?_mem max_eq_or (xs := x::xs) rfl))
((maximum?_le_iff max_le_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
theorem maximum?_replicate [Max α] {n : Nat} {a : α} (w : max a a = a) :
(replicate n a).maximum? = if n = 0 then none else some a := by
induction n with
| zero => rfl
| succ n ih => cases n <;> simp_all [replicate_succ, maximum?_cons]
@[simp] theorem maximum?_replicate_of_pos [Max α] {n : Nat} {a : α} (w : max a a = a) (h : 0 < n) :
(replicate n a).maximum? = some a := by
simp [maximum?_replicate, Nat.ne_of_gt h, w]
/-! ## Monadic operations -/
/-! ### mapM -/
/-- Alternate (non-tail-recursive) form of mapM for proofs. -/
def mapM' [Monad m] (f : α → m β) : List α → m (List β)
| [] => pure []
| a :: l => return (← f a) :: (← l.mapM' f)
@[simp] theorem mapM'_nil [Monad m] {f : α → m β} : mapM' f [] = pure [] := rfl
@[simp] theorem mapM'_cons [Monad m] {f : α → m β} :
mapM' f (a :: l) = return ((← f a) :: (← l.mapM' f)) :=
rfl
theorem mapM'_eq_mapM [Monad m] [LawfulMonad m] (f : α → m β) (l : List α) :
mapM' f l = mapM f l := by simp [go, mapM] where
go : ∀ l acc, mapM.loop f l acc = return acc.reverse ++ (← mapM' f l)
| [], acc => by simp [mapM.loop, mapM']
| a::l, acc => by simp [go l, mapM.loop, mapM']
@[simp] theorem mapM_nil [Monad m] (f : α → m β) : [].mapM f = pure [] := rfl
@[simp] theorem mapM_cons [Monad m] [LawfulMonad m] (f : α → m β) :
(a :: l).mapM f = (return (← f a) :: (← l.mapM f)) := by simp [← mapM'_eq_mapM, mapM']
@[simp] theorem mapM_append [Monad m] [LawfulMonad m] (f : α → m β) {l₁ l₂ : List α} :
(l₁ ++ l₂).mapM f = (return (← l₁.mapM f) ++ (← l₂.mapM f)) := by induction l₁ <;> simp [*]
/-! ### forM -/
-- We use `List.forM` as the simp normal form, rather that `ForM.forM`.
-- As such we need to replace `List.forM_nil` and `List.forM_cons`:
@[simp] theorem forM_nil' [Monad m] : ([] : List α).forM f = (pure .unit : m PUnit) := rfl
@[simp] theorem forM_cons' [Monad m] :
(a::as).forM f = (f a >>= fun _ => as.forM f : m PUnit) :=
List.forM_cons _ _ _
end List