Skip to content

Commit bc25f98

Browse files
marcusrosselTwoFX
andauthored
HumanEval155 (#165)
* Add (partial) solution to HumanEval5 * Add solution to HumanEval155 * Use `List.intersperse` * Simplify proof * Add proof for odd case * Gardening * Turn < into = * Rename theorem * Use Lean's theorems * Define decomposition theorem * Update to Lean v4.21.0-rc2 * Overhaul definitions * Reduce `count_decompose` to "simple" lemmas * Decouple "`Int`-ness" * Remove redundant lemma * Prove `digits_of_lt_10` * Remove redundant lemma * Prove more lemmas * Update batteries theorems * Remove redundant lemma * Fix incorrect lemmas * Remove redundant lemma * Complete all proofs * Sorry-free version * Undo accidental change --------- Co-authored-by: Markus Himmel <markus@himmel-villmar.de>
1 parent 088ff8e commit bc25f98

File tree

2 files changed

+212
-4
lines changed

2 files changed

+212
-4
lines changed

HumanEvalLean/HumanEval155.lean

Lines changed: 211 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,213 @@
1-
def even_odd_count : Unit :=
2-
()
1+
section Batteries -- https://github.com/leanprover-community/batteries/pull/1267
2+
3+
namespace Nat
4+
5+
@[simp]
6+
theorem isDigit_digitChar : n.digitChar.isDigit = decide (n < 10) :=
7+
match n with
8+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 => by simp [digitChar]
9+
| _ + 10 => by
10+
simp only [digitChar, ↓reduceIte, Nat.reduceEqDiff]
11+
(repeat' split) <;> simp
12+
13+
private theorem isDigit_of_mem_toDigitsCore
14+
(hc : c ∈ cs → c.isDigit) (hb₁ : 0 < b) (hb₂ : b ≤ 10) (h : c ∈ toDigitsCore b fuel n cs) :
15+
c.isDigit := by
16+
induction fuel generalizing n cs with rw [toDigitsCore] at h
17+
| zero => exact hc h
18+
| succ _ ih =>
19+
split at h
20+
case' isFalse => apply ih (fun h => ?_) h
21+
all_goals
22+
cases h with
23+
| head => simp [Nat.lt_of_lt_of_le (mod_lt _ hb₁) hb₂]
24+
| tail _ hm => exact hc hm
25+
26+
theorem isDigit_of_mem_toDigits (hb₁ : 0 < b) (hb₂ : b ≤ 10) (hc : c ∈ toDigits b n) : c.isDigit :=
27+
isDigit_of_mem_toDigitsCore (fun _ => by contradiction) hb₁ hb₂ hc
28+
29+
private theorem toDigitsCore_of_lt_base (hb : n < b) (hf : n < fuel) :
30+
toDigitsCore b fuel n cs = n.digitChar :: cs := by
31+
unfold toDigitsCore
32+
split <;> simp_all [mod_eq_of_lt]
33+
34+
theorem toDigits_of_lt_base (h : n < b) : toDigits b n = [n.digitChar] :=
35+
toDigitsCore_of_lt_base h (lt_succ_self _)
36+
37+
theorem toDigits_zero : (b : Nat) → toDigits b 0 = ['0']
38+
| 0 => rfl
39+
| _ + 1 => toDigits_of_lt_base (zero_lt_succ _)
40+
41+
private theorem toDigitsCore_append :
42+
toDigitsCore b fuel n cs₁ ++ cs₂ = toDigitsCore b fuel n (cs₁ ++ cs₂) := by
43+
induction fuel generalizing n cs₁ with simp only [toDigitsCore]
44+
| succ => split <;> simp_all
45+
46+
private theorem toDigitsCore_eq_of_lt_fuel (hb : 1 < b) (h₁ : n < fuel₁) (h₂ : n < fuel₂) :
47+
toDigitsCore b fuel₁ n cs = toDigitsCore b fuel₂ n cs := by
48+
cases fuel₁ <;> cases fuel₂ <;> try contradiction
49+
simp only [toDigitsCore, Nat.div_eq_zero_iff]
50+
split
51+
· simp
52+
· have := Nat.div_lt_self (by omega : 0 < n) hb
53+
exact toDigitsCore_eq_of_lt_fuel hb (by omega) (by omega)
54+
55+
private theorem toDigitsCore_toDigitsCore
56+
(hb : 1 < b) (hn : 0 < n) (hd : d < b) (hf : b * n + d < fuel) (hnf : n < nf) (hdf : d < df) :
57+
toDigitsCore b nf n (toDigitsCore b df d cs) = toDigitsCore b fuel (b * n + d) cs := by
58+
cases fuel with
59+
| zero => contradiction
60+
| succ fuel =>
61+
rw [toDigitsCore]
62+
split
63+
case isTrue h =>
64+
have : b ≤ b * n + d := Nat.le_trans (Nat.le_mul_of_pos_right _ hn) (le_add_right _ _)
65+
cases Nat.div_eq_zero_iff.mp h <;> omega
66+
case isFalse =>
67+
have h : (b * n + d) / b = n := by
68+
rw [mul_add_div (by omega), Nat.div_eq_zero_iff.mpr (.inr hd), Nat.add_zero]
69+
have := (Nat.lt_mul_iff_one_lt_left hn).mpr hb
70+
simp only [toDigitsCore_of_lt_base hd hdf, mul_add_mod_self_left, mod_eq_of_lt hd, h]
71+
apply toDigitsCore_eq_of_lt_fuel hb hnf (by omega)
72+
73+
theorem toDigits_append_toDigits (hb : 1 < b) (hn : 0 < n) (hd : d < b) :
74+
(toDigits b n) ++ (toDigits b d) = toDigits b (b * n + d) := by
75+
rw [toDigits, toDigitsCore_append]
76+
exact toDigitsCore_toDigitsCore hb hn hd (lt_succ_self _) (lt_succ_self _) (lt_succ_self _)
77+
78+
end Nat
79+
80+
end Batteries
81+
82+
abbrev Digit := { c : Char // c.isDigit }
83+
84+
namespace Nat
85+
86+
def digits (n : Nat) : List Digit :=
87+
n.toDigits (base := 10)
88+
|>.attachWith (·.isDigit) fun _ h => Nat.isDigit_of_mem_toDigits (by decide) (by decide) h
89+
90+
theorem digits_of_lt_10 (h : n < 10) : n.digits = [⟨n.digitChar, by simp_all⟩] := by
91+
simp [digits, toDigits_of_lt_base h]
92+
93+
theorem digits_append_digits (hn : 0 < n) (hd : d < 10) :
94+
n.digits ++ d.digits = (10 * n + d).digits := by
95+
simp [digits, ← toDigits_append_toDigits, *]
96+
97+
end Nat
98+
99+
structure Tally where
100+
even : Nat
101+
odd : Nat
102+
deriving Inhabited
103+
104+
namespace Tally
105+
106+
instance : Add Tally where
107+
add t₁ t₂ := { even := t₁.even + t₂.even, odd := t₁.odd + t₂.odd }
108+
109+
@[simp]
110+
theorem empty_add (t : Tally) : ⟨0, 0⟩ + t = t := by
111+
simp only [(· + ·), Add.add]
112+
simp
113+
114+
@[simp]
115+
theorem add_even (t₁ t₂ : Tally) : (t₁ + t₂).even = t₁.even + t₂.even := by
116+
simp only [(· + ·), Add.add]
117+
118+
@[simp]
119+
theorem add_odd (t₁ t₂ : Tally) : (t₁ + t₂).odd = t₁.odd + t₂.odd := by
120+
simp only [(· + ·), Add.add]
121+
122+
theorem add_comm (t₁ t₂ : Tally) : t₁ + t₂ = t₂ + t₁ := by
123+
simp only [(· + ·), Add.add]
124+
simp +arith
125+
126+
theorem add_assoc (t₁ t₂ : Tally) : t₁ + t₂ + t₃ = t₁ + (t₂ + t₃) := by
127+
simp only [(· + ·), Add.add]
128+
simp +arith
129+
130+
def total (t : Tally) : Nat :=
131+
t.even + t.odd
132+
133+
@[simp]
134+
theorem total_add (t₁ t₂ : Tally) : (t₁ + t₂).total = t₁.total + t₂.total := by
135+
simp +arith [total]
136+
137+
def log (t : Tally) (d : Digit) : Tally :=
138+
match d.val with
139+
| '0' | '2' | '4' | '6' | '8' => { t with even := t.even + 1 }
140+
| _ => { t with odd := t.odd + 1 }
141+
142+
theorem log_add (t₁ t₂ : Tally) (d : Digit) : (t₁.log d) + t₂ = (t₁ + t₂).log d := by
143+
simp only [log]
144+
split
145+
all_goals
146+
simp only [(· + ·), Add.add]
147+
simp +arith
148+
149+
theorem log_digitChar (h : d < 10) (t : Tally) :
150+
t.log ⟨d.digitChar, by simp_all⟩ = t + ⟨1 - d % 2, d % 2⟩ := by
151+
match d with
152+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 => rfl
153+
| _ + 10 => contradiction
154+
155+
-- Folding `log` over a given tally `init` produces the same tally as folding `log` over `⟨0, 0⟩`
156+
-- and adding that to `init`.
157+
theorem log_foldl {ds : List Digit} (init : Tally) :
158+
(ds.foldl log init) = (ds.foldl log ⟨0, 0⟩) + init := by
159+
induction ds generalizing init
160+
case nil => simp
161+
case cons hd _ ih => simp +arith [List.foldl_cons, ih (log _ hd), add_assoc, log_add]
162+
163+
-- Folding `log` over a given tally `init` produces the same total digit count as folding `log` over
164+
-- `⟨0, 0⟩` and adding that to `init`.
165+
theorem log_total_foldl {ds : List Digit} (init : Tally) :
166+
(ds.foldl log init).total = (ds.foldl log ⟨0, 0⟩).total + init.total := by
167+
rw [log_foldl]
168+
simp
169+
170+
-- Applying `log` increases a tally's total by `1`.
171+
theorem log_total (d : Digit) (t : Tally) : (t.log d).total = t.total + 1 := by
172+
rw [log]
173+
split <;> simp +arith [total]
174+
175+
def count (n : Nat) : Tally :=
176+
n.digits.foldl log ⟨0, 0
177+
178+
-- The tally total produced by `count` matches the number of digits in the input.
179+
theorem count_total_eq_length : (count n).total = n.digits.length := by
180+
rw [count]
181+
generalize n.digits = ds
182+
induction ds
183+
case nil => rfl
184+
case cons ih => rw [List.foldl_cons, List.length_cons, log_total_foldl, ih, log_total]; rfl
185+
186+
theorem count_of_lt_10 (hd : d < 10) : count d = ⟨1 - d % 2, d % 2⟩ := by
187+
simp [count, Nat.digits_of_lt_10, log_digitChar, hd]
188+
189+
theorem count_add (hn : 0 < n) (hd : d < 10) : count n + count d = count (10 * n + d) := by
190+
simp only [count, ← Nat.digits_append_digits hn hd, List.foldl_append]
191+
conv => rhs; rw [log_foldl]
192+
apply add_comm
193+
194+
theorem count_decompose (hn : 0 < n) (hd : d < 10) :
195+
count (10 * n + d) = count n + ⟨1 - d % 2, d % 2⟩ := by
196+
rw [← count_of_lt_10 hd, count_add hn hd]
197+
198+
def evenOddCount (i : Int) : Tally :=
199+
count i.natAbs
200+
201+
example : evenOddCount (-12) = ⟨1, 1⟩ := rfl
202+
example : evenOddCount 123 = ⟨1, 2⟩ := rfl
203+
example : evenOddCount 7 = ⟨0, 1⟩ := rfl
204+
example : evenOddCount (-78) = ⟨1, 1⟩ := rfl
205+
example : evenOddCount 3452 = ⟨2, 2⟩ := rfl
206+
example : evenOddCount 346211 = ⟨3, 3⟩ := rfl
207+
example : evenOddCount (-345821) = ⟨3, 3⟩ := rfl
208+
example : evenOddCount (-2) = ⟨1, 0⟩ := rfl
209+
example : evenOddCount (-45347) = ⟨2, 3⟩ := rfl
210+
example : evenOddCount 0 = ⟨1, 0⟩ := rfl
3211

4212
/-!
5213
## Prompt
@@ -48,4 +256,4 @@ def check(candidate):
48256
assert True
49257
50258
```
51-
-/
259+
-/

HumanEvalLean/HumanEval5.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,4 @@ def check(candidate):
5050
assert candidate([5, 6, 3, 2], 8) == [5, 8, 6, 8, 3, 8, 2]
5151
assert candidate([2, 2, 2], 2) == [2, 2, 2, 2, 2]
5252
```
53-
-/
53+
-/

0 commit comments

Comments
 (0)