|
1 | | -def Int.toDigits (i : Int) : List Char := |
2 | | - i.natAbs.toDigits (base := 10) |
3 | | - |
4 | | -def evenOddCount (num : Int) : Nat × Nat := |
5 | | - num.toDigits.foldl countDigit (0, 0) |
6 | | -where |
7 | | - countDigit (evenOdd : Nat × Nat) : Char → Nat × Nat |
8 | | - | '0' | '2' | '4' | '6' | '8' => (evenOdd.fst + 1, evenOdd.snd) |
9 | | - | _ => (evenOdd.fst, evenOdd.snd + 1) |
10 | | - |
11 | | -example : evenOddCount (-12) = (1, 1) := rfl |
12 | | -example : evenOddCount 123 = (1, 2) := rfl |
13 | | -example : evenOddCount 7 = (0, 1) := rfl |
14 | | -example : evenOddCount (-78) = (1, 1) := rfl |
15 | | -example : evenOddCount 3452 = (2, 2) := rfl |
16 | | -example : evenOddCount 346211 = (3, 3) := rfl |
17 | | -example : evenOddCount (-345821) = (3, 3) := rfl |
18 | | -example : evenOddCount (-2) = (1, 0) := rfl |
19 | | -example : evenOddCount (-45347) = (2, 3) := rfl |
20 | | -example : evenOddCount 0 = (1, 0) := rfl |
21 | | - |
22 | | -def Prod.sum : (Nat × Nat) → Nat |
23 | | - | (n₁, n₂) => n₁ + n₂ |
24 | | - |
25 | | -instance : Add (Nat × Nat) where |
26 | | - add | (l₁, r₁), (l₂, r₂) => (l₁ + l₂, r₁ + r₂) |
27 | | - |
28 | | -instance : Sub (Nat × Nat) where |
29 | | - sub | (l₁, r₁), (l₂, r₂) => (l₁ - l₂, r₁ - r₂) |
30 | | - |
31 | | --- Applying `evenOddCount.countDigit` increases the total digit count by `1`. |
32 | | -theorem evenOddCount.countDigit_sum (evenOdd : Nat × Nat) (d : Char) : |
33 | | - (evenOddCount.countDigit evenOdd d).sum = evenOdd.sum + 1 := by |
34 | | - unfold evenOddCount.countDigit |
35 | | - split <;> simp +arith [Prod.sum] |
36 | | - |
37 | | --- Folding `evenOddCount.countDigit` over a given digit count `init` produces the same total digit |
38 | | --- count as folding `evenOddCount.countDigit` over `(0, 0)` and adding that to `init`. |
39 | | -theorem evenOddCount.countDigit_sum_foldl (ds : List Char) (init : Nat × Nat) : |
40 | | - (ds.foldl evenOddCount.countDigit init).sum = |
41 | | - (ds.foldl evenOddCount.countDigit (0, 0)).sum + init.sum := by |
42 | | - induction ds generalizing init |
43 | | - case nil => simp [Prod.sum] |
44 | | - case cons ih => simp +arith only [List.foldl_cons, ih (countDigit _ _), countDigit_sum]; rfl |
| 1 | +-- https://github.com/leanprover-community/batteries/pull/1267 |
| 2 | +theorem Nat.isDigit_digitChar_iff_lt : n.digitChar.isDigit ↔ (n < 10) := sorry |
| 3 | +theorem Nat.mem_toDigits_base_10_isDigit (h : c ∈ toDigits 10 n) : c.isDigit := sorry |
| 4 | + |
| 5 | +abbrev Digit := { c : Char // c.isDigit } |
| 6 | + |
| 7 | +def Int.digits (i : Int) : List Digit := |
| 8 | + i.natAbs |
| 9 | + |>.toDigits (base := 10) |
| 10 | + |>.attach |
| 11 | + |>.map fun ⟨j, h⟩ => ⟨j, Nat.mem_toDigits_base_10_isDigit h⟩ |
| 12 | + |
| 13 | +structure Tally where |
| 14 | + even : Nat |
| 15 | + odd : Nat |
| 16 | +deriving Inhabited |
| 17 | + |
| 18 | +namespace Tally |
45 | 19 |
|
46 | | --- The total digit count produced by `evenOddCount` matches the number of digits in the input. |
47 | | -theorem evenOddCount_sum_eq_length : (evenOddCount i).sum = i.toDigits.length := by |
48 | | - rw [evenOddCount] |
49 | | - generalize i.toDigits = ds |
| 20 | +instance : Add Tally where |
| 21 | + add t₁ t₂ := { even := t₁.even + t₂.even, odd := t₁.odd + t₂.odd } |
| 22 | + |
| 23 | +def total (t : Tally) : Nat := |
| 24 | + t.even + t.odd |
| 25 | + |
| 26 | +def log (t : Tally) (d : Digit) : Tally := |
| 27 | + match d.val with |
| 28 | + | '0' | '2' | '4' | '6' | '8' => { t with even := t.even + 1 } |
| 29 | + | _ => { t with odd := t.odd + 1 } |
| 30 | + |
| 31 | +-- Applying `log` increases a tally's total by `1`. |
| 32 | +theorem log_total (d : Digit) (t : Tally) : (t.log d).total = t.total + 1 := by |
| 33 | + rw [log] |
| 34 | + split <;> simp +arith [total] |
| 35 | + |
| 36 | +-- Folding `log` over a given tally `init` produces the same total digit count as folding `log` over |
| 37 | +-- `⟨0, 0⟩` and adding that to `init`. |
| 38 | +theorem log_total_foldl {ds : List Digit} (init : Tally) : |
| 39 | + (ds.foldl log init).total = (ds.foldl log ⟨0, 0⟩).total + init.total := by |
| 40 | + induction ds generalizing init |
| 41 | + case' cons hd _ ih => simp +arith only [List.foldl_cons, ih (log _ hd), log_total] |
| 42 | + all_goals simp [total] |
| 43 | + |
| 44 | +def count (i : Int) : Tally := |
| 45 | + i.digits.foldl log ⟨0, 0⟩ |
| 46 | + |
| 47 | +example : count (-12) = ⟨1, 1⟩ := rfl |
| 48 | +example : count 123 = ⟨1, 2⟩ := rfl |
| 49 | +example : count 7 = ⟨0, 1⟩ := rfl |
| 50 | +example : count (-78) = ⟨1, 1⟩ := rfl |
| 51 | +example : count 3452 = ⟨2, 2⟩ := rfl |
| 52 | +example : count 346211 = ⟨3, 3⟩ := rfl |
| 53 | +example : count (-345821) = ⟨3, 3⟩ := rfl |
| 54 | +example : count (-2) = ⟨1, 0⟩ := rfl |
| 55 | +example : count (-45347) = ⟨2, 3⟩ := rfl |
| 56 | +example : count 0 = ⟨1, 0⟩ := rfl |
| 57 | + |
| 58 | +-- The tally total produced by `count` matches the number of digits in the input. |
| 59 | +theorem count_total_eq_length : (count i).total = i.digits.length := by |
| 60 | + rw [count] |
| 61 | + generalize i.digits = ds |
50 | 62 | induction ds |
51 | | - case nil => rfl |
52 | | - case cons ih => rw [List.foldl_cons, List.length_cons, evenOddCount.countDigit_sum_foldl, ih, |
53 | | - evenOddCount.countDigit_sum, Prod.sum] |
| 63 | + case nil => rfl |
| 64 | + case cons ih => rw [List.foldl_cons, List.length_cons, log_total_foldl, ih, log_total]; rfl |
54 | 65 |
|
55 | | -theorem evenOddCount_decompose : |
56 | | - evenOddCount (10 * n + d) = evenOddCount n + evenOddCount (d % 10) - (1, 0) := |
| 66 | +theorem count_decompose {d : Nat} (hd : d < 10) : |
| 67 | + count (10 * n + d) = count n + ⟨d % 2, 1 - d % 2⟩ := by |
57 | 68 | sorry |
58 | 69 |
|
59 | 70 | /-! |
|
0 commit comments