11module
22
3- def how_many_times : Unit :=
4- ()
3+ -- This compiles to a tight loop calling `memcpy` a bunch of times
4+ def howManyTimes (string substring : String) : Nat :=
5+ string.positions.filter (fun p => (string.sliceFrom p.1 ).startsWith substring) |>.length
6+
7+ example : howManyTimes "" "a" = 0 := by native_decide
8+ example : howManyTimes "aaa" "a" = 3 := by native_decide
9+ example : howManyTimes "aaaa" "aa" = 3 := by native_decide
10+ example : howManyTimes "xyxyxyx" "x" = 4 := by native_decide
11+ example : howManyTimes "cacacacac" "cac" = 4 := by native_decide
12+ example : howManyTimes "john doe" "john" = 1 := by native_decide
13+
14+ def howManyTimesSpec [DecidableEq α] (string substring : List α) : Nat :=
15+ ((0 ...=string.length).toList.filter (fun i => (string.drop i).take substring.length = substring)).length
16+
17+ theorem howManyTimes_eq {string substring : String} (h : substring ≠ "" ) :
18+ howManyTimes string substring = howManyTimesSpec string.toList substring.toList := by
19+ simp only [howManyTimes, ne_eq,
20+ ← Std.Iter.length_toList_eq_length, Std.Iter.toList_filter, String.toList_positions,
21+ howManyTimesSpec, String.length_toList, Nat.zero_le, Nat.toList_rcc_eq_append,
22+ List.filter_append, List.filter_cons, decide_eq_true_eq, List.filter_nil, List.length_append]
23+ rw [if_neg (by simpa [← String.length_toList])]
24+ simp only [List.length_nil, Nat.add_zero]
25+ suffices ∀ (p : string.Pos) (t₁ t₂ : String), p.Splits t₁ t₂ →
26+ (List.filter (fun p => (string.sliceFrom p.val).startsWith substring)
27+ (String.Model.positionsFrom p)).length =
28+ (List.filter (fun i => decide (List.take substring.length (List.drop i string.toList) = substring.toList))
29+ (t₁.length...string.length).toList).length by
30+ simpa using this string.startPos "" string
31+ intro p t₁ t₂ ht
32+ induction p using String.Pos.next_induction generalizing t₁ t₂ with
33+ | next p hp ih =>
34+ obtain ⟨t₂, rfl⟩ := ht.exists_eq_singleton_append hp
35+ rw [String.Model.positionsFrom_eq_cons hp, List.filter_cons, Nat.toList_rco_eq_cons]
36+ · rw [List.filter_cons, apply_ite List.length, apply_ite List.length]
37+ simp only [String.Slice.startsWith_string_iff, ne_eq, List.length_cons, ih _ _ ht.next,
38+ String.append_singleton, String.length_push, decide_eq_true_eq]
39+ congr
40+ simp [ht.eq_append, ht.copy_sliceFrom_eq, List.prefix_iff_eq_take, eq_comm]
41+ · have := congrArg String.length ht.eq_append
42+ simp at this
43+ omega
44+ | endPos => simp_all
545
646/-!
747## Prompt
@@ -49,4 +89,4 @@ def check(candidate):
4989 assert candidate('cacacacac', 'cac') == 4
5090 assert candidate('john doe', 'john') == 1
5191```
52- -/
92+ -/
0 commit comments