11module
22
3- def strange_sort_list : Unit :=
4- ()
3+ /-! ## Implementation -/
4+
5+ def strangeSortArray (xs : Array Int) : Array Int :=
6+ let sorted := xs.mergeSort
7+ Array.ofFn (n := sorted.size)
8+ (fun i => if i.val % 2 = 0 then sorted[i.val / 2 ] else sorted[sorted.size - 1 - i.val / 2 ])
9+
10+ /-! ## Tests -/
11+
12+ example : strangeSortArray #[1 , 2 , 3 , 4 ] = #[1 , 4 , 2 , 3 ] := by native_decide
13+ example : strangeSortArray #[5 , 6 , 7 , 8 , 9 ] = #[5 , 9 , 6 , 8 , 7 ] := by native_decide
14+ example : strangeSortArray #[1 , 2 , 3 , 4 , 5 ] = #[1 , 5 , 2 , 4 , 3 ] := by native_decide
15+ example : strangeSortArray #[5 , 6 , 7 , 8 , 9 , 1 ] = #[1 , 9 , 5 , 8 , 6 , 7 ] := by native_decide
16+ example : strangeSortArray #[5 , 5 , 5 , 5 ] = #[5 , 5 , 5 , 5 ] := by native_decide
17+ example : strangeSortArray #[] = #[] := by native_decide
18+ example : strangeSortArray #[1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] = #[1 , 8 , 2 , 7 , 3 , 6 , 4 , 5 ] := by native_decide
19+ example : strangeSortArray #[0 , 2 , 2 , 2 , 5 , 5 , -5 , -5 ] = #[-5 , 5 , -5 , 5 , 0 , 2 , 2 , 2 ] := by native_decide
20+ example : strangeSortArray #[111111 ] = #[111111 ] := by native_decide
21+
22+ /-! ## Missing API -/
23+
24+ theorem List.all_min?_mem {xs : List Int} :
25+ xs.min?.all (· ∈ xs) := by
26+ simp only [Option.all_eq_true_iff_get]
27+ grind
28+
29+ -- Helps grind to derive `b ∈ xs` from `xs.min? = some b`
30+ grind_pattern List.all_min?_mem => xs.min?
31+
32+ theorem List.Perm.min_eq [LE α] [Min α] [Std.LawfulOrderMin α] [Std.IsLinearOrder α]
33+ {xs ys : List α} (h_perm : xs.Perm ys) (h : xs ≠ []) :
34+ xs.min h = ys.min (by grind [Perm.eq_nil]) := by
35+ simp only [List.min_eq_iff]
36+ grind
37+
38+ theorem List.Perm.min?_eq [LE α] [Min α] [Std.LawfulOrderMin α] [Std.IsLinearOrder α]
39+ {xs ys : List α} (h_perm : xs.Perm ys) :
40+ xs.min? = ys.min? := by
41+ match xs, ys with
42+ | [], [] => rfl
43+ | x :: xs, y :: ys =>
44+ rw [min?_eq_some_min, min?_eq_some_min, h_perm.min_eq]
45+ simp
46+ | x :: xs, [] | [], y :: ys => grind [Perm.nil_eq, Perm.eq_nil]
47+
48+ theorem List.Perm.max_eq [LE α] [Max α] [Std.LawfulOrderMax α] [Std.IsLinearOrder α]
49+ {xs ys : List α} (h_perm : xs.Perm ys) (h : xs ≠ []) :
50+ xs.max h = ys.max (by grind [Perm.eq_nil]) := by
51+ simp only [List.max_eq_iff]
52+ grind
53+
54+ theorem List.Perm.max?_eq [LE α] [Max α] [Std.LawfulOrderMax α] [Std.IsLinearOrder α]
55+ {xs ys : List α} (h_perm : xs.Perm ys) :
56+ xs.max? = ys.max? := by
57+ match xs, ys with
58+ | [], [] => rfl
59+ | x :: xs, y :: ys =>
60+ rw [max?_eq_some_max, max?_eq_some_max, h_perm.max_eq]
61+ simp
62+ | x :: xs, [] | [], y :: ys => grind [Perm.nil_eq, Perm.eq_nil]
63+
64+ theorem Array.Perm.max?_eq [LE α] [Max α] [Std.LawfulOrderMax α] [Std.IsLinearOrder α]
65+ {xs ys : Array α} (h_perm : xs.Perm ys) :
66+ xs.max? = ys.max? := by
67+ simp [← max?_toList, h_perm.toList.max?_eq]
68+
69+ theorem Array.Perm.min?_eq [LE α] [Min α] [Std.LawfulOrderMin α] [Std.IsLinearOrder α]
70+ {xs ys : Array α} (h_perm : xs.Perm ys) :
71+ xs.min? = ys.min? := by
72+ simp [← min?_toList, h_perm.toList.min?_eq]
73+
74+ theorem List.max_eq_getLast [Max α] {xs : List α} (h) (hp : xs.Pairwise (fun a b => max a b = b)) :
75+ xs.max h = xs.getLast h := by
76+ rw [List.max.eq_def]
77+ split
78+ rename_i x xs _
79+ suffices ∀ xs : List α, (x :: xs.reverse).Pairwise (fun a b => max a b = b) → foldl max x xs.reverse = (x :: xs.reverse).getLast (by grind) by
80+ simp +singlePass only [← List.reverse_reverse (as := xs)]
81+ grind
82+ simp [- pairwise_cons]
83+ intro xs hp
84+ induction xs
85+ · simp
86+ · simp
87+ rename_i ih
88+ rw [ih]
89+ · rw [List.reverse_cons, ← List.cons_append, pairwise_append] at hp <;> grind
90+ · grind
91+
92+ theorem List.max?_eq_getLast? [Max α] {xs : List α} (hp : xs.Pairwise (fun a b => max a b = b)) :
93+ xs.max? = xs.getLast? := by
94+ cases xs
95+ · simp
96+ · rw [max?_eq_some_max, max_eq_getLast, getLast?_eq_some_getLast]
97+ · simp
98+ · exact hp
99+
100+ theorem Array.max_eq_back [Max α] {xs : Array α} (h) (hp : xs.toList.Pairwise (fun a b => max a b = b)) :
101+ xs.max h = xs.back (by grind) := by
102+ rw [← max_toList, List.max_eq_getLast, getLast_toList]
103+ · exact hp
104+ · simp [h]
105+
106+ theorem Array.max?_eq_back? [Max α] {xs : Array α} (hp : xs.toList.Pairwise (fun a b => max a b = b)) :
107+ xs.max? = xs.back? := by
108+ simp [← max?_toList, List.max?_eq_getLast? hp]
109+
110+ theorem Std.min_eq_left_iff [LE α] [Min α] [Std.Refl (α := α) (· ≤ ·)]
111+ [Std.LawfulOrderLeftLeaningMin α] {a b : α} :
112+ min a b = a ↔ a ≤ b := by
113+ by_cases a ≤ b
114+ · grind [Std.LawfulOrderLeftLeaningMin.min_eq_left]
115+ · rename_i h
116+ simp only [Std.LawfulOrderLeftLeaningMin.min_eq_right _ _ h, iff_false, h]
117+ rintro rfl
118+ exact h.elim (Std.Refl.refl _)
119+
120+ theorem Std.max_eq_right_iff [LE α] [Max α] [Std.IsLinearOrder α] [Std.LawfulOrderMax α]
121+ {a b : α} :
122+ max a b = b ↔ a ≤ b := by
123+ open scoped Classical in grind [max_eq_if]
124+
125+ theorem List.getElem_zero_mergeSort {xs : List Int} (h) :
126+ xs.mergeSort[0 ]'h = xs.min (by grind [length_mergeSort]) := by
127+ rw [(xs.mergeSort_perm (· ≤ ·)).symm.min_eq, List.min_eq_head, List.head_eq_getElem]
128+ simp only [Std.min_eq_left_iff]
129+ simpa using xs.pairwise_mergeSort (by grind) (by grind) (le := (· ≤ ·))
130+
131+ theorem Array.getElem_zero_mergeSort {xs : Array Int} (h) :
132+ xs.mergeSort[0 ]'h = xs.min (by grind [size_mergeSort]) := by
133+ simp only [mergeSort_eq_toArray_mergeSort_toList, List.getElem_toArray,
134+ List.getElem_zero_mergeSort, min_toList]
135+
136+ theorem List.getElem_length_sub_one_mergeSort {xs : List Int} (h) :
137+ xs.mergeSort[xs.length - 1 ]'h = xs.max (by grind [length_mergeSort]) := by
138+ rw [(xs.mergeSort_perm (· ≤ ·)).symm.max_eq, List.max_eq_getLast, List.getLast_eq_getElem]
139+ · simp [List.length_mergeSort]
140+ · simp only [Std.max_eq_right_iff]
141+ simpa using xs.pairwise_mergeSort (by grind) (by grind) (le := (· ≤ ·))
142+
143+ theorem Array.getElem_size_sub_one_mergeSort {xs : Array Int} (h) :
144+ xs.mergeSort[xs.size - 1 ]'h = xs.max (by grind [size_mergeSort]) := by
145+ simp only [mergeSort_eq_toArray_mergeSort_toList]
146+ simp only [← length_toList, List.getElem_toArray]
147+ rw [List.getElem_length_sub_one_mergeSort, max_toList]
148+
149+ theorem List.max_erase_le_max {xs : List Int} (h) :
150+ (xs.erase x).max h ≤ xs.max (by grind) := by
151+ simp only [List.max_le_iff]
152+ intro b hb
153+ apply List.le_max_of_mem
154+ exact List.mem_of_mem_erase hb
155+
156+ theorem Array.max_erase_le_max {xs : Array Int} {h} :
157+ (xs.erase x).max h ≤ xs.max (by grind) := by
158+ simp only [Array.max_le_iff]
159+ intro b hb
160+ apply Array.le_max_of_mem
161+ exact Array.mem_of_mem_erase hb
162+
163+ theorem List.count_dropLast [BEq α] {xs : List α} {a : α} :
164+ xs.dropLast.count a = xs.count a - if xs.getLast? == some a then 1 else 0 := by
165+ rw [← List.reverse_reverse (as := xs)]
166+ generalize xs.reverse = xs
167+ rw [dropLast_reverse, count_reverse, count_tail, getLast?_reverse, count_reverse]
168+
169+ theorem Array.count_pop [BEq α] {xs : Array α} {a : α} :
170+ xs.pop.count a = xs.count a - if xs.back? == some a then 1 else 0 := by
171+ rw [← count_toList, toList_pop, List.count_dropLast, getLast?_toList, count_toList]
172+
173+ /--
174+ If two lists are sorted by an antisymmetric relation, and permutations of each other,
175+ they must be equal.
176+ -/
177+ theorem Array.Perm.eq_of_pairwise {xs ys : Array α}
178+ (h : ∀ a b, a ∈ xs → b ∈ ys → le a b → le b a → a = b)
179+ (hpx : xs.toList.Pairwise le) (hpy : ys.toList.Pairwise le) (hp : xs.Perm ys) :
180+ xs = ys := by
181+ rw [← toList_inj]
182+ apply List.Perm.eq_of_pairwise (by simpa) hpx hpy (by simpa [perm_iff_toList_perm] using hp)
183+
184+ theorem Array.pop_eq_take {xs : Array α} :
185+ xs.pop = xs.take (xs.size - 1 ) := by
186+ rw [take_eq_extract, extract_eq_pop rfl]
187+
188+ theorem List.Pairwise.toList_arrayTake {xs : Array α} (h : xs.toList.Pairwise R) :
189+ (xs.take i).toList.Pairwise R := by
190+ simp only [Array.take_eq_extract, Array.toList_extract, extract_eq_take_drop, Nat.sub_zero,
191+ drop_zero]
192+ apply List.Pairwise.take
193+ exact h
194+
195+ theorem List.Pairwise.toList_arrayDrop {xs : Array α} (h : xs.toList.Pairwise R) :
196+ (xs.drop i).toList.Pairwise R := by
197+ simp only [Array.drop_eq_extract, Array.toList_extract, extract_eq_drop_take']
198+ apply List.Pairwise.drop
199+ apply List.Pairwise.take
200+ exact h
201+
202+ theorem Array.Perm.countP_eq [BEq α] {xs ys : Array α} (hp : xs.Perm ys) :
203+ xs.countP P = ys.countP P := by
204+ simp [← countP_toList, hp.toList.countP_eq]
205+
206+ theorem Array.Perm.count_eq [BEq α] {xs ys : Array α} (hp : xs.Perm ys) :
207+ xs.count a = ys.count a :=
208+ hp.countP_eq
209+
210+ theorem Array.head?_toList {xs : Array α} :
211+ xs.toList.head? = xs[0 ]? := by
212+ simp [List.head?_eq_getElem?]
213+
214+ @ [grind =]
215+ theorem Array.count_drop_one [BEq α] {xs : Array α} {a : α} :
216+ (xs.drop 1 ).count a = xs.count a - if xs[0 ]? == some a then 1 else 0 := by
217+ have := List.count_tail (l := xs.toList) (a := a)
218+ simp [← count_toList, List.take_of_length_le, this, head?_toList]
219+
220+ theorem Array.min?_eq_getElem?_zero {α : Type u} [Min α] {xs : Array α}
221+ (h : xs.toList.Pairwise (fun a b => min a b = a)) : xs.min? = xs[0 ]? := by
222+ simp [← min?_toList, List.min?_eq_head? h, List.head?_eq_getElem?]
223+
224+ /-! ## Verification -/
225+
226+ theorem strangeSortArray_empty :
227+ strangeSortArray #[] = #[] := by
228+ grind [strangeSortArray, Array.mergeSort_empty]
229+
230+ theorem strangeSortArray_singleton :
231+ strangeSortArray #[x] = #[x] := by
232+ ext <;> grind [strangeSortArray, Array.mergeSort_singleton, Array.size_ofFn, Array.getElem_ofFn]
233+
234+ theorem mergeSort_erase_max {xs : Array Int} {h} :
235+ (xs.erase (xs.max h)).mergeSort = xs.mergeSort.pop := by
236+ apply Array.Perm.eq_of_pairwise (le := (· ≤ ·))
237+ · grind
238+ · simpa using Array.pairwise_mergeSort (α := Int) (le := (· ≤ ·)) (by grind) (by grind)
239+ · simp only [Array.pop_eq_take]
240+ apply List.Pairwise.toList_arrayTake
241+ simpa using Array.pairwise_mergeSort (α := Int) (le := (· ≤ ·)) (by grind) (by grind)
242+ · refine Array.Perm.trans Array.mergeSort_perm ?_
243+ simp only [Array.perm_iff_toList_perm, List.perm_iff_count, Array.count_toList]
244+ intro a
245+ rw [Array.count_erase]
246+ have h_mem : xs.max h ∈ xs := by grind
247+ rw [Array.count_pop, Array.mergeSort_perm.count_eq,
248+ ← Array.max?_eq_back?, Array.mergeSort_perm.max?_eq,
249+ Array.max?_eq_some_max]
250+ · simp
251+ · grind
252+ · simp only [Std.max_eq_right_iff]
253+ simpa using Array.pairwise_mergeSort (α := Int) (le := (· ≤ ·)) (by grind) (by grind)
254+
255+ theorem mergeSort_erase_min {xs : Array Int} {h} :
256+ (xs.erase (xs.min h)).mergeSort = xs.mergeSort.drop 1 := by
257+ apply Array.Perm.eq_of_pairwise (le := (· ≤ ·))
258+ · grind
259+ · simpa using Array.pairwise_mergeSort (α := Int) (le := (· ≤ ·)) (by grind) (by grind)
260+ · apply List.Pairwise.toList_arrayDrop
261+ simpa using Array.pairwise_mergeSort (α := Int) (le := (· ≤ ·)) (by grind) (by grind)
262+ · refine Array.Perm.trans Array.mergeSort_perm ?_
263+ simp only [Array.perm_iff_toList_perm, List.perm_iff_count, Array.count_toList]
264+ intro a
265+ rw [Array.count_erase]
266+ have h_mem : xs.max h ∈ xs := by grind
267+ rw [Array.count_drop_one, Array.mergeSort_perm.count_eq,
268+ ← Array.min?_eq_getElem?_zero, Array.mergeSort_perm.min?_eq,
269+ Array.min?_eq_some_min]
270+ · simp
271+ · grind
272+ · simp only [Std.min_eq_left_iff]
273+ simpa using Array.pairwise_mergeSort (α := Int) (le := (· ≤ ·)) (by grind) (by grind)
274+
275+ theorem size_strangeSortArray {xs : Array Int} :
276+ (strangeSortArray xs).size = xs.size := by
277+ simp [strangeSortArray]
278+
279+ theorem strangeSortArray_of_two_le_size (h : 2 ≤ xs.size) :
280+ let minimum := xs.min (by grind)
281+ let withoutMin := xs.erase minimum
282+ let maximum := withoutMin.max (by grind)
283+ let withoutMinMax := withoutMin.erase maximum
284+ strangeSortArray xs = #[minimum, maximum] ++ strangeSortArray withoutMinMax := by
285+ intro minimum withoutMin maximum withoutMinMax
286+ ext
287+ · simp [strangeSortArray]
288+ grind
289+ · rename_i i h₁ h₂
290+ simp [strangeSortArray]
291+ match i with
292+ | 0 => grind [Array.getElem_zero_mergeSort]
293+ | 1 =>
294+ simp [Array.getElem_size_sub_one_mergeSort, maximum, withoutMin]
295+ rw [Array.max_eq_iff]
296+ constructor
297+ · grind [Array.mem_of_mem_erase]
298+ · intro b hb
299+ by_cases hb' : b = minimum
300+ · cases hb'
301+ simp only [minimum]
302+ apply Array.min_le_of_mem
303+ exact Array.mem_of_mem_erase (Array.max_mem _)
304+ · apply Array.le_max_of_mem
305+ rwa [Array.mem_erase_of_ne]
306+ assumption
307+ | j + 2 =>
308+ have : withoutMinMax.mergeSort = xs.mergeSort.extract 1 (xs.toList.mergeSort.length - 1 ) := by
309+ simp only [withoutMinMax, maximum, mergeSort_erase_max]
310+ simp only [withoutMin, minimum, mergeSort_erase_min]
311+ -- TODO: simplify the following as soon as the `Array.take/drop` API is merged
312+ simp only [← Array.extract_eq_pop, Array.drop_eq_extract, Array.extract_extract]
313+ simp only [Nat.add_zero, Array.size_mergeSort, Array.size_extract, Std.le_refl,
314+ Nat.min_eq_left, List.length_mergeSort, Array.length_toList]
315+ grind
316+ simp [this]
317+ grind [size_strangeSortArray]
5318
6319/-!
7320## Prompt
@@ -52,4 +365,4 @@ def check(candidate):
52365 assert True
53366
54367```
55- -/
368+ -/
0 commit comments