feat: add theorems relating find? with findIdx? and findFinIdx? (#12204)

This PR adds theorems showing the consistency between `find?` and the
various index-finding functions. The theorems establish bidirectional
relationships between finding elements and finding their indices.

**Forward direction** (find? in terms of index):
- `find?_eq_map_findFinIdx?_getElem`: `xs.find? p = (xs.findFinIdx?
p).map (xs[·])`
- `find?_eq_bind_findIdx?_getElem?`: `xs.find? p = (xs.findIdx? p).bind
(xs[·]?)`
- `find?_eq_getElem?_findIdx`: `xs.find? p = xs[xs.findIdx p]?`

**Reverse direction** (index in terms of find?):
- `findIdx?_eq_bind_find?_idxOf?`: `xs.findIdx? p = (xs.find? p).bind
(xs.idxOf?)`
- `findFinIdx?_eq_bind_find?_finIdxOf?`: `xs.findFinIdx? p = (xs.find?
p).bind (xs.finIdxOf?)`
- `findIdx_eq_getD_bind_find?_idxOf?`: `xs.findIdx p = ((xs.find?
p).bind (xs.idxOf?)).getD xs.length`

All theorems are provided for `List`, `Array`, and `Vector` (where
applicable).

Requested at
https://leanprover.zulipchat.com/#narrow/channel/113488-general/topic/show.20that.20Array.2Efind.3F.20and.20Array.2EfindFinIdx.3F.20consistent/near/567340199

🤖 Prepared with Claude Code

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Kim Morrison 2026-01-28 15:55:29 +11:00 committed by GitHub
parent e1b19198a9
commit fa4cd6d78c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 129 additions and 7 deletions

View file

@ -674,6 +674,39 @@ theorem isNone_findFinIdx? {xs : Array α} {p : α → Bool} :
simp only [Option.map_map, Function.comp_def, Fin.cast_cast]
simp [Array.size]
/-! ### find? and findFinIdx? -/
theorem find?_eq_map_findFinIdx?_getElem {xs : Array α} {p : α → Bool} :
xs.find? p = (xs.findFinIdx? p).map (xs[·]) := by
cases xs
simp [List.find?_eq_map_findFinIdx?_getElem]
rfl
theorem find?_eq_bind_findIdx?_getElem? {xs : Array α} {p : α → Bool} :
xs.find? p = (xs.findIdx? p).bind (xs[·]?) := by
cases xs
simp [List.find?_eq_bind_findIdx?_getElem?]
theorem find?_eq_getElem?_findIdx {xs : Array α} {p : α → Bool} :
xs.find? p = xs[xs.findIdx p]? := by
cases xs
simp [List.find?_eq_getElem?_findIdx]
theorem findIdx?_eq_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α → Bool} :
xs.findIdx? p = (xs.find? p).bind (xs.idxOf? ·) := by
cases xs
simp [List.findIdx?_eq_bind_find?_idxOf?]
theorem findFinIdx?_eq_bind_find?_finIdxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α → Bool} :
xs.findFinIdx? p = (xs.find? p).bind (xs.finIdxOf? ·) := by
cases xs
simp [List.findFinIdx?_eq_bind_find?_finIdxOf?]
theorem findIdx_eq_getD_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : Array α} {p : α → Bool} :
xs.findIdx p = ((xs.find? p).bind (xs.idxOf? ·)).getD xs.size := by
cases xs
simp [List.findIdx_eq_getD_bind_find?_idxOf?]
/-! ### idxOf
The verification API for `idxOf` is still incomplete.

View file

