11module
2+ import Std.Tactic.Do
3+ import Std
4+ import all Init.Data.Range.Polymorphic.UpwardEnumerable -- UpwardEnumerable.least not exposed
25
3- def is_happy : Unit :=
4- ()
6+ open Std Std.Do
7+
8+ def isHappy (s : String) : Bool := Id.run do
9+ if s.length < 3 then
10+ return false
11+
12+ let a := s.toList.toArray
13+ for h : i in *...a.size - 2 do
14+ if a[i] = a[i + 1 ] ∨ a[i + 1 ] = a[i + 2 ] ∨ a[i] = a[i + 2 ] then
15+ return false
16+
17+ return true
18+
19+ theorem Nat.eq_add_of_toList_rio_eq_append_cons {a : Nat} {pref cur suff}
20+ (h : (*...a).toList = pref ++ cur :: suff) :
21+ cur = pref.length := by
22+ have := Rio.eq_succMany?_of_toList_eq_append_cons h
23+ simpa [PRange.UpwardEnumerable.least, PRange.least?] using this
24+
25+ set_option mvcgen.warning false in
26+ theorem isHappy_iff {s : String} :
27+ isHappy s = true ↔ 3 ≤ s.length ∧ (∀ (a b c : Char), [a, b, c] <:+: s.toList → a ≠ b ∧ b ≠ c ∧ a ≠ c) := by
28+ generalize hwp : isHappy s = wp
29+ apply Id.of_wp_run_eq hwp
30+ mvcgen
31+ invariants
32+ · .withEarlyReturn
33+ (fun cur _ => ⌜∀ a b c : Char, [a, b, c] <:+: s.toList.take (cur.pos + 2 ) → a ≠ b ∧ b ≠ c ∧ a ≠ c⌝)
34+ (fun r _ => ⌜r = false ∧ ∃ a b c : Char, [a, b, c] <:+: s.toList ∧ (a = b ∨ b = c ∨ a = c)⌝)
35+ next => grind
36+ next hsl a pref cur suff h b heq ih =>
37+ subst a
38+ obtain rfl := Nat.eq_add_of_toList_rio_eq_append_cons h
39+ have := congrArg List.length h
40+ simp only [List.size_toArray, String.length_toList, Rio.length_toList, Nat.size_rio,
41+ List.length_append, List.length_cons, reduceCtorEq, List.Cursor.pos_mk, ne_eq, false_and,
42+ Option.some.injEq, Bool.false_eq, true_and, and_self_left, exists_eq_left, false_or] at this ⊢
43+ refine ⟨_, _, _, ?_, heq⟩
44+ simp only [List.getElem_toArray]
45+ rw [List.infix_iff_getElem?]
46+ refine ⟨pref.length, ?_⟩
47+ simp only [List.length_cons, List.length_nil, Nat.zero_add, Nat.reduceAdd, String.length_toList]
48+ exact ⟨by grind, by rintro (_|_|_|i) hi <;> grind⟩
49+ next hsl a pref cur suff h b heq ih =>
50+ subst a
51+ obtain rfl := Nat.eq_add_of_toList_rio_eq_append_cons h
52+ have := congrArg List.length h
53+ simp only [List.size_toArray, String.length_toList, Std.Rio.length_toList, Nat.size_rio,
54+ List.length_append, List.length_cons, List.Cursor.pos_mk, ne_eq, reduceCtorEq, false_and,
55+ and_false, exists_const, or_false, List.length_nil, Nat.zero_add, true_and] at this ih ⊢
56+ intro a b c
57+ rw [List.take_add_one, getElem?_pos _ _ (by simp; omega), Option.toList_some,
58+ List.infix_concat_iff, or_imp]
59+ refine ⟨?_, ih.2 a b c⟩
60+ simp only [List.take_add_one, List.append_assoc]
61+ repeat rw [getElem?_pos _ _ (by simp; omega), Option.toList_some]
62+ rw [← List.nil_append [a, b, c], List.suffix_append_inj_of_length_eq (by simp)]
63+ grind
64+ next h a =>
65+ simp
66+ intro a b c hs
67+ have := hs.length_le
68+ grind
69+ next h a r h₁ h₂ =>
70+ subst a
71+ simp [h₁] at ⊢ h₂
72+ refine ⟨by omega, ?_⟩
73+ rwa [Nat.sub_add_cancel (by omega), ← String.length_toList, List.take_length] at h₂
74+ next => grind
575
676/-!
777## Prompt
@@ -29,7 +99,7 @@ def is_happy(s):
2999 return False
30100
31101 for i in range(len(s) - 2):
32-
102+
33103 if s[i] == s[i+1] or s[i+1] == s[i+2] or s[i] == s[i+2]:
34104 return False
35105 return True
@@ -50,4 +120,4 @@ def check(candidate):
50120 assert candidate("iopaxpoi") == True , "iopaxpoi"
51121 assert candidate("iopaxioi") == False , "iopaxioi"
52122```
53- -/
123+ -/
0 commit comments