lean4-htt/src/Init/Data/List/Find.lean
Sebastian Ullrich 01dbbeed99
feat: do not export def bodies by default (#8221)
This PR adjusts the experimental module system to not export the bodies
of `def`s unless opted out by the new attribute `@[expose]` on the `def`
or on a surrounding `section`.

---------

Co-authored-by: Markus Himmel <markus@lean-fro.org>
2025-05-15 12:16:54 +00:00

1264 lines
46 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,
Kim Morrison, Jannis Limperg
-/
module
prelude
import Init.Data.List.Lemmas
import Init.Data.List.Sublist
import Init.Data.List.Range
import Init.Data.List.Impl
import all Init.Data.List.Attach
import Init.Data.Fin.Lemmas
/-!
Lemmas about `List.findSome?`, `List.find?`, `List.findIdx`, `List.findIdx?`, `List.idxOf`,
and `List.lookup`.
-/
set_option linter.listVariables true -- Enforce naming conventions for `List`/`Array`/`Vector` variables.
set_option linter.indexVariables true -- Enforce naming conventions for index variables.
namespace List
open Nat
/-! ### 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 = some 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?_eq_none_iff : findSome? p l = none ↔ ∀ x ∈ l, p x = none := by
induction l <;> simp [findSome?_cons]; split <;> simp [*]
@[simp] theorem findSome?_isSome_iff {f : α → Option β} {l : List α} :
(l.findSome? f).isSome ↔ ∃ x, x ∈ l ∧ (f x).isSome := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [findSome?_cons]
split <;> simp_all
theorem findSome?_eq_some_iff {f : α → Option β} {l : List α} {b : β} :
l.findSome? f = some b ↔ ∃ l₁ a l₂, l = l₁ ++ a :: l₂ ∧ f a = some b ∧ ∀ x ∈ l₁, f x = none := by
induction l with
| nil => simp
| cons p l ih =>
simp only [findSome?_cons]
split <;> rename_i b' h
· simp only [Option.some.injEq, exists_and_right]
constructor
· rintro rfl
exact ⟨[], p, ⟨l, rfl⟩, h, by simp⟩
· rintro ⟨(⟨⟩ | ⟨p', l₁⟩), a, ⟨l₂, h₁⟩, h₂, h₃⟩
· simp only [nil_append, cons.injEq] at h₁
apply Option.some.inj
simp [← h, ← h₂, h₁.1]
· simp only [cons_append, cons.injEq] at h₁
obtain ⟨rfl, rfl⟩ := h₁
specialize h₃ p
simp_all
· rw [ih]
constructor
· rintro ⟨l₁, a, l₂, rfl, h₁, h₂⟩
refine ⟨p :: l₁, a, l₂, rfl, h₁, ?_⟩
intro a w
simp at w
rcases w with rfl | w
· exact h
· exact h₂ _ w
· rintro ⟨l₁, a, l₂, h₁, h₂, h₃⟩
rcases l₁ with (⟨⟩ | ⟨a', l₁⟩)
· simp_all
· simp only [cons_append, cons.injEq] at h₁
obtain ⟨⟨rfl, rfl⟩, rfl⟩ := h₁
exact ⟨l₁, a, l₂, rfl, h₂, fun a' w => h₃ a' (mem_cons_of_mem p w)⟩
@[simp] theorem findSome?_guard {l : List α} : findSome? (Option.guard fun x => p x) l = find? p l := by
induction l with
| nil => simp
| cons x xs ih =>
simp [findSome?, find?]
split <;> rename_i h
· simp only [Option.guard_eq_some_iff] at h
obtain ⟨rfl, h⟩ := h
simp [h]
· simp only [Option.guard_eq_none_iff] at h
simp [ih, h]
theorem find?_eq_findSome?_guard {l : List α} : find? p l = findSome? (Option.guard fun x => p x) l :=
findSome?_guard.symm
@[simp] theorem head?_filterMap {f : α → Option β} {l : List α} : (l.filterMap f).head? = l.findSome? f := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [filterMap_cons, findSome?_cons]
split <;> simp [*]
@[simp] theorem head_filterMap {f : α → Option β} {l : List α} (h) :
(l.filterMap f).head h = (l.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [head_eq_iff_head?_eq_some]
@[simp] theorem getLast?_filterMap {f : α → Option β} {l : List α} : (l.filterMap f).getLast? = l.reverse.findSome? f := by
rw [getLast?_eq_head?_reverse]
simp [← filterMap_reverse]
@[simp] theorem getLast_filterMap {f : α → Option β} {l : List α} (h) :
(l.filterMap f).getLast h = (l.reverse.findSome? f).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [getLast_eq_iff_getLast?_eq_some]
@[simp] theorem map_findSome? {f : α → Option β} {g : β → γ} {l : List α} :
(l.findSome? f).map g = l.findSome? (Option.map g ∘ f) := by
induction l <;> simp [findSome?_cons]; split <;> 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 head_flatten {L : List (List α)} (h : ∃ l, l ∈ L ∧ l ≠ []) :
(flatten L).head (by simpa using h) = (L.findSome? fun l => l.head?).get (by simpa using h) := by
simp [head_eq_iff_head?_eq_some, head?_flatten]
theorem getLast_flatten {L : List (List α)} (h : ∃ l, l ∈ L ∧ l ≠ []) :
(flatten L).getLast (by simpa using h) =
(L.reverse.findSome? fun l => l.getLast?).get (by simpa using h) := by
simp [getLast_eq_iff_getLast?_eq_some, getLast?_flatten]
theorem findSome?_replicate : findSome? f (replicate n a) = if n = 0 then none else f a := by
cases n with
| zero => simp
| succ n =>
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]
-- Argument is unused, but used to decide whether `simp` should unfold.
@[simp] theorem findSome?_replicate_of_isSome (_ : (f a).isSome) : findSome? f (replicate n a) = if n = 0 then none else f a := by
simp [findSome?_replicate]
@[simp] theorem findSome?_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]
theorem Sublist.findSome?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
(l₁.findSome? f).isSome → (l₂.findSome? f).isSome := by
induction h with
| slnil => simp
| cons a h ih
| cons₂ a h ih =>
simp only [findSome?]
split
· simp_all
· exact ih
theorem Sublist.findSome?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) :
l₂.findSome? f = none → l₁.findSome? f = none := by
simp only [List.findSome?_eq_none_iff, Bool.not_eq_true]
exact fun w x m => w x (Sublist.mem m h)
theorem IsPrefix.findSome?_eq_some {l₁ l₂ : List α} {f : α → Option β} (h : l₁ <+: l₂) :
List.findSome? f l₁ = some b → List.findSome? f l₂ = some b := by
rw [IsPrefix] at h
obtain ⟨t, rfl⟩ := h
simp +contextual [findSome?_append]
theorem IsPrefix.findSome?_eq_none {l₁ l₂ : List α} {f : α → Option β} (h : l₁ <+: l₂) :
List.findSome? f l₂ = none → List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
theorem IsSuffix.findSome?_eq_none {l₁ l₂ : List α} {f : α → Option β} (h : l₁ <:+ l₂) :
List.findSome? f l₂ = none → List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
theorem IsInfix.findSome?_eq_none {l₁ l₂ : List α} {f : α → Option β} (h : l₁ <:+: l₂) :
List.findSome? f l₂ = none → List.findSome? f l₁ = none :=
h.sublist.findSome?_eq_none
/-! ### 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?_eq_some_iff_append :
xs.find? p = some b ↔ p b ∧ ∃ as bs, xs = as ++ b :: bs ∧ ∀ a ∈ as, !p a := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, exists_and_right]
split <;> rename_i h
· simp only [Option.some.injEq]
constructor
· rintro rfl
exact ⟨h, [], ⟨xs, rfl⟩, by simp⟩
· rintro ⟨-, ⟨as, ⟨⟨bs, h₁⟩, h₂⟩⟩⟩
cases as with
| nil => simp_all
| cons a as =>
specialize h₂ a mem_cons_self
simp only [cons_append] at h₁
obtain ⟨rfl, -⟩ := h₁
simp_all
· simp only [ih, Bool.not_eq_eq_eq_not, Bool.not_true, exists_and_right, and_congr_right_iff]
intro pb
constructor
· rintro ⟨as, ⟨⟨bs, rfl⟩, h₁⟩⟩
refine ⟨x :: as, ⟨⟨bs, rfl⟩, ?_⟩⟩
intro a m
simp at m
obtain (rfl|m) := m
· exact h
· exact h₁ a m
· rintro ⟨as, ⟨bs, h₁⟩, h₂⟩
cases as with
| nil => simp_all
| cons a as =>
refine ⟨as, ⟨⟨bs, ?_⟩, fun a m => h₂ a (mem_cons_of_mem _ m)⟩⟩
cases h₁
simp
@[deprecated find?_eq_some_iff_append (since := "2024-11-06")]
abbrev find?_eq_some := @find?_eq_some_iff_append
@[simp]
theorem find?_cons_eq_some : (a :: xs).find? p = some b ↔ (p a ∧ a = b) (!p a ∧ xs.find? p = some b) := by
rw [find?_cons]
split <;> simp_all
@[simp] theorem find?_isSome {xs : List α} {p : α → Bool} : (xs.find? p).isSome ↔ ∃ x, x ∈ xs ∧ p x := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, mem_cons, exists_eq_or_imp]
split <;> simp_all
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
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)
theorem get_find?_mem {xs : List α} {p : α → Bool} (h) : (xs.find? p).get h ∈ xs := by
induction xs with
| nil => simp at h
| cons x xs ih =>
simp only [find?_cons]
by_cases h : p x
· simp [h]
· simp only [h]
right
apply ih
@[simp] theorem find?_filter {xs : List α} {p : α → Bool} {q : α → Bool} :
(xs.filter p).find? q = xs.find? (fun a => p a ∧ q a) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [filter_cons]
split <;>
· simp only [find?_cons]
split <;> simp_all
@[simp] theorem head?_filter {p : α → Bool} {l : List α} : (l.filter p).head? = l.find? p := by
rw [← filterMap_eq_filter, head?_filterMap, findSome?_guard]
@[simp] theorem head_filter {p : α → Bool} {l : List α} (h) :
(l.filter p).head h = (l.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [head_eq_iff_head?_eq_some]
@[simp] theorem getLast?_filter {p : α → Bool} {l : List α} : (l.filter p).getLast? = l.reverse.find? p := by
rw [getLast?_eq_head?_reverse]
simp [← filter_reverse]
@[simp] theorem getLast_filter {p : α → Bool} {l : List α} (h) :
(l.filter p).getLast h = (l.reverse.find? p).get (by simp_all [Option.isSome_iff_ne_none]) := by
simp [getLast_eq_iff_getLast?_eq_some]
@[simp] theorem find?_filterMap {xs : List α} {f : α → Option β} {p : β → Bool} :
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [filterMap_cons]
split <;>
· simp only [find?_cons]
split <;> simp_all
@[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]
@[simp] theorem find?_flatten {xss : List (List α)} {p : α → Bool} :
xss.flatten.find? p = xss.findSome? (·.find? p) := by
induction xss with
| nil => simp
| cons _ _ ih =>
simp only [flatten_cons, find?_append, findSome?_cons, ih]
split <;> simp [*]
theorem find?_flatten_eq_none_iff {xs : List (List α)} {p : α → Bool} :
xs.flatten.find? p = none ↔ ∀ ys ∈ xs, ∀ x ∈ ys, !p x := by
simp
@[deprecated find?_flatten_eq_none_iff (since := "2025-02-03")]
abbrev find?_flatten_eq_none := @find?_flatten_eq_none_iff
/--
If `find? p` returns `some a` from `xs.flatten`, then `p a` holds, and
some list in `xs` contains `a`, and no earlier element of that list satisfies `p`.
Moreover, no earlier list in `xs` has an element satisfying `p`.
-/
theorem find?_flatten_eq_some_iff {xs : List (List α)} {p : α → Bool} {a : α} :
xs.flatten.find? p = some a ↔
p a ∧ ∃ as ys zs bs, xs = as ++ (ys ++ a :: zs) :: bs ∧
(∀ l ∈ as, ∀ x ∈ l, !p x) ∧ (∀ x ∈ ys, !p x) := by
rw [find?_eq_some_iff_append]
constructor
· rintro ⟨h, ⟨ys, zs, h₁, h₂⟩⟩
refine ⟨h, ?_⟩
rw [flatten_eq_append_iff] at h₁
obtain (⟨as, bs, rfl, rfl, h₁⟩ | ⟨as, bs, c, cs, ds, rfl, rfl, h₁⟩) := h₁
· replace h₁ := h₁.symm
rw [flatten_eq_cons_iff] at h₁
obtain ⟨bs, cs, ds, rfl, h₁, rfl⟩ := h₁
refine ⟨as ++ bs, [], cs, ds, by simp, ?_⟩
simp
rintro l (ma | mb) x m
· simpa using h₂ x (by simpa using ⟨l, ma, m⟩)
· specialize h₁ _ mb
simp_all
· simp [h₁]
refine ⟨as, bs, ?_⟩
refine ⟨?_, ?_, ?_⟩
· simp_all
· intro l ml a m
simpa using h₂ a (by simpa using .inl ⟨l, ml, m⟩)
· intro x m
simpa using h₂ x (by simpa using .inr m)
· rintro ⟨h, ⟨as, ys, zs, bs, rfl, h₁, h₂⟩⟩
refine ⟨h, as.flatten ++ ys, zs ++ bs.flatten, by simp, ?_⟩
intro a m
simp at m
obtain ⟨l, ml, m⟩ | m := m
· exact h₁ l ml a m
· exact h₂ a m
@[deprecated find?_flatten_eq_some_iff (since := "2025-02-03")]
abbrev find?_flatten_eq_some := @find?_flatten_eq_some_iff
@[simp] theorem find?_flatMap {xs : List α} {f : α → List β} {p : β → Bool} :
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
simp [flatMap_def, findSome?_map]; rfl
theorem find?_flatMap_eq_none_iff {xs : List α} {f : α → List β} {p : β → Bool} :
(xs.flatMap f).find? p = none ↔ ∀ x ∈ xs, ∀ y ∈ f x, !p y := by
simp
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]
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
theorem find?_replicate_eq_none_iff {n : Nat} {a : α} {p : α → Bool} :
(replicate n a).find? p = none ↔ n = 0 !p a := by
simp [Classical.or_iff_not_imp_left]
@[deprecated find?_replicate_eq_none_iff (since := "2025-02-03")]
abbrev find?_replicate_eq_none := @find?_replicate_eq_none_iff
@[simp] theorem find?_replicate_eq_some_iff {n : Nat} {a b : α} {p : α → Bool} :
(replicate n a).find? p = some b ↔ n ≠ 0 ∧ p a ∧ a = b := by
cases n <;> simp
@[deprecated find?_replicate_eq_some_iff (since := "2025-02-03")]
abbrev find?_replicate_eq_some := @find?_replicate_eq_some_iff
@[simp] theorem get_find?_replicate {n : Nat} {a : α} {p : α → Bool} (h) : ((replicate n a).find? p).get h = a := by
cases n with
| zero => simp at h
| succ n => simp
theorem Sublist.find?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) : (l₁.find? p).isSome → (l₂.find? p).isSome := by
induction h with
| slnil => simp
| cons a h ih
| cons₂ a h ih =>
simp only [find?]
split
· simp
· simpa using ih
theorem Sublist.find?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) : l₂.find? p = none → l₁.find? p = none := by
simp only [List.find?_eq_none, Bool.not_eq_true]
exact fun w x m => w x (Sublist.mem m h)
theorem IsPrefix.find?_eq_some {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂) :
List.find? p l₁ = some b → List.find? p l₂ = some b := by
rw [IsPrefix] at h
obtain ⟨t, rfl⟩ := h
simp +contextual [find?_append]
theorem IsPrefix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂) :
List.find? p l₂ = none → List.find? p l₁ = none :=
h.sublist.find?_eq_none
theorem IsSuffix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <:+ l₂) :
List.find? p l₂ = none → List.find? p l₁ = none :=
h.sublist.find?_eq_none
theorem IsInfix.find?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <:+: l₂) :
List.find? p l₂ = none → List.find? p l₁ = none :=
h.sublist.find?_eq_none
theorem find?_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : List α}
(H : ∀ (a : α), a ∈ xs → P a) {p : β → Bool} :
(xs.pmap f H).find? p = (xs.attach.find? (fun ⟨a, m⟩ => p (f a (H a m)))).map fun ⟨a, m⟩ => f a (H a m) := by
simp only [pmap_eq_map_attach, find?_map]
rfl
/-! ### findIdx? (preliminary lemmas) -/
@[local simp] private theorem findIdx?_go_nil {p : α → Bool} {i : Nat} :
findIdx?.go p [] i = none := rfl
@[local simp] private theorem findIdx?_go_cons :
findIdx?.go p (x :: xs) i = if p x then some i else findIdx?.go p xs (i + 1) := rfl
private theorem findIdx?_go_succ {p : α → Bool} {xs : List α} {i : Nat} :
findIdx?.go p xs (i+1) = (findIdx?.go p xs i).map fun i => i + 1 := by
induction xs generalizing i with simp
| cons _ _ _ => split <;> simp_all
private theorem findIdx?_go_eq {p : α → Bool} {xs : List α} {i : Nat} :
findIdx?.go p xs (i+1) = (findIdx?.go p xs 0).map fun k => k + (i + 1) := by
induction xs generalizing i with
| nil => simp
| cons _ _ _ =>
simp only [findIdx?_go_succ, findIdx?_go_cons, Nat.zero_add]
split
· simp_all
· simp_all only [findIdx?_go_succ, Bool.not_eq_true, Option.map_map, Nat.zero_add]
congr
ext
simp only [Nat.add_comm i, Function.comp_apply, Nat.add_assoc]
@[simp] theorem findIdx?_nil : ([] : List α).findIdx? p = none := rfl
theorem findIdx?_cons :
(x :: xs).findIdx? p = if p x then some 0 else (xs.findIdx? p).map fun i => i + 1 := by
simp [findIdx?, findIdx?_go_eq]
@[simp] theorem findIdx?_singleton {a : α} {p : α → Bool} : [a].findIdx? p = if p a then some 0 else none := by
simp [findIdx?_cons, findIdx?_nil]
/-! ### findIdx -/
theorem findIdx_cons {p : α → Bool} {b : α} {l : List α} :
(b :: l).findIdx p = bif p b then 0 else (l.findIdx p) + 1 := by
cases H : p b with
| true => simp [H, findIdx, findIdx.go]
| false => simp [H, findIdx, findIdx.go, findIdx_go_succ]
where
findIdx_go_succ (p : α → Bool) (l : List α) (n : Nat) :
List.findIdx.go p l (n + 1) = (findIdx.go p l n) + 1 := by
cases l with
| nil => unfold findIdx.go; exact Nat.succ_eq_add_one n
| cons hd tl =>
unfold findIdx.go
cases p hd <;> simp only [cond_false, cond_true]
exact findIdx_go_succ p tl (n + 1)
@[simp] theorem findIdx_singleton {a : α} {p : α → Bool} : [a].findIdx p = if p a then 0 else 1 := by
simp [findIdx_cons, findIdx_nil]
theorem findIdx_of_getElem?_eq_some {xs : List α} (w : xs[xs.findIdx p]? = some y) : p y := by
induction xs with
| nil => simp_all
| cons x xs ih => by_cases h : p x <;> simp_all [findIdx_cons]
theorem findIdx_getElem {xs : List α} {w : xs.findIdx p < xs.length} :
p xs[xs.findIdx p] :=
xs.findIdx_of_getElem?_eq_some (getElem?_eq_getElem w)
theorem findIdx_lt_length_of_exists {xs : List α} (h : ∃ x ∈ xs, p x) :
xs.findIdx p < xs.length := by
induction xs with
| nil => simp_all
| cons x xs ih =>
by_cases p x
· simp_all only [forall_exists_index, and_imp, mem_cons, exists_eq_or_imp, true_or,
findIdx_cons, cond_true, length_cons]
apply Nat.succ_pos
· simp_all [findIdx_cons, Nat.succ_lt_succ_iff]
obtain ⟨x', m', h'⟩ := h
exact ih x' m' h'
theorem findIdx_getElem?_eq_getElem_of_exists {xs : List α} (h : ∃ x ∈ xs, p x) :
xs[xs.findIdx p]? = some (xs[xs.findIdx p]'(xs.findIdx_lt_length_of_exists h)) :=
getElem?_eq_getElem (findIdx_lt_length_of_exists h)
@[simp]
theorem findIdx_eq_length {p : α → Bool} {xs : List α} :
xs.findIdx p = xs.length ↔ ∀ x ∈ xs, p x = false := by
induction xs with
| nil => simp_all
| cons x xs ih =>
rw [findIdx_cons, length_cons]
simp only [cond_eq_if]
split <;> simp_all [Nat.succ.injEq]
theorem findIdx_eq_length_of_false {p : α → Bool} {xs : List α} (h : ∀ x ∈ xs, p x = false) :
xs.findIdx p = xs.length := by
rw [findIdx_eq_length]
exact h
theorem findIdx_le_length {p : α → Bool} {xs : List α} : xs.findIdx p ≤ xs.length := by
by_cases e : ∃ x ∈ xs, p x
· exact Nat.le_of_lt (findIdx_lt_length_of_exists e)
· simp at e
exact Nat.le_of_eq (findIdx_eq_length.mpr e)
@[simp]
theorem findIdx_lt_length {p : α → Bool} {xs : List α} :
xs.findIdx p < xs.length ↔ ∃ x ∈ xs, p x := by
rw [← Decidable.not_iff_not, Nat.not_lt]
have := @Nat.le_antisymm_iff (xs.findIdx p) xs.length
simp only [findIdx_le_length, true_and] at this
rw [← this, findIdx_eq_length, not_exists]
simp only [Bool.not_eq_true, not_and]
/-- `p` does not hold for elements with indices less than `xs.findIdx p`. -/
theorem not_of_lt_findIdx {p : α → Bool} {xs : List α} {i : Nat} (h : i < xs.findIdx p) :
p (xs[i]'(Nat.le_trans h findIdx_le_length)) = false := by
revert i
induction xs with
| nil => intro i h; rw [findIdx_nil] at h; simp at h
| cons x xs ih =>
intro i h
have ho := h
rw [findIdx_cons] at h
have npx : p x = false := by
apply eq_false_of_ne_true
intro y
rw [y, cond_true] at h
simp at h
simp [npx, cond_false] at h
cases i.eq_zero_or_pos with
| inl e => simpa [e, Fin.zero_eta, get_cons_zero]
| inr e =>
have ipm := Nat.succ_pred_eq_of_pos e
simp +singlePass only [← ipm, getElem_cons_succ]
rw [← ipm, Nat.succ_lt_succ_iff] at h
simpa using ih h
/-- If `¬ p xs[j]` for all `j < i`, then `i ≤ xs.findIdx p`. -/
theorem le_findIdx_of_not {p : α → Bool} {xs : List α} {i : Nat} (h : i < xs.length)
(h2 : ∀ j (hji : j < i), p (xs[j]'(Nat.lt_trans hji h)) = false) : i ≤ xs.findIdx p := by
apply Decidable.byContradiction
intro f
simp only [Nat.not_le] at f
exact absurd (@findIdx_getElem _ p xs (Nat.lt_trans f h)) (by simpa using h2 (xs.findIdx p) f)
/-- If `¬ p xs[j]` for all `j ≤ i`, then `i < xs.findIdx p`. -/
theorem lt_findIdx_of_not {p : α → Bool} {xs : List α} {i : Nat} (h : i < xs.length)
(h2 : ∀ j (hji : j ≤ i), ¬p (xs[j]'(Nat.lt_of_le_of_lt hji h))) : i < xs.findIdx p := by
apply Decidable.byContradiction
intro f
simp only [Nat.not_lt] at f
exact absurd (@findIdx_getElem _ p xs (Nat.lt_of_le_of_lt f h)) (h2 (xs.findIdx p) f)
/-- `xs.findIdx p = i` iff `p xs[i]` and `¬ p xs [j]` for all `j < i`. -/
theorem findIdx_eq {p : α → Bool} {xs : List α} {i : Nat} (h : i < xs.length) :
xs.findIdx p = i ↔ p xs[i] ∧ ∀ j (hji : j < i), p (xs[j]'(Nat.lt_trans hji h)) = false := by
refine ⟨fun f ↦ ⟨f ▸ (@findIdx_getElem _ p xs (f ▸ h)), fun _ hji ↦ not_of_lt_findIdx (f ▸ hji)⟩,
fun ⟨_, h2⟩ ↦ ?_⟩
apply Nat.le_antisymm _ (le_findIdx_of_not h h2)
apply Decidable.byContradiction
intro h3
simp at h3
simp_all [not_of_lt_findIdx h3]
theorem findIdx_append {p : α → Bool} {l₁ l₂ : List α} :
(l₁ ++ l₂).findIdx p =
if l₁.findIdx p < l₁.length then l₁.findIdx p else l₂.findIdx p + l₁.length := by
induction l₁ with
| nil => simp
| cons x xs ih =>
simp only [findIdx_cons, length_cons, cons_append]
by_cases h : p x
· simp [h]
· simp only [h, ih, cond_eq_if, Bool.false_eq_true, ↓reduceIte, add_one_lt_add_one_iff]
split <;> simp [Nat.add_assoc]
theorem IsPrefix.findIdx_le {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂) :
l₁.findIdx p ≤ l₂.findIdx p := by
rw [IsPrefix] at h
obtain ⟨t, rfl⟩ := h
simp only [findIdx_append, findIdx_lt_length]
split
· exact Nat.le_refl ..
· simp_all [findIdx_eq_length_of_false]
theorem IsPrefix.findIdx_eq_of_findIdx_lt_length {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂)
(lt : l₁.findIdx p < l₁.length) : l₂.findIdx p = l₁.findIdx p := by
rw [IsPrefix] at h
obtain ⟨t, rfl⟩ := h
simp only [findIdx_append, findIdx_lt_length]
split
· rfl
· simp_all
theorem findIdx_le_findIdx {l : List α} {p q : α → Bool} (h : ∀ x ∈ l, p x → q x) : l.findIdx q ≤ l.findIdx p := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [findIdx_cons, cond_eq_if]
split
· simp
· split
· simp_all
· simp only [Nat.add_le_add_iff_right]
exact ih fun _ m w => h _ (mem_cons_of_mem x m) w
@[simp] theorem findIdx_subtype {p : α → Prop} {l : List { x // p x }}
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
l.findIdx f = l.unattach.findIdx g := by
unfold unattach
induction l with
| nil => simp
| cons a l ih =>
simp [ih, hf, findIdx_cons]
/-! ### findIdx? -/
@[simp]
theorem findIdx?_eq_none_iff {xs : List α} {p : α → Bool} :
xs.findIdx? p = none ↔ ∀ x, x ∈ xs → p x = false := by
induction xs with
| nil => simp_all
| cons x xs ih =>
simp only [findIdx?_cons]
split <;> simp_all [cond_eq_if]
@[simp]
theorem findIdx?_isSome {xs : List α} {p : α → Bool} :
(xs.findIdx? p).isSome = xs.any p := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons]
split <;> simp_all
@[simp]
theorem findIdx?_isNone {xs : List α} {p : α → Bool} :
(xs.findIdx? p).isNone = xs.all (¬p ·) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons]
split <;> simp_all
theorem findIdx?_eq_some_iff_findIdx_eq {xs : List α} {p : α → Bool} {i : Nat} :
xs.findIdx? p = some i ↔ i < xs.length ∧ xs.findIdx p = i := by
induction xs generalizing i with
| nil => simp_all
| cons x xs ih =>
simp only [findIdx?_cons, findIdx_cons]
split
· simp_all [cond_eq_if]
rintro rfl
exact zero_lt_succ xs.length
· simp_all [cond_eq_if, and_assoc]
constructor
· rintro ⟨a, lt, rfl, rfl⟩
simp_all [Nat.succ_lt_succ_iff]
· rintro ⟨h, rfl⟩
exact ⟨_, by simp_all [Nat.succ_lt_succ_iff], rfl, rfl⟩
theorem findIdx?_eq_some_of_exists {xs : List α} {p : α → Bool} (h : ∃ x, x ∈ xs ∧ p x) :
xs.findIdx? p = some (xs.findIdx p) := by
rw [findIdx?_eq_some_iff_findIdx_eq]
exact ⟨findIdx_lt_length_of_exists h, rfl⟩
theorem findIdx?_eq_none_iff_findIdx_eq {xs : List α} {p : α → Bool} :
xs.findIdx? p = none ↔ xs.findIdx p = xs.length := by
simp
theorem findIdx?_eq_guard_findIdx_lt {xs : List α} {p : α → Bool} :
xs.findIdx? p = Option.guard (fun i => i < xs.length) (xs.findIdx p) := by
match h : xs.findIdx? p with
| none =>
simp only [findIdx?_eq_none_iff] at h
simp [findIdx_eq_length_of_false h, Option.guard]
| some i =>
simp only [findIdx?_eq_some_iff_findIdx_eq] at h
simp [h]
theorem findIdx?_eq_some_iff_getElem {xs : List α} {p : α → Bool} {i : Nat} :
xs.findIdx? p = some i ↔
∃ h : i < xs.length, p xs[i] ∧ ∀ j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji h)) := by
induction xs generalizing i with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons, Nat.zero_add]
split
· simp only [Option.some.injEq, Bool.not_eq_true, length_cons]
cases i with
| zero => simp_all
| succ i =>
simp only [Bool.not_eq_true, zero_ne_add_one, getElem_cons_succ, false_iff, not_exists,
not_and, Classical.not_forall, Bool.not_eq_false]
intros
refine ⟨0, zero_lt_succ i, _
· simp only [Option.map_eq_some_iff, ih, Bool.not_eq_true, length_cons]
constructor
· rintro ⟨a, ⟨⟨h, h₁, h₂⟩, rfl⟩⟩
refine ⟨Nat.succ_lt_succ_iff.mpr h, by simpa, fun j hj => ?_⟩
cases j with
| zero => simp_all
| succ j =>
apply h₂
simp_all [Nat.succ_lt_succ_iff]
· rintro ⟨h, h₁, h₂⟩
cases i with
| zero => simp_all
| succ i =>
refine ⟨i, ⟨Nat.succ_lt_succ_iff.mp h, by simpa, fun j hj => ?_⟩, rfl⟩
simpa using h₂ (j + 1) (Nat.succ_lt_succ_iff.mpr hj)
theorem of_findIdx?_eq_some {xs : List α} {p : α → Bool} (w : xs.findIdx? p = some i) :
match xs[i]? with | some a => p a | none => false := by
induction xs generalizing i with
| nil => simp_all
| cons x xs ih =>
simp_all only [findIdx?_cons, Nat.zero_add]
split at w <;> cases i <;> simp_all [succ_inj]
@[deprecated of_findIdx?_eq_some (since := "2025-02-02")]
abbrev findIdx?_of_eq_some := @of_findIdx?_eq_some
theorem of_findIdx?_eq_none {xs : List α} {p : α → Bool} (w : xs.findIdx? p = none) :
∀ i : Nat, match xs[i]? with | some a => ¬ p a | none => true := by
intro i
induction xs generalizing i with
| nil => simp_all
| cons x xs ih =>
simp_all only [Bool.not_eq_true, findIdx?_cons, Nat.zero_add]
cases i with
| zero =>
split at w <;> simp_all
| succ i =>
simp only [getElem?_cons_succ]
apply ih
split at w <;> simp_all
@[deprecated of_findIdx?_eq_none (since := "2025-02-02")]
abbrev findIdx?_of_eq_none := @of_findIdx?_eq_none
@[simp] theorem findIdx?_map {f : β → α} {l : List β} : findIdx? p (l.map f) = l.findIdx? (p ∘ f) := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [map_cons, findIdx?_cons]
split <;> simp_all
@[simp] theorem findIdx?_append :
(xs ++ ys : List α).findIdx? p =
(xs.findIdx? p).or ((ys.findIdx? p).map fun i => i + xs.length) := by
induction xs with simp [findIdx?_cons]
| cons _ _ _ => split <;> simp_all [Option.map_or, Option.map_map]; rfl
theorem findIdx?_flatten {l : List (List α)} {p : α → Bool} :
l.flatten.findIdx? p =
(l.findIdx? (·.any p)).map
fun i => ((l.take i).map List.length).sum +
(l[i]?.map fun xs => xs.findIdx p).getD 0 := by
induction l with
| nil => simp
| cons xs l ih =>
rw [flatten_cons, findIdx?_append, ih, findIdx?_cons]
split <;> rename_i h
· simp only [any_eq_true] at h
rw [Option.or_of_isSome (by simp_all [findIdx?_isSome])]
simp_all [findIdx?_eq_some_of_exists]
· rw [Option.or_of_isNone (by simp_all [findIdx?_isNone])]
simp [Function.comp_def, Nat.add_comm, Nat.add_assoc]
@[simp] theorem findIdx?_replicate :
(replicate n a).findIdx? p = if 0 < n ∧ p a then some 0 else none := by
cases n with
| zero => simp
| succ n =>
simp only [replicate, findIdx?_cons, Nat.zero_add, zero_lt_succ, true_and]
split <;> simp_all
theorem findIdx?_eq_findSome?_zipIdx {xs : List α} {p : α → Bool} :
xs.findIdx? p = xs.zipIdx.findSome? fun ⟨a, i⟩ => if p a then some i else none := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons, Nat.zero_add, zipIdx]
split
· simp_all
· simp_all only [zipIdx_cons, ite_false, Option.isNone_none, findSome?_cons_of_isNone, reduceCtorEq]
rw [← map_snd_add_zipIdx_eq_zipIdx (n := 1) (k := 0)]
simp [Function.comp_def, findSome?_map]
theorem findIdx?_eq_fst_find?_zipIdx {xs : List α} {p : α → Bool} :
xs.findIdx? p = (xs.zipIdx.find? fun ⟨x, _⟩ => p x).map (·.2) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [findIdx?_cons, Nat.zero_add, zipIdx_cons]
split
· simp_all
· rw [ih, ← map_snd_add_zipIdx_eq_zipIdx (n := 1) (k := 0)]
simp [Function.comp_def, *]
-- See also `findIdx_le_findIdx`.
theorem findIdx?_eq_none_of_findIdx?_eq_none {xs : List α} {p q : α → Bool} (w : ∀ x ∈ xs, p x → q x) :
xs.findIdx? q = none → xs.findIdx? p = none := by
simp only [findIdx?_eq_none_iff]
intro h x m
cases z : p x
· rfl
· exfalso
specialize w x m z
specialize h x m
simp_all
theorem Sublist.findIdx?_isSome {l₁ l₂ : List α} (h : l₁ <+ l₂) :
(l₁.findIdx? p).isSome → (l₂.findIdx? p).isSome := by
simp only [List.findIdx?_isSome, any_eq_true]
rintro ⟨w, m, q⟩
exact ⟨w, h.mem m, q⟩
theorem Sublist.findIdx?_eq_none {l₁ l₂ : List α} (h : l₁ <+ l₂) :
l₂.findIdx? p = none → l₁.findIdx? p = none := by
simp only [findIdx?_eq_none_iff]
exact fun w x m => w x (h.mem m)
theorem IsPrefix.findIdx?_eq_some {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂) :
List.findIdx? p l₁ = some i → List.findIdx? p l₂ = some i := by
rw [IsPrefix] at h
obtain ⟨t, rfl⟩ := h
intro h
simp [findIdx?_append, h]
theorem IsPrefix.findIdx?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <+: l₂) :
List.findIdx? p l₂ = none → List.findIdx? p l₁ = none :=
h.sublist.findIdx?_eq_none
theorem IsSuffix.findIdx?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <:+ l₂) :
List.findIdx? p l₂ = none → List.findIdx? p l₁ = none :=
h.sublist.findIdx?_eq_none
theorem IsInfix.findIdx?_eq_none {l₁ l₂ : List α} {p : α → Bool} (h : l₁ <:+: l₂) :
List.findIdx? p l₂ = none → List.findIdx? p l₁ = none :=
h.sublist.findIdx?_eq_none
theorem findIdx_eq_getD_findIdx? {xs : List α} {p : α → Bool} :
xs.findIdx p = (xs.findIdx? p).getD xs.length := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [findIdx_cons, findIdx?_cons]
split <;> simp_all [ih]
@[simp] theorem findIdx?_subtype {p : α → Prop} {l : List { x // p x }}
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
l.findIdx? f = l.unattach.findIdx? g := by
unfold unattach
induction l with
| nil => simp
| cons a l ih =>
simp [hf, findIdx?_cons]
split <;> simp [ih, Function.comp_def]
/-! ### findFinIdx? -/
@[simp] theorem findFinIdx?_nil {p : α → Bool} : findFinIdx? p [] = none := rfl
theorem findIdx?_go_eq_map_findFinIdx?_go_val {xs : List α} {p : α → Bool} {i : Nat} {h} :
List.findIdx?.go p xs i =
(List.findFinIdx?.go p l xs i h).map (·.val) := by
unfold findIdx?.go
unfold findFinIdx?.go
split
· simp_all
· simp only
split
· simp
· rw [findIdx?_go_eq_map_findFinIdx?_go_val]
theorem findIdx?_eq_map_findFinIdx?_val {xs : List α} {p : α → Bool} :
xs.findIdx? p = (xs.findFinIdx? p).map (·.val) := by
simp [findIdx?, findFinIdx?]
rw [findIdx?_go_eq_map_findFinIdx?_go_val]
theorem findFinIdx?_eq_pmap_findIdx? {xs : List α} {p : α → Bool} :
xs.findFinIdx? p =
(xs.findIdx? p).pmap
(fun i m => by simp [findIdx?_eq_some_iff_getElem] at m; exact ⟨i, m.choose⟩)
(fun i h => h) := by
simp [findIdx?_eq_map_findFinIdx?_val, Option.pmap_map]
theorem findFinIdx?_cons {p : α → Bool} {x : α} {xs : List α} :
findFinIdx? p (x :: xs) = if p x then some 0 else (findFinIdx? p xs).map Fin.succ := by
rw [← Option.map_inj_right (f := Fin.val) (fun a b => Fin.eq_of_val_eq)]
rw [← findIdx?_eq_map_findFinIdx?_val]
rw [findIdx?_cons]
split
· simp
· rw [findIdx?_eq_map_findFinIdx?_val]
simp [Function.comp_def]
theorem findFinIdx?_append {xs ys : List α} {p : α → Bool} :
(xs ++ ys).findFinIdx? p =
((xs.findFinIdx? p).map (Fin.castLE (by simp))).or
((ys.findFinIdx? p).map (Fin.natAdd xs.length) |>.map (Fin.cast (by simp))) := by
simp only [findFinIdx?_eq_pmap_findIdx?, findIdx?_append, Option.pmap_or]
split <;> rename_i h _
· simp [h, Option.pmap_map, Option.map_pmap, Nat.add_comm]
· simp [h]
@[simp] theorem findFinIdx?_singleton {a : α} {p : α → Bool} :
[a].findFinIdx? p = if p a then some ⟨0, by simp⟩ else none := by
simp [findFinIdx?_cons, findFinIdx?_nil]
@[simp] theorem findFinIdx?_eq_none_iff {l : List α} {p : α → Bool} :
l.findFinIdx? p = none ↔ ∀ x ∈ l, ¬ p x := by
simp [findFinIdx?_eq_pmap_findIdx?]
@[simp]
theorem findFinIdx?_eq_some_iff {xs : List α} {p : α → Bool} {i : Fin xs.length} :
xs.findFinIdx? p = some i ↔
p xs[i] ∧ ∀ j (hji : j < i), ¬p (xs[j]'(Nat.lt_trans hji i.2)) := by
simp only [findFinIdx?_eq_pmap_findIdx?, Option.pmap_eq_some_iff, findIdx?_eq_some_iff_getElem,
Bool.not_eq_true, exists_and_left, and_exists_self, Fin.getElem_fin]
constructor
· rintro ⟨a, ⟨h, w₁, w₂⟩, rfl⟩
exact ⟨w₁, fun j hji => by simpa using w₂ j hji⟩
· rintro ⟨h, w⟩
exact ⟨i, ⟨i.2, h, fun j hji => w ⟨j, by omega⟩ hji⟩, rfl⟩
@[simp]
theorem isSome_findFinIdx? {l : List α} {p : α → Bool} :
(l.findFinIdx? p).isSome = l.any p := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [findFinIdx?_cons]
split <;> simp_all
@[simp]
theorem isNone_findFinIdx? {l : List α} {p : α → Bool} :
(l.findFinIdx? p).isNone = l.all (fun x => ¬ p x) := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [findFinIdx?_cons]
split <;> simp_all
@[simp] theorem findFinIdx?_subtype {p : α → Prop} {l : List { x // p x }}
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
l.findFinIdx? f = (l.unattach.findFinIdx? g).map (fun i => i.cast (by simp)) := by
induction l with
| nil => simp [unattach]
| cons a l ih =>
simp [hf, findFinIdx?_cons]
split <;> simp [ih, Function.comp_def]
/-! ### idxOf
The verification API for `idxOf` is still incomplete.
The lemmas below should be made consistent with those for `findIdx` (and proved using them).
-/
theorem idxOf_cons [BEq α] :
(x :: xs : List α).idxOf y = bif x == y then 0 else xs.idxOf y + 1 := by
dsimp [idxOf]
simp [findIdx_cons]
@[deprecated idxOf_cons (since := "2025-01-29")]
abbrev indexOf_cons := @idxOf_cons
@[simp] theorem idxOf_cons_self [BEq α] [ReflBEq α] {l : List α} : (a :: l).idxOf a = 0 := by
simp [idxOf_cons]
@[deprecated idxOf_cons_self (since := "2025-01-29")]
abbrev indexOf_cons_self := @idxOf_cons_self
theorem idxOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
(l₁ ++ l₂).idxOf a = if a ∈ l₁ then l₁.idxOf a else l₂.idxOf a + l₁.length := by
rw [idxOf, findIdx_append]
split <;> rename_i h
· rw [if_pos]
simpa using h
· rw [if_neg]
simpa using h
@[deprecated idxOf_append (since := "2025-01-29")]
abbrev indexOf_append := @idxOf_append
theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∉ l) : l.idxOf a = l.length := by
induction l with
| nil => rfl
| cons x xs ih =>
simp only [mem_cons, not_or] at h
simp only [idxOf_cons, cond_eq_if, beq_iff_eq]
split <;> simp_all
@[deprecated idxOf_eq_length (since := "2025-01-29")]
abbrev indexOf_eq_length := @idxOf_eq_length
theorem idxOf_lt_length [BEq α] [EquivBEq α] {l : List α} (h : a ∈ l) : l.idxOf a < l.length := by
induction l with
| nil => simp at h
| cons x xs ih =>
simp only [mem_cons] at h
obtain rfl | h := h
· simp
· simp only [idxOf_cons, cond_eq_if, beq_iff_eq, length_cons]
specialize ih h
split
· exact zero_lt_succ xs.length
· exact Nat.add_lt_add_right ih 1
@[deprecated idxOf_lt_length (since := "2025-01-29")]
abbrev indexOf_lt_length := @idxOf_lt_length
/-! ### finIdxOf?
The verification API for `finIdxOf?` is still incomplete.
The lemmas below should be made consistent with those for `findFinIdx?` (and proved using them).
-/
theorem idxOf?_eq_map_finIdxOf?_val [BEq α] {xs : List α} {a : α} :
xs.idxOf? a = (xs.finIdxOf? a).map (·.val) := by
simp [idxOf?, finIdxOf?, findIdx?_eq_map_findFinIdx?_val]
@[simp] theorem finIdxOf?_nil [BEq α] : ([] : List α).finIdxOf? a = none := rfl
theorem finIdxOf?_cons [BEq α] {a : α} {xs : List α} :
(a :: xs).finIdxOf? b =
if a == b then some ⟨0, by simp⟩ else (xs.finIdxOf? b).map (·.succ) := by
simp [finIdxOf?, findFinIdx?_cons]
@[simp] theorem finIdxOf?_eq_none_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
l.finIdxOf? a = none ↔ a ∉ l := by
simp only [finIdxOf?, findFinIdx?_eq_none_iff, beq_iff_eq]
constructor
· intro w m
exact w a m rfl
· rintro h a m rfl
exact h m
@[simp] theorem finIdxOf?_eq_some_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} {i : Fin l.length} :
l.finIdxOf? a = some i ↔ l[i] = a ∧ ∀ j (_ : j < i), ¬l[j] = a := by
simp only [finIdxOf?, findFinIdx?_eq_some_iff, beq_iff_eq]
@[simp]
theorem isSome_finIdxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.finIdxOf? a).isSome ↔ a ∈ l := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [finIdxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
@[simp]
theorem isNone_finIdxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.finIdxOf? a).isNone = ¬ a ∈ l := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [finIdxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
/-! ### idxOf?
The verification API for `idxOf?` is still incomplete.
The lemmas below should be made consistent with those for `findIdx?` (and proved using them).
-/
@[simp] theorem idxOf?_nil [BEq α] : ([] : List α).idxOf? a = none := rfl
theorem idxOf?_cons [BEq α] {a : α} {xs : List α} {b : α} :
(a :: xs).idxOf? b = if a == b then some 0 else (xs.idxOf? b).map (· + 1) := by
simp [idxOf?, findIdx?_cons]
@[simp] theorem idxOf?_singleton [BEq α] {a b : α} : [a].idxOf? b = if a == b then some 0 else none := by
simp [idxOf?_cons, idxOf?_nil]
@[simp] theorem idxOf?_eq_none_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
l.idxOf? a = none ↔ a ∉ l := by
simp only [idxOf?, findIdx?_eq_none_iff, beq_eq_false_iff_ne, ne_eq]
constructor
· intro w h
specialize w _ h
simp at w
· rintro w x h rfl
contradiction
@[deprecated idxOf?_eq_none_iff (since := "2025-01-29")]
abbrev indexOf?_eq_none_iff := @idxOf?_eq_none_iff
@[simp]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.idxOf? a).isSome ↔ a ∈ l := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [idxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
@[simp]
theorem isNone_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.idxOf? a).isNone = ¬ a ∈ l := by
induction l with
| nil => simp
| cons x xs ih =>
simp only [idxOf?_cons]
split <;> simp_all [@eq_comm _ x a]
/-! ### lookup -/
section lookup
variable [BEq α] [LawfulBEq α]
@[simp] theorem lookup_cons_self {k : α} : ((k,b) :: es).lookup k = some b := by
simp [lookup_cons]
@[simp] theorem lookup_singleton {a b : α} : [(a,b)].lookup c = if c == a then some b else none := by
simp [lookup_cons, lookup_nil]
split <;> simp_all
theorem lookup_eq_findSome? {l : List (α × β)} {k : α} :
l.lookup k = l.findSome? fun p => if k == p.1 then some p.2 else none := by
induction l with
| nil => rfl
| cons p l ih =>
match p with
| (k', v) =>
simp only [lookup_cons, findSome?_cons]
split <;> simp_all
@[simp] theorem lookup_eq_none_iff {l : List (α × β)} {k : α} :
l.lookup k = none ↔ ∀ p ∈ l, k != p.1 := by
simp [lookup_eq_findSome?]
@[simp] theorem lookup_isSome_iff {l : List (α × β)} {k : α} :
(l.lookup k).isSome ↔ ∃ p ∈ l, k == p.1 := by
simp [lookup_eq_findSome?]
theorem lookup_eq_some_iff {l : List (α × β)} {k : α} {b : β} :
l.lookup k = some b ↔ ∃ l₁ l₂, l = l₁ ++ (k, b) :: l₂ ∧ ∀ p ∈ l₁, k != p.1 := by
simp only [lookup_eq_findSome?, findSome?_eq_some_iff]
constructor
· rintro ⟨l₁, a, l₂, rfl, h₁, h₂⟩
simp only [beq_iff_eq, Option.ite_none_right_eq_some, Option.some.injEq] at h₁
obtain ⟨rfl, rfl⟩ := h₁
simp at h₂
exact ⟨l₁, l₂, rfl, by simpa using h₂⟩
· rintro ⟨l₁, l₂, rfl, h⟩
exact ⟨l₁, (k, b), l₂, rfl, by simp, by simpa using h⟩
theorem lookup_append {l₁ l₂ : List (α × β)} {k : α} :
(l₁ ++ l₂).lookup k = (l₁.lookup k).or (l₂.lookup k) := by
simp [lookup_eq_findSome?, findSome?_append]
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]
theorem Sublist.lookup_isSome {l₁ l₂ : List (α × β)} (h : l₁ <+ l₂) :
(l₁.lookup k).isSome → (l₂.lookup k).isSome := by
simp only [lookup_eq_findSome?]
exact h.findSome?_isSome
theorem Sublist.lookup_eq_none {l₁ l₂ : List (α × β)} (h : l₁ <+ l₂) :
l₂.lookup k = none → l₁.lookup k = none := by
simp only [lookup_eq_findSome?]
exact h.findSome?_eq_none
theorem IsPrefix.lookup_eq_some {l₁ l₂ : List (α × β)} (h : l₁ <+: l₂) :
List.lookup k l₁ = some b → List.lookup k l₂ = some b := by
simp only [lookup_eq_findSome?]
exact h.findSome?_eq_some
theorem IsPrefix.lookup_eq_none {l₁ l₂ : List (α × β)} (h : l₁ <+: l₂) :
List.lookup k l₂ = none → List.lookup k l₁ = none :=
h.sublist.lookup_eq_none
theorem IsSuffix.lookup_eq_none {l₁ l₂ : List (α × β)} (h : l₁ <:+ l₂) :
List.lookup k l₂ = none → List.lookup k l₁ = none :=
h.sublist.lookup_eq_none
theorem IsInfix.lookup_eq_none {l₁ l₂ : List (α × β)} (h : l₁ <:+: l₂) :
List.lookup k l₂ = none → List.lookup k l₁ = none :=
h.sublist.lookup_eq_none
end lookup
end List