Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - chore: move binary recursion on Nat into a new file #15567

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
de06324
chore: move binary recursion on `Nat` into a new file
FR-vdash-bot Aug 6, 2024
3033105
fix
FR-vdash-bot Aug 6, 2024
8506e10
name
FR-vdash-bot Aug 6, 2024
751f288
shake
FR-vdash-bot Aug 6, 2024
6bc124d
shake
FR-vdash-bot Aug 6, 2024
a57d78f
fix shake
FR-vdash-bot Aug 6, 2024
d5931d2
noshake
FR-vdash-bot Aug 6, 2024
23e2cb7
do not need now
FR-vdash-bot Aug 6, 2024
a80d55d
Update Size.lean
FR-vdash-bot Aug 7, 2024
c317792
fix
FR-vdash-bot Aug 7, 2024
3e04e87
Merge branch 'master' into FR_binrec_move
FR-vdash-bot Oct 18, 2024
68a1ea8
fix merge
FR-vdash-bot Oct 18, 2024
4258b9a
fix merge
FR-vdash-bot Oct 18, 2024
e75ec4a
`@[elab_as_elim]`
FR-vdash-bot Oct 18, 2024
d50abd4
fix
FR-vdash-bot Oct 18, 2024
4e82f35
fix
FR-vdash-bot Oct 18, 2024
5f8b6f1
fix merge
FR-vdash-bot Oct 18, 2024
cf0f0c0
fix
FR-vdash-bot Oct 18, 2024
666559e
shake
FR-vdash-bot Oct 18, 2024
6ecf59e
Merge branch 'master' into FR_binrec_move
FR-vdash-bot Oct 18, 2024
59c99d6
fix merge
FR-vdash-bot Oct 18, 2024
5a1ffd8
fix merge
FR-vdash-bot Oct 18, 2024
482dc99
chore(Data/Nat/{Bit, Bitwise}): make `Nat.bit_mod_two` `Nat.bit_eq_ze…
FR-vdash-bot Oct 18, 2024
0ad6c92
fix
FR-vdash-bot Oct 18, 2024
8e55b14
shake
FR-vdash-bot Oct 19, 2024
51a9eb2
fix shake
FR-vdash-bot Oct 19, 2024
cd22f61
Merge branch 'master' into FR_bit_simp
FR-vdash-bot Oct 19, 2024
d0457d0
Update BinaryRec.lean
FR-vdash-bot Oct 19, 2024
ae00268
fix merge
FR-vdash-bot Oct 19, 2024
d9077a5
Merge branch 'master' into FR_binrec_move
FR-vdash-bot Oct 19, 2024
10eb2d1
reduce diffs
FR-vdash-bot Oct 19, 2024
5eb3319
fix merge
FR-vdash-bot Oct 19, 2024
9522165
style
FR-vdash-bot Oct 19, 2024
9bb2be3
motive
FR-vdash-bot Oct 19, 2024
4ee3f7e
docs
FR-vdash-bot Oct 19, 2024
c48963f
Merge branch 'master' into FR_binrec_move
FR-vdash-bot Oct 19, 2024
c91878e
Merge branch 'FR_bit_simp' into FR_binrec_move
FR-vdash-bot Oct 19, 2024
2323b04
fix shake
FR-vdash-bot Oct 19, 2024
e2eb4d6
Revert "reduce diffs"
FR-vdash-bot Oct 19, 2024
96b026b
Merge branch 'master' into FR_binrec_move
FR-vdash-bot Oct 19, 2024
57a6f2c
copyright
FR-vdash-bot Oct 19, 2024
8d91e67
`Authors`
FR-vdash-bot Oct 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Mathlib.lean
Original file line number Diff line number Diff line change
Expand Up @@ -2434,6 +2434,7 @@ import Mathlib.Data.NNRat.Lemmas
import Mathlib.Data.NNRat.Order
import Mathlib.Data.NNReal.Basic
import Mathlib.Data.NNReal.Star
import Mathlib.Data.Nat.BinaryRec
import Mathlib.Data.Nat.BitIndices
import Mathlib.Data.Nat.Bits
import Mathlib.Data.Nat.Bitwise
Expand Down
157 changes: 157 additions & 0 deletions Mathlib/Data/Nat/BinaryRec.lean
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Praneeth Kolichala, Yuyang Zhao
-/

/-!
# Binary recursion on `Nat`

This file defines binary recursion on `Nat`.

## Main results
* `Nat.binaryRec`: A recursion principle for `bit` representations of natural numbers.
* `Nat.binaryRec'`: The same as `binaryRec`, but the induction step can assume that if `n=0`,
the bit being appended is `true`.
* `Nat.binaryRecFromOne`: The same as `binaryRec`, but special casing both 0 and 1 as base cases.
-/

