This PR removes some `grind` annotations for `Array.attach` and related functions. These lemmas introduce lambda on the right hand side which `grind` can't do much with. I've added a test file that verifies that the theorems with removed annotations can actually be proved already by grind. Removing the annotations will help with excessive instantiation.
820 lines
34 KiB
Text
820 lines
34 KiB
Text
/-
|
||
Copyright (c) 2021 Floris van Doorn. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Joachim Breitner, Mario Carneiro
|
||
-/
|
||
module
|
||
|
||
prelude
|
||
public import Init.Data.Array.Mem
|
||
public import Init.Data.Array.Lemmas
|
||
public import Init.Data.Array.Count
|
||
public import Init.Data.List.Attach
|
||
import all Init.Data.List.Attach
|
||
|
||
public section
|
||
|
||
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 Array
|
||
|
||
/--
|
||
Maps a partially defined function (defined on those terms of `α` that satisfy a predicate `P`) over
|
||
an array `xs : Array α`, given a proof that every element of `xs` in fact satisfies `P`.
|
||
|
||
`Array.pmap`, named for “partial map,” is the equivalent of `Array.map` for such partial functions.
|
||
-/
|
||
@[expose]
|
||
def pmap {P : α → Prop} (f : ∀ a, P a → β) (xs : Array α) (H : ∀ a ∈ xs, P a) : Array β :=
|
||
(xs.toList.pmap f (fun a m => H a (mem_def.mpr m))).toArray
|
||
|
||
/--
|
||
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
|
||
`Array {x // P x}` is the same as the input `Array α`.
|
||
-/
|
||
@[inline] private unsafe def attachWithImpl
|
||
(xs : Array α) (P : α → Prop) (_ : ∀ x ∈ xs, P x) : Array {x // P x} := unsafeCast xs
|
||
|
||
/--
|
||
“Attaches” individual proofs to an array of values that satisfy a predicate `P`, returning an array
|
||
of elements in the corresponding subtype `{ x // P x }`.
|
||
|
||
`O(1)`.
|
||
-/
|
||
@[implemented_by attachWithImpl, expose] def attachWith
|
||
(xs : Array α) (P : α → Prop) (H : ∀ x ∈ xs, P x) : Array {x // P x} :=
|
||
⟨xs.toList.attachWith P fun x h => H x (Array.Mem.mk h)⟩
|
||
|
||
/--
|
||
“Attaches” the proof that the elements of `xs` are in fact elements of `xs`, producing a new array with
|
||
the same elements but in the subtype `{ x // x ∈ xs }`.
|
||
|
||
`O(1)`.
|
||
|
||
This function is primarily used to allow definitions by [well-founded
|
||
recursion](lean-manual://section/well-founded-recursion) that use higher-order functions (such as
|
||
`Array.map`) to prove that an value taken from a list is smaller than the list. This allows the
|
||
well-founded recursion mechanism to prove that the function terminates.
|
||
-/
|
||
@[inline, expose] def attach (xs : Array α) : Array {x // x ∈ xs} := xs.attachWith _ fun _ => id
|
||
|
||
@[simp, grind =] theorem _root_.List.attachWith_toArray {l : List α} {P : α → Prop} {H : ∀ x ∈ l.toArray, P x} :
|
||
l.toArray.attachWith P H = (l.attachWith P (by simpa using H)).toArray := by
|
||
simp [attachWith]
|
||
|
||
@[simp, grind =] theorem _root_.List.attach_toArray {l : List α} :
|
||
l.toArray.attach = (l.attachWith (· ∈ l.toArray) (by simp)).toArray := by
|
||
simp [attach]
|
||
|
||
@[simp, grind =] theorem _root_.List.pmap_toArray {l : List α} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ l.toArray, P a} :
|
||
l.toArray.pmap f H = (l.pmap f (by simpa using H)).toArray := by
|
||
simp [pmap]
|
||
|
||
@[simp, grind =] theorem toList_attachWith {xs : Array α} {P : α → Prop} {H : ∀ x ∈ xs, P x} :
|
||
(xs.attachWith P H).toList = xs.toList.attachWith P (by simpa [mem_toList_iff] using H) := by
|
||
simp [attachWith]
|
||
|
||
@[simp, grind =] theorem toList_attach {xs : Array α} :
|
||
xs.attach.toList = xs.toList.attachWith (· ∈ xs) (by simp [mem_toList_iff]) := by
|
||
simp [attach]
|
||
|
||
@[simp, grind =] theorem toList_pmap {xs : Array α} {P : α → Prop} {f : ∀ a, P a → β} {H : ∀ a ∈ xs, P a} :
|
||
(xs.pmap f H).toList = xs.toList.pmap f (fun a m => H a (mem_def.mpr m)) := by
|
||
simp [pmap]
|
||
|
||
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
|
||
@[inline] private def pmapImpl {P : α → Prop} (f : ∀ a, P a → β) (xs : Array α) (H : ∀ a ∈ xs, P a) :
|
||
Array β := (xs.attachWith _ H).map fun ⟨x, h'⟩ => f x h'
|
||
|
||
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
|
||
funext α β p f xs H
|
||
cases xs
|
||
simp only [pmap, pmapImpl, List.attachWith_toArray, List.map_toArray, mk.injEq, List.map_attachWith_eq_pmap]
|
||
apply List.pmap_congr_left
|
||
intro a m h₁ h₂
|
||
congr
|
||
|
||
@[simp] theorem pmap_empty {P : α → Prop} (f : ∀ a, P a → β) : pmap f #[] (by simp) = #[] := rfl
|
||
|
||
@[simp] theorem pmap_push {P : α → Prop} (f : ∀ a, P a → β) (a : α) (xs : Array α) (h : ∀ b ∈ xs.push a, P b) :
|
||
pmap f (xs.push a) h =
|
||
(pmap f xs (fun a m => by simp at h; exact h a (.inl m))).push (f a (h a (by simp))) := by
|
||
simp [pmap]
|
||
|
||
@[simp] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
|
||
|
||
@[simp] theorem attachWith_empty {P : α → Prop} (H : ∀ x ∈ #[], P x) : (#[] : Array α).attachWith P H = #[] := rfl
|
||
|
||
@[simp] theorem _root_.List.attachWith_mem_toArray {l : List α} :
|
||
l.attachWith (fun x => x ∈ l.toArray) (fun x h => by simpa using h) =
|
||
l.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||
simp only [List.attachWith, List.attach, List.map_pmap]
|
||
apply List.pmap_congr_left
|
||
simp
|
||
|
||
@[simp]
|
||
theorem pmap_eq_map {p : α → Prop} {f : α → β} {xs : Array α} (H) :
|
||
@pmap _ _ p (fun a _ => f a) xs H = map f xs := by
|
||
cases xs; simp
|
||
|
||
theorem pmap_congr_left {p q : α → Prop} {f : ∀ a, p a → β} {g : ∀ a, q a → β} (xs : Array α) {H₁ H₂}
|
||
(h : ∀ a ∈ xs, ∀ (h₁ h₂), f a h₁ = g a h₂) : pmap f xs H₁ = pmap g xs H₂ := by
|
||
cases xs
|
||
simp only [List.mem_toArray] at h
|
||
simp only [List.pmap_toArray, mk.injEq]
|
||
rw [List.pmap_congr_left _ h]
|
||
|
||
theorem map_pmap {p : α → Prop} {g : β → γ} {f : ∀ a, p a → β} {xs : Array α} (H) :
|
||
map g (pmap f xs H) = pmap (fun a h => g (f a h)) xs H := by
|
||
cases xs
|
||
simp [List.map_pmap]
|
||
|
||
theorem pmap_map {p : β → Prop} {g : ∀ b, p b → γ} {f : α → β} {xs : Array α} (H) :
|
||
pmap g (map f xs) H = pmap (fun a h => g (f a) h) xs fun _ h => H _ (mem_map_of_mem h) := by
|
||
cases xs
|
||
simp [List.pmap_map]
|
||
|
||
theorem attach_congr {xs ys : Array α} (h : xs = ys) :
|
||
xs.attach = ys.attach.map (fun x => ⟨x.1, h ▸ x.2⟩) := by
|
||
subst h
|
||
simp
|
||
|
||
theorem attachWith_congr {xs ys : Array α} (w : xs = ys) {P : α → Prop} {H : ∀ x ∈ xs, P x} :
|
||
xs.attachWith P H = ys.attachWith P fun _ h => H _ (w ▸ h) := by
|
||
subst w
|
||
simp
|
||
|
||
@[simp] theorem attach_push {a : α} {xs : Array α} :
|
||
(xs.push a).attach =
|
||
(xs.attach.map (fun ⟨x, h⟩ => ⟨x, mem_push_of_mem a h⟩)).push ⟨a, by simp⟩ := by
|
||
cases xs
|
||
rw [attach_congr (List.push_toArray _ _)]
|
||
simp [Function.comp_def]
|
||
|
||
@[simp] theorem attachWith_push {a : α} {xs : Array α} {P : α → Prop} {H : ∀ x ∈ xs.push a, P x} :
|
||
(xs.push a).attachWith P H =
|
||
(xs.attachWith P (fun x h => by simp at H; exact H x (.inl h))).push ⟨a, H a (by simp)⟩ := by
|
||
cases xs
|
||
simp
|
||
|
||
theorem pmap_eq_map_attach {p : α → Prop} {f : ∀ a, p a → β} {xs : Array α} (H) :
|
||
pmap f xs H = xs.attach.map fun x => f x.1 (H _ x.2) := by
|
||
cases xs
|
||
simp [List.pmap_eq_map_attach]
|
||
|
||
@[simp]
|
||
theorem pmap_eq_attachWith {p q : α → Prop} {f : ∀ a, p a → q a} {xs : Array α} (H) :
|
||
pmap (fun a h => ⟨a, f a h⟩) xs H = xs.attachWith q (fun x h => f x (H x h)) := by
|
||
cases xs
|
||
simp [List.pmap_eq_attachWith]
|
||
|
||
theorem attach_map_val (xs : Array α) (f : α → β) :
|
||
(xs.attach.map fun (i : {i // i ∈ xs}) => f i) = xs.map f := by
|
||
cases xs
|
||
simp
|
||
|
||
-- The argument `xs : Array α` is explicit to allow rewriting from right to left.
|
||
theorem attach_map_subtype_val (xs : Array α) : xs.attach.map Subtype.val = xs := by
|
||
cases xs; simp
|
||
|
||
theorem attachWith_map_val {p : α → Prop} {f : α → β} {xs : Array α} (H : ∀ a ∈ xs, p a) :
|
||
((xs.attachWith p H).map fun (i : { i // p i}) => f i) = xs.map f := by
|
||
cases xs; simp
|
||
|
||
theorem attachWith_map_subtype_val {p : α → Prop} {xs : Array α} (H : ∀ a ∈ xs, p a) :
|
||
(xs.attachWith p H).map Subtype.val = xs := by
|
||
cases xs; simp
|
||
|
||
@[simp, grind ←]
|
||
theorem mem_attach (xs : Array α) : ∀ x, x ∈ xs.attach
|
||
| ⟨a, h⟩ => by
|
||
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
|
||
rcases this with ⟨⟨_, _⟩, m, rfl⟩
|
||
exact m
|
||
|
||
@[simp, grind =]
|
||
theorem mem_attachWith {xs : Array α} {q : α → Prop} (H) (x : {x // q x}) :
|
||
x ∈ xs.attachWith q H ↔ x.1 ∈ xs := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp, grind =]
|
||
theorem mem_pmap {p : α → Prop} {f : ∀ a, p a → β} {xs H b} :
|
||
b ∈ pmap f xs H ↔ ∃ (a : _) (h : a ∈ xs), f a (H a h) = b := by
|
||
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
|
||
|
||
theorem mem_pmap_of_mem {p : α → Prop} {f : ∀ a, p a → β} {xs H} {a} (h : a ∈ xs) :
|
||
f a (H a h) ∈ pmap f xs H := by
|
||
rw [mem_pmap]
|
||
exact ⟨a, h, rfl⟩
|
||
|
||
grind_pattern mem_pmap_of_mem => _ ∈ pmap f xs H, a ∈ xs
|
||
|
||
@[simp, grind =]
|
||
theorem size_pmap {p : α → Prop} {f : ∀ a, p a → β} {xs H} : (pmap f xs H).size = xs.size := by
|
||
cases xs; simp
|
||
|
||
@[simp, grind =]
|
||
theorem size_attach {xs : Array α} : xs.attach.size = xs.size := by
|
||
cases xs; simp
|
||
|
||
@[simp, grind =]
|
||
theorem size_attachWith {p : α → Prop} {xs : Array α} {H} : (xs.attachWith p H).size = xs.size := by
|
||
cases xs; simp
|
||
|
||
@[simp]
|
||
theorem pmap_eq_empty_iff {p : α → Prop} {f : ∀ a, p a → β} {xs H} : pmap f xs H = #[] ↔ xs = #[] := by
|
||
cases xs; simp
|
||
|
||
theorem pmap_ne_empty_iff {P : α → Prop} (f : (a : α) → P a → β) {xs : Array α}
|
||
(H : ∀ (a : α), a ∈ xs → P a) : xs.pmap f H ≠ #[] ↔ xs ≠ #[] := by
|
||
cases xs; simp
|
||
|
||
theorem pmap_eq_self {xs : Array α} {p : α → Prop} {hp : ∀ (a : α), a ∈ xs → p a}
|
||
{f : (a : α) → p a → α} : xs.pmap f hp = xs ↔ ∀ a (h : a ∈ xs), f a (hp a h) = a := by
|
||
cases xs; simp [List.pmap_eq_self]
|
||
|
||
@[simp]
|
||
theorem attach_eq_empty_iff {xs : Array α} : xs.attach = #[] ↔ xs = #[] := by
|
||
cases xs; simp
|
||
|
||
theorem attach_ne_empty_iff {xs : Array α} : xs.attach ≠ #[] ↔ xs ≠ #[] := by
|
||
cases xs; simp
|
||
|
||
@[simp]
|
||
theorem attachWith_eq_empty_iff {xs : Array α} {P : α → Prop} {H : ∀ a ∈ xs, P a} :
|
||
xs.attachWith P H = #[] ↔ xs = #[] := by
|
||
cases xs; simp
|
||
|
||
theorem attachWith_ne_empty_iff {xs : Array α} {P : α → Prop} {H : ∀ a ∈ xs, P a} :
|
||
xs.attachWith P H ≠ #[] ↔ xs ≠ #[] := by
|
||
cases xs; simp
|
||
|
||
@[simp, grind =]
|
||
theorem getElem?_pmap {p : α → Prop} {f : ∀ a, p a → β} {xs : Array α} (h : ∀ a ∈ xs, p a) (i : Nat) :
|
||
(pmap f xs h)[i]? = Option.pmap f xs[i]? fun x H => h x (mem_of_getElem? H) := by
|
||
cases xs; simp
|
||
|
||
-- The argument `f` is explicit to allow rewriting from right to left.
|
||
@[simp, grind =]
|
||
theorem getElem_pmap {p : α → Prop} (f : ∀ a, p a → β) {xs : Array α} (h : ∀ a ∈ xs, p a) {i : Nat}
|
||
(hi : i < (pmap f xs h).size) :
|
||
(pmap f xs h)[i] =
|
||
f (xs[i]'(@size_pmap _ _ p f xs h ▸ hi))
|
||
(h _ (getElem_mem (@size_pmap _ _ p f xs h ▸ hi))) := by
|
||
cases xs; simp
|
||
|
||
@[simp, grind =]
|
||
theorem getElem?_attachWith {xs : Array α} {i : Nat} {P : α → Prop} {H : ∀ a ∈ xs, P a} :
|
||
(xs.attachWith P H)[i]? = xs[i]?.pmap Subtype.mk (fun _ a => H _ (mem_of_getElem? a)) :=
|
||
getElem?_pmap ..
|
||
|
||
@[simp, grind =]
|
||
theorem getElem?_attach {xs : Array α} {i : Nat} :
|
||
xs.attach[i]? = xs[i]?.pmap Subtype.mk (fun _ a => mem_of_getElem? a) :=
|
||
getElem?_attachWith
|
||
|
||
@[simp, grind =]
|
||
theorem getElem_attachWith {xs : Array α} {P : α → Prop} {H : ∀ a ∈ xs, P a}
|
||
{i : Nat} (h : i < (xs.attachWith P H).size) :
|
||
(xs.attachWith P H)[i] = ⟨xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h))⟩ :=
|
||
getElem_pmap _ _ h
|
||
|
||
@[simp, grind =]
|
||
theorem getElem_attach {xs : Array α} {i : Nat} (h : i < xs.attach.size) :
|
||
xs.attach[i] = ⟨xs[i]'(by simpa using h), getElem_mem (by simpa using h)⟩ :=
|
||
getElem_attachWith h
|
||
|
||
@[simp] theorem pmap_attach {xs : Array α} {p : {x // x ∈ xs} → Prop} {f : ∀ a, p a → β} (H) :
|
||
pmap f xs.attach H =
|
||
xs.pmap (P := fun a => ∃ h : a ∈ xs, p ⟨a, h⟩)
|
||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨h, H ⟨a, h⟩ (by simp)⟩) := by
|
||
ext <;> simp
|
||
|
||
@[simp] theorem pmap_attachWith {xs : Array α} {p : {x // q x} → Prop} {f : ∀ a, p a → β} (H₁ H₂) :
|
||
pmap f (xs.attachWith q H₁) H₂ =
|
||
xs.pmap (P := fun a => ∃ h : q a, p ⟨a, h⟩)
|
||
(fun a h => f ⟨a, h.1⟩ h.2) (fun a h => ⟨H₁ _ h, H₂ ⟨a, H₁ _ h⟩ (by simpa)⟩) := by
|
||
ext <;> simp
|
||
|
||
theorem foldl_pmap {xs : Array α} {P : α → Prop} {f : (a : α) → P a → β}
|
||
(H : ∀ (a : α), a ∈ xs → P a) (g : γ → β → γ) (x : γ) :
|
||
(xs.pmap f H).foldl g x = xs.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
|
||
rw [pmap_eq_map_attach, foldl_map]
|
||
|
||
theorem foldr_pmap {xs : Array α} {P : α → Prop} {f : (a : α) → P a → β}
|
||
(H : ∀ (a : α), a ∈ xs → P a) (g : β → γ → γ) (x : γ) :
|
||
(xs.pmap f H).foldr g x = xs.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by
|
||
rw [pmap_eq_map_attach, foldr_map]
|
||
|
||
@[simp, grind =] theorem foldl_attachWith
|
||
{xs : Array α} {q : α → Prop} (H : ∀ a, a ∈ xs → q a) {f : β → { x // q x} → β} {b} (w : stop = xs.size) :
|
||
(xs.attachWith q H).foldl f b 0 stop = xs.attach.foldl (fun b ⟨a, h⟩ => f b ⟨a, H _ h⟩) b := by
|
||
subst w
|
||
rcases xs with ⟨xs⟩
|
||
simp [List.foldl_attachWith, List.foldl_map]
|
||
|
||
@[simp, grind =] theorem foldr_attachWith
|
||
{xs : Array α} {q : α → Prop} (H : ∀ a, a ∈ xs → q a) {f : { x // q x} → β → β} {b} (w : start = xs.size) :
|
||
(xs.attachWith q H).foldr f b start 0 = xs.attach.foldr (fun a acc => f ⟨a.1, H _ a.2⟩ acc) b := by
|
||
subst w
|
||
rcases xs with ⟨xs⟩
|
||
simp [List.foldr_attachWith, List.foldr_map]
|
||
|
||
/--
|
||
If we fold over `l.attach` with a function that ignores the membership predicate,
|
||
we get the same results as folding over `l` directly.
|
||
|
||
This is useful when we need to use `attach` to show termination.
|
||
|
||
Unfortunately this can't be applied by `simp` because of the higher order unification problem,
|
||
and even when rewriting we need to specify the function explicitly.
|
||
See however `foldl_subtype` below.
|
||
-/
|
||
theorem foldl_attach {xs : Array α} {f : β → α → β} {b : β} :
|
||
xs.attach.foldl (fun acc t => f acc t.1) b = xs.foldl f b := by
|
||
rcases xs with ⟨xs⟩
|
||
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
|
||
List.foldl_toArray', List.mem_toArray, List.foldl_subtype]
|
||
congr
|
||
ext
|
||
simpa using fun a => List.mem_of_getElem? a
|
||
|
||
/--
|
||
If we fold over `l.attach` with a function that ignores the membership predicate,
|
||
we get the same results as folding over `l` directly.
|
||
|
||
This is useful when we need to use `attach` to show termination.
|
||
|
||
Unfortunately this can't be applied by `simp` because of the higher order unification problem,
|
||
and even when rewriting we need to specify the function explicitly.
|
||
See however `foldr_subtype` below.
|
||
-/
|
||
theorem foldr_attach {xs : Array α} {f : α → β → β} {b : β} :
|
||
xs.attach.foldr (fun t acc => f t.1 acc) b = xs.foldr f b := by
|
||
rcases xs with ⟨xs⟩
|
||
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.size_toArray,
|
||
List.foldr_toArray', List.mem_toArray, List.foldr_subtype]
|
||
congr
|
||
ext
|
||
simpa using fun a => List.mem_of_getElem? a
|
||
|
||
theorem attach_map {xs : Array α} {f : α → β} :
|
||
(xs.map f).attach = xs.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem h⟩) := by
|
||
cases xs
|
||
ext <;> simp
|
||
|
||
theorem attachWith_map {xs : Array α} {f : α → β} {P : β → Prop} (H : ∀ (b : β), b ∈ xs.map f → P b) :
|
||
(xs.map f).attachWith P H = (xs.attachWith (P ∘ f) (fun _ h => H _ (mem_map_of_mem h))).map
|
||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||
cases xs
|
||
simp [List.attachWith_map]
|
||
|
||
@[simp] theorem map_attachWith {xs : Array α} {P : α → Prop} {H : ∀ (a : α), a ∈ xs → P a}
|
||
{f : { x // P x } → β} :
|
||
(xs.attachWith P H).map f = xs.attach.map fun ⟨x, h⟩ => f ⟨x, H _ h⟩ := by
|
||
cases xs <;> simp_all
|
||
|
||
theorem map_attachWith_eq_pmap {xs : Array α} {P : α → Prop} {H : ∀ (a : α), a ∈ xs → P a}
|
||
{f : { x // P x } → β} :
|
||
(xs.attachWith P H).map f =
|
||
xs.pmap (fun a (h : a ∈ xs ∧ P a) => f ⟨a, H _ h.1⟩) (fun a h => ⟨h, H a h⟩) := by
|
||
cases xs
|
||
ext <;> simp
|
||
|
||
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
|
||
theorem map_attach_eq_pmap {xs : Array α} {f : { x // x ∈ xs } → β} :
|
||
xs.attach.map f = xs.pmap (fun a h => f ⟨a, h⟩) (fun _ => id) := by
|
||
cases xs
|
||
ext <;> simp
|
||
|
||
@[grind =]
|
||
theorem attach_filterMap {xs : Array α} {f : α → Option β} :
|
||
(xs.filterMap f).attach = xs.attach.filterMap
|
||
fun ⟨x, h⟩ => (f x).pbind (fun b m => some ⟨b, mem_filterMap.mpr ⟨x, h, m⟩⟩) := by
|
||
cases xs
|
||
rw [attach_congr List.filterMap_toArray]
|
||
simp [List.attach_filterMap, List.map_filterMap, Function.comp_def]
|
||
|
||
@[grind =]
|
||
theorem attach_filter {xs : Array α} (p : α → Bool) :
|
||
(xs.filter p).attach = xs.attach.filterMap
|
||
fun x => if w : p x.1 then some ⟨x.1, mem_filter.mpr ⟨x.2, w⟩⟩ else none := by
|
||
cases xs
|
||
rw [attach_congr List.filter_toArray]
|
||
simp [List.attach_filter, List.map_filterMap, Function.comp_def]
|
||
|
||
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
|
||
|
||
@[simp, grind =]
|
||
theorem filterMap_attachWith {q : α → Prop} {xs : Array α} {f : {x // q x} → Option β} (H)
|
||
(w : stop = (xs.attachWith q H).size) :
|
||
(xs.attachWith q H).filterMap f 0 stop = xs.attach.filterMap (fun ⟨x, h⟩ => f ⟨x, H _ h⟩) := by
|
||
subst w
|
||
cases xs
|
||
simp [Function.comp_def]
|
||
|
||
@[simp, grind =]
|
||
theorem filter_attachWith {q : α → Prop} {xs : Array α} {p : {x // q x} → Bool} (H)
|
||
(w : stop = (xs.attachWith q H).size) :
|
||
(xs.attachWith q H).filter p 0 stop =
|
||
(xs.attach.filter (fun ⟨x, h⟩ => p ⟨x, H _ h⟩)).map (fun ⟨x, h⟩ => ⟨x, H _ h⟩) := by
|
||
subst w
|
||
cases xs
|
||
simp [Function.comp_def, List.filter_map]
|
||
|
||
theorem pmap_pmap {p : α → Prop} {q : β → Prop} {g : ∀ a, p a → β} {f : ∀ b, q b → γ} {xs} (H₁ H₂) :
|
||
pmap f (pmap g xs H₁) H₂ =
|
||
pmap (α := { x // x ∈ xs }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) xs.attach
|
||
(fun a _ => H₁ a a.2) := by
|
||
cases xs
|
||
simp [List.pmap_pmap, List.pmap_map]
|
||
|
||
@[simp] theorem pmap_append {p : ι → Prop} {f : ∀ a : ι, p a → α} {xs ys : Array ι}
|
||
(h : ∀ a ∈ xs ++ ys, p a) :
|
||
(xs ++ ys).pmap f h =
|
||
(xs.pmap f fun a ha => h a (mem_append_left ys ha)) ++
|
||
ys.pmap f fun a ha => h a (mem_append_right xs ha) := by
|
||
cases xs
|
||
cases ys
|
||
simp
|
||
|
||
theorem pmap_append' {p : α → Prop} {f : ∀ a : α, p a → β} {xs ys : Array α}
|
||
(h₁ : ∀ a ∈ xs, p a) (h₂ : ∀ a ∈ ys, p a) :
|
||
((xs ++ ys).pmap f fun a ha => (mem_append.1 ha).elim (h₁ a) (h₂ a)) =
|
||
xs.pmap f h₁ ++ ys.pmap f h₂ :=
|
||
pmap_append _
|
||
|
||
@[simp] theorem attach_append {xs ys : Array α} :
|
||
(xs ++ ys).attach = xs.attach.map (fun ⟨x, h⟩ => ⟨x, mem_append_left ys h⟩) ++
|
||
ys.attach.map fun ⟨x, h⟩ => ⟨x, mem_append_right xs h⟩ := by
|
||
cases xs
|
||
cases ys
|
||
rw [attach_congr (List.append_toArray _ _)]
|
||
simp [List.attach_append, Function.comp_def]
|
||
|
||
@[simp] theorem attachWith_append {P : α → Prop} {xs ys : Array α}
|
||
{H : ∀ (a : α), a ∈ xs ++ ys → P a} :
|
||
(xs ++ ys).attachWith P H = xs.attachWith P (fun a h => H a (mem_append_left ys h)) ++
|
||
ys.attachWith P (fun a h => H a (mem_append_right xs h)) := by
|
||
simp [attachWith]
|
||
|
||
@[simp] theorem pmap_reverse {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||
(H : ∀ (a : α), a ∈ xs.reverse → P a) :
|
||
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
|
||
induction xs <;> simp_all
|
||
|
||
theorem reverse_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
|
||
rw [pmap_reverse]
|
||
|
||
@[simp] theorem attachWith_reverse {P : α → Prop} {xs : Array α}
|
||
{H : ∀ (a : α), a ∈ xs.reverse → P a} :
|
||
xs.reverse.attachWith P H =
|
||
(xs.attachWith P (fun a h => H a (by simpa using h))).reverse := by
|
||
cases xs
|
||
simp
|
||
|
||
theorem reverse_attachWith {P : α → Prop} {xs : Array α}
|
||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||
(xs.attachWith P H).reverse = (xs.reverse.attachWith P (fun a h => H a (by simpa using h))) := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp] theorem attach_reverse {xs : Array α} :
|
||
xs.reverse.attach = xs.attach.reverse.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||
cases xs
|
||
rw [attach_congr List.reverse_toArray]
|
||
simp
|
||
|
||
theorem reverse_attach {xs : Array α} :
|
||
xs.attach.reverse = xs.reverse.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp] theorem back?_pmap {P : α → Prop} {f : (a : α) → P a → β} {xs : Array α}
|
||
(H : ∀ (a : α), a ∈ xs → P a) :
|
||
(xs.pmap f H).back? = xs.attach.back?.map fun ⟨a, m⟩ => f a (H a m) := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp] theorem back?_attachWith {P : α → Prop} {xs : Array α}
|
||
{H : ∀ (a : α), a ∈ xs → P a} :
|
||
(xs.attachWith P H).back? = xs.back?.pbind (fun a h => some ⟨a, H _ (mem_of_back? h)⟩) := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp]
|
||
theorem back?_attach {xs : Array α} :
|
||
xs.attach.back? = xs.back?.pbind fun a h => some ⟨a, mem_of_back? h⟩ := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp]
|
||
theorem countP_attach {xs : Array α} {p : α → Bool} :
|
||
xs.attach.countP (fun a : {x // x ∈ xs} => p a) = xs.countP p := by
|
||
cases xs
|
||
simp [Function.comp_def]
|
||
|
||
@[simp]
|
||
theorem countP_attachWith {p : α → Prop} {q : α → Bool} {xs : Array α} {H : ∀ a ∈ xs, p a} :
|
||
(xs.attachWith p H).countP (fun a : {x // p x} => q a) = xs.countP q := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp, grind =]
|
||
theorem count_attach [BEq α] {xs : Array α} {a : {x // x ∈ xs}} :
|
||
xs.attach.count a = xs.count ↑a := by
|
||
rcases xs with ⟨xs⟩
|
||
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.count_toArray]
|
||
rw [List.map_attach_eq_pmap, List.count_eq_countP]
|
||
simp only [Subtype.beq_iff]
|
||
rw [List.countP_pmap, List.countP_attach (p := (fun x => x == a.1)), List.count]
|
||
|
||
@[simp, grind =]
|
||
theorem count_attachWith [BEq α] {p : α → Prop} {xs : Array α} (H : ∀ a ∈ xs, p a) {a : {x // p x}} :
|
||
(xs.attachWith p H).count a = xs.count ↑a := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp, grind =] theorem countP_pmap {p : α → Prop} {g : ∀ a, p a → β} {f : β → Bool} {xs : Array α} (H₁) :
|
||
(xs.pmap g H₁).countP f =
|
||
xs.attach.countP (fun ⟨a, m⟩ => f (g a (H₁ a m))) := by
|
||
simp [pmap_eq_map_attach, countP_map, Function.comp_def]
|
||
|
||
/-! ## unattach
|
||
|
||
`Array.unattach` is the (one-sided) inverse of `Array.attach`. It is a synonym for `Array.map Subtype.val`.
|
||
|
||
We use it by providing a simp lemma `l.attach.unattach = l`, and simp lemmas which recognize higher order
|
||
functions applied to `l : Array { x // p x }` which only depend on the value, not the predicate, and rewrite these
|
||
in terms of a simpler function applied to `l.unattach`.
|
||
|
||
Further, we provide simp lemmas that push `unattach` inwards.
|
||
-/
|
||
|
||
/--
|
||
Maps an array of terms in a subtype to the corresponding terms in the type by forgetting that they
|
||
satisfy the predicate.
|
||
|
||
This is the inverse of `Array.attachWith` and a synonym for `xs.map (·.val)`.
|
||
|
||
Mostly this should not be needed by users. It is introduced as an intermediate step by lemmas such
|
||
as `map_subtype`, and is ideally subsequently simplified away by `unattach_attach`.
|
||
|
||
This function is usually inserted automatically by Lean as an intermediate step while proving
|
||
termination. It is rarely used explicitly in code. It is introduced as an intermediate step during
|
||
the elaboration of definitions by [well-founded
|
||
recursion](lean-manual://section/well-founded-recursion). If this function is encountered in a proof
|
||
state, the right approach is usually the tactic `simp [Array.unattach, -Array.map_subtype]`.
|
||
-/
|
||
def unattach {α : Type _} {p : α → Prop} (xs : Array { x // p x }) : Array α := xs.map (·.val)
|
||
|
||
@[simp] theorem unattach_empty {p : α → Prop} : (#[] : Array { x // p x }).unattach = #[] := by
|
||
simp [unattach]
|
||
|
||
@[deprecated unattach_empty (since := "2025-05-26")]
|
||
abbrev unattach_nil := @unattach_empty
|
||
|
||
@[simp] theorem unattach_push {p : α → Prop} {a : { x // p x }} {xs : Array { x // p x }} :
|
||
(xs.push a).unattach = xs.unattach.push a.1 := by
|
||
simp only [unattach, Array.map_push]
|
||
|
||
@[simp] theorem mem_unattach {p : α → Prop} {xs : Array { x // p x }} {a} :
|
||
a ∈ xs.unattach ↔ ∃ h : p a, ⟨a, h⟩ ∈ xs := by
|
||
simp only [unattach, mem_map, Subtype.exists, exists_and_right, exists_eq_right]
|
||
|
||
@[simp] theorem size_unattach {p : α → Prop} {xs : Array { x // p x }} :
|
||
xs.unattach.size = xs.size := by
|
||
unfold unattach
|
||
simp
|
||
|
||
@[simp, grind =] theorem _root_.List.unattach_toArray {p : α → Prop} {xs : List { x // p x }} :
|
||
xs.toArray.unattach = xs.unattach.toArray := by
|
||
simp only [unattach, List.map_toArray, List.unattach]
|
||
|
||
@[simp] theorem toList_unattach {p : α → Prop} {xs : Array { x // p x }} :
|
||
xs.unattach.toList = xs.toList.unattach := by
|
||
simp only [unattach, toList_map, List.unattach]
|
||
|
||
@[simp] theorem unattach_attach {xs : Array α} : xs.attach.unattach = xs := by
|
||
cases xs
|
||
simp only [List.attach_toArray, List.unattach_toArray, List.unattach_attachWith]
|
||
|
||
@[simp] theorem unattach_attachWith {p : α → Prop} {xs : Array α}
|
||
{H : ∀ a ∈ xs, p a} :
|
||
(xs.attachWith p H).unattach = xs := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp] theorem getElem?_unattach {p : α → Prop} {xs : Array { x // p x }} (i : Nat) :
|
||
xs.unattach[i]? = xs[i]?.map Subtype.val := by
|
||
simp [unattach]
|
||
|
||
@[simp] theorem getElem_unattach
|
||
{p : α → Prop} {xs : Array { x // p x }} (i : Nat) (h : i < xs.unattach.size) :
|
||
xs.unattach[i] = (xs[i]'(by simpa using h)).1 := by
|
||
simp [unattach]
|
||
|
||
/-! ### Recognizing higher order functions using a function that only depends on the value. -/
|
||
|
||
/--
|
||
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||
and simplifies these to the function directly taking the value.
|
||
-/
|
||
theorem foldl_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : β → { x // p x } → β} {g : β → α → β} {x : β}
|
||
(hf : ∀ b x h, f b ⟨x, h⟩ = g b x) :
|
||
xs.foldl f x = xs.unattach.foldl g x := by
|
||
cases xs
|
||
simp only [List.foldl_toArray', List.unattach_toArray]
|
||
rw [List.foldl_subtype] -- Why can't simp do this?
|
||
simp [hf]
|
||
|
||
/-- Variant of `foldl_subtype` with side condition to check `stop = l.size`. -/
|
||
@[simp] theorem foldl_subtype' {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : β → { x // p x } → β} {g : β → α → β} {x : β}
|
||
(hf : ∀ b x h, f b ⟨x, h⟩ = g b x) (h : stop = xs.size) :
|
||
xs.foldl f x 0 stop = xs.unattach.foldl g x := by
|
||
subst h
|
||
rwa [foldl_subtype]
|
||
|
||
/--
|
||
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||
and simplifies these to the function directly taking the value.
|
||
-/
|
||
theorem foldr_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : { x // p x } → β → β} {g : α → β → β} {x : β}
|
||
(hf : ∀ x h b, f ⟨x, h⟩ b = g x b) :
|
||
xs.foldr f x = xs.unattach.foldr g x := by
|
||
cases xs
|
||
simp only [List.foldr_toArray', List.unattach_toArray]
|
||
rw [List.foldr_subtype]
|
||
simp [hf]
|
||
|
||
/-- Variant of `foldr_subtype` with side condition to check `stop = l.size`. -/
|
||
@[simp] theorem foldr_subtype' {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : { x // p x } → β → β} {g : α → β → β} {x : β}
|
||
(hf : ∀ x h b, f ⟨x, h⟩ b = g x b) (h : start = xs.size) :
|
||
xs.foldr f x start 0 = xs.unattach.foldr g x := by
|
||
subst h
|
||
rwa [foldr_subtype]
|
||
|
||
/--
|
||
This lemma identifies maps over arrays of subtypes, where the function only depends on the value, not the proposition,
|
||
and simplifies these to the function directly taking the value.
|
||
-/
|
||
@[simp] theorem map_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : { x // p x } → β} {g : α → β} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||
xs.map f = xs.unattach.map g := by
|
||
cases xs
|
||
simp only [List.map_toArray, List.unattach_toArray]
|
||
rw [List.map_subtype]
|
||
simp [hf]
|
||
|
||
@[simp] theorem filterMap_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : { x // p x } → Option β} {g : α → Option β} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||
xs.filterMap f = xs.unattach.filterMap g := by
|
||
cases xs
|
||
simp only [List.size_toArray, List.filterMap_toArray', List.unattach_toArray, List.length_unattach,
|
||
mk.injEq]
|
||
rw [List.filterMap_subtype]
|
||
simp [hf]
|
||
|
||
|
||
@[simp] theorem flatMap_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : { x // p x } → Array β} {g : α → Array β} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||
(xs.flatMap f) = xs.unattach.flatMap g := by
|
||
cases xs
|
||
simp only [List.flatMap_toArray, List.unattach_toArray,
|
||
mk.injEq]
|
||
rw [List.flatMap_subtype]
|
||
simp [hf]
|
||
|
||
@[simp] theorem findSome?_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : { x // p x } → Option β} {g : α → Option β} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||
xs.findSome? f = xs.unattach.findSome? g := by
|
||
cases xs
|
||
simp
|
||
rw [List.findSome?_subtype hf]
|
||
|
||
@[simp] theorem find?_subtype {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||
(xs.find? f).map Subtype.val = xs.unattach.find? g := by
|
||
cases xs
|
||
simp
|
||
rw [List.find?_subtype hf]
|
||
|
||
@[simp] theorem all_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||
xs.all f 0 stop = xs.unattach.all g := by
|
||
subst w
|
||
rcases xs with ⟨xs⟩
|
||
simp [hf]
|
||
|
||
@[simp] theorem any_subtype {p : α → Prop} {xs : Array { x // p x }} {f : { x // p x } → Bool} {g : α → Bool}
|
||
(hf : ∀ x h, f ⟨x, h⟩ = g x) (w : stop = xs.size) :
|
||
xs.any f 0 stop = xs.unattach.any g := by
|
||
subst w
|
||
rcases xs with ⟨xs⟩
|
||
simp [hf]
|
||
|
||
/-! ### Simp lemmas pushing `unattach` inwards. -/
|
||
|
||
@[simp] theorem unattach_filter {p : α → Prop} {xs : Array { x // p x }}
|
||
{f : { x // p x } → Bool} {g : α → Bool} (hf : ∀ x h, f ⟨x, h⟩ = g x) :
|
||
(xs.filter f).unattach = xs.unattach.filter g := by
|
||
cases xs
|
||
simp [hf]
|
||
|
||
@[simp] theorem unattach_reverse {p : α → Prop} {xs : Array { x // p x }} :
|
||
xs.reverse.unattach = xs.unattach.reverse := by
|
||
cases xs
|
||
simp
|
||
|
||
@[simp] theorem unattach_append {p : α → Prop} {xs₁ xs₂ : Array { x // p x }} :
|
||
(xs₁ ++ xs₂).unattach = xs₁.unattach ++ xs₂.unattach := by
|
||
cases xs₁
|
||
cases xs₂
|
||
simp
|
||
|
||
@[simp] theorem unattach_flatten {p : α → Prop} {xs : Array (Array { x // p x })} :
|
||
xs.flatten.unattach = (xs.map unattach).flatten := by
|
||
unfold unattach
|
||
cases xs using array₂_induction
|
||
simp only [flatten_toArray, List.map_map, Function.comp_def, List.map_id_fun', id_eq,
|
||
List.map_toArray, List.map_flatten, map_subtype, map_id_fun', List.unattach_toArray, mk.injEq]
|
||
simp only [List.unattach]
|
||
|
||
@[simp] theorem unattach_replicate {p : α → Prop} {n : Nat} {x : { x // p x }} :
|
||
(Array.replicate n x).unattach = Array.replicate n x.1 := by
|
||
simp [unattach]
|
||
|
||
@[deprecated unattach_replicate (since := "2025-03-18")]
|
||
abbrev unattach_mkArray := @unattach_replicate
|
||
|
||
/-! ### Well-founded recursion preprocessing setup -/
|
||
|
||
@[wf_preprocess] theorem map_wfParam {xs : Array α} {f : α → β} :
|
||
(wfParam xs).map f = xs.attach.unattach.map f := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem map_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → β} :
|
||
xs.unattach.map f = xs.map fun ⟨x, h⟩ =>
|
||
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem foldl_wfParam {xs : Array α} {f : β → α → β} {x : β} :
|
||
(wfParam xs).foldl f x = xs.attach.unattach.foldl f x := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem foldl_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : β → α → β} {x : β} :
|
||
xs.unattach.foldl f x = xs.foldl (fun s ⟨x, h⟩ =>
|
||
binderNameHint s f <| binderNameHint x (f s) <| binderNameHint h () <| f s (wfParam x)) x := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem foldr_wfParam {xs : Array α} {f : α → β → β} {x : β} :
|
||
(wfParam xs).foldr f x = xs.attach.unattach.foldr f x := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem foldr_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → β → β} {x : β} :
|
||
xs.unattach.foldr f x = xs.foldr (fun ⟨x, h⟩ s =>
|
||
binderNameHint x f <| binderNameHint s (f x) <| binderNameHint h () <| f (wfParam x) s) x := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem filter_wfParam {xs : Array α} {f : α → Bool} :
|
||
(wfParam xs).filter f = xs.attach.unattach.filter f:= by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem filter_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → Bool} :
|
||
xs.unattach.filter f = (xs.filter (fun ⟨x, h⟩ =>
|
||
binderNameHint x f <| binderNameHint h () <| f (wfParam x))).unattach := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem reverse_wfParam {xs : Array α} :
|
||
(wfParam xs).reverse = xs.attach.unattach.reverse := by simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem reverse_unattach {P : α → Prop} {xs : Array (Subtype P)} :
|
||
xs.unattach.reverse = xs.reverse.unattach := by simp
|
||
|
||
@[wf_preprocess] theorem filterMap_wfParam {xs : Array α} {f : α → Option β} :
|
||
(wfParam xs).filterMap f = xs.attach.unattach.filterMap f := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem filterMap_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → Option β} :
|
||
xs.unattach.filterMap f = xs.filterMap fun ⟨x, h⟩ =>
|
||
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem flatMap_wfParam {xs : Array α} {f : α → Array β} :
|
||
(wfParam xs).flatMap f = xs.attach.unattach.flatMap f := by
|
||
simp [wfParam]
|
||
|
||
@[wf_preprocess] theorem flatMap_unattach {P : α → Prop} {xs : Array (Subtype P)} {f : α → Array β} :
|
||
xs.unattach.flatMap f = xs.flatMap fun ⟨x, h⟩ =>
|
||
binderNameHint x f <| binderNameHint h () <| f (wfParam x) := by
|
||
simp [wfParam]
|
||
|
||
end Array
|