178 lines
6.9 KiB
Text
178 lines
6.9 KiB
Text
/-
|
||
Copyright (c) 2024 Lean FRO. All rights reserved.
|
||
Released under Apache 2.0 license as described in the file LICENSE.
|
||
Authors: Kim Morrison
|
||
-/
|
||
prelude
|
||
import Init.Data.Option.Basic
|
||
import Init.Data.Option.List
|
||
import Init.Data.List.Attach
|
||
import Init.BinderPredicates
|
||
|
||
namespace Option
|
||
|
||
/--
|
||
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
|
||
`Option {x // P x}` is the same as the input `Option α`.
|
||
-/
|
||
@[inline] private unsafe def attachWithImpl
|
||
(o : Option α) (P : α → Prop) (_ : ∀ x ∈ o, P x) : Option {x // P x} := unsafeCast o
|
||
|
||
/-- "Attach" a proof `P x` that holds for the element of `o`, if present,
|
||
to produce a new option with the same element but in the type `{x // P x}`. -/
|
||
@[implemented_by attachWithImpl] def attachWith
|
||
(xs : Option α) (P : α → Prop) (H : ∀ x ∈ xs, P x) : Option {x // P x} :=
|
||
match xs with
|
||
| none => none
|
||
| some x => some ⟨x, H x (mem_some_self x)⟩
|
||
|
||
/-- "Attach" the proof that the element of `xs`, if present, is in `xs`
|
||
to produce a new option with the same elements but in the type `{x // x ∈ xs}`. -/
|
||
@[inline] def attach (xs : Option α) : Option {x // x ∈ xs} := xs.attachWith _ fun _ => id
|
||
|
||
@[simp] theorem attach_none : (none : Option α).attach = none := rfl
|
||
@[simp] theorem attachWith_none : (none : Option α).attachWith P H = none := rfl
|
||
|
||
@[simp] theorem attach_some {x : α} :
|
||
(some x).attach = some ⟨x, rfl⟩ := rfl
|
||
@[simp] theorem attachWith_some {x : α} {P : α → Prop} (h : ∀ (b : α), b ∈ some x → P b) :
|
||
(some x).attachWith P h = some ⟨x, by simpa using h⟩ := rfl
|
||
|
||
theorem attach_congr {o₁ o₂ : Option α} (h : o₁ = o₂) :
|
||
o₁.attach = o₂.attach.map (fun x => ⟨x.1, h ▸ x.2⟩) := by
|
||
subst h
|
||
simp
|
||
|
||
theorem attachWith_congr {o₁ o₂ : Option α} (w : o₁ = o₂) {P : α → Prop} {H : ∀ x ∈ o₁, P x} :
|
||
o₁.attachWith P H = o₂.attachWith P fun x h => H _ (w ▸ h) := by
|
||
subst w
|
||
simp
|
||
|
||
theorem attach_map_coe (o : Option α) (f : α → β) :
|
||
(o.attach.map fun (i : {i // i ∈ o}) => f i) = o.map f := by
|
||
cases o <;> simp
|
||
|
||
theorem attach_map_val (o : Option α) (f : α → β) :
|
||
(o.attach.map fun i => f i.val) = o.map f :=
|
||
attach_map_coe _ _
|
||
|
||
@[simp]
|
||
theorem attach_map_subtype_val (o : Option α) :
|
||
o.attach.map Subtype.val = o :=
|
||
(attach_map_coe _ _).trans (congrFun Option.map_id _)
|
||
|
||
theorem attachWith_map_coe {p : α → Prop} (f : α → β) (o : Option α) (H : ∀ a ∈ o, p a) :
|
||
((o.attachWith p H).map fun (i : { i // p i}) => f i.val) = o.map f := by
|
||
cases o <;> simp [H]
|
||
|
||
theorem attachWith_map_val {p : α → Prop} (f : α → β) (o : Option α) (H : ∀ a ∈ o, p a) :
|
||
((o.attachWith p H).map fun i => f i.val) = o.map f :=
|
||
attachWith_map_coe _ _ _
|
||
|
||
@[simp]
|
||
theorem attachWith_map_subtype_val {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||
(o.attachWith p H).map Subtype.val = o :=
|
||
(attachWith_map_coe _ _ _).trans (congrFun Option.map_id _)
|
||
|
||
@[simp] theorem mem_attach : ∀ (o : Option α) (x : {x // x ∈ o}), x ∈ o.attach
|
||
| none, ⟨x, h⟩ => by simp at h
|
||
| some a, ⟨x, h⟩ => by simpa using h
|
||
|
||
@[simp] theorem isNone_attach (o : Option α) : o.attach.isNone = o.isNone := by
|
||
cases o <;> simp
|
||
|
||
@[simp] theorem isNone_attachWith {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||
(o.attachWith p H).isNone = o.isNone := by
|
||
cases o <;> simp
|
||
|
||
@[simp] theorem isSome_attach (o : Option α) : o.attach.isSome = o.isSome := by
|
||
cases o <;> simp
|
||
|
||
@[simp] theorem isSome_attachWith {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||
(o.attachWith p H).isSome = o.isSome := by
|
||
cases o <;> simp
|
||
|
||
@[simp] theorem attach_eq_none_iff (o : Option α) : o.attach = none ↔ o = none := by
|
||
cases o <;> simp
|
||
|
||
@[simp] theorem attach_eq_some_iff {o : Option α} {x : {x // x ∈ o}} :
|
||
o.attach = some x ↔ o = some x.val := by
|
||
cases o <;> cases x <;> simp
|
||
|
||
@[simp] theorem attachWith_eq_none_iff {p : α → Prop} (o : Option α) (H : ∀ a ∈ o, p a) :
|
||
o.attachWith p H = none ↔ o = none := by
|
||
cases o <;> simp
|
||
|
||
@[simp] theorem attachWith_eq_some_iff {p : α → Prop} {o : Option α} (H : ∀ a ∈ o, p a) {x : {x // p x}} :
|
||
o.attachWith p H = some x ↔ o = some x.val := by
|
||
cases o <;> cases x <;> simp
|
||
|
||
@[simp] theorem get_attach {o : Option α} (h : o.attach.isSome = true) :
|
||
o.attach.get h = ⟨o.get (by simpa using h), by simp⟩ := by
|
||
cases o
|
||
· simp at h
|
||
· simp [get_some]
|
||
|
||
@[simp] theorem get_attachWith {p : α → Prop} {o : Option α} (H : ∀ a ∈ o, p a) (h : (o.attachWith p H).isSome) :
|
||
(o.attachWith p H).get h = ⟨o.get (by simpa using h), H _ (by simp)⟩ := by
|
||
cases o
|
||
· simp at h
|
||
· simp [get_some]
|
||
|
||
@[simp] theorem toList_attach (o : Option α) :
|
||
o.attach.toList = o.toList.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
|
||
cases o <;> simp
|
||
|
||
theorem attach_map {o : Option α} (f : α → β) :
|
||
(o.map f).attach = o.attach.map (fun ⟨x, h⟩ => ⟨f x, mem_map_of_mem f h⟩) := by
|
||
cases o <;> simp
|
||
|
||
theorem attachWith_map {o : Option α} (f : α → β) {P : β → Prop} {H : ∀ (b : β), b ∈ o.map f → P b} :
|
||
(o.map f).attachWith P H = (o.attachWith (P ∘ f) (fun a h => H _ (mem_map_of_mem f h))).map
|
||
fun ⟨x, h⟩ => ⟨f x, h⟩ := by
|
||
cases o <;> simp
|
||
|
||
theorem map_attach {o : Option α} (f : { x // x ∈ o } → β) :
|
||
o.attach.map f = o.pmap (fun a (h : a ∈ o) => f ⟨a, h⟩) (fun a h => h) := by
|
||
cases o <;> simp
|
||
|
||
theorem map_attachWith {o : Option α} {P : α → Prop} {H : ∀ (a : α), a ∈ o → P a}
|
||
(f : { x // P x } → β) :
|
||
(o.attachWith P H).map f =
|
||
o.pmap (fun a (h : a ∈ o ∧ P a) => f ⟨a, h.2⟩) (fun a h => ⟨h, H a h⟩) := by
|
||
cases o <;> simp
|
||
|
||
theorem attach_bind {o : Option α} {f : α → Option β} :
|
||
(o.bind f).attach =
|
||
o.attach.bind fun ⟨x, h⟩ => (f x).attach.map fun ⟨y, h'⟩ => ⟨y, mem_bind_iff.mpr ⟨x, h, h'⟩⟩ := by
|
||
cases o <;> simp
|
||
|
||
theorem bind_attach {o : Option α} {f : {x // x ∈ o} → Option β} :
|
||
o.attach.bind f = o.pbind fun a h => f ⟨a, h⟩ := by
|
||
cases o <;> simp
|
||
|
||
theorem pbind_eq_bind_attach {o : Option α} {f : (a : α) → a ∈ o → Option β} :
|
||
o.pbind f = o.attach.bind fun ⟨x, h⟩ => f x h := by
|
||
cases o <;> simp
|
||
|
||
theorem attach_filter {o : Option α} {p : α → Bool} :
|
||
(o.filter p).attach =
|
||
o.attach.bind fun ⟨x, h⟩ => if h' : p x then some ⟨x, by simp_all⟩ else none := by
|
||
cases o with
|
||
| none => simp
|
||
| some a =>
|
||
simp only [filter_some, attach_some]
|
||
ext
|
||
simp only [mem_def, attach_eq_some_iff, ite_none_right_eq_some, some.injEq, some_bind,
|
||
dite_none_right_eq_some]
|
||
constructor
|
||
· rintro ⟨h, w⟩
|
||
refine ⟨h, by ext; simpa using w⟩
|
||
· rintro ⟨h, rfl⟩
|
||
simp [h]
|
||
|
||
theorem filter_attach {o : Option α} {p : {x // x ∈ o} → Bool} :
|
||
o.attach.filter p = o.pbind fun a h => if p ⟨a, h⟩ then some ⟨a, h⟩ else none := by
|
||
cases o <;> simp [filter_some]
|
||
|
||
end Option
|