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+ -/
0 commit comments