This PR cleans up the style of the library in anticipation of a future PR that requires strict indentation for tactic sequences.
1535 lines
58 KiB
Text
1535 lines
58 KiB
Text
/-!
|
||
This file consists of everything from `Init.Data.List.Lemmas` that `grind` doesn't deal with effortlessly,
|
||
sometimes with incomplete efforts to `grindify` the proofs.
|
||
|
||
It may still be a good source of ideas for `grind` attributes, or `grind` bugs!
|
||
But it's also fine to just delete it at some point.
|
||
-/
|
||
|
||
-- Rejected `grind` attributes:
|
||
-- attribute [grind] List.getElem?_eq_getElem -- This is way too slow, it adds about 30% time to this file.
|
||
-- attribute [grind] List.not_mem_nil -- unnecessary
|
||
-- attribute [grind →] List.length_eq_of_beq -- very bad!
|
||
-- attribute [grind] List.getLastD_concat
|
||
-- attribute [grind] List.head?_eq_getElem?
|
||
-- attribute [grind] LawfulMonad.bind_assoc -- time out?
|
||
-- attribute [grind] List.getLast?_flatten
|
||
-- attribute [grind] List.getElem?_eq_getElem -- too slow
|
||
|
||
namespace List'
|
||
|
||
open List Nat
|
||
|
||
/-! ## Preliminaries -/
|
||
|
||
/-! ### length -/
|
||
|
||
|
||
-- theorem exists_mem_of_length_pos : ∀ {l : List α}, 0 < length l → ∃ a, a ∈ l := by grind
|
||
|
||
-- theorem length_pos_iff_exists_mem {l : List α} : 0 < length l ↔ ∃ a, a ∈ l := by grind
|
||
|
||
-- theorem exists_mem_of_length_eq_add_one :
|
||
-- ∀ {l : List α}, l.length = n + 1 → ∃ a, a ∈ l := by grind
|
||
|
||
-- theorem exists_cons_of_length_pos : ∀ {l : List α}, 0 < l.length → ∃ h t, l = h :: t := by grind
|
||
|
||
-- theorem length_pos_iff_exists_cons :
|
||
-- ∀ {l : List α}, 0 < l.length ↔ ∃ h t, l = h :: t := by grind
|
||
|
||
-- theorem exists_cons_of_length_eq_add_one :
|
||
-- ∀ {l : List α}, l.length = n + 1 → ∃ h t, l = h :: t := by grind
|
||
|
||
-- theorem length_eq_one_iff {l : List α} : length l = 1 ↔ ∃ a, l = [a] := by grind
|
||
|
||
/-! ### cons -/
|
||
|
||
-- theorem cons_ne_self (a : α) (l : List α) : a :: l ≠ l := by grind
|
||
|
||
-- theorem ne_cons_self {a : α} {l : List α} : l ≠ a :: l := by grind
|
||
|
||
-- theorem exists_cons_of_ne_nil : ∀ {l : List α}, l ≠ [] → ∃ b l', l = b :: l' := by grind
|
||
|
||
-- theorem ne_nil_iff_exists_cons {l : List α} : l ≠ [] ↔ ∃ b l', l = b :: l' := by grind
|
||
|
||
|
||
-- theorem concat_ne_nil (a : α) (l : List α) : l ++ [a] ≠ [] := by grind
|
||
|
||
/-! ## L[i] and L[i]? -/
|
||
|
||
/-! ### getElem? and getElem -/
|
||
|
||
-- theorem getElem?_eq_some_iff {l : List α} : l[i]? = some a ↔ ∃ h : i < l.length, l[i] = a := by
|
||
-- induction l
|
||
-- · grind
|
||
-- · cases i with grind -- reported
|
||
|
||
theorem some_eq_getElem?_iff {l : List α} : some a = l[i]? ↔ ∃ h : i < l.length, l[i] = a := by
|
||
rw [eq_comm, getElem?_eq_some_iff]
|
||
|
||
theorem some_getElem_eq_getElem?_iff {xs : List α} {i : Nat} (h : i < xs.length) :
|
||
(some xs[i] = xs[i]?) ↔ True := by
|
||
simp
|
||
|
||
theorem getElem?_eq_some_getElem_iff {xs : List α} {i : Nat} (h : i < xs.length) :
|
||
(xs[i]? = some xs[i]) ↔ True := by
|
||
simp
|
||
|
||
theorem getElem_eq_iff {l : List α} {i : Nat} (h : i < l.length) : l[i] = x ↔ l[i]? = some x := by
|
||
simp only [getElem?_eq_some_iff]
|
||
exact ⟨fun w => ⟨h, w⟩, fun h => h.2⟩
|
||
|
||
theorem getElem_eq_getElem?_get {l : List α} {i : Nat} (h : i < l.length) :
|
||
l[i] = l[i]?.get (by simp [getElem?_eq_getElem, h]) := by
|
||
simp [getElem_eq_iff]
|
||
|
||
theorem getD_getElem? {l : List α} {i : Nat} {d : α} :
|
||
l[i]?.getD d = if p : i < l.length then l[i]'p else d := by
|
||
if h : i < l.length then
|
||
simp [h, getElem?_def]
|
||
else
|
||
have p : i ≥ l.length := Nat.le_of_not_gt h
|
||
simp [getElem?_eq_none p, h]
|
||
|
||
theorem getElem_singleton {a : α} {i : Nat} (h : i < 1) : [a][i] = a :=
|
||
match i, h with
|
||
| 0, _ => rfl
|
||
|
||
theorem getElem?_singleton {a : α} {i : Nat} : [a][i]? = if i = 0 then some a else none := by
|
||
simp [getElem?_cons]
|
||
|
||
theorem getElem_zero {l : List α} (h : 0 < l.length) : l[0] = l.head (length_pos_iff.mp h) :=
|
||
match l, h with
|
||
| _ :: _, _ => rfl
|
||
|
||
@[ext] theorem ext_getElem? {l₁ l₂ : List α} (h : ∀ i : Nat, l₁[i]? = l₂[i]?) : l₁ = l₂ :=
|
||
match l₁, l₂, h with
|
||
| [], [], _ => by grind
|
||
| _ :: _, [], h => by simpa using h 0
|
||
| [], _ :: _, h => by simpa using h 0
|
||
| a :: l₁, a' :: l₂, h => by
|
||
have h0 : some a = some a' := by simpa using h 0
|
||
injection h0 with aa; simp only [aa, ext_getElem? fun n => by simpa using h (n+1)]
|
||
|
||
theorem ext_getElem {l₁ l₂ : List α} (hl : length l₁ = length l₂)
|
||
(h : ∀ (i : Nat) (h₁ : i < l₁.length) (h₂ : i < l₂.length), l₁[i]'h₁ = l₂[i]'h₂) : l₁ = l₂ :=
|
||
ext_getElem? fun n =>
|
||
if h₁ : n < length l₁ then by
|
||
simp_all [getElem?_eq_getElem]
|
||
else by
|
||
have h₁ := Nat.le_of_not_lt h₁
|
||
rw [getElem?_eq_none h₁, getElem?_eq_none]; rwa [← hl]
|
||
|
||
theorem getElem_concat_length {l : List α} {a : α} {i : Nat} (h : i = l.length) (w) :
|
||
(l ++ [a])[i]'w = a := by
|
||
subst h; grind -- doesn't work without `subst` first?
|
||
|
||
|
||
/-! ### mem -/
|
||
|
||
|
||
-- theorem exists_mem_cons {p : α → Prop} {a : α} {l : List α} :
|
||
-- (∃ x, ∃ _ : x ∈ a :: l, p x) ↔ p a ∨ ∃ x, ∃ _ : x ∈ l, p x := by grind -- fails
|
||
|
||
-- It would be great if we have some mechanism to make further progress with
|
||
-- `∀ (x : α), ¬x ∈ a :: l ∨ ¬p x`, using `mem_cons : x ∈ a :: l ↔ x = a ∨ x ∈ l`.
|
||
|
||
|
||
|
||
theorem getElem_of_mem : ∀ {a} {l : List α}, a ∈ l → ∃ (i : Nat) (h : i < l.length), l[i]'h = a
|
||
| _, _ :: _, .head .. => ⟨0, by grind⟩
|
||
| _, _ :: _, .tail _ m => let ⟨i, h, e⟩ := getElem_of_mem m; ⟨i+1, by grind, by grind⟩
|
||
|
||
theorem getElem?_of_mem {a} {l : List α} (h : a ∈ l) : ∃ i : Nat, l[i]? = some a := by
|
||
let ⟨n, _, e⟩ := getElem_of_mem h
|
||
exact ⟨n, e ▸ getElem?_eq_getElem _⟩
|
||
|
||
theorem mem_iff_getElem {a} {l : List α} : a ∈ l ↔ ∃ (i : Nat) (h : i < l.length), l[i]'h = a :=
|
||
⟨getElem_of_mem, by grind⟩
|
||
|
||
theorem mem_iff_getElem? {a} {l : List α} : a ∈ l ↔ ∃ i : Nat, l[i]? = some a := by
|
||
simp [getElem?_eq_some_iff, mem_iff_getElem]
|
||
|
||
theorem forall_getElem {l : List α} {p : α → Prop} :
|
||
(∀ (i : Nat) h, p (l[i]'h)) ↔ ∀ a, a ∈ l → p a := by
|
||
induction l with
|
||
| nil => grind
|
||
| cons a l ih =>
|
||
simp only [length_cons, mem_cons, forall_eq_or_imp]
|
||
constructor
|
||
· intro w
|
||
constructor
|
||
· exact w 0 (by grind)
|
||
· grind
|
||
· rintro ⟨h, w⟩ (_ | n) <;> grind
|
||
|
||
/-! ### `isEmpty` -/
|
||
|
||
theorem isEmpty_eq_false_iff_exists_mem {xs : List α} :
|
||
xs.isEmpty = false ↔ ∃ x, x ∈ xs := by
|
||
cases xs <;> simp
|
||
|
||
/-! ### any / all -/
|
||
|
||
-- Perhaps waiting on improvements to grind's handling of `decide`?
|
||
theorem any_eq {l : List α} : l.any p = decide (∃ x, x ∈ l ∧ p x) := by induction l <;> simp [*]
|
||
|
||
theorem all_eq {l : List α} : l.all p = decide (∀ x, x ∈ l → p x) := by induction l <;> simp [*]
|
||
|
||
theorem decide_exists_mem {l : List α} {p : α → Prop} [DecidablePred p] :
|
||
decide (∃ x, x ∈ l ∧ p x) = l.any p := by
|
||
simp [any_eq]
|
||
|
||
theorem decide_forall_mem {l : List α} {p : α → Prop} [DecidablePred p] :
|
||
decide (∀ x, x ∈ l → p x) = l.all p := by
|
||
simp [all_eq]
|
||
|
||
theorem any_eq_true {l : List α} : l.any p = true ↔ ∃ x, x ∈ l ∧ p x := by
|
||
simp only [any_eq, decide_eq_true_eq]
|
||
|
||
theorem all_eq_true {l : List α} : l.all p = true ↔ ∀ x, x ∈ l → p x := by
|
||
simp only [all_eq, decide_eq_true_eq]
|
||
|
||
theorem any_eq_false {l : List α} : l.any p = false ↔ ∀ x, x ∈ l → ¬p x := by
|
||
simp [any_eq]
|
||
|
||
theorem all_eq_false {l : List α} : l.all p = false ↔ ∃ x, x ∈ l ∧ ¬p x := by
|
||
simp [all_eq]
|
||
|
||
-- Consider `attribute [grind PartialEquivBEq.symm]`?
|
||
theorem any_beq' [BEq α] [PartialEquivBEq α] {l : List α} :
|
||
(l.any fun x => x == a) = l.contains a := by
|
||
induction l with grind [PartialEquivBEq.symm]
|
||
|
||
theorem all_bne [BEq α] {l : List α} : (l.all fun x => a != x) = !l.contains a := by
|
||
induction l <;> simp_all [bne]
|
||
|
||
/-- Variant of `all_bne` with `!=` reversed. -/
|
||
theorem all_bne' [BEq α] [PartialEquivBEq α] {l : List α} :
|
||
(l.all fun x => x != a) = !l.contains a := by
|
||
simp only [bne_comm, all_bne]
|
||
|
||
/-! ### set -/
|
||
|
||
|
||
set_option trace.grind.ematch.instance true in
|
||
set_option trace.grind.ematch.instance.assignment true in
|
||
theorem getElem?_set_self' {l : List α} {i : Nat} {a : α} :
|
||
(set l i a)[i]? = Function.const _ a <$> l[i]? := by
|
||
by_cases h : i < l.length
|
||
· simp [getElem?_set_self h, getElem?_eq_getElem h]
|
||
· simp only [Nat.not_lt] at h
|
||
simpa [getElem?_eq_none_iff.2 h]
|
||
|
||
theorem getElem?_set' {l : List α} {i j : Nat} {a : α} :
|
||
(set l i a)[j]? = if i = j then Function.const _ a <$> l[j]? else l[j]? := by
|
||
by_cases i = j
|
||
· -- FIXME
|
||
-- I think this is failing to instantiate `List.getElem?_eq_none`,
|
||
-- because it knows `i + 1 ≤ l.length` is false, but not that `l.length ≤ i`.
|
||
-- grind
|
||
simp only [getElem?_set_self', Option.map_eq_map, ↓reduceIte, *]
|
||
· grind
|
||
|
||
theorem set_getElem_self {as : List α} {i : Nat} (h : i < as.length) :
|
||
as.set i as[i] = as := by
|
||
-- `grind` fails, `grind +extAll` loops forever
|
||
apply ext_getElem <;> grind
|
||
|
||
theorem mem_set {l : List α} {i : Nat} (h : i < l.length) (a : α) :
|
||
a ∈ l.set i a := by
|
||
rw [mem_iff_getElem]
|
||
grind
|
||
|
||
theorem mem_or_eq_of_mem_set : ∀ {l : List α} {i : Nat} {a b : α}, a ∈ l.set i b → a ∈ l ∨ a = b
|
||
| _ :: _, 0, _, _, h => by grind
|
||
| _ :: _, _+1, _, _, .head .. => by grind
|
||
-- FIXME without the type annotation on `h` we get stuck on an unfolded `Mem`
|
||
| _ :: l, n+1, a, _, .tail _ (h : a ∈ l.set n _) => by grind
|
||
|
||
/-! ### BEq -/
|
||
|
||
theorem reflBEq_iff [BEq α] : ReflBEq (List α) ↔ ReflBEq α := by
|
||
constructor
|
||
· intro h
|
||
constructor
|
||
intro a
|
||
suffices ([a] == [a]) = true by
|
||
simpa only [List.instBEq, List.beq, Bool.and_true]
|
||
simp
|
||
· intro h
|
||
constructor
|
||
intro l
|
||
induction l with
|
||
| nil => simp only [List.instBEq, List.beq]
|
||
| cons _ _ ih =>
|
||
simp [List.instBEq, List.beq]
|
||
|
||
theorem lawfulBEq_iff [BEq α] : LawfulBEq (List α) ↔ LawfulBEq α := by
|
||
constructor
|
||
· intro h
|
||
have : ReflBEq α := reflBEq_iff.mp inferInstance
|
||
constructor
|
||
intro a b h
|
||
apply singleton_inj.1
|
||
apply eq_of_beq
|
||
simp only [List.instBEq, List.beq]
|
||
grind
|
||
· intro h
|
||
infer_instance
|
||
|
||
/-! ### isEqv -/
|
||
|
||
theorem isEqv_eq [DecidableEq α] {l₁ l₂ : List α} : l₁.isEqv l₂ (· == ·) = (l₁ = l₂) := by
|
||
induction l₁ generalizing l₂ with
|
||
| nil => cases l₂ <;> simp
|
||
| cons a l₁ ih =>
|
||
cases l₂ with
|
||
| nil => simp
|
||
| cons b l₂ => simp [isEqv, ih]
|
||
|
||
/-! ### getLast -/
|
||
|
||
-- theorem _root_.List.length_pos_of_ne_nil {l : List α} (h : l ≠ []) : 0 < l.length := by
|
||
-- cases l <;> simp_all
|
||
|
||
-- attribute [grind] List.length_pos_of_ne_nil -- FIXME bad!
|
||
|
||
theorem getLast_eq_getElem : ∀ {l : List α} (h : l ≠ []),
|
||
getLast l h = l[l.length - 1]'(by sorry)
|
||
| [_], _ => rfl -- FIXME by grind -- Can't see that [head].length - 1 = 0?
|
||
| _ :: _ :: _, _ => by
|
||
-- FIXME?
|
||
simp [getLast, Nat.succ_sub_succ, getLast_eq_getElem]
|
||
|
||
-- FIXME?
|
||
theorem getLast_eq_getLastD {a l} (h) : @getLast α (a::l) h = getLastD l a := by
|
||
cases l <;> rfl
|
||
|
||
theorem getLast!_cons_eq_getLastD [Inhabited α] : @getLast! α _ (a::l) = getLastD l a := by
|
||
simp [getLast!, getLast_eq_getLastD]
|
||
|
||
theorem getLast_mem : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ l
|
||
| [], h => absurd rfl h
|
||
| [_], _ => .head ..
|
||
| _::a::l, _ => .tail _ <| getLast_mem (cons_ne_nil a l)
|
||
|
||
theorem getLast_mem_getLast? : ∀ {l : List α} (h : l ≠ []), getLast l h ∈ getLast? l
|
||
| [], h => by contradiction
|
||
| _ :: _, _ => rfl
|
||
|
||
theorem getLastD_mem_cons : ∀ {l : List α} {a : α}, getLastD l a ∈ a::l
|
||
| [], _ => .head ..
|
||
| _::_, _ => .tail _ <| getLast_mem _
|
||
|
||
theorem getElem_cons_length {x : α} {xs : List α} {i : Nat} (h : i = xs.length) :
|
||
(x :: xs)[i]'(by simp [h]) = (x :: xs).getLast (cons_ne_nil x xs) := by
|
||
rw [getLast_eq_getElem]; cases h; rfl
|
||
|
||
/-! ### getLast? -/
|
||
|
||
theorem getLast_eq_iff_getLast?_eq_some {xs : List α} (h) :
|
||
xs.getLast h = a ↔ xs.getLast? = some a := by
|
||
grind [getLast?_eq_getLast]
|
||
|
||
/-! ### getLast! -/
|
||
|
||
theorem getLast!_nil [Inhabited α] : ([] : List α).getLast! = default := by grind
|
||
|
||
theorem getLast!_eq_getLast?_getD [Inhabited α] {l : List α} : getLast! l = (getLast? l).getD default := by
|
||
cases l with grind
|
||
|
||
theorem getLast!_of_getLast? [Inhabited α] : ∀ {l : List α}, getLast? l = some a → getLast! l = a
|
||
| _ :: _, rfl => rfl
|
||
|
||
theorem getLast!_eq_getElem! [Inhabited α] {l : List α} : l.getLast! = l[l.length - 1]! := by
|
||
cases l with grind [getLast!_of_getLast?]
|
||
|
||
/-! ## Head and tail -/
|
||
|
||
/-! ### head -/
|
||
|
||
theorem head?_singleton {a : α} : head? [a] = some a := by grind
|
||
|
||
set_option linter.unusedVariables false in -- See https://github.com/leanprover/lean4/issues/5259
|
||
theorem head!_of_head? [Inhabited α] : ∀ {l : List α}, head? l = some a → head! l = a
|
||
| _ :: _, rfl => rfl
|
||
|
||
theorem head?_eq_head : ∀ {l : List α} h, l.head? = some (head l h)
|
||
| _ :: _, _ => rfl
|
||
|
||
theorem head_mem : ∀ {l : List α} (h : l ≠ []), head l h ∈ l
|
||
| [], h => absurd rfl h
|
||
| _::_, _ => .head ..
|
||
|
||
/-! ### tail -/
|
||
|
||
theorem tail_eq_tail? {l : List α} : l.tail = (tail? l).getD [] := by grind [tail_eq_tailD]
|
||
|
||
theorem head_tail {l : List α} (h : l.tail ≠ []) :
|
||
(tail l).head h = l[1]'(one_lt_length_of_tail_ne_nil h) := by
|
||
cases l with grind
|
||
|
||
theorem head?_tail {l : List α} : (tail l).head? = l[1]? := by
|
||
grind [List.head?_eq_getElem?]
|
||
|
||
theorem getLast_tail {l : List α} (h : l.tail ≠ []) :
|
||
(tail l).getLast h = l.getLast (ne_nil_of_tail_ne_nil h) := by grind
|
||
|
||
theorem getLast?_tail {l : List α} : (tail l).getLast? = if l.length = 1 then none else l.getLast? := by
|
||
match l with
|
||
| [] | [_] | _ :: _ :: _ => grind
|
||
|
||
/-! ## Basic operations -/
|
||
|
||
/-! ### map -/
|
||
|
||
-- FIXME work out how to get grind to do something useful here!
|
||
-- The argument `f : α → β` is explicit, to facilitate rewriting from right to left.
|
||
theorem getElem_map (f : α → β) {l} {i : Nat} {h : i < (map f l).length} :
|
||
(map f l)[i] = f (l[i]'(length_map f ▸ h)) :=
|
||
Option.some.inj <| by rw [← getElem?_eq_getElem, getElem?_map, getElem?_eq_getElem]; rfl
|
||
|
||
@[simp 500] theorem mem_map {f : α → β} {l : List α} : b ∈ l.map f ↔ ∃ a, a ∈ l ∧ f a = b := by
|
||
induction l with
|
||
| nil => grind
|
||
| cons a l ih => simp [ih, eq_comm (a := b)] -- FIXME what is grind missing here?
|
||
|
||
theorem exists_of_mem_map (h : b ∈ map f l) : ∃ a, a ∈ l ∧ f a = b := mem_map.1 h
|
||
|
||
theorem mem_map_of_mem {f : α → β} (h : a ∈ l) : f a ∈ map f l := mem_map.2 ⟨_, h, rfl⟩
|
||
|
||
theorem forall_mem_map {f : α → β} {l : List α} {P : β → Prop} :
|
||
(∀ (i) (_ : i ∈ l.map f), P i) ↔ ∀ (j) (_ : j ∈ l), P (f j) := by
|
||
simp
|
||
|
||
-- example {f : α → β} (w : ∀ x y, f x = f y → x = y) (x y : α) (h : f x = f y) : x = y := by
|
||
-- grind -- Reported
|
||
|
||
theorem map_inj_right {f : α → β} (w : ∀ x y, f x = f y → x = y) : map f l = map f l' ↔ l = l' := by
|
||
induction l generalizing l' with
|
||
| nil => grind
|
||
| cons a l ih =>
|
||
simp only [map_cons]
|
||
cases l' with
|
||
| nil => grind
|
||
| cons a' l' =>
|
||
simp only [map_cons, cons.injEq, ih, and_congr_left_iff]
|
||
intro h
|
||
constructor
|
||
· apply w
|
||
· grind
|
||
|
||
theorem map_congr_left (h : ∀ a ∈ l, f a = g a) : map f l = map g l :=
|
||
map_inj_left.2 h
|
||
|
||
theorem map_inj : map f = map g ↔ f = g := by
|
||
constructor
|
||
· intro h; ext a; replace h := congrFun h [a]; grind
|
||
· grind
|
||
|
||
theorem map_eq_cons_iff {f : α → β} {l : List α} :
|
||
map f l = b :: l₂ ↔ ∃ a l₁, l = a :: l₁ ∧ f a = b ∧ map f l₁ = l₂ := by
|
||
cases l
|
||
case nil => grind
|
||
case cons a l₁ =>
|
||
simp only [map_cons, cons.injEq]
|
||
-- sad that grind can't do this
|
||
constructor <;> grind
|
||
|
||
theorem tailD_map {f : α → β} {l l' : List α} :
|
||
tailD (map f l) (map f l') = map f (tailD l l') := by sorry -- simp [← map_tail?]
|
||
|
||
theorem getLast_map {f : α → β} {l : List α} (h) :
|
||
getLast (map f l) h = f (getLast l (by simpa using h)) := by
|
||
cases l
|
||
· grind
|
||
· simp only [← getElem_cons_length rfl]
|
||
simp only [map_cons]
|
||
simp only [← getElem_cons_length rfl]
|
||
simp only [← map_cons, getElem_map]
|
||
grind
|
||
|
||
theorem getLast?_map {f : α → β} {l : List α} : (map f l).getLast? = l.getLast?.map f := by
|
||
cases l
|
||
· simp
|
||
· rw [getLast?_eq_getLast, getLast?_eq_getLast, getLast_map] <;> simp
|
||
|
||
|
||
/-! ### filter -/
|
||
|
||
theorem length_filter_eq_length_iff {l} : (filter p l).length = l.length ↔ ∀ a ∈ l, p a := by
|
||
induction l with
|
||
| nil => grind
|
||
| cons a l ih =>
|
||
simp only [mem_cons]
|
||
grind
|
||
|
||
theorem filter_eq_nil_iff {l} : filter p l = [] ↔ ∀ a, a ∈ l → ¬p a := by
|
||
simp only [eq_nil_iff_forall_not_mem, mem_filter, not_and]
|
||
|
||
theorem filter_eq_cons_iff {l} {a} {as} :
|
||
filter p l = a :: as ↔
|
||
∃ l₁ l₂, l = l₁ ++ a :: l₂ ∧ (∀ x, x ∈ l₁ → ¬p x) ∧ p a ∧ filter p l₂ = as := by
|
||
constructor
|
||
· induction l with
|
||
| nil => grind
|
||
| cons x l ih =>
|
||
intro h
|
||
simp only [filter_cons] at h
|
||
split at h <;> rename_i w
|
||
· simp only [cons.injEq] at h
|
||
obtain ⟨rfl, rfl⟩ := h
|
||
exact ⟨[], l, by grind⟩
|
||
· obtain ⟨l₁, l₂, rfl, w₁, w₂, w₃⟩ := ih h
|
||
exact ⟨x :: l₁, l₂, by grind⟩
|
||
· rintro ⟨l₁, l₂, rfl, h₁, h, h₂⟩
|
||
have := filter_eq_nil_iff.mpr h₁
|
||
grind
|
||
|
||
theorem filter_congr {p q : α → Bool} :
|
||
∀ {l : List α}, (∀ x ∈ l, p x = q x) → filter p l = filter q l
|
||
| [], _ => rfl
|
||
| a :: l, h => by
|
||
rw [forall_mem_cons] at h; by_cases pa : p a
|
||
· simp [pa, h.1 ▸ pa, filter_congr h.2]
|
||
· simp [pa, h.1 ▸ pa, filter_congr h.2]
|
||
|
||
|
||
theorem filter_sublist {p : α → Bool} : ∀ {l : List α}, filter p l <+ l
|
||
| [] => .slnil
|
||
| a :: l => by rw [filter]; split <;> simp [Sublist.cons, Sublist.cons₂, filter_sublist]
|
||
|
||
|
||
theorem filterMap_length_eq_length {l} :
|
||
(filterMap f l).length = l.length ↔ ∀ a ∈ l, (f a).isSome := by
|
||
induction l with
|
||
| nil => grind
|
||
| cons a l ih =>
|
||
simp only [mem_cons] -- FIXME?
|
||
grind
|
||
|
||
theorem filterMap_eq_filter {p : α → Bool} :
|
||
filterMap (Option.guard (p ·)) = filter p := by
|
||
funext l
|
||
induction l with grind [Option.guard]
|
||
|
||
-- FIXME?
|
||
theorem filter_filterMap {f : α → Option β} {p : β → Bool} {l : List α} :
|
||
filter p (filterMap f l) = filterMap (fun x => (f x).filter p) l := by
|
||
rw [← filterMap_eq_filter, filterMap_filterMap]
|
||
congr; funext x; cases f x with grind [Option.guard]
|
||
|
||
-- FIXME
|
||
theorem mem_filterMap {f : α → Option β} {l : List α} {b : β} :
|
||
b ∈ filterMap f l ↔ ∃ a, a ∈ l ∧ f a = some b := by
|
||
induction l <;> simp [filterMap_cons]; grind
|
||
|
||
theorem map_filterMap_of_inv
|
||
{f : α → Option β} {g : β → α} (H : ∀ x : α, (f x).map g = some x) {l : List α} :
|
||
map g (filterMap f l) = l := by simp only [map_filterMap, H, filterMap_some, id]
|
||
|
||
theorem forall_none_of_filterMap_eq_nil (h : filterMap f xs = []) : ∀ x ∈ xs, f x = none := by
|
||
intro x hx
|
||
induction xs with
|
||
| nil => grind
|
||
| cons y ys ih =>
|
||
simp only [filterMap_cons] at h
|
||
split at h
|
||
· cases hx with
|
||
| head => grind
|
||
| tail _ hmem => exact ih h hmem -- FIXME hmem's type is broken
|
||
· grind
|
||
|
||
theorem filterMap_eq_nil_iff {l} : filterMap f l = [] ↔ ∀ a ∈ l, f a = none := by
|
||
constructor
|
||
· grind
|
||
· induction l with grind (ematch := 6)
|
||
|
||
theorem filterMap_eq_cons_iff {l} {b} {bs} :
|
||
filterMap f l = b :: bs ↔
|
||
∃ l₁ a l₂, l = l₁ ++ a :: l₂ ∧ (∀ x, x ∈ l₁ → f x = none) ∧ f a = some b ∧
|
||
filterMap f l₂ = bs := by
|
||
constructor
|
||
· induction l with
|
||
| nil => grind
|
||
| cons a l ih =>
|
||
cases h : f a with
|
||
| none =>
|
||
simp only [filterMap_cons_none h]
|
||
intro w
|
||
specialize ih w
|
||
obtain ⟨l₁, a', l₂, rfl, w₁, w₂, w₃⟩ := ih
|
||
exact ⟨a :: l₁, a', l₂, by grind⟩
|
||
| some b =>
|
||
simp only [filterMap_cons_some h, cons.injEq, and_imp]
|
||
rintro rfl rfl
|
||
refine ⟨[], a, l, by grind⟩
|
||
· rintro ⟨l₁, a, l₂, rfl, h₁, h₂, h₃⟩
|
||
simp_all [filterMap_eq_nil_iff.mpr h₁, filterMap_cons_some h₂]
|
||
|
||
/-! ### append -/
|
||
|
||
theorem append_of_mem {a : α} {l : List α} : a ∈ l → ∃ s t : List α, l = s ++ a :: t
|
||
| .head l => ⟨[], l, rfl⟩
|
||
| .tail b h => let ⟨s, t, h'⟩ := append_of_mem h; ⟨b::s, t, by grind⟩
|
||
|
||
theorem mem_iff_append {a : α} {l : List α} : a ∈ l ↔ ∃ s t : List α, l = s ++ a :: t :=
|
||
⟨append_of_mem, fun ⟨s, t, e⟩ => by grind⟩
|
||
|
||
theorem forall_mem_append {p : α → Prop} {l₁ l₂ : List α} :
|
||
(∀ (x) (_ : x ∈ l₁ ++ l₂), p x) ↔ (∀ (x) (_ : x ∈ l₁), p x) ∧ (∀ (x) (_ : x ∈ l₂), p x) := by
|
||
simp only [mem_append, or_imp, forall_and]
|
||
|
||
theorem getElem_append_right' (l₁ : List α) {l₂ : List α} {i : Nat} (hi : i < l₂.length) :
|
||
l₂[i] = (l₁ ++ l₂)[i + l₁.length]'(by grind) := by
|
||
sorry -- grind -- fails
|
||
|
||
theorem getElem_of_append {l : List α} (eq : l = l₁ ++ a :: l₂) (h : l₁.length = i) :
|
||
l[i]'(by grind) = a := Option.some.inj <| by
|
||
rw [← getElem?_eq_getElem, eq, getElem?_append_right (h ▸ Nat.le_refl _), h]
|
||
grind
|
||
|
||
theorem append_inj :
|
||
∀ {s₁ s₂ t₁ t₂ : List α}, s₁ ++ t₁ = s₂ ++ t₂ → length s₁ = length s₂ → s₁ = s₂ ∧ t₁ = t₂
|
||
| [], [], _, _, h, _ => ⟨rfl, h⟩
|
||
| _ :: _, _ :: _, _, _, h, hl => by
|
||
simp [append_inj (cons.inj h).2 (Nat.succ.inj hl)] at h ⊢; grind
|
||
|
||
theorem append_inj_right (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length s₁ = length s₂) : t₁ = t₂ :=
|
||
(append_inj h hl).right
|
||
|
||
theorem append_inj_left (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length s₁ = length s₂) : s₁ = s₂ :=
|
||
(append_inj h hl).left
|
||
|
||
/-- Variant of `append_inj` instead requiring equality of the lengths of the second lists. -/
|
||
theorem append_inj' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : s₁ = s₂ ∧ t₁ = t₂ :=
|
||
append_inj h <| @Nat.add_right_cancel _ t₁.length _ <| by
|
||
let hap := congrArg length h; simp only [length_append, ← hl] at hap; exact hap
|
||
|
||
/-- Variant of `append_inj_right` instead requiring equality of the lengths of the second lists. -/
|
||
theorem append_inj_right' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : t₁ = t₂ :=
|
||
(append_inj' h hl).right
|
||
|
||
/-- Variant of `append_inj_left` instead requiring equality of the lengths of the second lists. -/
|
||
theorem append_inj_left' (h : s₁ ++ t₁ = s₂ ++ t₂) (hl : length t₁ = length t₂) : s₁ = s₂ :=
|
||
(append_inj' h hl).left
|
||
|
||
theorem append_right_inj {t₁ t₂ : List α} (s) : s ++ t₁ = s ++ t₂ ↔ t₁ = t₂ :=
|
||
⟨fun h => append_inj_right h rfl, congrArg _⟩
|
||
|
||
theorem append_left_inj {s₁ s₂ : List α} (t) : s₁ ++ t = s₂ ++ t ↔ s₁ = s₂ :=
|
||
⟨fun h => append_inj_left' h rfl, congrArg (· ++ _)⟩
|
||
|
||
theorem append_left_eq_self {xs ys : List α} : xs ++ ys = ys ↔ xs = [] := by
|
||
rw [← append_left_inj (s₁ := xs), nil_append]
|
||
|
||
theorem self_eq_append_left {xs ys : List α} : ys = xs ++ ys ↔ xs = [] := by
|
||
rw [eq_comm, append_left_eq_self]
|
||
|
||
theorem append_right_eq_self {xs ys : List α} : xs ++ ys = xs ↔ ys = [] := by
|
||
rw [← append_right_inj (t₁ := ys), append_nil]
|
||
|
||
theorem self_eq_append_right {xs ys : List α} : xs = xs ++ ys ↔ ys = [] := by
|
||
rw [eq_comm, append_right_eq_self]
|
||
|
||
theorem append_eq_cons_iff :
|
||
as ++ bs = x :: c ↔ (as = [] ∧ bs = x :: c) ∨ (∃ as', as = x :: as' ∧ c = as' ++ bs) := by
|
||
cases as with simp | cons a as => ?_
|
||
exact ⟨fun h => ⟨as, by grind⟩, fun ⟨as', ⟨aeq, aseq⟩, h⟩ => ⟨aeq, by grind⟩⟩
|
||
|
||
theorem append_eq_append_iff {ws xs ys zs : List α} :
|
||
ws ++ xs = ys ++ zs ↔ (∃ as, ys = ws ++ as ∧ xs = as ++ zs) ∨ ∃ bs, ws = ys ++ bs ∧ zs = bs ++ xs := by
|
||
induction ws generalizing ys with
|
||
| nil => simp_all
|
||
| cons a as ih => cases ys <;> simp [eq_comm, and_assoc, ih, and_or_left]
|
||
|
||
theorem filterMap_eq_append_iff {f : α → Option β} :
|
||
filterMap f l = L₁ ++ L₂ ↔ ∃ l₁ l₂, l = l₁ ++ l₂ ∧ filterMap f l₁ = L₁ ∧ filterMap f l₂ = L₂ := by
|
||
constructor
|
||
· induction l generalizing L₁ with
|
||
| nil =>
|
||
simp only [filterMap_nil, nil_eq_append_iff, and_imp]
|
||
rintro rfl rfl
|
||
exact ⟨[], [], by simp⟩
|
||
| cons x l ih =>
|
||
simp only [filterMap_cons]
|
||
split
|
||
· intro h
|
||
obtain ⟨l₁, l₂, rfl, rfl, rfl⟩ := ih h
|
||
refine ⟨x :: l₁, l₂, ?_⟩
|
||
grind
|
||
· rename_i b w
|
||
intro h
|
||
rcases cons_eq_append_iff.mp h with (⟨rfl, rfl⟩ | ⟨_, ⟨rfl, h⟩⟩)
|
||
· refine ⟨[], x :: l, ?_⟩
|
||
grind
|
||
· obtain ⟨l₁, l₂, rfl, rfl, rfl⟩ := ih ‹_›
|
||
refine ⟨x :: l₁, l₂, ?_⟩
|
||
grind
|
||
· grind
|
||
|
||
theorem filter_eq_append_iff {p : α → Bool} :
|
||
filter p l = L₁ ++ L₂ ↔ ∃ l₁ l₂, l = l₁ ++ l₂ ∧ filter p l₁ = L₁ ∧ filter p l₂ = L₂ := by
|
||
rw [← filterMap_eq_filter, filterMap_eq_append_iff]
|
||
|
||
theorem map_eq_append_iff {f : α → β} :
|
||
map f l = L₁ ++ L₂ ↔ ∃ l₁ l₂, l = l₁ ++ l₂ ∧ map f l₁ = L₁ ∧ map f l₂ = L₂ := by
|
||
rw [← filterMap_eq_map, filterMap_eq_append_iff]
|
||
|
||
/-! ### concat
|
||
-/
|
||
|
||
-- As `List.concat` is defined in `Init.Prelude`, we write the basic simplification lemmas here.
|
||
theorem concat_nil {a : α} : concat [] a = [a] :=
|
||
rfl
|
||
theorem concat_cons {a b : α} {l : List α} : concat (a :: l) b = a :: concat l b :=
|
||
rfl
|
||
|
||
theorem init_eq_of_concat_eq {a b : α} {l₁ l₂ : List α} : concat l₁ a = concat l₂ b → l₁ = l₂ := by
|
||
simp only [concat_eq_append]
|
||
intro h
|
||
apply append_inj_left' h (by simp)
|
||
|
||
theorem last_eq_of_concat_eq {a b : α} {l₁ l₂ : List α} : concat l₁ a = concat l₂ b → a = b := by
|
||
simp only [concat_eq_append]
|
||
intro h
|
||
simpa using append_inj_right' h (by simp)
|
||
|
||
theorem concat_inj {a b : α} {l l' : List α} : concat l a = concat l' b ↔ l = l' ∧ a = b :=
|
||
⟨fun h => ⟨init_eq_of_concat_eq h, last_eq_of_concat_eq h⟩, by rintro ⟨rfl, rfl⟩; rfl⟩
|
||
|
||
theorem concat_inj_left {l l' : List α} (a : α) : concat l a = concat l' a ↔ l = l' :=
|
||
⟨init_eq_of_concat_eq, by simp⟩
|
||
|
||
theorem concat_inj_right {l : List α} {a a' : α} : concat l a = concat l a' ↔ a = a' :=
|
||
⟨last_eq_of_concat_eq, by simp⟩
|
||
|
||
theorem concat_append {a : α} {l₁ l₂ : List α} : concat l₁ a ++ l₂ = l₁ ++ a :: l₂ := by simp
|
||
|
||
theorem append_concat {a : α} {l₁ l₂ : List α} : l₁ ++ concat l₂ a = concat (l₁ ++ l₂) a := by simp
|
||
|
||
theorem map_concat {f : α → β} {a : α} {l : List α} : map f (concat l a) = concat (map f l) (f a) := by
|
||
induction l with
|
||
| nil => rfl
|
||
| cons x xs ih => simp [ih]
|
||
|
||
theorem eq_nil_or_concat : ∀ l : List α, l = [] ∨ ∃ l' b, l = concat l' b
|
||
| [] => .inl rfl
|
||
| a::l => match l, eq_nil_or_concat l with
|
||
| _, .inl rfl => .inr ⟨[], a, rfl⟩
|
||
| _, .inr ⟨l', b, rfl⟩ => .inr ⟨a::l', b, rfl⟩
|
||
|
||
/-! ### flatten -/
|
||
|
||
theorem mem_flatten : ∀ {L : List (List α)}, a ∈ L.flatten ↔ ∃ l, l ∈ L ∧ a ∈ l
|
||
| [] => by grind
|
||
| _ :: _ => by simp [mem_flatten, or_and_right, exists_or]
|
||
|
||
theorem flatten_eq_nil_iff {L : List (List α)} : L.flatten = [] ↔ ∀ l ∈ L, l = [] := by
|
||
induction L with simp_all
|
||
|
||
theorem flatten_ne_nil_iff {xss : List (List α)} : xss.flatten ≠ [] ↔ ∃ xs, xs ∈ xss ∧ xs ≠ [] := by
|
||
simp
|
||
|
||
theorem exists_of_mem_flatten : a ∈ flatten L → ∃ l, l ∈ L ∧ a ∈ l := mem_flatten.1
|
||
|
||
theorem mem_flatten_of_mem (lL : l ∈ L) (al : a ∈ l) : a ∈ flatten L := mem_flatten.2 ⟨l, lL, al⟩
|
||
|
||
theorem forall_mem_flatten {p : α → Prop} {L : List (List α)} :
|
||
(∀ (x) (_ : x ∈ flatten L), p x) ↔ ∀ (l) (_ : l ∈ L) (x) (_ : x ∈ l), p x := by
|
||
simp only [mem_flatten, forall_exists_index]
|
||
grind
|
||
|
||
theorem flatten_filter_not_isEmpty :
|
||
∀ {L : List (List α)}, flatten (L.filter fun l => !l.isEmpty) = L.flatten
|
||
| [] => by grind
|
||
| [] :: L
|
||
| (a :: l) :: L => by
|
||
simp [flatten_filter_not_isEmpty (L := L)]
|
||
|
||
theorem flatten_filter_ne_nil [DecidablePred fun l : List α => l ≠ []] {L : List (List α)} :
|
||
flatten (L.filter fun l => l ≠ []) = L.flatten := by
|
||
simp only [ne_eq, ← isEmpty_iff, Bool.not_eq_true, Bool.decide_eq_false,
|
||
flatten_filter_not_isEmpty]
|
||
|
||
theorem flatten_eq_cons_iff {xss : List (List α)} {y : α} {ys : List α} :
|
||
xss.flatten = y :: ys ↔
|
||
∃ as bs cs, xss = as ++ (y :: bs) :: cs ∧ (∀ l, l ∈ as → l = []) ∧ ys = bs ++ cs.flatten := by
|
||
constructor
|
||
· induction xss with
|
||
| nil => grind
|
||
| cons xs xss ih =>
|
||
intro h
|
||
simp only [flatten_cons] at h
|
||
replace h := h.symm
|
||
rw [cons_eq_append_iff] at h
|
||
obtain (⟨rfl, h⟩ | ⟨z⟩) := h
|
||
· obtain ⟨as, bs, cs, rfl, _, rfl⟩ := ih h
|
||
exact ⟨[] :: as, bs, cs, by grind⟩
|
||
· obtain ⟨as', rfl, rfl⟩ := z
|
||
exact ⟨[], as', xss, by grind⟩
|
||
· rintro ⟨as, bs, cs, rfl, h₁, rfl⟩
|
||
grind [flatten_eq_nil_iff]
|
||
|
||
theorem cons_eq_flatten_iff {xs : List (List α)} {y : α} {ys : List α} :
|
||
y :: ys = xs.flatten ↔
|
||
∃ as bs cs, xs = as ++ (y :: bs) :: cs ∧ (∀ l, l ∈ as → l = []) ∧ ys = bs ++ cs.flatten := by
|
||
grind [flatten_eq_cons_iff]
|
||
|
||
theorem flatten_eq_singleton_iff {xs : List (List α)} {y : α} :
|
||
xs.flatten = [y] ↔ ∃ as bs, xs = as ++ [y] :: bs ∧ (∀ l, l ∈ as → l = []) ∧ (∀ l, l ∈ bs → l = []) := by
|
||
rw [flatten_eq_cons_iff]
|
||
constructor
|
||
· rintro ⟨as, bs, cs, rfl, h₁, h₂⟩
|
||
simp at h₂
|
||
obtain ⟨rfl, h₂⟩ := h₂
|
||
exact ⟨as, cs, by grind, h₁, h₂⟩
|
||
· rintro ⟨as, bs, rfl, h₁, h₂⟩
|
||
exact ⟨as, [], bs, rfl, h₁, by simpa⟩
|
||
|
||
theorem flatten_eq_append_iff {xss : List (List α)} {ys zs : List α} :
|
||
xss.flatten = ys ++ zs ↔
|
||
(∃ as bs, xss = as ++ bs ∧ ys = as.flatten ∧ zs = bs.flatten) ∨
|
||
∃ as bs c cs ds, xss = as ++ (bs ++ c :: cs) :: ds ∧ ys = as.flatten ++ bs ∧
|
||
zs = c :: cs ++ ds.flatten := by
|
||
constructor
|
||
· induction xss generalizing ys with
|
||
| nil =>
|
||
simp only [flatten_nil, nil_eq, append_eq_nil_iff, and_false, cons_append, false_and,
|
||
exists_const, exists_false, or_false, and_imp, List.cons_ne_nil]
|
||
rintro rfl rfl
|
||
exact ⟨[], [], by grind⟩
|
||
| cons xs xss ih =>
|
||
intro h
|
||
simp only [flatten_cons] at h
|
||
rw [append_eq_append_iff] at h
|
||
obtain (⟨ys, rfl, h⟩ | ⟨bs, rfl, h⟩) := h
|
||
· obtain (⟨as, bs, rfl, rfl, rfl⟩ | ⟨as, bs, c, cs, ds, rfl, rfl, rfl⟩) := ih h
|
||
· exact .inl ⟨xs :: as, bs, by grind⟩
|
||
· exact .inr ⟨xs :: as, bs, c, cs, ds, by grind⟩
|
||
· simp only [h]
|
||
cases bs with
|
||
| nil => exact .inl ⟨[ys], xss, by grind⟩
|
||
| cons b bs => exact .inr ⟨[], ys, b, bs, xss, by grind⟩
|
||
· grind
|
||
|
||
|
||
/-- Two lists of sublists are equal iff their flattens coincide, as well as the lengths of the
|
||
sublists. -/
|
||
theorem eq_iff_flatten_eq : ∀ {L L' : List (List α)},
|
||
L = L' ↔ L.flatten = L'.flatten ∧ map length L = map length L'
|
||
| _, [] => by simp_all
|
||
| [], _ :: _ => by simp_all
|
||
| _ :: _, _ :: _ => by
|
||
simp only [cons.injEq, flatten_cons, map_cons]
|
||
rw [eq_iff_flatten_eq]
|
||
constructor
|
||
· rintro ⟨rfl, h₁, h₂⟩
|
||
grind
|
||
· rintro ⟨h₁, h₂, h₃⟩
|
||
obtain ⟨rfl, h⟩ := append_inj h₁ h₂
|
||
grind
|
||
|
||
/-! ### flatMap -/
|
||
|
||
theorem flatMap_def {l : List α} {f : α → List β} : l.flatMap f = flatten (map f l) := by rfl
|
||
|
||
theorem flatMap_id {L : List (List α)} : L.flatMap id = L.flatten := by simp [flatMap_def]
|
||
|
||
theorem flatMap_id' {L : List (List α)} : L.flatMap (fun as => as) = L.flatten := by simp [flatMap_def]
|
||
|
||
theorem length_flatMap {l : List α} {f : α → List β} :
|
||
length (l.flatMap f) = sum (map (fun a => (f a).length) l) := by
|
||
rw [List.flatMap, length_flatten, map_map, Function.comp_def]
|
||
|
||
theorem mem_flatMap {f : α → List β} {b} {l : List α} : b ∈ l.flatMap f ↔ ∃ a, a ∈ l ∧ b ∈ f a := by
|
||
simp [flatMap_def, mem_flatten]
|
||
exact ⟨fun ⟨_, ⟨a, h₁, rfl⟩, h₂⟩ => ⟨a, h₁, h₂⟩, fun ⟨a, h₁, h₂⟩ => ⟨_, ⟨a, h₁, rfl⟩, h₂⟩⟩
|
||
|
||
-- attribute [grind] List.mem_map
|
||
|
||
theorem flatMap_eq_nil_iff {l : List α} {f : α → List β} : l.flatMap f = [] ↔ ∀ x ∈ l, f x = [] :=
|
||
flatten_eq_nil_iff.trans <| by
|
||
simp only [mem_map, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]
|
||
|
||
theorem map_flatMap {f : β → γ} {g : α → List β} :
|
||
∀ {l : List α}, (l.flatMap g).map f = l.flatMap fun a => (g a).map f
|
||
| [] => rfl
|
||
| a::l => by simp only [flatMap_cons, map_append, map_flatMap]
|
||
|
||
theorem flatMap_map (f : α → β) (g : β → List γ) (l : List α) :
|
||
(map f l).flatMap g = l.flatMap (fun a => g (f a)) := by
|
||
induction l <;> simp [flatMap_cons, *]
|
||
|
||
theorem map_eq_flatMap {f : α → β} {l : List α} : map f l = l.flatMap fun x => [f x] := by
|
||
simp only [← map_singleton]
|
||
rw [← flatMap_singleton' l, map_flatMap, flatMap_singleton']
|
||
|
||
theorem flatMap_eq_foldl {f : α → List β} {l : List α} :
|
||
l.flatMap f = l.foldl (fun acc a => acc ++ f a) [] := by
|
||
suffices ∀ l', l' ++ l.flatMap f = l.foldl (fun acc a => acc ++ f a) l' by simpa using this []
|
||
intro l'
|
||
induction l generalizing l'
|
||
· grind
|
||
next ih => rw [flatMap_cons, ← append_assoc, ih, foldl_cons]
|
||
|
||
/-! ### replicate -/
|
||
|
||
theorem getElem_replicate {a : α} {n : Nat} {i : Nat} (h : i < (replicate n a).length) :
|
||
(replicate n a)[i] = a :=
|
||
eq_of_mem_replicate (getElem_mem _)
|
||
|
||
theorem getElem?_replicate : (replicate n a)[i]? = if i < n then some a else none := by
|
||
by_cases h : i < n
|
||
· rw [getElem?_eq_getElem (by simpa), getElem_replicate, if_pos h]
|
||
· rw [List.getElem?_eq_none (by simpa using h), if_neg h]
|
||
|
||
theorem replicate_inj : replicate n a = replicate m b ↔ n = m ∧ (n = 0 ∨ a = b) :=
|
||
⟨fun h => have eq : n = m := by simpa using congrArg length h
|
||
⟨eq, by
|
||
by_cases w : n = 0
|
||
· grind
|
||
· have p := congrArg (·[0]?) h
|
||
grind⟩,
|
||
by grind⟩
|
||
|
||
theorem eq_replicate_of_mem {a : α} :
|
||
∀ {l : List α}, (∀ (b) (_ : b ∈ l), b = a) → l = replicate l.length a
|
||
| [], _ => rfl
|
||
| b :: l, H => by
|
||
let ⟨rfl, H₂⟩ := forall_mem_cons (l := l).1 H
|
||
rw [length_cons, replicate, ← eq_replicate_of_mem H₂]
|
||
|
||
theorem eq_replicate_iff {a : α} {n} {l : List α} :
|
||
l = replicate n a ↔ length l = n ∧ ∀ (b) (_ : b ∈ l), b = a :=
|
||
⟨fun h => h ▸ ⟨length_replicate .., fun _ => eq_of_mem_replicate⟩,
|
||
fun ⟨e, al⟩ => e ▸ eq_replicate_of_mem al⟩
|
||
|
||
theorem map_eq_replicate_iff {l : List α} {f : α → β} {b : β} :
|
||
l.map f = replicate l.length b ↔ ∀ x ∈ l, f x = b := by
|
||
simp [eq_replicate_iff]
|
||
|
||
theorem map_const {l : List α} {b : β} : map (Function.const α b) l = replicate l.length b :=
|
||
map_eq_replicate_iff.mpr fun _ _ => rfl
|
||
|
||
theorem map_const_fun {x : β} : map (Function.const α x) = (replicate ·.length x) := by
|
||
funext l
|
||
simp
|
||
|
||
theorem map_const' {l : List α} {b : β} : map (fun _ => b) l = replicate l.length b :=
|
||
map_const
|
||
|
||
theorem append_eq_replicate_iff {l₁ l₂ : List α} {a : α} :
|
||
l₁ ++ l₂ = replicate n a ↔
|
||
l₁.length + l₂.length = n ∧ l₁ = replicate l₁.length a ∧ l₂ = replicate l₂.length a := by
|
||
simp only [eq_replicate_iff, length_append, mem_append, true_and, and_congr_right_iff]
|
||
exact fun _ =>
|
||
{ mp := fun h => ⟨fun b m => h b (Or.inl m), fun b m => h b (Or.inr m)⟩,
|
||
mpr := fun h b x => Or.casesOn x (fun m => h.left b m) fun m => h.right b m }
|
||
|
||
theorem filter_replicate : (replicate n a).filter p = if p a then replicate n a else [] := by
|
||
cases n with
|
||
| zero => simp
|
||
| succ n =>
|
||
simp only [replicate_succ, filter_cons]
|
||
split <;>
|
||
simp_all [-filter_replicate_of_pos, -filter_replicate_of_neg]
|
||
|
||
theorem filterMap_replicate {f : α → Option β} :
|
||
(replicate n a).filterMap f = match f a with | none => [] | .some b => replicate n b := by
|
||
induction n with
|
||
| zero => split <;> simp
|
||
| succ n ih =>
|
||
simp only [replicate_succ, filterMap_cons]
|
||
split <;> simp_all
|
||
|
||
-- This is not a useful `simp` lemma because `b` is unknown.
|
||
theorem filterMap_replicate_of_some {f : α → Option β} (h : f a = some b) :
|
||
(replicate n a).filterMap f = replicate n b := by
|
||
simp [filterMap_replicate, h]
|
||
|
||
theorem filterMap_replicate_of_isSome {f : α → Option β} (h : (f a).isSome) :
|
||
(replicate n a).filterMap f = replicate n (Option.get _ h) := by
|
||
rw [Option.isSome_iff_exists] at h
|
||
obtain ⟨b, h⟩ := h
|
||
simp [filterMap_replicate, h]
|
||
|
||
theorem filterMap_replicate_of_none {f : α → Option β} (h : f a = none) :
|
||
(replicate n a).filterMap f = [] := by
|
||
simp [filterMap_replicate, h]
|
||
|
||
theorem flatten_replicate_replicate : (replicate n (replicate m a)).flatten = replicate (n * m) a := by
|
||
induction n with
|
||
| zero => grind
|
||
| succ n ih =>
|
||
simp only [replicate_succ, flatten_cons, ih, replicate_append_replicate, replicate_inj, or_true,
|
||
and_true, add_one_mul, Nat.add_comm]
|
||
|
||
/-- Every list is either empty, a non-empty `replicate`, or begins with a non-empty `replicate`
|
||
followed by a different element. -/
|
||
theorem eq_replicate_or_eq_replicate_append_cons {α : Type _} (l : List α) :
|
||
(l = []) ∨ (∃ n a, l = replicate n a ∧ 0 < n) ∨
|
||
(∃ n a b l', l = replicate n a ++ b :: l' ∧ 0 < n ∧ a ≠ b) := by
|
||
induction l with
|
||
| nil => grind
|
||
| cons x l ih =>
|
||
right
|
||
rcases ih with rfl | ⟨n, a, rfl, h⟩ | ⟨n, a, b, l', rfl, h⟩
|
||
· left
|
||
exact ⟨1, x, rfl, by grind⟩
|
||
· by_cases h' : x = a
|
||
· subst h'
|
||
left
|
||
exact ⟨n + 1, x, rfl, by grind⟩
|
||
· right
|
||
refine ⟨1, x, a, replicate (n - 1) a, ?_, by grind, h'⟩
|
||
match n with | n + 1 => grind
|
||
· right
|
||
by_cases h' : x = a
|
||
· subst h'
|
||
refine ⟨n + 1, x, b, l', by grind, by simp, h.2⟩
|
||
· refine ⟨1, x, a, replicate (n - 1) a ++ b :: l', ?_, by grind, h'⟩
|
||
match n with | n + 1 => grind
|
||
|
||
theorem replicateRecOn {α : Type _} {p : List α → Prop} (l : List α)
|
||
(h0 : p []) (hr : ∀ a n, 0 < n → p (replicate n a))
|
||
(hi : ∀ a b n l, a ≠ b → 0 < n → p (b :: l) → p (replicate n a ++ b :: l)) : p l := by
|
||
rcases eq_replicate_or_eq_replicate_append_cons l with
|
||
rfl | ⟨n, a, rfl, hn⟩ | ⟨n, a, b, l', w, hn, h⟩
|
||
· exact h0
|
||
· exact hr _ _ hn
|
||
· have : (b :: l').length < l.length := by grind
|
||
subst w
|
||
exact hi _ _ _ _ h hn (replicateRecOn (b :: l') h0 hr hi)
|
||
termination_by l.length
|
||
|
||
theorem sum_replicate_nat {n : Nat} {a : Nat} : (replicate n a).sum = n * a := by
|
||
induction n <;> simp_all [replicate_succ, Nat.add_mul, Nat.add_comm]
|
||
|
||
/-! ### reverse -/
|
||
|
||
theorem mem_reverseAux {x : α} : ∀ {as bs}, x ∈ reverseAux as bs ↔ x ∈ as ∨ x ∈ bs
|
||
| [], _ => ⟨.inr, fun | .inr h => h⟩
|
||
| a :: _, _ => by rw [reverseAux, mem_cons, or_assoc, or_left_comm, mem_reverseAux, mem_cons]
|
||
|
||
/-- Variant of `getElem?_reverse` with a hypothesis giving the linear relation between the indices. -/
|
||
theorem getElem?_reverse' : ∀ {l : List α} {i j}, i + j + 1 = length l →
|
||
l.reverse[i]? = l[j]?
|
||
| [], _, _, _ => rfl
|
||
| a::l, i, 0, h => by
|
||
simp [Nat.succ.injEq] at h
|
||
simp [h, getElem?_append_right, Nat.succ.injEq]
|
||
| a::l, i, j+1, h => by
|
||
have := Nat.succ.inj h; simp at this ⊢
|
||
rw [getElem?_append_left, getElem?_reverse' this]
|
||
rw [length_reverse, ← this]
|
||
grind
|
||
|
||
theorem getElem?_reverse {l : List α} {i} (h : i < length l) :
|
||
l.reverse[i]? = l[l.length - 1 - i]? :=
|
||
getElem?_reverse' <| by grind
|
||
|
||
theorem getElem_reverse {l : List α} {i} (h : i < l.reverse.length) :
|
||
l.reverse[i] = l[l.length - 1 - i]'(by grind) := by
|
||
apply Option.some.inj
|
||
rw [← getElem?_eq_getElem, ← getElem?_eq_getElem]
|
||
grind
|
||
|
||
-- The argument `as : List α` is explicit to allow rewriting from right to left.
|
||
theorem reverse_reverse (as : List α) : as.reverse.reverse = as := by
|
||
simp only [reverse]; rw [reverseAux_reverseAux_nil]; rfl
|
||
|
||
theorem reverse_inj {xs ys : List α} : xs.reverse = ys.reverse ↔ xs = ys := by
|
||
simp [reverse_eq_iff]
|
||
|
||
theorem reverse_eq_cons_iff {xs : List α} {a : α} {ys : List α} :
|
||
xs.reverse = a :: ys ↔ xs = ys.reverse ++ [a] := by
|
||
rw [reverse_eq_iff, reverse_cons]
|
||
|
||
theorem getLast?_reverse {l : List α} : l.reverse.getLast? = l.head? := by
|
||
cases l <;> simp [getLast?_concat]
|
||
|
||
theorem head?_reverse {l : List α} : l.reverse.head? = l.getLast? := by
|
||
rw [← getLast?_reverse, reverse_reverse]
|
||
|
||
theorem mem_of_mem_getLast? {l : List α} {a : α} (h : a ∈ getLast? l) : a ∈ l := by
|
||
grind [getLast?_eq_head?_reverse]
|
||
|
||
theorem filterMap_reverse {f : α → Option β} {l : List α} : (l.reverse.filterMap f) = (l.filterMap f).reverse := by
|
||
induction l with
|
||
| nil => grind
|
||
| cons a l ih =>
|
||
simp only [reverse_cons, filterMap_append, filterMap_cons, ih]
|
||
split <;> grind -- FIXME what's going on here?
|
||
|
||
theorem reverseAux_eq {as bs : List α} : reverseAux as bs = reverse as ++ bs :=
|
||
reverseAux_eq_append ..
|
||
|
||
theorem reverse_replicate {n : Nat} {a : α} : reverse (replicate n a) = replicate n a :=
|
||
eq_replicate_iff.2 (by grind)
|
||
|
||
|
||
/-! ### foldlM and foldrM -/
|
||
|
||
theorem foldlM_append [Monad m] [LawfulMonad m] {f : β → α → m β} {b : β} {l l' : List α} :
|
||
(l ++ l').foldlM f b = l.foldlM f b >>= l'.foldlM f := by
|
||
induction l generalizing b <;> simp [*]
|
||
|
||
theorem foldrM_cons [Monad m] [LawfulMonad m] {a : α} {l : List α} {f : α → β → m β} {b : β} :
|
||
(a :: l).foldrM f b = l.foldrM f b >>= f a := by
|
||
simp only [foldrM]
|
||
induction l <;> simp_all
|
||
|
||
theorem foldlM_pure [Monad m] [LawfulMonad m] {f : β → α → β} {b : β} {l : List α} :
|
||
l.foldlM (m := m) (pure <| f · ·) b = pure (l.foldl f b) := by
|
||
induction l generalizing b <;> simp [*]
|
||
|
||
theorem foldrM_pure [Monad m] [LawfulMonad m] {f : α → β → β} {b : β} {l : List α} :
|
||
l.foldrM (m := m) (pure <| f · ·) b = pure (l.foldr f b) := by
|
||
induction l generalizing b <;> simp [*]
|
||
|
||
theorem foldl_eq_foldlM {f : β → α → β} {b : β} {l : List α} :
|
||
l.foldl f b = l.foldlM (m := Id) f b := by
|
||
induction l generalizing b with sorry
|
||
|
||
theorem foldr_eq_foldrM {f : α → β → β} {b : β} {l : List α} :
|
||
l.foldr f b = l.foldrM (m := Id) f b := by
|
||
induction l with sorry
|
||
|
||
theorem id_run_foldlM {f : β → α → Id β} {b : β} {l : List α} :
|
||
Id.run (l.foldlM f b) = l.foldl f b := foldl_eq_foldlM.symm
|
||
|
||
theorem id_run_foldrM {f : α → β → Id β} {b : β} {l : List α} :
|
||
Id.run (l.foldrM f b) = l.foldr f b := foldr_eq_foldrM.symm
|
||
|
||
theorem foldlM_reverse [Monad m] {l : List α} {f : β → α → m β} {b : β} :
|
||
l.reverse.foldlM f b = l.foldrM (fun x y => f y x) b := rfl
|
||
|
||
theorem foldrM_reverse [Monad m] {l : List α} {f : α → β → m β} {b : β} :
|
||
l.reverse.foldrM f b = l.foldlM (fun x y => f y x) b :=
|
||
(foldlM_reverse ..).symm.trans <| by simp
|
||
|
||
/-! ### foldl and foldr -/
|
||
|
||
theorem foldrM_append [Monad m] [LawfulMonad m] {f : α → β → m β} {b : β} {l l' : List α} :
|
||
(l ++ l').foldrM f b = l'.foldrM f b >>= l.foldrM f := by
|
||
induction l <;> simp [*]
|
||
|
||
theorem foldl_append {β : Type _} {f : β → α → β} {b : β} {l l' : List α} :
|
||
(l ++ l').foldl f b = l'.foldl f (l.foldl f b) := sorry
|
||
|
||
theorem foldr_append {f : α → β → β} {b : β} {l l' : List α} :
|
||
(l ++ l').foldr f b = l.foldr f (l'.foldr f b) := sorry
|
||
|
||
theorem foldl_reverse {l : List α} {f : β → α → β} {b : β} :
|
||
l.reverse.foldl f b = l.foldr (fun x y => f y x) b := by simp [foldl_eq_foldlM, foldr_eq_foldrM]
|
||
|
||
theorem foldr_reverse {l : List α} {f : α → β → β} {b : β} :
|
||
l.reverse.foldr f b = l.foldl (fun x y => f y x) b :=
|
||
(foldl_reverse ..).symm.trans <| by simp
|
||
|
||
theorem foldl_eq_foldr_reverse {l : List α} {f : β → α → β} {b : β} :
|
||
l.foldl f b = l.reverse.foldr (fun x y => f y x) b := by simp -- FIXME reported
|
||
|
||
theorem foldr_eq_foldl_reverse {l : List α} {f : α → β → β} {b : β} :
|
||
l.foldr f b = l.reverse.foldl (fun x y => f y x) b := by simp
|
||
|
||
theorem foldl_assoc {op : α → α → α} [ha : Std.Associative op]
|
||
{l : List α} {a₁ a₂} : l.foldl op (op a₁ a₂) = op a₁ (l.foldl op a₂) := by
|
||
induction l generalizing a₁ a₂ <;> simp [*, ha.assoc] -- FIXME how to get grind to do something useful? needs directly support for associativity?
|
||
|
||
theorem foldr_assoc {op : α → α → α} [ha : Std.Associative op]
|
||
{l : List α} {a₁ a₂} : l.foldr op (op a₁ a₂) = op (l.foldr op a₁) a₂ := by
|
||
induction l generalizing a₁ a₂ <;> simp [*, ha.assoc]
|
||
|
||
/--
|
||
A reasoning principle for proving propositions about the result of `List.foldl` by establishing an
|
||
invariant that is true for the initial data and preserved by the operation being folded.
|
||
|
||
Because the motive can return a type in any sort, this function may be used to construct data as
|
||
well as to prove propositions.
|
||
|
||
Example:
|
||
```lean example
|
||
example {xs : List Nat} : xs.foldl (· + ·) 1 > 0 := by
|
||
apply List.foldlRecOn
|
||
. show 0 < 1; trivial
|
||
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < b + a
|
||
intros; omega
|
||
```
|
||
-/
|
||
def foldlRecOn {motive : β → Sort _} : ∀ (l : List α) (op : β → α → β) {b : β} (_ : motive b)
|
||
(_ : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ l), motive (op b a)), motive (List.foldl op b l)
|
||
| [], _, _, hb, _ => hb
|
||
| hd :: tl, op, b, hb, hl =>
|
||
foldlRecOn tl op (hl b hb hd (by grind))
|
||
fun y hy x hx => hl y hy x (by grind)
|
||
|
||
theorem foldlRecOn_nil {motive : β → Sort _} {op : β → α → β} (hb : motive b)
|
||
(hl : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ []), motive (op b a)) :
|
||
foldlRecOn [] op hb hl = hb := rfl
|
||
|
||
theorem foldlRecOn_cons {motive : β → Sort _} {op : β → α → β} (hb : motive b)
|
||
(hl : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ x :: l), motive (op b a)) :
|
||
foldlRecOn (x :: l) op hb hl =
|
||
foldlRecOn l op (hl b hb x (by grind))
|
||
(fun b c a m => hl b c a (by grind)) :=
|
||
rfl
|
||
|
||
/--
|
||
A reasoning principle for proving propositions about the result of `List.foldr` by establishing an
|
||
invariant that is true for the initial data and preserved by the operation being folded.
|
||
|
||
Because the motive can return a type in any sort, this function may be used to construct data as
|
||
well as to prove propositions.
|
||
|
||
Example:
|
||
```lean example
|
||
example {xs : List Nat} : xs.foldr (· + ·) 1 > 0 := by
|
||
apply List.foldrRecOn
|
||
. show 0 < 1; trivial
|
||
. show ∀ (b : Nat), 0 < b → ∀ (a : Nat), a ∈ xs → 0 < a + b
|
||
intros; omega
|
||
```
|
||
-/
|
||
def foldrRecOn {motive : β → Sort _} : ∀ (l : List α) (op : α → β → β) {b : β} (_ : motive b)
|
||
(_ : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ l), motive (op a b)), motive (List.foldr op b l)
|
||
| nil, _, _, hb, _ => hb
|
||
| x :: l, op, b, hb, hl =>
|
||
hl (foldr op b l)
|
||
(foldrRecOn l op hb fun b c a m => hl b c a (by grind)) x (by grind)
|
||
|
||
theorem foldrRecOn_nil {motive : β → Sort _} {op : α → β → β} (hb : motive b)
|
||
(hl : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ []), motive (op a b)) :
|
||
foldrRecOn [] op hb hl = hb := rfl
|
||
|
||
theorem foldrRecOn_cons {motive : β → Sort _} {op : α → β → β} (hb : motive b)
|
||
(hl : ∀ (b : β) (_ : motive b) (a : α) (_ : a ∈ x :: l), motive (op a b)) :
|
||
foldrRecOn (x :: l) op hb hl =
|
||
hl _ (foldrRecOn l op hb fun b c a m => hl b c a (by grind))
|
||
x (by grind) :=
|
||
rfl
|
||
|
||
|
||
|
||
theorem foldl_add_const {l : List α} {a b : Nat} :
|
||
l.foldl (fun x _ => x + a) b = b + a * l.length := by
|
||
induction l generalizing b with
|
||
| nil => simp
|
||
| cons y l ih =>
|
||
-- needs more arithmetic support in grind!
|
||
simp only [foldl_cons, ih, length_cons, Nat.mul_add, Nat.mul_one, Nat.add_assoc,
|
||
Nat.add_comm a]
|
||
|
||
theorem foldr_add_const {l : List α} {a b : Nat} :
|
||
l.foldr (fun _ x => x + a) b = b + a * l.length := by
|
||
induction l generalizing b with
|
||
| nil => simp
|
||
| cons y l ih =>
|
||
-- needs more arithmetic support in grind!
|
||
simp only [foldr_cons, ih, length_cons, Nat.mul_add, Nat.mul_one, Nat.add_assoc]
|
||
|
||
/-! #### Further results about `getLast` and `getLast?` -/
|
||
|
||
theorem head_reverse {l : List α} (h : l.reverse ≠ []) :
|
||
l.reverse.head h = getLast l (by simp_all) := by
|
||
induction l with
|
||
| nil => contradiction
|
||
| cons a l ih =>
|
||
simp only [reverse_cons]
|
||
by_cases h' : l = []
|
||
· grind
|
||
· simp only [head_eq_iff_head?_eq_some, head?_reverse] at ih
|
||
simp [ih, h, h', getLast_cons, head_eq_iff_head?_eq_some]
|
||
|
||
theorem getLast?_eq_none_iff {xs : List α} : xs.getLast? = none ↔ xs = [] := by
|
||
rw [getLast?_eq_head?_reverse, head?_eq_none_iff, reverse_eq_nil_iff]
|
||
|
||
theorem getLast?_eq_some_iff {xs : List α} {a : α} : xs.getLast? = some a ↔ ∃ ys, xs = ys ++ [a] := by
|
||
rw [getLast?_eq_head?_reverse, head?_eq_some_iff]
|
||
simp only [reverse_eq_cons_iff]
|
||
exact ⟨fun ⟨ys, h⟩ => ⟨ys.reverse, by simpa using h⟩, fun ⟨ys, h⟩ => ⟨ys.reverse, by simpa using h⟩⟩
|
||
|
||
theorem getLast?_isSome : l.getLast?.isSome ↔ l ≠ [] := by
|
||
rw [getLast?_eq_head?_reverse, isSome_head?]
|
||
simp
|
||
|
||
theorem mem_of_getLast? {xs : List α} {a : α} (h : xs.getLast? = some a) : a ∈ xs := by
|
||
obtain ⟨ys, rfl⟩ := getLast?_eq_some_iff.1 h
|
||
exact mem_concat_self
|
||
|
||
theorem getLast_reverse {l : List α} (h : l.reverse ≠ []) :
|
||
l.reverse.getLast h = l.head (by simp_all) := by
|
||
simp [getLast_eq_head_reverse]
|
||
|
||
theorem head_eq_getLast_reverse {l : List α} (h : l ≠ []) :
|
||
l.head h = l.reverse.getLast (by simp_all) := by
|
||
rw [← getLast_reverse]
|
||
|
||
theorem getLast_append_of_ne_nil {l : List α} (h₁) (h₂ : l' ≠ []) :
|
||
(l ++ l').getLast h₁ = l'.getLast h₂ := by
|
||
simp only [getLast_eq_head_reverse, reverse_append]
|
||
rw [head_append_of_ne_nil]
|
||
|
||
|
||
theorem getLast?_append {l l' : List α} : (l ++ l').getLast? = l'.getLast?.or l.getLast? := by
|
||
simp [← head?_reverse, -List.head?_reverse]
|
||
|
||
|
||
|
||
-- attribute [grind] List.head_filter_of_pos
|
||
|
||
-- theorem getLast_filter_of_pos {p : α → Bool} {l : List α} (w : l ≠ []) (h : p (getLast l w) = true) :
|
||
-- getLast (filter p l) (ne_nil_of_mem (mem_filter.2 ⟨getLast_mem w, by grind⟩)) = getLast l w := by grind [head_filter_of_pos]
|
||
|
||
-- attribute [grind] List.head_filterMap_of_eq_some
|
||
|
||
-- theorem getLast_filterMap_of_eq_some {f : α → Option β} {l : List α} (w : l ≠ []) {b : β} (h : f (l.getLast w) = some b) :
|
||
-- (filterMap f l).getLast (ne_nil_of_mem (mem_filterMap.2 ⟨_, getLast_mem w, h⟩)) = b := by grind
|
||
|
||
|
||
-- attribute [grind] List.getLast?_eq_head?_reverse List.head?_eq_getLast?_reverse
|
||
|
||
-- theorem getLast?_flatMap {l : List α} {f : α → List β} :
|
||
-- (l.flatMap f).getLast? = l.reverse.findSome? fun a => (f a).getLast? := by
|
||
-- grind
|
||
|
||
|
||
-- theorem getLast?_flatten {L : List (List α)} :
|
||
-- (flatten L).getLast? = L.reverse.findSome? fun l => l.getLast? := by
|
||
-- grind?
|
||
|
||
|
||
|
||
|
||
|
||
/-! ## Additional operations -/
|
||
|
||
/-! ### leftpad -/
|
||
|
||
theorem leftpad_prefix {n : Nat} {a : α} {l : List α} :
|
||
replicate (n - length l) a <+: leftpad n a l := by
|
||
simp only [IsPrefix, leftpad]
|
||
exact Exists.intro l rfl
|
||
|
||
theorem leftpad_suffix {n : Nat} {a : α} {l : List α} : l <:+ (leftpad n a l) := by
|
||
simp only [IsSuffix, leftpad]
|
||
exact Exists.intro (replicate (n - length l) a) rfl
|
||
|
||
/-! ## List membership -/
|
||
|
||
/-! ### elem / contains -/
|
||
|
||
theorem elem_cons_self [BEq α] [LawfulBEq α] {a : α} : (a::as).elem a = true := by simp
|
||
|
||
theorem contains_iff_exists_mem_beq [BEq α] {l : List α} {a : α} :
|
||
l.contains a ↔ ∃ a' ∈ l, a == a' := by
|
||
induction l <;> simp_all
|
||
|
||
/-! ## Sublists -/
|
||
|
||
/-! ### partition
|
||
|
||
Because we immediately simplify `partition` into two `filter`s for verification purposes,
|
||
we do not separately develop much theory about it.
|
||
-/
|
||
|
||
theorem partition_eq_filter_filter {p : α → Bool} {l : List α} :
|
||
partition p l = (filter p l, filter (not ∘ p) l) := by simp [partition, aux]
|
||
where
|
||
aux l {as bs} : partition.loop p l (as, bs) =
|
||
(as.reverse ++ filter p l, bs.reverse ++ filter (not ∘ p) l) := by
|
||
induction l generalizing as bs with
|
||
| nil => grind [partition.loop]
|
||
| cons a l ih => cases pa : p a <;> simp [partition.loop, pa, ih, append_assoc]
|
||
|
||
/-! ### dropLast
|
||
|
||
`dropLast` is the specification for `Array.pop`, so theorems about `List.dropLast`
|
||
are often used for theorems about `Array.pop`.
|
||
-/
|
||
|
||
theorem length_dropLast {xs : List α} : xs.dropLast.length = xs.length - 1 := by
|
||
induction xs with simp
|
||
|
||
-- FIXME
|
||
theorem getElem_dropLast : ∀ {xs : List α} {i : Nat} (h : i < xs.dropLast.length),
|
||
xs.dropLast[i] = xs[i]'(by grind)
|
||
| _ :: _ :: _, 0, _ => rfl
|
||
| _ :: _ :: _, _ + 1, h => getElem_dropLast (Nat.add_one_lt_add_one_iff.mp h)
|
||
|
||
theorem head?_dropLast {xs : List α} : xs.dropLast.head? = if 1 < xs.length then xs.head? else none := by
|
||
cases xs with
|
||
| nil => grind
|
||
| cons x xs => cases xs with grind
|
||
|
||
theorem getLast?_dropLast {xs : List α} :
|
||
xs.dropLast.getLast? = if xs.length ≤ 1 then none else xs[xs.length - 2]? := by
|
||
grind
|
||
|
||
theorem dropLast_cons_of_ne_nil {α : Type u} {x : α}
|
||
{l : List α} (h : l ≠ []) : (x :: l).dropLast = x :: l.dropLast := by
|
||
simp [dropLast, h]
|
||
|
||
theorem dropLast_concat_getLast : ∀ {l : List α} (h : l ≠ []), dropLast l ++ [getLast l h] = l
|
||
| [], h => absurd rfl h
|
||
| [_], _ => rfl
|
||
| _ :: b :: l, _ => by
|
||
rw [dropLast_cons₂, cons_append, getLast_cons (cons_ne_nil _ _)]
|
||
congr
|
||
exact dropLast_concat_getLast (cons_ne_nil b l)
|
||
|
||
|
||
theorem dropLast_append_of_ne_nil {α : Type u} {l : List α} :
|
||
∀ {l' : List α} (_ : l ≠ []), (l' ++ l).dropLast = l' ++ l.dropLast
|
||
| [], _ => by simp only [nil_append]
|
||
| a :: l', h => by
|
||
rw [cons_append, dropLast, dropLast_append_of_ne_nil h, cons_append]
|
||
simp [h]
|
||
|
||
theorem dropLast_cons_self_replicate {n : Nat} {a : α} :
|
||
dropLast (a :: replicate n a) = replicate n a := by
|
||
rw [← replicate_succ, dropLast_replicate, Nat.add_sub_cancel]
|
||
|
||
theorem tail_reverse {l : List α} : l.reverse.tail = l.dropLast.reverse := by
|
||
apply ext_getElem
|
||
· simp
|
||
· intro i h₁ h₂
|
||
simp [Nat.add_comm i, Nat.sub_add_eq]
|
||
|
||
/-!
|
||
### splitAt
|
||
-/
|
||
|
||
theorem splitAt_go {i : Nat} {l acc : List α} :
|
||
splitAt.go l xs i acc =
|
||
if i < xs.length then (acc.reverse ++ xs.take i, xs.drop i) else (l, []) := by
|
||
induction xs generalizing i acc with
|
||
| nil => simp [splitAt.go]
|
||
| cons x xs ih =>
|
||
cases i with
|
||
| zero => simp [splitAt.go]
|
||
| succ i =>
|
||
rw [splitAt.go, take_succ_cons, drop_succ_cons, ih, reverse_cons, append_assoc,
|
||
singleton_append, length_cons]
|
||
simp only [Nat.succ_lt_succ_iff]
|
||
|
||
/-! ## Manipulating elements -/
|
||
|
||
/-! ### replace -/
|
||
section replace
|
||
variable [BEq α]
|
||
|
||
theorem getElem?_replace [LawfulBEq α] {l : List α} {i : Nat} :
|
||
(l.replace a b)[i]? = if l[i]? == some a then if a ∈ l.take i then some a else some b else l[i]? := by
|
||
induction l generalizing i with
|
||
| nil => cases i <;> grind
|
||
| cons x xs ih =>
|
||
cases i <;>
|
||
· simp only [replace_cons]
|
||
split <;> split <;> grind -- FIXME, sadly grind doesn't do the case split here
|
||
|
||
|
||
theorem getElem_replace [LawfulBEq α] {l : List α} {i : Nat} (h : i < l.length) :
|
||
(l.replace a b)[i]'(by grind) = if l[i] == a then if a ∈ l.take i then a else b else l[i] := by
|
||
apply Option.some.inj
|
||
rw [← getElem?_eq_getElem, getElem?_replace]
|
||
split <;> split <;> grind [getElem?_eq_getElem] -- FIXME, sadly grind doesn't do the case split here
|
||
|
||
theorem head?_replace {l : List α} {a b : α} :
|
||
(l.replace a b).head? = match l.head? with
|
||
| none => none
|
||
| some x => some (if a == x then b else x) := by
|
||
cases l with
|
||
| nil => grind
|
||
| cons x xs =>
|
||
simp [replace_cons]
|
||
grind
|
||
|
||
theorem head_replace {l : List α} {a b : α} (w) :
|
||
(l.replace a b).head w =
|
||
if a == l.head (by rintro rfl; simp_all) then
|
||
b
|
||
else
|
||
l.head (by rintro rfl; simp_all) := by
|
||
apply Option.some.inj
|
||
rw [← head?_eq_head, head?_replace, head?_eq_head]
|
||
|
||
theorem replace_take {l : List α} {i : Nat} :
|
||
(l.take i).replace a b = (l.replace a b).take i := by
|
||
induction l generalizing i with
|
||
| nil => grind
|
||
| cons x xs ih =>
|
||
cases i with
|
||
| zero => grind
|
||
| succ i =>
|
||
simp only [replace_cons, take_succ_cons]
|
||
split <;> grind -- FIXME grind won't do the split?
|
||
|
||
theorem replace_replicate_ne [LawfulBEq α] {a b c : α} (h : !b == a) :
|
||
(replicate n a).replace b c = replicate n a := by
|
||
rw [replace_of_not_mem]
|
||
grind
|
||
|
||
end replace
|
||
|
||
/-! ### insert -/
|
||
|
||
section insert
|
||
variable [BEq α]
|
||
|
||
variable [LawfulBEq α]
|
||
|
||
theorem getElem?_insert {l : List α} {a : α} {i : Nat} :
|
||
(l.insert a)[i]? = if a ∈ l then l[i]? else if i = 0 then some a else l[i-1]? := by
|
||
-- I'm surprised grind won't do this case split?
|
||
cases i with grind [List.insert]
|
||
|
||
theorem getElem_insert {l : List α} {a : α} {i : Nat} (h : i < l.length) :
|
||
(l.insert a)[i]'(Nat.lt_of_lt_of_le h length_le_length_insert) =
|
||
if a ∈ l then l[i] else if i = 0 then a else l[i-1]'(Nat.lt_of_le_of_lt (Nat.pred_le _) h) := by
|
||
apply Option.some.inj
|
||
rw [← getElem?_eq_getElem, getElem?_insert]
|
||
split
|
||
· grind [getElem?_eq_getElem]
|
||
· split
|
||
· grind
|
||
· have h' : i - 1 < l.length := Nat.lt_of_le_of_lt (Nat.pred_le _) h
|
||
simp [getElem?_eq_getElem, h']
|
||
|
||
theorem head?_insert {l : List α} {a : α} :
|
||
(l.insert a).head? = some (if h : a ∈ l then l.head (ne_nil_of_mem h) else a) := by
|
||
simp only [insert_eq]
|
||
split <;> rename_i h
|
||
· simp [head?_eq_head (ne_nil_of_mem h)]
|
||
· grind
|
||
|
||
theorem head_insert {l : List α} {a : α} (w) :
|
||
(l.insert a).head w = if h : a ∈ l then l.head (ne_nil_of_mem h) else a := by
|
||
apply Option.some.inj
|
||
rw [← head?_eq_head, head?_insert]
|
||
|
||
end insert
|
||
|
||
/-! ## Logic -/
|
||
|
||
/-! ### any / all -/
|
||
|
||
theorem any_replicate {n : Nat} {a : α} :
|
||
(replicate n a).any f = if n = 0 then false else f a := by
|
||
cases n <;> simp [replicate_succ, -List.any_replicate]
|
||
|
||
theorem all_replicate {n : Nat} {a : α} :
|
||
(replicate n a).all f = if n = 0 then true else f a := by
|
||
cases n <;> simp +contextual [replicate_succ]
|
||
|
||
theorem any_insert [BEq α] [LawfulBEq α] {l : List α} {a : α} :
|
||
(l.insert a).any f = (f a || l.any f) := by
|
||
simp [any_eq]
|
||
|
||
theorem all_insert [BEq α] [LawfulBEq α] {l : List α} {a : α} :
|
||
(l.insert a).all f = (f a && l.all f) := by
|
||
simp [all_eq]
|
||
|
||
end List'
|