|
| 1 | +/- |
| 2 | +Copyright (c) 2022 Eric Wieser. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Eric Wieser |
| 5 | +-/ |
| 6 | +import data.nat.log |
| 7 | +import algebra.order.floor |
| 8 | +import algebra.field_power |
| 9 | + |
| 10 | +/-! |
| 11 | +# Integer logarithms in a field with respect to a natural base |
| 12 | +
|
| 13 | +This file defines two `ℤ`-valued analogs of the logarithm of `r : R` with base `b : ℕ`: |
| 14 | +
|
| 15 | +* `int.log b r`: Lower logarithm, or floor **log**. Greatest `k` such that `↑b^k ≤ r`. |
| 16 | +* `int.clog b r`: Upper logarithm, or **c**eil **log**. Least `k` such that `r ≤ ↑b^k`. |
| 17 | +
|
| 18 | +Note that `int.log` gives the position of the left-most non-zero digit: |
| 19 | +```lean |
| 20 | +#eval (int.log 10 (0.09 : ℚ), int.log 10 (0.10 : ℚ), int.log 10 (0.11 : ℚ)) |
| 21 | +-- (-2, -1, -1) |
| 22 | +#eval (int.log 10 (9 : ℚ), int.log 10 (10 : ℚ), int.log 10 (11 : ℚ)) |
| 23 | +-- (0, 1, 1) |
| 24 | +``` |
| 25 | +which means it can be used for computing digit expansions |
| 26 | +```lean |
| 27 | +import data.fin.vec_notation |
| 28 | +
|
| 29 | +def digits (b : ℕ) (q : ℚ) (n : ℕ) : ℕ := |
| 30 | +⌊q*b^(↑n - int.log b q)⌋₊ % b |
| 31 | +
|
| 32 | +#eval digits 10 (1/7) ∘ (coe : fin 8 → ℕ) |
| 33 | +-- ![1, 4, 2, 8, 5, 7, 1, 4] |
| 34 | +``` |
| 35 | +
|
| 36 | +## Main results |
| 37 | +
|
| 38 | +* For `int.log`: |
| 39 | + * `int.zpow_log_le_self`, `int.lt_zpow_succ_log_self`: the bounds formed by `int.log`, |
| 40 | + `(b : R) ^ log b r ≤ r < (b : R) ^ (log b r + 1)`. |
| 41 | + * `int.zpow_log_gi`: the galois coinsertion between `zpow` and `int.log`. |
| 42 | +* For `int.clog`: |
| 43 | + * `int.zpow_pred_clog_lt_self`, `int.self_le_zpow_clog`: the bounds formed by `int.clog`, |
| 44 | + `(b : R) ^ (clog b r - 1) < r ≤ (b : R) ^ clog b r`. |
| 45 | + * `int.clog_zpow_gi`: the galois insertion between `int.clog` and `zpow`. |
| 46 | +* `int.neg_log_inv_eq_clog`, `int.neg_clog_inv_eq_log`: the link between the two definitions. |
| 47 | +
|
| 48 | +-/ |
| 49 | +variables {R : Type*} [linear_ordered_field R] [floor_ring R] |
| 50 | + |
| 51 | +namespace int |
| 52 | + |
| 53 | +/-- The greatest power of `b` such that `b ^ log b r ≤ r`. -/ |
| 54 | +def log (b : ℕ) (r : R) : ℤ := |
| 55 | +if 1 ≤ r then |
| 56 | + nat.log b ⌊r⌋₊ |
| 57 | +else |
| 58 | + -nat.clog b ⌈r⁻¹⌉₊ |
| 59 | + |
| 60 | +lemma log_of_one_le_right (b : ℕ) {r : R} (hr : 1 ≤ r) : log b r = nat.log b ⌊r⌋₊ := |
| 61 | +if_pos hr |
| 62 | + |
| 63 | +lemma log_of_right_le_one (b : ℕ) {r : R} (hr : r ≤ 1) : log b r = -nat.clog b ⌈r⁻¹⌉₊ := |
| 64 | +begin |
| 65 | + obtain rfl | hr := hr.eq_or_lt, |
| 66 | + { rw [log, if_pos hr, inv_one, nat.ceil_one, nat.floor_one, nat.log_one_right, nat.clog_one_right, |
| 67 | + int.coe_nat_zero, neg_zero], }, |
| 68 | + { exact if_neg hr.not_le } |
| 69 | +end |
| 70 | + |
| 71 | +@[simp, norm_cast] lemma log_nat_cast (b : ℕ) (n : ℕ) : log b (n : R) = nat.log b n := |
| 72 | +begin |
| 73 | + cases n, |
| 74 | + { simp [log_of_right_le_one _ _, nat.log_zero_right] }, |
| 75 | + { have : 1 ≤ (n.succ : R) := by simp, |
| 76 | + simp [log_of_one_le_right _ this, ←nat.cast_succ] } |
| 77 | +end |
| 78 | + |
| 79 | +lemma log_of_left_le_one {b : ℕ} (hb : b ≤ 1) (r : R) : log b r = 0 := |
| 80 | +begin |
| 81 | + cases le_total 1 r, |
| 82 | + { rw [log_of_one_le_right _ h, nat.log_of_left_le_one hb, int.coe_nat_zero] }, |
| 83 | + { rw [log_of_right_le_one _ h, nat.clog_of_left_le_one hb, int.coe_nat_zero, neg_zero] }, |
| 84 | +end |
| 85 | + |
| 86 | +lemma log_of_right_le_zero (b : ℕ) {r : R} (hr : r ≤ 0) : log b r = 0 := |
| 87 | +by rw [log_of_right_le_one _ (hr.trans zero_le_one), |
| 88 | + nat.clog_of_right_le_one ((nat.ceil_eq_zero.mpr $ inv_nonpos.2 hr).trans_le zero_le_one), |
| 89 | + int.coe_nat_zero, neg_zero] |
| 90 | + |
| 91 | +lemma zpow_log_le_self {b : ℕ} {r : R} (hb : 1 < b) (hr : 0 < r) : |
| 92 | + (b : R) ^ log b r ≤ r := |
| 93 | +begin |
| 94 | + cases le_total 1 r with hr1 hr1, |
| 95 | + { rw log_of_one_le_right _ hr1, |
| 96 | + refine le_trans _ (nat.floor_le hr.le), |
| 97 | + rw [zpow_coe_nat, ←nat.cast_pow, nat.cast_le], |
| 98 | + exact nat.pow_log_le_self hb (nat.floor_pos.mpr hr1) }, |
| 99 | + { rw [log_of_right_le_one _ hr1, zpow_neg, zpow_coe_nat, ← nat.cast_pow], |
| 100 | + apply inv_le_of_inv_le hr, |
| 101 | + refine (nat.le_ceil _).trans (nat.cast_le.2 _), |
| 102 | + exact nat.le_pow_clog hb _ }, |
| 103 | +end |
| 104 | + |
| 105 | +lemma lt_zpow_succ_log_self {b : ℕ} (hb : 1 < b) (r : R) : |
| 106 | + r < (b : R) ^ (log b r + 1) := |
| 107 | +begin |
| 108 | + cases le_or_lt r 0 with hr hr, |
| 109 | + { rw [log_of_right_le_zero _ hr, zero_add, zpow_one], |
| 110 | + exact hr.trans_lt (zero_lt_one.trans_le $ by exact_mod_cast hb.le) }, |
| 111 | + cases le_or_lt 1 r with hr1 hr1, |
| 112 | + { rw log_of_one_le_right _ hr1, |
| 113 | + rw [int.coe_nat_add_one_out, zpow_coe_nat, ←nat.cast_pow], |
| 114 | + apply nat.lt_of_floor_lt, |
| 115 | + exact nat.lt_pow_succ_log_self hb _, }, |
| 116 | + { rw log_of_right_le_one _ hr1.le, |
| 117 | + have hcri : 1 < r⁻¹ := one_lt_inv hr hr1, |
| 118 | + have : 1 ≤ nat.clog b ⌈r⁻¹⌉₊ := |
| 119 | + nat.succ_le_of_lt (nat.clog_pos hb $ nat.one_lt_cast.1 $ hcri.trans_le (nat.le_ceil _)), |
| 120 | + rw [neg_add_eq_sub, ←neg_sub, ←int.coe_nat_one, ← int.coe_nat_sub this, |
| 121 | + zpow_neg, zpow_coe_nat, lt_inv hr (pow_pos (nat.cast_pos.mpr $ zero_lt_one.trans hb) _), |
| 122 | + ←nat.cast_pow], |
| 123 | + refine nat.lt_ceil.1 _, |
| 124 | + exact (nat.pow_pred_clog_lt_self hb $ nat.one_lt_cast.1 $ hcri.trans_le $ nat.le_ceil _), } |
| 125 | +end |
| 126 | + |
| 127 | +@[simp] lemma log_zero_right (b : ℕ) : log b (0 : R) = 0 := |
| 128 | +log_of_right_le_zero b le_rfl |
| 129 | + |
| 130 | +@[simp] lemma log_one_right (b : ℕ) : log b (1 : R) = 0 := |
| 131 | +by rw [log_of_one_le_right _ le_rfl, nat.floor_one, nat.log_one_right, int.coe_nat_zero] |
| 132 | + |
| 133 | +lemma log_zpow {b : ℕ} (hb : 1 < b) (z : ℤ) : log b (b ^ z : R) = z := |
| 134 | +begin |
| 135 | + obtain ⟨n, rfl | rfl⟩ := z.eq_coe_or_neg, |
| 136 | + { rw [log_of_one_le_right _ (one_le_zpow_of_nonneg _ $ int.coe_nat_nonneg _), |
| 137 | + zpow_coe_nat, ←nat.cast_pow, nat.floor_coe, nat.log_pow hb], |
| 138 | + exact_mod_cast hb.le, }, |
| 139 | + { rw [log_of_right_le_one _ (zpow_le_one_of_nonpos _ $ neg_nonpos.mpr (int.coe_nat_nonneg _)), |
| 140 | + zpow_neg, inv_inv, zpow_coe_nat, ←nat.cast_pow, nat.ceil_coe, nat.clog_pow _ _ hb], |
| 141 | + exact_mod_cast hb.le, }, |
| 142 | +end |
| 143 | + |
| 144 | +@[mono] lemma log_mono_right {b : ℕ} {r₁ r₂ : R} (h₀ : 0 < r₁) (h : r₁ ≤ r₂) : |
| 145 | + log b r₁ ≤ log b r₂ := |
| 146 | +begin |
| 147 | + cases le_or_lt b 1 with hb hb, |
| 148 | + { rw [log_of_left_le_one hb, log_of_left_le_one hb], }, |
| 149 | + cases le_total r₁ 1 with h₁ h₁; cases le_total r₂ 1 with h₂ h₂, |
| 150 | + { rw [log_of_right_le_one _ h₁, log_of_right_le_one _ h₂, neg_le_neg_iff, int.coe_nat_le], |
| 151 | + exact nat.clog_mono_right _ (nat.ceil_mono $ inv_le_inv_of_le h₀ h), }, |
| 152 | + { rw [log_of_right_le_one _ h₁, log_of_one_le_right _ h₂], |
| 153 | + exact (neg_nonpos.mpr (int.coe_nat_nonneg _)).trans (int.coe_nat_nonneg _) }, |
| 154 | + { obtain rfl := le_antisymm h (h₂.trans h₁), refl, }, |
| 155 | + { rw [log_of_one_le_right _ h₁, log_of_one_le_right _ h₂, int.coe_nat_le], |
| 156 | + exact nat.log_mono_right (nat.floor_mono h), }, |
| 157 | +end |
| 158 | + |
| 159 | +variables (R) |
| 160 | + |
| 161 | +/-- Over suitable subtypes, `zpow` and `int.log` form a galois coinsertion -/ |
| 162 | +def zpow_log_gi {b : ℕ} (hb : 1 < b) : |
| 163 | + galois_coinsertion |
| 164 | + (λ z : ℤ, subtype.mk ((b : R) ^ z) $ zpow_pos_of_pos (by exact_mod_cast zero_lt_one.trans hb) z) |
| 165 | + (λ r : set.Ioi (0 : R), int.log b (r : R)) := |
| 166 | +galois_coinsertion.monotone_intro |
| 167 | + (λ r₁ r₂, log_mono_right r₁.prop) |
| 168 | + (λ z₁ z₂ hz, subtype.coe_le_coe.mp $ (zpow_strict_mono $ by exact_mod_cast hb).monotone hz) |
| 169 | + (λ r, subtype.coe_le_coe.mp $ zpow_log_le_self hb r.prop) |
| 170 | + (λ _, log_zpow hb _) |
| 171 | + |
| 172 | +variables {R} |
| 173 | + |
| 174 | +/-- `zpow b` and `int.log b` (almost) form a Galois connection. -/ |
| 175 | +lemma lt_zpow_iff_log_lt {b : ℕ} (hb : 1 < b) {x : ℤ} {r : R} (hr : 0 < r) : |
| 176 | + r < (b : R) ^ x ↔ log b r < x := |
| 177 | +@galois_connection.lt_iff_lt _ _ _ _ _ _ (zpow_log_gi R hb).gc x ⟨r, hr⟩ |
| 178 | + |
| 179 | +/-- `zpow b` and `int.log b` (almost) form a Galois connection. -/ |
| 180 | +lemma zpow_le_iff_le_log {b : ℕ} (hb : 1 < b) {x : ℤ} {r : R} (hr : 0 < r) : |
| 181 | + (b : R) ^ x ≤ r ↔ x ≤ log b r := |
| 182 | +@galois_connection.le_iff_le _ _ _ _ _ _ (zpow_log_gi R hb).gc x ⟨r, hr⟩ |
| 183 | + |
| 184 | +/-- The least power of `b` such that `r ≤ b ^ log b r`. -/ |
| 185 | +def clog (b : ℕ) (r : R) : ℤ := |
| 186 | +if 1 ≤ r then |
| 187 | + nat.clog b ⌈r⌉₊ |
| 188 | +else |
| 189 | + -nat.log b ⌊r⁻¹⌋₊ |
| 190 | + |
| 191 | +lemma clog_of_one_le_right (b : ℕ) {r : R} (hr : 1 ≤ r) : clog b r = nat.clog b ⌈r⌉₊ := |
| 192 | +if_pos hr |
| 193 | + |
| 194 | +lemma clog_of_right_le_one (b : ℕ) {r : R} (hr : r ≤ 1) : clog b r = -nat.log b ⌊r⁻¹⌋₊ := |
| 195 | +begin |
| 196 | + obtain rfl | hr := hr.eq_or_lt, |
| 197 | + { rw [clog, if_pos hr, inv_one, nat.ceil_one, nat.floor_one, nat.log_one_right, |
| 198 | + nat.clog_one_right, int.coe_nat_zero, neg_zero], }, |
| 199 | + { exact if_neg hr.not_le } |
| 200 | +end |
| 201 | + |
| 202 | +lemma clog_of_right_le_zero (b : ℕ) {r : R} (hr : r ≤ 0) : clog b r = 0 := |
| 203 | +begin |
| 204 | + rw [clog, if_neg (hr.trans_lt zero_lt_one).not_le, neg_eq_zero, int.coe_nat_eq_zero, |
| 205 | + nat.log_eq_zero_iff], |
| 206 | + cases le_or_lt b 1 with hb hb, |
| 207 | + { exact or.inr hb }, |
| 208 | + { refine or.inl (lt_of_le_of_lt _ hb), |
| 209 | + exact nat.floor_le_one_of_le_one ((inv_nonpos.2 hr).trans zero_le_one) }, |
| 210 | +end |
| 211 | + |
| 212 | +@[simp] lemma clog_inv (b : ℕ) (r : R) : clog b r⁻¹ = -log b r := |
| 213 | +begin |
| 214 | + cases lt_or_le 0 r with hrp hrp, |
| 215 | + { obtain hr | hr := le_total 1 r, |
| 216 | + { rw [clog_of_right_le_one _ (inv_le_one hr), log_of_one_le_right _ hr, inv_inv] }, |
| 217 | + { rw [clog_of_one_le_right _ (one_le_inv hrp hr), log_of_right_le_one _ hr, neg_neg] }, }, |
| 218 | + { rw [clog_of_right_le_zero _ (inv_nonpos.mpr hrp), log_of_right_le_zero _ hrp, neg_zero], }, |
| 219 | +end |
| 220 | + |
| 221 | +@[simp] lemma log_inv (b : ℕ) (r : R) : log b r⁻¹ = -clog b r := |
| 222 | +by rw [←inv_inv r, clog_inv, neg_neg, inv_inv] |
| 223 | + |
| 224 | +-- note this is useful for writing in reverse |
| 225 | +lemma neg_log_inv_eq_clog (b : ℕ) (r : R) : -log b r⁻¹ = clog b r := |
| 226 | +by rw [log_inv, neg_neg] |
| 227 | + |
| 228 | +lemma neg_clog_inv_eq_log (b : ℕ) (r : R) : -clog b r⁻¹ = log b r := |
| 229 | +by rw [clog_inv, neg_neg] |
| 230 | + |
| 231 | +@[simp, norm_cast] lemma clog_nat_cast (b : ℕ) (n : ℕ) : clog b (n : R) = nat.clog b n := |
| 232 | +begin |
| 233 | + cases n, |
| 234 | + { simp [clog_of_right_le_one _ _, nat.clog_zero_right] }, |
| 235 | + { have : 1 ≤ (n.succ : R) := by simp, |
| 236 | + simp [clog_of_one_le_right _ this, ←nat.cast_succ] } |
| 237 | +end |
| 238 | + |
| 239 | +lemma clog_of_left_le_one {b : ℕ} (hb : b ≤ 1) (r : R) : clog b r = 0 := |
| 240 | +by rw [←neg_log_inv_eq_clog, log_of_left_le_one hb, neg_zero] |
| 241 | + |
| 242 | +lemma self_le_zpow_clog {b : ℕ} (hb : 1 < b) (r : R) : r ≤ (b : R) ^ clog b r := |
| 243 | +begin |
| 244 | + cases le_or_lt r 0 with hr hr, |
| 245 | + { rw [clog_of_right_le_zero _ hr, zpow_zero], |
| 246 | + exact hr.trans zero_le_one }, |
| 247 | + rw [←neg_log_inv_eq_clog, zpow_neg, le_inv hr (zpow_pos_of_pos _ _)], |
| 248 | + { exact zpow_log_le_self hb (inv_pos.mpr hr), }, |
| 249 | + { exact nat.cast_pos.mpr (zero_le_one.trans_lt hb), }, |
| 250 | +end |
| 251 | + |
| 252 | +lemma zpow_pred_clog_lt_self {b : ℕ} {r : R} (hb : 1 < b) (hr : 0 < r) : |
| 253 | + (b : R) ^ (clog b r - 1) < r := |
| 254 | +begin |
| 255 | + rw [←neg_log_inv_eq_clog, ←neg_add', zpow_neg, inv_lt (zpow_pos_of_pos _ _) hr], |
| 256 | + { exact lt_zpow_succ_log_self hb _, }, |
| 257 | + { exact nat.cast_pos.mpr (zero_le_one.trans_lt hb), }, |
| 258 | +end |
| 259 | + |
| 260 | +@[simp] lemma clog_zero_right (b : ℕ) : clog b (0 : R) = 0 := |
| 261 | +clog_of_right_le_zero _ le_rfl |
| 262 | + |
| 263 | +@[simp] lemma clog_one_right (b : ℕ) : clog b (1 : R) = 0 := |
| 264 | +by rw [clog_of_one_le_right _ le_rfl, nat.ceil_one, nat.clog_one_right, int.coe_nat_zero] |
| 265 | + |
| 266 | +lemma clog_zpow {b : ℕ} (hb : 1 < b) (z : ℤ) : clog b (b ^ z : R) = z := |
| 267 | +by rw [←neg_log_inv_eq_clog, ←zpow_neg, log_zpow hb, neg_neg] |
| 268 | + |
| 269 | +@[mono] lemma clog_mono_right {b : ℕ} {r₁ r₂ : R} (h₀ : 0 < r₁) (h : r₁ ≤ r₂) : |
| 270 | + clog b r₁ ≤ clog b r₂ := |
| 271 | +begin |
| 272 | + rw [←neg_log_inv_eq_clog, ←neg_log_inv_eq_clog, neg_le_neg_iff], |
| 273 | + exact log_mono_right (inv_pos.mpr $ h₀.trans_le h) (inv_le_inv_of_le h₀ h), |
| 274 | +end |
| 275 | + |
| 276 | +variables (R) |
| 277 | +/-- Over suitable subtypes, `int.clog` and `zpow` form a galois insertion -/ |
| 278 | +def clog_zpow_gi {b : ℕ} (hb : 1 < b) : |
| 279 | + galois_insertion |
| 280 | + (λ r : set.Ioi (0 : R), int.clog b (r : R)) |
| 281 | + (λ z : ℤ, ⟨(b : R) ^ z, zpow_pos_of_pos (by exact_mod_cast zero_lt_one.trans hb) z⟩) := |
| 282 | +galois_insertion.monotone_intro |
| 283 | + (λ z₁ z₂ hz, subtype.coe_le_coe.mp $ (zpow_strict_mono $ by exact_mod_cast hb).monotone hz) |
| 284 | + (λ r₁ r₂, clog_mono_right r₁.prop) |
| 285 | + (λ r, subtype.coe_le_coe.mp $ self_le_zpow_clog hb _) |
| 286 | + (λ _, clog_zpow hb _) |
| 287 | +variables {R} |
| 288 | + |
| 289 | +/-- `int.clog b` and `zpow b` (almost) form a Galois connection. -/ |
| 290 | +lemma zpow_lt_iff_lt_clog {b : ℕ} (hb : 1 < b) {x : ℤ} {r : R} (hr : 0 < r) : |
| 291 | + (b : R) ^ x < r ↔ x < clog b r := |
| 292 | +(@galois_connection.lt_iff_lt _ _ _ _ _ _ (clog_zpow_gi R hb).gc ⟨r, hr⟩ x).symm |
| 293 | + |
| 294 | +/-- `int.clog b` and `zpow b` (almost) form a Galois connection. -/ |
| 295 | +lemma le_zpow_iff_clog_le {b : ℕ} (hb : 1 < b) {x : ℤ} {r : R} (hr : 0 < r) : |
| 296 | + r ≤ (b : R) ^ x ↔ clog b r ≤ x := |
| 297 | +(@galois_connection.le_iff_le _ _ _ _ _ _ (clog_zpow_gi R hb).gc ⟨r, hr⟩ x).symm |
| 298 | + |
| 299 | +end int |
0 commit comments