1- def is_sorted : Unit :=
2- ()
1+ module
2+
3+ import Std
4+ open Std Std.Do
5+
6+ set_option mvcgen.warning false
7+
8+ /-!
9+ ## Implementation
10+ -/
11+
12+ def isSorted (xs : Array Nat) : Bool := Id.run do
13+ if h : xs.size > 0 then
14+ let mut last := xs[0 ]
15+ let mut repeated := false
16+ for x in xs[1 ...*] do
17+ match compare last x with
18+ | .lt =>
19+ last := x
20+ repeated := false
21+ | .eq =>
22+ if repeated then
23+ return false
24+ else
25+ repeated := true
26+ | .gt =>
27+ return false
28+ return true
29+
30+ /-!
31+ ## Tests
32+ -/
33+
34+ example : isSorted #[5 ] = true := by native_decide
35+ example : isSorted #[1 , 2 , 3 , 4 , 5 ] = true := by native_decide
36+ example : isSorted #[1 , 3 , 2 , 4 , 5 ] = false := by native_decide
37+ example : isSorted #[1 , 2 , 3 , 4 , 5 , 6 ] = true := by native_decide
38+ example : isSorted #[1 , 2 , 3 , 4 , 5 , 6 , 7 ] = true := by native_decide
39+ example : isSorted #[1 , 3 , 2 , 4 , 5 , 6 , 7 ] = false := by native_decide
40+ example : isSorted #[] = true := by native_decide
41+ example : isSorted #[1 ] = true := by native_decide
42+ example : isSorted #[3 , 2 , 1 ] = false := by native_decide
43+ example : isSorted #[1 , 2 , 2 , 2 , 3 , 4 ] = false := by native_decide
44+ example : isSorted #[1 , 2 , 3 , 3 , 3 , 4 ] = false := by native_decide
45+ example : isSorted #[1 , 2 , 2 , 3 , 3 , 4 ] = true := by native_decide
46+ example : isSorted #[1 , 2 , 3 , 4 ] = true := by native_decide
47+
48+ /-!
49+ ## Missing API
50+ -/
51+
52+ instance : LawfulOrderOrd Nat where
53+ isLE_compare := by grind [Nat.isLE_compare]
54+ isGE_compare := by grind [Nat.isGE_compare]
55+
56+ attribute [- grind] Array.mkSlice_rci_eq_mkSlice_rco
57+ attribute [grind =] Array.toList_mkSlice_rci
58+ attribute [grind .] List.Pairwise.nil
59+
60+ grind_pattern compare_eq_lt => compare a b, Ordering.lt where
61+ guard compare a b = .lt
62+
63+ grind_pattern compare_eq_eq => compare a b, Ordering.eq where
64+ guard compare a b = .eq
65+
66+ grind_pattern compare_eq_gt => compare a b, Ordering.gt where
67+ guard compare a b = .gt
68+
69+ /-!
70+ ## Verification
71+ -/
72+
73+ theorem pairwise_append_of_trans {xs ys : List α} {R : α → α → Prop } [Trans R R R] :
74+ (xs ++ ys).Pairwise R ↔ xs.Pairwise R ∧ ys.Pairwise R ∧ (∀ (hxs : xs ≠ []) (hys : ys ≠ []), R (xs.getLast hxs) (ys.head hys)) := by
75+ rw [List.pairwise_append]
76+ apply Iff.intro
77+ · grind
78+ · rintro ⟨hpxs, hpys, h⟩
79+ refine ⟨hpxs, hpys, fun x hx y hy => ?_⟩
80+ rw [List.pairwise_iff_getElem] at hpxs hpys
81+ specialize h (by grind) (by grind)
82+ simp only [List.getLast_eq_getElem, List.head_eq_getElem] at h
83+ rw [List.mem_iff_getElem] at hx hy
84+ obtain ⟨i, hi, rfl⟩ := hx
85+ obtain ⟨j, hj, rfl⟩ := hy
86+ have h₁ : i < xs.length - 1 → R xs[i] xs[xs.length - 1 ] := by grind
87+ have h₂ : 0 < j → R ys[0 ] ys[j] := by grind
88+ by_cases hi' : i = xs.length - 1 <;> by_cases hj' : j = 0
89+ · grind
90+ · exact Trans.trans (r := R) (by grind) (h₂ (by grind))
91+ · exact Trans.trans (s := R) (h₁ (by grind)) (by grind)
92+ · exact Trans.trans (h₁ (by grind)) (Trans.trans h (h₂ (by grind)))
93+
94+ theorem pairwise_cons_of_trans {x : α} {xs : List α} {R : α → α → Prop } [Trans R R R] :
95+ (x :: xs).Pairwise R ↔ (∀ (hxs : xs ≠ []), R x (xs.head hxs)) ∧ xs.Pairwise R := by
96+ have := pairwise_append_of_trans (R := R) (xs := [x]) (ys := xs)
97+ grind
98+
99+ theorem sorted_of_isSorted {xs : Array Nat} (h : isSorted xs) : xs.toList.Pairwise (· ≤ ·) := by
100+ revert h -- Without reverting, we will not be able to use that the return value is `true` to show
101+ --that early returns cannot happen.
102+ generalize hwp : isSorted xs = wp
103+ rw [← Array.toArray_toList (xs := xs)] at *
104+ generalize xs.toList = xs at *
105+ apply Id.of_wp_run_eq hwp
106+ mvcgen
107+ invariants
108+ | inv1 => .withEarlyReturn
109+ (fun cur ⟨last, _⟩ =>
110+ ⌜(xs[0 ] :: cur.prefix).getLast? = some last ∧ (xs[0 ] :: cur.prefix).Pairwise (· ≤ ·)⌝)
111+ (fun ret _ => ⌜ret = false ⌝)
112+ case vc1 =>
113+ simp only [pairwise_cons_of_trans, pairwise_append_of_trans] at *
114+ grind
115+ case vc3 =>
116+ simp only [pairwise_cons_of_trans, pairwise_append_of_trans] at *
117+ grind
118+ all_goals grind [List.getElem_zero, List.drop_one]
119+
120+ theorem count_le_two_of_isSorted {xs : Array Nat} {x : Nat} (h : isSorted xs) : xs.count x ≤ 2 := by
121+ have hp : xs.toList.Pairwise (· ≤ ·) := sorted_of_isSorted h
122+ rw [List.pairwise_iff_getElem] at hp
123+ revert h
124+ generalize hwp : isSorted xs = wp
125+ rw [← Array.toArray_toList (xs := xs)] at *
126+ generalize xs.toList = xs at *
127+ apply Id.of_wp_run_eq hwp
128+ mvcgen
129+ invariants
130+ | inv1 => .withEarlyReturn
131+ (fun cur ⟨last, repeated⟩ => ⌜(xs[0 ] :: cur.prefix).getLast? = some last ∧ (xs[0 ] :: cur.prefix).count x ≤ (if (last = x ∧ repeated) ∨ x < last then 2 else 1 )⌝)
132+ (fun ret _ => ⌜ret = false ⌝)
133+ case vc1 pref cur suff _ _ _ _ _ _ _ =>
134+ simp only [← List.cons_append]
135+ -- We generalize to simplify the problem.
136+ generalize hpref' : xs[0 ] :: pref = pref' at *
137+ have : xs = pref' ++ cur :: suff := by grind [List.getElem_zero, List.drop_one]
138+ clear hpref'
139+ simp only [List.count_append, List.count_singleton]
140+ have : ¬ cur ∈ pref' := by grind [List.mem_iff_getElem]
141+ split <;> grind [List.count_eq_zero]
142+ case vc6 a b c d e f =>
143+ grind [List.getElem_zero, List.drop_one]
144+ all_goals (clear hp; grind)
145+
146+ theorem not_pairwise_or_exists_count_of_isSorted_eq_false {xs : Array Nat} (h : isSorted xs = false ) :
147+ ¬ xs.toList.Pairwise (· ≤ ·) ∨ (∃ x, xs.count x ≥ 3 ) := by
148+ revert h
149+ generalize hwf : isSorted xs = wf
150+ rw [← Array.toArray_toList (xs := xs)] at *
151+ generalize xs.toList = xs at *
152+ apply Id.of_wp_run_eq hwf
153+ mvcgen
154+ invariants
155+ | inv1 => .withEarlyReturn
156+ (fun cur ⟨last, repeated⟩ => ⌜(xs[0 ] :: cur.prefix).getLast? = some last ∧ (repeated → (xs[0 ] :: cur.prefix).count last ≥ 2 )⌝)
157+ (fun ret ⟨last, repeated⟩ => ⌜¬ xs.Pairwise (· ≤ ·) ∨ xs.count last ≥ 3 ⌝)
158+ case vc2 pref cur suff _ _ _ _ _ _ _ _ =>
159+ have : xs = xs[0 ] :: pref ++ cur :: suff := by grind [List.getElem_zero, List.drop_one]
160+ grind
161+ case vc4 pref cur suff _ _ _ last _ _ _ =>
162+ have : xs = xs[0 ] :: pref ++ cur :: suff := by grind [List.getElem_zero, List.drop_one]
163+ have : ∃ (i j : Nat), i < j ∧ ∃ hi, ∃ hj, (xs[j]'hj) < (xs[i]'hi) := ⟨pref.length, pref.length + 1 , by grind⟩
164+ simp [List.pairwise_iff_getElem, this]
165+ case vc7 =>
166+ -- Because the `xs.toArray.count` call is under an `∃` binder in the goal, `grind`'s
167+ -- congruence closure is not able to use `List.count_toArray`.
168+ simp only [List.count_toArray] at *
169+ grind
170+
171+ all_goals grind
172+
173+ theorem isSorted_iff {xs : Array Nat} :
174+ isSorted xs ↔ xs.toList.Pairwise (· ≤ ·) ∧ ∀ x, xs.count x ≤ 2 := by
175+ grind [sorted_of_isSorted, count_le_two_of_isSorted,
176+ not_pairwise_or_exists_count_of_isSorted_eq_false]
3177
4178/-!
5179## Prompt
@@ -29,15 +203,15 @@ def is_sorted(lst):
29203```python3
30204 count_digit = dict([(i, 0) for i in lst])
31205 for i in lst:
32- count_digit[i]+=1
206+ count_digit[i]+=1
33207 if any(count_digit[i] > 2 for i in lst):
34208 return False
35209 if all(lst[i-1] <= lst[i] for i in range(1, len(lst))):
36210 return True
37211 else:
38212 return False
39-
40-
213+
214+
41215```
42216
43217## Tests
@@ -55,12 +229,12 @@ def check(candidate):
55229 assert candidate([]) == True, "This prints if this assert fails 2 (good for debugging!)"
56230 assert candidate([1]) == True, "This prints if this assert fails 3 (good for debugging!)"
57231 assert candidate([3, 2, 1]) == False, "This prints if this assert fails 4 (good for debugging!)"
58-
232+
59233 # Check some edge cases that are easy to work out by hand.
60234 assert candidate([1, 2, 2, 2, 3, 4]) == False, "This prints if this assert fails 5 (good for debugging!)"
61235 assert candidate([1, 2, 3, 3, 3, 4]) == False, "This prints if this assert fails 6 (good for debugging!)"
62236 assert candidate([1, 2, 2, 3, 3, 4]) == True, "This prints if this assert fails 7 (good for debugging!)"
63237 assert candidate([1, 2, 3, 4]) == True, "This prints if this assert fails 8 (good for debugging!)"
64238
65239```
66- -/
240+ -/
0 commit comments