Skip to content

Commit

Permalink
feat: lemmas about List.reverseRecOn (#11257)
Browse files Browse the repository at this point in the history
This renames the arguments to the eliminators to be more ergonomic when using the `induction` tactic.

I also changed the definition in an attempt to make the proof easier.
  • Loading branch information
eric-wieser committed Mar 19, 2024
1 parent 82e96fe commit e623116
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 19 deletions.
99 changes: 82 additions & 17 deletions Mathlib/Data/List/Basic.lean
Expand Up @@ -905,41 +905,106 @@ theorem modifyHead_modifyHead (l : List α) (f g : α → α) :
for `l ++ [a]` if it holds for `l`, then it holds for all lists. The principle is given for
a `Sort`-valued predicate, i.e., it can also be used to construct data. -/
@[elab_as_elim]
def reverseRecOn {C : List α → Sort*} (l : List α) (H0 : C [])
(H1 : ∀ (l : List α) (a : α), C l → C (l ++ [a])) : C l := by
rw [← reverse_reverse l]
match h:(reverse l) with
| [] => exact H0
def reverseRecOn {motive : List α → Sort*} (l : List α) (nil : motive [])
(append_singleton : ∀ (l : List α) (a : α), motive l → motive (l ++ [a])) : motive l :=
match h : reverse l with
| [] => cast (congr_arg motive <| by simpa using congr(reverse $h.symm)) <|
nil
| head :: tail =>
have : tail.length < l.length := by
rw [← length_reverse l, h, length_cons]
simp [Nat.lt_succ]
let ih := reverseRecOn (reverse tail) H0 H1
rw [reverse_cons]
exact H1 _ _ ih
cast (congr_arg motive <| by simpa using congr(reverse $h.symm)) <|
append_singleton _ head <| reverseRecOn (reverse tail) nil append_singleton
termination_by l.length
#align list.reverse_rec_on List.reverseRecOn

@[simp]
theorem reverseRecOn_nil {motive : List α → Sort*} (nil : motive [])
(append_singleton : ∀ (l : List α) (a : α), motive l → motive (l ++ [a])) :
reverseRecOn [] nil append_singleton = nil := rfl

-- `unusedHavesSuffices` is getting confused by the unfolding of `reverseRecOn`
@[simp, nolint unusedHavesSuffices]
theorem reverseRecOn_concat {motive : List α → Sort*} (x : α) (xs : List α) (nil : motive [])
(append_singleton : ∀ (l : List α) (a : α), motive l → motive (l ++ [a])) :
reverseRecOn (motive := motive) (xs ++ [x]) nil append_singleton =
append_singleton _ _ (reverseRecOn (motive := motive) xs nil append_singleton) := by
suffices ∀ ys (h : reverse (reverse xs) = ys),
reverseRecOn (motive := motive) (xs ++ [x]) nil append_singleton =
cast (by simp [(reverse_reverse _).symm.trans h])
(append_singleton _ x (reverseRecOn (motive := motive) ys nil append_singleton)) by
exact this _ (reverse_reverse xs)
intros ys hy
conv_lhs => unfold reverseRecOn
split
next h => simp at h
next heq =>
revert heq
simp only [reverse_append, reverse_cons, reverse_nil, nil_append, singleton_append, cons.injEq]
rintro ⟨rfl, rfl⟩
subst ys
rfl

/-- Bidirectional induction principle for lists: if a property holds for the empty list, the
singleton list, and `a :: (l ++ [b])` from `l`, then it holds for all lists. This can be used to
prove statements about palindromes. The principle is given for a `Sort`-valued predicate, i.e., it
can also be used to construct data. -/
def bidirectionalRec {C : List α → Sort*} (H0 : C []) (H1 : ∀ a : α, C [a])
(Hn : ∀ (a : α) (l : List α) (b : α), C l → C (a :: (l ++ [b]))) : ∀ l, C l
| [] => H0
| [a] => H1 a
| a :: b :: l => by
@[elab_as_elim]
def bidirectionalRec {motive : List α → Sort*} (nil : motive []) (singleton : ∀ a : α, motive [a])
(cons_append : ∀ (a : α) (l : List α) (b : α), motive l → motive (a :: (l ++ [b]))) :
∀ l, motive l
| [] => nil
| [a] => singleton a
| a :: b :: l =>
let l' := dropLast (b :: l)
let b' := getLast (b :: l) (cons_ne_nil _ _)
rw [← dropLast_append_getLast (cons_ne_nil b l)]
have : C l' := bidirectionalRec H0 H1 Hn l'
exact Hn a l' b' this
cast (by rw [← dropLast_append_getLast (cons_ne_nil b l)]) <|
cons_append a l' b' (bidirectionalRec nil singleton cons_append l')
termination_by l => l.length
#align list.bidirectional_rec List.bidirectionalRecₓ -- universe order

@[simp]
theorem bidirectionalRec_nil {motive : List α → Sort*}
(nil : motive []) (singleton : ∀ a : α, motive [a])
(cons_append : ∀ (a : α) (l : List α) (b : α), motive l → motive (a :: (l ++ [b]))) :
bidirectionalRec nil singleton cons_append [] = nil :=
rfl

@[simp]
theorem bidirectionalRec_singleton {motive : List α → Sort*}
(nil : motive []) (singleton : ∀ a : α, motive [a])
(cons_append : ∀ (a : α) (l : List α) (b : α), motive l → motive (a :: (l ++ [b]))) (a : α):
bidirectionalRec nil singleton cons_append [a] = singleton a :=
rfl

@[simp]
theorem bidirectionalRec_cons_append {motive : List α → Sort*}
(nil : motive []) (singleton : ∀ a : α, motive [a])
(cons_append : ∀ (a : α) (l : List α) (b : α), motive l → motive (a :: (l ++ [b])))
(a : α) (l : List α) (b : α) :
bidirectionalRec nil singleton cons_append (a :: (l ++ [b])) =
cons_append a l b (bidirectionalRec nil singleton cons_append l) := by
conv_lhs => unfold bidirectionalRec
cases l with
| nil => rfl
| cons x xs =>
simp only [List.cons_append]
congr
dsimp only [← List.cons_append]
suffices ∀ (ys init : List α) (hinit : init = ys) (last : α) (hlast : last = b),
(cons_append a init last
(bidirectionalRec nil singleton cons_append init)) =
cast (congr_arg motive <| by simp [hinit, hlast])
(cons_append a ys b (bidirectionalRec nil singleton cons_append ys)) by
rw [this (x :: xs) _ (by rw [dropLast_append_cons, dropLast_single, append_nil]) _ (by simp)]
simp
rintro ys init rfl last rfl
rfl

/-- Like `bidirectionalRec`, but with the list parameter placed first. -/
@[elab_as_elim]
def bidirectionalRecOn {C : List α → Sort*} (l : List α) (H0 : C []) (H1 : ∀ a : α, C [a])
abbrev bidirectionalRecOn {C : List α → Sort*} (l : List α) (H0 : C []) (H1 : ∀ a : α, C [a])
(Hn : ∀ (a : α) (l : List α) (b : α), C l → C (a :: (l ++ [b]))) : C l :=
bidirectionalRec H0 H1 Hn l
#align list.bidirectional_rec_on List.bidirectionalRecOn
Expand Down
4 changes: 2 additions & 2 deletions Mathlib/Data/List/ToFinsupp.lean
Expand Up @@ -151,8 +151,8 @@ theorem toFinsupp_eq_sum_map_enum_single {R : Type*} [AddMonoid R] (l : List R)
`[DecidablePred (getD l · 0 ≠ 0)]`, so we manually do some `revert`/`intro` as a workaround -/
revert l; intro l
induction l using List.reverseRecOn with
| H0 => exact toFinsupp_nil
| H1 x xs ih =>
| nil => exact toFinsupp_nil
| append_singleton x xs ih =>
classical simp [toFinsupp_concat_eq_toFinsupp_add_single, enum_append, ih]
#align list.to_finsupp_eq_sum_map_enum_single List.toFinsupp_eq_sum_map_enum_single

Expand Down

0 comments on commit e623116

Please sign in to comment.