feat: add BitVec.msb_(smod, srem) (#8974)

This PR adds `BitVec.msb_(smod, srem)`. 

co-authored with @tobiasgrosser and @bollu

---------

Co-authored-by: Tobias Grosser <github@grosser.es>
Co-authored-by: Siddharth <siddu.druid@gmail.com>
This commit is contained in:
Luisa Cicolini 2025-06-25 15:49:33 +02:00 committed by GitHub
parent 0ddd9341d6
commit 25b1b46572
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1930,6 +1930,44 @@ theorem toInt_sub_neg_umod {x y : BitVec w} (hxmsb : x.msb = true) (hymsb : y.ms
Int.dvd_neg] at hdvd
simp only [hdvd, ↓reduceIte, Int.natAbs_cast]
theorem srem_zero_of_dvd {x y : BitVec w} (h : y.toInt x.toInt) :
x.srem y = 0#w := by
have := toInt_dvd_toInt_iff (x := x) (y := y)
by_cases hx : x.msb <;> by_cases hy : y.msb
<;> simp only [h, hx, reduceIte, hy, false_eq_true, true_iff] at this
<;> simp [srem, hx, hy, this]
/--
The remainder for `srem`, i.e. division with rounding to zero is negative
iff `x` is negative and `y` does not divide `x`.
We can eventually build fast circuits for the divisibility test `x.srem y = 0`.
-/
theorem msb_srem {x y : BitVec w} : (x.srem y).msb =
(x.msb && decide (x.srem y ≠ 0)) := by
rw [msb_eq_toInt]
by_cases hx : x.msb
· by_cases hsrem : x.srem y = 0#w
· simp [hsrem]
· have := toInt_neg_of_msb_true hx
by_cases hdvd : y.toInt x.toInt
· simp [BitVec.srem_zero_of_dvd hdvd] at hsrem
· simp only [toInt_srem, Int.tmod_eq_emod, show ¬0 ≤ x.toInt by omega, hdvd, _root_.or_self,
reduceIte, hx, ofNat_eq_ofNat, ne_eq, hsrem, not_false_eq_true, decide_true, Bool.and_self,
decide_eq_true_eq, gt_iff_lt]
have hlt := Int.emod_lt (a := x.toInt) (b := y.toInt)
by_cases hy0 : y = 0#w
· simp only [hy0, toInt_zero, Int.emod_zero, Int.natAbs_zero, Int.cast_ofNat_Int,
Int.sub_zero, gt_iff_lt]
exact toInt_neg_of_msb_true hx
· simp only [← toInt_inj, toInt_zero] at hy0
simp only [ne_eq, hy0, not_false_eq_true, forall_const] at hlt
have := Int.le_natAbs (a := y.toInt)
omega
· simp only [toInt_srem, hx, ofNat_eq_ofNat, ne_eq, decide_not, Bool.false_and,
decide_eq_false_iff_not, Int.not_lt]
apply Int.tmod_nonneg y.toInt (by exact toInt_nonneg_of_msb_false (by simp at hx; exact hx))
theorem toInt_smod {x y : BitVec w} :
(x.smod y).toInt = x.toInt.fmod y.toInt := by
rcases w with _|w
@ -1998,6 +2036,30 @@ theorem getMsbD_smod {x y : BitVec w} :
by_cases hx : x.msb <;> by_cases hy : y.msb
<;> simp [hx, hy]
theorem msb_smod {x y : BitVec w} :
(x.smod y).msb = (x.msb && y = 0) || (y.msb && (x.smod y) ≠ 0) := by
rw [msb_eq_toInt]
by_cases hx : x.msb <;> by_cases hy : y.msb
· by_cases hsmod : x.smod y = 0#w <;> simp [hx, hy, hsmod]
· simp only [hx, ofNat_eq_ofNat, Bool.true_and, decide_eq_decide, decide_iff_dist, hy, ne_eq,
decide_not, Bool.false_and, Bool.or_false, beq_iff_eq]
constructor
· intro h
apply Classical.byContradiction
intro hcontra
rw [toInt_smod] at h
have := toInt_nonneg_of_msb_false (by simp at hy; exact hy)
have := Int.fmod_nonneg_of_pos (a := x.toInt) (b := y.toInt) (by simp [← toInt_inj] at hcontra; omega)
omega
· intro h
simp only [h, smod_zero]
exact toInt_neg_of_msb_true hx
· by_cases hsmod : x.smod y = 0#w <;> simp [hx, hy, hsmod]
· simp only [toInt_smod, hx, ofNat_eq_ofNat, Bool.false_and, decide_eq_false_iff_not, Int.not_lt,
hy, ne_eq, decide_not, Bool.or_false, decide_eq_true_eq]
simp only [not_eq_true] at hx hy
apply Int.fmod_nonneg (by exact toInt_nonneg_of_msb_false hx) (by exact toInt_nonneg_of_msb_false hy)
/-! ### Lemmas that use bit blasting circuits -/
theorem add_sub_comm {x y : BitVec w} : x + y - z = x - z + y := by