Skip to content

Commit da9df8d

Browse files
TimerootYaelDillies
andcommitted
feat: List.destutter on cotransitive relations. (#9082)
`List.destutter` behaves nicely when the condition is a _cotransitive_ relation, and even better when it's a _coequivalence_ relation. This proves that behavior. Co-authored-by: Alex Meiburg <timeroot.alex@gmail.com> Co-authored-by: Yaël Dillies <yael.dillies@gmail.com>
1 parent 14e2a6b commit da9df8d

File tree

3 files changed

+130
-1
lines changed

3 files changed

+130
-1
lines changed

Mathlib/Data/List/Destutter.lean

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ Note that we make no guarantees of being the longest sublist with this property;
2525
adjacent, chain, duplicates, remove, list, stutter, destutter
2626
-/
2727

28+
open Function
2829

29-
variable {α : Type*} (l : List α) (R : α → α → Prop) [DecidableRel R] {a b : α}
30+
variable {α β : Type*} (l l₁ l₂ : List α) (R : α → α → Prop) [DecidableRel R] {a b : α}
31+
32+
variable {R₂ : β → β → Prop} [DecidableRel R₂]
3033

3134
namespace List
3235

@@ -148,4 +151,116 @@ theorem destutter_eq_nil : ∀ {l : List α}, destutter R l = [] ↔ l = []
148151
| [] => Iff.rfl
149152
| _ :: l => ⟨fun h => absurd h <| l.destutter'_ne_nil R, fun h => nomatch h⟩
150153

154+
variable {R}
155+
156+
/-- For a relation-preserving map, `destutter` commutes with `map`. -/
157+
theorem map_destutter {f : α → β} : ∀ {l : List α}, (∀ a ∈ l, ∀ b ∈ l, R a b ↔ R₂ (f a) (f b)) →
158+
(l.destutter R).map f = (l.map f).destutter R₂
159+
| [], hl => by simp
160+
| [a], hl => by simp
161+
| a :: b :: l, hl => by
162+
have := hl a (by simp) b (by simp)
163+
simp_rw [map_cons, destutter_cons_cons, ← this]
164+
by_cases hr : R a b <;>
165+
simp [hr, ← destutter_cons', map_destutter fun c hc d hd ↦ hl _ (cons_subset_cons _
166+
(subset_cons_self _ _) hc) _ (cons_subset_cons _ (subset_cons_self _ _) hd),
167+
map_destutter fun c hc d hd ↦ hl _ (subset_cons_self _ _ hc) _ (subset_cons_self _ _ hd)]
168+
169+
/-- For a injective function `f`, `destutter' (·≠·)` commutes with `map f`. -/
170+
theorem map_destutter_ne {f : α → β} (h : Injective f) [DecidableEq α] [DecidableEq β] :
171+
(l.destutter (·≠·)).map f = (l.map f).destutter (·≠·) :=
172+
map_destutter fun _ _ _ _ ↦ h.ne_iff.symm
173+
174+
/-- `destutter'` on a relation like ≠ or <, whose negation is transitive, has length monotone
175+
under a `¬R` changing of the first element. -/
176+
theorem length_destutter'_cotrans_ge [i : IsTrans α Rᶜ] :
177+
∀ {a} {l : List α}, ¬R b a → (l.destutter' R b).length ≤ (l.destutter' R a).length
178+
| a, [], hba => by simp
179+
| a, c :: l, hba => by
180+
by_cases hbc : R b c
181+
case pos =>
182+
have hac : ¬Rᶜ a c := (mt (_root_.trans hba)) (not_not.2 hbc)
183+
simp_rw [destutter', if_pos (not_not.1 hac), if_pos hbc, length_cons, le_refl]
184+
case neg =>
185+
simp only [destutter', if_neg hbc]
186+
by_cases hac : R a c
187+
case pos =>
188+
simp only [if_pos hac, length_cons]
189+
exact Nat.le_succ_of_le (length_destutter'_cotrans_ge hbc)
190+
case neg =>
191+
simp only [if_neg hac]
192+
exact length_destutter'_cotrans_ge hba
193+
194+
/-- `List.destutter'` on a relation like `≠`, whose negation is an equivalence, gives the same
195+
length if the first elements are not related. -/
196+
theorem length_destutter'_congr [IsEquiv α Rᶜ] (hab : ¬R a b) :
197+
(l.destutter' R a).length = (l.destutter' R b).length :=
198+
(length_destutter'_cotrans_ge hab).antisymm <| length_destutter'_cotrans_ge (symm hab : Rᶜ b a)
199+
200+
/-- `List.destutter'` on a relation like ≠, whose negation is an equivalence, has length
201+
monotonic under List.cons -/
202+
/-
203+
TODO: Replace this lemma by the more general version:
204+
theorem Sublist.length_destutter'_mono [IsEquiv α Rᶜ] (h : a :: l₁ <+ b :: l₂) :
205+
(List.destutter' R a l₁).length ≤ (List.destutter' R b l₂).length
206+
-/
207+
theorem le_length_destutter'_cons [IsEquiv α Rᶜ] :
208+
∀ {l : List α}, (l.destutter' R b).length ≤ ((b :: l).destutter' R a).length
209+
| [] => by by_cases hab : (R a b) <;> simp_all [Nat.le_succ]
210+
| c :: cs => by
211+
by_cases hab : R a b
212+
case pos => simp [destutter', if_pos hab, Nat.le_succ]
213+
obtain hac | hac : R a c ∨ Rᶜ a c := em _
214+
· have hbc : ¬Rᶜ b c := mt (_root_.trans hab) (not_not.2 hac)
215+
simp [destutter', if_pos hac, if_pos (not_not.1 hbc), if_neg hab]
216+
· have hbc : ¬R b c := trans (symm hab) hac
217+
simp only [destutter', if_neg hbc, if_neg hac, if_neg hab]
218+
exact (length_destutter'_congr cs hab).ge
219+
220+
/-- `List.destutter` on a relation like ≠, whose negation is an equivalence, has length
221+
monotone under List.cons -/
222+
theorem length_destutter_le_length_destutter_cons [IsEquiv α Rᶜ] :
223+
∀ {l : List α}, (l.destutter R).length ≤ ((a :: l).destutter R).length
224+
| [] => by simp [destutter]
225+
| b :: l => le_length_destutter'_cons
226+
227+
variable {l l₁ l₂}
228+
229+
/-- `destutter ≠` has length monotone under `List.cons`. -/
230+
theorem length_destutter_ne_le_length_destutter_cons [DecidableEq α] :
231+
(l.destutter (· ≠ ·)).length ≤ ((a :: l).destutter (· ≠ ·)).length :=
232+
length_destutter_le_length_destutter_cons
233+
234+
/-- `destutter` of relations like `≠`, whose negation is an equivalence relation,
235+
gives a list of maximal length over any chain.
236+
237+
In other words, `l.destutter R` is an `R`-chain sublist of `l`, and is at least as long as any other
238+
`R`-chain sublist. -/
239+
lemma Chain'.length_le_length_destutter [IsEquiv α Rᶜ] :
240+
∀ {l₁ l₂ : List α}, l₁ <+ l₂ → l₁.Chain' R → l₁.length ≤ (l₂.destutter R).length
241+
-- `l₁ := []`, `l₂ := []`
242+
| [], [], _, _ => by simp
243+
-- `l₁ := l₁`, `l₂ := a :: l₂`
244+
| l₁, _, .cons (l₂ := l₂) a hl, hl₁ =>
245+
(hl₁.length_le_length_destutter hl).trans length_destutter_le_length_destutter_cons
246+
-- `l₁ := [a]`, `l₂ := a :: l₂`
247+
| _, _, .cons₂ (l₁ := []) (l₂ := l₁) a hl, hl₁ => by simp [Nat.one_le_iff_ne_zero]
248+
-- `l₁ := a :: l₁`, `l₂ := a :: b :: l₂`
249+
| _, _, .cons₂ a <| .cons (l₁ := l₁) (l₂ := l₂) b hl, hl₁ => by
250+
by_cases hab : R a b
251+
· simpa [destutter_cons_cons, hab] using hl₁.tail.length_le_length_destutter (hl.cons _)
252+
· simpa [destutter_cons_cons, hab] using hl₁.length_le_length_destutter (hl.cons₂ _)
253+
-- `l₁ := a :: b :: l₁`, `l₂ := a :: b :: l₂`
254+
| _, _, .cons₂ a <| .cons₂ (l₁ := l₁) (l₂ := l₂) b hl, hl₁ => by
255+
simpa [destutter_cons_cons, rel_of_chain_cons hl₁]
256+
using hl₁.tail.length_le_length_destutter (hl.cons₂ _)
257+
258+
/-- `destutter` of `≠` gives a list of maximal length over any chain.
259+
260+
In other words, `l.destutter (· ≠ ·)` is a `≠`-chain sublist of `l`, and is at least as long as any
261+
other `≠`-chain sublist. -/
262+
lemma Chain'.length_le_length_destutter_ne [DecidableEq α] (hl : l₁ <+ l₂)
263+
(hl₁ : l₁.Chain' (· ≠ ·)) : l₁.length ≤ (l₂.destutter (· ≠ ·)).length :=
264+
hl₁.length_le_length_destutter hl
265+
151266
end List

Mathlib/Data/List/GetD.lean

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ theorem getD_eq_default {n : ℕ} (hn : l.length ≤ n) : l.getD n d = d := by
4949
· simp at hn
5050
· exact ih (Nat.le_of_succ_le_succ hn)
5151

52+
theorem getD_reverse {l : List α} (i) (h : i < length l) :
53+
getD l.reverse i = getD l (l.length - 1 - i) := by
54+
funext a
55+
rwa [List.getD_eq_getElem?_getD, List.getElem?_reverse, ← List.getD_eq_getElem?_getD]
56+
5257
/-- An empty list can always be decidably checked for the presence of an element.
5358
Not an instance because it would clash with `DecidableEq α`. -/
5459
def decidableGetDNilNe (a : α) : DecidablePred fun i : ℕ => getD ([] : List α) i a ≠ a :=
@@ -69,6 +74,11 @@ theorem getElem?_getD_replicate_default_eq (r n : ℕ) : (replicate r d)[n]?.get
6974
@[deprecated (since := "2024-06-12")]
7075
alias getD_replicate_default_eq := getElem?_getD_replicate_default_eq
7176

77+
theorem getD_replicate {y i n} (h : i < n) :
78+
getD (replicate n x) i y = x := by
79+
rw [getD_eq_getElem, getElem_replicate]
80+
rwa [length_replicate]
81+
7282
theorem getD_append (l l' : List α) (d : α) (n : ℕ) (h : n < l.length) :
7383
(l ++ l').getD n d = l.getD n d := by
7484
rw [getD_eq_getElem _ _ (Nat.lt_of_lt_of_le h (length_append _ _ ▸ Nat.le_add_right _ _)),

Mathlib/Order/Basic.lean

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,10 @@ theorem compl_le [LinearOrder α] : (· ≤ · : α → α → _)ᶜ = (· > ·)
762762
theorem compl_gt [LinearOrder α] : (· > · : α → α → _)ᶜ = (· ≤ ·) := by ext; simp [compl]
763763
theorem compl_ge [LinearOrder α] : (· ≥ · : α → α → _)ᶜ = (· < ·) := by ext; simp [compl]
764764

765+
instance Ne.instIsEquiv_compl : IsEquiv α (· ≠ ·)ᶜ := by
766+
convert eq_isEquiv α
767+
simp [compl]
768+
765769
/-! ### Order instances on the function space -/
766770

767771

0 commit comments

Comments
 (0)