1- def pairs_sum_to_zero : Unit :=
2- ()
1+ import Std.Data.HashSet.Lemmas
2+ import Std.Tactic.Do
3+
4+ open Std Do
5+
6+ theorem List.exists_mem_iff_exists_getElem (P : α → Prop ) (l : List α) :
7+ (∃ x ∈ l, P x) ↔ ∃ (i : Nat), ∃ hi, P (l[i]'hi) := by
8+ grind [mem_iff_getElem]
9+
10+ structure List.Any₂ (P : α → α → Prop ) (l : List α) where
11+ not_pairwise : ¬ l.Pairwise (fun x y => ¬P x y)
12+
13+ theorem List.any₂_iff_not_pairwise {P : α → α → Prop } {l : List α} :
14+ l.Any₂ P ↔ ¬l.Pairwise (fun x y => ¬P x y) := by
15+ grind [List.Any₂]
16+
17+ @ [simp, grind]
18+ theorem not_any₂_nil {P : α → α → Prop } : ¬List.Any₂ P [] := by
19+ simp [List.any₂_iff_not_pairwise]
20+
21+ @ [simp, grind]
22+ theorem List.any₂_cons {P : α → α → Prop } {x : α} {xs : List α} :
23+ List.Any₂ P (x::xs) ↔ (∃ y ∈ xs, P x y) ∨ List.Any₂ P xs := by
24+ grind [List.any₂_iff_not_pairwise]
25+
26+ @[grind]
27+ theorem List.any₂_append {P : α → α → Prop } {xs ys : List α} :
28+ List.Any₂ P (xs ++ ys) ↔ List.Any₂ P xs ∨ List.Any₂ P ys ∨ (∃ x ∈ xs, ∃ y ∈ ys, P x y) := by
29+ grind [List.any₂_iff_not_pairwise]
30+
31+ def pairsSumToZero (l : List Int) : Bool :=
32+ go l ∅
33+ where
34+ go (m : List Int) (seen : HashSet Int) : Bool :=
35+ match m with
36+ | [] => false
37+ | x::xs => if -x ∈ seen then true else go xs (seen.insert x)
38+
39+ example : pairsSumToZero [1 , 3 , 5 , 0 ] = false := by native_decide
40+ example : pairsSumToZero [1 , 3 , -2 , 1 ] = false := by native_decide
41+ example : pairsSumToZero [1 , 2 , 3 , 7 ] = false := by native_decide
42+ example : pairsSumToZero [2 , 4 , -5 , 3 , 5 , 7 ] = true := by native_decide
43+ example : pairsSumToZero [1 ] = false := by native_decide
44+
45+ -- `simp` does not solve this!!
46+ example {x : Int} : -x + x = 0 := by omega
47+
48+ theorem pairsSumToZero_go_iff (l : List Int) (seen : HashSet Int) :
49+ pairsSumToZero.go l seen = true ↔
50+ l.Any₂ (fun a b => a + b = 0 ) ∨ ∃ a ∈ seen, ∃ b ∈ l, a + b = 0 := by
51+ fun_induction pairsSumToZero.go <;> simp <;> grind
52+
53+ theorem pairsSumToZero_iff (l : List Int) :
54+ pairsSumToZero l = true ↔ l.Any₂ (fun a b => a + b = 0 ) := by
55+ simp [pairsSumToZero, pairsSumToZero_go_iff]
56+
57+ def pairsSumToZero' (l : List Int) : Id Bool := do
58+ let mut seen : HashSet Int := ∅
59+ for x in l do
60+ if -x ∈ seen then
61+ return true
62+ seen := seen.insert x
63+ return false
64+
65+ -- Pain: I have no idea what the system wants from me when building an invariant.
66+ -- What is a PostCond? What does the function I'm returning mean? How are early returns
67+ -- and things like that represented?
68+
69+ -- Pain: The need to have the `l = z.pref` trick in the loop invariant. If you don't know this
70+ -- it's hard to make progress. It would be really cool if there was some kind of "interactive"
71+ -- way to build the loop invariant in multiple steps where I only have to input the
72+ -- `(∀ x, x ∈ s ↔ x ∈ z.rpref) ∧ ¬ z.pref.Any₂ (fun a b => a + b = 0)` and
73+ -- `l.Any₂ (fun a b => a + b = 0)` parts and the `o = none/some` and `l = z.pref` gadgets are
74+ -- added automatically.
75+
76+ -- Pain: `refine` does not play well with `⇓` it seems
77+
78+ -- Pain: mvcgen generated multiple assumptions named `h`, leading to inaccessible names
79+ -- Pain: mvcgen generally creates inaccessible names
80+
81+ @[simp]
82+ theorem List.Zipper.pref_mk {l : List α} {rpref suff h} :
83+ (List.Zipper.mk rpref suff h : List.Zipper l).pref = rpref.reverse := rfl
84+
85+ set_option mvcgen.warning false
86+
87+ theorem pairsSumToZero'_spec (l : List Int) :
88+ ⦃⌜True⌝⦄ pairsSumToZero' l ⦃⇓r => r = true ↔ l.Any₂ (fun a b => a + b = 0 )⦄ := by
89+ mvcgen [pairsSumToZero']
90+
91+ case inv =>
92+ exact (fun (⟨o, s⟩, z) =>
93+ (o = none ∧ (∀ x, x ∈ s ↔ x ∈ z.rpref) ∧ ¬ z.pref.Any₂ (fun a b => a + b = 0 )) ∨
94+ (o = some true ∧ l.Any₂ (fun a b => a + b = 0 ) ∧ l = z.pref), ())
95+
96+ all_goals simp_all [List.any₂_append] <;> grind
397
498/-!
599## Prompt
@@ -56,4 +150,64 @@ def check(candidate):
56150 assert candidate([-3, 9, -1, 4, 2, 31]) == False
57151
58152```
59- -/
153+ -/
154+
155+ /-! Bonus material -/
156+
157+ theorem List.exists_append (l : List α) (n : Nat) (h : n ≤ l.length) : ∃ xs ys, ys.length = n ∧ l = xs ++ ys :=
158+ ⟨l.take (l.length - n), l.drop (l.length - n), by grind, by simp⟩
159+
160+ @[simp]
161+ theorem Std.HashSet.toList_emptyWithCapacity {α : Type u} [BEq α] [Hashable α]
162+ [EquivBEq α] [LawfulHashable α] {n : Nat} :
163+ (HashSet.emptyWithCapacity n : HashSet α).toList = [] := by
164+ simp [← List.isEmpty_iff]
165+
166+ @[simp]
167+ theorem Std.HashSet.toList_empty {α : Type u} [BEq α] [Hashable α]
168+ [EquivBEq α] [LawfulHashable α] : (∅ : HashSet α).toList = [] := by
169+ simp [← List.isEmpty_iff]
170+
171+ theorem List.Any₂.append_left {P : α → α → Prop } (xs : List α) {ys : List α} (h : ys.Any₂ P) : (xs ++ ys).Any₂ P :=
172+ List.any₂_append.2 (by simp [h])
173+
174+ theorem List.Any₂.append_right {P : α → α → Prop } {xs : List α} (ys : List α) (h : xs.Any₂ P) : (xs ++ ys).Any₂ P :=
175+ List.any₂_append.2 (by simp [h])
176+
177+ theorem List.any₂_append_left_right {P : α → α → Prop } (xs ys : List α) (h : ∃ x ∈ xs, ∃ y ∈ ys, P x y) :
178+ (xs ++ ys).Any₂ P :=
179+ List.any₂_append.2 (by simp [h])
180+
181+ theorem List.any₂_iff_exists (P : α → α → Prop ) (l : List α) :
182+ List.Any₂ P l ↔ ∃ xs x ys, l = xs ++ x :: ys ∧ ∃ y ∈ ys, P x y := by
183+ constructor
184+ · rintro ⟨h⟩
185+ induction l with
186+ | nil => grind
187+ | cons x xs ih =>
188+ rw [List.pairwise_cons, Classical.not_and_iff_not_or_not] at h
189+ simp only [Classical.not_forall, Classical.not_not] at h
190+ rcases h with (⟨y, hy, hxy⟩|h)
191+ · exact ⟨[], by grind⟩
192+ · grind
193+ · grind
194+
195+ theorem List.any₂_iff_exists_getElem (P : α → α → Prop ) (l : List α) :
196+ List.Any₂ P l ↔ ∃ (i j : Nat), ∃ hi hj, i < j ∧ P (l[i]'hi) (l[j]'hj) := by
197+ rw [List.any₂_iff_exists]
198+ constructor
199+ · rintro ⟨xs, x, ys, ⟨rfl, h⟩⟩
200+ obtain ⟨j₀, hj₀, hj₀'⟩ := (List.exists_mem_iff_exists_getElem _ _).1 h
201+ exact ⟨xs.length, xs.length + 1 + j₀, by grind⟩
202+ · rintro ⟨i, j, hi, hj, hij, h⟩
203+ exact ⟨l.take i, l[i], l.drop (i + 1 ), by simp,
204+ (List.exists_mem_iff_exists_getElem _ _).2 ⟨j - i - 1 , by grind, by grind⟩⟩
205+
206+ @ [simp, grind]
207+ theorem List.any₂_append_singleton {P : α → α → Prop } {xs : List α} {x : α} :
208+ List.Any₂ P (xs ++ [x]) ↔ List.Any₂ P xs ∨ ∃ y ∈ xs, P y x := by
209+ grind [List.any₂_iff_not_pairwise]
210+
211+ @ [simp, grind]
212+ theorem not_any₂_singleton {P : α → α → Prop } {x : α} : ¬List.Any₂ P [x] := by
213+ simp [List.any₂_iff_not_pairwise]
0 commit comments