universe u

namespace Nat

/-- `bit b` appends the digit `b` to the binary representation of its natural number input. -/
def bit (b : Bool) : Nat → Nat := cond b (2 * · + 1) (2 * ·)

theorem shiftRight_one (n) : n >>> 1 = n / 2 := rfl

@[simp]
theorem bit_decide_mod_two_eq_one_shiftRight_one (n : Nat) : bit (n % 2 = 1) (n >>> 1) = n := by
simp only [bit, shiftRight_one]
cases mod_two_eq_zero_or_one n with | _ h => simpa [h] using Nat.div_add_mod n 2

theorem bit_testBit_zero_shiftRight_one (n : Nat) : bit (n.testBit 0) (n >>> 1) = n := by
simp

@[simp]
theorem bit_eq_zero_iff {n : Nat} {b : Bool} : bit b n = 0 ↔ n = 0 ∧ b = false := by
cases n <;> cases b <;> simp [bit, Nat.shiftLeft_succ, Nat.two_mul, ← Nat.add_assoc]

/-- For a predicate `motive : Nat → Sort u`, if instances can be
constructed for natural numbers of the form `bit b n`,
they can be constructed for any given natural number. -/
@[inline]
def bitCasesOn {motive : Nat → Sort u} (n) (h : ∀ b n, motive (bit b n)) : motive n :=
-- `1 &&& n != 0` is faster than `n.testBit 0`. This may change when we have faster `testBit`.
let x := h (1 &&& n != 0) (n >>> 1)
-- `congrArg motive _ ▸ x` is defeq to `x` in non-dependent case
congrArg motive n.bit_testBit_zero_shiftRight_one ▸ x

/-- A recursion principle for `bit` representations of natural numbers.
For a predicate `motive : Nat → Sort u`, if instances can be
constructed for natural numbers of the form `bit b n`,
they can be constructed for all natural numbers. -/
@[elab_as_elim, specialize]
def binaryRec {motive : Nat → Sort u} (z : motive 0) (f : ∀ b n, motive n → motive (bit b n))
(n : Nat) : motive n :=
if n0 : n = 0 then congrArg motive n0 ▸ z
else
let x := f (1 &&& n != 0) (n >>> 1) (binaryRec z f (n >>> 1))
congrArg motive n.bit_testBit_zero_shiftRight_one ▸ x
decreasing_by exact bitwise_rec_lemma n0

/-- The same as `binaryRec`, but the induction step can assume that if `n=0`,
the bit being appended is `true`-/
@[elab_as_elim, specialize]
def binaryRec' {motive : Nat → Sort u} (z : motive 0)
(f : ∀ b n, (n = 0 → b = true) → motive n → motive (bit b n)) :
∀ n, motive n :=
binaryRec z fun b n ih =>
if h : n = 0 → b = true then f b n h ih
else
have : bit b n = 0 := by
rw [bit_eq_zero_iff]
cases n <;> cases b <;> simp at h ⊢
congrArg motive this ▸ z

