lean4-htt/tests/lean/grind/experiments/list.lean
Kyle Miller 4575799f8e
chore: library style cleanup (#9654)
This PR cleans up the style of the library in anticipation of a future
PR that requires strict indentation for tactic sequences.
2025-07-31 21:28:59 +00:00

1535 lines
58 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-!
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'