Skip to content

Commit b771d12

Browse files
georgerenniekim-em
andauthored
feat: Decidable instance for Nat.isPowerOfTwo (#11905)
This PR provides a `Decidable` instance for `Nat.isPowerOfTwo` based on the formula `(n ≠ 0) ∧ (n &&& (n - 1)) = 0`. To do this it includes theorems about `Nat.testBit` to show that the `n.log2`th bit is set in `n` and `n - 1` for non powers of two. Bitwise lemmas are needed to reason about the `&&&` so the file `Init.Data.Nat.Power2` is renamed to `Init.Data.Nat.Power2.Basic` and `Init.Data.Nat.Power2.Lemmas` introduced that depends on `Init.Data.Nat.Bitwise.Lemmas` to prevent circular includes. --------- Co-authored-by: Kim Morrison <477956+kim-em@users.noreply.github.com>
1 parent 214abb7 commit b771d12

File tree

5 files changed

+150
-64
lines changed

5 files changed

+150
-64
lines changed

src/Init/Data/Nat/Bitwise/Lemmas.lean

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,16 @@ theorem testBit_lt_two_pow {x i : Nat} (lt : x < 2^i) : x.testBit i = false := b
223223
exfalso
224224
exact Nat.not_le_of_gt lt (ge_two_pow_of_testBit p)
225225

226+
theorem testBit_of_two_pow_le_and_two_pow_add_one_gt {n i : Nat}
227+
(hle : 2^i ≤ n) (hgt : n < 2^(i + 1)) : n.testBit i = true := by
228+
rcases exists_ge_and_testBit_of_ge_two_pow hle with ⟨i', ⟨_, _⟩⟩
229+
have : i = i' := by
230+
false_or_by_contra
231+
have : 2 ^ (i + 1) ≤ 2 ^ i' := Nat.pow_le_pow_of_le (by decide) (by omega)
232+
have : n.testBit i' = false := testBit_lt_two_pow (by omega)
233+
simp_all only [Bool.false_eq_true]
234+
rwa [this]
235+
226236
theorem lt_pow_two_of_testBit (x : Nat) (p : ∀i, i ≥ n → testBit x i = false) : x < 2^n := by
227237
apply Decidable.by_contra
228238
intro not_lt
@@ -231,6 +241,10 @@ theorem lt_pow_two_of_testBit (x : Nat) (p : ∀i, i ≥ n → testBit x i = fal
231241
have test_false := p _ i_ge_n
232242
simp [test_true] at test_false
233243

244+
theorem testBit_log2 {n : Nat} (h : n ≠ 0) : n.testBit n.log2 = true := by
245+
have := log2_eq_iff (n := n) (k := n.log2) (by omega)
246+
apply testBit_of_two_pow_le_and_two_pow_add_one_gt <;> omega
247+
234248
private theorem succ_mod_two : succ x % 2 = 1 - x % 2 := by
235249
induction x with
236250
| zero =>

src/Init/Data/Nat/Lemmas.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import all Init.Data.Nat.Bitwise.Basic
1010
public import Init.Data.Nat.MinMax
1111
public import Init.Data.Nat.Log2
1212
import all Init.Data.Nat.Log2
13-
public import Init.Data.Nat.Power2
13+
public import Init.Data.Nat.Power2.Basic
1414
public import Init.Data.Nat.Mod
1515
import Init.TacticsExtra
1616
import Init.BinderPredicates

src/Init/Data/Nat/Power2.lean

Lines changed: 2 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,66 +6,5 @@ Authors: Leonardo de Moura
66
module
77

88
prelude
9-
public import Init.Data.Nat.Linear
10-
11-
public section
12-
13-
namespace Nat
14-
15-
theorem nextPowerOfTwo_dec {n power : Nat} (h₁ : power > 0) (h₂ : power < n) : n - power * 2 < n - power := by
16-
have : power * 2 = power + power := by simp +arith
17-
rw [this, Nat.sub_add_eq]
18-
exact Nat.sub_lt (Nat.zero_lt_sub_of_lt h₂) h₁
19-
20-
/--
21-
Returns the least power of two that's greater than or equal to `n`.
22-
23-
Examples:
24-
* `Nat.nextPowerOfTwo 0 = 1`
25-
* `Nat.nextPowerOfTwo 1 = 1`
26-
* `Nat.nextPowerOfTwo 2 = 2`
27-
* `Nat.nextPowerOfTwo 3 = 4`
28-
* `Nat.nextPowerOfTwo 5 = 8`
29-
-/
30-
def nextPowerOfTwo (n : Nat) : Nat :=
31-
go 1 (by decide)
32-
where
33-
go (power : Nat) (h : power > 0) : Nat :=
34-
if power < n then
35-
go (power * 2) (Nat.mul_pos h (by decide))
36-
else
37-
power
38-
termination_by n - power
39-
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
40-
41-
/--
42-
A natural number `n` is a power of two if there exists some `k : Nat` such that `n = 2 ^ k`.
43-
-/
44-
def isPowerOfTwo (n : Nat) := ∃ k, n = 2 ^ k
45-
46-
theorem isPowerOfTwo_one : isPowerOfTwo 1 :=
47-
0, by decide⟩
48-
49-
theorem isPowerOfTwo_mul_two_of_isPowerOfTwo (h : isPowerOfTwo n) : isPowerOfTwo (n * 2) :=
50-
have ⟨k, h⟩ := h
51-
⟨k+1, by simp [h, Nat.pow_succ]⟩
52-
53-
theorem pos_of_isPowerOfTwo (h : isPowerOfTwo n) : n > 0 := by
54-
have ⟨k, h⟩ := h
55-
rw [h]
56-
apply Nat.pow_pos
57-
decide
58-
59-
theorem isPowerOfTwo_nextPowerOfTwo (n : Nat) : n.nextPowerOfTwo.isPowerOfTwo := by
60-
apply isPowerOfTwo_go
61-
apply isPowerOfTwo_one
62-
where
63-
isPowerOfTwo_go (power : Nat) (h₁ : power > 0) (h₂ : power.isPowerOfTwo) : (nextPowerOfTwo.go n power h₁).isPowerOfTwo := by
64-
unfold nextPowerOfTwo.go
65-
split
66-
. exact isPowerOfTwo_go (power*2) (Nat.mul_pos h₁ (by decide)) (Nat.isPowerOfTwo_mul_two_of_isPowerOfTwo h₂)
67-
. assumption
68-
termination_by n - power
69-
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
70-
71-
end Nat
9+
public import Init.Data.Nat.Power2.Basic
10+
public import Init.Data.Nat.Power2.Lemmas
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/-
2+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Leonardo de Moura
5+
-/
6+
module
7+
8+
prelude
9+
public import Init.Data.Nat.Linear
10+
11+
public section
12+
13+
namespace Nat
14+
15+
theorem nextPowerOfTwo_dec {n power : Nat} (h₁ : power > 0) (h₂ : power < n) : n - power * 2 < n - power := by
16+
have : power * 2 = power + power := by simp +arith
17+
rw [this, Nat.sub_add_eq]
18+
exact Nat.sub_lt (Nat.zero_lt_sub_of_lt h₂) h₁
19+
20+
/--
21+
Returns the least power of two that's greater than or equal to `n`.
22+
23+
Examples:
24+
* `Nat.nextPowerOfTwo 0 = 1`
25+
* `Nat.nextPowerOfTwo 1 = 1`
26+
* `Nat.nextPowerOfTwo 2 = 2`
27+
* `Nat.nextPowerOfTwo 3 = 4`
28+
* `Nat.nextPowerOfTwo 5 = 8`
29+
-/
30+
def nextPowerOfTwo (n : Nat) : Nat :=
31+
go 1 (by decide)
32+
where
33+
go (power : Nat) (h : power > 0) : Nat :=
34+
if power < n then
35+
go (power * 2) (Nat.mul_pos h (by decide))
36+
else
37+
power
38+
termination_by n - power
39+
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
40+
41+
/--
42+
A natural number `n` is a power of two if there exists some `k : Nat` such that `n = 2 ^ k`.
43+
-/
44+
def isPowerOfTwo (n : Nat) := ∃ k, n = 2 ^ k
45+
46+
theorem isPowerOfTwo_one : isPowerOfTwo 1 :=
47+
0, by decide⟩
48+
49+
theorem isPowerOfTwo_mul_two_of_isPowerOfTwo (h : isPowerOfTwo n) : isPowerOfTwo (n * 2) :=
50+
have ⟨k, h⟩ := h
51+
⟨k+1, by simp [h, Nat.pow_succ]⟩
52+
53+
theorem pos_of_isPowerOfTwo (h : isPowerOfTwo n) : n > 0 := by
54+
have ⟨k, h⟩ := h
55+
rw [h]
56+
apply Nat.pow_pos
57+
decide
58+
59+
theorem isPowerOfTwo_nextPowerOfTwo (n : Nat) : n.nextPowerOfTwo.isPowerOfTwo := by
60+
apply isPowerOfTwo_go
61+
apply isPowerOfTwo_one
62+
where
63+
isPowerOfTwo_go (power : Nat) (h₁ : power > 0) (h₂ : power.isPowerOfTwo) : (nextPowerOfTwo.go n power h₁).isPowerOfTwo := by
64+
unfold nextPowerOfTwo.go
65+
split
66+
. exact isPowerOfTwo_go (power*2) (Nat.mul_pos h₁ (by decide)) (Nat.isPowerOfTwo_mul_two_of_isPowerOfTwo h₂)
67+
. assumption
68+
termination_by n - power
69+
decreasing_by simp_wf; apply nextPowerOfTwo_dec <;> assumption
70+
71+
end Nat
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/-
2+
Copyright (c) George Rennie. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: George Rennie
5+
-/
6+
module
7+
8+
prelude
9+
import all Init.Data.Nat.Power2.Basic
10+
public import Init.Data.Nat.Bitwise.Lemmas
11+
12+
public section
13+
14+
/-!
15+
# Further lemmas about `Nat.isPowerOfTwo`, with the convenience of having bitwise lemmas available.
16+
-/
17+
18+
namespace Nat
19+
20+
theorem not_isPowerOfTwo_zero : ¬isPowerOfTwo 0 := by
21+
rw [isPowerOfTwo, not_exists]
22+
intro x
23+
have := one_le_pow x 2 (by decide)
24+
omega
25+
26+
theorem and_sub_one_testBit_log2 {n : Nat} (h : n ≠ 0) (hpow2 : ¬n.isPowerOfTwo) :
27+
(n &&& (n - 1)).testBit n.log2 := by
28+
rw [testBit_and, Bool.and_eq_true]
29+
constructor
30+
· exact testBit_log2 (by omega)
31+
· by_cases n = 2^n.log2
32+
· rw [isPowerOfTwo, not_exists] at hpow2
33+
have := hpow2 n.log2
34+
trivial
35+
· have := log2_eq_iff (n := n) (k := n.log2) (by omega)
36+
have : (n - 1).log2 = n.log2 := by rw [log2_eq_iff] <;> omega
37+
rw [←this]
38+
exact testBit_log2 (by omega)
39+
40+
theorem and_sub_one_eq_zero_iff_isPowerOfTwo {n : Nat} (h : n ≠ 0) :
41+
(n &&& (n - 1)) = 0 ↔ n.isPowerOfTwo := by
42+
constructor
43+
· intro hbitwise
44+
false_or_by_contra
45+
rename_i hpow2
46+
have := and_sub_one_testBit_log2 h hpow2
47+
rwa [hbitwise, zero_testBit n.log2, Bool.false_eq_true] at this
48+
· intro hpow2
49+
rcases hpow2 with ⟨_, hpow2⟩
50+
rw [hpow2, and_two_pow_sub_one_eq_mod, mod_self]
51+
52+
theorem ne_zero_and_sub_one_eq_zero_iff_isPowerOfTwo {n : Nat} :
53+
((n ≠ 0) ∧ (n &&& (n - 1)) = 0) ↔ n.isPowerOfTwo := by
54+
match h : n with
55+
| 0 => simp [not_isPowerOfTwo_zero]
56+
| n + 1 => simp; exact and_sub_one_eq_zero_iff_isPowerOfTwo (by omega)
57+
58+
@[inline]
59+
instance {n : Nat} : Decidable n.isPowerOfTwo :=
60+
decidable_of_iff _ ne_zero_and_sub_one_eq_zero_iff_isPowerOfTwo
61+
62+
end Nat

0 commit comments

Comments
 (0)