/-- The same as `binaryRec`, but special casing both 0 and 1 as base cases -/
@[elab_as_elim, specialize]
def binaryRecFromOne {motive : Nat → Sort u} (z₀ : motive 0) (z₁ : motive 1)
(f : ∀ b n, n ≠ 0 → motive n → motive (bit b n)) :
∀ n, motive n :=
binaryRec' z₀ fun b n h ih =>
if h' : n = 0 then
have : bit b n = bit true 0 := by
rw [h', h h']
congrArg motive this ▸ z₁
else f b n h' ih

theorem bit_val (b n) : bit b n = 2 * n + b.toNat := by
cases b <;> rfl

@[simp]
theorem bit_div_two (b n) : bit b n / 2 = n := by
rw [bit_val, Nat.add_comm, add_mul_div_left, div_eq_of_lt, Nat.zero_add]
· cases b <;> decide
· decide

@[simp]
theorem bit_mod_two (b n) : bit b n % 2 = b.toNat := by
cases b <;> simp [bit_val, mul_add_mod]

@[simp]
theorem bit_shiftRight_one (b n) : bit b n >>> 1 = n :=
bit_div_two b n

theorem testBit_bit_zero (b n) : (bit b n).testBit 0 = b := by
simp

@[simp]
theorem bitCasesOn_bit {motive : Nat → Sort u} (h : ∀ b n, motive (bit b n)) (b : Bool) (n : Nat) :
bitCasesOn (bit b n) h = h b n := by
change congrArg motive (bit b n).bit_testBit_zero_shiftRight_one ▸ h _ _ = h b n
generalize congrArg motive (bit b n).bit_testBit_zero_shiftRight_one = e; revert e
rw [testBit_bit_zero, bit_shiftRight_one]
intros; rfl

unseal binaryRec in
@[simp]
theorem binaryRec_zero {motive : Nat → Sort u} (z : motive 0) (f : ∀ b n, motive n → motive (bit b n)) :
binaryRec z f 0 = z :=
rfl

@[simp]
theorem binaryRec_one {motive : Nat → Sort u} (z : motive 0) (f : ∀ b n, motive n → motive (bit b n)) :
binaryRec (motive := motive) z f 1 = f true 0 z := by
rw [binaryRec]
simp only [add_one_ne_zero, ↓reduceDIte, Nat.reduceShiftRight, binaryRec_zero]
rfl

