perf: fast version of Nat.log2 (#10046)

This PR replaces the implementation of `Nat.log2` with a version that
reduces faster.
The new version can handle:
```lean-4
example : Nat.log2 (1 <<< 500) = 500 := rfl
```
This commit is contained in:
Rob23oba 2025-08-22 13:32:00 +02:00 committed by GitHub
parent 962ba9649c
commit 9c6b698227
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 6 deletions

View file

@ -1313,13 +1313,13 @@ theorem pow_eq_self_iff {a b : Nat} (ha : 1 < a) : a ^ b = a ↔ b = 1 := by
@[simp]
theorem log2_zero : Nat.log2 0 = 0 := by
simp [Nat.log2]
simp [Nat.log2_def]
theorem le_log2 (h : n ≠ 0) : k ≤ n.log2 ↔ 2 ^ k ≤ n := by
match k with
| 0 => simp [show 1 ≤ n from Nat.pos_of_ne_zero h]
| k+1 =>
rw [log2]; split
rw [log2_def]; split
· have n0 : 0 < n / 2 := (Nat.le_div_iff_mul_le (by decide)).2 _
simp only [Nat.add_le_add_iff_right, le_log2 (Nat.ne_of_gt n0),
Nat.pow_succ]

View file

@ -1,7 +1,7 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner
Authors: Gabriel Ebner, Robin Arnez
-/
module
@ -37,11 +37,38 @@ Examples:
-/
@[extern "lean_nat_log2"]
def log2 (n : @& Nat) : Nat :=
if n ≥ 2 then log2 (n / 2) + 1 else 0
decreasing_by exact log2_terminates _ _
-- Lean "assembly"
n.rec (fun _ => nat_lit 0) (fun _ ih n =>
((nat_lit 2).ble n).rec (nat_lit 0) ((ih (n.div (nat_lit 2))).succ)) n
private theorem log2_rec_irrel {n k k' : Nat} (hk : n ≤ k) (hk' : n ≤ k') :
(k.rec (fun _ => 0) (fun _ ih n => ((2).ble n).rec 0 ((ih (n / 2)).succ)) n : Nat) =
k'.rec (fun _ => 0) (fun _ ih n => ((2).ble n).rec 0 ((ih (n / 2)).succ)) n := by
induction k generalizing n k' with
| zero => cases hk; cases k' <;> rfl
| succ k ih =>
cases k'
· cases hk'; rfl
· dsimp only
cases h : ble 2 n
· rfl
· have hn : n / 2 < n := log2_terminates n (Nat.le_of_ble_eq_true h)
exact congrArg Nat.succ (ih (Nat.le_of_lt_add_one (Nat.lt_of_lt_of_le hn hk))
(Nat.le_of_lt_add_one (Nat.lt_of_lt_of_le hn hk')))
theorem log2_def (n : Nat) : n.log2 = if 2 ≤ n then (n / 2).log2 + 1 else 0 := by
rw [Nat.log2, Nat.log2]
cases n
· rfl
split
· rename_i n h
simp only [ble_eq_true_of_le h]
exact congrArg Nat.succ
(log2_rec_irrel (Nat.le_of_lt_add_one (log2_terminates _ h)) (Nat.le_refl _))
· simp only [mt le_of_ble_eq_true _]
theorem log2_le_self (n : Nat) : Nat.log2 n ≤ n := by
unfold Nat.log2; split
rw [log2_def]; split
next h =>
have := log2_le_self (n / 2)
exact Nat.lt_of_le_of_lt this (Nat.div_lt_self (Nat.le_of_lt h) (by decide))