Skip to content

Commit ec79c5e

Browse files
authored
Bracket problems (#247)
More cleanup and upstreaming needed, but let's do that in a follow-up.
1 parent dfb9f52 commit ec79c5e

File tree

6 files changed

+626
-16
lines changed

6 files changed

+626
-16
lines changed

HumanEvalLean/Common/Brackets.lean

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
module
2+
3+
import Std.Tactic.Do
4+
public section
5+
6+
inductive Paren where
7+
| open : Paren
8+
| close : Paren
9+
10+
def Paren.toInt : Paren → Int
11+
| .open => 1
12+
| .close => -1
13+
14+
@[simp, grind =] theorem Paren.toInt_open : Paren.open.toInt = 1 := (rfl)
15+
@[simp, grind =] theorem Paren.toInt_close : Paren.close.toInt = -1 := (rfl)
16+
17+
@[simp] theorem Paren.toInt_nonneg_iff {p : Paren} : 0 ≤ p.toInt ↔ p = .open := by cases p <;> simp
18+
@[simp] theorem Paren.toInt_pos_iff {p : Paren} : 0 < p.toInt ↔ p = .open := by cases p <;> simp
19+
@[simp] theorem Paren.toInt_nonpos_iff {p : Paren} : p.toInt ≤ 0 ↔ p = .close := by cases p <;> simp
20+
@[simp] theorem Paren.toInt_neg_iff {p : Paren} : p.toInt < 0 ↔ p = .close := by cases p <;> simp
21+
@[simp] theorem Paren.toInt_eq_one_iff {p : Paren} : p.toInt = 1 ↔ p = .open := by cases p <;> simp
22+
@[simp] theorem Paren.toInt_eq_neg_one_iff {p : Paren} : p.toInt = -1 ↔ p = .close := by cases p <;> simp
23+
24+
inductive IsBalanced : List Paren → Prop where
25+
| empty : IsBalanced []
26+
| append (l₁ l₂) : IsBalanced l₁ → IsBalanced l₂ → IsBalanced (l₁ ++ l₂)
27+
| enclose (l) : IsBalanced l → IsBalanced (.open :: l ++ [.close])
28+
29+
attribute [simp] IsBalanced.empty
30+
31+
def balance (l : List Paren) : Int :=
32+
l.map (·.toInt) |>.sum
33+
34+
@[simp, grind =]
35+
theorem balance_nil : balance [] = 0 := by
36+
simp [balance]
37+
38+
@[simp, grind =]
39+
theorem balance_append : balance (l₁ ++ l₂) = balance l₁ + balance l₂ := by
40+
simp [balance]
41+
42+
@[simp, grind =]
43+
theorem balance_cons : balance (p :: l) = p.toInt + balance l := by
44+
simp [balance]
45+
46+
@[simp]
47+
theorem balance_flatMap {l : List α} {f : α → List Paren} : balance (l.flatMap f) = (l.map (balance ∘ f)).sum := by
48+
induction l with simp_all
49+
50+
theorem List.sum_eq_zero {l : List α} [Add α] [Zero α] [Std.LawfulIdentity (α := α) (· + ·) 0] (hl : ∀ a ∈ l, a = 0) : l.sum = 0 := by
51+
induction l with
52+
| nil => simp
53+
| cons hd tl ih =>
54+
simp only [mem_cons, forall_eq_or_imp] at hl
55+
simp [hl.1, ih hl.2, Std.LawfulLeftIdentity.left_id]
56+
57+
theorem List.take_cons_eq_if {l : List α} {a : α} {n : Nat} :
58+
(a::l).take n = if 0 < n then a :: l.take (n - 1) else [] := by
59+
split
60+
· exact take_cons ‹_›
61+
· obtain rfl : n = 0 := by omega
62+
simp
63+
64+
theorem List.take_singleton {a : α} {n : Nat} : [a].take n = if 0 < n then [a] else [] := by
65+
rw [List.take_cons_eq_if, List.take_nil]
66+
67+
def minBalance (l : List Paren) : Int :=
68+
(0...=l.length).toList.map (fun k => balance (l.take k)) |>.min (by simp)
69+
70+
attribute [-simp] Nat.toList_rcc_eq_append
71+
attribute [simp] Std.Rcc.mem_toList_iff_mem Std.Rcc.mem_iff
72+
73+
@[grind! .]
74+
theorem minBalance_nonpos (l : List Paren) : minBalance l ≤ 0 := by
75+
rw [minBalance]
76+
apply List.min_le_of_mem
77+
simp only [List.mem_map, Std.Rcc.mem_toList_iff_mem, Std.Rcc.mem_iff, Nat.zero_le, true_and]
78+
exact ⟨0, by simp⟩
79+
80+
theorem minBalance_le_balance (l : List Paren) : minBalance l ≤ balance l := by
81+
rw [minBalance]
82+
apply List.min_le_of_mem
83+
simp only [List.mem_map, Std.Rcc.mem_toList_iff_mem, Std.Rcc.mem_iff, Nat.zero_le, true_and]
84+
exact ⟨l.length, by simp⟩
85+
86+
@[simp]
87+
theorem minBalance_nil : minBalance [] = 0 := by
88+
simp [minBalance]
89+
90+
attribute [simp] Nat.min_le_left Nat.min_le_right
91+
92+
theorem minBalance_eq_zero_iff {l : List Paren} : minBalance l = 0 ↔ ∀ k, 0 ≤ balance (l.take k) := by
93+
simp only [← Std.le_antisymm_iff, minBalance_nonpos, true_and]
94+
simp only [minBalance, List.le_min_iff, List.mem_map, Std.Rcc.mem_toList_iff_mem, Std.Rcc.mem_iff,
95+
Nat.zero_le, true_and, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂]
96+
exact ⟨fun h n => List.take_eq_take_min ▸ h (min n l.length) (by simp), fun h n _ => h n⟩
97+
98+
theorem List.min_cons_cons {a b : α} {l : List α} [Min α] :
99+
(a :: b :: l).min (by simp) = (min a b :: l).min (by simp) := by rfl
100+
101+
theorem List.min_cons {a : α} {l : List α} [Min α] [Std.Associative (α := α) Min.min] {h} :
102+
(a :: l).min h = l.min?.elim a (min a ·) :=
103+
match l with
104+
| nil => by simp
105+
| cons hd tl => by simp [List.min?_cons, List.min_eq_get_min?]
106+
107+
@[simp]
108+
theorem List.min_cons_cons_nil [Min α] {a b : α} : [a, b].min (by simp) = min a b := by
109+
simp [List.min_cons_cons]
110+
111+
theorem add_min [Add α] [Min α] [LE α] [comm : Std.Commutative (α := α) (· + ·)] [Std.IsPartialOrder α] [Std.LawfulOrderMin α]
112+
[Lean.Grind.OrderedAdd α] {a b c : α} : a + min b c = min (a + b) (a + c) := by
113+
refine Std.le_antisymm ?_ ?_
114+
· rw [Std.le_min_iff, comm.comm a, comm.comm a, ← Lean.Grind.OrderedAdd.add_le_left_iff,
115+
comm.comm a, ← Lean.Grind.OrderedAdd.add_le_left_iff]
116+
exact ⟨Std.min_le_left, Std.min_le_right⟩
117+
· rw [Std.min_le, comm.comm a, comm.comm a, ← Lean.Grind.OrderedAdd.add_le_left_iff,
118+
comm.comm a, ← Lean.Grind.OrderedAdd.add_le_left_iff]
119+
obtain (h|h) := Std.min_eq_or (a := b) (b := c)
120+
· rw (occs := [1]) [← h]
121+
simp
122+
· rw (occs := [2]) [← h]
123+
simp
124+
125+
theorem List.add_min [Add α] [Min α] [LE α] [comm : Std.Commutative (α := α) (· + ·)] [Std.IsPartialOrder α] [Std.LawfulOrderMin α]
126+
[Lean.Grind.OrderedAdd α] {a : α} {l : List α} {h} :
127+
a + l.min h = (l.map (a + ·)).min (by simpa) := by
128+
generalize hlen : l.length = n
129+
induction n generalizing l with
130+
| zero => simp_all
131+
| succ n ih =>
132+
match n, l, hlen with
133+
| 0, [b], _ => simp
134+
| 1, [b, c], _ => simp [_root_.add_min]
135+
| n + 2, b :: c :: tl, _ =>
136+
simp only [min_cons_cons, map_cons]
137+
rw [ih (by grind)]
138+
simp [map_cons, _root_.add_min]
139+
140+
@[simp]
141+
theorem minBalance_cons {l : List Paren} {p : Paren} :
142+
minBalance (p :: l) = min 0 (p.toInt + minBalance l) := by
143+
rw [minBalance]
144+
simp [Nat.toList_rcc_succ_right_eq_cons_map]
145+
rw [List.min_cons, List.min?_eq_some_min (by simp)]
146+
simp only [Option.elim_some, minBalance, List.add_min, List.map_map]
147+
congr 1
148+
149+
@[simp]
150+
theorem minBalance_append_singleton {l : List Paren} {p : Paren} :
151+
minBalance (l ++ [p]) = min (minBalance l) (balance l + p.toInt) := by
152+
induction l with
153+
| nil => simp
154+
| cons hd tl ih =>
155+
simp only [List.cons_append, minBalance_cons, ih, balance_cons, Int.min_assoc]
156+
grind
157+
158+
theorem minBalance_append {l m : List Paren} :
159+
minBalance (l ++ m) = min (minBalance l) (balance l + minBalance m) := by
160+
induction l with
161+
| nil => simpa using (Int.min_eq_right (minBalance_nonpos _)).symm
162+
| cons p l ih => grind [minBalance_cons]
163+
164+
theorem isBalanced_iff {l : List Paren} :
165+
IsBalanced l ↔ (balance l = 0 ∧ minBalance l = 0) := by
166+
rw [minBalance_eq_zero_iff]
167+
refine ⟨fun h => ?_, fun h => ?_⟩
168+
· induction h with
169+
| empty => simp
170+
| append l₁ l₂ ih₁ ih₂ h₁ h₂ => exact ⟨by grind, by grind [List.take_append]⟩ -- https://github.com/leanprover/lean4/issues/12581
171+
| enclose l h ih =>
172+
refine ⟨by grind, fun k => ?_⟩
173+
simp only [List.cons_append, List.take_cons_eq_if]
174+
grind [List.take_append, List.take_singleton]
175+
· rcases h with ⟨h₁, h₂⟩
176+
generalize h : l.length = n
177+
induction n using Nat.strongRecOn generalizing l with | ind n ih
178+
subst h
179+
by_cases h : ∃ k, 0 < k ∧ k < l.length ∧ balance (l.take k) = 0
180+
· obtain ⟨k, hk₁, hk₂, hk₃⟩ := h
181+
rw [← List.take_append_drop k l]
182+
have := List.take_append_drop k l
183+
refine IsBalanced.append (l.take k) (l.drop k)
184+
(ih k (by grind) (by grind) (by grind) (by grind))
185+
(ih (l.length - k) (by grind) (by grind) (fun n => ?_) (by grind))
186+
suffices balance (List.take n (List.drop k l)) = balance (List.take ((l.take k).length + n) l) by
187+
simpa [this] using h₂ _
188+
rw (occs := [3]) [← this]
189+
rw [List.take_length_add_append, balance_append, hk₃, Int.zero_add]
190+
· by_cases h₀ : l.length = 0
191+
· simp_all
192+
· have h' : ∀ k, 0 < k → k < l.length → 0 < balance (l.take k) := by grind
193+
obtain ⟨l, rfl⟩ : ∃ l', l = .open :: l' ++ [.close] := by
194+
obtain ⟨l, rfl⟩ : ∃ l', l = .open :: l' := by
195+
refine ⟨l.tail, ?_⟩
196+
rw (occs := [1]) [← List.cons_head_tail (l := l) (by grind), List.cons.injEq]
197+
have := h₂ 1
198+
simp_all [List.take_one, List.head?_eq_some_head (l := l) (by grind)]
199+
refine ⟨l.take (l.length - 1), ?_⟩
200+
have : l ≠ [] := by grind
201+
have hl : l = l.take (l.length - 1) ++ [l[l.length - 1]'(by grind)] := by
202+
rw (occs := [1]) [← List.take_append_drop (l.length - 1) l, List.append_right_inj]
203+
rw [List.drop_sub_one (by grind), List.drop_length, List.append_nil,
204+
getElem?_pos _ _ (by grind), Option.toList_some]
205+
suffices l[l.length - 1]'(by grind) = .close by
206+
rw (occs := [1]) [hl, this, List.cons_append]
207+
have : balance [l[l.length - 1]'(by grind)] ≤ 0 := by grind
208+
simpa using this
209+
refine IsBalanced.enclose l (ih _ (by grind) (by grind) (fun k => ?_) rfl)
210+
rw [List.take_eq_take_min]
211+
have := h' (min k l.length + 1)
212+
grind
213+
214+
theorem not_isBalanced_append_of_balance_neg {l m : List Paren} (h : balance l < 0) :
215+
¬ IsBalanced (l ++ m) := by
216+
simpa [isBalanced_iff, minBalance_eq_zero_iff] using fun _ => ⟨l.length, by simp [h]⟩
217+
218+
theorem balance_nonneg_of_isBalanced_append {l m : List Paren} (h : IsBalanced (l ++ m)) :
219+
0 ≤ balance l := by
220+
simp only [isBalanced_iff, balance_append, minBalance_eq_zero_iff] at h
221+
simpa using h.2 l.length
222+
223+
theorem IsBalanced.balance_eq_zero {l : List Paren} (h : IsBalanced l) : balance l = 0 :=
224+
(isBalanced_iff.1 h).1
225+
226+
def parens (openBracket closeBracket : Char) (s : String) : List Paren :=
227+
s.toList.filterMap (fun c => if c = openBracket then some .open else if c = closeBracket then some .close else none)
228+
229+
@[simp]
230+
theorem parens_empty {o c : Char} : parens o c "" = [] := by
231+
simp [parens]
232+
233+
@[simp]
234+
theorem parens_append {o c : Char} {s t : String} :
235+
parens o c (s ++ t) = parens o c s ++ parens o c t := by
236+
simp [parens]
237+
238+
@[simp]
239+
theorem parens_push {o c : Char} {s : String} {t : Char} :
240+
parens o c (s.push t) =
241+
parens o c s ++ (if t = o then some Paren.open else if t = c then some Paren.close else none).toList := by
242+
simp only [parens, String.toList_push, List.filterMap_append, List.filterMap_cons,
243+
List.filterMap_nil, List.append_cancel_left_eq]
244+
grind
245+
246+
def isBalanced (openBracket closeBracket : Char) (s : String) : Bool := Id.run do
247+
let mut depth := 0
248+
for c in s do
249+
if c == openBracket then
250+
depth := depth + 1
251+
else if c == closeBracket then
252+
if depth == 0 then
253+
return false
254+
depth := depth - 1
255+
return depth == 0
256+
257+
open Std.Do
258+
259+
set_option mvcgen.warning false
260+
theorem isBalanced_eq_true_iff {openBracket closeBracket : Char} {s : String} (h : openBracket ≠ closeBracket) :
261+
isBalanced openBracket closeBracket s = true ↔ IsBalanced (parens openBracket closeBracket s) := by
262+
generalize hwp : isBalanced openBracket closeBracket s = w
263+
apply Std.Do.Id.of_wp_run_eq hwp
264+
mvcgen
265+
case inv =>
266+
exact Std.Do.StringInvariant.withEarlyReturn
267+
(fun pos depth => ⌜∀ t₁ t₂, pos.Splits t₁ t₂ → minBalance (parens openBracket closeBracket t₁) = 0 ∧ depth = balance (parens openBracket closeBracket t₁)⌝)
268+
(fun res depth => ⌜res = false ∧ ¬ IsBalanced (parens openBracket closeBracket s)⌝)
269+
next pos _ hp depth h₁ ih =>
270+
simp only [SPred.and_nil, SPred.down_pure, SPred.exists_nil, Bool.exists_bool, true_and,
271+
Bool.true_eq_false, false_and, and_false, or_false, SPred.or_nil] at ih
272+
have := ih.resolve_right (by simp [hp])
273+
simp only [Int.natCast_add, Int.cast_ofNat_Int, SPred.and_nil, SPred.down_pure, true_and,
274+
reduceCtorEq, false_and, SPred.exists_nil, exists_const, SPred.or_nil, or_false]
275+
intro t₁ t₂ hsp
276+
obtain ⟨t₁, rfl⟩ := hsp.exists_eq_append_singleton
277+
simp only [beq_iff_eq] at h₁
278+
simp only [h₁, String.append_singleton, parens_push, ↓reduceIte, Option.toList_some,
279+
minBalance_append, minBalance_cons, Paren.toInt_open, minBalance_nil, Int.add_zero,
280+
balance_append, balance_cons, balance_nil, Int.add_left_inj]
281+
have := this.2 _ _ hsp.of_next
282+
grind
283+
next pos _ hp depth h₁ h₂ h₃ ih =>
284+
simp only [SPred.and_nil, SPred.down_pure, SPred.exists_nil, Bool.exists_bool, true_and,
285+
Bool.true_eq_false, false_and, and_false, or_false, SPred.or_nil, reduceCtorEq,
286+
String.splits_endPos_iff, and_imp, forall_eq_apply_imp_iff, forall_eq, Option.some.injEq,
287+
Bool.false_eq, and_self_left, exists_eq_left, false_or] at ⊢ ih
288+
have := ih.resolve_right (by simp [hp])
289+
obtain ⟨t₁, t₂, hsp⟩ : ∃ t₁ t₂, (pos.next hp).Splits t₁ t₂ := ⟨_, _, (pos.next hp).splits⟩
290+
obtain ⟨t₁, rfl⟩ := hsp.exists_eq_append_singleton
291+
rw [hsp.eq_append, parens_append]
292+
apply not_isBalanced_append_of_balance_neg
293+
simp only [beq_iff_eq] at h₂ h₃
294+
simp only [h₂, String.append_singleton, parens_push, Ne.symm h, ↓reduceIte, Option.toList_some,
295+
balance_append, balance_cons, Paren.toInt_close, Int.reduceNeg, balance_nil, Int.add_zero]
296+
have := this.2 _ _ hsp.of_next
297+
grind
298+
next pos _ hp depth h₁ h₂ h₃ ih =>
299+
simp only [SPred.and_nil, SPred.down_pure, SPred.exists_nil, Bool.exists_bool, true_and,
300+
Bool.true_eq_false, false_and, and_false, or_false, SPred.or_nil, reduceCtorEq,
301+
exists_const] at ⊢ ih
302+
have := ih.resolve_right (by simp [hp])
303+
intro t₁ t₂ hsp
304+
obtain ⟨t₁, rfl⟩ := hsp.exists_eq_append_singleton
305+
simp only [beq_iff_eq] at h₂ h₃
306+
simp only [h₂, String.append_singleton, parens_push, Ne.symm h, ↓reduceIte, Option.toList_some,
307+
minBalance_append, minBalance_cons, Paren.toInt_close, Int.reduceNeg, minBalance_nil,
308+
Int.add_zero, balance_append, balance_cons, balance_nil]
309+
have := this.2 _ _ hsp.of_next
310+
grind
311+
next pos _ hp depth h₁ h₂ ih =>
312+
simp only [SPred.and_nil, SPred.down_pure, SPred.exists_nil, Bool.exists_bool, true_and,
313+
Bool.true_eq_false, false_and, and_false, or_false, SPred.or_nil, reduceCtorEq,
314+
exists_const] at ⊢ ih
315+
have := ih.resolve_right (by simp [hp])
316+
intro t₁ t₂ hsp
317+
obtain ⟨t₁, rfl⟩ := hsp.exists_eq_append_singleton
318+
simp only [beq_iff_eq] at h₁ h₂
319+
simpa [h₁, h₂] using this.2 _ _ hsp.of_next
320+
next => simp
321+
next p h ih =>
322+
simp only [h, String.splits_endPos_iff, and_imp, forall_eq_apply_imp_iff, forall_eq,
323+
SPred.and_nil, SPred.down_pure, true_and, reduceCtorEq, false_and, SPred.exists_nil,
324+
exists_const, SPred.or_nil, or_false, beq_iff_eq] at ⊢ ih
325+
rw [isBalanced_iff]
326+
grind
327+
next p b h ih => simp_all

0 commit comments

Comments
 (0)