/--
The same as `binaryRec_eq`,
but that one unfortunately requires `f` to be the identity when appending `false` to `0`.
Here, we allow you to explicitly say that that case is not happening,
i.e. supplying `n = 0 → b = true`. -/
theorem binaryRec_eq' {motive : Nat → Sort u} {z : motive 0}
{f : ∀ b n, motive n → motive (bit b n)} (b n)
(h : f false 0 z = z ∨ (n = 0 → b = true)) :
binaryRec z f (bit b n) = f b n (binaryRec z f n) := by
by_cases h' : bit b n = 0
case pos =>
obtain ⟨rfl, rfl⟩ := bit_eq_zero_iff.mp h'
simp only [Bool.false_eq_true, imp_false, not_true_eq_false, or_false] at h
unfold binaryRec
exact h.symm
case neg =>
rw [binaryRec, dif_neg h']
change congrArg motive (bit b n).bit_testBit_zero_shiftRight_one ▸ f _ _ _ = _
generalize congrArg motive (bit b n).bit_testBit_zero_shiftRight_one = e; revert e
rw [testBit_bit_zero, bit_shiftRight_one]
intros; rfl

theorem binaryRec_eq {motive : Nat → Sort u} {z : motive 0} {f : ∀ b n, motive n → motive (bit b n)}
(h : f false 0 z = z) (b n) :
binaryRec z f (bit b n) = f b n (binaryRec z f n) :=
binaryRec_eq' b n (.inl h)

end Nat
140 changes: 1 addition & 139 deletions Mathlib/Data/Nat/Bits.lean
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ Authors: Praneeth Kolichala
-/
import Mathlib.Algebra.Group.Basic
import Mathlib.Algebra.Group.Nat
import Mathlib.Algebra.Group.Units.Basic
import Mathlib.Data.Nat.Defs
import Mathlib.Data.Nat.BinaryRec
import Mathlib.Data.List.Defs
import Mathlib.Tactic.Convert
import Mathlib.Tactic.GeneralizeProofs
Expand Down Expand Up @@ -114,35 +114,9 @@ lemma div2_val (n) : div2 n = n / 2 := by
(Nat.add_left_cancel (Eq.trans ?_ (Nat.mod_add_div n 2).symm))
rw [mod_two_of_bodd, bodd_add_div2]

/-- `bit b` appends the digit `b` to the binary representation of its natural number input. -/
def bit (b : Bool) : ℕ → ℕ := cond b (2 * · + 1) (2 * ·)

lemma bit_val (b n) : bit b n = 2 * n + b.toNat := by
cases b <;> rfl

lemma bit_decomp (n : Nat) : bit (bodd n) (div2 n) = n :=
(bit_val _ _).trans <| (Nat.add_comm _ _).trans <| bodd_add_div2 _

theorem shiftRight_one (n) : n >>> 1 = n / 2 := rfl

@[simp]
theorem bit_decide_mod_two_eq_one_shiftRight_one (n : Nat) : bit (n % 2 = 1) (n >>> 1) = n := by
simp only [bit, shiftRight_one]
cases mod_two_eq_zero_or_one n with | _ h => simpa [h] using Nat.div_add_mod n 2

theorem bit_testBit_zero_shiftRight_one (n : Nat) : bit (n.testBit 0) (n >>> 1) = n := by
simp

/-- For a predicate `motive : Nat → Sort*`, if instances can be
constructed for natural numbers of the form `bit b n`,
they can be constructed for any given natural number. -/
@[inline]
def bitCasesOn {motive : Nat → Sort u} (n) (h : ∀ b n, motive (bit b n)) : motive n :=
-- `1 &&& n != 0` is faster than `n.testBit 0`. This may change when we have faster `testBit`.
let x := h (1 &&& n != 0) (n >>> 1)
-- `congrArg motive _ ▸ x` is defeq to `x` in non-dependent case
congrArg motive n.bit_testBit_zero_shiftRight_one ▸ x

lemma bit_zero : bit false 0 = 0 :=
rfl

Expand Down Expand Up @@ -172,19 +146,6 @@ lemma binaryRec_decreasing (h : n ≠ 0) : div2 n < n := by
(lt_of_le_of_ne n.zero_le h.symm)
rwa [Nat.mul_one] at this

/-- A recursion principle for `bit` representations of natural numbers.
For a predicate `motive : Nat → Sort*`, if instances can be
constructed for natural numbers of the form `bit b n`,
they can be constructed for all natural numbers. -/
@[elab_as_elim, specialize]
def binaryRec {motive : Nat → Sort u} (z : motive 0) (f : ∀ b n, motive n → motive (bit b n))
(n : Nat) : motive n :=
if n0 : n = 0 then congrArg motive n0 ▸ z
else
let x := f (1 &&& n != 0) (n >>> 1) (binaryRec z f (n >>> 1))
congrArg motive n.bit_testBit_zero_shiftRight_one ▸ x
decreasing_by exact bitwise_rec_lemma n0

/-- `size n` : Returns the size of a natural number in
bits i.e. the length of its binary representation -/
def size : ℕ → ℕ :=
Expand All @@ -201,20 +162,6 @@ def bits : ℕ → List Bool :=
def ldiff : ℕ → ℕ → ℕ :=
bitwise fun a b => a && not b

@[simp]
lemma binaryRec_zero {motive : Nat → Sort u}
(z : motive 0) (f : ∀ b n, motive n → motive (bit b n)) :
binaryRec z f 0 = z := by
rw [binaryRec]
rfl

@[simp]
lemma binaryRec_one {C : Nat → Sort u} (z : C 0) (f : ∀ b n, C n → C (bit b n)) :
binaryRec (motive := C) z f 1 = f true 0 z := by
rw [binaryRec]
simp only [succ_ne_self, ↓reduceDIte, reduceShiftRight, binaryRec_zero]
rfl

/-! bitwise ops -/

lemma bodd_bit (b n) : bodd (bit b n) = b := by
Expand Down Expand Up @@ -242,13 +189,6 @@ lemma shiftLeft'_sub (b m) : ∀ {n k}, k ≤ n → shiftLeft' b m (n - k) = (sh
lemma shiftLeft_sub : ∀ (m : Nat) {n k}, k ≤ n → m <<< (n - k) = (m <<< n) >>> k :=
fun _ _ _ hk => by simp only [← shiftLeft'_false, shiftLeft'_sub false _ hk]

-- Not a `simp` lemma, as later `simp` will be able to prove this.
lemma testBit_bit_zero (b n) : testBit (bit b n) 0 = b := by
rw [testBit, bit]
cases b
· simp [← Nat.mul_two]
· simp [← Nat.mul_two]

lemma bodd_eq_one_and_ne_zero : ∀ n, bodd n = (1 &&& n != 0)
| 0 => rfl
| 1 => rfl
Expand Down Expand Up @@ -289,24 +229,6 @@ theorem bit_add' : ∀ (b : Bool) (n m : ℕ), bit b (n + m) = bit b n + bit fal
theorem bit_ne_zero (b) {n} (h : n ≠ 0) : bit b n ≠ 0 := by
cases b <;> dsimp [bit] <;> omega

@[simp]
theorem bit_div_two (b n) : bit b n / 2 = n := by
rw [bit_val, Nat.add_comm, add_mul_div_left, div_eq_of_lt, Nat.zero_add]
· cases b <;> decide
· decide

@[simp]
theorem bit_shiftRight_one (b n) : bit b n >>> 1 = n :=
bit_div_two b n

@[simp]
theorem bitCasesOn_bit {motive : ℕ → Sort u} (h : ∀ b n, motive (bit b n)) (b : Bool) (n : ℕ) :
bitCasesOn (bit b n) h = h b n := by
change congrArg motive (bit b n).bit_testBit_zero_shiftRight_one ▸ h _ _ = h b n
generalize congrArg motive (bit b n).bit_testBit_zero_shiftRight_one = e; revert e
rw [testBit_bit_zero, bit_shiftRight_one]
intros; rfl

@[simp]
theorem bitCasesOn_bit0 {motive : ℕ → Sort u} (H : ∀ b n, motive (bit b n)) (n : ℕ) :
bitCasesOn (2 * n) H = H false n :=
Expand All @@ -328,13 +250,6 @@ theorem bit_cases_on_inj {motive : ℕ → Sort u} (H₁ H₂ : ∀ b n, motive
((fun n => bitCasesOn n H₁) = fun n => bitCasesOn n H₂) ↔ H₁ = H₂ :=
bit_cases_on_injective.eq_iff

@[simp]
theorem bit_eq_zero_iff {n : ℕ} {b : Bool} : bit b n = 0 ↔ n = 0 ∧ b = false := by
constructor
· cases b <;> simp [Nat.bit]; omega
· rintro ⟨rfl, rfl⟩
rfl

lemma bit_le : ∀ (b : Bool) {m n : ℕ}, m ≤ n → bit b m ≤ bit b n
| true, _, _, h => by dsimp [bit]; omega
| false, _, _, h => by dsimp [bit]; omega
Expand All @@ -343,59 +258,6 @@ lemma bit_lt_bit (a b) (h : m < n) : bit a m < bit b n := calc
bit a m < 2 * n := by cases a <;> dsimp [bit] <;> omega
_ ≤ bit b n := by cases b <;> dsimp [bit] <;> omega

/--
The same as `binaryRec_eq`,
but that one unfortunately requires `f` to be the identity when appending `false` to `0`.
Here, we allow you to explicitly say that that case is not happening,
i.e. supplying `n = 0 → b = true`. -/
theorem binaryRec_eq' {motive : ℕ → Sort*} {z : motive 0} {f : ∀ b n, motive n → motive (bit b n)}
(b n) (h : f false 0 z = z ∨ (n = 0 → b = true)) :
binaryRec z f (bit b n) = f b n (binaryRec z f n) := by
by_cases h' : bit b n = 0
case pos =>
obtain ⟨rfl, rfl⟩ := bit_eq_zero_iff.mp h'
simp only [imp_false, or_false, eq_self_iff_true, not_true, reduceCtorEq] at h
unfold binaryRec
exact h.symm
case neg =>
rw [binaryRec, dif_neg h']
change congrArg motive (bit b n).bit_testBit_zero_shiftRight_one ▸ f _ _ _ = _
generalize congrArg motive (bit b n).bit_testBit_zero_shiftRight_one = e; revert e
rw [testBit_bit_zero, bit_shiftRight_one]
intros; rfl

theorem binaryRec_eq {motive : Nat → Sort u} {z : motive 0}
{f : ∀ b n, motive n → motive (bit b n)}
(h : f false 0 z = z) (b n) :
binaryRec z f (bit b n) = f b n (binaryRec z f n) :=
binaryRec_eq' b n (.inl h)

/-- The same as `binaryRec`, but the induction step can assume that if `n=0`,
the bit being appended is `true`-/
@[elab_as_elim, specialize]
def binaryRec' {motive : ℕ → Sort*} (z : motive 0)
(f : ∀ b n, (n = 0 → b = true) → motive n → motive (bit b n)) :
∀ n, motive n :=
binaryRec z fun b n ih =>
if h : n = 0 → b = true then f b n h ih
else
have : bit b n = 0 := by
rw [bit_eq_zero_iff]
cases n <;> cases b <;> simp at h ⊢
congrArg motive this ▸ z

/-- The same as `binaryRec`, but special casing both 0 and 1 as base cases -/
@[elab_as_elim, specialize]
def binaryRecFromOne {motive : ℕ → Sort*} (z₀ : motive 0) (z₁ : motive 1)
(f : ∀ b n, n ≠ 0 → motive n → motive (bit b n)) :
∀ n, motive n :=
binaryRec' z₀ fun b n h ih =>
if h' : n = 0 then
have : bit b n = bit true 0 := by
rw [h', h h']
congrArg motive this ▸ z₁
else f b n h' ih

@[simp]
theorem zero_bits : bits 0 = [] := by simp [Nat.bits]

Expand Down
10 changes: 3 additions & 7 deletions Mathlib/Data/Nat/Bitwise.lean
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ Copyright (c) 2020 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel, Alex Keizer
-/
import Mathlib.Algebra.Group.Units.Basic
import Mathlib.Algebra.NeZero
import Mathlib.Algebra.Ring.Nat
import Mathlib.Data.List.GetD
import Mathlib.Data.Nat.Bits
import Mathlib.Algebra.Ring.Nat
import Mathlib.Order.Basic
import Mathlib.Tactic.AdaptationNote
import Mathlib.Tactic.Common
import Mathlib.Algebra.NeZero

/-!
# Bitwise operations on natural numbers
Expand Down Expand Up @@ -88,11 +89,6 @@ lemma bitwise_bit {f : Bool → Bool → Bool} (h : f false false = false := by
cases a <;> cases b <;> simp [h2, h4] <;> split_ifs
<;> simp_all (config := {decide := true}) [two_mul]

@[simp]
lemma bit_mod_two (a : Bool) (x : ℕ) :
bit a x % 2 = a.toNat := by
cases a <;> simp [bit_val, mul_add_mod]

lemma bit_mod_two_eq_zero_iff (a x) :
bit a x % 2 = 0 ↔ !a := by
simp
Expand Down
Loading
Loading