11module
22
3- def search : Unit :=
4- ()
3+ public import Std
4+ public import Init.Data.Iterators.Lemmas.Basic
5+ open Std
6+
7+ public section
8+
9+ def frequencies (xs : List Nat) : TreeMap Nat Nat (fun a b => compare b a) :=
10+ xs.foldl (init := ∅) (fun freq (x : Nat) => freq.alter x (fun v? => some (v?.getD 0 + 1 )))
11+
12+ def search (xs : List Nat) : Int :=
13+ let frequencies := frequencies xs
14+ let kv := frequencies.iter
15+ |>.filter (fun (k, v) => 0 < k ∧ k ≤ v)
16+ |>.map (fun (k, _) => k)
17+ |>.first?
18+ kv.getD (-1 )
19+
20+ @ [simp, grind =]
21+ theorem Std.Iter.first?_toList {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
22+ [Iterators.Finite α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
23+ it.toList.head? = it.first? := by
24+ induction it using Iter.inductSteps with | step it ihy ihs
25+ rw [first?_eq_match_step, toList_eq_match_step]
26+ cases it.step using PlausibleIterStep.casesOn <;> simp [*]
27+
28+ theorem getElem?_frequencies {k : Nat} :
29+ (frequencies xs)[k]? = (some (xs.count k)).filter (0 < ·) := by
30+ -- We express the statement in terms of `List.foldr`, which makes the induction over `xs`
31+ -- easier because the initial tree map of the fold never changes. If the statement is expressed in
32+ -- terms of `List.foldl`, we would need to generalize the statement for general initial tree maps.
33+ rw [← List.reverse_reverse (as := xs), frequencies, List.foldl_reverse]
34+ generalize xs.reverse = xs
35+ induction xs <;> grind
36+
37+ theorem getElem_frequencies {k : Nat} {h} :
38+ (frequencies xs)[k]'h = xs.count k := by
39+ grind [getElem?_frequencies]
40+
41+ theorem mem_frequencies :
42+ x ∈ frequencies xs ↔ x ∈ xs := by
43+ rw [← TreeMap.isSome_getElem?_iff_mem, getElem?_frequencies]
44+ simp
45+
46+ theorem search_eq_neg_one (h : ∀ x, x = 0 ∨ xs.count x < x) :
47+ search xs = -1 := by
48+ simp only [search, ← Iter.first?_toList, Option.getD_eq_iff, Iter.toList_map, Iter.toList_filter,
49+ TreeMap.toList_iter]
50+ grind [List.length_filter_pos_iff, getElem?_frequencies]
51+
52+ theorem search_eq_neg_one_iff :
53+ search xs = -1 ↔ ∀ x, x = 0 ∨ xs.count x < x := by
54+ simp [search, ← Iter.first?_toList, Option.getD_eq_iff, Iter.toList_map, Iter.toList_filter,
55+ TreeMap.toList_iter, getElem?_frequencies]
56+ grind [List.length_filter_pos_iff]
57+
58+ def IsCandidate (xs : List Nat) (x : Nat) : Prop := 0 < x ∧ x ≤ xs.count x
59+
60+ theorem List.find?_eq_some_iff_of_pairwise_eq_lt {xs : List α} {cmp : α → α → Ordering}
61+ [OrientedCmp cmp] (h : xs.Pairwise (cmp · · = .lt)) :
62+ xs.find? P = some x ↔ x ∈ xs ∧ P x ∧ ∀ y ∈ xs, P y → (cmp x y).isLE := by
63+ rw [List.find?_eq_some_iff_append]
64+ constructor
65+ · intro h'
66+ refine ⟨by grind, by grind, ?_⟩
67+ intro y hm hy
68+ obtain ⟨as, bs, rfl, h''⟩ := h'.2
69+ have : y ∈ x :: bs := by grind
70+ simp only [pairwise_append, pairwise_cons] at h
71+ grind [Ordering.isLE_of_eq_eq, Ordering.isLE_of_eq_lt]
72+ · intro h'
73+ refine ⟨by grind, ?_⟩
74+ obtain ⟨as, bs, rfl⟩ := List.mem_iff_append.mp h'.1
75+ refine ⟨as, bs, rfl, ?_⟩
76+ intro a ha
77+ simp only [pairwise_append, pairwise_cons] at h
78+ replace h' := h'.2 .2
79+ specialize h' a (by grind)
80+ have : cmp x a = .gt := by
81+ rw [OrientedCmp.eq_swap (cmp := cmp), Ordering.swap_eq_gt]
82+ grind
83+ grind [Ordering.ne_gt_iff_isLE]
84+
85+ theorem search_eq (h : IsCandidate xs x ∧ ∀ y, IsCandidate xs y → y ≤ x) :
86+ search xs = x := by
87+ have : OrientedCmp (fun x y : Nat × Nat => compare y.1 x.1 ) := by
88+ constructor
89+ grind [Nat.compare_swap]
90+ simp [search, ← Iter.first?_toList, Option.getD_eq_iff, List.find?_eq_some_iff_of_pairwise_eq_lt (frequencies xs).ordered_keys_toList, Int.natCast_inj]
91+ have : x ∈ xs := by grind [IsCandidate, List.count_pos_iff]
92+ refine ⟨(frequencies xs)[x]'(by simpa [mem_frequencies]), ?_⟩
93+ grind [getElem?_frequencies, IsCandidate, Nat.isLE_compare]
94+
95+ /-
96+ Memo:
97+
98+ * grind runs into deep recursion
99+ * hard to debug and minimize
100+
101+ [ grind ] Diagnostics ▼
102+ [ thm ] E-Matching instances ▼
103+ [] getElem?_neg ↦ 1
104+ [] getElem?_pos ↦ 1
105+ [] List.getElem?_map ↦ 1
106+ [] List.getElem_of_getElem? ↦ 1
107+ [] List.length_filter_pos_iff ↦ 1
108+
109+
110+ [ thm ] getElem?_neg: [@getElem? #8 #7 #6 #5 #4 #2 #1]
111+ [ thm ] getElem?_pos: [@getElem? #8 #7 #6 #5 #4 #2 #1]
112+ [ thm ] List.getElem?_map: [@getElem? (List #3) `[ Nat ] _ _ _ (@List.map #4 _ #2 #1) #0]
113+ [ thm ] List.map_map: [@List.map #5 #4 #2 (@List.map #3 _ #1 #0)]
114+ [ thm ] List.filter_map: [@List.filter #3 #1 (@List.map #4 _ #2 #0)]
115+ [ thm ] List.getElem?_filter: [@getElem? (List #6) `[ Nat ] _ _ _ (@List.filter _ #3 #4) #2, @some _ #5]
116+ [ thm ] List.getElem_of_getElem?: [@getElem? (List #4) `[ Nat ] _ _ _ #1 #3, @some _ #2]
117+ [ thm ] TreeMap.length_toList: [@List.length (Prod #3 #2) (@TreeMap.toList _ _ #1 #0)]
118+ [ thm ] List.length_filter_le: [@List.length #2 (@List.filter _ #1 #0)]
119+ [ thm ] List.length_map: [@List.length #2 (@List.map #3 _ #0 #1)]
120+ [ thm ] List.eq_nil_of_length_eq_zero: [@List.length #2 #1]
121+ [ thm ] List.getElem?_eq_none: [@List.length #3 #2, @getElem? (List _) `[ Nat ] _ _ _ #2 #1]
122+ [ thm ] Option.some_le_some: [@LE.le (Option #3) _ (@some _ #1) (@some _ #0)]
123+ [ thm ] Option.not_some_le_none: [@LE.le (Option #2) _ (@some _ #0) (@none _)]
124+ [ thm ] Option.none_le: [@LE.le (Option #2) _ (@none _) #0]
125+ [ thm ] Option.map_some: [@Option.map #3 #2 #0 (@some _ #1)]
126+ [ thm ] Option.map_none: [@Option.map #2 #1 #0 (@none _)]
127+ -/
5128
6129/-!
7130## Prompt
@@ -10,8 +133,8 @@ def search : Unit :=
10133
11134def search(lst):
12135 '''
13- You are given a non-empty list of positive integers. Return the greatest integer that is greater than
14- zero, and has a frequency greater than or equal to the value of the integer itself.
136+ You are given a non-empty list of positive integers. Return the greatest integer that is greater than
137+ zero, and has a frequency greater than or equal to the value of the integer itself.
15138 The frequency of an integer is the number of times it appears in the list.
16139 If no such a value exist, return -1.
17140 Examples:
@@ -32,7 +155,7 @@ def search(lst):
32155 for i in range(1, len(frq)):
33156 if frq[i] >= i:
34157 ans = i
35-
158+
36159 return ans
37160```
38161
@@ -71,4 +194,4 @@ def check(candidate):
71194 assert candidate([3, 10, 10, 9, 2]) == -1
72195
73196```
74- -/
197+ -/
0 commit comments