1- def longest : Unit :=
2- ()
1+ module
2+
3+ open Std
4+
5+ /-!
6+ ## Implementation
7+ -/
8+
9+ def argmax [LE β] [DecidableLE β] (f : α → β) (x y : α) : α :=
10+ if f y ≤ f x then x else y
11+
12+ /-
13+ `List.argmax` exists in mathlib, but:
14+ * it returns an `Option`, so it should actually be named `argmax?`
15+ * it relies on mathlib's `Preorder` type class and `DecidableLT`. In the standard library,
16+ it would be more consistent to use `LE` and `DecidableLE`.
17+
18+ Moreover, lemmas such as `List.index_of_argmax` aren't easily applicable because one would need
19+ `BEq α` and `LawfulBEq α` in order to use `idxOf`. Moreover, some API about `idxOf` and `findIdx`
20+ is still missing. In this file, we avoid these difficulties by not relying on `idxOf` and `findIdx`
21+ at all.
22+ -/
23+ def List.argmax [LE β] [DecidableLE β] (xs : List α) (f : α → β) (h : xs ≠ []) : α :=
24+ match xs with
25+ | x :: xs => xs.foldl (init := x) (_root_.argmax f)
26+
27+ def List.argmax? [LE β] [DecidableLE β] (xs : List α) (f : α → β) : Option α :=
28+ if h : xs ≠ [] then
29+ some (xs.argmax f h)
30+ else
31+ none
32+
33+ def longest? (xs : List String) : Option String :=
34+ xs.argmax? String.length
35+
36+ /-!
37+ ## Tests
38+ -/
39+
40+ example : longest? [] = none := by native_decide
41+ example : longest? ["x" , "y" , "z" ] = some "x" := by native_decide
42+ example : longest? ["x" , "yyy" , "zzzz" , "www" , "kkkk" , "abc" ] = some "zzzz" := by native_decide
43+
44+ /-!
45+ ## Verification
46+ -/
47+
48+ @ [grind =]
49+ theorem List.argmax_singleton [LE β] [DecidableLE β] {x : α} {f : α → β} :
50+ [x].argmax f (by grind) = x := by
51+ grind [argmax]
52+
53+ @ [grind =]
54+ theorem argmax_assoc [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β} {x y z : α} :
55+ argmax f (argmax f x y) z = argmax f x (argmax f y z) := by
56+ grind [argmax]
57+
58+ instance [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β} :
59+ Associative (argmax f) where
60+ assoc := by apply argmax_assoc
61+
62+ theorem List.argmax_cons
63+ [LE β] [DecidableLE β] [IsLinearPreorder β] {x : α} {xs : List α} {f : α → β} :
64+ (x :: xs).argmax f (by grind) =
65+ if h : xs = [] then x else _root_.argmax f x (xs.argmax f h) := by
66+ simp only [argmax]
67+ match xs with
68+ | [] => simp
69+ | y :: xs => simp [foldl_assoc]
70+
71+ theorem argmax_eq_or [LE β] [DecidableLE β] {f : α → β} {x y : α} :
72+ argmax f x y = x ∨ argmax f x y = y := by
73+ grind [argmax]
74+
75+ @ [grind =]
76+ theorem argmax_self [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β} {x : α} :
77+ argmax f x x = x := by
78+ grind [argmax]
79+
80+ @ [grind =]
81+ theorem argmax_eq_left [LE β] [DecidableLE β] {f : α → β} {x y : α} (h : f y ≤ f x) :
82+ argmax f x y = x := by
83+ grind [argmax]
84+
85+ @ [grind =]
86+ theorem argmax_eq_right [LE β] [DecidableLE β] {f : α → β} {x y : α} (h : ¬ f y ≤ f x) :
87+ argmax f x y = y := by
88+ grind [argmax]
89+
90+ @ [grind =>]
91+ theorem apply_left_le_apply_argmax [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β}
92+ {x y : α} : f x ≤ f (argmax f x y) := by
93+ grind [argmax]
94+
95+ @ [grind =>]
96+ theorem apply_right_le_apply_argmax [LE β] [DecidableLE β] [IsLinearPreorder β]
97+ {f : α → β} {x y : α} : f y ≤ f (argmax f x y) := by
98+ grind [argmax]
99+
100+ @ [grind .]
101+ theorem List.argmax_mem [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α}
102+ {f : α → β} {h : xs ≠ []} : xs.argmax f h ∈ xs := by
103+ simp only [List.argmax]
104+ match xs with
105+ | x :: xs =>
106+ fun_induction xs.foldl (init := x) (_root_.argmax f) <;> grind [argmax_eq_or]
107+
108+ @ [grind =>]
109+ theorem List.le_apply_argmax_of_mem [LE β] [DecidableLE β] [IsLinearPreorder β]
110+ {xs : List α} {f : α → β} {y : α} (hx : y ∈ xs) :
111+ f y ≤ f (xs.argmax f (List.ne_nil_of_mem hx)) := by
112+ have h : xs ≠ [] := List.ne_nil_of_mem hx
113+ simp only [List.argmax]
114+ match xs with
115+ | x :: xs =>
116+ fun_induction xs.foldl (init := x) (_root_.argmax f) generalizing y <;> grind
117+
118+ @ [grind =]
119+ theorem List.argmax_append [LE β] [DecidableLE β] [IsLinearPreorder β] {xs ys : List α}
120+ {f : α → β} (hxs : xs ≠ []) (hys : ys ≠ []) :
121+ (xs ++ ys).argmax f (by simp [hxs]) = _root_.argmax f (xs.argmax f hxs) (ys.argmax f hys) := by
122+ match xs, ys with
123+ | x :: xs, y :: ys => simp [argmax, foldl_assoc]
124+
125+ /--
126+ `List.argmax xs f h` comes before any other element in `xs` where `f` attains its maximum.
127+ -/
128+ theorem List.argmax_left_leaning
129+ [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α} {f : α → β} (h : xs ≠ []) :
130+ ∃ j : Fin xs.length, xs[j] = xs.argmax f h ∧
131+ ∀ i : Fin j, ¬ f (xs.argmax f h) ≤ f xs[i] := by
132+ simp only [List.argmax]
133+ match xs with
134+ | x :: xs =>
135+ simp only
136+ clear h
137+ fun_induction xs.foldl (init := x) (_root_.argmax f)
138+ · exact ⟨⟨0 , by grind⟩, by grind⟩
139+ · rename_i x y xs ih
140+ obtain ⟨j, ih⟩ := ih
141+ by_cases hj : j.val = 0
142+ · by_cases hm : f y ≤ f x
143+ · exact ⟨⟨0 , by grind⟩, by grind⟩
144+ · exact ⟨⟨1 , by grind⟩, by grind⟩
145+ · refine ⟨⟨j + 1 , by grind⟩, ?_⟩
146+ obtain ⟨j, _⟩ := Nat.exists_eq_succ_of_ne_zero hj
147+ apply And.intro
148+ · grind
149+ · intro i hi
150+ have : i.val ≥ 2 := by have := ih.2 ⟨0 , by grind⟩; grind
151+ obtain ⟨i, _⟩ := Nat.exists_eq_add_of_le this
152+ have := ih.2 ⟨i + 1 , by grind⟩
153+ grind
154+
155+ /-- `List.argmax?` returns `none` when applied to an empty list. -/
156+ @ [grind =]
157+ theorem List.argmax?_nil [LE β] [DecidableLE β] {f : α → β} :
158+ ([] : List α).argmax? f = none := by
159+ simp [argmax?]
160+
161+ @ [grind =]
162+ theorem List.argmax?_cons
163+ [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β} {x : α} {xs : List α} :
164+ (x :: xs).argmax? f = (xs.argmax? f).elim x (_root_.argmax f x) := by
165+ grind [argmax?, argmax_cons]
166+
167+ @ [grind =>]
168+ theorem List.isSome_argmax?_of_mem
169+ [LE β] [DecidableLE β] {f : α → β} {xs : List α} {x : α} (h : x ∈ xs) :
170+ (xs.argmax? f).isSome := by
171+ grind [argmax?]
172+
173+ theorem List.le_apply_argmax?_get_of_mem
174+ [LE β] [DecidableLE β] [IsLinearPreorder β] {f : α → β} {xs : List α} {x : α} (h : x ∈ xs) :
175+ f x ≤ f ((xs.argmax? f).get (isSome_argmax?_of_mem h)) := by
176+ grind [argmax?]
177+
178+ -- The suggested patterns are not useful because all involve `IsLinearPreorder`.
179+ grind_pattern List.le_apply_argmax?_get_of_mem => x ∈ xs, (xs.argmax? f).get _
180+
181+ theorem List.argmax?_left_leaning [LE β] [DecidableLE β] [IsLinearPreorder β] {xs : List α} {f : α → β} {x : α}
182+ (hx : xs.argmax? f = some x) :
183+ ∃ j : Fin xs.length, xs[j] = x ∧ ∀ i : Fin j, ¬ f x ≤ f xs[i] := by
184+ simp only [argmax?] at hx
185+ split at hx
186+ · simp only [Option.some.injEq] at hx
187+ rw [← hx]
188+ apply argmax_left_leaning
189+ · grind
190+
191+ @ [grind =]
192+ theorem List.argmax?_append [LE β] [DecidableLE β] [IsLinearPreorder β] (xs ys : List α) (f : α → β) :
193+ (xs ++ ys).argmax? f =
194+ (xs.argmax? f).merge (_root_.argmax f) (ys.argmax? f) := by
195+ grind [argmax?, append_eq_nil_iff]
196+
197+ /-!
198+ ### Main theorems
199+
200+ The following theorems verify important properties of `longest?`.
201+ The requirements from the prompt are verified by `le_length_longest?_get_of_mem` and
202+ `longest?_left_leaning`.
203+
204+ Some other useful properties are proved by the remaining lemmas.
205+ -/
206+
207+ theorem longest?_nil : longest? [] = none := by
208+ grind [longest?]
209+
210+ theorem longest?_singleton : longest? [x] = some x := by
211+ grind [longest?]
212+
213+ theorem longest?_append {xs ys : List String} :
214+ longest? (xs ++ ys) = (longest? xs).merge (_root_.argmax String.length) (longest? ys) := by
215+ grind [longest?]
216+
217+ theorem isSome_longest?_of_mem {xs : List String} {x : String} (h : x ∈ xs) :
218+ (longest? xs).isSome := by
219+ grind [longest?]
220+
221+ theorem le_length_longest?_get_of_mem {xs : List String} {x : String} (h : x ∈ xs) :
222+ x.length ≤ ((longest? xs).get (isSome_longest?_of_mem h)).length := by
223+ grind [longest?]
224+
225+ /--
226+ `longest?` returns the first string with maximum length: any other string with maximum length
227+ appears at an index greater than the returned string's index.
228+ -/
229+ theorem longest?_left_leaning {xs : List String} {x : String} (h : longest? xs = some x) :
230+ ∃ j : Fin xs.length, xs[j] = x ∧ ∀ i : Fin j, xs[i].length < x.length := by
231+ rw [longest?] at h
232+ have := List.argmax?_left_leaning h
233+ grind
3234
4235/-!
5236## Prompt
@@ -48,4 +279,4 @@ def check(candidate):
48279 assert candidate(['x', 'y', 'z']) == 'x'
49280 assert candidate(['x', 'yyy', 'zzzz', 'www', 'kkkk', 'abc']) == 'zzzz'
50281```
51- -/
282+ -/
0 commit comments