|
| 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