This PR gives the `specialize` tactic the ability to instantiate universal quantifiers other than the first using `specialize h (y := v)` syntax. It also fixes an issue where `MVarId.assertAfter` did not record variable alias information, and an issue where `MVarId.replace` and `MVarId.replaceLocalDecl` did not take metavariables into account when calculating dependencies. Additionally it fixes some uninstantiated metavariables bugs, including one in the Infoview tactic state hypothesis diff. The `specialize` tactic now uses `Lean.MVarId.replace` to simplify the implementation, and as a consequence it tries to keep the specialized hypothesis close to its original spot in the local context. Additional metaprogramming API: - `Lean.Expr.getLambdaBody` to accompany `Lean.Expr.getNumHeadLambdas` - `Lean.LocalContext.setType`, `Lean.MetavarContext.setFVarType`, `Lean.MVarId.setFVarType` - `Lean.MVarId.assertAfter'` to assert a new hypothesis as early as possibly in the context where it is well-formed, as a frontend to `Lean.MVarId.assertAfter`, which assumes the new hypothesis is well-formed Breaking change: metaprograms cannot assume that `MVarId`s change if metavariables are assigned. For example, the `change` tactic will no longer change `MVarId`s if the only effect is incidental metavariable assignments. Mathlib impact: this revealed many `dsimp`s that did nothing and could be deleted. Closes #9893
4545 lines
177 KiB
Text
4545 lines
177 KiB
Text
module
|
||
set_option linter.unusedSimpArgs false
|
||
|
||
open BitVec
|
||
|
||
namespace BitVec'
|
||
|
||
theorem mk_zero : BitVec.ofFin (w := w) ⟨0, h⟩ = 0#w := rfl
|
||
theorem ofNatLT_zero : BitVec.ofNatLT (w := w) 0 h = 0#w := rfl
|
||
|
||
theorem getElem_ofFin (x : Fin (2^n)) (i : Nat) (h : i < n) :
|
||
(BitVec.ofFin x)[i] = x.val.testBit i := rfl
|
||
|
||
theorem getMsbD_of_ge (x : BitVec w) (i : Nat) (ge : w ≤ i) : getMsbD x i = false := by
|
||
grind [getMsbD]
|
||
|
||
theorem lt_of_getLsbD {x : BitVec w} {i : Nat} : getLsbD x i = true → i < w := by grind
|
||
|
||
theorem lt_of_getMsbD {x : BitVec w} {i : Nat} : getMsbD x i = true → i < w := by grind
|
||
|
||
theorem getElem?_eq_getElem {l : BitVec w} {n} (h : n < w) : l[n]? = some l[n] := by grind
|
||
|
||
theorem getElem?_eq_some_iff {l : BitVec w} :
|
||
l[n]? = some a ↔ ∃ h : n < w, l[n] = a := by grind
|
||
|
||
theorem some_eq_getElem?_iff {l : BitVec w} :
|
||
some a = l[n]? ↔ ∃ h : n < w, l[n] = a := by grind
|
||
|
||
theorem getElem_of_getElem? {l : BitVec w} :
|
||
l[n]? = some a → ∃ h : n < w, l[n] = a := by grind
|
||
|
||
theorem getElem?_eq_none_iff {l : BitVec w} : l[n]? = none ↔ w ≤ n := by grind
|
||
|
||
theorem none_eq_getElem?_iff {l : BitVec w} : none = l[n]? ↔ w ≤ n := by grind
|
||
|
||
theorem getElem?_eq_none {l : BitVec w} (h : w ≤ n) : l[n]? = none := by grind
|
||
|
||
theorem getElem?_eq (l : BitVec w) (i : Nat) :
|
||
l[i]? = if h : i < w then some l[i] else none := by grind
|
||
|
||
theorem some_getElem_eq_getElem? (l : BitVec w) (i : Nat) (h : i < w) :
|
||
(some l[i] = l[i]?) ↔ True := by grind
|
||
|
||
theorem getElem?_eq_some_getElem (l : BitVec w) (i : Nat) (h : i < w) :
|
||
(l[i]? = some l[i]) ↔ True := by grind
|
||
|
||
theorem getElem_eq_iff {l : BitVec w} {n : Nat} {h : n < w} : l[n] = x ↔ l[n]? = some x := by grind
|
||
|
||
theorem getElem_eq_getElem? (l : BitVec w) (i : Nat) (h : i < w) :
|
||
l[i] = l[i]?.get (by simp [h]) := by grind
|
||
|
||
theorem getLsbD_eq_getElem?_getD {x : BitVec w} {i : Nat} :
|
||
x.getLsbD i = x[i]?.getD false := by
|
||
rw [getElem?_def]
|
||
split
|
||
· rfl
|
||
· grind
|
||
|
||
theorem getElem_of_getLsbD_eq_true {x : BitVec w} {i : Nat} (h : x.getLsbD i = true) :
|
||
(x[i]'(lt_of_getLsbD h) = true) = True := by grind
|
||
|
||
/--
|
||
This normalized a bitvec using `ofFin` to `ofNat`.
|
||
-/
|
||
theorem ofFin_eq_ofNat : @BitVec.ofFin w (Fin.mk x lt) = BitVec.ofNat w x := by
|
||
simp only [BitVec.ofNat, Fin.Internal.ofNat_eq_ofNat, Fin.ofNat, lt, Nat.mod_eq_of_lt]
|
||
|
||
/-- Prove nonequality of bitvectors in terms of nat operations. -/
|
||
theorem toNat_ne_iff_ne {n} {x y : BitVec n} : x.toNat ≠ y.toNat ↔ x ≠ y := by
|
||
grind [eq_of_toNat_eq]
|
||
|
||
theorem val_toFin (x : BitVec w) : x.toFin.val = x.toNat := rfl
|
||
|
||
@[bitvec_to_nat] theorem toNat_eq {x y : BitVec n} : x = y ↔ x.toNat = y.toNat := by
|
||
grind [eq_of_toNat_eq]
|
||
|
||
theorem toNat_inj {x y : BitVec n} : x.toNat = y.toNat ↔ x = y := toNat_eq.symm
|
||
|
||
@[bitvec_to_nat] theorem toNat_ne {x y : BitVec n} : x ≠ y ↔ x.toNat ≠ y.toNat := by
|
||
grind [toNat_eq]
|
||
|
||
protected theorem toNat_lt_twoPow_of_le (h : m ≤ n) {x : BitVec m} :
|
||
x.toNat < 2 ^ n := by
|
||
apply Nat.lt_of_lt_of_le x.isLt
|
||
apply Nat.pow_le_pow_of_le
|
||
<;> grind
|
||
|
||
theorem two_pow_le_toNat_of_getElem_eq_true {i : Nat} {x : BitVec w}
|
||
(hi : i < w) (hx : x[i] = true) : 2^i ≤ x.toNat := by
|
||
apply Nat.ge_two_pow_of_testBit
|
||
grind
|
||
|
||
theorem msb_eq_getMsbD (x : BitVec w) : x.msb = x.getMsbD 0 := by grind [BitVec.msb]
|
||
|
||
theorem getMsb_eq_getLsb (x : BitVec w) (i : Fin w) :
|
||
x.getMsb i = x.getLsb ⟨w - 1 - i, by omega⟩ := by
|
||
simp only [getMsb, getLsb]
|
||
|
||
theorem getMsb?_eq_getLsb? (x : BitVec w) (i : Nat) :
|
||
x.getMsb? i = if i < w then x.getLsb? (w - 1 - i) else none := by
|
||
grind [getMsb?]
|
||
|
||
theorem getMsbD_eq_getLsbD (x : BitVec w) (i : Nat) :
|
||
x.getMsbD i = (decide (i < w) && x.getLsbD (w - 1 - i)) := by
|
||
grind [getMsbD]
|
||
|
||
theorem getMsb'_eq_getLsb' (x : BitVec w) (i : Nat) : x.getMsbD i = (decide (i < w) && x.getLsbD (w - 1 - i)) := by
|
||
rw [getMsbD, getLsbD]
|
||
|
||
theorem getLsbD_eq_getMsbD (x : BitVec w) (i : Nat) : x.getLsbD i = (decide (i < w) && x.getMsbD (w - 1 - i)) := by
|
||
grind
|
||
|
||
theorem getElem?_of_ge (x : BitVec w) (i : Nat) (ge : w ≤ i) : x[i]? = none := by grind
|
||
|
||
theorem getMsb?_of_ge (x : BitVec w) (i : Nat) (ge : w ≤ i) : getMsb? x i = none := by grind
|
||
|
||
theorem lt_of_getElem?_eq_some (x : BitVec w) (i : Nat) : x[i]? = some b → i < w := by grind
|
||
|
||
theorem lt_of_getMsb?_eq_some (x : BitVec w) (i : Nat) : getMsb? x i = some b → i < w := by grind
|
||
|
||
theorem lt_of_isSome_getElem? (x : BitVec w) (i : Nat) : x[i]?.isSome → i < w := by grind
|
||
|
||
theorem lt_of_isSome_getMsb? (x : BitVec w) (i : Nat) : (getMsb? x i).isSome → i < w := by grind
|
||
|
||
theorem getMsbD_eq_getMsb?_getD (x : BitVec w) (i : Nat) :
|
||
x.getMsbD i = (x.getMsb? i).getD false := by grind
|
||
|
||
theorem eq_of_getLsbD_eq_iff {w : Nat} {x y : BitVec w} :
|
||
x = y ↔ ∀ (i : Nat), i < w → x.getLsbD i = y.getLsbD i := by
|
||
have iff := @BitVec.eq_of_getElem_eq_iff w x y
|
||
constructor <;> grind
|
||
|
||
theorem eq_of_getMsbD_eq {x y : BitVec w}
|
||
(pred : ∀ i, i < w → x.getMsbD i = y.getMsbD i) : x = y := by
|
||
simp only [getMsbD] at pred
|
||
apply eq_of_getLsbD_eq
|
||
intro i i_lt
|
||
if w_zero : w = 0 then
|
||
grind
|
||
else
|
||
have w_pos : 0 < w := by grind
|
||
have r : i ≤ w - 1 := by
|
||
-- FIXME: `grind` fails with a kernel type mismatch here
|
||
-- with a failure in reduction of `Lean.Grind.CommRing.Stepwise.imp_1eq_cert`
|
||
-- We need an `@[expose]` somewhere, but where?
|
||
simp [Nat.le_sub_iff_add_le w_pos]
|
||
grind
|
||
have q_lt : w - 1 - i < w := by
|
||
simp only [Nat.sub_sub]
|
||
-- FIXME: `grind` fails with a kernel type mismatch here
|
||
-- with a failure in reduction of `Lean.Grind.CommRing.Stepwise.imp_1eq_cert`
|
||
apply Nat.sub_lt w_pos
|
||
grind
|
||
have q := pred (w - 1 - i) q_lt
|
||
simpa [q_lt, Nat.sub_sub_self, r] using q
|
||
|
||
-- This cannot be a `@[simp]` lemma, as it would be tried at every term.
|
||
theorem of_length_zero {x : BitVec 0} : x = 0#0 := by grind
|
||
|
||
theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 := by simp [of_length_zero]
|
||
theorem toInt_zero_length (x : BitVec 0) : x.toInt = 0 := by simp [of_length_zero]
|
||
|
||
theorem toNat_zero : (0#w).toNat = 0 := rfl
|
||
|
||
theorem getLsbD_zero_length (x : BitVec 0) : x.getLsbD i = false := by grind
|
||
theorem getMsbD_zero_length (x : BitVec 0) : x.getMsbD i = false := by grind
|
||
theorem msb_zero_length (x : BitVec 0) : x.msb = false := by grind
|
||
|
||
theorem toNat_of_zero_length (h : w = 0) (x : BitVec w) : x.toNat = 0 := by grind
|
||
theorem toInt_of_zero_length (h : w = 0) (x : BitVec w) : x.toInt = 0 := by grind
|
||
theorem getLsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getLsbD i = false := by grind
|
||
theorem getMsbD_of_zero_length (h : w = 0) (x : BitVec w) : x.getMsbD i = false := by grind
|
||
theorem msb_of_zero_length (h : w = 0) (x : BitVec w) : x.msb = false := by grind
|
||
theorem eq_of_zero_length (h : w = 0) {x y : BitVec w} : x = y := by grind
|
||
|
||
theorem length_pos_of_ne {x y : BitVec w} (h : x ≠ y) : 0 < w := by grind
|
||
|
||
theorem ofFin_ofNat (n : Nat) :
|
||
ofFin (no_index (OfNat.ofNat n : Fin (2^w))) = OfNat.ofNat n := by
|
||
simp only [OfNat.ofNat, Fin.Internal.ofNat_eq_ofNat, Fin.ofNat, BitVec.ofNat]
|
||
|
||
-- We use a `grind_pattern` as `@[grind]` will not use the `no_index` term.
|
||
grind_pattern ofFin_ofNat => ofFin (OfNat.ofNat n : Fin (2^w))
|
||
|
||
theorem ofFin_neg {x : Fin (2 ^ w)} : ofFin (-x) = -(ofFin x) := by
|
||
rfl
|
||
|
||
open Fin.NatCast in
|
||
@[simp, norm_cast] theorem ofFin_natCast (n : Nat) : ofFin (n : Fin (2^w)) = (n : BitVec w) := by
|
||
rfl
|
||
|
||
theorem eq_of_toFin_eq : ∀ {x y : BitVec w}, x.toFin = y.toFin → x = y
|
||
| ⟨_, _⟩, ⟨_, _⟩, rfl => rfl
|
||
|
||
theorem toFin_inj {x y : BitVec w} : x.toFin = y.toFin ↔ x = y :=
|
||
⟨@eq_of_toFin_eq w x y, by grind⟩
|
||
|
||
theorem toFin_zero : toFin (0 : BitVec w) = 0 := rfl
|
||
theorem toFin_one : toFin (1 : BitVec w) = 1 := by
|
||
rw [toFin_inj]; grind
|
||
|
||
open Fin.NatCast in
|
||
@[simp, norm_cast]
|
||
theorem toFin_natCast (n : Nat) : toFin (n : BitVec w) = (n : Fin (2^w)) := by
|
||
rfl
|
||
|
||
theorem toFin_ofBool (b : Bool) : (ofBool b).toFin = Fin.ofNat 2 (b.toNat) := by
|
||
cases b <;> rfl
|
||
|
||
theorem ofNat_one (n : Nat) : BitVec.ofNat 1 n = BitVec.ofBool (n % 2 = 1) := by
|
||
rcases (Nat.mod_two_eq_zero_or_one n) with h | h <;> simp [h, BitVec.ofNat, Fin.ofNat]
|
||
|
||
theorem ofBool_eq_iff_eq : ∀ {b b' : Bool}, BitVec.ofBool b = BitVec.ofBool b' ↔ b = b' := by
|
||
decide
|
||
|
||
theorem getLsbD_ofNatLT {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
|
||
getLsbD (x#'lt) i = x.testBit i := by
|
||
simp [getLsbD, BitVec.ofNatLT]
|
||
|
||
theorem getMsbD_ofNatLT {n x i : Nat} (h : x < 2^n) :
|
||
getMsbD (x#'h) i = (decide (i < n) && x.testBit (n - 1 - i)) := by grind
|
||
|
||
theorem ofNatLT_eq_ofNat {w : Nat} {n : Nat} (hn) : BitVec.ofNatLT n hn = BitVec.ofNat w n :=
|
||
eq_of_toNat_eq (by simp [Nat.mod_eq_of_lt hn])
|
||
|
||
theorem toFin_ofNat (x : Nat) : toFin (BitVec.ofNat w x) = Fin.ofNat (2^w) x := rfl
|
||
|
||
theorem finMk_toNat (x : BitVec w) : Fin.mk x.toNat x.isLt = x.toFin := rfl
|
||
|
||
theorem toFin_ofNatLT {n : Nat} (h : n < 2 ^ w) : (BitVec.ofNatLT n h).toFin = Fin.mk n h := rfl
|
||
|
||
theorem toFin_ofFin (n : Fin (2 ^ w)) : (BitVec.ofFin n).toFin = n := rfl
|
||
theorem ofFin_toFin (x : BitVec w) : BitVec.ofFin x.toFin = x := rfl
|
||
|
||
theorem ofNatLT_finVal (n : Fin (2 ^ w)) : BitVec.ofNatLT n.val n.isLt = BitVec.ofFin n := rfl
|
||
|
||
theorem ofNatLT_toNat (x : BitVec w) : BitVec.ofNatLT x.toNat x.isLt = x := rfl
|
||
|
||
theorem ofNat_finVal (n : Fin (2 ^ w)) : BitVec.ofNat w n.val = BitVec.ofFin n := by
|
||
rw [← BitVec.ofNatLT_eq_ofNat n.isLt, ofNatLT_finVal]
|
||
|
||
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
|
||
-- If `x` and `n` are not literals, applying this theorem eagerly may not be a good idea.
|
||
theorem getLsbD_ofNat (n : Nat) (x : Nat) (i : Nat) :
|
||
getLsbD (BitVec.ofNat n x) i = (i < n && x.testBit i) := by
|
||
simp [getLsbD, BitVec.ofNat, Fin.val_ofNat]
|
||
|
||
theorem getLsbD_zero : (0#w).getLsbD i = false := by grind [getLsbD]
|
||
|
||
theorem getElem_zero (h : i < w) : (0#w)[i] = false := by grind [getElem_eq_testBit_toNat]
|
||
|
||
theorem getMsbD_zero : (0#w).getMsbD i = false := by grind [getMsbD]
|
||
|
||
theorem getLsbD_one : (1#w).getLsbD i = (decide (0 < w) && decide (i = 0)) := by
|
||
simp only [getLsbD, toNat_ofNat, Nat.testBit_mod_two_pow]
|
||
by_cases h : i = 0
|
||
<;> simp [h, Nat.testBit_eq_decide_div_mod_eq, Nat.div_eq_of_lt]
|
||
|
||
theorem getElem_one (h : i < w) : (1#w)[i] = decide (i = 0) := by
|
||
grind [=_ getLsbD_eq_getElem]
|
||
|
||
/-- The msb at index `w-1` is the least significant bit, and is true when the width is nonzero. -/
|
||
theorem getMsbD_one : (1#w).getMsbD i = (decide (i = w - 1) && decide (0 < w)) := by grind
|
||
|
||
-- This is not a good `grind` lemma because we would have to index on `%`.
|
||
theorem toNat_mod_cancel (x : BitVec n) : x.toNat % 2^n = x.toNat :=
|
||
Nat.mod_eq_of_lt x.isLt
|
||
|
||
theorem toNat_mod_cancel' {x : BitVec n} :
|
||
(x.toNat : Int) % (((2 ^ n) : Nat) : Int) = x.toNat := by
|
||
grind [toNat_mod_cancel]
|
||
|
||
theorem sub_toNat_mod_cancel {x : BitVec w} (h : ¬ x = 0#w) :
|
||
(2 ^ w - x.toNat) % 2 ^ w = 2 ^ w - x.toNat := by
|
||
simp only [toNat_eq, toNat_ofNat, Nat.zero_mod] at h
|
||
rw [Nat.mod_eq_of_lt (by omega)]
|
||
|
||
theorem sub_toNat_mod_cancel_of_toNat {x : BitVec w} (h : x.toNat ≠ 0) :
|
||
(2 ^ w - x.toNat) % 2 ^ w = 2 ^ w - x.toNat := by
|
||
have := x.isLt
|
||
grind [= Nat.mod_eq_of_lt]
|
||
|
||
theorem toNat_mod_cancel_of_lt {x : BitVec n} (h : n < m) : x.toNat % (2 ^ m) = x.toNat := by
|
||
have : 2 ^ n < 2 ^ m := Nat.pow_lt_pow_of_lt (by omega) h
|
||
exact Nat.mod_eq_of_lt (by omega)
|
||
|
||
theorem sub_sub_toNat_cancel {x : BitVec w} :
|
||
2 ^ w - (2 ^ w - x.toNat) = x.toNat := by
|
||
grind
|
||
|
||
theorem sub_add_bmod_cancel {x y : BitVec w} :
|
||
((((2 ^ w : Nat) - y.toNat) : Int) + x.toNat).bmod (2 ^ w) =
|
||
((x.toNat : Int) - y.toNat).bmod (2 ^ w) := by
|
||
/-
|
||
**Note**: This is a goal containing nonlinear integer arithmetic.
|
||
This is not in `grind`s scope. The following command used to work
|
||
before we expanded `Int.bmod` into `Int.emod`.
|
||
-/
|
||
-- grind [=_ Int.add_bmod_right]
|
||
sorry
|
||
|
||
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m ≤ n) : x < 2 ^ n :=
|
||
Nat.lt_of_lt_of_le lt (Nat.pow_le_pow_right (by trivial : 0 < 2) le)
|
||
|
||
theorem getElem_zero_ofNat_zero (i : Nat) (h : i < w) : (BitVec.ofNat w 0)[i] = false := by grind
|
||
|
||
theorem getElem_zero_ofNat_one (h : 0 < w) : (BitVec.ofNat w 1)[0] = true := by grind
|
||
|
||
theorem getElem?_zero_ofNat_zero : (BitVec.ofNat (w+1) 0)[0]? = some false := by grind
|
||
|
||
theorem getElem?_zero_ofNat_one : (BitVec.ofNat (w+1) 1)[0]? = some true := by grind
|
||
|
||
-- This does not need to be a `@[simp]` or `@[grind]` theorem as it is already handled by
|
||
-- `getElem_zero_ofBool` and `getElem?_eq_getElem`.
|
||
theorem getElem?_zero_ofBool (b : Bool) : (ofBool b)[0]? = some b := by
|
||
simp only [ofBool, ofNat_eq_ofNat, cond_eq_ite]
|
||
split <;>
|
||
-- FIXME: `grind` gives a kernel type mismatch here:
|
||
-- (kernel) application type mismatch
|
||
-- forall_prop_domain_congr (Eq.trans (Lean.Grind.Nat.lt_eq 0 1) (eq_true_of_decide (Eq.refl true))) fun h =>
|
||
-- Eq.refl ((1#1)[0]? = some true)
|
||
-- argument has type
|
||
-- True → ((1#1)[0]? = some true) = ((1#1)[0]? = some true)
|
||
-- but function has type
|
||
-- (∀ (a : True), (fun h => (1#1)[0]? = some (1#1)[0]) ⋯ = (fun h => (1#1)[0]? = some true) a) →
|
||
-- (∀ (a : 0 < 1), (fun h => (1#1)[0]? = some (1#1)[0]) a) = ∀ (a : True), (fun h => (1#1)[0]? = some true) a
|
||
simp_all
|
||
|
||
theorem getElem_zero_ofBool (b : Bool) : (ofBool b)[0] = b := by
|
||
grind [getElem?_zero_ofBool]
|
||
|
||
theorem getElem?_succ_ofBool (b : Bool) (i : Nat) : (ofBool b)[i + 1]? = none := by grind
|
||
|
||
theorem getLsbD_ofBool (b : Bool) (i : Nat) : (ofBool b).getLsbD i = ((i = 0) && b) := by grind
|
||
|
||
theorem getElem_ofBool_zero {b : Bool} : (ofBool b)[0] = b := by grind
|
||
|
||
theorem getElem_ofBool {b : Bool} {h : i < 1} : (ofBool b)[i] = b := by grind
|
||
|
||
theorem getMsbD_ofBool (b : Bool) : (ofBool b).getMsbD i = (decide (i = 0) && b) := by grind
|
||
|
||
theorem msb_ofBool (b : Bool) : (ofBool b).msb = b := by grind
|
||
|
||
theorem one_eq_zero_iff : 1#w = 0#w ↔ w = 0 := by
|
||
constructor
|
||
· intro h
|
||
cases w
|
||
· rfl
|
||
· replace h := congrArg BitVec.toNat h
|
||
simp at h
|
||
· grind
|
||
|
||
/-- `0#w = 1#w` iff the width is zero. -/
|
||
theorem zero_eq_one_iff (w : Nat) : (0#w = 1#w) ↔ (w = 0) := by
|
||
grind [one_eq_zero_iff]
|
||
|
||
/-! ### msb -/
|
||
|
||
theorem msb_zero : (0#w).msb = false := by grind
|
||
|
||
theorem msb_one : (1#w).msb = decide (w = 1) := by grind
|
||
|
||
theorem msb_eq_getLsbD_last (x : BitVec w) :
|
||
x.msb = x.getLsbD (w - 1) := by grind
|
||
|
||
@[bitvec_to_nat] theorem getLsbD_last (x : BitVec w) :
|
||
x.getLsbD (w - 1) = decide (2 ^ (w - 1) ≤ x.toNat) := by
|
||
rcases w with rfl | w
|
||
· grind
|
||
· simp only [getLsbD, Nat.testBit_eq_decide_div_mod_eq, Nat.succ_sub_succ_eq_sub, Nat.sub_zero]
|
||
rcases (Nat.lt_or_ge (BitVec.toNat x) (2 ^ w)) with h | h
|
||
· grind [Nat.div_eq_of_lt]
|
||
· simp only [h]
|
||
rw [Nat.div_eq_sub_div (Nat.two_pow_pos w) h, Nat.div_eq_of_lt]
|
||
· grind
|
||
· omega -- TODO: `grind` can't do this because it doesn't know that `0 < 2^w` and `2^(w+1) = 2 * 2^w`.
|
||
|
||
@[bitvec_to_nat] theorem getLsbD_succ_last (x : BitVec (w + 1)) :
|
||
x.getLsbD w = decide (2 ^ w ≤ x.toNat) := getLsbD_last x
|
||
|
||
@[bitvec_to_nat] theorem msb_eq_decide (x : BitVec w) : BitVec.msb x = decide (2 ^ (w - 1) ≤ x.toNat) := by grind
|
||
|
||
theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat ≥ 2 ^ (n - 1) := by grind
|
||
|
||
theorem msb_eq_getMsbD_zero (x : BitVec w) : x.msb = x.getMsbD 0 := by
|
||
grind [msb_eq_getLsbD_last]
|
||
|
||
/-! ### cast -/
|
||
|
||
theorem toFin_cast (h : w = v) (x : BitVec w) :
|
||
(x.cast h).toFin = x.toFin.cast (by rw [h]) :=
|
||
rfl
|
||
|
||
theorem getLsbD_cast (h : w = v) (x : BitVec w) : (x.cast h).getLsbD i = x.getLsbD i := by grind
|
||
|
||
theorem getMsbD_cast (h : w = v) (x : BitVec w) : (x.cast h).getMsbD i = x.getMsbD i := by grind
|
||
|
||
theorem getElem_cast (h : w = v) (x : BitVec w) (p : i < v) : (x.cast h)[i] = x[i] := by
|
||
subst h; grind
|
||
|
||
theorem msb_cast (h : w = v) (x : BitVec w) : (x.cast h).msb = x.msb := by grind
|
||
|
||
/-! ### toInt/ofInt -/
|
||
|
||
/-- Prove equality of bitvectors in terms of nat operations. -/
|
||
theorem toInt_eq_toNat_cond (x : BitVec n) :
|
||
x.toInt =
|
||
if 2*x.toNat < 2^n then
|
||
(x.toNat : Int)
|
||
else
|
||
(x.toNat : Int) - (2^n : Nat) :=
|
||
rfl
|
||
|
||
theorem toInt_eq_toNat_of_lt {x : BitVec n} (h : 2 * x.toNat < 2^n) :
|
||
x.toInt = x.toNat := by
|
||
grind [toInt_eq_toNat_cond]
|
||
|
||
theorem msb_eq_false_iff_two_mul_lt {x : BitVec w} : x.msb = false ↔ 2 * x.toNat < 2^w := by
|
||
cases w <;> grind [msb_eq_decide]
|
||
|
||
grind_pattern msb_eq_false_iff_two_mul_lt => x.msb, x.toNat
|
||
|
||
theorem msb_eq_true_iff_two_mul_ge {x : BitVec w} : x.msb = true ↔ 2 * x.toNat ≥ 2^w := by grind
|
||
|
||
/-- Characterize `x.toInt` in terms of `x.msb`. -/
|
||
theorem toInt_eq_msb_cond (x : BitVec w) :
|
||
x.toInt = if x.msb then (x.toNat : Int) - (2^w : Nat) else (x.toNat : Int) := by
|
||
grind [BitVec.toInt]
|
||
|
||
theorem toInt_eq_toNat_of_msb {x : BitVec w} (h : x.msb = false) :
|
||
x.toInt = x.toNat := by
|
||
grind [toInt_eq_msb_cond]
|
||
|
||
-- Activate `toInt_eq_toNat_of_msb` if we have already seen both `x.toInt` and `x.toNat`
|
||
grind_pattern toInt_eq_toNat_of_msb => x.toInt, x.toNat
|
||
|
||
theorem toNat_toInt_of_msb {w : Nat} (b : BitVec w) (hb : b.msb = false) : b.toInt.toNat = b.toNat := by grind
|
||
|
||
theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) := by
|
||
simp only [toInt_eq_toNat_cond]
|
||
split
|
||
next g =>
|
||
rw [Int.bmod_pos] <;> simp only [← Int.natCast_emod, toNat_mod_cancel]
|
||
grind
|
||
next g =>
|
||
rw [Int.bmod_neg] <;> simp only [← Int.natCast_emod, toNat_mod_cancel]
|
||
grind
|
||
|
||
grind_pattern toInt_eq_toNat_bmod => x.toInt, x.toNat
|
||
|
||
theorem toInt_neg_of_msb_true {x : BitVec w} (h : x.msb = true) : x.toInt < 0 := by
|
||
grind [BitVec.toInt]
|
||
|
||
theorem toInt_nonneg_of_msb_false {x : BitVec w} (h : x.msb = false) : 0 ≤ x.toInt := by grind
|
||
|
||
theorem toInt_one_of_lt {w : Nat} (h : 1 < w) : (1#w).toInt = 1 := by
|
||
rw [toInt_eq_msb_cond]
|
||
simp only [msb_one, show w ≠ 1 by omega, decide_false, Bool.false_eq_true, ↓reduceIte,
|
||
toNat_ofNat, Int.natCast_emod]
|
||
norm_cast
|
||
apply Nat.mod_eq_of_lt
|
||
apply Nat.one_lt_two_pow (by grind)
|
||
|
||
/-- Prove equality of bitvectors in terms of integer operations. -/
|
||
theorem eq_of_toInt_eq {x y : BitVec n} : x.toInt = y.toInt → x = y := by
|
||
grind [toInt_eq_toNat_cond, eq_of_toNat_eq]
|
||
|
||
theorem toInt_inj {x y : BitVec n} : x.toInt = y.toInt ↔ x = y := by
|
||
grind [eq_of_toInt_eq]
|
||
|
||
theorem toInt_ne {x y : BitVec n} : x.toInt ≠ y.toInt ↔ x ≠ y := by
|
||
rw [Ne, toInt_inj]
|
||
|
||
@[simp, bitvec_to_nat] theorem toNat_ofInt {n : Nat} (i : Int) :
|
||
(BitVec.ofInt n i).toNat = (i % (2^n : Nat)).toNat := by
|
||
unfold BitVec.ofInt
|
||
simp
|
||
|
||
theorem toInt_ofNat' {n : Nat} (x : Nat) : (BitVec.ofNat n x).toInt = (x : Int).bmod (2^n) := by
|
||
simp [toInt_eq_toNat_bmod, -Int.natCast_pow]
|
||
|
||
theorem toInt_ofNat {n : Nat} (x : Nat) :
|
||
(no_index (OfNat.ofNat (α := BitVec n) x)).toInt = (x : Int).bmod (2^n) :=
|
||
toInt_ofNat' x
|
||
|
||
theorem toInt_ofFin {w : Nat} (x : Fin (2^w)) :
|
||
(BitVec.ofFin x).toInt = Int.bmod x (2^w) := by
|
||
grind [toInt_eq_toNat_bmod]
|
||
|
||
theorem toInt_ofInt {n : Nat} (i : Int) :
|
||
(BitVec.ofInt n i).toInt = i.bmod (2^n) := by
|
||
have _ := Nat.two_pow_pos n
|
||
have p : 0 ≤ i % (2^n : Nat) := by omega
|
||
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p, -Int.natCast_pow]
|
||
|
||
theorem toInt_ofInt_eq_self {w : Nat} (hw : 0 < w) {n : Int}
|
||
(h : -2 ^ (w - 1) ≤ n) (h' : n < 2 ^ (w - 1)) : (BitVec.ofInt w n).toInt = n := by
|
||
have hw : w = (w - 1) + 1 := by omega
|
||
rw [toInt_ofInt, Int.bmod_eq_of_le] <;> (rw [hw]; simp [Int.natCast_pow]; omega)
|
||
|
||
theorem ofInt_natCast (w n : Nat) :
|
||
BitVec.ofInt w (n : Int) = BitVec.ofNat w n := rfl
|
||
|
||
theorem ofInt_ofNat (w n : Nat) :
|
||
BitVec.ofInt w (no_index (OfNat.ofNat n)) = BitVec.ofNat w (OfNat.ofNat n) := rfl
|
||
|
||
grind_pattern ofInt_ofNat => BitVec.ofInt w (OfNat.ofNat n)
|
||
|
||
theorem toInt_neg_iff {w : Nat} {x : BitVec w} :
|
||
BitVec.toInt x < 0 ↔ 2 ^ w ≤ 2 * x.toNat := by
|
||
grind [toInt_eq_toNat_cond]
|
||
|
||
theorem toInt_pos_iff {w : Nat} {x : BitVec w} :
|
||
0 ≤ BitVec.toInt x ↔ 2 * x.toNat < 2 ^ w := by
|
||
grind [toInt_eq_toNat_cond]
|
||
|
||
theorem eq_zero_or_eq_one (a : BitVec 1) : a = 0#1 ∨ a = 1#1 := by
|
||
obtain ⟨a, ha⟩ := a
|
||
simp only [Nat.reducePow]
|
||
have acases : a = 0 ∨ a = 1 := by grind
|
||
rcases acases with ⟨rfl | rfl⟩
|
||
· grind
|
||
case inr h =>
|
||
-- TODO: why can't `grind` do this?
|
||
subst h
|
||
grind
|
||
|
||
theorem toInt_zero {w : Nat} : (0#w).toInt = 0 := by
|
||
simp [BitVec.toInt, show 0 < 2^w from Nat.two_pow_pos w]
|
||
|
||
theorem toInt_ofBool (b : Bool) : (ofBool b).toInt = -b.toInt := by
|
||
cases b <;> simp
|
||
|
||
theorem toNat_one (h : 0 < w) : (1#w : BitVec w).toNat = 1 := by
|
||
simp
|
||
grind
|
||
|
||
theorem toInt_one (h : 1 < w) : (1#w : BitVec w).toInt = 1 := by
|
||
grind [toInt_eq_toNat_of_msb]
|
||
|
||
theorem sub_toNat_mod_cancel_of_msb_true {x : BitVec w} (h : x.msb = true) :
|
||
(2 ^ w - x.toNat) % 2 ^ w = 2 ^ w - x.toNat := by
|
||
simp only [msb_eq_decide, decide_eq_true_eq] at h
|
||
have := Nat.two_pow_pos (w-1)
|
||
exact sub_toNat_mod_cancel_of_toNat (by grind)
|
||
|
||
theorem toNat_lt_of_msb_false {w : Nat} {x : BitVec w} (h : x.msb = false) : x.toNat < 2 ^ (w - 1) := by grind
|
||
|
||
theorem le_toNat_of_msb_true {w : Nat} {x : BitVec w} (h : x.msb = true) : 2 ^ (w - 1) ≤ x.toNat := by grind
|
||
|
||
/--
|
||
`x.toInt` is less than `2^(w-1)`.
|
||
We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used.
|
||
-/
|
||
theorem two_mul_toInt_lt {w : Nat} {x : BitVec w} : 2 * x.toInt < 2 ^ w := by
|
||
simp only [BitVec.toInt]
|
||
rcases w with _|w'
|
||
· grind
|
||
· rw [← Nat.two_pow_pred_add_two_pow_pred (by omega), ← Nat.two_mul, Nat.add_sub_cancel]
|
||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int]
|
||
norm_cast
|
||
-- TODO: `grind` won't do this because it doesn't have special handling for `2 ^ (w + 1)`.
|
||
omega
|
||
|
||
theorem two_mul_toInt_le {w : Nat} {x : BitVec w} : 2 * x.toInt ≤ 2 ^ w - 1 :=
|
||
Int.le_sub_one_of_lt two_mul_toInt_lt
|
||
|
||
theorem toInt_lt {w : Nat} {x : BitVec w} : x.toInt < 2 ^ (w - 1) := by
|
||
by_cases h : w = 0
|
||
· subst h
|
||
grind
|
||
· have := @two_mul_toInt_lt w x
|
||
rw_mod_cast [← Nat.two_pow_pred_add_two_pow_pred (by omega), Int.mul_comm, Int.natCast_add] at this
|
||
grind
|
||
|
||
theorem toInt_le {w : Nat} {x : BitVec w} : x.toInt ≤ 2 ^ (w - 1) - 1 :=
|
||
Int.le_sub_one_of_lt toInt_lt
|
||
|
||
/--
|
||
`x.toInt` is greater than or equal to `-2^(w-1)`.
|
||
We phrase the fact in terms of `2^w` to prevent a case split on `w=0` when the lemma is used.
|
||
-/
|
||
theorem le_two_mul_toInt {w : Nat} {x : BitVec w} : -2 ^ w ≤ 2 * x.toInt := by
|
||
simp only [BitVec.toInt]
|
||
rcases w with _|w'
|
||
· grind
|
||
· rw [← Nat.two_pow_pred_add_two_pow_pred (by omega), ← Nat.two_mul, Nat.add_sub_cancel]
|
||
simp only [Nat.zero_lt_succ, Nat.mul_lt_mul_left, Int.natCast_mul, Int.cast_ofNat_Int]
|
||
norm_cast
|
||
omega
|
||
|
||
theorem le_toInt {w : Nat} (x : BitVec w) : -2 ^ (w - 1) ≤ x.toInt := by
|
||
by_cases h : w = 0
|
||
· grind
|
||
· have := le_two_mul_toInt (w := w) (x := x)
|
||
generalize x.toInt = x at *
|
||
rw [(show w = w - 1 + 1 by omega), Int.pow_succ] at this
|
||
grind
|
||
|
||
theorem ofInt_toInt {x : BitVec w} : BitVec.ofInt w x.toInt = x := by
|
||
apply eq_of_toInt_eq
|
||
rw [toInt_ofInt, Int.bmod_eq_of_le_mul_two]
|
||
· simpa [Int.mul_comm _ 2] using le_two_mul_toInt
|
||
· simpa [Int.mul_comm _ 2] using two_mul_toInt_lt
|
||
|
||
theorem toNat_intCast {w : Nat} (x : Int) : (x : BitVec w).toNat = (x % 2^w).toNat := by
|
||
change (BitVec.ofInt w x).toNat = _
|
||
simp
|
||
|
||
theorem toInt_intCast {w : Nat} (x : Int) : (x : BitVec w).toInt = Int.bmod x (2^w) := by
|
||
rw [toInt_eq_toNat_bmod, toNat_intCast, Int.natCast_toNat_eq_self.mpr]
|
||
· have h : (2 ^ w : Int) = (2 ^ w : Nat) := by simp
|
||
rw [h, Int.emod_bmod]
|
||
· apply Int.emod_nonneg
|
||
exact Int.pow_ne_zero (by decide)
|
||
|
||
/-! ### sle/slt -/
|
||
|
||
/--
|
||
A bitvector, when interpreted as an integer, is less than zero iff
|
||
its most significant bit is true.
|
||
-/
|
||
theorem slt_zero_iff_msb_cond {x : BitVec w} : x.slt 0#w ↔ x.msb = true := by
|
||
have := toInt_eq_msb_cond x
|
||
constructor
|
||
· intro h
|
||
apply Classical.byContradiction
|
||
intro hmsb
|
||
simp only [Bool.not_eq_true] at hmsb
|
||
simp only [hmsb, Bool.false_eq_true, ↓reduceIte] at this
|
||
simp only [BitVec.slt, toInt_zero, decide_eq_true_eq] at h
|
||
omega /- Can't have `x.toInt` which is equal to `x.toNat` be strictly less than zero -/
|
||
· intro h
|
||
simp only [h, ↓reduceIte] at this
|
||
simp only [BitVec.slt, this, toInt_zero, decide_eq_true_eq]
|
||
omega
|
||
|
||
theorem slt_zero_eq_msb {w : Nat} {x : BitVec w} : x.slt 0#w = x.msb := by
|
||
grind [BitVec.slt_zero_iff_msb_cond]
|
||
|
||
theorem sle_eq_decide {x y : BitVec w} : x.sle y = decide (x.toInt ≤ y.toInt) := rfl
|
||
|
||
theorem slt_eq_decide {x y : BitVec w} : x.slt y = decide (x.toInt < y.toInt) := rfl
|
||
|
||
theorem ule_eq_decide {x y : BitVec w} : x.ule y = decide (x.toNat ≤ y.toNat) := rfl
|
||
|
||
theorem ult_eq_decide {x y : BitVec w} : x.ult y = decide (x.toNat < y.toNat) := rfl
|
||
|
||
theorem ule_eq_decide_le {x y : BitVec w} : x.ule y = decide (x ≤ y) := rfl
|
||
|
||
theorem ult_eq_decide_lt {x y : BitVec w} : x.ult y = decide (x < y) := rfl
|
||
|
||
theorem ule_iff_le {x y : BitVec w} : x.ule y ↔ x ≤ y :=
|
||
decide_eq_true_iff
|
||
|
||
theorem ult_iff_lt {x y : BitVec w} : x.ult y ↔ x < y :=
|
||
decide_eq_true_iff
|
||
|
||
theorem sle_iff_toInt_le {w : Nat} {x y : BitVec w} : x.sle y ↔ x.toInt ≤ y.toInt :=
|
||
decide_eq_true_iff
|
||
|
||
theorem slt_iff_toInt_lt {w : Nat} {x y : BitVec w} : x.slt y ↔ x.toInt < y.toInt :=
|
||
decide_eq_true_iff
|
||
|
||
theorem ule_iff_toNat_le {x y : BitVec w} : x.ule y ↔ x.toNat ≤ y.toNat :=
|
||
decide_eq_true_iff
|
||
|
||
theorem ult_iff_toNat_lt {x y : BitVec w} : x.ult y ↔ x.toNat < y.toNat :=
|
||
decide_eq_true_iff
|
||
|
||
theorem sle_eq_slt_or_eq {x y : BitVec w} : x.sle y = (x.slt y || x == y) := by
|
||
apply Bool.eq_iff_iff.2
|
||
simp only [BitVec.sle, decide_eq_true_eq, BitVec.slt, Bool.or_eq_true, beq_iff_eq, ← toInt_inj]
|
||
omega
|
||
|
||
theorem slt_eq_sle_and_ne {x y : BitVec w} : x.slt y = (x.sle y && x != y) := by
|
||
apply Bool.eq_iff_iff.2
|
||
simp [BitVec.slt, BitVec.sle, Int.lt_iff_le_and_ne, BitVec.toInt_inj]
|
||
|
||
/-- For all bitvectors `x, y`, either `x` is signed less than `y`,
|
||
or is equal to `y`, or is signed greater than `y`. -/
|
||
theorem slt_trichotomy (x y : BitVec w) : x.slt y ∨ x = y ∨ y.slt x := by
|
||
simpa [slt_iff_toInt_lt, ← toInt_inj]
|
||
using Int.lt_trichotomy x.toInt y.toInt
|
||
|
||
/-- For all bitvectors `x, y`, either `x` is unsigned less than `y`,
|
||
or is equal to `y`, or is unsigned greater than `y`. -/
|
||
theorem lt_trichotomy (x y : BitVec w) :
|
||
x < y ∨ x = y ∨ y < x := by
|
||
simpa [← ult_iff_lt, ult_eq_decide, decide_eq_true_eq, ← toNat_inj]
|
||
using Nat.lt_trichotomy x.toNat y.toNat
|
||
|
||
/-! ### setWidth, zeroExtend and truncate -/
|
||
|
||
theorem truncate_eq_setWidth {v : Nat} {x : BitVec w} :
|
||
truncate v x = setWidth v x := rfl
|
||
|
||
theorem zeroExtend_eq_setWidth {v : Nat} {x : BitVec w} :
|
||
zeroExtend v x = setWidth v x := rfl
|
||
|
||
theorem toInt_setWidth (x : BitVec w) :
|
||
(x.setWidth v).toInt = Int.bmod x.toNat (2^v) := by
|
||
grind [toInt_eq_toNat_bmod, Int.emod_bmod]
|
||
|
||
theorem toFin_setWidth {x : BitVec w} :
|
||
(x.setWidth v).toFin = Fin.ofNat (2^v) x.toNat := by
|
||
ext; grind
|
||
|
||
theorem setWidth_eq (x : BitVec n) : setWidth n x = x := by grind
|
||
|
||
theorem setWidth_zero (m n : Nat) : setWidth m 0#n = 0#m := by grind
|
||
|
||
/-- Moves one-sided left toNat equality to BitVec equality. -/
|
||
theorem toNat_eq_nat {x : BitVec w} {y : Nat} :
|
||
(x.toNat = y) ↔ (y < 2^w ∧ (x = BitVec.ofNat w y)) := by
|
||
apply Iff.intro
|
||
· intro eq
|
||
simp [←eq, x.isLt]
|
||
· intro eq
|
||
simp [Nat.mod_eq_of_lt, eq]
|
||
|
||
grind_pattern toNat_eq_nat => BitVec.ofNat w y, x.toNat
|
||
|
||
/-- Moves one-sided right toNat equality to BitVec equality. -/
|
||
theorem nat_eq_toNat {x : BitVec w} {y : Nat} :
|
||
(y = x.toNat) ↔ (y < 2^w ∧ (x = BitVec.ofNat w y)) := by
|
||
grind
|
||
|
||
theorem getElem?_setWidth' (x : BitVec w) (i : Nat) (h : w ≤ v) :
|
||
(setWidth' h x)[i]? = if i < v then some (x.getLsbD i) else none := by grind
|
||
|
||
theorem getElem?_setWidth (m : Nat) (x : BitVec n) (i : Nat) :
|
||
(x.setWidth m)[i]? = if i < m then some (x.getLsbD i) else none := by grind
|
||
|
||
theorem getLsbD_setWidth' (ge : m ≥ n) (x : BitVec n) (i : Nat) :
|
||
getLsbD (setWidth' ge x) i = getLsbD x i := by grind
|
||
|
||
theorem getMsbD_setWidth' (ge : m ≥ n) (x : BitVec n) (i : Nat) :
|
||
getMsbD (setWidth' ge x) i = (decide (m - n ≤ i) && getMsbD x (i + n - m)) := by
|
||
grind (splits := 10)
|
||
|
||
theorem getLsbD_setWidth (m : Nat) (x : BitVec n) (i : Nat) :
|
||
getLsbD (setWidth m x) i = (decide (i < m) && getLsbD x i) := by grind
|
||
|
||
theorem getMsbD_setWidth {m : Nat} {x : BitVec n} {i : Nat} :
|
||
getMsbD (setWidth m x) i = (decide (m - n ≤ i) && getMsbD x (i + n - m)) := by
|
||
unfold setWidth
|
||
by_cases h : n ≤ m <;> grind
|
||
|
||
-- This is a simp lemma as there is only a runtime difference between `setWidth'` and `setWidth`,
|
||
-- and for verification purposes they are equivalent.
|
||
theorem setWidth'_eq {x : BitVec w} (h : w ≤ v) : x.setWidth' h = x.setWidth v := by
|
||
apply eq_of_toNat_eq
|
||
rw [toNat_setWidth, toNat_setWidth']
|
||
rw [Nat.mod_eq_of_lt]
|
||
exact Nat.lt_of_lt_of_le x.isLt (Nat.pow_le_pow_right (Nat.zero_lt_two) h)
|
||
|
||
theorem getMsbD_setWidth_add {x : BitVec w} (h : k ≤ i) :
|
||
(x.setWidth (w + k)).getMsbD i = x.getMsbD (i - k) := by
|
||
grind (splits := 12)
|
||
|
||
theorem cast_setWidth (h : v = v') (x : BitVec w) :
|
||
(x.setWidth v).cast h = x.setWidth v' := by grind
|
||
|
||
theorem setWidth_setWidth_of_le (x : BitVec w) (h : k ≤ l) :
|
||
(x.setWidth l).setWidth k = x.setWidth k := by grind
|
||
|
||
theorem setWidth_cast {x : BitVec w} {h : w = v} : (x.cast h).setWidth k = x.setWidth k := by grind
|
||
|
||
theorem msb_setWidth (x : BitVec w) : (x.setWidth v).msb = (decide (0 < v) && x.getLsbD (v - 1)) := by
|
||
grind [msb_eq_getLsbD_last]
|
||
|
||
theorem msb_setWidth' (x : BitVec w) (h : w ≤ v) : (x.setWidth' h).msb = (decide (0 < v) && x.getLsbD (v - 1)) := by grind
|
||
|
||
theorem msb_setWidth'' (x : BitVec w) : (x.setWidth (k + 1)).msb = x.getLsbD k := by grind
|
||
|
||
/-- zero extending a bitvector to width 1 equals the boolean of the lsb. -/
|
||
theorem setWidth_one_eq_ofBool_getLsb_zero (x : BitVec w) :
|
||
x.setWidth 1 = BitVec.ofBool (x.getLsbD 0) := by grind
|
||
|
||
/-- Zero extending `1#v` to `1#w` equals `1#w` when `v > 0`. -/
|
||
theorem setWidth_ofNat_one_eq_ofNat_one_of_lt {v w : Nat} (hv : 0 < v) :
|
||
(BitVec.ofNat v 1).setWidth w = BitVec.ofNat w 1 := by
|
||
ext i h
|
||
have hv := (@Nat.testBit_one_eq_true_iff_self_eq_zero i)
|
||
grind
|
||
|
||
/-- Truncating to width 1 produces a bitvector equal to the least significant bit. -/
|
||
theorem setWidth_one {x : BitVec w} :
|
||
x.setWidth 1 = ofBool (x.getLsbD 0) := by grind
|
||
|
||
theorem setWidth_ofNat_of_le (h : v ≤ w) (x : Nat) : setWidth v (BitVec.ofNat w x) = BitVec.ofNat v x := by
|
||
apply BitVec.eq_of_toNat_eq
|
||
simp only [toNat_setWidth, toNat_ofNat]
|
||
rw [Nat.mod_mod_of_dvd]
|
||
exact Nat.pow_dvd_pow_iff_le_right'.mpr h
|
||
|
||
/--
|
||
Iterated `setWidth` agrees with the second `setWidth`
|
||
except in the case the first `setWidth` is a non-trivial truncation,
|
||
and the second `setWidth` is a non-trivial extension.
|
||
-/
|
||
-- Note that in the special cases `v = u` or `v = w`,
|
||
-- `simp` can discharge the side condition itself.
|
||
theorem setWidth_setWidth {x : BitVec u} {w v : Nat} (h : ¬ (v < u ∧ v < w)) :
|
||
setWidth w (setWidth v x) = setWidth w x := by grind
|
||
|
||
theorem msb_setWidth'_of_lt {m n : Nat} (p : m < n) {x : BitVec m} :
|
||
(setWidth' (by omega : m ≤ n) x).msb = false := by grind
|
||
|
||
theorem toInt_setWidth'_of_lt {m n : Nat} (p : m < n) {x : BitVec m} :
|
||
(setWidth' (by omega : m ≤ n) x).toInt = x.toNat := by
|
||
grind [toInt_eq_toNat_of_msb]
|
||
|
||
theorem toInt_setWidth' {m n : Nat} (p : m ≤ n) {x : BitVec m} :
|
||
(setWidth' p x).toInt = if m = n then x.toInt else x.toNat := by grind
|
||
|
||
theorem toFin_setWidth' {m n : Nat} (p : m ≤ n) (x : BitVec m) :
|
||
(setWidth' p x).toFin = x.toFin.castLE (Nat.pow_le_pow_right (by omega) (by omega)) := by
|
||
ext
|
||
grind [Nat.mod_eq_of_lt]
|
||
|
||
/-! ## extractLsb -/
|
||
|
||
protected theorem extractLsb_ofFin {n} (x : Fin (2^n)) (hi lo : Nat) :
|
||
extractLsb hi lo (@BitVec.ofFin n x) = .ofNat (hi-lo+1) (x.val >>> lo) := rfl
|
||
|
||
protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
|
||
extractLsb hi lo (BitVec.ofNat n x) = .ofNat (hi - lo + 1) ((x % 2^n) >>> lo) := by
|
||
ext i
|
||
grind [BitVec.ofNat]
|
||
|
||
theorem extractLsb'_toNat (s m : Nat) (x : BitVec n) :
|
||
(extractLsb' s m x).toNat = (x.toNat >>> s) % 2^m := rfl
|
||
|
||
theorem extractLsb_toNat (hi lo : Nat) (x : BitVec n) :
|
||
(extractLsb hi lo x).toNat = (x.toNat >>> lo) % 2^(hi-lo+1) := rfl
|
||
|
||
theorem toInt_extractLsb' {s m : Nat} {x : BitVec n} :
|
||
(extractLsb' s m x).toInt = ((x.toNat >>> s) : Int).bmod (2 ^ m) := by
|
||
simp [extractLsb', toInt_ofNat']
|
||
|
||
theorem toInt_extractLsb {hi lo : Nat} {x : BitVec n} :
|
||
(extractLsb hi lo x).toInt = ((x.toNat >>> lo) : Int).bmod (2 ^ (hi - lo + 1)) := by
|
||
simp [extractLsb]
|
||
|
||
theorem toFin_extractLsb' {s m : Nat} {x : BitVec n} :
|
||
(extractLsb' s m x).toFin = Fin.ofNat (2 ^ m) (x.toNat >>> s) := by
|
||
simp [extractLsb']
|
||
|
||
theorem toFin_extractLsb {hi lo : Nat} {x : BitVec n} :
|
||
(extractLsb hi lo x).toFin = Fin.ofNat (2 ^ (hi - lo + 1)) (x.toNat >>> lo) := by
|
||
simp [extractLsb]
|
||
|
||
theorem getElem_extractLsb' {start len : Nat} {x : BitVec n} {i : Nat} (h : i < len) :
|
||
(extractLsb' start len x)[i] = x.getLsbD (start+i) := by
|
||
grind [getElem_eq_testBit_toNat, getLsbD]
|
||
|
||
theorem getLsbD_extractLsb' (start len : Nat) (x : BitVec n) (i : Nat) :
|
||
(extractLsb' start len x).getLsbD i = (i < len && x.getLsbD (start+i)) := by
|
||
grind [getLsbD]
|
||
|
||
/--
|
||
Get the most significant bit after `extractLsb'`. With `extractLsb'`, we extract
|
||
a `BitVec len` `x'` with length `len` from `BitVec w` `x`, starting from the
|
||
element at position `start`. The function `getMsb` extracts a bit counting from
|
||
the most significant bit. Assuming certain conditions,
|
||
`(@extractLsbD' w x start len).getMsbD i` is equal to
|
||
`@getMsbD w x (w - (start + len - i))`.
|
||
|
||
Example (w := 10, start := 3, len := 4):
|
||
|
||
|---| = w - (start + len) = 3
|
||
|start + len| = 7
|
||
|start| = 3
|
||
| len | = 4
|
||
let x = 9 8 7 6 5 4 3 2 1 0
|
||
let x' = x.extractLsb' 3 4 = 6 5 4 3
|
||
| |
|
||
| x'.getMsbD 1 =
|
||
x.getMsbD (i := w - (start + len - i) = 10 - (3 + 4 - 1) = 4)
|
||
|
|
||
x'.getMsbD 0 =
|
||
x.getMsbD (i := w - (start + len - i) = 10 - (3 + 4 - 0) = 3)
|
||
|
||
# Condition 1: `i < len`
|
||
|
||
The index `i` must be within the range of `len`.
|
||
|
||
# Condition 2: `start + len - i ≤ w`
|
||
|
||
If `start + len` is larger than `w`, the high bits at `i` with `w ≤ i` are filled with 0,
|
||
meaning that `getMsbD[i] = false` for these `i`.
|
||
If `i` is large enough, `getMsbD[i]` is again within the bounds `x`.
|
||
The precise condition is:
|
||
|
||
`start + len - i ≤ w`
|
||
|
||
Example (w := 10, start := 7, len := 5):
|
||
|
||
|= w - (start + len) = 0
|
||
| start + len | = 12
|
||
| start | = 7
|
||
| len | = 5
|
||
let x = 9 8 7 6 5 4 3 2 1 0
|
||
let x' = x.extractLsb' 7 5 = _ _ 9 8 7
|
||
| |
|
||
| x'.getMsbD (i := 2) =
|
||
| x.getMsbD (i := w - (start + len - i) = 10 - (7 + 5 - 2)) =
|
||
| x.getMsbD 0
|
||
| ✅ start + len - i ≤ w
|
||
| 7 + 5 - 2 = 10 ≤ 10
|
||
|
|
||
x'.getMsbD (i := 0) =
|
||
x.getMsbD (i := w - (start + len - i) = 10 - (7 + 5 - 0)) =
|
||
x.getMsbD (i := w - (start + len - i) = x.getMsbD (i := -2) -- in Nat becomes 0
|
||
❌ start + len - i ≤ w
|
||
7 + 5 - 0 ≤ w
|
||
-/
|
||
theorem getMsbD_extractLsb' {start len : Nat} {x : BitVec w} {i : Nat} :
|
||
(extractLsb' start len x).getMsbD i =
|
||
(decide (i < len) &&
|
||
(decide (start + len - i ≤ w) &&
|
||
x.getMsbD (w - (start + len - i)))) := by
|
||
grind (splits := 13)
|
||
|
||
theorem msb_extractLsb' {start len : Nat} {x : BitVec w} :
|
||
(extractLsb' start len x).msb =
|
||
(decide (0 < len) &&
|
||
(decide (start + len ≤ w) &&
|
||
x.getMsbD (w - (start + len)))) := by
|
||
simp [BitVec.msb]
|
||
|
||
theorem getElem_extract {hi lo : Nat} {x : BitVec n} {i : Nat} (h : i < hi - lo + 1) :
|
||
(extractLsb hi lo x)[i] = getLsbD x (lo+i) := by
|
||
grind [getElem_eq_testBit_toNat, getLsbD]
|
||
|
||
theorem getLsbD_extract (hi lo : Nat) (x : BitVec n) (i : Nat) :
|
||
getLsbD (extractLsb hi lo x) i = (i ≤ (hi-lo) && getLsbD x (lo+i)) := by grind
|
||
|
||
theorem getLsbD_extractLsb {hi lo : Nat} {x : BitVec n} {i : Nat} :
|
||
(extractLsb hi lo x).getLsbD i = (decide (i < hi - lo + 1) && x.getLsbD (lo + i)) := by grind
|
||
|
||
theorem getMsbD_extractLsb {hi lo : Nat} {x : BitVec w} {i : Nat} :
|
||
(extractLsb hi lo x).getMsbD i =
|
||
(decide (i < hi - lo + 1) &&
|
||
(decide (max hi lo - i < w) &&
|
||
x.getMsbD (w - 1 - (max hi lo - i)))) := by
|
||
grind (splits := 14)
|
||
|
||
theorem msb_extractLsb {hi lo : Nat} {x : BitVec w} :
|
||
(extractLsb hi lo x).msb = (decide (max hi lo < w) && x.getMsbD (w - 1 - max hi lo)) := by
|
||
simp [BitVec.msb]
|
||
|
||
theorem extractLsb'_eq_extractLsb {w : Nat} (x : BitVec w) (start len : Nat) (h : len > 0) :
|
||
x.extractLsb' start len = (x.extractLsb (len - 1 + start) start).cast (by omega) := by grind
|
||
|
||
/-- Extracting all the bits of a bitvector is an identity operation. -/
|
||
theorem extractLsb'_eq_self {x : BitVec w} : x.extractLsb' 0 w = x := by grind
|
||
|
||
theorem getLsbD_eq_extractLsb' (x : BitVec w) (i : Nat) :
|
||
x.getLsbD i = (x.extractLsb' i 1 == 1#1) := by
|
||
rw [Bool.eq_iff_iff]
|
||
simp [BitVec.eq_of_getLsbD_eq_iff]
|
||
|
||
theorem getElem_eq_extractLsb' (x : BitVec w) (i : Nat) (h : i < w) :
|
||
x[i] = (x.extractLsb' i 1 == 1#1) := by
|
||
rw [← getLsbD_eq_getElem, getLsbD_eq_extractLsb']
|
||
|
||
theorem extractLsb'_zero {w start len : Nat} : (0#w).extractLsb' start len = 0#len := by grind
|
||
|
||
theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
|
||
x.extractLsb' start 0 = 0#0 := by grind
|
||
|
||
/-! ### allOnes -/
|
||
|
||
theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
|
||
grind [allOnes]
|
||
|
||
theorem toInt_allOnes : (allOnes w).toInt = if 0 < w then -1 else 0 := by
|
||
by_cases h : w = 0
|
||
· grind
|
||
· have : 1 < 2 ^ w := by simp [h]
|
||
grind [BitVec.toInt]
|
||
|
||
theorem toFin_allOnes : (allOnes w).toFin = Fin.ofNat (2^w) (2^w - 1) := by
|
||
ext
|
||
simp
|
||
|
||
theorem getLsbD_allOnes : (allOnes v).getLsbD i = decide (i < v) := by
|
||
simp [allOnes]
|
||
|
||
theorem getMsbD_allOnes : (allOnes v).getMsbD i = decide (i < v) := by grind
|
||
|
||
theorem getElem_allOnes (i : Nat) (h : i < v) : (allOnes v)[i] = true := by
|
||
grind [getElem_eq_testBit_toNat]
|
||
|
||
theorem ofFin_add_rev (x : Fin (2^n)) : ofFin (x + x.rev) = allOnes n := by
|
||
ext
|
||
simp only [Fin.rev, getElem_ofFin, getElem_allOnes]
|
||
rw [Fin.add_def]
|
||
simp only [Nat.testBit_mod_two_pow]
|
||
have h : (x : Nat) + (2 ^ n - (x + 1)) = 2 ^ n - 1 := by omega
|
||
rw [h, Nat.testBit_two_pow_sub_one]
|
||
grind
|
||
|
||
theorem msb_allOnes (hw : 0 < w) : (allOnes w).msb = true := by grind
|
||
|
||
/-! ### or -/
|
||
|
||
theorem toNat_or (x y : BitVec v) :
|
||
BitVec.toNat (x ||| y) = BitVec.toNat x ||| BitVec.toNat y := rfl
|
||
|
||
theorem toInt_or (x y : BitVec w) :
|
||
BitVec.toInt (x ||| y) = Int.bmod (BitVec.toNat x ||| BitVec.toNat y) (2^w) := by
|
||
rw_mod_cast [Int.bmod_def, BitVec.toInt, toNat_or, Nat.mod_eq_of_lt
|
||
(Nat.or_lt_two_pow (BitVec.isLt x) (BitVec.isLt y))]
|
||
grind
|
||
|
||
theorem toFin_or (x y : BitVec v) :
|
||
BitVec.toFin (x ||| y) = BitVec.toFin x ||| BitVec.toFin y := by
|
||
ext
|
||
exact (Nat.mod_eq_of_lt <| Nat.or_lt_two_pow x.isLt y.isLt).symm
|
||
|
||
theorem getLsbD_or {x y : BitVec v} : (x ||| y).getLsbD i = (x.getLsbD i || y.getLsbD i) := by
|
||
grind [testBit_toNat, getLsbD]
|
||
|
||
theorem getMsbD_or {x y : BitVec w} : (x ||| y).getMsbD i = (x.getMsbD i || y.getMsbD i) := by grind
|
||
|
||
theorem getElem_or {x y : BitVec w} {i : Nat} (h : i < w) : (x ||| y)[i] = (x[i] || y[i]) := by
|
||
grind [getElem_eq_testBit_toNat]
|
||
|
||
theorem msb_or {x y : BitVec w} : (x ||| y).msb = (x.msb || y.msb) := by grind
|
||
|
||
theorem setWidth_or {x y : BitVec w} :
|
||
(x ||| y).setWidth k = x.setWidth k ||| y.setWidth k := by grind
|
||
|
||
theorem or_assoc (x y z : BitVec w) :
|
||
x ||| y ||| z = x ||| (y ||| z) := by grind
|
||
instance : Std.Associative (α := BitVec n) (· ||| ·) := ⟨BitVec.or_assoc⟩
|
||
|
||
theorem or_comm (x y : BitVec w) :
|
||
x ||| y = y ||| x := by grind
|
||
instance : Std.Commutative (fun (x y : BitVec w) => x ||| y) := ⟨BitVec.or_comm⟩
|
||
|
||
theorem or_self {x : BitVec w} : x ||| x = x := by grind
|
||
|
||
instance : Std.IdempotentOp (α := BitVec n) (· ||| · ) where
|
||
idempotent _ := BitVec.or_self
|
||
|
||
theorem or_zero {x : BitVec w} : x ||| 0#w = x := by grind
|
||
|
||
instance : Std.LawfulCommIdentity (α := BitVec n) (· ||| · ) (0#n) where
|
||
right_id _ := BitVec.or_zero
|
||
|
||
theorem zero_or {x : BitVec w} : 0#w ||| x = x := by grind
|
||
|
||
theorem ofBool_or_ofBool : ofBool b ||| ofBool b' = ofBool (b || b') := by grind
|
||
|
||
theorem or_allOnes {x : BitVec w} : x ||| allOnes w = allOnes w := by grind
|
||
|
||
theorem allOnes_or {x : BitVec w} : allOnes w ||| x = allOnes w := by grind
|
||
|
||
theorem or_eq_zero_iff {x y : BitVec w} : (x ||| y) = 0#w ↔ x = 0#w ∧ y = 0#w := by grind
|
||
|
||
theorem extractLsb'_or {x y : BitVec w} {start len : Nat} :
|
||
(x ||| y).extractLsb' start len = (x.extractLsb' start len) ||| (y.extractLsb' start len) := by grind
|
||
|
||
theorem extractLsb_or {x : BitVec w} {hi lo : Nat} :
|
||
(x ||| y).extractLsb lo hi = (x.extractLsb lo hi) ||| (y.extractLsb lo hi) := by grind
|
||
|
||
theorem ofNat_or {x y : Nat} : BitVec.ofNat w (x ||| y) = BitVec.ofNat w x ||| BitVec.ofNat w y :=
|
||
eq_of_toNat_eq (by simp [Nat.or_mod_two_pow])
|
||
|
||
/-! ### and -/
|
||
|
||
theorem toNat_and (x y : BitVec v) :
|
||
BitVec.toNat (x &&& y) = BitVec.toNat x &&& BitVec.toNat y := rfl
|
||
|
||
theorem toInt_and (x y : BitVec w) :
|
||
BitVec.toInt (x &&& y) = Int.bmod (BitVec.toNat x &&& BitVec.toNat y) (2^w) := by
|
||
rw_mod_cast [Int.bmod_def, BitVec.toInt, toNat_and, Nat.mod_eq_of_lt
|
||
(Nat.and_lt_two_pow x.toNat (BitVec.isLt y))]
|
||
omega
|
||
|
||
theorem toFin_and (x y : BitVec v) :
|
||
BitVec.toFin (x &&& y) = BitVec.toFin x &&& BitVec.toFin y := by
|
||
apply Fin.eq_of_val_eq
|
||
exact (Nat.mod_eq_of_lt <| Nat.and_lt_two_pow _ y.isLt).symm
|
||
|
||
theorem getLsbD_and {x y : BitVec v} : (x &&& y).getLsbD i = (x.getLsbD i && y.getLsbD i) := by
|
||
grind [testBit_toNat, getLsbD]
|
||
|
||
theorem getMsbD_and {x y : BitVec w} : (x &&& y).getMsbD i = (x.getMsbD i && y.getMsbD i) := by grind
|
||
|
||
theorem getElem_and {x y : BitVec w} {i : Nat} (h : i < w) : (x &&& y)[i] = (x[i] && y[i]) := by
|
||
grind [getElem_eq_testBit_toNat]
|
||
|
||
theorem msb_and {x y : BitVec w} : (x &&& y).msb = (x.msb && y.msb) := by grind
|
||
|
||
theorem setWidth_and {x y : BitVec w} :
|
||
(x &&& y).setWidth k = x.setWidth k &&& y.setWidth k := by grind
|
||
|
||
theorem and_assoc (x y z : BitVec w) :
|
||
x &&& y &&& z = x &&& (y &&& z) := by grind
|
||
instance : Std.Associative (α := BitVec n) (· &&& ·) := ⟨BitVec.and_assoc⟩
|
||
|
||
theorem and_comm (x y : BitVec w) :
|
||
x &&& y = y &&& x := by grind
|
||
instance : Std.Commutative (fun (x y : BitVec w) => x &&& y) := ⟨BitVec.and_comm⟩
|
||
|
||
theorem and_self {x : BitVec w} : x &&& x = x := by grind
|
||
|
||
instance : Std.IdempotentOp (α := BitVec n) (· &&& · ) where
|
||
idempotent _ := BitVec.and_self
|
||
|
||
theorem and_zero {x : BitVec w} : x &&& 0#w = 0#w := by grind
|
||
|
||
theorem zero_and {x : BitVec w} : 0#w &&& x = 0#w := by grind
|
||
|
||
theorem ofBool_and_ofBool : ofBool b &&& ofBool b' = ofBool (b && b') := by grind
|
||
|
||
theorem and_allOnes {x : BitVec w} : x &&& allOnes w = x := by grind
|
||
|
||
instance : Std.LawfulCommIdentity (α := BitVec n) (· &&& · ) (allOnes n) where
|
||
right_id _ := BitVec.and_allOnes
|
||
|
||
theorem allOnes_and {x : BitVec w} : allOnes w &&& x = x := by grind
|
||
|
||
theorem and_eq_allOnes_iff {x y : BitVec w} :
|
||
x &&& y = allOnes w ↔ x = allOnes w ∧ y = allOnes w := by grind
|
||
|
||
theorem extractLsb'_and {x y : BitVec w} {start len : Nat} :
|
||
(x &&& y).extractLsb' start len = (x.extractLsb' start len) &&& (y.extractLsb' start len) := by grind
|
||
|
||
theorem extractLsb_and {x : BitVec w} {hi lo : Nat} :
|
||
(x &&& y).extractLsb lo hi = (x.extractLsb lo hi) &&& (y.extractLsb lo hi) := by grind
|
||
|
||
theorem ofNat_and {x y : Nat} : BitVec.ofNat w (x &&& y) = BitVec.ofNat w x &&& BitVec.ofNat w y :=
|
||
eq_of_toNat_eq (by simp [Nat.and_mod_two_pow])
|
||
|
||
/-! ### xor -/
|
||
|
||
theorem toNat_xor (x y : BitVec v) :
|
||
BitVec.toNat (x ^^^ y) = BitVec.toNat x ^^^ BitVec.toNat y := rfl
|
||
|
||
theorem toInt_xor (x y : BitVec w) :
|
||
BitVec.toInt (x ^^^ y) = Int.bmod (BitVec.toNat x ^^^ BitVec.toNat y) (2^w) := by
|
||
rw_mod_cast [Int.bmod_def, BitVec.toInt, toNat_xor, Nat.mod_eq_of_lt
|
||
(Nat.xor_lt_two_pow (BitVec.isLt x) (BitVec.isLt y))]
|
||
omega
|
||
|
||
theorem toFin_xor (x y : BitVec v) :
|
||
BitVec.toFin (x ^^^ y) = BitVec.toFin x ^^^ BitVec.toFin y := by
|
||
apply Fin.eq_of_val_eq
|
||
exact (Nat.mod_eq_of_lt <| Nat.xor_lt_two_pow x.isLt y.isLt).symm
|
||
|
||
theorem getLsbD_xor {x y : BitVec v} :
|
||
(x ^^^ y).getLsbD i = ((x.getLsbD i) ^^ (y.getLsbD i)) := by
|
||
grind [testBit_toNat, getLsbD]
|
||
|
||
theorem getMsbD_xor {x y : BitVec w} :
|
||
(x ^^^ y).getMsbD i = (x.getMsbD i ^^ y.getMsbD i) := by grind
|
||
|
||
theorem getElem_xor {x y : BitVec w} {i : Nat} (h : i < w) : (x ^^^ y)[i] = (x[i] ^^ y[i]) := by
|
||
grind [getElem_eq_testBit_toNat]
|
||
|
||
theorem msb_xor {x y : BitVec w} :
|
||
(x ^^^ y).msb = (x.msb ^^ y.msb) := by grind
|
||
|
||
theorem setWidth_xor {x y : BitVec w} :
|
||
(x ^^^ y).setWidth k = x.setWidth k ^^^ y.setWidth k := by grind
|
||
|
||
theorem xor_assoc (x y z : BitVec w) :
|
||
x ^^^ y ^^^ z = x ^^^ (y ^^^ z) := by grind
|
||
instance : Std.Associative (fun (x y : BitVec w) => x ^^^ y) := ⟨BitVec.xor_assoc⟩
|
||
|
||
theorem xor_comm (x y : BitVec w) :
|
||
x ^^^ y = y ^^^ x := by grind
|
||
instance : Std.Commutative (fun (x y : BitVec w) => x ^^^ y) := ⟨BitVec.xor_comm⟩
|
||
|
||
theorem xor_self {x : BitVec w} : x ^^^ x = 0#w := by grind
|
||
|
||
theorem xor_zero {x : BitVec w} : x ^^^ 0#w = x := by grind
|
||
|
||
instance : Std.LawfulCommIdentity (α := BitVec n) (· ^^^ · ) (0#n) where
|
||
right_id _ := BitVec.xor_zero
|
||
|
||
theorem zero_xor {x : BitVec w} : 0#w ^^^ x = x := by grind
|
||
|
||
theorem xor_left_inj {x y : BitVec w} (z : BitVec w) : (x ^^^ z = y ^^^ z) ↔ x = y := by
|
||
constructor
|
||
· intro h
|
||
ext i ih
|
||
have := BitVec.eq_of_getElem_eq_iff.mp h i
|
||
simp only [getElem_xor, Bool.xor_left_inj] at this
|
||
exact this ih
|
||
· grind
|
||
|
||
theorem xor_right_inj {x y : BitVec w} (z : BitVec w) : (z ^^^ x = z ^^^ y) ↔ x = y := by
|
||
grind [xor_comm, xor_left_inj]
|
||
|
||
theorem xor_eq_zero_iff {x y : BitVec w} : (x ^^^ y = 0#w) ↔ x = y := by
|
||
constructor
|
||
· intro h
|
||
apply (xor_left_inj y).mp
|
||
grind
|
||
· grind
|
||
|
||
theorem ofBool_xor_ofBool : ofBool b ^^^ ofBool b' = ofBool (b ^^ b') := by grind
|
||
|
||
theorem extractLsb'_xor {x y : BitVec w} {start len : Nat} :
|
||
(x ^^^ y).extractLsb' start len = (x.extractLsb' start len) ^^^ (y.extractLsb' start len) := by grind
|
||
|
||
theorem extractLsb_xor {x : BitVec w} {hi lo : Nat} :
|
||
(x ^^^ y).extractLsb lo hi = (x.extractLsb lo hi) ^^^ (y.extractLsb lo hi) := by grind
|
||
|
||
theorem ofNat_xor {x y : Nat} : BitVec.ofNat w (x ^^^ y) = BitVec.ofNat w x ^^^ BitVec.ofNat w y :=
|
||
eq_of_toNat_eq (by simp [Nat.xor_mod_two_pow])
|
||
|
||
/-! ### not -/
|
||
|
||
theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_not {x : BitVec v} : (~~~x).toNat = 2^v - 1 - x.toNat := by
|
||
rw [Nat.sub_sub, Nat.add_comm, not_def, toNat_xor]
|
||
apply Nat.eq_of_testBit_eq
|
||
intro i
|
||
simp only [toNat_allOnes, Nat.testBit_xor, Nat.testBit_two_pow_sub_one]
|
||
match h : BitVec.toNat x with
|
||
| 0 => simp
|
||
| y+1 =>
|
||
rw [Nat.succ_eq_add_one] at h
|
||
rw [← h]
|
||
rw [Nat.testBit_two_pow_sub_succ (isLt _)]
|
||
· cases w : decide (i < v)
|
||
· simp only [decide_eq_false_iff_not, Nat.not_lt] at w
|
||
simp only [Bool.false_bne, Bool.false_and]
|
||
rw [Nat.testBit_lt_two_pow]
|
||
calc BitVec.toNat x < 2 ^ v := isLt _
|
||
_ ≤ 2 ^ i := Nat.pow_le_pow_right Nat.zero_lt_two w
|
||
· grind
|
||
|
||
theorem toInt_not {x : BitVec w} :
|
||
(~~~x).toInt = Int.bmod (2^w - 1 - x.toNat) (2^w) := by
|
||
rw_mod_cast [BitVec.toInt, BitVec.toNat_not, Int.bmod_def]
|
||
simp [show ((2^w : Nat) : Int) - 1 - x.toNat = ((2^w - 1 - x.toNat) : Nat) by omega,
|
||
-Int.natCast_pow]
|
||
rw_mod_cast [Nat.mod_eq_of_lt (by omega)]
|
||
omega
|
||
|
||
theorem ofInt_negSucc_eq_not_ofNat {w n : Nat} :
|
||
BitVec.ofInt w (Int.negSucc n) = ~~~.ofNat w n := by
|
||
simp only [BitVec.ofInt, Int.toNat, Int.ofNat_eq_coe, toNat_eq, toNat_ofNatLT, toNat_not,
|
||
toNat_ofNat]
|
||
cases h : Int.negSucc n % ((2 ^ w : Nat) : Int)
|
||
case ofNat =>
|
||
rw [Int.ofNat_eq_coe, Int.negSucc_emod] at h
|
||
· dsimp only
|
||
omega
|
||
· omega
|
||
case negSucc a =>
|
||
have neg := Int.negSucc_lt_zero a
|
||
have _ : 0 ≤ Int.negSucc n % ((2 ^ w : Nat) : Int) := Int.emod_nonneg _ (by omega)
|
||
omega
|
||
|
||
theorem toFin_not (x : BitVec w) :
|
||
(~~~x).toFin = x.toFin.rev := by
|
||
apply Fin.val_inj.mp
|
||
grind
|
||
|
||
theorem getLsbD_not {x : BitVec v} : (~~~x).getLsbD i = (decide (i < v) && ! x.getLsbD i) := by
|
||
grind [not_def]
|
||
|
||
theorem getMsbD_not {x : BitVec v} :
|
||
(~~~x).getMsbD i = (decide (i < v) && ! x.getMsbD i) := by grind
|
||
|
||
theorem getElem_not {x : BitVec w} {i : Nat} (h : i < w) : (~~~x)[i] = !x[i] := by
|
||
grind [getElem_eq_testBit_toNat]
|
||
|
||
theorem setWidth_not {x : BitVec w} (_ : k ≤ w) :
|
||
(~~~x).setWidth k = ~~~(x.setWidth k) := by grind
|
||
|
||
theorem not_zero : ~~~(0#n) = allOnes n := by grind
|
||
|
||
theorem not_ofBool : ~~~ (ofBool b) = ofBool (!b) := by grind
|
||
|
||
theorem not_allOnes : ~~~ allOnes w = 0#w := by grind
|
||
|
||
theorem xor_allOnes {x : BitVec w} : x ^^^ allOnes w = ~~~ x := by grind
|
||
|
||
theorem allOnes_xor {x : BitVec w} : allOnes w ^^^ x = ~~~ x := by grind
|
||
|
||
theorem not_not {b : BitVec w} : ~~~(~~~b) = b := by grind
|
||
|
||
protected theorem not_inj {x y : BitVec w} : ~~~x = ~~~y ↔ x = y :=
|
||
⟨fun h => by rw [← @not_not w x, ← @not_not w y, h], congrArg _⟩
|
||
|
||
theorem and_not_self (x : BitVec w) : x &&& ~~~x = 0 := by grind
|
||
|
||
theorem not_and_self (x : BitVec w) : ~~~x &&& x = 0 := by grind
|
||
|
||
theorem or_not_self (x : BitVec w) : x ||| ~~~x = allOnes w := by grind
|
||
|
||
theorem not_or_self (x : BitVec w) : ~~~x ||| x = allOnes w := by grind
|
||
|
||
theorem not_eq_comm {x y : BitVec w} : ~~~ x = y ↔ x = ~~~ y := by grind
|
||
|
||
theorem msb_not {x : BitVec w} : (~~~x).msb = (decide (0 < w) && !x.msb) := by grind
|
||
|
||
/--
|
||
Negating `x` and then extracting [start..start+len) is the same as extracting and then negating,
|
||
as long as the range [start..start+len) is in bounds.
|
||
See that if the index is out-of-bounds, then `extractLsb` will return `false`,
|
||
which makes the operation not commute.
|
||
-/
|
||
theorem extractLsb'_not_of_lt {x : BitVec w} {start len : Nat} (h : start + len < w) :
|
||
(~~~ x).extractLsb' start len = ~~~ (x.extractLsb' start len) := by grind
|
||
|
||
/--
|
||
Negating `x` and then extracting [lo:hi] is the same as extracting and then negating.
|
||
For the extraction to be well-behaved,
|
||
we need the range [lo:hi] to be a valid closed interval inside the bitvector:
|
||
1. `lo ≤ hi` for the interval to be a well-formed closed interval.
|
||
2. `hi < w`, for the interval to be contained inside the bitvector.
|
||
-/
|
||
theorem extractLsb_not_of_lt {x : BitVec w} {hi lo : Nat} (hlo : lo ≤ hi) (hhi : hi < w) :
|
||
(~~~ x).extractLsb hi lo = ~~~ (x.extractLsb hi lo) := by grind
|
||
|
||
theorem ne_not_self {a : BitVec w} (h : 0 < w) : a ≠ ~~~a := by
|
||
have : ∃ x, x < w := ⟨w - 1, by omega⟩
|
||
simp [BitVec.eq_of_getElem_eq_iff, this]
|
||
|
||
theorem not_self_ne {a : BitVec w} (h : 0 < w) : ~~~a ≠ a := by
|
||
grind [ne_not_self]
|
||
|
||
theorem not_and {x y : BitVec w} : ~~~ (x &&& y) = ~~~ x ||| ~~~ y := by grind
|
||
|
||
theorem not_or {x y : BitVec w} : ~~~ (x ||| y) = ~~~ x &&& ~~~ y := by grind
|
||
|
||
theorem not_xor_left {x y : BitVec w} : ~~~ (x ^^^ y) = ~~~ x ^^^ y := by grind
|
||
|
||
theorem not_xor_right {x y : BitVec w} : ~~~ (x ^^^ y) = x ^^^ ~~~ y := by grind
|
||
|
||
/-! ### cast -/
|
||
|
||
theorem not_cast {x : BitVec w} (h : w = w') : ~~~(x.cast h) = (~~~x).cast h := by grind
|
||
|
||
theorem and_cast {x y : BitVec w} (h : w = w') : x.cast h &&& y.cast h = (x &&& y).cast h := by grind
|
||
|
||
theorem or_cast {x y : BitVec w} (h : w = w') : x.cast h ||| y.cast h = (x ||| y).cast h := by grind
|
||
|
||
theorem xor_cast {x y : BitVec w} (h : w = w') : x.cast h ^^^ y.cast h = (x ^^^ y).cast h := by grind
|
||
|
||
/-! ### shiftLeft -/
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_shiftLeft {x : BitVec v} :
|
||
(x <<< n).toNat = x.toNat <<< n % 2^v :=
|
||
BitVec.toNat_ofNat _ _
|
||
|
||
theorem toInt_shiftLeft {x : BitVec w} :
|
||
(x <<< n).toInt = (x.toNat <<< n : Int).bmod (2^w) := by
|
||
grind [toInt_eq_toNat_bmod, Int.emod_bmod]
|
||
|
||
theorem toFin_shiftLeft {n : Nat} (x : BitVec w) :
|
||
(x <<< n).toFin = Fin.ofNat (2^w) (x.toNat <<< n) := rfl
|
||
|
||
theorem shiftLeft_zero (x : BitVec w) : x <<< 0 = x := by
|
||
apply eq_of_toNat_eq
|
||
grind [toNat_mod_cancel]
|
||
|
||
theorem zero_shiftLeft (n : Nat) : 0#w <<< n = 0#w := by
|
||
simp [bitvec_to_nat]
|
||
|
||
theorem getLsbD_shiftLeft (x : BitVec m) (n) :
|
||
getLsbD (x <<< n) i = (decide (i < m) && !decide (i < n) && getLsbD x (i - n)) := by
|
||
grind [getLsbD]
|
||
|
||
theorem getElem_shiftLeft {x : BitVec m} {n : Nat} (h : i < m) :
|
||
(x <<< n)[i] = (!decide (i < n) && x[i - n]) := by
|
||
grind [getElem_eq_testBit_toNat]
|
||
|
||
theorem shiftLeft_xor_distrib (x y : BitVec w) (n : Nat) :
|
||
(x ^^^ y) <<< n = (x <<< n) ^^^ (y <<< n) := by grind
|
||
|
||
theorem shiftLeft_and_distrib (x y : BitVec w) (n : Nat) :
|
||
(x &&& y) <<< n = (x <<< n) &&& (y <<< n) := by grind
|
||
|
||
theorem shiftLeft_or_distrib (x y : BitVec w) (n : Nat) :
|
||
(x ||| y) <<< n = (x <<< n) ||| (y <<< n) := by grind
|
||
|
||
theorem getMsbD_shiftLeft (x : BitVec w) (i) :
|
||
(x <<< i).getMsbD k = x.getMsbD (k + i) := by grind
|
||
|
||
theorem shiftLeftZeroExtend_eq {x : BitVec w} :
|
||
shiftLeftZeroExtend x n = setWidth (w+n) x <<< n := by
|
||
apply eq_of_toNat_eq
|
||
rw [shiftLeftZeroExtend, setWidth]
|
||
split
|
||
· simp only [toNat_ofNatLT, toNat_shiftLeft, toNat_setWidth']
|
||
rw [Nat.mod_eq_of_lt]
|
||
rw [Nat.shiftLeft_eq, Nat.pow_add]
|
||
exact Nat.mul_lt_mul_of_pos_right x.isLt (Nat.two_pow_pos _)
|
||
· grind
|
||
|
||
theorem toNat_shiftLeftZeroExtend {x : BitVec w} :
|
||
(shiftLeftZeroExtend x n).toNat = x.toNat <<< n := by
|
||
rcases n with _|n
|
||
· grind [shiftLeftZeroExtend]
|
||
· simp only [shiftLeftZeroExtend_eq, toNat_shiftLeft, toNat_setWidth]
|
||
have := Nat.pow_lt_pow_of_lt (a := 2) (n := w) (m := w + (n + 1)) (by omega) (by omega)
|
||
have : x.toNat <<< (n + 1) < 2 ^ (w + (n + 1)) := by
|
||
rw [Nat.shiftLeft_eq, Nat.pow_add (m := w) (n := n + 1), Nat.mul_lt_mul_right (by apply Nat.two_pow_pos (w := n + 1))]
|
||
grind
|
||
rw [Nat.mod_eq_of_lt (by rw [Nat.mod_eq_of_lt (by omega)]; omega), Nat.mod_eq_of_lt (by omega)]
|
||
|
||
theorem toInt_shiftLeftZeroExtend {x : BitVec w} :
|
||
(shiftLeftZeroExtend x n).toInt = x.toInt * 2 ^ n := by
|
||
rw [shiftLeftZeroExtend_eq]
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· rcases n with _|n
|
||
· simp
|
||
· have := Nat.pow_pos (a := 2) (n := n + 1) (by omega)
|
||
have : x.toNat <<< (n + 1) < 2 ^ (w + 1 + (n + 1)) := by
|
||
rw [Nat.shiftLeft_eq, Nat.pow_add (a := 2) (m := w + 1) (n := n + 1), Nat.mul_lt_mul_right (by omega)]
|
||
omega
|
||
simp only [toInt_shiftLeft, toNat_setWidth, Nat.lt_add_right_iff_pos,
|
||
Nat.zero_lt_succ, toNat_mod_cancel_of_lt, Int.bmod_def]
|
||
by_cases hmsb : x.msb
|
||
· have hge := toNat_ge_of_msb_true hmsb
|
||
simp only [Nat.add_one_sub_one, ge_iff_le] at hge
|
||
rw [Int.emod_eq_of_lt (by norm_cast; rw [Nat.shiftLeft_eq]; omega) (by omega)]
|
||
rw_mod_cast [← Nat.add_assoc]
|
||
rw [show (2 ^ (w + 1 + n + 1) + 1) / 2 = 2 ^ (w + 1 + n) by omega, Int.natCast_pow,
|
||
Int.cast_ofNat_Int, Nat.shiftLeft_eq, Nat.add_assoc, Nat.pow_add (a := 2) (m := w) (n := 1 + n),
|
||
Nat.add_comm 1 n]
|
||
simp only [Nat.mul_lt_mul_right (by omega), show ¬x.toNat < 2 ^ w by omega, reduceIte,
|
||
Int.natCast_mul, Int.natCast_pow, Int.cast_ofNat_Int, toInt_eq_toNat_cond,
|
||
show ¬2 * x.toNat < 2 ^ (w + 1) by simp [Nat.pow_add, Nat.mul_comm (2 ^ w) 2, hge]]
|
||
norm_cast
|
||
simp [Int.natCast_mul, Int.natCast_pow, Int.cast_ofNat_Int, Int.sub_mul,
|
||
show w + (n + 1) + 1 = (w + 1) + (n + 1) by omega, Nat.pow_add]
|
||
· simp only [Bool.not_eq_true] at hmsb
|
||
have hle := toNat_lt_of_msb_false (x := x) hmsb
|
||
simp only [Nat.add_one_sub_one] at hle
|
||
rw [Int.emod_eq_of_lt (by norm_cast; rw [Nat.shiftLeft_eq]; omega) (by omega)]
|
||
rw_mod_cast [← Nat.add_assoc]
|
||
rw [show (2 ^ (w + 1 + n + 1) + 1) / 2 = 2 ^ (w + 1 + n) by omega, Int.natCast_pow,
|
||
Int.cast_ofNat_Int, Nat.shiftLeft_eq, Nat.add_assoc, Nat.pow_add (a := 2) (m := w) (n := 1 + n), Nat.add_comm 1 n]
|
||
simp [Nat.mul_lt_mul_right (b := x.toNat) (c := 2 ^ w) (a := 2 ^ (n + 1)) (by omega), hle,
|
||
reduceIte, Int.natCast_mul, Int.natCast_pow, Int.cast_ofNat_Int, toInt_eq_toNat_of_msb hmsb]
|
||
|
||
theorem toFin_shiftLeftZeroExtend {x : BitVec w} :
|
||
(shiftLeftZeroExtend x n).toFin = Fin.ofNat (2 ^ (w + n)) (x.toNat * 2 ^ n) := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero, shiftLeftZeroExtend_eq]
|
||
· have := Nat.pow_le_pow_of_le (a := 2) (n := w + 1) (m := w + 1 + n) (by omega) (by omega)
|
||
rw [shiftLeftZeroExtend_eq, toFin_shiftLeft, toNat_setWidth, Nat.mod_eq_of_lt (by omega), Nat.shiftLeft_eq]
|
||
|
||
theorem getElem_shiftLeftZeroExtend {x : BitVec m} {n : Nat} (h : i < m + n) :
|
||
(shiftLeftZeroExtend x n)[i] = if h' : i < n then false else x[i - n] := by grind
|
||
|
||
theorem getLsbD_shiftLeftZeroExtend (x : BitVec m) (n : Nat) :
|
||
getLsbD (shiftLeftZeroExtend x n) i = ((! decide (i < n)) && getLsbD x (i - n)) := by grind
|
||
|
||
theorem getMsbD_shiftLeftZeroExtend (x : BitVec m) (n : Nat) :
|
||
getMsbD (shiftLeftZeroExtend x n) i = getMsbD x i := by
|
||
grind (splits := 11)
|
||
|
||
theorem msb_shiftLeftZeroExtend (x : BitVec w) (i : Nat) :
|
||
(shiftLeftZeroExtend x i).msb = x.msb := by grind
|
||
|
||
theorem shiftLeft_add {w : Nat} (x : BitVec w) (n m : Nat) :
|
||
x <<< (n + m) = (x <<< n) <<< m := by grind
|
||
|
||
theorem allOnes_shiftLeft_and_shiftLeft {x : BitVec w} {n : Nat} :
|
||
BitVec.allOnes w <<< n &&& x <<< n = x <<< n := by grind
|
||
|
||
theorem allOnes_shiftLeft_or_shiftLeft {x : BitVec w} {n : Nat} :
|
||
BitVec.allOnes w <<< n ||| x <<< n = BitVec.allOnes w <<< n := by grind
|
||
|
||
theorem setWidth_shiftLeft_of_le {x : BitVec w} {y : Nat} (hi : i ≤ w) :
|
||
(x <<< y).setWidth i = x.setWidth i <<< y := by grind
|
||
|
||
/-! ### shiftLeft reductions from BitVec to Nat -/
|
||
|
||
theorem shiftLeft_eq' {x : BitVec w₁} {y : BitVec w₂} : x <<< y = x <<< y.toNat := rfl
|
||
|
||
theorem shiftLeft_zero' {x : BitVec w₁} : x <<< 0#w₂ = x := by simp
|
||
|
||
theorem shiftLeft_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {z : BitVec w₃} :
|
||
x <<< y <<< z = x <<< (y.toNat + z.toNat) := by
|
||
simp [shiftLeft_add]
|
||
|
||
theorem getLsbD_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {i : Nat} :
|
||
(x <<< y).getLsbD i = (decide (i < w₁) && !decide (i < y.toNat) && x.getLsbD (i - y.toNat)) := by grind
|
||
|
||
theorem getElem_shiftLeft' {x : BitVec w₁} {y : BitVec w₂} {i : Nat} (h : i < w₁) :
|
||
(x <<< y)[i] = (!decide (i < y.toNat) && x[i - y.toNat]) := by grind
|
||
|
||
theorem shiftLeft_eq_zero {x : BitVec w} {n : Nat} (hn : w ≤ n) : x <<< n = 0#w := by grind
|
||
|
||
theorem shiftLeft_ofNat_eq {x : BitVec w} {k : Nat} : x <<< (BitVec.ofNat w k) = x <<< (k % 2^w) := rfl
|
||
|
||
/-! ### ushiftRight -/
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
|
||
(x >>> i).toNat = x.toNat >>> i := rfl
|
||
|
||
theorem getLsbD_ushiftRight (x : BitVec n) (i j : Nat) :
|
||
getLsbD (x >>> i) j = getLsbD x (i+j) := by
|
||
grind [getLsbD]
|
||
|
||
theorem getElem_ushiftRight (x : BitVec w) (i n : Nat) (h : i < w) :
|
||
(x >>> n)[i] = x.getLsbD (n + i) := by
|
||
grind [getElem_eq_testBit_toNat, getLsbD]
|
||
|
||
theorem ushiftRight_xor_distrib (x y : BitVec w) (n : Nat) :
|
||
(x ^^^ y) >>> n = (x >>> n) ^^^ (y >>> n) := by grind
|
||
|
||
theorem ushiftRight_and_distrib (x y : BitVec w) (n : Nat) :
|
||
(x &&& y) >>> n = (x >>> n) &&& (y >>> n) := by grind
|
||
|
||
theorem ushiftRight_or_distrib (x y : BitVec w) (n : Nat) :
|
||
(x ||| y) >>> n = (x >>> n) ||| (y >>> n) := by grind
|
||
|
||
theorem ushiftRight_zero (x : BitVec w) : x >>> 0 = x := by
|
||
simp [bitvec_to_nat]
|
||
|
||
theorem zero_ushiftRight {n : Nat} : 0#w >>> n = 0#w := by
|
||
simp [bitvec_to_nat]
|
||
|
||
/--
|
||
Shifting right by `n < w` yields a bitvector whose value is less than `2 ^ (w - n)`.
|
||
-/
|
||
theorem toNat_ushiftRight_lt (x : BitVec w) (n : Nat) (hn : n ≤ w) :
|
||
(x >>> n).toNat < 2 ^ (w - n) := by
|
||
rw [toNat_ushiftRight, Nat.shiftRight_eq_div_pow, Nat.div_lt_iff_lt_mul]
|
||
· rw [Nat.pow_sub_mul_pow]
|
||
· apply x.isLt
|
||
· apply hn
|
||
· apply Nat.pow_pos (by decide)
|
||
|
||
|
||
/-- Shifting right by `n`, which is larger than the bitwidth `w` produces `0. -/
|
||
theorem ushiftRight_eq_zero {x : BitVec w} {n : Nat} (hn : w ≤ n) :
|
||
x >>> n = 0#w := by
|
||
simp only [toNat_eq, toNat_ushiftRight, toNat_ofNat, Nat.zero_mod]
|
||
have : 2^w ≤ 2^n := Nat.pow_le_pow_of_le Nat.one_lt_two hn
|
||
rw [Nat.shiftRight_eq_div_pow, Nat.div_eq_of_lt (by omega)]
|
||
|
||
/--
|
||
Unsigned shift right by at least one bit makes the interpretations of the bitvector as an `Int` or `Nat` agree,
|
||
because it makes the value of the bitvector less than or equal to `2^(w-1)`.
|
||
-/
|
||
theorem toInt_ushiftRight_of_lt {x : BitVec w} {n : Nat} (hn : 0 < n) :
|
||
(x >>> n).toInt = x.toNat >>> n := by
|
||
rw [toInt_eq_toNat_cond]
|
||
simp only [toNat_ushiftRight, ite_eq_left_iff, Nat.not_lt]
|
||
intro h
|
||
by_cases hn : n ≤ w
|
||
· have h1 := Nat.mul_lt_mul_of_pos_left (toNat_ushiftRight_lt x n hn) Nat.two_pos
|
||
simp only [toNat_ushiftRight, Nat.zero_lt_succ, Nat.mul_lt_mul_left] at h1
|
||
have : 2 ^ (w - n).succ ≤ 2^ w := Nat.pow_le_pow_of_le (by decide) (by omega)
|
||
have := show 2 * x.toNat >>> n < 2 ^ w by
|
||
omega
|
||
omega
|
||
· have : x.toNat >>> n = 0 := by
|
||
apply Nat.shiftRight_eq_zero
|
||
have : 2^w ≤ 2^n := Nat.pow_le_pow_of_le (by decide) (by omega)
|
||
omega
|
||
simp [this] at h
|
||
|
||
/--
|
||
Unsigned shift right by at least one bit makes the interpretations of the bitvector as an `Int` or `Nat` agree,
|
||
because it makes the value of the bitvector less than or equal to `2^(w-1)`.
|
||
In the case when `n = 0`, then the shift right value equals the integer interpretation.
|
||
-/
|
||
theorem toInt_ushiftRight {x : BitVec w} {n : Nat} :
|
||
(x >>> n).toInt = if n = 0 then x.toInt else x.toNat >>> n := by
|
||
grind [toInt_ushiftRight_of_lt]
|
||
|
||
theorem toFin_ushiftRight {x : BitVec w} {n : Nat} :
|
||
(x >>> n).toFin = x.toFin / (Fin.ofNat (2^w) (2^n)) := by
|
||
apply Fin.eq_of_val_eq
|
||
by_cases hn : n < w
|
||
· simp [Nat.shiftRight_eq_div_pow, Nat.mod_eq_of_lt (Nat.pow_lt_pow_of_lt Nat.one_lt_two hn)]
|
||
· simp only [Nat.not_lt] at hn
|
||
rw [ushiftRight_eq_zero (by omega)]
|
||
simp [Nat.dvd_iff_mod_eq_zero.mp (Nat.pow_dvd_pow 2 hn)]
|
||
|
||
theorem getMsbD_ushiftRight {x : BitVec w} {i n : Nat} :
|
||
(x >>> n).getMsbD i = (decide (i < w) && (!decide (i < n) && x.getMsbD (i - n))) := by
|
||
grind (splits := 11)
|
||
|
||
theorem msb_ushiftRight {x : BitVec w} {n : Nat} :
|
||
(x >>> n).msb = (!decide (0 < n) && x.msb) := by grind
|
||
|
||
theorem setWidth_ushiftRight {x : BitVec w} {y : Nat} (hi : w ≤ i) :
|
||
(x >>> y).setWidth i = x.setWidth i >>> y := by grind
|
||
|
||
/-! ### ushiftRight reductions from BitVec to Nat -/
|
||
|
||
theorem ushiftRight_eq' (x : BitVec w₁) (y : BitVec w₂) :
|
||
x >>> y = x >>> y.toNat := rfl
|
||
|
||
theorem ushiftRight_ofNat_eq {x : BitVec w} {k : Nat} : x >>> (BitVec.ofNat w k) = x >>> (k % 2^w) := rfl
|
||
|
||
theorem ushiftRight_self (n : BitVec w) : n >>> n.toNat = 0#w := by
|
||
simp [BitVec.toNat_eq, Nat.shiftRight_eq_div_pow, Nat.lt_two_pow_self, Nat.div_eq_of_lt]
|
||
|
||
/-! ### sshiftRight -/
|
||
|
||
theorem sshiftRight_eq {x : BitVec n} {i : Nat} :
|
||
x.sshiftRight i = BitVec.ofInt n (x.toInt >>> i) := by
|
||
grind [BitVec.sshiftRight]
|
||
|
||
/-- if the msb is false, the arithmetic shift right equals logical shift right -/
|
||
@[local grind =]
|
||
theorem sshiftRight_eq_of_msb_false {x : BitVec w} {s : Nat} (h : x.msb = false) :
|
||
(x.sshiftRight s) = x >>> s := by
|
||
apply BitVec.eq_of_toNat_eq
|
||
rw [BitVec.sshiftRight_eq, BitVec.toInt_eq_toNat_cond]
|
||
have hxbound : 2 * x.toNat < 2 ^ w := BitVec.msb_eq_false_iff_two_mul_lt.mp h
|
||
simp only [hxbound, ↓reduceIte, Int.natCast_shiftRight, ofInt_natCast,
|
||
toNat_ofNat, toNat_ushiftRight]
|
||
replace hxbound : x.toNat >>> s < 2 ^ w := by
|
||
rw [Nat.shiftRight_eq_div_pow]
|
||
exact Nat.lt_of_le_of_lt (Nat.div_le_self ..) x.isLt
|
||
apply Nat.mod_eq_of_lt hxbound
|
||
|
||
/--
|
||
If the msb is `true`, the arithmetic shift right equals negating,
|
||
then logical shifting right, then negating again.
|
||
The double negation preserves the lower bits that have been shifted,
|
||
and the outer negation ensures that the high bits are '1'. -/
|
||
theorem sshiftRight_eq_of_msb_true {x : BitVec w} {s : Nat} (h : x.msb = true) :
|
||
(x.sshiftRight s) = ~~~((~~~x) >>> s) := by
|
||
apply BitVec.eq_of_toNat_eq
|
||
rcases w with rfl | w
|
||
· simp [toNat_of_zero_length]
|
||
· rw [BitVec.sshiftRight_eq, BitVec.toInt_eq_toNat_cond]
|
||
have hxbound : (2 * x.toNat ≥ 2 ^ (w + 1)) := BitVec.msb_eq_true_iff_two_mul_ge.mp h
|
||
replace hxbound : ¬ (2 * x.toNat < 2 ^ (w + 1)) := by omega
|
||
simp only [hxbound, ↓reduceIte, toNat_ofInt, toNat_not, toNat_ushiftRight]
|
||
rw [← Int.subNatNat_eq_coe, Int.subNatNat_of_lt (by omega),
|
||
Nat.pred_eq_sub_one, Int.negSucc_shiftRight,
|
||
Int.emod_negSucc, Int.natAbs_natCast, Nat.succ_eq_add_one,
|
||
Int.subNatNat_of_le (by omega), Int.toNat_natCast, Nat.mod_eq_of_lt,
|
||
Nat.sub_right_comm]
|
||
omega
|
||
· rw [Nat.shiftRight_eq_div_pow]
|
||
apply Nat.lt_of_le_of_lt (Nat.div_le_self _ _) (by omega)
|
||
|
||
theorem getLsbD_sshiftRight (x : BitVec w) (s i : Nat) :
|
||
getLsbD (x.sshiftRight s) i =
|
||
(!decide (w ≤ i) && if s + i < w then x.getLsbD (s + i) else x.msb) := by grind
|
||
|
||
theorem getElem_sshiftRight {x : BitVec w} {s i : Nat} (h : i < w) :
|
||
(x.sshiftRight s)[i] = (if h : s + i < w then x[s + i] else x.msb) := by
|
||
rw [← getLsbD_eq_getElem]
|
||
grind
|
||
|
||
theorem sshiftRight_xor_distrib (x y : BitVec w) (n : Nat) :
|
||
(x ^^^ y).sshiftRight n = (x.sshiftRight n) ^^^ (y.sshiftRight n) := by grind
|
||
|
||
theorem sshiftRight_and_distrib (x y : BitVec w) (n : Nat) :
|
||
(x &&& y).sshiftRight n = (x.sshiftRight n) &&& (y.sshiftRight n) := by grind
|
||
|
||
theorem sshiftRight_or_distrib (x y : BitVec w) (n : Nat) :
|
||
(x ||| y).sshiftRight n = (x.sshiftRight n) ||| (y.sshiftRight n) := by grind
|
||
|
||
theorem sshiftRight'_ofNat_eq_sshiftRight {x : BitVec w} {k : Nat} :
|
||
x.sshiftRight' (BitVec.ofNat w k) = x.sshiftRight (k % 2^w) := rfl
|
||
|
||
/-- The msb after arithmetic shifting right equals the original msb. -/
|
||
theorem msb_sshiftRight {n : Nat} {x : BitVec w} :
|
||
(x.sshiftRight n).msb = x.msb := by grind (splits := 11)
|
||
|
||
theorem sshiftRight_zero {x : BitVec w} : x.sshiftRight 0 = x := by grind
|
||
|
||
theorem zero_sshiftRight {n : Nat} : (0#w).sshiftRight n = 0#w := by grind
|
||
|
||
theorem sshiftRight_add {x : BitVec w} {m n : Nat} :
|
||
x.sshiftRight (m + n) = (x.sshiftRight m).sshiftRight n := by grind (splits := 15)
|
||
|
||
theorem not_sshiftRight {b : BitVec w} :
|
||
~~~b.sshiftRight n = (~~~b).sshiftRight n := by grind
|
||
|
||
theorem not_sshiftRight_not {x : BitVec w} {n : Nat} :
|
||
~~~((~~~x).sshiftRight n) = x.sshiftRight n := by grind
|
||
|
||
theorem getMsbD_sshiftRight {x : BitVec w} {i n : Nat} :
|
||
getMsbD (x.sshiftRight n) i = (decide (i < w) && if i < n then x.msb else getMsbD x (i - n)) := by
|
||
grind (splits := 16)
|
||
|
||
theorem toInt_shiftRight_lt {x : BitVec w} {n : Nat} :
|
||
x.toInt >>> n < 2 ^ (w - 1) := by
|
||
have := @Int.shiftRight_le_of_nonneg x.toInt n
|
||
have := @Int.shiftRight_le_of_nonpos x.toInt n
|
||
have := @BitVec.toInt_lt w x
|
||
have := @Nat.one_le_two_pow (w-1)
|
||
norm_cast at *
|
||
grind
|
||
|
||
theorem le_toInt_shiftRight {x : BitVec w} {n : Nat} :
|
||
-(2 ^ (w - 1)) ≤ x.toInt >>> n := by
|
||
have := @Int.le_shiftRight_of_nonpos x.toInt n
|
||
have := @Int.le_shiftRight_of_nonneg x.toInt n
|
||
have := @BitVec.le_toInt w x
|
||
have := @Nat.one_le_two_pow (w-1)
|
||
norm_cast at *
|
||
grind
|
||
|
||
theorem toFin_sshiftRight_of_msb_true {x : BitVec w} {n : Nat} (h : x.msb = true) :
|
||
(x.sshiftRight n).toFin = Fin.ofNat (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n) := by
|
||
apply Fin.eq_of_val_eq
|
||
simp only [val_toFin, toNat_sshiftRight, h, ↓reduceIte, Fin.val_ofNat]
|
||
rw [Nat.mod_eq_of_lt]
|
||
grind
|
||
|
||
theorem toFin_sshiftRight_of_msb_false {x : BitVec w} {n : Nat} (h : x.msb = false) :
|
||
(x.sshiftRight n).toFin = Fin.ofNat (2^w) (x.toNat >>> n) := by
|
||
apply Fin.eq_of_val_eq
|
||
simp only [val_toFin, toNat_sshiftRight, h, Bool.false_eq_true, ↓reduceIte, Fin.val_ofNat]
|
||
have := Nat.shiftRight_le x.toNat n
|
||
rw [Nat.mod_eq_of_lt (by omega)]
|
||
|
||
theorem toFin_sshiftRight {x : BitVec w} {n : Nat} :
|
||
(x.sshiftRight n).toFin =
|
||
if x.msb then
|
||
Fin.ofNat (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> n)
|
||
else
|
||
Fin.ofNat (2^w) (x.toNat >>> n) := by
|
||
grind [toFin_sshiftRight_of_msb_true, toFin_sshiftRight_of_msb_false]
|
||
|
||
theorem toInt_sshiftRight {x : BitVec w} {n : Nat} :
|
||
(x.sshiftRight n).toInt = x.toInt >>> n := by
|
||
by_cases h : w = 0
|
||
· subst h
|
||
simp [BitVec.eq_nil x]
|
||
· rw [sshiftRight, toInt_ofInt, ←Nat.two_pow_pred_add_two_pow_pred (by omega)]
|
||
have := @toInt_shiftRight_lt w x n
|
||
have := @le_toInt_shiftRight w x n
|
||
norm_cast at *
|
||
exact Int.bmod_eq_of_le (by omega) (by omega)
|
||
|
||
/-! ### sshiftRight reductions from BitVec to Nat -/
|
||
|
||
theorem sshiftRight_eq' (x : BitVec w) : x.sshiftRight' y = x.sshiftRight y.toNat := rfl
|
||
|
||
theorem toNat_sshiftRight'_of_msb_true {x y : BitVec w} (h : x.msb = true) :
|
||
(x.sshiftRight' y).toNat = 2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat := by
|
||
rw [sshiftRight_eq', toNat_sshiftRight_of_msb_true h]
|
||
|
||
theorem toNat_sshiftRight'_of_msb_false {x y : BitVec w} (h : x.msb = false) :
|
||
(x.sshiftRight' y).toNat = x.toNat >>> y.toNat := by
|
||
rw [sshiftRight_eq', toNat_sshiftRight_of_msb_false h]
|
||
|
||
theorem toNat_sshiftRight' {x y : BitVec w} :
|
||
(x.sshiftRight' y).toNat =
|
||
if x.msb then
|
||
2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat
|
||
else
|
||
x.toNat >>> y.toNat := by
|
||
rw [sshiftRight_eq', toNat_sshiftRight]
|
||
|
||
theorem toFin_sshiftRight'_of_msb_true {x y : BitVec w} (h : x.msb = true) :
|
||
(x.sshiftRight' y).toFin = Fin.ofNat (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat) := by
|
||
rw [sshiftRight_eq', toFin_sshiftRight_of_msb_true h]
|
||
|
||
theorem toFin_sshiftRight'_of_msb_false {x y : BitVec w} (h : x.msb = false) :
|
||
(x.sshiftRight' y).toFin = Fin.ofNat (2^w) (x.toNat >>> y.toNat) := by
|
||
rw [sshiftRight_eq', toFin_sshiftRight_of_msb_false h]
|
||
|
||
theorem toFin_sshiftRight' {x y : BitVec w} :
|
||
(x.sshiftRight' y).toFin =
|
||
if x.msb then
|
||
Fin.ofNat (2^w) (2 ^ w - 1 - (2 ^ w - 1 - x.toNat) >>> y.toNat)
|
||
else
|
||
Fin.ofNat (2^w) (x.toNat >>> y.toNat) := by
|
||
rw [sshiftRight_eq', toFin_sshiftRight]
|
||
|
||
theorem toInt_sshiftRight' {x y : BitVec w} :
|
||
(x.sshiftRight' y).toInt = x.toInt >>> y.toNat := by grind
|
||
|
||
-- This should not be a `@[simp]` lemma as the left hand side is not in simp normal form.
|
||
theorem getLsbD_sshiftRight' {x y : BitVec w} {i : Nat} :
|
||
getLsbD (x.sshiftRight' y) i =
|
||
(!decide (w ≤ i) && if y.toNat + i < w then x.getLsbD (y.toNat + i) else x.msb) := by grind
|
||
|
||
-- This should not be a `@[simp]` lemma as the left hand side is not in simp normal form.
|
||
theorem getElem_sshiftRight' {x y : BitVec w} {i : Nat} (h : i < w) :
|
||
(x.sshiftRight' y)[i] = (if h : y.toNat + i < w then x[y.toNat + i] else x.msb) := by grind
|
||
|
||
theorem getMsbD_sshiftRight' {x y: BitVec w} {i : Nat} :
|
||
(x.sshiftRight y.toNat).getMsbD i =
|
||
(decide (i < w) && if i < y.toNat then x.msb else x.getMsbD (i - y.toNat)) := by grind
|
||
|
||
theorem msb_sshiftRight' {x y: BitVec w} :
|
||
(x.sshiftRight' y).msb = x.msb := by simp
|
||
|
||
/-! ### signExtend -/
|
||
|
||
/-- Equation theorem for `Int.sub` when both arguments are `Int.ofNat` -/
|
||
private theorem Int.ofNat_sub_ofNat_of_lt {n m : Nat} (hlt : n < m) :
|
||
(n : Int) - (m : Int) = -(↑(m - 1 - n) + 1) := by
|
||
omega
|
||
|
||
/-- Equation theorem for `Int.mod` -/
|
||
private theorem Int.negSucc_emod (m : Nat) (n : Int) :
|
||
-(m + 1) % n = Int.subNatNat (Int.natAbs n) ((m % Int.natAbs n) + 1) := rfl
|
||
|
||
/-- The sign extension is the same as zero extending when `msb = false`. -/
|
||
theorem signExtend_eq_setWidth_of_msb_false {x : BitVec w} {v : Nat} (hmsb : x.msb = false) :
|
||
x.signExtend v = x.setWidth v := by
|
||
grind [signExtend]
|
||
|
||
/--
|
||
The sign extension is a bitwise not, followed by a zero extend, followed by another bitwise not
|
||
when `msb = true`. The double bitwise not ensures that the high bits are '1',
|
||
and the lower bits are preserved. -/
|
||
theorem signExtend_eq_not_setWidth_not_of_msb_true {x : BitVec w} {v : Nat} (hmsb : x.msb = true) :
|
||
x.signExtend v = ~~~((~~~x).setWidth v) := by
|
||
apply BitVec.eq_of_toNat_eq
|
||
simp only [signExtend, BitVec.toInt_eq_msb_cond, toNat_ofInt, toNat_not,
|
||
toNat_setWidth, hmsb, ↓reduceIte]
|
||
norm_cast
|
||
rw [Int.ofNat_sub_ofNat_of_lt, Int.negSucc_emod]
|
||
simp only [Int.natAbs_natCast]
|
||
rw [Int.subNatNat_of_le]
|
||
· rw [Int.toNat_natCast, Nat.add_comm, Nat.sub_add_eq]
|
||
· apply Nat.le_trans
|
||
· apply Nat.succ_le_of_lt
|
||
apply Nat.mod_lt
|
||
apply Nat.two_pow_pos
|
||
· apply Nat.le_refl
|
||
· omega
|
||
|
||
theorem getLsbD_signExtend (x : BitVec w) {v i : Nat} :
|
||
(x.signExtend v).getLsbD i = (decide (i < v) && if i < w then x.getLsbD i else x.msb) := by
|
||
grind [signExtend_eq_setWidth_of_msb_false, signExtend_eq_not_setWidth_not_of_msb_true]
|
||
|
||
theorem getMsbD_signExtend {x : BitVec w} {v i : Nat} :
|
||
(x.signExtend v).getMsbD i =
|
||
(decide (i < v) && if v - w ≤ i then x.getMsbD (i + w - v) else x.msb) := by
|
||
grind (splits := 16) [signExtend_eq_setWidth_of_msb_false, signExtend_eq_not_setWidth_not_of_msb_true]
|
||
|
||
theorem getElem_signExtend {x : BitVec w} {v i : Nat} (h : i < v) :
|
||
(x.signExtend v)[i] = if h : i < w then x[i] else x.msb := by
|
||
simp [←getLsbD_eq_getElem, getLsbD_signExtend, h]
|
||
|
||
theorem msb_signExtend {x : BitVec w} :
|
||
(x.signExtend v).msb = (decide (0 < v) && if w ≥ v then x.getMsbD (w - v) else x.msb) := by
|
||
grind (splits := 10)
|
||
|
||
/-- Sign extending to a width smaller than the starting width is a truncation. -/
|
||
theorem signExtend_eq_setWidth_of_le (x : BitVec w) {v : Nat} (hv : v ≤ w) :
|
||
x.signExtend v = x.setWidth v := by grind
|
||
|
||
/-- Sign extending to the same bitwidth is a no op. -/
|
||
theorem signExtend_eq (x : BitVec w) : x.signExtend w = x := by grind
|
||
|
||
/-- Sign extending to a larger bitwidth depends on the msb.
|
||
If the msb is false, then the result equals the original value.
|
||
If the msb is true, then we add a value of `(2^v - 2^w)`, which arises from the sign extension. -/
|
||
private theorem toNat_signExtend_of_le (x : BitVec w) {v : Nat} (hv : w ≤ v) :
|
||
(x.signExtend v).toNat = x.toNat + if x.msb then 2^v - 2^w else 0 := by
|
||
apply Nat.eq_of_testBit_eq
|
||
intro i
|
||
have ⟨k, hk⟩ := Nat.exists_eq_add_of_le hv
|
||
rw [hk, testBit_toNat, getLsbD_signExtend, Nat.pow_add, ← Nat.mul_sub_one, Nat.add_comm (x.toNat)]
|
||
by_cases hx : x.msb
|
||
· simp only [hx, ↓reduceIte, Nat.testBit_two_pow_mul_add _ x.isLt, Nat.testBit_two_pow_sub_one]
|
||
grind
|
||
· simp only [hx]
|
||
grind [testBit_toNat]
|
||
|
||
/-- Sign extending to a larger bitwidth depends on the msb.
|
||
If the msb is false, then the result equals the original value.
|
||
If the msb is true, then we add a value of `(2^v - 2^w)`, which arises from the sign extension. -/
|
||
theorem toNat_signExtend (x : BitVec w) {v : Nat} :
|
||
(x.signExtend v).toNat = (x.setWidth v).toNat + if x.msb then 2^v - 2^w else 0 := by
|
||
by_cases h : v ≤ w
|
||
· have : 2^v ≤ 2^w := Nat.pow_le_pow_right Nat.two_pos h
|
||
simp [signExtend_eq_setWidth_of_le x h, toNat_setWidth, Nat.sub_eq_zero_of_le this]
|
||
· have : 2^w ≤ 2^v := Nat.pow_le_pow_right Nat.two_pos (by omega)
|
||
rw [toNat_signExtend_of_le x (by omega), toNat_setWidth, Nat.mod_eq_of_lt (by omega)]
|
||
|
||
/--
|
||
If the current width `w` is smaller than the extended width `v`,
|
||
then the value when interpreted as an integer does not change.
|
||
-/
|
||
theorem toInt_signExtend_of_le {x : BitVec w} (h : w ≤ v) :
|
||
(x.signExtend v).toInt = x.toInt := by
|
||
by_cases hlt : w < v
|
||
· rw [toInt_signExtend_of_lt hlt]
|
||
· obtain rfl : w = v := by omega
|
||
simp
|
||
where
|
||
toInt_signExtend_of_lt {x : BitVec w} (hv : w < v):
|
||
(x.signExtend v).toInt = x.toInt := by
|
||
simp only [toInt_eq_msb_cond, toNat_signExtend]
|
||
have : (x.signExtend v).msb = x.msb := by grind
|
||
have H : 2^w ≤ 2^v := Nat.pow_le_pow_right (by omega) (by omega)
|
||
simp only [this, toNat_setWidth, Int.natCast_add, Int.natCast_emod]
|
||
by_cases h : x.msb
|
||
<;> norm_cast
|
||
<;> simp [h, Nat.mod_eq_of_lt (Nat.lt_of_lt_of_le x.isLt H), -Int.natCast_pow]
|
||
omega
|
||
|
||
/--
|
||
If the current width `w` is larger than the extended width `v`,
|
||
then the value when interpreted as an integer is truncated,
|
||
and we compute a modulo by `2^v`.
|
||
-/
|
||
theorem toInt_signExtend_eq_toNat_bmod_of_le {x : BitVec w} (hv : v ≤ w) :
|
||
(x.signExtend v).toInt = Int.bmod x.toNat (2^v) := by
|
||
grind [signExtend_eq_setWidth_of_le]
|
||
|
||
/--
|
||
Interpreting the sign extension of `(x : BitVec w)` to width `v`
|
||
computes `x % 2^v` (where `%` is the balanced mod). See `toInt_signExtend` for a version stated
|
||
in terms of `toInt` instead of `toNat`.
|
||
-/
|
||
theorem toInt_signExtend_eq_toNat_bmod (x : BitVec w) :
|
||
(x.signExtend v).toInt = Int.bmod x.toNat (2 ^ min v w) := by
|
||
grind [toInt_signExtend_eq_toNat_bmod_of_le, toInt_signExtend_of_le]
|
||
|
||
theorem toInt_signExtend (x : BitVec w) :
|
||
(x.signExtend v).toInt = x.toInt.bmod (2 ^ min v w) := by
|
||
rw [toInt_signExtend_eq_toNat_bmod, BitVec.toInt_eq_toNat_bmod, Int.bmod_bmod_of_dvd]
|
||
exact Nat.pow_dvd_pow _ (Nat.min_le_right v w)
|
||
|
||
theorem toInt_signExtend_eq_toInt_bmod_of_le (x : BitVec w) (h : v ≤ w) :
|
||
(x.signExtend v).toInt = x.toInt.bmod (2 ^ v) := by
|
||
grind [BitVec.toInt_signExtend]
|
||
|
||
theorem toFin_signExtend_of_le {x : BitVec w} (hv : v ≤ w):
|
||
(x.signExtend v).toFin = Fin.ofNat (2 ^ v) x.toNat := by
|
||
grind [signExtend_eq_setWidth_of_le]
|
||
|
||
theorem toFin_signExtend (x : BitVec w) :
|
||
(x.signExtend v).toFin = Fin.ofNat (2 ^ v) (x.toNat + if x.msb = true then 2 ^ v - 2 ^ w else 0):= by
|
||
by_cases hv : v ≤ w
|
||
· simp [toFin_signExtend_of_le hv, show 2 ^ v - 2 ^ w = 0 by rw [@Nat.sub_eq_zero_iff_le]; apply Nat.pow_le_pow_of_le (by decide) (by omega)]
|
||
· simp only [Nat.not_le] at hv
|
||
apply Fin.eq_of_val_eq
|
||
simp only [val_toFin, Fin.val_ofNat]
|
||
rw [toNat_signExtend_of_le _ (by omega)]
|
||
have : 2 ^ w < 2 ^ v := by apply Nat.pow_lt_pow_of_lt <;> omega
|
||
rw [Nat.mod_eq_of_lt]
|
||
grind
|
||
|
||
theorem signExtend_and {x y : BitVec w} :
|
||
(x &&& y).signExtend v = (x.signExtend v) &&& (y.signExtend v) := by
|
||
grind (splits := 11)
|
||
|
||
theorem signExtend_or {x y : BitVec w} :
|
||
(x ||| y).signExtend v = (x.signExtend v) ||| (y.signExtend v) := by
|
||
grind (splits := 12)
|
||
|
||
theorem signExtend_xor {x y : BitVec w} :
|
||
(x ^^^ y).signExtend v = (x.signExtend v) ^^^ (y.signExtend v) := by
|
||
grind (splits := 11)
|
||
|
||
theorem signExtend_not {x : BitVec w} (h : 0 < w) :
|
||
(~~~x).signExtend v = ~~~(x.signExtend v) := by
|
||
grind (splits := 11)
|
||
|
||
/-! ### append -/
|
||
|
||
theorem append_def (x : BitVec v) (y : BitVec w) :
|
||
x ++ y = (shiftLeftZeroExtend x w ||| setWidth' (Nat.le_add_left w v) y) := rfl
|
||
|
||
theorem getLsbD_append {x : BitVec n} {y : BitVec m} :
|
||
getLsbD (x ++ y) i = if i < m then getLsbD y i else getLsbD x (i - m) := by
|
||
grind [append_def]
|
||
|
||
theorem getElem_append {x : BitVec n} {y : BitVec m} (h : i < n + m) :
|
||
(x ++ y)[i] = if h : i < m then y[i] else x[i - m] := by
|
||
grind [append_def]
|
||
|
||
theorem getMsbD_append {x : BitVec n} {y : BitVec m} :
|
||
getMsbD (x ++ y) i = if n ≤ i then getMsbD y (i - n) else getMsbD x i := by
|
||
grind (splits := 13) [append_def]
|
||
|
||
theorem msb_append {x : BitVec w} {y : BitVec v} :
|
||
(x ++ y).msb = if w = 0 then y.msb else x.msb := by
|
||
grind (splits := 14) [append_def]
|
||
|
||
theorem append_zero_width (x : BitVec w) (y : BitVec 0) : x ++ y = x := by grind
|
||
|
||
theorem toInt_append {x : BitVec n} {y : BitVec m} :
|
||
(x ++ y).toInt = if n = 0 then y.toInt else (2 ^ m) * x.toInt + y.toNat := by
|
||
by_cases n0 : n = 0
|
||
· subst n0
|
||
simp [BitVec.eq_nil x, BitVec.toInt]
|
||
· by_cases m0 : m = 0
|
||
· subst m0; rw [BitVec.eq_nil y, append_zero_width]; simp [n0]
|
||
· simp only [n0, ↓reduceIte]
|
||
by_cases x.msb
|
||
case pos h =>
|
||
rw [toInt_eq_msb_cond]
|
||
simp only [show ((x ++ y).msb = true) by simp [msb_append, n0, h], ↓reduceIte, toNat_append,
|
||
Nat.pow_add, ← Nat.shiftLeft_eq, toInt_eq_msb_cond, h]
|
||
rw_mod_cast [← Nat.shiftLeft_add_eq_or_of_lt (by omega), Nat.shiftLeft_eq, Nat.shiftLeft_eq,
|
||
Nat.mul_comm, Int.mul_sub]
|
||
norm_cast
|
||
rw [Int.natCast_add, Nat.mul_comm (n := 2 ^ n)]
|
||
omega
|
||
case neg h =>
|
||
rw [Bool.not_eq_true] at h
|
||
rw [toInt_eq_toNat_of_msb h, toInt_eq_toNat_of_msb (by simp [msb_append, n0, h])]
|
||
rw_mod_cast [toNat_append, ← Nat.shiftLeft_add_eq_or_of_lt (by omega), Nat.shiftLeft_eq,
|
||
Nat.mul_comm]
|
||
|
||
theorem toInt_append_zero {n m : Nat} {x : BitVec n} :
|
||
(x ++ 0#m).toInt = (2 ^ m) * x.toInt := by
|
||
simp only [toInt_append, toInt_zero, toNat_ofNat, Nat.zero_mod, Int.cast_ofNat_Int, Int.add_zero,
|
||
ite_eq_right_iff]
|
||
-- FIXME: `grind` fails because of a reduction failure in`Lean.Grind.CommRing.Stepwise.d_step1_cert`.
|
||
-- Something needs `@[expose]`, but what?
|
||
-- grind only [two_mul_toInt_lt, le_two_mul_toInt, = toInt_zero_length]
|
||
intro h
|
||
subst h
|
||
simp [BitVec.eq_nil x]
|
||
|
||
theorem toInt_zero_append {n m : Nat} {x : BitVec n} :
|
||
(0#m ++ x).toInt = if m = 0 then x.toInt else x.toNat := by
|
||
simp [toInt_append]
|
||
|
||
/--
|
||
Show that `(x.toNat <<< n) ||| y.toNat` is within bounds of `BitVec (m + n)`.
|
||
-/
|
||
theorem toNat_shiftLeft_or_toNat_lt_two_pow_add {m n : Nat} (x : BitVec m) (y : BitVec n) :
|
||
x.toNat <<< n ||| y.toNat < 2 ^ (m + n) := by
|
||
have hnLe : 2^n ≤ 2 ^(m + n) := by
|
||
rw [Nat.pow_add]
|
||
exact Nat.le_mul_of_pos_left (2 ^ n) (Nat.two_pow_pos m)
|
||
apply Nat.or_lt_two_pow
|
||
· have := Nat.two_pow_pos n
|
||
rw [Nat.shiftLeft_eq, Nat.pow_add, Nat.mul_lt_mul_right]
|
||
<;> omega
|
||
· omega
|
||
|
||
theorem toFin_append {x : BitVec m} {y : BitVec n} :
|
||
(x ++ y).toFin =
|
||
@Fin.mk (2^(m+n)) (x.toNat <<< n ||| y.toNat) (toNat_shiftLeft_or_toNat_lt_two_pow_add x y) := by
|
||
ext
|
||
simp
|
||
|
||
theorem zero_width_append (x : BitVec 0) (y : BitVec v) : x ++ y = y.cast (by omega) := by
|
||
ext i ih
|
||
simp [getElem_append, show i < v by omega]
|
||
|
||
theorem zero_append_zero : 0#v ++ 0#w = 0#(v + w) := by grind
|
||
|
||
theorem cast_append_right (h : w + v = w + v') (x : BitVec w) (y : BitVec v) :
|
||
(x ++ y).cast h = x ++ y.cast (by omega) := by grind
|
||
|
||
theorem cast_append_left (h : w + v = w' + v) (x : BitVec w) (y : BitVec v) :
|
||
(x ++ y).cast h = x.cast (by omega) ++ y := by grind
|
||
|
||
theorem setWidth_append {x : BitVec w} {y : BitVec v} :
|
||
(x ++ y).setWidth k = if h : k ≤ v then y.setWidth k else (x.setWidth (k - v) ++ y).cast (by omega) := by grind
|
||
|
||
theorem setWidth_append_of_eq {x : BitVec v} {y : BitVec w} (h : w' = w) :
|
||
setWidth (v' + w') (x ++ y) = setWidth v' x ++ setWidth w' y := by
|
||
subst h
|
||
grind
|
||
|
||
theorem setWidth_cons {x : BitVec w} : (cons a x).setWidth w = x := by grind
|
||
|
||
theorem not_append {x : BitVec w} {y : BitVec v} : ~~~ (x ++ y) = (~~~ x) ++ (~~~ y) := by grind
|
||
|
||
theorem and_append {x₁ x₂ : BitVec w} {y₁ y₂ : BitVec v} :
|
||
(x₁ ++ y₁) &&& (x₂ ++ y₂) = (x₁ &&& x₂) ++ (y₁ &&& y₂) := by grind
|
||
|
||
theorem or_append {x₁ x₂ : BitVec w} {y₁ y₂ : BitVec v} :
|
||
(x₁ ++ y₁) ||| (x₂ ++ y₂) = (x₁ ||| x₂) ++ (y₁ ||| y₂) := by grind
|
||
|
||
theorem xor_append {x₁ x₂ : BitVec w} {y₁ y₂ : BitVec v} :
|
||
(x₁ ++ y₁) ^^^ (x₂ ++ y₂) = (x₁ ^^^ x₂) ++ (y₁ ^^^ y₂) := by grind
|
||
|
||
theorem shiftRight_add {w : Nat} (x : BitVec w) (n m : Nat) :
|
||
x >>> (n + m) = (x >>> n) >>> m:= by grind
|
||
|
||
theorem shiftLeft_ushiftRight {x : BitVec w} {n : Nat}:
|
||
x >>> n <<< n = x &&& BitVec.allOnes w <<< n := by
|
||
induction n generalizing x with grind
|
||
|
||
theorem msb_shiftLeft {x : BitVec w} {n : Nat} :
|
||
(x <<< n).msb = x.getMsbD n := by grind
|
||
|
||
/--
|
||
A `(x : BitVec v)` set to width `w` equals `(v - w)` zeros,
|
||
followed by the low `(min v w) bits of `x`
|
||
-/
|
||
theorem setWidth_eq_append_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} :
|
||
x.setWidth w = ((0#(w - v)) ++ x.extractLsb' 0 (min v w)).cast (by omega) := by
|
||
ext i hi
|
||
simp only [getElem_cast]
|
||
grind
|
||
|
||
/--
|
||
A `(x : BitVec v)` set to a width `w ≥ v` equals `(w - v)` zeros, followed by `x`.
|
||
-/
|
||
theorem setWidth_eq_append {v : Nat} {x : BitVec v} {w : Nat} (h : v ≤ w) :
|
||
x.setWidth w = ((0#(w - v)) ++ x).cast (by omega) := by grind
|
||
|
||
theorem setWidth_eq_extractLsb' {v : Nat} {x : BitVec v} {w : Nat} (h : w ≤ v) :
|
||
x.setWidth w = x.extractLsb' 0 w := by
|
||
rw [setWidth_eq_append_extractLsb']
|
||
ext i hi
|
||
simp only [getElem_cast, getElem_append]
|
||
grind
|
||
|
||
theorem ushiftRight_eq_extractLsb'_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
|
||
x >>> n = ((0#n) ++ (x.extractLsb' n (w - n))).cast (by omega) := by grind
|
||
|
||
theorem shiftLeft_eq_concat_of_lt {x : BitVec w} {n : Nat} (hn : n < w) :
|
||
x <<< n = (x.extractLsb' 0 (w - n) ++ 0#n).cast (by omega) := by grind
|
||
|
||
/-- Combine adjacent `extractLsb'` operations into a single `extractLsb'`. -/
|
||
theorem extractLsb'_append_extractLsb'_eq_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
|
||
((x.extractLsb' start₂ len₂) ++ (x.extractLsb' start₁ len₁)) =
|
||
(x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
|
||
grind (splits := 14)
|
||
|
||
/-- Combine adjacent `~~~ (extractLsb _)'` operations into a single `~~~ (extractLsb _)'`. -/
|
||
theorem not_extractLsb'_append_not_extractLsb'_eq_not_extractLsb' {x : BitVec w} (h : start₂ = start₁ + len₁) :
|
||
(~~~ (x.extractLsb' start₂ len₂) ++ ~~~ (x.extractLsb' start₁ len₁)) =
|
||
(~~~ x.extractLsb' start₁ (len₁ + len₂)).cast (by omega) := by
|
||
grind (splits := 14)
|
||
|
||
/-- A sign extension of `x : BitVec w` equals high bits of either `0` or `1` depending on `x.msb`,
|
||
followed by the low bits of `x`. -/
|
||
theorem signExtend_eq_append_extractLsb' {w v : Nat} {x : BitVec w} :
|
||
x.signExtend v =
|
||
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x.extractLsb' 0 (min w v)).cast (by omega) := by
|
||
ext i hi
|
||
simp only [getElem_cast]
|
||
grind (splits := 12)
|
||
|
||
/-- A sign extension of `x : BitVec w` to a larger bitwidth `v ≥ w`
|
||
equals high bits of either `0` or `1` depending on `x.msb`, followed by `x`. -/
|
||
theorem signExtend_eq_append_of_le {w v : Nat} {x : BitVec w} (h : w ≤ v) :
|
||
x.signExtend v =
|
||
((if x.msb then allOnes (v - w) else 0#(v - w)) ++ x).cast (by omega) := by grind
|
||
|
||
/--
|
||
The 'master theorem' for extracting bits from `(xhi ++ xlo)`,
|
||
which performs a case analysis on the start index, length, and the lengths of `xlo, xhi`.
|
||
· If the start index is entirely out of the `xlo` bitvector, then grab the bits from `xhi`.
|
||
· If the start index is entirely contained in the `xlo` bitvector, then grab the bits from `xlo`.
|
||
· If the start index is split between the two bitvectors,
|
||
then append `(w - start)` bits from `xlo` with `(len - (w - start))` bits from xhi.
|
||
Diagramatically:
|
||
```
|
||
xhi xlo
|
||
(<---------------------](<-------w--------]
|
||
start+len..start: (<-----len---*------]
|
||
w - start: *------*
|
||
len - (w -start): *------------*
|
||
```
|
||
-/
|
||
theorem extractLsb'_append_eq_ite {v w} {xhi : BitVec v} {xlo : BitVec w} {start len : Nat} :
|
||
extractLsb' start len (xhi ++ xlo) =
|
||
if hstart : start < w
|
||
then
|
||
if hlen : start + len ≤ w
|
||
then extractLsb' start len xlo
|
||
else
|
||
(((extractLsb' (start - w) (len - (w - start)) xhi) ++
|
||
extractLsb' start (w - start) xlo)).cast (by omega)
|
||
else
|
||
extractLsb' (start - w) len xhi := by
|
||
grind (splits := 21)
|
||
|
||
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
|
||
the bits from `xlo` when `start + len` is within `xlo`.
|
||
-/
|
||
theorem extractLsb'_append_eq_of_add_le {v w} {xhi : BitVec v} {xlo : BitVec w}
|
||
{start len : Nat} (h : start + len ≤ w) :
|
||
extractLsb' start len (xhi ++ xlo) = extractLsb' start len xlo := by grind
|
||
|
||
/-- Extracting bits `[start..start+len)` from `(xhi ++ xlo)` equals extracting
|
||
the bits from `xhi` when `start` is outside `xlo`.
|
||
-/
|
||
theorem extractLsb'_append_eq_of_le {v w} {xhi : BitVec v} {xlo : BitVec w}
|
||
{start len : Nat} (h : w ≤ start) :
|
||
extractLsb' start len (xhi ++ xlo) = extractLsb' (start - w) len xhi := by grind
|
||
|
||
/-! ### rev -/
|
||
|
||
theorem getLsbD_rev (x : BitVec w) (i : Fin w) :
|
||
x.getLsbD i.rev = x.getMsbD i := by
|
||
grind [Fin.is_lt]
|
||
|
||
theorem getElem_rev {x : BitVec w} {i : Fin w}:
|
||
x[i.rev] = x.getMsbD i := by
|
||
grind [Fin.is_lt]
|
||
|
||
theorem getMsbD_rev (x : BitVec w) (i : Fin w) :
|
||
x.getMsbD i.rev = x.getLsbD i := by
|
||
simp only [← getLsbD_rev]
|
||
simp only [Fin.rev]
|
||
congr
|
||
omega
|
||
|
||
/-! ### cons -/
|
||
|
||
/-- Variant of `toNat_cons` using `+` instead of `|||`. -/
|
||
theorem toNat_cons' {x : BitVec w} :
|
||
(cons a x).toNat = (a.toNat <<< w) + x.toNat := by
|
||
simp [cons, Nat.shiftLeft_eq, Nat.mul_comm _ (2^w), Nat.two_pow_add_eq_or_of_lt, x.isLt]
|
||
|
||
theorem getLsbD_cons (b : Bool) {n} (x : BitVec n) (i : Nat) :
|
||
getLsbD (cons b x) i = if i = n then b else getLsbD x i := by
|
||
grind [cases Bool, testBit_toNat]
|
||
|
||
theorem msb_cons : (cons a x).msb = a := by grind
|
||
|
||
theorem getMsbD_cons_zero : (cons a x).getMsbD 0 = a := by grind
|
||
|
||
theorem getMsbD_cons_succ : (cons a x).getMsbD (i + 1) = x.getMsbD i := by grind
|
||
|
||
theorem setWidth_succ (x : BitVec w) :
|
||
setWidth (i+1) x = cons (getLsbD x i) (setWidth i x) := by grind
|
||
|
||
theorem not_cons (x : BitVec w) (b : Bool) : ~~~(cons b x) = cons (!b) (~~~x) := by grind
|
||
|
||
theorem cons_or_cons (x y : BitVec w) (a b : Bool) :
|
||
(cons a x) ||| (cons b y) = cons (a || b) (x ||| y) := by grind
|
||
|
||
theorem cons_and_cons (x y : BitVec w) (a b : Bool) :
|
||
(cons a x) &&& (cons b y) = cons (a && b) (x &&& y) := by grind
|
||
|
||
theorem cons_xor_cons (x y : BitVec w) (a b : Bool) :
|
||
(cons a x) ^^^ (cons b y) = cons (a ^^ b) (x ^^^ y) := by grind
|
||
|
||
theorem cons_append (x : BitVec w₁) (y : BitVec w₂) (a : Bool) :
|
||
(cons a x) ++ y = (cons a (x ++ y)).cast (by omega) := by grind
|
||
|
||
theorem cons_append_append (x : BitVec w₁) (y : BitVec w₂) (z : BitVec w₃) (a : Bool) :
|
||
(cons a x) ++ y ++ z = (cons a (x ++ y ++ z)).cast (by omega) := by grind
|
||
|
||
/-! ### concat -/
|
||
|
||
theorem toNat_concat (x : BitVec w) (b : Bool) :
|
||
(concat x b).toNat = x.toNat * 2 + b.toNat := by
|
||
apply Nat.eq_of_testBit_eq
|
||
simp only [concat, toNat_append, Nat.shiftLeft_eq, Nat.pow_one, toNat_ofBool, Nat.testBit_or]
|
||
cases b
|
||
· simp
|
||
· rintro (_ | i)
|
||
<;> simp [Nat.add_comm, Nat.add_mul_div_right, Nat.testBit_add_one]
|
||
|
||
theorem getLsbD_concat (x : BitVec w) (b : Bool) (i : Nat) :
|
||
(concat x b).getLsbD i = if i = 0 then b else x.getLsbD (i - 1) := by
|
||
simp only [concat, getLsbD, toNat_append, toNat_ofBool, Nat.testBit_or, Nat.shiftLeft_eq]
|
||
cases i
|
||
· simp [Nat.mod_eq_of_lt b.toNat_lt]
|
||
· simp [Nat.div_eq_of_lt b.toNat_lt, Nat.testBit_add_one]
|
||
|
||
theorem getElem_concat (x : BitVec w) (b : Bool) (i : Nat) (h : i < w + 1) :
|
||
(concat x b)[i] = if h : i = 0 then b else x[i - 1] := by
|
||
simp only [concat, getElem_eq_testBit_toNat, toNat_append,
|
||
toNat_ofBool, Nat.testBit_or, Nat.shiftLeft_eq]
|
||
cases i
|
||
· simp [Nat.mod_eq_of_lt b.toNat_lt]
|
||
· simp [Nat.div_eq_of_lt b.toNat_lt, Nat.testBit_add_one]
|
||
|
||
theorem getElem_concat_zero {x : BitVec w} : (concat x b)[0] = b := by grind
|
||
|
||
theorem getLsbD_concat_zero : (concat x b).getLsbD 0 = b := by grind
|
||
|
||
theorem getLsbD_concat_succ : (concat x b).getLsbD (i + 1) = x.getLsbD i := by grind
|
||
|
||
theorem getElem_concat_succ {x : BitVec w} {i : Nat} (h : i + 1 < w + 1) :
|
||
(concat x b)[i + 1] = x[i] := by grind
|
||
|
||
theorem getMsbD_concat {i w : Nat} {b : Bool} {x : BitVec w} :
|
||
(x.concat b).getMsbD i = if i < w then x.getMsbD i else decide (i = w) && b := by grind
|
||
|
||
theorem msb_concat {w : Nat} {b : Bool} {x : BitVec w} :
|
||
(x.concat b).msb = if 0 < w then x.msb else b := by grind
|
||
|
||
theorem toInt_concat (x : BitVec w) (b : Bool) :
|
||
(concat x b).toInt = if w = 0 then -b.toInt else x.toInt * 2 + b.toInt := by
|
||
simp only [BitVec.toInt, toNat_concat]
|
||
cases w
|
||
· grind [cases Bool]
|
||
· cases b <;> simp <;> omega
|
||
|
||
theorem toFin_concat (x : BitVec w) (b : Bool) :
|
||
(concat x b).toFin = Fin.mk (x.toNat * 2 + b.toNat) (by
|
||
have := Bool.toNat_lt b
|
||
simp [← Nat.two_pow_pred_add_two_pow_pred]
|
||
omega
|
||
) := by
|
||
simp [← Fin.val_inj]
|
||
|
||
theorem not_concat (x : BitVec w) (b : Bool) : ~~~(concat x b) = concat (~~~x) !b := by grind
|
||
|
||
theorem concat_or_concat (x y : BitVec w) (a b : Bool) :
|
||
(concat x a) ||| (concat y b) = concat (x ||| y) (a || b) := by grind
|
||
|
||
theorem concat_and_concat (x y : BitVec w) (a b : Bool) :
|
||
(concat x a) &&& (concat y b) = concat (x &&& y) (a && b) := by grind
|
||
|
||
theorem concat_xor_concat (x y : BitVec w) (a b : Bool) :
|
||
(concat x a) ^^^ (concat y b) = concat (x ^^^ y) (a ^^ b) := by grind
|
||
|
||
theorem zero_concat_false : concat 0#w false = 0#(w + 1) := by grind
|
||
|
||
/-! ### shiftConcat -/
|
||
|
||
theorem getLsbD_shiftConcat (x : BitVec w) (b : Bool) (i : Nat) :
|
||
(shiftConcat x b).getLsbD i =
|
||
(decide (i < w) && (if (i = 0) then b else x.getLsbD (i - 1))) := by
|
||
grind [shiftConcat]
|
||
|
||
theorem getElem_shiftConcat {x : BitVec w} {b : Bool} (h : i < w) :
|
||
(x.shiftConcat b)[i] = if i = 0 then b else x[i-1] := by
|
||
rw [← getLsbD_eq_getElem, getLsbD_shiftConcat, getLsbD_eq_getElem, decide_eq_true h, Bool.true_and]
|
||
|
||
theorem getElem_shiftConcat_zero {x : BitVec w} (b : Bool) (h : 0 < w) :
|
||
(x.shiftConcat b)[0] = b := by grind
|
||
|
||
theorem getElem_shiftConcat_succ {x : BitVec w} {b : Bool} (h : i + 1 < w) :
|
||
(x.shiftConcat b)[i+1] = x[i] := by grind
|
||
|
||
theorem getLsbD_shiftConcat_eq_decide (x : BitVec w) (b : Bool) (i : Nat) :
|
||
(shiftConcat x b).getLsbD i =
|
||
(decide (i < w) && ((decide (i = 0) && b) || (decide (0 < i) && x.getLsbD (i - 1)))) := by grind
|
||
|
||
theorem shiftRight_sub_one_eq_shiftConcat (n : BitVec w) (hwn : 0 < wn) :
|
||
n >>> (wn - 1) = (n >>> wn).shiftConcat (n.getLsbD (wn - 1)) := by grind
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_shiftConcat {x : BitVec w} {b : Bool} :
|
||
(x.shiftConcat b).toNat =
|
||
(x.toNat <<< 1 + b.toNat) % 2 ^ w := by
|
||
grind [shiftConcat, Nat.shiftLeft_eq]
|
||
|
||
/-- `x.shiftConcat b` does not overflow if `x < 2^k` for `k < w`, and so
|
||
`x.shiftConcat b |>.toNat = x.toNat * 2 + b.toNat`. -/
|
||
theorem toNat_shiftConcat_eq_of_lt {x : BitVec w} {b : Bool} {k : Nat}
|
||
(hk : k < w) (hx : x.toNat < 2 ^ k) :
|
||
(x.shiftConcat b).toNat = x.toNat * 2 + b.toNat := by
|
||
simp only [toNat_shiftConcat, Nat.shiftLeft_eq, Nat.pow_one]
|
||
have : 2 ^ k < 2 ^ w := Nat.pow_lt_pow_of_lt (by omega) (by omega)
|
||
have : 2 ^ k * 2 ≤ 2 ^ w := (Nat.pow_lt_pow_iff_pow_mul_le_pow (by omega)).mp this
|
||
rw [Nat.mod_eq_of_lt (by cases b <;> simp [bitvec_to_nat] <;> omega)]
|
||
|
||
theorem toNat_shiftConcat_lt_of_lt {x : BitVec w} {b : Bool} {k : Nat}
|
||
(hk : k < w) (hx : x.toNat < 2 ^ k) :
|
||
(x.shiftConcat b).toNat < 2 ^ (k + 1) := by
|
||
rw [toNat_shiftConcat_eq_of_lt hk hx]
|
||
have := Bool.toNat_lt b
|
||
omega
|
||
|
||
/-! ### add -/
|
||
|
||
theorem add_def {n} (x y : BitVec n) : x + y = .ofNat n (x.toNat + y.toNat) := rfl
|
||
|
||
/--
|
||
Definition of bitvector addition as a nat.
|
||
-/
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_add (x y : BitVec w) : (x + y).toNat = (x.toNat + y.toNat) % 2^w := rfl
|
||
theorem toFin_add (x y : BitVec w) : (x + y).toFin = toFin x + toFin y := rfl
|
||
theorem ofFin_add (x : Fin (2^n)) (y : BitVec n) :
|
||
.ofFin x + y = .ofFin (x + y.toFin) := rfl
|
||
theorem add_ofFin (x : BitVec n) (y : Fin (2^n)) :
|
||
x + .ofFin y = .ofFin (x.toFin + y) := rfl
|
||
|
||
theorem ofNat_add {n} (x y : Nat) : BitVec.ofNat n (x + y) = BitVec.ofNat n x + BitVec.ofNat n y := by
|
||
apply eq_of_toNat_eq
|
||
simp [BitVec.ofNat, Fin.ofNat_add]
|
||
|
||
theorem ofNat_add_ofNat {n} (x y : Nat) : BitVec.ofNat n x + BitVec.ofNat n y = BitVec.ofNat n (x + y) :=
|
||
(ofNat_add x y).symm
|
||
|
||
theorem toNat_add_of_not_uaddOverflow {x y : BitVec w} (h : ¬ uaddOverflow x y) :
|
||
(x + y).toNat = x.toNat + y.toNat := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· simp only [uaddOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h
|
||
rw [toNat_add, Nat.mod_eq_of_lt h]
|
||
|
||
/--
|
||
Unsigned addition overflow reassociation.
|
||
If `(x + y)` and `(y + z)` do not overflow, then `(x + y) + z` overflows iff `x + (y + z)` overflows.
|
||
-/
|
||
theorem uaddOverflow_assoc {x y z : BitVec w} (h : ¬ x.uaddOverflow y) (h' : ¬ y.uaddOverflow z) :
|
||
(x + y).uaddOverflow z = x.uaddOverflow (y + z) := by
|
||
simp only [uaddOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h h'
|
||
simp only [uaddOverflow, toNat_add, ge_iff_le, decide_eq_decide]
|
||
repeat rw [Nat.mod_eq_of_lt (by grind)]
|
||
grind
|
||
|
||
protected theorem add_assoc (x y z : BitVec n) : x + y + z = x + (y + z) := by
|
||
apply eq_of_toNat_eq ; simp [Nat.add_assoc]
|
||
instance : Std.Associative (α := BitVec n) (· + ·) := ⟨BitVec.add_assoc⟩
|
||
|
||
protected theorem add_comm (x y : BitVec n) : x + y = y + x := by
|
||
simp [add_def, Nat.add_comm]
|
||
instance : Std.Commutative (α := BitVec n) (· + ·) := ⟨BitVec.add_comm⟩
|
||
|
||
@[simp] protected theorem add_zero (x : BitVec n) : x + 0#n = x := by simp [add_def]
|
||
|
||
@[simp] protected theorem zero_add (x : BitVec n) : 0#n + x = x := by simp [add_def]
|
||
instance : Std.LawfulIdentity (α := BitVec n) (· + ·) 0#n where
|
||
left_id := BitVec.zero_add
|
||
right_id := BitVec.add_zero
|
||
|
||
theorem setWidth_add (x y : BitVec w) (h : i ≤ w) :
|
||
(x + y).setWidth i = x.setWidth i + y.setWidth i := by
|
||
have dvd : 2^i ∣ 2^w := Nat.pow_dvd_pow _ h
|
||
simp [bitvec_to_nat, Nat.mod_mod_of_dvd _ dvd]
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toInt_add (x y : BitVec w) :
|
||
(x + y).toInt = (x.toInt + y.toInt).bmod (2^w) := by
|
||
simp [toInt_eq_toNat_bmod, -Int.natCast_pow]
|
||
|
||
theorem ofInt_add {n} (x y : Int) : BitVec.ofInt n (x + y) =
|
||
BitVec.ofInt n x + BitVec.ofInt n y := by
|
||
apply eq_of_toInt_eq
|
||
simp
|
||
|
||
theorem toInt_add_of_not_saddOverflow {x y : BitVec w} (h : ¬ saddOverflow x y) :
|
||
(x + y).toInt = x.toInt + y.toInt := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· simp only [saddOverflow, Nat.add_one_sub_one, ge_iff_le, Bool.or_eq_true, decide_eq_true_eq,
|
||
_root_.not_or, Int.not_le, Int.not_lt] at h
|
||
rw [toInt_add, Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
|
||
|
||
/--
|
||
Signed addition overflow reassociation.
|
||
If `(x + y)` and `(y + z)` do not overflow, then `(x + y) + z` overflows iff `x + (y + z)` overflows.
|
||
-/
|
||
theorem saddOverflow_assoc {x y z : BitVec w} (h : ¬ x.saddOverflow y) (h' : ¬ y.saddOverflow z) :
|
||
(x + y).saddOverflow z = x.saddOverflow (y + z) := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· simp only [saddOverflow, Nat.add_one_sub_one, ge_iff_le, Bool.or_eq_true, decide_eq_true_eq,
|
||
_root_.not_or, Int.not_le, Int.not_lt] at h h'
|
||
simp only [bool_to_prop, saddOverflow, toInt_add, ge_iff_le, Nat.add_one_sub_one]
|
||
repeat rw [Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
|
||
grind
|
||
|
||
theorem shiftLeft_add_distrib {x y : BitVec w} {n : Nat} :
|
||
(x + y) <<< n = x <<< n + y <<< n := by
|
||
induction n
|
||
case zero =>
|
||
simp
|
||
case succ n ih =>
|
||
simp [toNat_eq, Nat.shiftLeft_eq, ← Nat.add_mul]
|
||
|
||
theorem add_eq_xor {a b : BitVec 1} : a + b = a ^^^ b := by
|
||
have ha : a = 0 ∨ a = 1 := eq_zero_or_eq_one _
|
||
have hb : b = 0 ∨ b = 1 := eq_zero_or_eq_one _
|
||
rcases ha with h | h <;> (rcases hb with h' | h' <;> (simp [h, h']))
|
||
|
||
/-! ### sub/neg -/
|
||
|
||
theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n ((2^n - y.toNat) + x.toNat) := rfl
|
||
|
||
theorem toNat_sub {n} (x y : BitVec n) :
|
||
(x - y).toNat = (((2^n - y.toNat) + x.toNat) % 2^n) := rfl
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toInt_sub {x y : BitVec w} :
|
||
(x - y).toInt = (x.toInt - y.toInt).bmod (2 ^ w) := by
|
||
simp [toInt_eq_toNat_bmod, @Int.ofNat_sub y.toNat (2 ^ w) (by omega), -Int.natCast_pow]
|
||
|
||
theorem toNat_sub_of_not_usubOverflow {x y : BitVec w} (h : ¬ usubOverflow x y) :
|
||
(x - y).toNat = x.toNat - y.toNat := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· simp only [usubOverflow, decide_eq_true_eq, Nat.not_lt] at h
|
||
rw [toNat_sub, ← Nat.sub_add_comm (by omega), Nat.add_sub_assoc h, Nat.add_mod_left,
|
||
Nat.mod_eq_of_lt (by omega)]
|
||
|
||
theorem toInt_sub_of_not_ssubOverflow {x y : BitVec w} (h : ¬ ssubOverflow x y) :
|
||
(x - y).toInt = x.toInt - y.toInt := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· simp only [ssubOverflow, Nat.add_one_sub_one, ge_iff_le, Bool.or_eq_true, decide_eq_true_eq,
|
||
_root_.not_or, Int.not_le, Int.not_lt] at h
|
||
rw [toInt_sub, Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
|
||
|
||
theorem toInt_sub_toInt_lt_twoPow_iff {x y : BitVec w} :
|
||
(x.toInt - y.toInt < - 2 ^ (w - 1))
|
||
↔ (x.toInt < 0 ∧ 0 ≤ y.toInt ∧ 0 ≤ (x.toInt - y.toInt).bmod (2 ^ w)) := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· have := le_two_mul_toInt (x := x)
|
||
have := two_mul_toInt_lt (x := y)
|
||
simp only [Nat.add_one_sub_one]
|
||
constructor
|
||
· intro h
|
||
rw_mod_cast [← Int.add_bmod_right, Int.bmod_eq_of_le]
|
||
<;> omega
|
||
· have := Int.bmod_neg_iff (x := x.toInt - y.toInt) (m := 2 ^ (w + 1))
|
||
push_cast at this
|
||
omega
|
||
|
||
theorem twoPow_le_toInt_sub_toInt_iff {x y : BitVec w} :
|
||
(2 ^ (w - 1) ≤ x.toInt - y.toInt)
|
||
↔ (0 ≤ x.toInt ∧ y.toInt < 0 ∧ (x.toInt - y.toInt).bmod (2 ^ w) < 0) := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· have := le_two_mul_toInt (x := x); have := two_mul_toInt_lt (x := x)
|
||
have := le_two_mul_toInt (x := y); have := two_mul_toInt_lt (x := y)
|
||
simp only [Nat.add_one_sub_one]
|
||
constructor
|
||
· intro h
|
||
simp only [show 0 ≤ x.toInt by omega, show y.toInt < 0 by omega, _root_.true_and]
|
||
rw_mod_cast [← Int.sub_bmod_right, Int.bmod_eq_of_le (by omega) (by omega)]
|
||
omega
|
||
· have := Int.bmod_neg_iff (x := x.toInt - y.toInt) (m := 2 ^ (w + 1))
|
||
push_cast at this
|
||
omega
|
||
|
||
-- We prefer this lemma to `toNat_sub` for the `bitvec_to_nat` simp set.
|
||
-- For reasons we don't yet understand, unfolding via `toNat_sub` sometimes
|
||
-- results in `omega` generating proof terms that are very slow in the kernel.
|
||
@[bitvec_to_nat] theorem toNat_sub' {n} (x y : BitVec n) :
|
||
(x - y).toNat = ((x.toNat + (2^n - y.toNat)) % 2^n) := by
|
||
rw [toNat_sub, Nat.add_comm]
|
||
|
||
theorem toFin_sub (x y : BitVec n) : (x - y).toFin = toFin x - toFin y := rfl
|
||
|
||
theorem ofFin_sub (x : Fin (2^n)) (y : BitVec n) : .ofFin x - y = .ofFin (x - y.toFin) :=
|
||
rfl
|
||
theorem sub_ofFin (x : BitVec n) (y : Fin (2^n)) : x - .ofFin y = .ofFin (x.toFin - y) :=
|
||
rfl
|
||
|
||
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
|
||
-- If `x` and `n` are not literals, applying this theorem eagerly may not be a good idea.
|
||
theorem ofNat_sub_ofNat {n} (x y : Nat) : BitVec.ofNat n x - BitVec.ofNat n y = .ofNat n ((2^n - y % 2^n) + x) := by
|
||
apply eq_of_toNat_eq
|
||
simp [BitVec.ofNat]
|
||
|
||
theorem ofNat_sub_ofNat_of_le (x y : Nat) (hy : y < 2 ^ w) (hlt : y ≤ x):
|
||
BitVec.ofNat w x - BitVec.ofNat w y = BitVec.ofNat w (x - y) := by
|
||
apply eq_of_toNat_eq
|
||
simp [Nat.mod_eq_of_lt hy, show 2 ^ w - y + x = 2 ^ w + (x - y) by omega, Nat.add_mod_left]
|
||
|
||
@[simp] protected theorem sub_zero (x : BitVec n) : x - 0#n = x := by apply eq_of_toNat_eq ; simp
|
||
|
||
@[simp] protected theorem zero_sub (x : BitVec n) : 0#n - x = -x := rfl
|
||
|
||
@[simp] protected theorem sub_self (x : BitVec n) : x - x = 0#n := by
|
||
apply eq_of_toNat_eq
|
||
simp only [toNat_sub]
|
||
rw [Nat.add_comm, Nat.add_sub_of_le]
|
||
· simp
|
||
· exact Nat.le_of_lt x.isLt
|
||
|
||
theorem toNat_neg_of_pos {x : BitVec n} (h : 0#n < x) :
|
||
(- x).toNat = 2^n - x.toNat := by
|
||
change 0 < x.toNat at h
|
||
rw [toNat_neg, Nat.mod_eq_of_lt]
|
||
omega
|
||
|
||
theorem toInt_neg {x : BitVec w} :
|
||
(-x).toInt = (-x.toInt).bmod (2 ^ w) := by
|
||
rw [← BitVec.zero_sub, toInt_sub]
|
||
simp
|
||
|
||
theorem toInt_neg_of_not_negOverflow {x : BitVec w} (h : ¬ negOverflow x):
|
||
(-x).toInt = -x.toInt := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· have := toInt_lt (x := x); simp only [Nat.add_one_sub_one] at this
|
||
have := le_toInt (x := x); simp only [Nat.add_one_sub_one] at this
|
||
simp only [negOverflow, Nat.add_one_sub_one, beq_iff_eq] at h
|
||
rw [toInt_neg, Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
|
||
|
||
theorem ofInt_neg {w : Nat} {n : Int} : BitVec.ofInt w (-n) = -BitVec.ofInt w n :=
|
||
eq_of_toInt_eq (by simp [toInt_neg])
|
||
|
||
theorem toFin_neg (x : BitVec n) :
|
||
(-x).toFin = Fin.ofNat (2^n) (2^n - x.toNat) :=
|
||
rfl
|
||
|
||
theorem sub_eq_add_neg {n} (x y : BitVec n) : x - y = x + - y := by
|
||
apply eq_of_toNat_eq
|
||
simp only [toNat_sub, toNat_add, toNat_neg, Nat.add_mod_mod]
|
||
rw [Nat.add_comm]
|
||
|
||
theorem add_left_neg (x : BitVec w) : -x + x = 0#w := by
|
||
apply toInt_inj.mp
|
||
simp [toInt_neg, Int.add_left_neg]
|
||
|
||
theorem add_right_neg (x : BitVec w) : x + -x = 0#w := by
|
||
rw [BitVec.add_comm]
|
||
exact add_left_neg x
|
||
|
||
theorem neg_zero (n : Nat) : -BitVec.ofNat n 0 = BitVec.ofNat n 0 := by apply eq_of_toNat_eq ; simp
|
||
|
||
theorem add_sub_cancel (x y : BitVec w) : x + y - y = x := by
|
||
apply eq_of_toNat_eq
|
||
have y_toNat_le := Nat.le_of_lt y.isLt
|
||
rw [toNat_sub, toNat_add, Nat.add_comm, Nat.mod_add_mod, Nat.add_assoc, ← Nat.add_sub_assoc y_toNat_le,
|
||
Nat.add_sub_cancel_left, Nat.add_mod_right, toNat_mod_cancel]
|
||
|
||
theorem sub_add_cancel (x y : BitVec w) : x - y + y = x := by
|
||
rw [sub_eq_add_neg, BitVec.add_assoc, BitVec.add_comm _ y,
|
||
← BitVec.add_assoc, ← sub_eq_add_neg, add_sub_cancel]
|
||
|
||
theorem eq_sub_iff_add_eq {x y z : BitVec w} : x = z - y ↔ x + y = z := by
|
||
apply Iff.intro <;> intro h
|
||
· simp [h, sub_add_cancel]
|
||
· simp [←h, add_sub_cancel]
|
||
|
||
theorem sub_eq_iff_eq_add {x y z : BitVec w} : x - y = z ↔ x = z + y := by
|
||
apply Iff.intro <;> intro h
|
||
· simp [← h, sub_add_cancel]
|
||
· simp [h, add_sub_cancel]
|
||
|
||
theorem neg_one_eq_allOnes : -1#w = allOnes w := by
|
||
apply eq_of_toNat_eq
|
||
if g : w = 0 then
|
||
simp [g]
|
||
else
|
||
have q : 1 < 2^w := by simp [g]
|
||
have r : (2^w - 1) < 2^w := by omega
|
||
simp [Nat.mod_eq_of_lt q, Nat.mod_eq_of_lt r]
|
||
|
||
theorem neg_eq_not_add (x : BitVec w) : -x = ~~~x + 1#w := by
|
||
apply eq_of_toNat_eq
|
||
simp only [toNat_neg, toNat_add, toNat_not, toNat_ofNat, Nat.add_mod_mod]
|
||
congr
|
||
have hx : x.toNat < 2^w := x.isLt
|
||
rw [Nat.sub_sub, Nat.add_comm 1 x.toNat, ← Nat.sub_sub, Nat.sub_add_cancel (by omega)]
|
||
|
||
theorem not_eq_neg_add (x : BitVec w) : ~~~ x = -x - 1#w := by
|
||
rw [eq_sub_iff_add_eq, neg_eq_not_add, BitVec.add_comm]
|
||
|
||
theorem neg_neg {x : BitVec w} : - - x = x := by
|
||
by_cases h : x = 0#w
|
||
· simp [h]
|
||
· simp [bitvec_to_nat, h]
|
||
|
||
protected theorem neg_inj {x y : BitVec w} : -x = -y ↔ x = y :=
|
||
⟨fun h => by rw [← @neg_neg w x, ← @neg_neg w y, h], congrArg _⟩
|
||
|
||
theorem neg_eq_iff_eq_neg {x y : BitVec w} : -x = y ↔ x = -y := by
|
||
constructor
|
||
all_goals
|
||
intro h
|
||
symm at h
|
||
subst h
|
||
simp
|
||
|
||
theorem neg_ne_iff_ne_neg {x y : BitVec w} : -x ≠ y ↔ x ≠ -y := by
|
||
constructor
|
||
all_goals
|
||
intro h h'
|
||
subst h'
|
||
simp at h
|
||
|
||
theorem neg_eq_zero_iff {x : BitVec w} : -x = 0#w ↔ x = 0#w := by
|
||
constructor
|
||
· intro h
|
||
have : - (- x) = - 0 := by simp [h]
|
||
simpa using this
|
||
· intro h
|
||
simp [h]
|
||
|
||
theorem sub_eq_xor {a b : BitVec 1} : a - b = a ^^^ b := by
|
||
have ha : a = 0 ∨ a = 1 := eq_zero_or_eq_one _
|
||
have hb : b = 0 ∨ b = 1 := eq_zero_or_eq_one _
|
||
rcases ha with h | h <;> (rcases hb with h' | h' <;> (simp [h, h']))
|
||
|
||
theorem sub_eq_self {x : BitVec 1} : -x = x := by
|
||
have ha : x = 0 ∨ x = 1 := eq_zero_or_eq_one _
|
||
rcases ha with h | h <;> simp [h]
|
||
|
||
theorem not_neg (x : BitVec w) : ~~~(-x) = x - 1#w := by
|
||
rw [not_eq_neg_add, neg_neg]
|
||
|
||
theorem neg_add {x y : BitVec w} : - (x + y) = - x - y := by
|
||
apply eq_of_toInt_eq
|
||
simp [toInt_neg, toInt_add, Int.neg_add, Int.add_neg_eq_sub]
|
||
|
||
theorem add_neg_eq_sub {x y : BitVec w} : x + - y = (x - y) := by
|
||
apply eq_of_toInt_eq
|
||
simp [toInt_neg, Int.sub_eq_add_neg]
|
||
|
||
theorem sub_neg {x y : BitVec w} : x - - y = x + y := by
|
||
apply eq_of_toInt_eq
|
||
simp [toInt_neg]
|
||
|
||
theorem neg_sub {x y : BitVec w} : - (x - y) = - x + y := by
|
||
rw [sub_eq_add_neg, neg_add, sub_neg]
|
||
|
||
/- ### add/sub injectivity -/
|
||
|
||
protected theorem add_left_inj {x y : BitVec w} (z : BitVec w) : (x + z = y + z) ↔ x = y := by
|
||
apply Iff.intro
|
||
· intro p
|
||
rw [← add_sub_cancel x z, ← add_sub_cancel y z, p]
|
||
· exact congrArg (· + z)
|
||
|
||
protected theorem add_right_inj {x y : BitVec w} (z : BitVec w) : (z + x = z + y) ↔ x = y := by
|
||
simp [BitVec.add_comm z]
|
||
|
||
protected theorem sub_left_inj {x y : BitVec w} (z : BitVec w) : (x - z = y - z) ↔ x = y := by
|
||
simp [sub_eq_add_neg]
|
||
|
||
protected theorem sub_right_inj {x y : BitVec w} (z : BitVec w) : (z - x = z - y) ↔ x = y := by
|
||
simp [sub_eq_add_neg]
|
||
|
||
/-! ### add self -/
|
||
|
||
protected theorem add_left_eq_self {x y : BitVec w} : x + y = y ↔ x = 0#w := by
|
||
conv => lhs; rhs; rw [← BitVec.zero_add y]
|
||
exact BitVec.add_left_inj y
|
||
|
||
protected theorem add_right_eq_self {x y : BitVec w} : x + y = x ↔ y = 0#w := by
|
||
rw [BitVec.add_comm]
|
||
exact BitVec.add_left_eq_self
|
||
|
||
protected theorem self_eq_add_right {x y : BitVec w} : x = x + y ↔ y = 0#w := by
|
||
rw [Eq.comm]
|
||
exact BitVec.add_right_eq_self
|
||
|
||
protected theorem self_eq_add_left {x y : BitVec w} : x = y + x ↔ y = 0#w := by
|
||
rw [BitVec.add_comm]
|
||
exact BitVec.self_eq_add_right
|
||
|
||
/-! ### fill -/
|
||
|
||
theorem getLsbD_fill {w i : Nat} {v : Bool} :
|
||
(fill w v).getLsbD i = (v && decide (i < w)) := by
|
||
grind [cases Bool, BitVec.fill, BitVec.neg_one_eq_allOnes]
|
||
|
||
theorem getMsbD_fill {w i : Nat} {v : Bool} :
|
||
(fill w v).getMsbD i = (v && decide (i < w)) := by grind
|
||
|
||
theorem getElem_fill {w i : Nat} {v : Bool} (h : i < w) :
|
||
(fill w v)[i] = v := by
|
||
grind [cases Bool, BitVec.fill, BitVec.neg_one_eq_allOnes]
|
||
|
||
theorem msb_fill {w : Nat} {v : Bool} :
|
||
(fill w v).msb = (v && decide (0 < w)) := by grind
|
||
|
||
theorem fill_eq {w : Nat} {v : Bool} : fill w v = if v = true then allOnes w else 0#w := by grind
|
||
|
||
theorem fill_true {w : Nat} : fill w true = allOnes w := by grind
|
||
|
||
theorem fill_false {w : Nat} : fill w false = 0#w := by grind
|
||
|
||
theorem fill_toNat {w : Nat} {v : Bool} :
|
||
(fill w v).toNat = if v = true then 2^w - 1 else 0 := by
|
||
by_cases h : v <;> simp [h]
|
||
|
||
theorem fill_toInt {w : Nat} {v : Bool} :
|
||
(fill w v).toInt = if v = true && 0 < w then -1 else 0 := by
|
||
by_cases h : v <;> simp [h]
|
||
|
||
theorem fill_toFin {w : Nat} {v : Bool} :
|
||
(fill w v).toFin = if v = true then (allOnes w).toFin else Fin.ofNat (2 ^ w) 0 := by
|
||
by_cases h : v <;> simp [h]
|
||
|
||
/-! ### mul -/
|
||
|
||
theorem mul_def {n} {x y : BitVec n} : x * y = (ofFin <| x.toFin * y.toFin) := rfl
|
||
|
||
@[simp, bitvec_to_nat] theorem toNat_mul (x y : BitVec n) : (x * y).toNat = (x.toNat * y.toNat) % 2 ^ n := rfl
|
||
theorem toFin_mul (x y : BitVec n) : (x * y).toFin = (x.toFin * y.toFin) := rfl
|
||
|
||
theorem ofNat_mul {n} (x y : Nat) : BitVec.ofNat n (x * y) = BitVec.ofNat n x * BitVec.ofNat n y := by
|
||
apply eq_of_toNat_eq
|
||
simp [BitVec.ofNat]
|
||
|
||
theorem ofNat_mul_ofNat {n} (x y : Nat) : BitVec.ofNat n x * BitVec.ofNat n y = BitVec.ofNat n (x * y) :=
|
||
(ofNat_mul x y).symm
|
||
|
||
protected theorem mul_comm (x y : BitVec w) : x * y = y * x := by
|
||
apply eq_of_toFin_eq; simpa using Fin.mul_comm ..
|
||
instance : Std.Commutative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_comm⟩
|
||
|
||
protected theorem mul_assoc (x y z : BitVec w) : x * y * z = x * (y * z) := by
|
||
apply eq_of_toFin_eq; simpa using Fin.mul_assoc ..
|
||
instance : Std.Associative (fun (x y : BitVec w) => x * y) := ⟨BitVec.mul_assoc⟩
|
||
|
||
@[simp] protected theorem mul_one (x : BitVec w) : x * 1#w = x := by
|
||
cases w
|
||
· apply Subsingleton.elim
|
||
· apply eq_of_toNat_eq; simp [Nat.mod_eq_of_lt]
|
||
|
||
@[simp] protected theorem one_mul (x : BitVec w) : 1#w * x = x := by
|
||
rw [BitVec.mul_comm, BitVec.mul_one]
|
||
|
||
instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) where
|
||
right_id := BitVec.mul_one
|
||
|
||
theorem mul_zero {x : BitVec w} : x * 0#w = 0#w := by
|
||
apply eq_of_toNat_eq
|
||
simp [toNat_mul]
|
||
|
||
theorem zero_mul {x : BitVec w} : 0#w * x = 0#w := by
|
||
apply eq_of_toNat_eq
|
||
simp [toNat_mul]
|
||
|
||
theorem mul_add {x y z : BitVec w} :
|
||
x * (y + z) = x * y + x * z := by
|
||
apply eq_of_toNat_eq
|
||
simp only [toNat_mul, toNat_add, Nat.add_mod_mod, Nat.mod_add_mod]
|
||
rw [Nat.mul_mod, Nat.mod_mod (y.toNat + z.toNat),
|
||
← Nat.mul_mod, Nat.mul_add]
|
||
|
||
theorem add_mul {x y z : BitVec w} :
|
||
(x + y) * z = x * z + y * z := by
|
||
rw [BitVec.mul_comm, mul_add, BitVec.mul_comm z, BitVec.mul_comm z]
|
||
|
||
theorem mul_succ {x y : BitVec w} : x * (y + 1#w) = x * y + x := by simp [mul_add]
|
||
theorem succ_mul {x y : BitVec w} : (x + 1#w) * y = x * y + y := by simp [BitVec.mul_comm, BitVec.mul_add]
|
||
|
||
theorem mul_two {x : BitVec w} : x * 2#w = x + x := by
|
||
have : 2#w = 1#w + 1#w := by apply BitVec.eq_of_toNat_eq; simp
|
||
simp [this, mul_succ]
|
||
|
||
theorem two_mul {x : BitVec w} : 2#w * x = x + x := by rw [BitVec.mul_comm, mul_two]
|
||
|
||
@[simp, bitvec_to_nat] theorem toInt_mul (x y : BitVec w) :
|
||
(x * y).toInt = (x.toInt * y.toInt).bmod (2^w) := by
|
||
simp [toInt_eq_toNat_bmod, -Int.natCast_pow]
|
||
|
||
theorem toNat_mul_of_not_umulOverflow {x y : BitVec w} (h : ¬ umulOverflow x y) :
|
||
(x * y).toNat = x.toNat * y.toNat := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· simp only [umulOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h
|
||
rw [toNat_mul, Nat.mod_eq_of_lt h]
|
||
|
||
/--
|
||
Unsigned multiplication overflow reassociation.
|
||
If `(x * y)` and `(y * z)` do not overflow, then `(x * y) * z` overflows iff `x * (y * z)` overflows.
|
||
-/
|
||
theorem umulOverflow_assoc {x y z : BitVec w} (h : ¬ x.umulOverflow y) (h' : ¬ y.umulOverflow z) :
|
||
(x * y).umulOverflow z = x.umulOverflow (y * z) := by
|
||
simp only [umulOverflow, ge_iff_le, decide_eq_true_eq, Nat.not_le] at h h'
|
||
simp only [umulOverflow, toNat_mul, ge_iff_le, decide_eq_decide]
|
||
repeat rw [Nat.mod_eq_of_lt (by omega)]
|
||
rw [Nat.mul_assoc]
|
||
|
||
theorem toInt_mul_of_not_smulOverflow {x y : BitVec w} (h : ¬ smulOverflow x y) :
|
||
(x * y).toInt = x.toInt * y.toInt := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· simp only [smulOverflow, Nat.add_one_sub_one, ge_iff_le, Bool.or_eq_true, decide_eq_true_eq,
|
||
_root_.not_or, Int.not_le, Int.not_lt] at h
|
||
rw [toInt_mul, Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
|
||
|
||
/--
|
||
Signed multiplication overflow reassociation.
|
||
If `(x * y)` and `(y * z)` do not overflow, then `(x * y) * z` overflows iff `x * (y * z)` overflows.
|
||
-/
|
||
theorem smulOverflow_assoc {x y z : BitVec w} (h : ¬ x.smulOverflow y) (h' : ¬ y.smulOverflow z) :
|
||
(x * y).smulOverflow z = x.smulOverflow (y * z) := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· simp only [smulOverflow, Nat.add_one_sub_one, ge_iff_le, Bool.or_eq_true, decide_eq_true_eq,
|
||
_root_.not_or, Int.not_le, Int.not_lt] at h h'
|
||
simp only [smulOverflow, toInt_mul, Nat.add_one_sub_one, ge_iff_le, bool_to_prop]
|
||
repeat rw [Int.bmod_eq_of_le (by push_cast; omega) (by push_cast; omega)]
|
||
rw [Int.mul_assoc]
|
||
|
||
theorem ofInt_mul {n} (x y : Int) : BitVec.ofInt n (x * y) =
|
||
BitVec.ofInt n x * BitVec.ofInt n y := by
|
||
apply eq_of_toInt_eq
|
||
simp
|
||
|
||
theorem mul_eq_and {a b : BitVec 1} : a * b = a &&& b := by
|
||
have ha : a = 0 ∨ a = 1 := eq_zero_or_eq_one _
|
||
have hb : b = 0 ∨ b = 1 := eq_zero_or_eq_one _
|
||
rcases ha with h | h <;> (rcases hb with h' | h' <;> (simp [h, h']))
|
||
|
||
@[simp] protected theorem neg_mul (x y : BitVec w) : -x * y = -(x * y) := by
|
||
apply eq_of_toInt_eq
|
||
simp [toInt_neg, Int.neg_mul]
|
||
|
||
@[simp] protected theorem mul_neg (x y : BitVec w) : x * -y = -(x * y) := by
|
||
rw [BitVec.mul_comm, BitVec.neg_mul, BitVec.mul_comm]
|
||
|
||
protected theorem neg_mul_neg (x y : BitVec w) : -x * -y = x * y := by simp
|
||
|
||
protected theorem neg_mul_comm (x y : BitVec w) : -x * y = x * -y := by simp
|
||
|
||
theorem mul_sub {x y z : BitVec w} :
|
||
x * (y - z) = x * y - x * z := by
|
||
rw [← add_neg_eq_sub, mul_add, BitVec.mul_neg, add_neg_eq_sub]
|
||
|
||
theorem neg_add_mul_eq_mul_not {x y : BitVec w} :
|
||
- (x + x * y) = x * ~~~ y := by
|
||
rw [neg_add, sub_eq_add_neg, ← BitVec.mul_neg, neg_eq_not_add y, mul_add,
|
||
BitVec.mul_one, BitVec.add_comm, BitVec.add_assoc,
|
||
BitVec.add_right_eq_self, add_neg_eq_sub, BitVec.sub_self]
|
||
|
||
theorem neg_mul_not_eq_add_mul {x y : BitVec w} :
|
||
- (x * ~~~ y) = x + x * y := by
|
||
rw [not_eq_neg_add, mul_sub, neg_sub, ← BitVec.mul_neg, neg_neg,
|
||
BitVec.mul_one, BitVec.add_comm]
|
||
|
||
theorem neg_eq_neg_one_mul (b : BitVec w) : -b = -1#w * b :=
|
||
BitVec.eq_of_toInt_eq (by simp)
|
||
|
||
theorem setWidth_mul (x y : BitVec w) (h : i ≤ w) :
|
||
(x * y).setWidth i = x.setWidth i * y.setWidth i := by
|
||
have dvd : 2^i ∣ 2^w := Nat.pow_dvd_pow _ h
|
||
simp [bitvec_to_nat, Nat.mod_mod_of_dvd _ dvd]
|
||
|
||
/-! ### pow -/
|
||
|
||
protected theorem pow_zero {x : BitVec w} : x ^ 0 = 1#w := rfl
|
||
|
||
protected theorem pow_succ {x : BitVec w} : x ^ (n + 1) = x ^ n * x := rfl
|
||
|
||
protected theorem pow_one {x : BitVec w} : x ^ 1 = x := by simp [BitVec.pow_succ]
|
||
|
||
protected theorem pow_add {x : BitVec w} {n m : Nat}: x ^ (n + m) = (x ^ n) * (x ^ m):= by
|
||
induction m with
|
||
| zero => simp
|
||
| succ m ih =>
|
||
rw [← Nat.add_assoc, BitVec.pow_succ, ih, BitVec.mul_assoc, BitVec.pow_succ]
|
||
|
||
/-! ### le and lt -/
|
||
|
||
@[bitvec_to_nat] theorem le_def {x y : BitVec n} :
|
||
x ≤ y ↔ x.toNat ≤ y.toNat := Iff.rfl
|
||
|
||
theorem le_ofFin {x : BitVec n} {y : Fin (2^n)} :
|
||
x ≤ BitVec.ofFin y ↔ x.toFin ≤ y := Iff.rfl
|
||
theorem ofFin_le {x : Fin (2^n)} {y : BitVec n} :
|
||
BitVec.ofFin x ≤ y ↔ x ≤ y.toFin := Iff.rfl
|
||
theorem ofNat_le_ofNat {n} {x y : Nat} : (BitVec.ofNat n x) ≤ (BitVec.ofNat n y) ↔ x % 2^n ≤ y % 2^n := by
|
||
simp [le_def]
|
||
|
||
@[bitvec_to_nat] theorem lt_def {x y : BitVec n} :
|
||
x < y ↔ x.toNat < y.toNat := Iff.rfl
|
||
|
||
theorem lt_ofFin {x : BitVec n} {y : Fin (2^n)} :
|
||
x < BitVec.ofFin y ↔ x.toFin < y := Iff.rfl
|
||
theorem ofFin_lt {x : Fin (2^n)} {y : BitVec n} :
|
||
BitVec.ofFin x < y ↔ x < y.toFin := Iff.rfl
|
||
theorem ofNat_lt_ofNat {n} {x y : Nat} : BitVec.ofNat n x < BitVec.ofNat n y ↔ x % 2^n < y % 2^n := by
|
||
simp [lt_def]
|
||
|
||
@[simp] protected theorem not_le {x y : BitVec n} : ¬ x ≤ y ↔ y < x := by
|
||
simp [le_def, lt_def]
|
||
|
||
@[simp] protected theorem not_lt {x y : BitVec n} : ¬ x < y ↔ y ≤ x := by
|
||
simp [le_def, lt_def]
|
||
|
||
@[simp] protected theorem le_refl (x : BitVec n) : x ≤ x := by
|
||
simp [le_def]
|
||
|
||
@[simp] protected theorem lt_irrefl (x : BitVec n) : ¬x < x := by
|
||
simp [lt_def]
|
||
|
||
protected theorem le_trans {x y z : BitVec n} : x ≤ y → y ≤ z → x ≤ z := by
|
||
simp only [le_def]
|
||
apply Nat.le_trans
|
||
|
||
protected theorem lt_trans {x y z : BitVec n} : x < y → y < z → x < z := by
|
||
simp only [lt_def]
|
||
apply Nat.lt_trans
|
||
|
||
protected theorem le_total (x y : BitVec n) : x ≤ y ∨ y ≤ x := by
|
||
simp only [le_def]
|
||
apply Nat.le_total
|
||
|
||
protected theorem le_antisymm {x y : BitVec n} : x ≤ y → y ≤ x → x = y := by
|
||
simp only [le_def, BitVec.toNat_eq]
|
||
apply Nat.le_antisymm
|
||
|
||
protected theorem lt_asymm {x y : BitVec n} : x < y → ¬ y < x := by
|
||
simp only [lt_def]
|
||
apply Nat.lt_asymm
|
||
|
||
protected theorem lt_of_le_ne {x y : BitVec n} : x ≤ y → ¬ x = y → x < y := by
|
||
simp only [lt_def, le_def, BitVec.toNat_eq]
|
||
apply Nat.lt_of_le_of_ne
|
||
|
||
protected theorem ne_of_lt {x y : BitVec n} : x < y → x ≠ y := by
|
||
simp only [lt_def, ne_eq, toNat_eq]
|
||
apply Nat.ne_of_lt
|
||
|
||
protected theorem umod_lt (x : BitVec n) {y : BitVec n} : 0 < y → x % y < y := by
|
||
simp only [ofNat_eq_ofNat, lt_def, toNat_ofNat, Nat.zero_mod]
|
||
apply Nat.mod_lt
|
||
|
||
theorem not_lt_iff_le {x y : BitVec w} : (¬ x < y) ↔ y ≤ x := by
|
||
constructor <;>
|
||
(intro h; simp only [lt_def, Nat.not_lt, le_def] at h ⊢; omega)
|
||
|
||
protected theorem le_of_lt {x y : BitVec w} (h : x < y) : x ≤ y := Nat.le_of_lt h
|
||
|
||
protected theorem le_of_eq {x y : BitVec w} (h : x = y) : x ≤ y :=
|
||
Nat.le_of_eq (toNat_eq.mp h)
|
||
|
||
theorem not_lt_zero {x : BitVec w} : ¬x < 0#w := of_decide_eq_false rfl
|
||
|
||
theorem le_zero_iff {x : BitVec w} : x ≤ 0#w ↔ x = 0#w := by
|
||
constructor
|
||
· intro h
|
||
have : x ≥ 0 := not_lt_iff_le.mp not_lt_zero
|
||
exact Eq.symm (BitVec.le_antisymm this h)
|
||
· simp_all
|
||
|
||
theorem lt_one_iff {x : BitVec w} (h : 0 < w) : x < 1#w ↔ x = 0#w := by
|
||
constructor
|
||
· intro h₂
|
||
rw [lt_def, toNat_ofNat, ← Int.ofNat_lt, Int.natCast_emod, Int.ofNat_one, Int.natCast_pow,
|
||
Int.ofNat_two, @Int.emod_eq_of_lt 1 (2^w) (by omega) (by omega)] at h₂
|
||
simp [toNat_eq, show x.toNat = 0 by omega]
|
||
· simp_all
|
||
|
||
theorem not_allOnes_lt {x : BitVec w} : ¬allOnes w < x := by
|
||
have : 2^w ≠ 0 := Ne.symm (NeZero.ne' (2^w))
|
||
rw [BitVec.not_lt, le_def, Nat.le_iff_lt_add_one, toNat_allOnes, Nat.sub_one_add_one this]
|
||
exact isLt x
|
||
|
||
theorem allOnes_le_iff {x : BitVec w} : allOnes w ≤ x ↔ x = allOnes w := by
|
||
constructor
|
||
· intro h
|
||
have : x ≤ allOnes w := not_lt_iff_le.mp not_allOnes_lt
|
||
exact Eq.symm (BitVec.le_antisymm h this)
|
||
· simp_all
|
||
|
||
theorem lt_allOnes_iff {x : BitVec w} : x < allOnes w ↔ x ≠ allOnes w := by
|
||
have := not_congr (@allOnes_le_iff w x)
|
||
rw [BitVec.not_le] at this
|
||
exact this
|
||
|
||
theorem le_of_zero_length (h : w = 0) {x y : BitVec w} : x ≤ y := by
|
||
exact BitVec.le_of_eq (eq_of_zero_length h)
|
||
|
||
theorem pos_of_msb {x : BitVec w} (hx : x.msb = true) : 0#w < x := by
|
||
apply Decidable.by_contra
|
||
intro h
|
||
rw [BitVec.not_lt, le_zero_iff] at h
|
||
simp [h] at hx
|
||
|
||
theorem lt_of_msb_false_of_msb_true {x y : BitVec w} (hx : x.msb = false) (hy : y.msb = true) :
|
||
x < y := by
|
||
simp only [LT.lt]
|
||
have := toNat_ge_of_msb_true hy
|
||
have := toNat_lt_of_msb_false hx
|
||
simp
|
||
omega
|
||
|
||
/-! ### udiv -/
|
||
|
||
theorem udiv_def {x y : BitVec n} : x / y = BitVec.ofNat n (x.toNat / y.toNat) := by
|
||
have h : x.toNat / y.toNat < 2 ^ n := Nat.lt_of_le_of_lt (Nat.div_le_self ..) (by omega)
|
||
rw [← udiv_eq]
|
||
simp [udiv, bitvec_to_nat, h, Nat.mod_eq_of_lt]
|
||
|
||
theorem toFin_udiv {x y : BitVec n} : (x / y).toFin = x.toFin / y.toFin := by
|
||
rfl
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_udiv {x y : BitVec n} : (x / y).toNat = x.toNat / y.toNat := by
|
||
rfl
|
||
|
||
theorem zero_udiv {x : BitVec w} : (0#w) / x = 0#w := by
|
||
simp [bitvec_to_nat]
|
||
|
||
theorem udiv_zero {x : BitVec n} : x / 0#n = 0#n := by
|
||
simp [udiv_def]
|
||
|
||
theorem udiv_one {x : BitVec w} : x / 1#w = x := by
|
||
simp only [toNat_eq, toNat_udiv, toNat_ofNat]
|
||
cases w
|
||
· simp [eq_nil x]
|
||
· simp
|
||
|
||
theorem udiv_eq_and {x y : BitVec 1} :
|
||
x / y = (x &&& y) := by
|
||
have hx : x = 0#1 ∨ x = 1#1 := by bv_omega
|
||
have hy : y = 0#1 ∨ y = 1#1 := by bv_omega
|
||
rcases hx with rfl | rfl <;>
|
||
rcases hy with rfl | rfl <;>
|
||
rfl
|
||
|
||
theorem udiv_self {x : BitVec w} :
|
||
x / x = if x == 0#w then 0#w else 1#w := by
|
||
by_cases h : x = 0#w
|
||
· simp [h]
|
||
· simp only [toNat_eq, toNat_ofNat, Nat.zero_mod] at h
|
||
simp only [beq_iff_eq, toNat_eq, toNat_ofNat, Nat.zero_mod, h,
|
||
↓reduceIte, toNat_udiv]
|
||
rw [Nat.div_self (by omega), Nat.mod_eq_of_lt (by omega)]
|
||
|
||
theorem msb_udiv (x y : BitVec w) :
|
||
(x / y).msb = (x.msb && y == 1#w) := by
|
||
cases msb_x : x.msb
|
||
· suffices x.toNat / y.toNat < 2 ^ (w - 1) by simpa [msb_eq_decide]
|
||
calc
|
||
x.toNat / y.toNat ≤ x.toNat := by apply Nat.div_le_self
|
||
_ < 2 ^ (w - 1) := by simpa [msb_eq_decide] using msb_x
|
||
. rcases w with _|w
|
||
· contradiction
|
||
· have : (y == 1#_) = decide (y.toNat = 1) := by
|
||
simp [(· == ·), toNat_eq]
|
||
simp only [this, Bool.true_and]
|
||
match hy : y.toNat with
|
||
| 0 =>
|
||
obtain rfl : y = 0#_ := eq_of_toNat_eq hy
|
||
simp
|
||
| 1 =>
|
||
obtain rfl : y = 1#_ := eq_of_toNat_eq (by simp [hy])
|
||
simpa using msb_x
|
||
| y + 2 =>
|
||
suffices x.toNat / (y + 2) < 2 ^ w by
|
||
simp_all [msb_eq_decide]
|
||
calc
|
||
x.toNat / (y + 2)
|
||
≤ x.toNat / 2 := by apply Nat.div_add_le_right (by omega)
|
||
_ < 2 ^ w := by omega
|
||
|
||
theorem msb_udiv_eq_false_of {x : BitVec w} {y : BitVec w} (h : x.msb = false) :
|
||
(x / y).msb = false := by
|
||
simp [msb_udiv, h]
|
||
|
||
/--
|
||
If `x` is nonnegative (i.e., does not have its msb set),
|
||
then `x / y` is nonnegative, thus `toInt` and `toNat` coincide.
|
||
-/
|
||
theorem toInt_udiv_of_msb {x : BitVec w} (h : x.msb = false) (y : BitVec w) :
|
||
(x / y).toInt = x.toNat / y.toNat := by
|
||
simp [toInt_eq_msb_cond, msb_udiv_eq_false_of h]
|
||
|
||
/-- Unsigned division is zero if and only if either the denominator is zero,
|
||
or the numerator is unsigned less than the denominator -/
|
||
theorem udiv_eq_zero_iff_eq_zero_or_lt {x y : BitVec w} :
|
||
x / y = 0#w ↔ (y = 0#w ∨ x < y) := by
|
||
simp [toNat_eq, toNat_udiv, toNat_ofNat, Nat.zero_mod, Nat.div_eq_zero_iff, BitVec.lt_def]
|
||
|
||
theorem udiv_ne_zero_iff_ne_zero_and_le {x y : BitVec w} :
|
||
¬ (x / y = 0#w) ↔ (y ≠ 0#w ∧ y ≤ x) := by
|
||
simp only [ne_eq, udiv_eq_zero_iff_eq_zero_or_lt, _root_.not_or, BitVec.not_lt]
|
||
|
||
/-! ### umod -/
|
||
|
||
theorem umod_def {x y : BitVec n} :
|
||
x % y = BitVec.ofNat n (x.toNat % y.toNat) := by
|
||
rw [← umod_eq]
|
||
have h : x.toNat % y.toNat < 2 ^ n := Nat.lt_of_le_of_lt (Nat.mod_le _ _) x.isLt
|
||
simp [umod, bitvec_to_nat, Nat.mod_eq_of_lt h]
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_umod {x y : BitVec n} :
|
||
(x % y).toNat = x.toNat % y.toNat := rfl
|
||
|
||
theorem toFin_umod {x y : BitVec w} :
|
||
(x % y).toFin = x.toFin % y.toFin := rfl
|
||
|
||
theorem umod_zero {x : BitVec n} : x % 0#n = x := by
|
||
simp [umod_def]
|
||
|
||
theorem zero_umod {x : BitVec w} : (0#w) % x = 0#w := by
|
||
simp [bitvec_to_nat]
|
||
|
||
theorem umod_one {x : BitVec w} : x % (1#w) = 0#w := by
|
||
simp only [toNat_eq, toNat_umod, toNat_ofNat, Nat.zero_mod]
|
||
cases w
|
||
· simp [eq_nil x]
|
||
· simp [Nat.mod_one]
|
||
|
||
theorem umod_self {x : BitVec w} : x % x = 0#w := by
|
||
simp [bitvec_to_nat]
|
||
|
||
theorem umod_eq_and {x y : BitVec 1} : x % y = x &&& (~~~y) := by
|
||
have hx : x = 0#1 ∨ x = 1#1 := by bv_omega
|
||
have hy : y = 0#1 ∨ y = 1#1 := by bv_omega
|
||
rcases hx with rfl | rfl <;>
|
||
rcases hy with rfl | rfl <;>
|
||
rfl
|
||
|
||
theorem umod_eq_of_lt {x y : BitVec w} (h : x < y) :
|
||
x % y = x := by
|
||
apply eq_of_toNat_eq
|
||
simp [Nat.mod_eq_of_lt h]
|
||
|
||
theorem msb_umod {x y : BitVec w} :
|
||
(x % y).msb = (x.msb && (x < y || y == 0#w)) := by
|
||
rw [msb_eq_decide, toNat_umod]
|
||
cases msb_x : x.msb
|
||
· suffices x.toNat % y.toNat < 2 ^ (w - 1) by simpa
|
||
calc
|
||
x.toNat % y.toNat ≤ x.toNat := by apply Nat.mod_le
|
||
_ < 2 ^ (w - 1) := by simpa [msb_eq_decide] using msb_x
|
||
. by_cases hy : y = 0
|
||
· simp_all [msb_eq_decide]
|
||
· suffices 2 ^ (w - 1) ≤ x.toNat % y.toNat ↔ x < y by simp_all
|
||
by_cases x_lt_y : x < y
|
||
. simp_all [Nat.mod_eq_of_lt x_lt_y, msb_eq_decide]
|
||
· suffices x.toNat % y.toNat < 2 ^ (w - 1) by
|
||
simpa [x_lt_y]
|
||
have y_le_x : y.toNat ≤ x.toNat := by
|
||
simpa using x_lt_y
|
||
replace hy : y.toNat ≠ 0 :=
|
||
toNat_ne_iff_ne.mpr hy
|
||
by_cases msb_y : y.toNat < 2 ^ (w - 1)
|
||
· have : x.toNat % y.toNat < y.toNat := Nat.mod_lt _ (by omega)
|
||
omega
|
||
· rcases w with _|w
|
||
· contradiction
|
||
simp only [Nat.add_one_sub_one]
|
||
replace msb_y : 2 ^ w ≤ y.toNat := by
|
||
simpa using msb_y
|
||
have : y.toNat ≤ y.toNat * (x.toNat / y.toNat) := by
|
||
apply Nat.le_mul_of_pos_right
|
||
apply Nat.div_pos y_le_x
|
||
omega
|
||
have : x.toNat % y.toNat ≤ x.toNat - y.toNat := by
|
||
rw [Nat.mod_eq_sub]; omega
|
||
omega
|
||
|
||
theorem toInt_umod {x y : BitVec w} :
|
||
(x % y).toInt = (x.toNat % y.toNat : Int).bmod (2 ^ w) := by
|
||
simp [toInt_eq_toNat_bmod]
|
||
|
||
theorem toInt_umod_of_msb {x y : BitVec w} (h : x.msb = false) :
|
||
(x % y).toInt = x.toInt % y.toNat := by
|
||
simp [toInt_eq_msb_cond, h]
|
||
|
||
theorem msb_umod_of_msb_false_of_ne_zero {x y : BitVec w} (hmsb : y.msb = false) (h_ne_zero : y ≠ 0#w) :
|
||
(x % y).msb = false := by
|
||
simp only [msb_umod, Bool.and_eq_false_imp, Bool.or_eq_false_iff, beq_eq_false_iff_ne,
|
||
ne_eq, h_ne_zero]
|
||
intro h
|
||
simp [BitVec.le_of_lt, lt_of_msb_false_of_msb_true hmsb h]
|
||
|
||
/-! ### smtUDiv -/
|
||
|
||
theorem smtUDiv_eq (x y : BitVec w) : smtUDiv x y = if y = 0#w then allOnes w else x / y := by
|
||
simp [smtUDiv]
|
||
|
||
theorem smtUDiv_zero {x : BitVec n} : x.smtUDiv 0#n = allOnes n := rfl
|
||
|
||
/-! ### sdiv -/
|
||
|
||
/-- Equation theorem for `sdiv` in terms of `udiv`. -/
|
||
theorem sdiv_eq (x y : BitVec w) : x.sdiv y =
|
||
match x.msb, y.msb with
|
||
| false, false => udiv x y
|
||
| false, true => - (x.udiv (- y))
|
||
| true, false => - ((- x).udiv y)
|
||
| true, true => (- x).udiv (- y) := by
|
||
rw [BitVec.sdiv]
|
||
rcases x.msb <;> rcases y.msb <;> simp
|
||
|
||
@[bitvec_to_nat]
|
||
theorem toNat_sdiv {x y : BitVec w} : (x.sdiv y).toNat =
|
||
match x.msb, y.msb with
|
||
| false, false => (udiv x y).toNat
|
||
| false, true => (- (x.udiv (- y))).toNat
|
||
| true, false => (- ((- x).udiv y)).toNat
|
||
| true, true => ((- x).udiv (- y)).toNat := by
|
||
simp only [sdiv_eq]
|
||
by_cases h : x.msb <;> by_cases h' : y.msb <;> simp [h, h']
|
||
|
||
theorem zero_sdiv {x : BitVec w} : (0#w).sdiv x = 0#w := by
|
||
simp only [sdiv_eq]
|
||
rcases x.msb with msb | msb <;> simp
|
||
|
||
theorem sdiv_zero {x : BitVec n} : x.sdiv 0#n = 0#n := by
|
||
simp only [sdiv_eq, msb_zero]
|
||
rcases x.msb with msb | msb <;> apply eq_of_toNat_eq <;> simp
|
||
|
||
theorem sdiv_one {x : BitVec w} : x.sdiv 1#w = x := by
|
||
simp only [sdiv_eq]
|
||
· by_cases h : w = 1
|
||
· subst h
|
||
rcases x.msb with msb | msb <;> simp
|
||
· rcases x.msb with msb | msb <;> simp [h]
|
||
|
||
theorem sdiv_eq_and (x y : BitVec 1) : x.sdiv y = x &&& y := by
|
||
have hx : x = 0#1 ∨ x = 1#1 := by bv_omega
|
||
have hy : y = 0#1 ∨ y = 1#1 := by bv_omega
|
||
rcases hx with rfl | rfl <;>
|
||
rcases hy with rfl | rfl <;>
|
||
simp
|
||
|
||
theorem sdiv_self {x : BitVec w} :
|
||
x.sdiv x = if x == 0#w then 0#w else 1#w := by
|
||
simp [sdiv_eq]
|
||
|
||
/-- Unsigned division never overflows. -/
|
||
theorem toNat_div_toNat_lt {w : Nat} {x y : BitVec w} :
|
||
x.toNat / y.toNat < 2 ^ w := by
|
||
have hy : y.toNat = 0 ∨ y.toNat = 1 ∨ 1 < y.toNat := by omega
|
||
rcases hy with hy|hy|hy
|
||
· simp [hy]; omega
|
||
· simp [hy]; omega
|
||
· rw [Nat.div_lt_iff_lt_mul (k := y.toNat) (x := x.toNat) (y := 2 ^ w) (by omega), show x.toNat = x.toNat * 1 by omega]
|
||
apply Nat.mul_lt_mul_of_le_of_lt (by omega) (by omega) (by omega)
|
||
|
||
/-- Non-overflowing signed division bounds when numerator is nonneg, denominator is nonneg. -/
|
||
theorem toInt_ediv_toInt_lt_of_nonneg_of_nonneg {w : Nat} {x y : BitVec w} (hx : 0 ≤ x.toInt) (hy : 0 ≤ y.toInt) :
|
||
x.toInt / y.toInt < 2 ^ (w - 1) := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· have xle := le_two_mul_toInt (x := x); have xlt := two_mul_toInt_lt (x := x)
|
||
by_cases hy' : y.toInt = 1
|
||
· simp [hy', Int.ediv_one]; omega
|
||
· by_cases hx' : x.toInt = 0
|
||
· simp only [hx', Int.zero_ediv, Nat.add_one_sub_one, gt_iff_lt]
|
||
norm_cast
|
||
exact Nat.two_pow_pos (w := w)
|
||
· have := Int.ediv_lt_self_of_pos_of_ne_one (x := x.toInt) (y := y.toInt) (by omega) (by omega)
|
||
simp; omega
|
||
|
||
/-- Non-overflowing signed division bounds when numerator is nonpos, denominator is nonneg. -/
|
||
theorem toInt_ediv_toInt_nonpos_of_nonpos_of_nonneg {w : Nat} {x y : BitVec w} (hx : x.toInt ≤ 0) (hy : 0 ≤ y.toInt) :
|
||
x.toInt / y.toInt ≤ 0 := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· by_cases hx' : x.toInt = 0
|
||
· simp [hx']
|
||
· by_cases hy' : y.toInt = 0
|
||
· simp [hy']
|
||
· have := Int.ediv_neg_of_neg_of_pos (a := x.toInt) (b := y.toInt) (by omega) (by omega)
|
||
simp; omega
|
||
|
||
/-- Non-overflowing signed division bounds when numerator is nonneg, denominator is nonpos. -/
|
||
theorem toInt_ediv_toInt_nonpos_of_nonneg_of_nonpos {w : Nat} {x y : BitVec w} (hx : 0 ≤ x.toInt) (hy : y.toInt ≤ 0) :
|
||
x.toInt / y.toInt ≤ 0 := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· by_cases hy' : y.toInt = -1
|
||
· simp [hy']; omega
|
||
· have := Int.ediv_nonpos_of_nonneg_of_nonpos (a := x.toInt) (b := y.toInt) (by omega) (by omega)
|
||
simp; omega
|
||
|
||
/-- Given the definition of ediv/emod for signed integer division (https://dl.acm.org/doi/pdf/10.1145/128861.128862)
|
||
we have that for two integers `x` and `y`: `x/y = q ↔ x.ediv y = q ↔ r = x.emod y`
|
||
and in particular: `-1/y = q ↔ -1.ediv y = q ↔ r = -1.emod y`.
|
||
from which it follows that:
|
||
(-1)/0 = 0
|
||
(-1)/y = -1 when 0 < y
|
||
(-1)/(-5) = 1 when y < 0
|
||
-/
|
||
theorem neg_one_ediv_toInt_eq {w : Nat} {y : BitVec w} :
|
||
(-1) / y.toInt = if y.toInt = 0 then 0 else if 0 < y.toInt then -1 else 1 := by
|
||
rcases w with _|_|w
|
||
· simp [of_length_zero]
|
||
· cases eq_zero_or_eq_one y
|
||
case _ h => simp [h]
|
||
case _ h => simp [h]
|
||
· by_cases 0 < y.toInt
|
||
· simp [Int.sign_eq_one_of_pos (a := y.toInt) (by omega), Int.neg_one_ediv]
|
||
omega
|
||
· by_cases hy : y.toInt = 0
|
||
· simp [hy]
|
||
· simp [Int.sign_eq_neg_one_of_neg (a := y.toInt) (by omega), Int.neg_one_ediv]
|
||
omega
|
||
|
||
/-- Non-overflowing signed division bounds when numerator is nonpos, denominator is less than -1. -/
|
||
theorem toInt_ediv_toInt_lt_of_nonpos_of_lt_neg_one {w : Nat} {x y : BitVec w} (hx : x.toInt ≤ 0) (hy : y.toInt < -1) :
|
||
x.toInt / y.toInt < 2 ^ (w - 1) := by
|
||
rcases w with _|_|w
|
||
· simp [of_length_zero]
|
||
· have hy := eq_zero_or_eq_one (a := y)
|
||
simp [← toInt_inj, toInt_zero] at hy
|
||
omega
|
||
· have xle := le_two_mul_toInt (x := x); have xlt := two_mul_toInt_lt (x := x)
|
||
have hx' : x.toInt = 0 ∨ x.toInt = - 1 ∨ x.toInt < - 1 := by omega
|
||
rcases hx' with hx'|hx'|hx'
|
||
· simp [hx']; omega
|
||
· have := BitVec.neg_one_ediv_toInt_eq (y := y)
|
||
simp [hx', this]
|
||
omega
|
||
· have := Int.ediv_lt_natAbs_self_of_lt_neg_one_of_lt_neg_one (x := x.toInt) (y := y.toInt) (by omega) hy
|
||
simp; omega
|
||
|
||
/-- Signed division of (x y : BitVec w) is always -2 ^ w ≤ x.toInt / y.toInt. -/
|
||
theorem neg_two_pow_le_toInt_ediv {x y : BitVec w} :
|
||
- 2 ^ (w - 1) ≤ x.toInt / y.toInt := by
|
||
have xlt := @toInt_lt w x; have lex := @le_toInt w x
|
||
by_cases hx : 0 ≤ x.toInt <;> by_cases hy : 0 ≤ y.toInt
|
||
· have := Int.ediv_nonneg_of_nonneg_of_nonneg (x := x.toInt) (y := y.toInt) hx hy
|
||
omega
|
||
· have := Int.neg_self_le_ediv_of_nonneg_of_nonpos (x := x.toInt) (y := y.toInt) hx (by omega)
|
||
omega
|
||
· have := Int.self_le_ediv_of_nonpos_of_nonneg (x := x.toInt) (y := y.toInt) (by omega) hy
|
||
omega
|
||
· have := Int.ediv_nonneg_of_nonpos_of_nonpos (a := x.toInt) (b := y.toInt) (by omega) (by omega)
|
||
omega
|
||
|
||
/-! ### smtSDiv -/
|
||
|
||
theorem smtSDiv_eq (x y : BitVec w) : smtSDiv x y =
|
||
match x.msb, y.msb with
|
||
| false, false => smtUDiv x y
|
||
| false, true => -(smtUDiv x (-y))
|
||
| true, false => -(smtUDiv (-x) y)
|
||
| true, true => smtUDiv (-x) (-y) := by
|
||
rw [BitVec.smtSDiv]
|
||
rcases x.msb <;> rcases y.msb <;> simp
|
||
|
||
theorem smtSDiv_zero {x : BitVec n} : x.smtSDiv 0#n = if x.slt 0#n then 1#n else (allOnes n) := by
|
||
rcases hx : x.msb <;> simp [smtSDiv, slt_zero_iff_msb_cond, hx, ← neg_one_eq_allOnes]
|
||
|
||
/-! ### srem -/
|
||
|
||
theorem srem_eq (x y : BitVec w) : srem x y =
|
||
match x.msb, y.msb with
|
||
| false, false => x % y
|
||
| false, true => x % (-y)
|
||
| true, false => - ((-x) % y)
|
||
| true, true => -((-x) % (-y)) := by
|
||
rw [BitVec.srem]
|
||
rcases x.msb <;> rcases y.msb <;> simp
|
||
|
||
theorem srem_zero {x : BitVec w} : x.srem 0#w = x := by
|
||
cases h : x.msb <;> simp [h, srem_eq]
|
||
|
||
theorem zero_srem {x : BitVec w} : (0#w).srem x = 0#w := by
|
||
cases h : x.msb <;> simp [h, srem_eq]
|
||
|
||
theorem srem_one {x : BitVec w} : x.srem 1#w = 0#w := by
|
||
cases h : x.msb <;> by_cases hw : w = 1 <;> (try subst hw) <;> simp_all [srem_eq]
|
||
|
||
theorem srem_self {x : BitVec w} : x.srem x = 0#w := by
|
||
cases h : x.msb <;> simp [h, srem_eq]
|
||
|
||
/-! ### smod -/
|
||
|
||
/-- Equation theorem for `smod` in terms of `umod`. -/
|
||
theorem smod_eq (x y : BitVec w) : x.smod y =
|
||
match x.msb, y.msb with
|
||
| false, false => x.umod y
|
||
| false, true =>
|
||
let u := x.umod (- y)
|
||
(if u = 0#w then u else u + y)
|
||
| true, false =>
|
||
let u := umod (- x) y
|
||
(if u = 0#w then u else y - u)
|
||
| true, true => - ((- x).umod (- y)) := by
|
||
rw [BitVec.smod]
|
||
rcases x.msb <;> rcases y.msb <;> simp
|
||
|
||
@[bitvec_to_nat]
|
||
theorem toNat_smod {x y : BitVec w} : (x.smod y).toNat =
|
||
match x.msb, y.msb with
|
||
| false, false => (x.umod y).toNat
|
||
| false, true =>
|
||
let u := x.umod (- y)
|
||
(if u = 0#w then u.toNat else (u + y).toNat)
|
||
| true, false =>
|
||
let u := (-x).umod y
|
||
(if u = 0#w then u.toNat else (y - u).toNat)
|
||
| true, true => (- ((- x).umod (- y))).toNat := by
|
||
simp only [smod_eq]
|
||
by_cases h : x.msb <;> by_cases h' : y.msb
|
||
<;> by_cases h'' : (-x).umod y = 0#w <;> by_cases h''' : x.umod (-y) = 0#w
|
||
<;> simp only [h, h', h'', h''']
|
||
<;> simp only [umod, toNat_eq, toNat_ofNatLT, toNat_ofNat, Nat.zero_mod] at h'' h'''
|
||
<;> simp
|
||
|
||
theorem smod_zero {x : BitVec w} : x.smod 0#w = x := by
|
||
simp only [smod_eq, msb_zero]
|
||
rcases x.msb with msb | msb <;> apply eq_of_toNat_eq
|
||
· simp
|
||
· by_cases h : x = 0#w <;> simp [h]
|
||
|
||
theorem zero_smod {x : BitVec w} : (0#w).smod x = 0#w := by
|
||
cases _ : x.msb <;> simp_all [smod_eq]
|
||
|
||
/-! ### ofBoolList -/
|
||
|
||
theorem getMsbD_ofBoolListBE : (ofBoolListBE bs).getMsbD i = bs.getD i false := by
|
||
induction bs generalizing i <;> cases i <;> simp_all [ofBoolListBE]
|
||
|
||
theorem getLsbD_ofBoolListBE :
|
||
(ofBoolListBE bs).getLsbD i = (decide (i < bs.length) && bs.getD (bs.length - 1 - i) false) := by
|
||
simp [getLsbD_eq_getMsbD]
|
||
|
||
theorem getElem_ofBoolListBE (h : i < bs.length) :
|
||
(ofBoolListBE bs)[i] = bs[bs.length - 1 - i] := by
|
||
rw [← getLsbD_eq_getElem]
|
||
grind
|
||
|
||
theorem getLsbD_ofBoolListLE : (ofBoolListLE bs).getLsbD i = bs.getD i false := by
|
||
induction bs generalizing i <;> cases i <;> simp_all [ofBoolListLE]
|
||
|
||
theorem getMsbD_ofBoolListLE :
|
||
(ofBoolListLE bs).getMsbD i = (decide (i < bs.length) && bs.getD (bs.length - 1 - i) false) := by
|
||
simp [getMsbD_eq_getLsbD]
|
||
|
||
/-! # Rotate Left -/
|
||
|
||
/--`rotateLeft` is defined in terms of left and right shifts. -/
|
||
theorem rotateLeft_def {x : BitVec w} {r : Nat} :
|
||
x.rotateLeft r = (x <<< (r % w)) ||| (x >>> (w - r % w)) := by
|
||
simp only [rotateLeft, rotateLeftAux]
|
||
|
||
/-- `rotateLeft` is invariant under `mod` by the bitwidth. -/
|
||
theorem rotateLeft_mod_eq_rotateLeft {x : BitVec w} {r : Nat} :
|
||
x.rotateLeft (r % w) = x.rotateLeft r := by
|
||
simp only [rotateLeft, Nat.mod_mod]
|
||
|
||
/-- `rotateLeft` equals the bit fiddling definition of `rotateLeftAux` when the rotation amount is
|
||
smaller than the bitwidth. -/
|
||
theorem rotateLeft_eq_rotateLeftAux_of_lt {x : BitVec w} {r : Nat} (hr : r < w) :
|
||
x.rotateLeft r = x.rotateLeftAux r := by
|
||
simp only [rotateLeft, Nat.mod_eq_of_lt hr]
|
||
|
||
|
||
/--
|
||
Accessing bits in `x.rotateLeft r` the range `[0, r)` is equal to
|
||
accessing bits `x` in the range `[w - r, w)`.
|
||
|
||
Proof by example:
|
||
Let x := <6 5 4 3 2 1 0> : BitVec 7.
|
||
x.rotateLeft 2 = (<6 5 | 4 3 2 1 0>).rotateLeft 2 = <3 2 1 0 | 6 5>
|
||
|
||
(x.rotateLeft 2).getLsbD ⟨i, i < 2⟩
|
||
= <3 2 1 0 | 6 5>.getLsbD ⟨i, i < 2⟩
|
||
= <6 5>[i]
|
||
= <6 5 | 4 3 2 1 0>[i + len(<4 3 2 1 0>)]
|
||
= <6 5 | 4 3 2 1 0>[i + 7 - 2]
|
||
-/
|
||
theorem getLsbD_rotateLeftAux_of_lt {x : BitVec w} {r : Nat} {i : Nat} (hi : i < r) :
|
||
(x.rotateLeftAux r).getLsbD i = x.getLsbD (w - r + i) := by
|
||
grind [rotateLeftAux]
|
||
|
||
/--
|
||
Accessing bits in `x.rotateLeft r` the range `[r, w)` is equal to
|
||
accessing bits `x` in the range `[0, w - r)`.
|
||
|
||
Proof by example:
|
||
Let x := <6 5 4 3 2 1 0> : BitVec 7.
|
||
x.rotateLeft 2 = (<6 5 | 4 3 2 1 0>).rotateLeft 2 = <3 2 1 0 | 6 5>
|
||
|
||
(x.rotateLeft 2).getLsbD ⟨i, i ≥ 2⟩
|
||
= <3 2 1 0 | 6 5>.getLsbD ⟨i, i ≥ 2⟩
|
||
= <3 2 1 0>[i - 2]
|
||
= <6 5 | 3 2 1 0>[i - 2]
|
||
|
||
Intuitively, grab the full width (7), then move the marker `|` by `r` to the right `(-2)`
|
||
Then, access the bit at `i` from the right `(+i)`.
|
||
-/
|
||
theorem getLsbD_rotateLeftAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i ≥ r) :
|
||
(x.rotateLeftAux r).getLsbD i = (decide (i < w) && x.getLsbD (i - r)) := by
|
||
grind [rotateLeftAux]
|
||
|
||
/-- When `r < w`, we give a formula for `(x.rotateLeft r).getLsbD i`. -/
|
||
theorem getLsbD_rotateLeft_of_le {x : BitVec w} {r i : Nat} (hr: r < w) :
|
||
(x.rotateLeft r).getLsbD i =
|
||
cond (i < r)
|
||
(x.getLsbD (w - r + i))
|
||
(decide (i < w) && x.getLsbD (i - r)) := by
|
||
· grind [rotateLeft_eq_rotateLeftAux_of_lt, getLsbD_rotateLeftAux_of_lt, getLsbD_rotateLeftAux_of_ge]
|
||
|
||
theorem getLsbD_rotateLeft {x : BitVec w} {r i : Nat} :
|
||
(x.rotateLeft r).getLsbD i =
|
||
cond (i < r % w)
|
||
(x.getLsbD (w - (r % w) + i))
|
||
(decide (i < w) && x.getLsbD (i - (r % w))) := by
|
||
rcases w with ⟨rfl, w⟩
|
||
· simp
|
||
· rw [← rotateLeft_mod_eq_rotateLeft, getLsbD_rotateLeft_of_le (Nat.mod_lt _ (by omega))]
|
||
|
||
theorem getElem_rotateLeft {x : BitVec w} {r i : Nat} (h : i < w) :
|
||
(x.rotateLeft r)[i] =
|
||
if h' : i < r % w then x[(w - (r % w) + i)] else x[i - (r % w)] := by
|
||
simp [← BitVec.getLsbD_eq_getElem, h]
|
||
|
||
theorem getMsbD_rotateLeftAux_of_lt {x : BitVec w} {r : Nat} {i : Nat} (hi : i < w - r) :
|
||
(x.rotateLeftAux r).getMsbD i = x.getMsbD (r + i) := by
|
||
grind [rotateLeftAux]
|
||
|
||
theorem getMsbD_rotateLeftAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i ≥ w - r) :
|
||
(x.rotateLeftAux r).getMsbD i = (decide (i < w) && x.getMsbD (i - (w - r))) := by
|
||
simp [rotateLeftAux, getMsbD_or, show i + r ≥ w by omega, show ¬i < w - r by omega]
|
||
|
||
/--
|
||
If a number `w * n ≤ i < w * (n + 1)`, then `i - w * n` equals `i % w`.
|
||
This is true by subtracting `w * n` from the inequality, giving
|
||
`0 ≤ i - w * n < w`, which uniquely identifies `i % w`.
|
||
-/
|
||
private theorem Nat.sub_mul_eq_mod_of_lt_of_le (hlo : w * n ≤ i) (hhi : i < w * (n + 1)) :
|
||
i - w * n = i % w := by
|
||
rw [Nat.mod_def]
|
||
congr
|
||
symm
|
||
apply Nat.div_eq_of_lt_le
|
||
(by rw [Nat.mul_comm]; omega)
|
||
(by rw [Nat.mul_comm]; omega)
|
||
|
||
/-- When `r < w`, we give a formula for `(x.rotateLeft r).getMsbD i`. -/
|
||
theorem getMsbD_rotateLeft_of_lt {n w : Nat} {x : BitVec w} (hi : r < w):
|
||
(x.rotateLeft r).getMsbD n = (decide (n < w) && x.getMsbD ((r + n) % w)) := by
|
||
rcases w with rfl | w
|
||
· simp
|
||
· rw [BitVec.rotateLeft_eq_rotateLeftAux_of_lt (by omega)]
|
||
by_cases h : n < (w + 1) - r
|
||
· simp [getMsbD_rotateLeftAux_of_lt h, Nat.mod_eq_of_lt, show r + n < (w + 1) by omega, show n < w + 1 by omega]
|
||
· simp only [getMsbD_rotateLeftAux_of_ge <| Nat.ge_of_not_lt h]
|
||
by_cases h₁ : n < w + 1
|
||
· simp only [h₁, decide_true, Bool.true_and]
|
||
have h₂ : (r + n) < 2 * (w + 1) := by omega
|
||
congr 1
|
||
rw [← Nat.sub_mul_eq_mod_of_lt_of_le (n := 1) (by omega) (by omega)]
|
||
grind
|
||
· grind
|
||
|
||
theorem getMsbD_rotateLeft {r n w : Nat} {x : BitVec w} :
|
||
(x.rotateLeft r).getMsbD n = (decide (n < w) && x.getMsbD ((r + n) % w)) := by
|
||
rcases w with rfl | w
|
||
· simp
|
||
· by_cases h : r < w
|
||
· rw [getMsbD_rotateLeft_of_lt (by omega)]
|
||
· rw [← rotateLeft_mod_eq_rotateLeft, getMsbD_rotateLeft_of_lt (by apply Nat.mod_lt; simp)]
|
||
simp
|
||
|
||
theorem msb_rotateLeft {m w : Nat} {x : BitVec w} :
|
||
(x.rotateLeft m).msb = x.getMsbD (m % w) := by grind
|
||
|
||
theorem toNat_rotateLeft {x : BitVec w} {r : Nat} :
|
||
(x.rotateLeft r).toNat = (x.toNat <<< (r % w)) % (2^w) ||| x.toNat >>> (w - r % w) := by
|
||
simp only [rotateLeft_def, toNat_shiftLeft, toNat_ushiftRight, toNat_or]
|
||
|
||
theorem toInt_rotateLeft {x : BitVec w} {r : Nat} :
|
||
(x.rotateLeft r).toInt =
|
||
((x <<< (r % w)).toNat ||| (x >>> (w - r % w)).toNat : Int).bmod (2 ^ w) := by
|
||
simp [rotateLeft_def, toInt_or]
|
||
|
||
theorem toFin_rotateLeft {x : BitVec w} {r : Nat} :
|
||
(x.rotateLeft r).toFin =
|
||
Fin.ofNat (2 ^ w) (x.toNat <<< (r % w)) ||| x.toFin / Fin.ofNat (2 ^ w) (2 ^ (w - r % w)) := by
|
||
simp [rotateLeft_def, toFin_shiftLeft, toFin_ushiftRight, toFin_or]
|
||
|
||
/-! ## Rotate Right -/
|
||
|
||
/-- `rotateRight` is defined in terms of left and right shifts. -/
|
||
theorem rotateRight_def {x : BitVec w} {r : Nat} :
|
||
x.rotateRight r = (x >>> (r % w)) ||| (x <<< (w - r % w)) := by
|
||
simp only [rotateRight, rotateRightAux]
|
||
|
||
/--
|
||
Accessing bits in `x.rotateRight r` the range `[0, w-r)` is equal to
|
||
accessing bits `x` in the range `[r, w)`.
|
||
|
||
Proof by example:
|
||
Let x := <6 5 4 3 2 1 0> : BitVec 7.
|
||
x.rotateRight 2 = (<6 5 4 3 2 | 1 0>).rotateRight 2 = <1 0 | 6 5 4 3 2>
|
||
|
||
(x.rotateLeft 2).getLsbD ⟨i, i ≤ 7 - 2⟩
|
||
= <1 0 | 6 5 4 3 2>.getLsbD ⟨i, i ≤ 7 - 2⟩
|
||
= <6 5 4 3 2>.getLsbD i
|
||
= <6 5 4 3 2 | 1 0>[i + 2]
|
||
-/
|
||
theorem getLsbD_rotateRightAux_of_lt {x : BitVec w} {r : Nat} {i : Nat} (hi : i < w - r) :
|
||
(x.rotateRightAux r).getLsbD i = x.getLsbD (r + i) := by
|
||
grind [rotateRightAux]
|
||
|
||
/--
|
||
Accessing bits in `x.rotateRight r` the range `[w-r, w)` is equal to
|
||
accessing bits `x` in the range `[0, r)`.
|
||
|
||
Proof by example:
|
||
Let x := <6 5 4 3 2 1 0> : BitVec 7.
|
||
x.rotateRight 2 = (<6 5 4 3 2 | 1 0>).rotateRight 2 = <1 0 | 6 5 4 3 2>
|
||
|
||
(x.rotateLeft 2).getLsbD ⟨i, i ≥ 7 - 2⟩
|
||
= <1 0 | 6 5 4 3 2>.getLsbD ⟨i, i ≤ 7 - 2⟩
|
||
= <1 0>.getLsbD (i - len(<6 5 4 3 2>)
|
||
= <6 5 4 3 2 | 1 0> (i - len<6 4 4 3 2>)
|
||
-/
|
||
theorem getLsbD_rotateRightAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i ≥ w - r) :
|
||
(x.rotateRightAux r).getLsbD i = (decide (i < w) && x.getLsbD (i - (w - r))) := by
|
||
grind [rotateRightAux]
|
||
|
||
/-- `rotateRight` equals the bit fiddling definition of `rotateRightAux` when the rotation amount is
|
||
smaller than the bitwidth. -/
|
||
theorem rotateRight_eq_rotateRightAux_of_lt {x : BitVec w} {r : Nat} (hr : r < w) :
|
||
x.rotateRight r = x.rotateRightAux r := by
|
||
simp only [rotateRight, Nat.mod_eq_of_lt hr]
|
||
|
||
/-- rotateRight is invariant under `mod` by the bitwidth. -/
|
||
theorem rotateRight_mod_eq_rotateRight {x : BitVec w} {r : Nat} :
|
||
x.rotateRight (r % w) = x.rotateRight r := by
|
||
simp only [rotateRight, Nat.mod_mod]
|
||
|
||
/-- When `r < w`, we give a formula for `(x.rotateRight r).getLsb i`. -/
|
||
theorem getLsbD_rotateRight_of_lt {x : BitVec w} {r i : Nat} (hr: r < w) :
|
||
(x.rotateRight r).getLsbD i =
|
||
cond (i < w - r)
|
||
(x.getLsbD (r + i))
|
||
(decide (i < w) && x.getLsbD (i - (w - r))) := by
|
||
· grind [rotateRight_eq_rotateRightAux_of_lt, getLsbD_rotateRightAux_of_lt, getLsbD_rotateRightAux_of_ge]
|
||
|
||
theorem getLsbD_rotateRight {x : BitVec w} {r i : Nat} :
|
||
(x.rotateRight r).getLsbD i =
|
||
cond (i < w - (r % w))
|
||
(x.getLsbD ((r % w) + i))
|
||
(decide (i < w) && x.getLsbD (i - (w - (r % w)))) := by
|
||
rcases w with ⟨rfl, w⟩
|
||
· simp
|
||
· rw [← rotateRight_mod_eq_rotateRight, getLsbD_rotateRight_of_lt (Nat.mod_lt _ (by omega))]
|
||
|
||
theorem getElem_rotateRight {x : BitVec w} {r i : Nat} (h : i < w) :
|
||
(x.rotateRight r)[i] = if h' : i < w - (r % w) then x[(r % w) + i] else x[(i - (w - (r % w)))] := by
|
||
simp [← BitVec.getLsbD_eq_getElem, getLsbD_rotateRight, h]
|
||
|
||
theorem getMsbD_rotateRightAux_of_lt {x : BitVec w} {r : Nat} {i : Nat} (hi : i < r) :
|
||
(x.rotateRightAux r).getMsbD i = x.getMsbD (i + (w - r)) := by
|
||
rw [rotateRightAux, getMsbD_or, getMsbD_ushiftRight]
|
||
simp [show i < r by omega]
|
||
|
||
theorem getMsbD_rotateRightAux_of_ge {x : BitVec w} {r : Nat} {i : Nat} (hi : i ≥ r) :
|
||
(x.rotateRightAux r).getMsbD i = (decide (i < w) && x.getMsbD (i - r)) := by
|
||
simp [rotateRightAux, show ¬ i < r by omega, show i + (w - r) ≥ w by omega]
|
||
|
||
/-- When `m < w`, we give a formula for `(x.rotateLeft m).getMsbD i`. -/
|
||
-- This should not be a simp lemma as `getMsbD_rotateRight` will apply first.
|
||
theorem getMsbD_rotateRight_of_lt {w n m : Nat} {x : BitVec w} (hr : m < w) :
|
||
(x.rotateRight m).getMsbD n = (decide (n < w) && (if (n < m % w)
|
||
then x.getMsbD ((w + n - m % w) % w) else x.getMsbD (n - m % w))) := by
|
||
rcases w with rfl | w
|
||
· simp
|
||
· rw [rotateRight_eq_rotateRightAux_of_lt (by omega)]
|
||
by_cases h : n < m
|
||
· simp only [getMsbD_rotateRightAux_of_lt h, show n < w + 1 by omega, decide_true,
|
||
show m % (w + 1) = m by rw [Nat.mod_eq_of_lt hr], h, ↓reduceIte,
|
||
show (w + 1 + n - m) < (w + 1) by omega, Nat.mod_eq_of_lt, Bool.true_and]
|
||
grind
|
||
· simp [getMsbD_rotateRightAux_of_ge <| Nat.ge_of_not_lt h]
|
||
by_cases h₁ : n < w + 1
|
||
· simp [h, h₁, decide_true, Bool.true_and, Nat.mod_eq_of_lt hr]
|
||
· simp [h₁]
|
||
|
||
theorem getMsbD_rotateRight {w n m : Nat} {x : BitVec w} :
|
||
(x.rotateRight m).getMsbD n = (decide (n < w) && (if (n < m % w)
|
||
then x.getMsbD ((w + n - m % w) % w) else x.getMsbD (n - m % w))):= by
|
||
rcases w with rfl | w
|
||
· simp
|
||
· by_cases h₀ : m < w
|
||
· rw [getMsbD_rotateRight_of_lt (by omega)]
|
||
· rw [← rotateRight_mod_eq_rotateRight, getMsbD_rotateRight_of_lt (by apply Nat.mod_lt; simp)]
|
||
simp
|
||
|
||
theorem msb_rotateRight {r w : Nat} {x : BitVec w} :
|
||
(x.rotateRight r).msb = x.getMsbD ((w - r % w) % w) := by
|
||
simp only [BitVec.msb, getMsbD_rotateRight]
|
||
by_cases h₀ : 0 < w
|
||
· simp only [h₀, decide_true, Nat.add_zero, Nat.zero_le, Nat.sub_eq_zero_of_le, Bool.true_and,
|
||
ite_eq_left_iff, Nat.not_lt, Nat.le_zero_eq]
|
||
intro h₁
|
||
simp [h₁]
|
||
· grind
|
||
|
||
theorem toNat_rotateRight {x : BitVec w} {r : Nat} :
|
||
(x.rotateRight r).toNat = (x.toNat >>> (r % w)) ||| x.toNat <<< (w - r % w) % (2^w) := by
|
||
simp only [rotateRight_def, toNat_shiftLeft, toNat_ushiftRight, toNat_or]
|
||
|
||
theorem toInt_rotateRight {x : BitVec w} {r : Nat} :
|
||
(x.rotateRight r).toInt = ((x >>> (r % w)).toNat ||| (x <<< (w - r % w)).toNat : Int).bmod (2 ^ w) := by
|
||
simp [rotateRight_def, toInt_or]
|
||
|
||
theorem toFin_rotateRight {x : BitVec w} {r : Nat} :
|
||
(x.rotateRight r).toFin = x.toFin / Fin.ofNat (2 ^ w) (2 ^ (r % w)) ||| Fin.ofNat (2 ^ w) (x.toNat <<< (w - r % w)) := by
|
||
simp [rotateRight_def, toFin_shiftLeft, toFin_ushiftRight, toFin_or]
|
||
|
||
/- ## twoPow -/
|
||
|
||
theorem twoPow_eq (w : Nat) (i : Nat) : twoPow w i = 1#w <<< i := by
|
||
dsimp [twoPow]
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w := by
|
||
rcases w with rfl | w
|
||
· simp [Nat.mod_one, toNat_of_zero_length]
|
||
· simp only [twoPow, toNat_shiftLeft, toNat_ofNat]
|
||
have h1 : 1 < 2 ^ (w + 1) := Nat.one_lt_two_pow (by omega)
|
||
rw [Nat.mod_eq_of_lt h1, Nat.shiftLeft_eq, Nat.one_mul]
|
||
|
||
theorem toNat_twoPow_of_le {i w : Nat} (h : w ≤ i) : (twoPow w i).toNat = 0 := by
|
||
rw [toNat_twoPow]
|
||
apply Nat.mod_eq_zero_of_dvd
|
||
exact Nat.pow_dvd_pow_iff_le_right'.mpr h
|
||
|
||
theorem toNat_twoPow_of_lt {i w : Nat} (h : i < w) : (twoPow w i).toNat = 2^i := by
|
||
rw [toNat_twoPow]
|
||
apply Nat.mod_eq_of_lt
|
||
apply Nat.pow_lt_pow_of_lt (by omega) (by omega)
|
||
|
||
theorem toNat_twoPow_eq_ite {i w : Nat} : (twoPow w i).toNat = if i < w then 2^i else 0 := by
|
||
by_cases h : i < w
|
||
· simp only [h, toNat_twoPow_of_lt, if_true]
|
||
· simp only [h, if_false]
|
||
rw [toNat_twoPow_of_le (by omega)]
|
||
|
||
theorem getLsbD_twoPow (i j : Nat) : (twoPow w i).getLsbD j = ((i < w) && (i = j)) := by
|
||
rcases w with rfl | w
|
||
· simp
|
||
· simp only [twoPow, getLsbD_shiftLeft, getLsbD_ofNat]
|
||
by_cases hj : j < i
|
||
· simp only [hj, decide_true, Bool.not_true, Bool.and_false, Bool.false_and, Bool.false_eq,
|
||
Bool.and_eq_false_imp, decide_eq_true_eq, decide_eq_false_iff_not]
|
||
grind
|
||
· by_cases hi : Nat.testBit 1 (j - i)
|
||
· obtain hi' := Nat.testBit_one_eq_true_iff_self_eq_zero.mp hi
|
||
have hij : j = i := by omega
|
||
grind
|
||
· have hij : i ≠ j := by
|
||
intro h; subst h
|
||
grind
|
||
grind
|
||
|
||
theorem msb_twoPow {i w: Nat} :
|
||
(twoPow w i).msb = (decide (i < w) && decide (i = w - 1)) := by grind
|
||
|
||
theorem toInt_twoPow {w i : Nat} :
|
||
(BitVec.twoPow w i).toInt = if w ≤ i then 0
|
||
else if i + 1 = w then (-(2^i : Nat) : Int) else 2^i := by
|
||
simp only [BitVec.toInt_eq_msb_cond, toNat_twoPow_eq_ite]
|
||
rcases w with _ | w
|
||
· simp
|
||
· by_cases h : i = w
|
||
· simp [h, show ¬ (w + 1 ≤ w) by omega]
|
||
omega
|
||
· by_cases h' : w + 1 ≤ i
|
||
· simp [h', show ¬ i < w + 1 by omega]
|
||
· simp [h, h', show i < w + 1 by omega, Int.natCast_pow]
|
||
|
||
theorem toFin_twoPow {w i : Nat} :
|
||
(BitVec.twoPow w i).toFin = Fin.ofNat (2^w) (2^i) := by
|
||
rcases w with rfl | w
|
||
· simp [BitVec.twoPow, Fin.fin_one_eq_zero]
|
||
· simp [BitVec.twoPow, toFin_shiftLeft, Nat.shiftLeft_eq]
|
||
|
||
theorem getElem_twoPow {i j : Nat} (h : j < w) : (twoPow w i)[j] = decide (j = i) := by
|
||
grind [=_ getLsbD_eq_getElem]
|
||
|
||
theorem getMsbD_twoPow {i j w: Nat} :
|
||
(twoPow w i).getMsbD j = (decide (i < w) && decide (j = w - i - 1)) := by grind
|
||
|
||
theorem and_twoPow (x : BitVec w) (i : Nat) :
|
||
x &&& (twoPow w i) = if x.getLsbD i then twoPow w i else 0#w := by grind
|
||
|
||
theorem twoPow_and (x : BitVec w) (i : Nat) :
|
||
(twoPow w i) &&& x = if x.getLsbD i then twoPow w i else 0#w := by grind
|
||
|
||
theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
|
||
x * (twoPow w i) = x <<< i := by
|
||
apply eq_of_toNat_eq
|
||
simp only [toNat_mul, toNat_twoPow, Nat.mul_mod_mod, Nat.shiftLeft_eq, toNat_shiftLeft]
|
||
|
||
theorem twoPow_mul_eq_shiftLeft (x : BitVec w) (i : Nat) :
|
||
(twoPow w i) * x = x <<< i := by
|
||
rw [BitVec.mul_comm, mul_twoPow_eq_shiftLeft]
|
||
|
||
theorem twoPow_zero {w : Nat} : twoPow w 0 = 1#w := by grind
|
||
|
||
theorem shiftLeft_eq_mul_twoPow (x : BitVec w) (n : Nat) :
|
||
x <<< n = x * (BitVec.twoPow w n) := by
|
||
ext i
|
||
simp [mul_twoPow_eq_shiftLeft]
|
||
|
||
/-- 2^i * 2^j = 2^(i + j) with bitvectors as well -/
|
||
theorem twoPow_mul_twoPow_eq {w : Nat} (i j : Nat) : twoPow w i * twoPow w j = twoPow w (i + j) := by
|
||
apply BitVec.eq_of_toNat_eq
|
||
simp only [toNat_mul, toNat_twoPow]
|
||
rw [← Nat.mul_mod, Nat.pow_add]
|
||
|
||
/--
|
||
The unsigned division of `x` by `2^k` equals shifting `x` right by `k`,
|
||
when `k` is less than the bitwidth `w`.
|
||
-/
|
||
theorem udiv_twoPow_eq_of_lt {w : Nat} {x : BitVec w} {k : Nat} (hk : k < w) : x / (twoPow w k) = x >>> k := by
|
||
have : 2^k < 2^w := Nat.pow_lt_pow_of_lt (by decide) hk
|
||
simp [bitvec_to_nat, Nat.shiftRight_eq_div_pow, Nat.mod_eq_of_lt this]
|
||
|
||
theorem toInt_mul_toInt_le {x y : BitVec w} : x.toInt * y.toInt ≤ 2 ^ (w * 2 - 2) := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· have xlt := two_mul_toInt_lt (x := x); have xle := le_two_mul_toInt (x := x)
|
||
have ylt := two_mul_toInt_lt (x := y); have yle := le_two_mul_toInt (x := y)
|
||
have h : 2 ^ ((w + 1) * 2 - 2) = 2 ^ ((w + 1) - 1) * 2 ^ ((w + 1) - 1) := by
|
||
rw [← Nat.pow_add, ←Nat.mul_two, Nat.mul_comm (m := 2) (n := (w + 1) - 1),
|
||
Nat.mul_sub_one, Nat.mul_comm]
|
||
rw_mod_cast [h]
|
||
rw [← Nat.two_pow_pred_mul_two (by omega), Int.natCast_mul] at xlt ylt xle yle
|
||
exact Int.mul_le_mul_of_natAbs_le (by omega) (by omega)
|
||
|
||
theorem le_toInt_mul_toInt {x y : BitVec w} : - (2 ^ (w * 2 - 2)) ≤ x.toInt * y.toInt := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· have xlt := two_mul_toInt_lt (x := x); have xle := le_two_mul_toInt (x := x)
|
||
have ylt := two_mul_toInt_lt (x := y); have yle := le_two_mul_toInt (x := y)
|
||
have h : 2 ^ ((w + 1) * 2 - 2) = 2 ^ ((w + 1) - 1) * 2 ^ ((w + 1) - 1) := by
|
||
rw [← Nat.pow_add, ←Nat.mul_two, Nat.mul_comm (m := 2) (n := (w + 1) - 1),
|
||
Nat.mul_sub_one, Nat.mul_comm]
|
||
rw_mod_cast [h]
|
||
rw [← Nat.two_pow_pred_mul_two (by omega), Int.natCast_mul] at xlt ylt xle yle
|
||
exact Int.neg_mul_le_mul (by omega) (by omega) (by omega) (by omega)
|
||
|
||
theorem shiftLeft_neg {x : BitVec w} {y : Nat} :
|
||
(-x) <<< y = - (x <<< y) := by
|
||
rw [shiftLeft_eq_mul_twoPow, shiftLeft_eq_mul_twoPow, BitVec.neg_mul]
|
||
|
||
/- ### cons -/
|
||
|
||
theorem true_cons_zero : cons true 0#w = twoPow (w + 1) w := by grind
|
||
|
||
theorem false_cons_zero : cons false 0#w = 0#(w + 1) := by grind
|
||
|
||
theorem zero_concat_true : concat 0#w true = 1#(w + 1) := by grind
|
||
|
||
/- ### setWidth, setWidth, and bitwise operations -/
|
||
|
||
/--
|
||
When the `(i+1)`th bit of `x` is false,
|
||
keeping the lower `(i + 1)` bits of `x` equals keeping the lower `i` bits.
|
||
-/
|
||
theorem setWidth_setWidth_succ_eq_setWidth_setWidth_of_getLsbD_false
|
||
{x : BitVec w} {i : Nat} (hx : x.getLsbD i = false) :
|
||
setWidth w (x.setWidth (i + 1)) =
|
||
setWidth w (x.setWidth i) := by grind
|
||
|
||
/--
|
||
When the `(i+1)`th bit of `x` is true,
|
||
keeping the lower `(i + 1)` bits of `x` equalsk eeping the lower `i` bits
|
||
and then performing bitwise-or with `twoPow i = (1 << i)`,
|
||
-/
|
||
theorem setWidth_setWidth_succ_eq_setWidth_setWidth_or_twoPow_of_getLsbD_true
|
||
{x : BitVec w} {i : Nat} (hx : x.getLsbD i = true) :
|
||
setWidth w (x.setWidth (i + 1)) =
|
||
setWidth w (x.setWidth i) ||| (twoPow w i) := by grind
|
||
|
||
/-- Bitwise and of `(x : BitVec w)` with `1#w` equals zero extending `x.lsb` to `w`. -/
|
||
theorem and_one_eq_setWidth_ofBool_getLsbD {x : BitVec w} :
|
||
(x &&& 1#w) = setWidth w (ofBool (x.getLsbD 0)) := by grind
|
||
|
||
theorem replicate_zero {x : BitVec w} : x.replicate 0 = 0#0 := by grind
|
||
|
||
theorem replicate_one {w : Nat} {x : BitVec w} :
|
||
(x.replicate 1) = x.cast (by rw [Nat.mul_one]) := by
|
||
simp only [replicate]; rw [append_zero_width]
|
||
|
||
theorem replicate_succ {x : BitVec w} :
|
||
x.replicate (n + 1) =
|
||
(x ++ replicate n x).cast (by rw [Nat.mul_succ]; omega) := by
|
||
simp [replicate]
|
||
|
||
theorem getLsbD_replicate {n w : Nat} {x : BitVec w} :
|
||
(x.replicate n).getLsbD i =
|
||
(decide (i < w * n) && x.getLsbD (i % w)) := by
|
||
induction n generalizing x
|
||
case zero => simp
|
||
case succ n ih =>
|
||
simp only [replicate_succ, getLsbD_cast, getLsbD_append]
|
||
by_cases hi : i < w * (n + 1)
|
||
· simp only [hi, decide_true, Bool.true_and]
|
||
by_cases hi' : i < w * n
|
||
· grind
|
||
· simp only [hi', ↓reduceIte]
|
||
rw [Nat.sub_mul_eq_mod_of_lt_of_le (by omega) (by omega)]
|
||
· rw [Nat.mul_succ] at hi ⊢
|
||
grind
|
||
|
||
theorem getElem_replicate {n w : Nat} {x : BitVec w} (h : i < w * n) :
|
||
(x.replicate n)[i] = if h' : w = 0 then false else x[i % w]'(@Nat.mod_lt i w (by omega)) := by
|
||
grind [=_ getLsbD_eq_getElem, getLsbD_replicate]
|
||
|
||
theorem append_assoc {x₁ : BitVec w₁} {x₂ : BitVec w₂} {x₃ : BitVec w₃} :
|
||
(x₁ ++ x₂) ++ x₃ = (x₁ ++ (x₂ ++ x₃)).cast (by omega) := by grind
|
||
|
||
theorem append_assoc' {x₁ : BitVec w₁} {x₂ : BitVec w₂} {x₃ : BitVec w₃} :
|
||
(x₁ ++ (x₂ ++ x₃)) = ((x₁ ++ x₂) ++ x₃).cast (by omega) := by grind
|
||
|
||
theorem replicate_append_self {x : BitVec w} :
|
||
x ++ x.replicate n = (x.replicate n ++ x).cast (by omega) := by
|
||
induction n with
|
||
| zero => grind
|
||
| succ n ih =>
|
||
rw [replicate_succ]
|
||
conv => lhs; rw [ih]
|
||
simp only [BitVec.cast_cast, BitVec.cast_eq]
|
||
rw [← cast_append_left]
|
||
· grind
|
||
· rw [Nat.add_comm, Nat.mul_add, Nat.mul_one]; omega
|
||
|
||
theorem replicate_succ' {x : BitVec w} :
|
||
x.replicate (n + 1) =
|
||
(replicate n x ++ x).cast (by rw [Nat.mul_succ]) := by
|
||
simp [replicate_append_self]
|
||
|
||
theorem BitVec.setWidth_add_eq_mod {x y : BitVec w} : BitVec.setWidth i (x + y) = (BitVec.setWidth i x + BitVec.setWidth i y) % (BitVec.twoPow i w) := by
|
||
apply BitVec.eq_of_toNat_eq
|
||
rw [toNat_setWidth]
|
||
simp only [toNat_setWidth, toNat_add, toNat_umod, Nat.add_mod_mod, Nat.mod_add_mod, toNat_twoPow]
|
||
by_cases h : i ≤ w
|
||
· rw [Nat.mod_eq_zero_of_dvd (Nat.pow_dvd_pow 2 h), Nat.mod_zero, Nat.mod_mod_of_dvd _ (Nat.pow_dvd_pow 2 h)]
|
||
· have hk : 2 ^ w < 2 ^ i := Nat.pow_lt_pow_of_lt (by decide) (Nat.lt_of_not_le h)
|
||
rw [Nat.mod_eq_of_lt hk, Nat.mod_mod_eq_mod_mod_of_dvd (Nat.pow_dvd_pow _ (Nat.le_of_not_le h))]
|
||
|
||
/-! ### intMin -/
|
||
|
||
theorem getLsbD_intMin (w : Nat) : (intMin w).getLsbD i = decide (i + 1 = w) := by
|
||
grind [intMin]
|
||
|
||
theorem getMsbD_intMin {w i : Nat} :
|
||
(intMin w).getMsbD i = (decide (0 < w) && decide (i = 0)) := by grind
|
||
|
||
/--
|
||
The RHS is zero in case `w = 0` which is modeled by wrapping the expression in `... % 2 ^ w`.
|
||
-/
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_intMin : (intMin w).toNat = 2 ^ (w - 1) % 2 ^ w := by
|
||
simp [intMin]
|
||
|
||
theorem toNat_intMin_of_pos (hw : 0 < w) : (intMin w).toNat = 2 ^ (w - 1) := by
|
||
rw [toNat_intMin, Nat.mod_eq_of_lt (Nat.pow_lt_pow_of_lt (by decide) (Nat.sub_one_lt_of_lt hw))]
|
||
|
||
theorem intMin_eq_zero_iff {w : Nat} : intMin w = 0#w ↔ w = 0 := by
|
||
by_cases h : w = 0
|
||
· subst h
|
||
decide +revert
|
||
· constructor
|
||
· have := Nat.two_pow_pos (w - 1)
|
||
simp [toNat_eq, show 0 < w by omega]
|
||
· simp [h]
|
||
|
||
/--
|
||
The RHS is zero in case `w = 0` which is modeled by wrapping the expression in `... % 2 ^ w`.
|
||
-/
|
||
theorem toInt_intMin {w : Nat} :
|
||
(intMin w).toInt = -((2 ^ (w - 1) % 2 ^ w) : Nat) := by
|
||
by_cases h : w = 0
|
||
· subst h
|
||
simp [BitVec.toInt]
|
||
· have w_pos : 0 < w := by omega
|
||
simp only [BitVec.toInt, toNat_intMin, w_pos, Nat.two_pow_pred_mod_two_pow,
|
||
Int.two_pow_pred_sub_two_pow, ite_eq_right_iff]
|
||
rw [Nat.mul_comm]
|
||
simp [w_pos]
|
||
|
||
theorem toInt_intMin_of_pos {v : Nat} (hv : 0 < v) : (intMin v).toInt = -2 ^ (v - 1) := by
|
||
rw [toInt_intMin, Nat.mod_eq_of_lt]
|
||
· simp [Int.natCast_pow]
|
||
· rw [Nat.pow_lt_pow_iff_right (by omega)]
|
||
omega
|
||
|
||
theorem toInt_intMin_le (x : BitVec w) :
|
||
(intMin w).toInt ≤ x.toInt := by
|
||
cases w
|
||
case zero => simp [toInt_intMin, @of_length_zero x]
|
||
case succ w =>
|
||
simp only [toInt_intMin, Nat.add_one_sub_one, Int.natCast_emod]
|
||
have : 0 < 2 ^ w := Nat.two_pow_pos w
|
||
rw [Int.emod_eq_of_lt (by omega) (by omega)]
|
||
rw [BitVec.toInt_eq_toNat_bmod]
|
||
rw [show (2 ^ w : Nat) = ((2 ^ (w + 1) : Nat) : Int) / 2 by omega]
|
||
apply Int.le_bmod (by omega)
|
||
|
||
theorem intMin_sle (x : BitVec w) : (intMin w).sle x := by
|
||
simp only [BitVec.sle, toInt_intMin_le x, decide_true]
|
||
|
||
theorem neg_intMin {w : Nat} : -intMin w = intMin w := by
|
||
by_cases h : 0 < w
|
||
· simp [bitvec_to_nat, h]
|
||
· simp only [Nat.not_lt, Nat.le_zero_eq] at h
|
||
simp [bitvec_to_nat, h]
|
||
|
||
theorem neg_eq_intMin {x : BitVec w} : -x = intMin w ↔ x = intMin w := by
|
||
rw [← BitVec.neg_inj, neg_neg, neg_intMin]
|
||
|
||
theorem neg_ne_intMin_inj {x : BitVec w} :
|
||
-x ≠ intMin w ↔ x ≠ intMin w := by
|
||
rw [←neg_intMin, neg_ne_iff_ne_neg, neg_neg, neg_intMin]
|
||
|
||
theorem abs_intMin {w : Nat} : (intMin w).abs = intMin w := by
|
||
simp [BitVec.abs, bitvec_to_nat]
|
||
|
||
theorem toInt_neg_of_ne_intMin {x : BitVec w} (rs : x ≠ intMin w) :
|
||
(-x).toInt = -(x.toInt) := by
|
||
simp only [ne_eq, toNat_eq, toNat_intMin] at rs
|
||
by_cases x_zero : x = 0
|
||
· subst x_zero
|
||
simp [BitVec.toInt]
|
||
omega
|
||
by_cases w_0 : w = 0
|
||
· subst w_0
|
||
simp [BitVec.eq_nil x]
|
||
have : 0 < w := by omega
|
||
rw [Nat.two_pow_pred_mod_two_pow (by omega)] at rs
|
||
simp only [BitVec.toInt, BitVec.toNat_neg, BitVec.sub_toNat_mod_cancel x_zero]
|
||
have := @Nat.two_pow_pred_mul_two w (by omega)
|
||
split <;> split <;> omega
|
||
|
||
theorem toInt_neg_eq_ite {x : BitVec w} :
|
||
(-x).toInt = if x = intMin w then x.toInt else -(x.toInt) := by
|
||
by_cases hx : x = intMin w <;>
|
||
simp [hx, neg_intMin, toInt_neg_of_ne_intMin]
|
||
|
||
theorem msb_intMin {w : Nat} : (intMin w).msb = decide (0 < w) := by grind
|
||
|
||
theorem ne_intMin_of_msb_eq_false (h : 0 < w) {n : BitVec w} (hn : n.msb = false) :
|
||
n ≠ intMin w := by
|
||
rintro rfl
|
||
simp only [msb_intMin, decide_eq_false_iff_not, Nat.not_lt, Nat.le_zero_eq] at hn
|
||
omega
|
||
|
||
theorem toInt_neg_eq_of_msb {x : BitVec w} (h : x.msb = false) : (-x).toInt = -x.toInt := by
|
||
match w with
|
||
| 0 => rw [of_length_zero (x := x), neg_zero, toInt_zero, Int.neg_zero]
|
||
| w' + 1 => exact toInt_neg_of_ne_intMin (ne_intMin_of_msb_eq_false (Nat.zero_lt_succ _) h)
|
||
|
||
theorem lt_intMin_iff_msb_eq_false {x : BitVec w} (hw : 0 < w) :
|
||
x < intMin w ↔ x.msb = false := by
|
||
simp only [msb_eq_false_iff_two_mul_lt, toNat_intMin_of_pos hw, lt_def]
|
||
rw [← Nat.mul_lt_mul_left (by decide : 0 < 2), ← Nat.pow_add_one', Nat.sub_one_add_one_eq_of_pos hw]
|
||
|
||
theorem intMin_le_iff_msb_eq_true {x : BitVec w} (hw : 0 < w) :
|
||
intMin w ≤ x ↔ x.msb = true := by
|
||
rw [← Decidable.not_iff_not, BitVec.not_le, Bool.not_eq_true]
|
||
exact lt_intMin_iff_msb_eq_false hw
|
||
|
||
theorem le_intMin_of_msb_eq_false {x : BitVec w} (hx : x.msb = false) : x ≤ intMin w := by
|
||
match w with
|
||
| 0 => exact le_of_zero_length rfl
|
||
| w' + 1 =>
|
||
apply BitVec.le_of_lt
|
||
exact (lt_intMin_iff_msb_eq_false (Nat.zero_lt_succ _)).mpr hx
|
||
|
||
theorem neg_le_intMin_of_msb_eq_true {x : BitVec w} (hx : x.msb = true) : -x ≤ intMin w := by
|
||
match w with
|
||
| 0 => exact le_of_zero_length rfl
|
||
| w' + 1 =>
|
||
rw [le_def, toNat_neg_of_pos (pos_of_msb hx), toNat_intMin_of_pos (Nat.zero_lt_succ _)]
|
||
simp only [Nat.succ_eq_add_one, Nat.add_one_sub_one, Nat.pow_add_one]
|
||
rw [msb_eq_true_iff_two_mul_ge, Nat.pow_add_one] at hx
|
||
omega
|
||
|
||
/-! ### intMax -/
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_intMax : (intMax w).toNat = 2 ^ (w - 1) - 1 := by
|
||
simp only [intMax]
|
||
by_cases h : w = 0
|
||
· simp [h]
|
||
· have h' : 0 < w := by omega
|
||
rw [toNat_sub, toNat_twoPow, ← Nat.sub_add_comm (by simpa [h'] using Nat.one_le_two_pow),
|
||
Nat.add_sub_assoc (by simpa [h'] using Nat.one_le_two_pow),
|
||
Nat.two_pow_pred_mod_two_pow h', ofNat_eq_ofNat, toNat_ofNat, Nat.one_mod_two_pow h',
|
||
Nat.add_mod_left, Nat.mod_eq_of_lt]
|
||
have := Nat.two_pow_pred_lt_two_pow h'
|
||
have := Nat.two_pow_pos w
|
||
omega
|
||
|
||
theorem getLsbD_intMax (w : Nat) : (intMax w).getLsbD i = decide (i + 1 < w) := by
|
||
rw [← testBit_toNat, toNat_intMax, Nat.testBit_two_pow_sub_one, decide_eq_decide]
|
||
omega
|
||
|
||
theorem intMax_add_one {w : Nat} : intMax w + 1#w = intMin w := by
|
||
simp only [toNat_eq, toNat_intMax, toNat_add, toNat_intMin, toNat_ofNat, Nat.add_mod_mod]
|
||
by_cases h : w = 0
|
||
· simp [h]
|
||
· rw [Nat.sub_add_cancel (Nat.two_pow_pos (w - 1)), Nat.two_pow_pred_mod_two_pow (by omega)]
|
||
|
||
theorem toInt_intMax : (BitVec.intMax w).toInt = 2 ^ (w - 1) - 1 := by
|
||
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; simp [BitVec.toInt_of_zero_length]) (fun hw => ?_)
|
||
rw [BitVec.toInt, toNat_intMax, if_pos]
|
||
· rw [Int.ofNat_sub Nat.one_le_two_pow, Int.natCast_pow, Int.cast_ofNat_Int, Int.cast_ofNat_Int]
|
||
· rw [Nat.mul_sub_left_distrib, ← Nat.pow_succ', Nat.succ_eq_add_one, Nat.sub_add_cancel hw]
|
||
apply Nat.sub_lt_self (by decide)
|
||
rw [Nat.mul_one]
|
||
apply Nat.le_pow hw
|
||
|
||
/-! ### Non-overflow theorems -/
|
||
|
||
/-- If `x.toNat + y.toNat < 2^w`, then the addition `(x + y)` does not overflow. -/
|
||
theorem toNat_add_of_lt {w} {x y : BitVec w} (h : x.toNat + y.toNat < 2^w) :
|
||
(x + y).toNat = x.toNat + y.toNat := by
|
||
rw [BitVec.toNat_add, Nat.mod_eq_of_lt h]
|
||
|
||
/--
|
||
If `y ≤ x`, then the subtraction `(x - y)` does not overflow.
|
||
Thus, `(x - y).toNat = x.toNat - y.toNat`
|
||
-/
|
||
theorem toNat_sub_of_le {x y : BitVec n} (h : y ≤ x) :
|
||
(x - y).toNat = x.toNat - y.toNat := by
|
||
simp only [toNat_sub]
|
||
rw [BitVec.le_def] at h
|
||
by_cases h' : x.toNat = y.toNat
|
||
· rw [h', Nat.sub_self, Nat.sub_add_cancel (by omega), Nat.mod_self]
|
||
· have : 2 ^ n - y.toNat + x.toNat = 2 ^ n + (x.toNat - y.toNat) := by omega
|
||
rw [this, Nat.add_mod_left, Nat.mod_eq_of_lt (by omega)]
|
||
|
||
/--
|
||
If `y > x`, then the subtraction `(x - y)` *does* overflow, and the result is the wraparound.
|
||
Thus, `(x - y).toNat = 2^w - (y.toNat - x.toNat)`.
|
||
-/
|
||
theorem toNat_sub_of_lt {x y : BitVec w} (h : x < y) :
|
||
(x - y).toNat = 2^w - (y.toNat - x.toNat) := by
|
||
simp only [toNat_sub]
|
||
rw [Nat.mod_eq_of_lt (by bv_omega)]
|
||
bv_omega
|
||
|
||
/-- If `x.toNat * y.toNat < 2^w`, then the multiplication `(x * y)` does not overflow.
|
||
Thus, `(x * y).toNat = x.toNat * y.toNat`.
|
||
-/
|
||
theorem toNat_mul_of_lt {w} {x y : BitVec w} (h : x.toNat * y.toNat < 2^w) :
|
||
(x * y).toNat = x.toNat * y.toNat := by
|
||
rw [BitVec.toNat_mul, Nat.mod_eq_of_lt h]
|
||
|
||
theorem toNat_mul_toNat_lt {x y : BitVec w} : x.toNat * y.toNat < 2 ^ (w * 2) := by
|
||
have := BitVec.isLt x; have := BitVec.isLt y
|
||
simp only [Nat.mul_two, Nat.pow_add]
|
||
exact Nat.mul_lt_mul_of_le_of_lt (by omega) (by omega) (by omega)
|
||
|
||
/--
|
||
`x ≤ y + z` if and only if `x - z ≤ y`
|
||
when `x - z` and `y + z` do not overflow.
|
||
-/
|
||
theorem le_add_iff_sub_le {x y z : BitVec w}
|
||
(hxz : z ≤ x) (hbz : y.toNat + z.toNat < 2^w) :
|
||
x ≤ y + z ↔ x - z ≤ y := by
|
||
simp_all only [BitVec.le_def]
|
||
rw [BitVec.toNat_sub_of_le (by rw [BitVec.le_def]; omega),
|
||
BitVec.toNat_add_of_lt (by omega)]
|
||
omega
|
||
|
||
/--
|
||
`x - z ≤ y - z` if and only if `x ≤ y`
|
||
when `x - z` and `y - z` do not overflow.
|
||
-/
|
||
theorem sub_le_sub_iff_le {x y z : BitVec w} (hxz : z ≤ x) (hyz : z ≤ y) :
|
||
(x - z ≤ y - z) ↔ x ≤ y := by
|
||
simp_all only [BitVec.le_def]
|
||
rw [BitVec.toNat_sub_of_le (by rw [BitVec.le_def]; omega),
|
||
BitVec.toNat_sub_of_le (by rw [BitVec.le_def]; omega)]
|
||
omega
|
||
|
||
theorem sdiv_neg_one {w : Nat} {x : BitVec w} :
|
||
x.toInt / -1 = -x.toInt := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· simp
|
||
|
||
theorem sdivOverflow_eq_negOverflow_of_eq_allOnes {w : Nat} {x y : BitVec w} (hy : y = allOnes w) :
|
||
sdivOverflow x y = negOverflow x := by
|
||
rcases w with _|w
|
||
· simp [sdivOverflow, negOverflow, of_length_zero]
|
||
· have xle := le_two_mul_toInt (x := x); have xlt := two_mul_toInt_lt (x := x)
|
||
simp only [sdivOverflow, hy, negOverflow]
|
||
by_cases hx : x.toInt = - 2 ^ w
|
||
· simp [hx]
|
||
· simp [show ¬x.toInt == -2 ^ w by simp only [beq_iff_eq, hx, not_false_eq_true]]; omega
|
||
|
||
theorem two_pow_le_toInt_mul_toInt_iff {x y : BitVec w} :
|
||
2 ^ (w - 1) ≤ x.toInt * y.toInt ↔
|
||
(signExtend (w * 2) (intMax w)).slt (signExtend (w * 2) x * signExtend (w * 2) y) := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· have := Int.pow_lt_pow_of_lt (a := 2) (b := (w + 1) * 2 - 2) (c := (w + 1) * 2 - 1) (by omega)
|
||
have := @BitVec.le_toInt_mul_toInt (w + 1) x y
|
||
have := @BitVec.toInt_mul_toInt_le (w + 1) x y
|
||
simp only [Nat.add_one_sub_one, BitVec.slt, intMax, ofNat_eq_ofNat, toInt_mul,
|
||
decide_eq_true_eq]
|
||
repeat rw [BitVec.toInt_signExtend_of_le (by omega)]
|
||
simp only [show BitVec.twoPow (w + 1) w - 1#(w + 1) = BitVec.intMax (w + 1) by simp [intMax],
|
||
toInt_intMax, Nat.add_one_sub_one]
|
||
push_cast
|
||
rw [← Nat.two_pow_pred_add_two_pow_pred (by omega),
|
||
Int.bmod_eq_of_le_mul_two (by rw [← Nat.mul_two]; push_cast; omega)
|
||
(by rw [← Nat.mul_two]; push_cast; omega)]
|
||
omega
|
||
|
||
theorem toInt_mul_toInt_lt_neg_two_pow_iff {x y : BitVec w} :
|
||
x.toInt * y.toInt < - 2 ^ (w - 1) ↔
|
||
(signExtend (w * 2) x * signExtend (w * 2) y).slt (signExtend (w * 2) (intMin w)) := by
|
||
rcases w with _|w
|
||
· simp [of_length_zero]
|
||
· have := Int.pow_lt_pow_of_lt (a := 2) (b := (w + 1) * 2 - 2) (c := (w + 1) * 2 - 1) (by omega)
|
||
have := @BitVec.le_toInt_mul_toInt (w + 1) x y
|
||
have := @BitVec.toInt_mul_toInt_le (w + 1) x y
|
||
simp only [BitVec.slt, toInt_mul, intMin, Nat.add_one_sub_one, decide_eq_true_eq]
|
||
repeat rw [BitVec.toInt_signExtend_of_le (by omega)]
|
||
simp only [toInt_twoPow, show ¬w + 1 ≤ w by omega, ↓reduceIte]
|
||
push_cast
|
||
rw [← Nat.two_pow_pred_add_two_pow_pred (by omega),
|
||
Int.bmod_eq_of_le_mul_two (by rw [← Nat.mul_two]; push_cast; omega)
|
||
(by rw [← Nat.mul_two]; push_cast; omega)]
|
||
|
||
/-! ### neg -/
|
||
|
||
theorem msb_eq_toInt {x : BitVec w}:
|
||
x.msb = decide (x.toInt < 0) := by
|
||
by_cases h : x.msb <;> simp [h, toInt_eq_msb_cond, -Int.natCast_pow] <;> omega
|
||
|
||
theorem msb_eq_toNat {x : BitVec w}:
|
||
x.msb = decide (x.toNat ≥ 2 ^ (w - 1)) := by
|
||
simp only [msb_eq_decide, ge_iff_le]
|
||
|
||
/-- Negating a bitvector created from a natural number equals
|
||
creating a bitvector from the negative of that number.
|
||
-/
|
||
theorem neg_ofNat_eq_ofInt_neg {w : Nat} {x : Nat} :
|
||
- BitVec.ofNat w x = BitVec.ofInt w (- x) := by
|
||
apply BitVec.eq_of_toInt_eq
|
||
simp [BitVec.toInt_neg, BitVec.toInt_ofNat']
|
||
|
||
theorem neg_toInt_neg {x : BitVec w} (h : x.msb = false) :
|
||
-(-x).toInt = x.toNat := by
|
||
simp [toInt_neg_eq_of_msb h, toInt_eq_toNat_of_msb, h]
|
||
|
||
theorem toNat_pos_of_ne_zero {x : BitVec w} (hx : x ≠ 0#w) :
|
||
0 < x.toNat := by
|
||
simp [toNat_eq] at hx; omega
|
||
|
||
theorem toNat_neg_lt_of_msb (x : BitVec w) (hmsb : x.msb = true) :
|
||
(-x).toNat ≤ 2^(w-1) := by
|
||
rcases w with _|w
|
||
· simp [BitVec.eq_nil x]
|
||
· by_cases hx : x = 0#(w + 1)
|
||
· simp [hx]
|
||
· have := BitVec.le_toNat_of_msb_true hmsb
|
||
have := toNat_pos_of_ne_zero hx
|
||
rw [toNat_neg, Nat.mod_eq_of_lt (by omega), ← Nat.two_pow_pred_add_two_pow_pred (by omega),
|
||
← Nat.two_mul]
|
||
omega
|
||
|
||
/-! ### abs -/
|
||
|
||
theorem abs_eq (x : BitVec w) : x.abs = if x.msb then -x else x := rfl
|
||
|
||
@[simp, bitvec_to_nat]
|
||
theorem toNat_abs {x : BitVec w} : x.abs.toNat = if x.msb then 2^w - x.toNat else x.toNat := by
|
||
simp only [BitVec.abs, neg_eq]
|
||
by_cases h : x.msb = true
|
||
· simp only [h, ↓reduceIte, toNat_neg]
|
||
have : 2 * x.toNat ≥ 2 ^ w := BitVec.msb_eq_true_iff_two_mul_ge.mp h
|
||
rw [Nat.mod_eq_of_lt (by omega)]
|
||
· simp [h]
|
||
|
||
theorem getLsbD_abs {i : Nat} {x : BitVec w} :
|
||
getLsbD x.abs i = if x.msb then getLsbD (-x) i else getLsbD x i := by
|
||
grind [BitVec.abs]
|
||
|
||
theorem getElem_abs {i : Nat} {x : BitVec w} (h : i < w) :
|
||
x.abs[i] = if x.msb then (-x)[i] else x[i] := by
|
||
grind [BitVec.abs]
|
||
|
||
theorem getMsbD_abs {i : Nat} {x : BitVec w} :
|
||
getMsbD (x.abs) i = if x.msb then getMsbD (-x) i else getMsbD x i := by grind
|
||
|
||
/-
|
||
The absolute value of `x : BitVec w` is naively a case split on the sign of `x`.
|
||
However, recall that when `x = intMin w`, `-x = x`.
|
||
Thus, the full value of `abs x` is computed by the case split:
|
||
- If `x : BitVec w` is `intMin`, then its absolute value is also `intMin w`, and
|
||
thus `toInt` will equal `intMin.toInt`.
|
||
- Otherwise, if `x` is negative, then `x.abs.toInt = (-x).toInt`.
|
||
- If `x` is positive, then it is equal to `x.abs.toInt = x.toInt`.
|
||
-/
|
||
theorem toInt_abs_eq_ite {x : BitVec w} :
|
||
x.abs.toInt =
|
||
if x = intMin w then (intMin w).toInt
|
||
else if x.msb then -x.toInt
|
||
else x.toInt := by
|
||
by_cases hx : x = intMin w
|
||
· simp [hx]
|
||
· simp [hx]
|
||
by_cases hx₂ : x.msb
|
||
· simp [hx₂, abs_eq, toInt_neg_of_ne_intMin hx]
|
||
· simp [hx₂, abs_eq]
|
||
|
||
/-
|
||
The absolute value of `x : BitVec w` is a case split on the sign of `x`, when `x ≠ intMin w`.
|
||
This is a variant of `toInt_abs_eq_ite`.
|
||
-/
|
||
theorem toInt_abs_eq_ite_of_ne_intMin {x : BitVec w} (hx : x ≠ intMin w) :
|
||
x.abs.toInt = if x.msb then -x.toInt else x.toInt := by
|
||
simp [toInt_abs_eq_ite, hx]
|
||
|
||
/--
|
||
The absolute value of `x : BitVec w`, interpreted as an integer, is a case split:
|
||
- When `x = intMin w`, then `x.abs = intMin w`
|
||
- Otherwise, `x.abs.toInt` equals the absolute value (`x.toInt.natAbs`).
|
||
|
||
This is a simpler version of `BitVec.toInt_abs_eq_ite`, which hides a case split on `x.msb`.
|
||
-/
|
||
theorem toInt_abs_eq_natAbs {x : BitVec w} : x.abs.toInt =
|
||
if x = intMin w then (intMin w).toInt else x.toInt.natAbs := by
|
||
rw [toInt_abs_eq_ite]
|
||
by_cases hx : x = intMin w
|
||
· simp [hx]
|
||
· simp [hx]
|
||
by_cases h : x.msb
|
||
· simp only [h, ↓reduceIte]
|
||
have : x.toInt < 0 := by
|
||
rw [toInt_neg_iff]
|
||
have := msb_eq_true_iff_two_mul_ge.mp h
|
||
omega
|
||
omega
|
||
· simp only [h, Bool.false_eq_true, ↓reduceIte]
|
||
have : 0 ≤ x.toInt := by
|
||
rw [toInt_pos_iff]
|
||
exact msb_eq_false_iff_two_mul_lt.mp (by simp [h])
|
||
omega
|
||
|
||
/-
|
||
The absolute value of `(x : BitVec w)`, when interpreted as an integer,
|
||
is the absolute value of `x.toInt` when `(x ≠ intMin)`.
|
||
-/
|
||
theorem toInt_abs_eq_natAbs_of_ne_intMin {x : BitVec w} (hx : x ≠ intMin w) :
|
||
x.abs.toInt = x.toInt.natAbs := by
|
||
simp [toInt_abs_eq_natAbs, hx]
|
||
|
||
theorem toFin_abs {x : BitVec w} :
|
||
x.abs.toFin = if x.msb then Fin.ofNat (2 ^ w) (2 ^ w - x.toNat) else x.toFin := by
|
||
by_cases h : x.msb <;> simp [BitVec.abs, h]
|
||
|
||
/-! ### Reverse -/
|
||
|
||
theorem getLsbD_reverse {i : Nat} {x : BitVec w} :
|
||
(x.reverse).getLsbD i = x.getMsbD i := by
|
||
induction w generalizing i
|
||
case zero => grind
|
||
case succ n ih => cases i <;> grind [reverse]
|
||
|
||
theorem getElem_reverse (x : BitVec w) (h : i < w) :
|
||
x.reverse[i] = x.getMsbD i := by
|
||
grind [=_ getLsbD_eq_getElem]
|
||
|
||
theorem getMsbD_reverse {i : Nat} {x : BitVec w} :
|
||
(x.reverse).getMsbD i = x.getLsbD i := by grind
|
||
|
||
theorem msb_reverse {x : BitVec w} :
|
||
(x.reverse).msb = x.getLsbD 0 := by grind
|
||
|
||
theorem reverse_append {x : BitVec w} {y : BitVec v} :
|
||
(x ++ y).reverse = (y.reverse ++ x.reverse).cast (by omega) := by grind
|
||
|
||
theorem reverse_cast {w v : Nat} (h : w = v) (x : BitVec w) :
|
||
(x.cast h).reverse = x.reverse.cast h := by grind
|
||
|
||
theorem reverse_replicate {n : Nat} {x : BitVec w} :
|
||
(x.replicate n).reverse = (x.reverse).replicate n := by
|
||
induction n with
|
||
| zero => rfl
|
||
| succ n ih =>
|
||
conv => lhs; simp only [replicate_succ']
|
||
simp [reverse_append, ih]
|
||
|
||
theorem getMsbD_replicate {n w : Nat} {x : BitVec w} :
|
||
(x.replicate n).getMsbD i = (decide (i < w * n) && x.getMsbD (i % w)) := by
|
||
rw [← getLsbD_reverse, reverse_replicate, getLsbD_replicate, getLsbD_reverse]
|
||
|
||
theorem msb_replicate {n w : Nat} {x : BitVec w} :
|
||
(x.replicate n).msb = (decide (0 < n) && x.msb) := by
|
||
simp only [BitVec.msb, getMsbD_replicate, Nat.zero_mod]
|
||
cases n <;> cases w <;> simp
|
||
|
||
/-! ### Count leading zeros -/
|
||
|
||
theorem clzAuxRec_zero (x : BitVec w) :
|
||
x.clzAuxRec 0 = if x.getLsbD 0 then BitVec.ofNat w (w - 1) else BitVec.ofNat w w := by rfl
|
||
|
||
theorem clzAuxRec_succ (x : BitVec w) :
|
||
x.clzAuxRec (n + 1) = if x.getLsbD (n + 1) then BitVec.ofNat w (w - 1 - (n + 1)) else BitVec.clzAuxRec x n := by rfl
|
||
|
||
theorem clzAuxRec_eq_clzAuxRec_of_le (x : BitVec w) (h : w - 1 ≤ n) :
|
||
x.clzAuxRec n = x.clzAuxRec (w - 1) := by
|
||
let k := n - (w - 1)
|
||
rw [show n = (w - 1) + k by omega]
|
||
induction k
|
||
case zero => simp
|
||
case succ k ihk =>
|
||
simp [show w - 1 + (k + 1) = (w - 1 + k) + 1 by omega, clzAuxRec_succ, ihk,
|
||
show x.getLsbD (w - 1 + k + 1) = false by simp only [show w ≤ w - 1 + k + 1 by omega, getLsbD_of_ge]]
|
||
|
||
/-! ### Inequalities (le / lt) -/
|
||
|
||
theorem ule_eq_not_ult (x y : BitVec w) : x.ule y = !y.ult x := by
|
||
simp [BitVec.ule, BitVec.ult, ← decide_not]
|
||
|
||
/-- If two bitvectors have the same `msb`, then signed and unsigned comparisons coincide -/
|
||
theorem slt_eq_ult_of_msb_eq {x y : BitVec w} (h : x.msb = y.msb) :
|
||
x.slt y = x.ult y := by
|
||
simp only [BitVec.slt, toInt_eq_msb_cond, BitVec.ult, decide_eq_decide, h]
|
||
cases y.msb <;> simp
|
||
|
||
/-- If two bitvectors have different `msb`s, then unsigned comparison is determined by this bit -/
|
||
theorem ult_eq_msb_of_msb_neq {x y : BitVec w} (h : x.msb ≠ y.msb) :
|
||
x.ult y = y.msb := by
|
||
simp only [BitVec.ult, msb_eq_decide, ne_eq, decide_eq_decide] at *
|
||
omega
|
||
|
||
/-- If two bitvectors have different `msb`s, then signed and unsigned comparisons are opposites -/
|
||
theorem slt_eq_not_ult_of_msb_neq {x y : BitVec w} (h : x.msb ≠ y.msb) :
|
||
x.slt y = !x.ult y := by
|
||
simp only [BitVec.slt, toInt_eq_msb_cond, Bool.eq_not_of_ne h, ult_eq_msb_of_msb_neq h]
|
||
cases y.msb <;> (simp [-Int.natCast_pow]; omega)
|
||
|
||
theorem slt_eq_ult {x y : BitVec w} :
|
||
x.slt y = (x.msb != y.msb).xor (x.ult y) := by
|
||
by_cases h : x.msb = y.msb
|
||
· simp [h, slt_eq_ult_of_msb_eq]
|
||
· have h' : x.msb != y.msb := by simp_all
|
||
simp [slt_eq_not_ult_of_msb_neq h, h']
|
||
|
||
theorem sle_eq_not_slt {x y : BitVec w} : x.sle y = !y.slt x := by
|
||
simp only [BitVec.sle, BitVec.slt, ← decide_not, decide_eq_decide]; omega
|
||
|
||
theorem zero_sle_eq_not_msb {w : Nat} {x : BitVec w} : BitVec.sle 0#w x = !x.msb := by
|
||
rw [sle_eq_not_slt, BitVec.slt_zero_eq_msb]
|
||
|
||
theorem zero_sle_iff_msb_eq_false {w : Nat} {x : BitVec w} : BitVec.sle 0#w x ↔ x.msb = false := by
|
||
simp [zero_sle_eq_not_msb]
|
||
|
||
theorem toNat_toInt_of_sle {w : Nat} {x : BitVec w} (hx : BitVec.sle 0#w x) : x.toInt.toNat = x.toNat :=
|
||
toNat_toInt_of_msb x (zero_sle_iff_msb_eq_false.1 hx)
|
||
|
||
theorem sle_eq_ule {x y : BitVec w} : x.sle y = (x.msb != y.msb ^^ x.ule y) := by
|
||
rw [sle_eq_not_slt, slt_eq_ult, ← Bool.xor_not, ← ule_eq_not_ult, bne_comm]
|
||
|
||
theorem sle_eq_ule_of_msb_eq {x y : BitVec w} (h : x.msb = y.msb) : x.sle y = x.ule y := by
|
||
simp [BitVec.sle_eq_ule, h]
|
||
|
||
end BitVec'
|