@ -1077,6 +1077,37 @@ theorem isNone_findFinIdx? {l : List α} {p : α → Bool} :
simp [hf, findFinIdx?_cons]
split <;> simp [ih, Function.comp_def]
/-! ### find? and findFinIdx? -/
theorem find?_eq_map_findFinIdx?_getElem {xs : List α} {p : α → Bool} :
xs.find? p = (xs.findFinIdx? p).map (xs[·]) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, findFinIdx?_cons]
split <;> rename_i h
· simp [h]
· simp [h, ih, Function.comp_def]
theorem find?_eq_bind_findIdx?_getElem? {xs : List α} {p : α → Bool} :
xs.find? p = (xs.findIdx? p).bind (xs[·]?) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, findIdx?_cons]
split <;> rename_i h
· simp [h]
· simp [h, ih, Option.bind_map, Function.comp_def]
theorem find?_eq_getElem?_findIdx {xs : List α} {p : α → Bool} :
xs.find? p = xs[xs.findIdx p]? := by
induction xs with
| nil => simp
| cons x xs ih =>
simp only [find?_cons, findIdx_cons]
split <;> rename_i h
· simp [h]
· simp [h, ih]
/-! ### idxOf
@ -1103,8 +1134,6 @@ theorem idxOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
· rw [if_neg]
simpa using h
theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∉ l) : l.idxOf a = l.length := by
induction l with
| nil => rfl
@ -1113,8 +1142,6 @@ theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∉ l) : l.
simp only [idxOf_cons, cond_eq_ite, beq_iff_eq]
split <;> simp_all
theorem idxOf_lt_length_of_mem [BEq α] [EquivBEq α] {l : List α} (h : a ∈ l) : l.idxOf a < l.length := by
induction l with
| nil => simp at h
@ -1143,8 +1170,6 @@ theorem idxOf_lt_length_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
grind_pattern idxOf_lt_length_iff => l.idxOf a, l.length
/-! ### finIdxOf?
The verification API for `finIdxOf?` is still incomplete.
@ -1214,7 +1239,9 @@ The lemmas below should be made consistent with those for `findIdx?` (and proved
· rintro w x h rfl
contradiction
theorem idxOf?_eq_some_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} {i : Nat} :
l.idxOf? a = some i ↔ ∃ (h : i < l.length), l[i] = a ∧ ∀ j (_ : j < i), ¬l[j] = a := by
simp [idxOf?, findIdx?_eq_some_iff_getElem]
@[simp, grind =]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
@ -1230,6 +1257,56 @@ theorem isNone_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :
(l.idxOf? a).isNone = ¬ a ∈ l := by
simp
theorem finIdxOf?_eq_pmap_idxOf? {l : List α} {a : α} [BEq α] [LawfulBEq α] :
l.finIdxOf? a =
(l.idxOf? a).pmap
(fun i h => ⟨i, (idxOf?_eq_some_iff.mp h).1⟩)
(fun _ h => h) := by
ext ⟨i, h⟩
simp only [finIdxOf?_eq_some_iff, Fin.getElem_fin, Fin.forall_iff, Fin.mk_lt_mk,
idxOf?_eq_some_iff, Option.pmap_eq_some_iff, Fin.mk.injEq, exists_and_left, exists_prop,
and_self_left, exists_eq_right', h, exists_true_left, and_congr_right_iff]
intro w
constructor
· intro w j h₁
apply w <;> omega
· intro w j h₁ h₂
apply w <;> omega
/-! ### find? and idxOf? -/
theorem findIdx?_eq_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : List α} {p : α → Bool} :
xs.findIdx? p = (xs.find? p).bind (xs.idxOf?) := by
induction xs with
| nil => simp
| cons x xs ih =>
simp [findIdx?_cons, find?_cons]
split <;> rename_i h
· simp [h, idxOf?_cons]
· simp [h, ih, Function.comp_def, idxOf?_cons]
cases w : xs.find? p with
| none => simp
| some x' =>
simp
rintro rfl
have := find?_some w
contradiction
theorem findFinIdx?_eq_bind_find?_finIdxOf? [BEq α] [LawfulBEq α] {xs : List α} {p : α → Bool} :
xs.findFinIdx? p = (xs.find? p).bind (xs.finIdxOf?) := by
simp only [findFinIdx?_eq_pmap_findIdx?, findIdx?_eq_bind_find?_idxOf?, finIdxOf?_eq_pmap_idxOf?]
ext i
simp only [Option.bind_eq_some_iff, Option.pmap_eq_some_iff, exists_and_left, and_exists_self]
constructor
· rintro ⟨a, ⟨h₁, h₂⟩, rfl⟩
exact ⟨h₁, by simp [h₂]⟩
· rintro ⟨h₁, h₂, a, h₃, rfl⟩
exact ⟨a, ⟨h₁, h₂, h₃⟩, rfl⟩
theorem findIdx_eq_getD_bind_find?_idxOf? [BEq α] [LawfulBEq α] {xs : List α} {p : α → Bool} :
xs.findIdx p = ((xs.find? p).bind (xs.idxOf?)).getD xs.length := by
rw [findIdx_eq_getD_findIdx?, findIdx?_eq_bind_find?_idxOf?]
/-! ### lookup -/
section lookup

View file

@ -341,4 +341,16 @@ theorem isNone_findFinIdx? {xs : Vector α n} {p : α → Bool} :
rcases xs with ⟨xs, rfl⟩
simp [hf, Function.comp_def]
/-! ### find? and findFinIdx? -/
theorem find?_eq_map_findFinIdx?_getElem {xs : Vector α n} {p : α → Bool} :
xs.find? p = (xs.findFinIdx? p).map (xs[·]) := by
rcases xs with ⟨xs, rfl⟩
simp [Array.find?_eq_map_findFinIdx?_getElem]
theorem findFinIdx?_eq_bind_find?_finIdxOf? [BEq α] [LawfulBEq α] {xs : Vector α n} {p : α → Bool} :
xs.findFinIdx? p = (xs.find? p).bind (xs.finIdxOf? ·) := by
rcases xs with ⟨xs, rfl⟩
simp [Array.findFinIdx?_eq_bind_find?_finIdxOf?]
end Vector