@@ -17,6 +17,8 @@ def search (xs : List Nat) : Int :=
1717 |>.first?
1818 kv.getD (-1 )
1919
20+ -- missing api
21+
2022@ [simp, grind =]
2123theorem Std.Iter.first?_toList {α β : Type w} [Iterator α Id β] [IteratorLoop α Id Id]
2224 [Iterators.Finite α Id] [LawfulIteratorLoop α Id Id] {it : Iter (α := α) β} :
@@ -25,38 +27,6 @@ theorem Std.Iter.first?_toList {α β : Type w} [Iterator α Id β] [IteratorLoo
2527 rw [first?_eq_match_step, toList_eq_match_step]
2628 cases it.step using PlausibleIterStep.casesOn <;> simp [*]
2729
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-
6030theorem List.find?_eq_some_iff_of_pairwise_eq_lt {xs : List α} {cmp : α → α → Ordering}
6131 [OrientedCmp cmp] (h : xs.Pairwise (cmp · · = .lt)) :
6232 xs.find? P = some x ↔ x ∈ xs ∧ P x ∧ ∀ y ∈ xs, P y → (cmp x y).isLE := by
@@ -82,49 +52,56 @@ theorem List.find?_eq_some_iff_of_pairwise_eq_lt {xs : List α} {cmp : α → α
8252 grind
8353 grind [Ordering.ne_gt_iff_isLE]
8454
85- theorem search_eq (h : IsCandidate xs x ∧ ∀ y, IsCandidate xs y → y ≤ x) :
86- search xs = x := by
55+ theorem List.find?_eq_find? {xs : List α} {p q : α → Bool} (h : ∀ x ∈ xs, p x = q x) :
56+ xs.find? p = xs.find? q := by
57+ ext
58+ grind [List.find?_eq_some_iff_append]
59+
60+ theorem Option.getD_map_eq_elim {o : Option α} {f : α → β} {fallback : β} :
61+ (o.map f).getD fallback = o.elim fallback f := by
62+ cases o <;> simp
63+
64+ -- verification
65+
66+ theorem getElem?_frequencies {k : Nat} :
67+ (frequencies xs)[k]? = (some (xs.count k)).filter (0 < ·) := by
68+ -- We express the statement in terms of `List.foldr`, which makes the induction over `xs`
69+ -- easier because the initial tree map of the fold never changes. If the statement is expressed in
70+ -- terms of `List.foldl`, we would need to generalize the statement for general initial tree maps.
71+ rw [← List.reverse_reverse (as := xs), frequencies, List.foldl_reverse]
72+ generalize xs.reverse = xs
73+ induction xs <;> grind
74+
75+ theorem mem_frequencies :
76+ x ∈ frequencies xs ↔ x ∈ xs := by
77+ rw [← TreeMap.isSome_getElem?_iff_mem, getElem?_frequencies]
78+ simp
79+
80+ theorem search_eq_getD_find? :
81+ search xs = (frequencies xs |>.toList.find? (fun x => 0 < x.1 ∧ x.1 ≤ xs.count x.1 ) |>.map (↑·.fst) |>.getD (-1 : Int)) := by
82+ simp only [search, ← Iter.first?_toList, Iter.toList_map, Iter.toList_filter,
83+ TreeMap.toList_iter, List.head?_map]
84+ grind [List.find?_eq_find?, getElem?_frequencies]
85+
86+ theorem search_eq_neg_one_iff :
87+ search xs = -1 ↔ ∀ x, x = 0 ∨ xs.count x < x := by
88+ simp [search_eq_getD_find?, Option.getD_eq_iff, getElem?_frequencies]
89+ grind [List.length_filter_pos_iff]
90+
91+ theorem search_eq_iff_of_ne_neg_one (h : y ≠ -1 ) :
92+ search xs = y ↔ 0 < y ∧ y ≤ xs.count y.toNat ∧ ∀ z, 0 < z → z ≤ xs.count z → z ≤ y := by
93+ -- Prepare `OrientedCmp` instance to be used by `List.find?_eq_some_iff_of_pairwise_eq_lt`
8794 have : OrientedCmp (fun x y : Nat × Nat => compare y.1 x.1 ) := by
8895 constructor
8996 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- -/
97+ simp only [search_eq_getD_find?, Option.getD_map_eq_elim, Option.elim]
98+ split <;> rename_i heq
99+ · simp only [List.find?_eq_some_iff_of_pairwise_eq_lt (frequencies xs).ordered_keys_toList,
100+ Prod.forall, TreeMap.mem_toList_iff_getElem?_eq_some, getElem?_frequencies] at heq
101+ grind [List.count_pos_iff, isLE_compare]
102+ · simp only [List.find?_eq_none, Prod.forall, TreeMap.mem_toList_iff_getElem?_eq_some,
103+ getElem?_frequencies, Option.filter_eq_some_iff, and_imp] at heq
104+ grind [mem_frequencies]
128105
129106/-!
130107## Prompt
0 commit comments