File: .lake/packages/lean4/src/lean/Init/Data/BitVec/Lemmas.lean

full_name: BitVec.ofFin_eq_ofNat
code: /--
This normalized a bitvec using `ofFin` to `ofNat`.
-/
theorem ofFin_eq_ofNat : @BitVec.ofFin w (Fin.mk x lt) = BitVec.ofNat w x 
goal: { toFin := ⟨x, lt⟩ } = BitVec.ofNat w x

full_name: BitVec.eq_of_toNat_eq
code: /-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toNat_eq {n} : ∀ {i j : BitVec n}, i.toNat = j.toNat → i = j
  
goal: i.toNat = j.toNat → i = j

full_name: BitVec.val_toFin
code: @[simp] theorem val_toFin (x : BitVec w) : x.toFin.val = x.toNat 
goal: ↑x.toFin = x.toNat

full_name: BitVec.toNat_eq
code: @[bv_toNat] theorem toNat_eq (x y : BitVec n) : x = y ↔ x.toNat = y.toNat 
goal: x = y ↔ x.toNat = y.toNat

full_name: BitVec.toNat_ne
code: @[bv_toNat] theorem toNat_ne (x y : BitVec n) : x ≠ y ↔ x.toNat ≠ y.toNat 
goal: x ≠ y ↔ x.toNat ≠ y.toNat

full_name: BitVec.testBit_toNat
code: theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsb i 
goal: x.toNat.testBit i = x.getLsb i

full_name: BitVec.getLsb_ofFin
code: @[simp] theorem getLsb_ofFin (x : Fin (2^n)) (i : Nat) :
  getLsb (BitVec.ofFin x) i = x.val.testBit i 
goal: { toFin := x }.getLsb i = (↑x).testBit i

full_name: BitVec.getLsb_ge
code: @[simp] theorem getLsb_ge (x : BitVec w) (i : Nat) (ge : w ≤ i) : getLsb x i = false 
goal: x.getLsb i = false

full_name: BitVec.getMsb_ge
code: @[simp] theorem getMsb_ge (x : BitVec w) (i : Nat) (ge : w ≤ i) : getMsb x i = false 
goal: x.getMsb i = false

full_name: BitVec.lt_of_getLsb
code: theorem lt_of_getLsb (x : BitVec w) (i : Nat) : getLsb x i = true → i < w 
goal: x.getLsb i = true → i < w

full_name: BitVec.lt_of_getMsb
code: theorem lt_of_getMsb (x : BitVec w) (i : Nat) : getMsb x i = true → i < w 
goal: x.getMsb i = true → i < w

full_name: BitVec.getMsb_eq_getLsb
code: theorem getMsb_eq_getLsb (x : BitVec w) (i : Nat) : x.getMsb i = (decide (i < w) && x.getLsb (w - 1 - i)) 
goal: x.getMsb i = (decide (i < w) && x.getLsb (w - 1 - i))

full_name: BitVec.getLsb_eq_getMsb
code: theorem getLsb_eq_getMsb (x : BitVec w) (i : Nat) : x.getLsb i = (decide (i < w) && x.getMsb (w - 1 - i)) 
goal: x.getLsb i = (decide (i < w) && x.getMsb (w - 1 - i))

full_name: BitVec.eq_of_getLsb_eq
code: @[ext] theorem eq_of_getLsb_eq {x y : BitVec w}
    (pred : ∀(i : Fin w), x.getLsb i.val = y.getLsb i.val) : x = y 
goal: x = y

full_name: BitVec.eq_of_getMsb_eq
code: theorem eq_of_getMsb_eq {x y : BitVec w}
    (pred : ∀(i : Fin w), x.getMsb i = y.getMsb i.val) : x = y 
goal: x = y

full_name: BitVec.of_length_zero
code: theorem of_length_zero {x : BitVec 0} : x = 0#0 
goal: x = 0#0

full_name: BitVec.toNat_zero_length
code: @[simp] theorem toNat_zero_length (x : BitVec 0) : x.toNat = 0 
goal: x.toNat = 0

full_name: BitVec.getLsb_zero_length
code: @[simp] theorem getLsb_zero_length (x : BitVec 0) : x.getLsb i = false 
goal: x.getLsb i = false

full_name: BitVec.getMsb_zero_length
code: @[simp] theorem getMsb_zero_length (x : BitVec 0) : x.getMsb i = false 
goal: x.getMsb i = false

full_name: BitVec.msb_zero_length
code: @[simp] theorem msb_zero_length (x : BitVec 0) : x.msb = false 
goal: x.msb = false

full_name: BitVec.eq_of_toFin_eq
code: theorem eq_of_toFin_eq : ∀ {x y : BitVec w}, x.toFin = y.toFin → x = y
  
goal: x.toFin = y.toFin → x = y

full_name: BitVec.toNat_ofBool
code: @[simp] theorem toNat_ofBool (b : Bool) : (ofBool b).toNat = b.toNat 
goal: (BitVec.ofBool b).toNat = b.toNat

full_name: BitVec.msb_ofBool
code: @[simp] theorem msb_ofBool (b : Bool) : (ofBool b).msb = b 
goal: (BitVec.ofBool b).msb = b

full_name: BitVec.ofNat_one
code: theorem ofNat_one (n : Nat) : BitVec.ofNat 1 n = BitVec.ofBool (n % 2 = 1) 
goal: BitVec.ofNat 1 n = BitVec.ofBool (decide (n % 2 = 1))

full_name: BitVec.ofBool_eq_iff_eq
code: theorem ofBool_eq_iff_eq : ∀(b b' : Bool), BitVec.ofBool b = BitVec.ofBool b' ↔ b = b' 
goal: BitVec.ofBool b = BitVec.ofBool b' ↔ b = b'

full_name: BitVec.not_ofBool
code: @[simp] theorem not_ofBool : ~~~ (ofBool b) = ofBool (!b) 
goal: ~~~BitVec.ofBool b = BitVec.ofBool !b

full_name: BitVec.toNat_ofFin
code: @[simp, bv_toNat] theorem toNat_ofFin (x : Fin (2^n)) : (BitVec.ofFin x).toNat = x.val 
goal: { toFin := x }.toNat = ↑x

full_name: BitVec.toNat_ofNatLt
code: @[simp] theorem toNat_ofNatLt (x : Nat) (p : x < 2^w) : (x#'p).toNat = x 
goal: (x#'p).toNat = x

full_name: BitVec.getLsb_ofNatLt
code: @[simp] theorem getLsb_ofNatLt {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
  getLsb (x#'lt) i = x.testBit i 
goal: (x#'lt).getLsb i = x.testBit i

full_name: BitVec.toNat_ofNat
code: @[simp, bv_toNat] theorem toNat_ofNat (x w : Nat) : (BitVec.ofNat w x).toNat = x % 2^w 
goal: (BitVec.ofNat w x).toNat = x % 2 ^ w

full_name: BitVec.toFin_ofNat
code: @[simp] theorem toFin_ofNat (x : Nat) : toFin (BitVec.ofNat w x) = Fin.ofNat' x (Nat.two_pow_pos w) 
goal: (BitVec.ofNat w x).toFin = Fin.ofNat' x ⋯

full_name: BitVec.getLsb_ofNat
code: theorem getLsb_ofNat (n : Nat) (x : Nat) (i : Nat) :
  getLsb (BitVec.ofNat n x) i = (i < n && x.testBit i) 
goal: (BitVec.ofNat n x).getLsb i = (decide (i < n) && x.testBit i)

full_name: BitVec.toNat_zero
code: @[simp, deprecated toNat_ofNat (since := "2024-02-22")]
theorem toNat_zero (n : Nat) : (0#n).toNat = 0 
goal: (0#n).toNat = 0

full_name: BitVec.getLsb_zero
code: @[simp] theorem getLsb_zero : (0#w).getLsb i = false 
goal: (0#w).getLsb i = false

full_name: BitVec.getMsb_zero
code: @[simp] theorem getMsb_zero : (0#w).getMsb i = false 
goal: (0#w).getMsb i = false

full_name: BitVec.toNat_mod_cancel
code: @[simp] theorem toNat_mod_cancel (x : BitVec n) : x.toNat % (2^n) = x.toNat 
goal: x.toNat % 2 ^ n = x.toNat

full_name: _private.«.lake».packages.lean4.src.lean.Init.Data.BitVec.Lemmas.0.BitVec.lt_two_pow_of_le
code: private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m ≤ n) : x < 2 ^ n 
goal: x < 2 ^ n

full_name: BitVec.msb_zero
code: @[simp] theorem msb_zero : (0#w).msb = false 
goal: (0#w).msb = false

full_name: BitVec.msb_eq_getLsb_last
code: theorem msb_eq_getLsb_last (x : BitVec w) :
    x.msb = x.getLsb (w - 1) 
goal: x.msb = x.getLsb (w - 1)

full_name: BitVec.getLsb_last
code: @[bv_toNat] theorem getLsb_last (x : BitVec w) :
    x.getLsb (w-1) = decide (2 ^ (w-1) ≤ x.toNat) 
goal: x.getLsb (w - 1) = decide (2 ^ (w - 1) ≤ x.toNat)

full_name: BitVec.getLsb_succ_last
code: @[bv_toNat] theorem getLsb_succ_last (x : BitVec (w + 1)) :
    x.getLsb w = decide (2 ^ w ≤ x.toNat) 
goal: x.getLsb w = decide (2 ^ w ≤ x.toNat)

full_name: BitVec.msb_eq_decide
code: @[bv_toNat] theorem msb_eq_decide (x : BitVec w) : BitVec.msb x = decide (2 ^ (w-1) ≤ x.toNat) 
goal: x.msb = decide (2 ^ (w - 1) ≤ x.toNat)

full_name: BitVec.toNat_ge_of_msb_true
code: theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat ≥ 2^(n-1) 
goal: x.toNat ≥ 2 ^ (n - 1)

full_name: BitVec.toNat_cast
code: @[simp, bv_toNat] theorem toNat_cast (h : w = v) (x : BitVec w) : (cast h x).toNat = x.toNat 
goal: (BitVec.cast h x).toNat = x.toNat

full_name: BitVec.toFin_cast
code: @[simp] theorem toFin_cast (h : w = v) (x : BitVec w) :
    (cast h x).toFin = x.toFin.cast (by rw [h]) 
goal: (BitVec.cast h x).toFin = Fin.cast ⋯ x.toFin

full_name: BitVec.getLsb_cast
code: @[simp] theorem getLsb_cast (h : w = v) (x : BitVec w) : (cast h x).getLsb i = x.getLsb i 
goal: (BitVec.cast h x).getLsb i = x.getLsb i

full_name: BitVec.getMsb_cast
code: @[simp] theorem getMsb_cast (h : w = v) (x : BitVec w) : (cast h x).getMsb i = x.getMsb i 
goal: (BitVec.cast h x).getMsb i = x.getMsb i

full_name: BitVec.msb_cast
code: @[simp] theorem msb_cast (h : w = v) (x : BitVec w) : (cast h x).msb = x.msb 
goal: (BitVec.cast h x).msb = x.msb

full_name: BitVec.toInt_eq_toNat_cond
code: /-- Prove equality of bitvectors in terms of nat operations. -/
theorem toInt_eq_toNat_cond (i : BitVec n) :
    i.toInt =
      if 2*i.toNat < 2^n then
        (i.toNat : Int)
      else
        (i.toNat : Int) - (2^n : Nat) 
goal: i.toInt = if 2 * i.toNat < 2 ^ n then ↑i.toNat else ↑i.toNat - ↑(2 ^ n)

full_name: BitVec.msb_eq_false_iff_two_mul_lt
code: theorem msb_eq_false_iff_two_mul_lt (x : BitVec w) : x.msb = false ↔ 2 * x.toNat < 2^w 
goal: x.msb = false ↔ 2 * x.toNat < 2 ^ w

full_name: BitVec.msb_eq_true_iff_two_mul_ge
code: theorem msb_eq_true_iff_two_mul_ge (x : BitVec w) : x.msb = true ↔ 2 * x.toNat ≥ 2^w 
goal: x.msb = true ↔ 2 * x.toNat ≥ 2 ^ w

full_name: BitVec.toInt_eq_msb_cond
code: /-- 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) 
goal: x.toInt = if x.msb = true then ↑x.toNat - ↑(2 ^ w) else ↑x.toNat

full_name: BitVec.toInt_eq_toNat_bmod
code: theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) 
goal: x.toInt = (↑x.toNat).bmod (2 ^ n)

full_name: BitVec.eq_of_toInt_eq
code: /-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toInt_eq {i j : BitVec n} : i.toInt = j.toInt → i = j 
goal: i.toInt = j.toInt → i = j

full_name: BitVec.toInt_inj
code: theorem toInt_inj (x y : BitVec n) : x.toInt = y.toInt ↔ x = y 
goal: x.toInt = y.toInt ↔ x = y

full_name: BitVec.toInt_ne
code: theorem toInt_ne (x y : BitVec n) : x.toInt ≠ y.toInt ↔ x ≠ y  
goal: x.toInt ≠ y.toInt ↔ x ≠ y

full_name: BitVec.toNat_ofInt
code: @[simp] theorem toNat_ofInt {n : Nat} (i : Int) :
  (BitVec.ofInt n i).toNat = (i % (2^n : Nat)).toNat 
goal: (BitVec.ofInt n i).toNat = (i % ↑(2 ^ n)).toNat

full_name: BitVec.toInt_ofNat
code: theorem toInt_ofNat {n : Nat} (x : Nat) :
  (BitVec.ofNat n x).toInt = (x : Int).bmod (2^n) 
goal: (BitVec.ofNat n x).toInt = (↑x).bmod (2 ^ n)

full_name: BitVec.toInt_ofInt
code: @[simp] theorem toInt_ofInt {n : Nat} (i : Int) :
  (BitVec.ofInt n i).toInt = i.bmod (2^n) 
goal: (BitVec.ofInt n i).toInt = i.bmod (2 ^ n)

full_name: BitVec.ofInt_natCast
code: @[simp] theorem ofInt_natCast (w n : Nat) :
  BitVec.ofInt w (n : Int) = BitVec.ofNat w n 
goal: BitVec.ofInt w ↑n = BitVec.ofNat w n

full_name: BitVec.toNat_zeroExtend'
code: @[simp, bv_toNat] theorem toNat_zeroExtend' {m n : Nat} (p : m ≤ n) (x : BitVec m) :
    (zeroExtend' p x).toNat = x.toNat 
goal: (BitVec.zeroExtend' p x).toNat = x.toNat

full_name: BitVec.toNat_zeroExtend
code: @[bv_toNat] theorem toNat_zeroExtend (i : Nat) (x : BitVec n) :
    BitVec.toNat (zeroExtend i x) = x.toNat % 2^i 
goal: (BitVec.zeroExtend i x).toNat = x.toNat % 2 ^ i

full_name: BitVec.zeroExtend'_eq
code: theorem zeroExtend'_eq {x : BitVec w} (h : w ≤ v) : x.zeroExtend' h = x.zeroExtend v 
goal: BitVec.zeroExtend' h x = BitVec.zeroExtend v x

full_name: BitVec.toNat_truncate
code: @[simp, bv_toNat] theorem toNat_truncate (x : BitVec n) : (truncate i x).toNat = x.toNat % 2^i 
goal: (BitVec.truncate i x).toNat = x.toNat % 2 ^ i

full_name: BitVec.zeroExtend_eq
code: @[simp] theorem zeroExtend_eq (x : BitVec n) : zeroExtend n x = x 
goal: BitVec.zeroExtend n x = x

full_name: BitVec.zeroExtend_zero
code: @[simp] theorem zeroExtend_zero (m n : Nat) : zeroExtend m 0#n = 0#m 
goal: BitVec.zeroExtend m 0#n = 0#m

full_name: BitVec.truncate_eq
code: @[simp] theorem truncate_eq (x : BitVec n) : truncate n x = x 
goal: BitVec.truncate n x = x

full_name: BitVec.ofNat_toNat
code: @[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : BitVec.ofNat m x.toNat = truncate m x 
goal: BitVec.ofNat m x.toNat = BitVec.truncate m x

full_name: BitVec.toNat_eq_nat
code: /-- 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)) 
goal: x.toNat = y ↔ y < 2 ^ w ∧ x = BitVec.ofNat w y

full_name: BitVec.nat_eq_toNat
code: /-- 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)) 
goal: y = x.toNat ↔ y < 2 ^ w ∧ x = BitVec.ofNat w y

full_name: BitVec.getLsb_zeroExtend'
code: @[simp] theorem getLsb_zeroExtend' (ge : m ≥ n) (x : BitVec n) (i : Nat) :
    getLsb (zeroExtend' ge x) i = getLsb x i 
goal: (BitVec.zeroExtend' ge x).getLsb i = x.getLsb i

full_name: BitVec.getMsb_zeroExtend'
code: @[simp] theorem getMsb_zeroExtend' (ge : m ≥ n) (x : BitVec n) (i : Nat) :
    getMsb (zeroExtend' ge x) i = (decide (i ≥ m - n) && getMsb x (i - (m - n))) 
goal: (BitVec.zeroExtend' ge x).getMsb i = (decide (i ≥ m - n) && x.getMsb (i - (m - n)))

full_name: BitVec.getLsb_zeroExtend
code: @[simp] theorem getLsb_zeroExtend (m : Nat) (x : BitVec n) (i : Nat) :
    getLsb (zeroExtend m x) i = (decide (i < m) && getLsb x i) 
goal: (BitVec.zeroExtend m x).getLsb i = (decide (i < m) && x.getLsb i)

full_name: BitVec.getMsb_zeroExtend_add
code: @[simp] theorem getMsb_zeroExtend_add {x : BitVec w} (h : k ≤ i) :
    (x.zeroExtend (w + k)).getMsb i = x.getMsb (i - k) 
goal: (BitVec.zeroExtend (w + k) x).getMsb i = x.getMsb (i - k)

full_name: BitVec.getLsb_truncate
code: @[simp] theorem getLsb_truncate (m : Nat) (x : BitVec n) (i : Nat) :
    getLsb (truncate m x) i = (decide (i < m) && getLsb x i) 
goal: (BitVec.truncate m x).getLsb i = (decide (i < m) && x.getLsb i)

full_name: BitVec.msb_truncate
code: theorem msb_truncate (x : BitVec w) : (x.truncate (k + 1)).msb = x.getLsb k 
goal: (BitVec.truncate (k + 1) x).msb = x.getLsb k

full_name: BitVec.zeroExtend_zeroExtend_of_le
code: @[simp] theorem zeroExtend_zeroExtend_of_le (x : BitVec w) (h : k ≤ l) :
    (x.zeroExtend l).zeroExtend k = x.zeroExtend k 
goal: BitVec.zeroExtend k (BitVec.zeroExtend l x) = BitVec.zeroExtend k x

full_name: BitVec.truncate_truncate_of_le
code: @[simp] theorem truncate_truncate_of_le (x : BitVec w) (h : k ≤ l) :
    (x.truncate l).truncate k = x.truncate k 
goal: BitVec.truncate k (BitVec.truncate l x) = BitVec.truncate k x

full_name: BitVec.truncate_cast
code: @[simp] theorem truncate_cast {h : w = v} : (cast h x).truncate k = x.truncate k 
goal: BitVec.truncate k (BitVec.cast h x) = BitVec.truncate k x

full_name: BitVec.msb_zeroExtend
code: theorem msb_zeroExtend (x : BitVec w) : (x.zeroExtend v).msb = (decide (0 < v) && x.getLsb (v - 1)) 
goal: (BitVec.zeroExtend v x).msb = (decide (0 < v) && x.getLsb (v - 1))

full_name: BitVec.msb_zeroExtend'
code: theorem msb_zeroExtend' (x : BitVec w) (h : w ≤ v) : (x.zeroExtend' h).msb = (decide (0 < v) && x.getLsb (v - 1)) 
goal: (BitVec.zeroExtend' h x).msb = (decide (0 < v) && x.getLsb (v - 1))

full_name: BitVec.extractLsb_ofFin
code: @[simp]
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) 
goal: BitVec.extractLsb hi lo { toFin := x } = BitVec.ofNat (hi - lo + 1) (↑x >>> lo)

full_name: BitVec.extractLsb_ofNat
code: @[simp]
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) 
goal: BitVec.extractLsb hi lo (BitVec.ofNat n x) = BitVec.ofNat (hi - lo + 1) ((x % 2 ^ n) >>> lo)

full_name: BitVec.extractLsb'_toNat
code: @[simp] theorem extractLsb'_toNat (s m : Nat) (x : BitVec n) :
  (extractLsb' s m x).toNat = (x.toNat >>> s) % 2^m 
goal: (BitVec.extractLsb' s m x).toNat = x.toNat >>> s % 2 ^ m

full_name: BitVec.extractLsb_toNat
code: @[simp] theorem extractLsb_toNat (hi lo : Nat) (x : BitVec n) :
  (extractLsb hi lo x).toNat = (x.toNat >>> lo) % 2^(hi-lo+1) 
goal: (BitVec.extractLsb hi lo x).toNat = x.toNat >>> lo % 2 ^ (hi - lo + 1)

full_name: BitVec.getLsb_extract
code: @[simp] theorem getLsb_extract (hi lo : Nat) (x : BitVec n) (i : Nat) :
    getLsb (extractLsb hi lo x) i = (i ≤ (hi-lo) && getLsb x (lo+i)) 
goal: (BitVec.extractLsb hi lo x).getLsb i = (decide (i ≤ hi - lo) && x.getLsb (lo + i))

full_name: BitVec.toNat_allOnes
code: @[simp] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 
goal: (BitVec.allOnes v).toNat = 2 ^ v - 1

full_name: BitVec.getLsb_allOnes
code: @[simp] theorem getLsb_allOnes : (allOnes v).getLsb i = decide (i < v) 
goal: (BitVec.allOnes v).getLsb i = decide (i < v)

full_name: BitVec.toNat_or
code: @[simp] theorem toNat_or (x y : BitVec v) :
    BitVec.toNat (x ||| y) = BitVec.toNat x ||| BitVec.toNat y 
goal: (x ||| y).toNat = x.toNat ||| y.toNat

full_name: BitVec.toFin_or
code: @[simp] theorem toFin_or (x y : BitVec v) :
    BitVec.toFin (x ||| y) = BitVec.toFin x ||| BitVec.toFin y 
goal: (x ||| y).toFin = x.toFin ||| y.toFin

full_name: BitVec.getLsb_or
code: @[simp] theorem getLsb_or {x y : BitVec v} : (x ||| y).getLsb i = (x.getLsb i || y.getLsb i) 
goal: (x ||| y).getLsb i = (x.getLsb i || y.getLsb i)

full_name: BitVec.getMsb_or
code: @[simp] theorem getMsb_or {x y : BitVec w} : (x ||| y).getMsb i = (x.getMsb i || y.getMsb i) 
goal: (x ||| y).getMsb i = (x.getMsb i || y.getMsb i)

full_name: BitVec.msb_or
code: @[simp] theorem msb_or {x y : BitVec w} : (x ||| y).msb = (x.msb || y.msb) 
goal: (x ||| y).msb = (x.msb || y.msb)

full_name: BitVec.truncate_or
code: @[simp] theorem truncate_or {x y : BitVec w} :
    (x ||| y).truncate k = x.truncate k ||| y.truncate k 
goal: BitVec.truncate k (x ||| y) = BitVec.truncate k x ||| BitVec.truncate k y

full_name: BitVec.or_assoc
code: theorem or_assoc (x y z : BitVec w) :
    x ||| y ||| z = x ||| (y ||| z) 
goal: x ||| y ||| z = x ||| (y ||| z)

full_name: BitVec.toNat_and
code: @[simp] theorem toNat_and (x y : BitVec v) :
    BitVec.toNat (x &&& y) = BitVec.toNat x &&& BitVec.toNat y 
goal: (x &&& y).toNat = x.toNat &&& y.toNat

full_name: BitVec.toFin_and
code: @[simp] theorem toFin_and (x y : BitVec v) :
    BitVec.toFin (x &&& y) = BitVec.toFin x &&& BitVec.toFin y 
goal: (x &&& y).toFin = x.toFin &&& y.toFin

full_name: BitVec.getLsb_and
code: @[simp] theorem getLsb_and {x y : BitVec v} : (x &&& y).getLsb i = (x.getLsb i && y.getLsb i) 
goal: (x &&& y).getLsb i = (x.getLsb i && y.getLsb i)

full_name: BitVec.getMsb_and
code: @[simp] theorem getMsb_and {x y : BitVec w} : (x &&& y).getMsb i = (x.getMsb i && y.getMsb i) 
goal: (x &&& y).getMsb i = (x.getMsb i && y.getMsb i)

full_name: BitVec.msb_and
code: @[simp] theorem msb_and {x y : BitVec w} : (x &&& y).msb = (x.msb && y.msb) 
goal: (x &&& y).msb = (x.msb && y.msb)

full_name: BitVec.truncate_and
code: @[simp] theorem truncate_and {x y : BitVec w} :
    (x &&& y).truncate k = x.truncate k &&& y.truncate k 
goal: BitVec.truncate k (x &&& y) = BitVec.truncate k x &&& BitVec.truncate k y

full_name: BitVec.and_assoc
code: theorem and_assoc (x y z : BitVec w) :
    x &&& y &&& z = x &&& (y &&& z) 
goal: x &&& y &&& z = x &&& (y &&& z)

full_name: BitVec.toNat_xor
code: @[simp] theorem toNat_xor (x y : BitVec v) :
    BitVec.toNat (x ^^^ y) = BitVec.toNat x ^^^ BitVec.toNat y 
goal: (x ^^^ y).toNat = x.toNat ^^^ y.toNat

full_name: BitVec.toFin_xor
code: @[simp] theorem toFin_xor (x y : BitVec v) :
    BitVec.toFin (x ^^^ y) = BitVec.toFin x ^^^ BitVec.toFin y 
goal: (x ^^^ y).toFin = x.toFin ^^^ y.toFin

full_name: BitVec.getLsb_xor
code: @[simp] theorem getLsb_xor {x y : BitVec v} :
    (x ^^^ y).getLsb i = (xor (x.getLsb i) (y.getLsb i)) 
goal: (x ^^^ y).getLsb i = xor (x.getLsb i) (y.getLsb i)

full_name: BitVec.truncate_xor
code: @[simp] theorem truncate_xor {x y : BitVec w} :
    (x ^^^ y).truncate k = x.truncate k ^^^ y.truncate k 
goal: BitVec.truncate k (x ^^^ y) = BitVec.truncate k x ^^^ BitVec.truncate k y

full_name: BitVec.xor_assoc
code: theorem xor_assoc (x y z : BitVec w) :
    x ^^^ y ^^^ z = x ^^^ (y ^^^ z) 
goal: x ^^^ y ^^^ z = x ^^^ (y ^^^ z)

full_name: BitVec.not_def
code: theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x 
goal: ~~~x = BitVec.allOnes v ^^^ x

full_name: BitVec.toNat_not
code: @[simp, bv_toNat] theorem toNat_not {x : BitVec v} : (~~~x).toNat = 2^v - 1 - x.toNat 
goal: (~~~x).toNat = 2 ^ v - 1 - x.toNat

full_name: BitVec.toFin_not
code: @[simp] theorem toFin_not (x : BitVec w) :
    (~~~x).toFin = x.toFin.rev 
goal: (~~~x).toFin = x.toFin.rev

full_name: BitVec.getLsb_not
code: @[simp] theorem getLsb_not {x : BitVec v} : (~~~x).getLsb i = (decide (i < v) && ! x.getLsb i) 
goal: (~~~x).getLsb i = (decide (i < v) && !x.getLsb i)

full_name: BitVec.truncate_not
code: @[simp] theorem truncate_not {x : BitVec w} (h : k ≤ w) :
    (~~~x).truncate k = ~~~(x.truncate k) 
goal: BitVec.truncate k (~~~x) = ~~~BitVec.truncate k x

full_name: BitVec.not_cast
code: @[simp] theorem not_cast {x : BitVec w} (h : w = w') : ~~~(cast h x) = cast h (~~~x) 
goal: ~~~BitVec.cast h x = BitVec.cast h (~~~x)

full_name: BitVec.and_cast
code: @[simp] theorem and_cast {x y : BitVec w} (h : w = w') : cast h x &&& cast h y = cast h (x &&& y) 
goal: BitVec.cast h x &&& BitVec.cast h y = BitVec.cast h (x &&& y)

full_name: BitVec.or_cast
code: @[simp] theorem or_cast {x y : BitVec w} (h : w = w') : cast h x ||| cast h y = cast h (x ||| y) 
goal: BitVec.cast h x ||| BitVec.cast h y = BitVec.cast h (x ||| y)

full_name: BitVec.xor_cast
code: @[simp] theorem xor_cast {x y : BitVec w} (h : w = w') : cast h x &&& cast h y = cast h (x &&& y) 
goal: BitVec.cast h x &&& BitVec.cast h y = BitVec.cast h (x &&& y)

full_name: BitVec.toNat_shiftLeft
code: @[simp, bv_toNat] theorem toNat_shiftLeft {x : BitVec v} :
    BitVec.toNat (x <<< n) = BitVec.toNat x <<< n % 2^v 
goal: (x <<< n).toNat = x.toNat <<< n % 2 ^ v

full_name: BitVec.toFin_shiftLeft
code: @[simp] theorem toFin_shiftLeft {n : Nat} (x : BitVec w) :
    BitVec.toFin (x <<< n) = Fin.ofNat' (x.toNat <<< n) (Nat.two_pow_pos w) 
goal: (x <<< n).toFin = Fin.ofNat' (x.toNat <<< n) ⋯

full_name: BitVec.getLsb_shiftLeft
code: @[simp] theorem getLsb_shiftLeft (x : BitVec m) (n) :
    getLsb (x <<< n) i = (decide (i < m) && !decide (i < n) && getLsb x (i - n)) 
goal: (x <<< n).getLsb i = (decide (i < m) && !decide (i < n) && x.getLsb (i - n))

full_name: BitVec.getMsb_shiftLeft
code: @[simp] theorem getMsb_shiftLeft (x : BitVec w) (i) :
    (x <<< i).getMsb k = x.getMsb (k + i) 
goal: (x <<< i).getMsb k = x.getMsb (k + i)

full_name: BitVec.shiftLeftZeroExtend_eq
code: theorem shiftLeftZeroExtend_eq {x : BitVec w} :
    shiftLeftZeroExtend x n = zeroExtend (w+n) x <<< n 
goal: x.shiftLeftZeroExtend n = BitVec.zeroExtend (w + n) x <<< n

full_name: BitVec.getLsb_shiftLeftZeroExtend
code: @[simp] theorem getLsb_shiftLeftZeroExtend (x : BitVec m) (n : Nat) :
    getLsb (shiftLeftZeroExtend x n) i = ((! decide (i < n)) && getLsb x (i - n)) 
goal: (x.shiftLeftZeroExtend n).getLsb i = (!decide (i < n) && x.getLsb (i - n))

full_name: BitVec.getMsb_shiftLeftZeroExtend
code: @[simp] theorem getMsb_shiftLeftZeroExtend (x : BitVec m) (n : Nat) :
    getMsb (shiftLeftZeroExtend x n) i = getMsb x i 
goal: (x.shiftLeftZeroExtend n).getMsb i = x.getMsb i

full_name: BitVec.msb_shiftLeftZeroExtend
code: @[simp] theorem msb_shiftLeftZeroExtend (x : BitVec w) (i : Nat) :
    (shiftLeftZeroExtend x i).msb = x.msb 
goal: (x.shiftLeftZeroExtend i).msb = x.msb

full_name: BitVec.shiftLeft_add
code: theorem shiftLeft_add {w : Nat} (x : BitVec w) (n m : Nat) :
    x <<< (n + m) = (x <<< n) <<< m 
goal: x <<< (n + m) = x <<< n <<< m

full_name: BitVec.shiftLeft_shiftLeft
code: @[deprecated shiftLeft_add (since := "2024-06-02")]
theorem shiftLeft_shiftLeft {w : Nat} (x : BitVec w) (n m : Nat) :
    (x <<< n) <<< m = x <<< (n + m) 
goal: x <<< n <<< m = x <<< (n + m)

full_name: BitVec.toNat_ushiftRight
code: @[simp, bv_toNat] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
    (x >>> i).toNat = x.toNat >>> i 
goal: (x >>> i).toNat = x.toNat >>> i

full_name: BitVec.getLsb_ushiftRight
code: @[simp] theorem getLsb_ushiftRight (x : BitVec n) (i j : Nat) :
    getLsb (x >>> i) j = getLsb x (i+j) 
goal: (x >>> i).getLsb j = x.getLsb (i + j)

full_name: BitVec.sshiftRight_eq
code: theorem sshiftRight_eq {x : BitVec n} {i : Nat} :
    x.sshiftRight i = BitVec.ofInt n (x.toInt >>> i) 
goal: x.sshiftRight i = BitVec.ofInt n (x.toInt >>> i)

full_name: BitVec.sshiftRight_eq_of_msb_false
code: /-- if the msb is false, the arithmetic shift right equals logical shift right -/
theorem sshiftRight_eq_of_msb_false {x : BitVec w} {s : Nat} (h : x.msb = false) :
    (x.sshiftRight s) = x >>> s 
goal: x.sshiftRight s = x >>> s

full_name: BitVec.sshiftRight_eq_of_msb_true
code: /--
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) 
goal: x.sshiftRight s = ~~~(~~~x >>> s)

full_name: BitVec.getLsb_sshiftRight
code: theorem getLsb_sshiftRight (x : BitVec w) (s i : Nat) :
    getLsb (x.sshiftRight s) i =
      (!decide (w ≤ i) && if s + i < w then x.getLsb (s + i) else x.msb) 
goal: (x.sshiftRight s).getLsb i = (!decide (w ≤ i) && if s + i < w then x.getLsb (s + i) else x.msb)

full_name: _private.«.lake».packages.lean4.src.lean.Init.Data.BitVec.Lemmas.0.BitVec.Int.ofNat_sub_ofNat_of_lt
code: /-- 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) 
goal: ↑n - ↑m = -(↑(m - 1 - n) + 1)

full_name: _private.«.lake».packages.lean4.src.lean.Init.Data.BitVec.Lemmas.0.BitVec.Int.negSucc_emod
code: /-- 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) 
goal: -(↑m + 1) % n = Int.subNatNat n.natAbs (m % n.natAbs + 1)

full_name: BitVec.signExtend_eq_not_zeroExtend_not_of_msb_false
code: /-- The sign extension is the same as zero extending when `msb = false`. -/
theorem signExtend_eq_not_zeroExtend_not_of_msb_false {x : BitVec w} {v : Nat} (hmsb : x.msb = false) :
    x.signExtend v = x.zeroExtend v 
goal: BitVec.signExtend v x = BitVec.zeroExtend v x

full_name: BitVec.signExtend_eq_not_zeroExtend_not_of_msb_true
code: /--
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_zeroExtend_not_of_msb_true {x : BitVec w} {v : Nat} (hmsb : x.msb = true) :
    x.signExtend v = ~~~((~~~x).zeroExtend v) 
goal: BitVec.signExtend v x = ~~~BitVec.zeroExtend v (~~~x)

full_name: BitVec.getLsb_signExtend
code: @[simp] theorem getLsb_signExtend (x  : BitVec w) {v i : Nat} :
    (x.signExtend v).getLsb i = (decide (i < v) && if i < w then x.getLsb i else x.msb) 
goal: (BitVec.signExtend v x).getLsb i = (decide (i < v) && if i < w then x.getLsb i else x.msb)

full_name: BitVec.append_def
code: theorem append_def (x : BitVec v) (y : BitVec w) :
    x ++ y = (shiftLeftZeroExtend x w ||| zeroExtend' (Nat.le_add_left w v) y) 
goal: x ++ y = x.shiftLeftZeroExtend w ||| BitVec.zeroExtend' ⋯ y

full_name: BitVec.toNat_append
code: @[simp] theorem toNat_append (x : BitVec m) (y : BitVec n) :
    (x ++ y).toNat = x.toNat <<< n ||| y.toNat 
goal: (x ++ y).toNat = x.toNat <<< n ||| y.toNat

full_name: BitVec.getLsb_append
code: @[simp] theorem getLsb_append {v : BitVec n} {w : BitVec m} :
    getLsb (v ++ w) i = bif i < m then getLsb w i else getLsb v (i - m) 
goal: (v ++ w).getLsb i = bif decide (i < m) then w.getLsb i else v.getLsb (i - m)

full_name: BitVec.getMsb_append
code: @[simp] theorem getMsb_append {v : BitVec n} {w : BitVec m} :
    getMsb (v ++ w) i = bif n ≤ i then getMsb w (i - n) else getMsb v i 
goal: (v ++ w).getMsb i = bif decide (n ≤ i) then w.getMsb (i - n) else v.getMsb i

full_name: BitVec.msb_append
code: theorem msb_append {x : BitVec w} {y : BitVec v} :
    (x ++ y).msb = bif (w == 0) then (y.msb) else (x.msb) 
goal: (x ++ y).msb = bif w == 0 then y.msb else x.msb

full_name: BitVec.truncate_append
code: @[simp] theorem truncate_append {x : BitVec w} {y : BitVec v} :
    (x ++ y).truncate k = if h : k ≤ v then y.truncate k else (x.truncate (k - v) ++ y).cast (by omega) 
goal: BitVec.truncate k (x ++ y) = if h : k ≤ v then BitVec.truncate k y else BitVec.cast ⋯ (BitVec.truncate (k - v) x ++ y)

full_name: BitVec.truncate_cons
code: @[simp] theorem truncate_cons {x : BitVec w} : (cons a x).truncate w = x 
goal: BitVec.truncate w (BitVec.cons a x) = x

full_name: BitVec.not_append
code: @[simp] theorem not_append {x : BitVec w} {y : BitVec v} : ~~~ (x ++ y) = (~~~ x) ++ (~~~ y) 
goal: ~~~(x ++ y) = ~~~x ++ ~~~y

full_name: BitVec.and_append
code: @[simp] theorem and_append {x₁ x₂ : BitVec w} {y₁ y₂ : BitVec v} :
    (x₁ ++ y₁) &&& (x₂ ++ y₂) = (x₁ &&& x₂) ++ (y₁ &&& y₂) 
goal: x₁ ++ y₁ &&& x₂ ++ y₂ = (x₁ &&& x₂) ++ (y₁ &&& y₂)

full_name: BitVec.or_append
code: @[simp] theorem or_append {x₁ x₂ : BitVec w} {y₁ y₂ : BitVec v} :
    (x₁ ++ y₁) ||| (x₂ ++ y₂) = (x₁ ||| x₂) ++ (y₁ ||| y₂) 
goal: x₁ ++ y₁ ||| x₂ ++ y₂ = (x₁ ||| x₂) ++ (y₁ ||| y₂)

full_name: BitVec.xor_append
code: @[simp] theorem xor_append {x₁ x₂ : BitVec w} {y₁ y₂ : BitVec v} :
    (x₁ ++ y₁) ^^^ (x₂ ++ y₂) = (x₁ ^^^ x₂) ++ (y₁ ^^^ y₂) 
goal: x₁ ++ y₁ ^^^ x₂ ++ y₂ = (x₁ ^^^ x₂) ++ (y₁ ^^^ y₂)

full_name: BitVec.shiftRight_add
code: theorem shiftRight_add {w : Nat} (x : BitVec w) (n m : Nat) :
    x >>> (n + m) = (x >>> n) >>> m
goal: x >>> (n + m) = x >>> n >>> m

full_name: BitVec.shiftRight_shiftRight
code: @[deprecated shiftRight_add (since := "2024-06-02")]
theorem shiftRight_shiftRight {w : Nat} (x : BitVec w) (n m : Nat) :
    (x >>> n) >>> m = x >>> (n + m) 
goal: x >>> n >>> m = x >>> (n + m)

full_name: BitVec.getLsb_rev
code: theorem getLsb_rev (x : BitVec w) (i : Fin w) :
    x.getLsb i.rev = x.getMsb i 
goal: x.getLsb ↑i.rev = x.getMsb ↑i

full_name: BitVec.getMsb_rev
code: theorem getMsb_rev (x : BitVec w) (i : Fin w) :
    x.getMsb i.rev = x.getLsb i 
goal: x.getMsb ↑i.rev = x.getLsb ↑i

full_name: BitVec.toNat_cons
code: @[simp] theorem toNat_cons (b : Bool) (x : BitVec w) :
    (cons b x).toNat = (b.toNat <<< w) ||| x.toNat 
goal: (BitVec.cons b x).toNat = b.toNat <<< w ||| x.toNat

full_name: BitVec.toNat_cons'
code: /-- Variant of `toNat_cons` using `+` instead of `|||`. -/
theorem toNat_cons' {x : BitVec w} :
    (cons a x).toNat = (a.toNat <<< w) + x.toNat 
goal: (BitVec.cons a x).toNat = a.toNat <<< w + x.toNat

full_name: BitVec.getLsb_cons
code: @[simp] theorem getLsb_cons (b : Bool) {n} (x : BitVec n) (i : Nat) :
    getLsb (cons b x) i = if i = n then b else getLsb x i 
goal: (BitVec.cons b x).getLsb i = if i = n then b else x.getLsb i

full_name: BitVec.msb_cons
code: @[simp] theorem msb_cons : (cons a x).msb = a 
goal: ∀ {w : Nat} {x : BitVec w}, (BitVec.cons a x).msb = a

full_name: BitVec.getMsb_cons_zero
code: @[simp] theorem getMsb_cons_zero : (cons a x).getMsb 0 = a 
goal: ∀ {w : Nat} {x : BitVec w}, (BitVec.cons a x).getMsb 0 = a

full_name: BitVec.getMsb_cons_succ
code: @[simp] theorem getMsb_cons_succ : (cons a x).getMsb (i + 1) = x.getMsb i 
goal: ∀ {w : Nat} {x : BitVec w} {i : Nat}, (BitVec.cons a x).getMsb (i + 1) = x.getMsb i

full_name: BitVec.truncate_succ
code: theorem truncate_succ (x : BitVec w) :
    truncate (i+1) x = cons (getLsb x i) (truncate i x) 
goal: BitVec.truncate (i + 1) x = BitVec.cons (x.getLsb i) (BitVec.truncate i x)

full_name: BitVec.eq_msb_cons_truncate
code: theorem eq_msb_cons_truncate (x : BitVec (w+1)) : x = (cons x.msb (x.truncate w)) 
goal: x = BitVec.cons x.msb (BitVec.truncate w x)

full_name: BitVec.not_cons
code: @[simp] theorem not_cons (x : BitVec w) (b : Bool) : ~~~(cons b x) = cons (!b) (~~~x) 
goal: ~~~BitVec.cons b x = BitVec.cons (!b) (~~~x)

full_name: BitVec.cons_or_cons
code: @[simp] theorem cons_or_cons (x y : BitVec w) (a b : Bool) :
    (cons a x) ||| (cons b y) = cons (a || b) (x ||| y) 
goal: BitVec.cons a x ||| BitVec.cons b y = BitVec.cons (a || b) (x ||| y)

full_name: BitVec.cons_and_cons
code: @[simp] theorem cons_and_cons (x y : BitVec w) (a b : Bool) :
    (cons a x) &&& (cons b y) = cons (a && b) (x &&& y) 
goal: BitVec.cons a x &&& BitVec.cons b y = BitVec.cons (a && b) (x &&& y)

full_name: BitVec.cons_xor_cons
code: @[simp] theorem cons_xor_cons (x y : BitVec w) (a b : Bool) :
    (cons a x) ^^^ (cons b y) = cons (xor a b) (x ^^^ y) 
goal: BitVec.cons a x ^^^ BitVec.cons b y = BitVec.cons (xor a b) (x ^^^ y)

full_name: BitVec.toNat_concat
code: @[simp] theorem toNat_concat (x : BitVec w) (b : Bool) :
    (concat x b).toNat = x.toNat * 2 + b.toNat 
goal: (x.concat b).toNat = x.toNat * 2 + b.toNat

full_name: BitVec.getLsb_concat
code: theorem getLsb_concat (x : BitVec w) (b : Bool) (i : Nat) :
    (concat x b).getLsb i = if i = 0 then b else x.getLsb (i - 1) 
goal: (x.concat b).getLsb i = if i = 0 then b else x.getLsb (i - 1)

full_name: BitVec.getLsb_concat_zero
code: @[simp] theorem getLsb_concat_zero : (concat x b).getLsb 0 = b 
goal: ∀ {w : Nat} {x : BitVec w} {b : Bool}, (x.concat b).getLsb 0 = b

full_name: BitVec.getLsb_concat_succ
code: @[simp] theorem getLsb_concat_succ : (concat x b).getLsb (i + 1) = x.getLsb i 
goal: ∀ {w : Nat} {x : BitVec w} {b : Bool} {i : Nat}, (x.concat b).getLsb (i + 1) = x.getLsb i

full_name: BitVec.not_concat
code: @[simp] theorem not_concat (x : BitVec w) (b : Bool) : ~~~(concat x b) = concat (~~~x) !b 
goal: ~~~x.concat b = (~~~x).concat !b

full_name: BitVec.concat_or_concat
code: @[simp] theorem concat_or_concat (x y : BitVec w) (a b : Bool) :
    (concat x a) ||| (concat y b) = concat (x ||| y) (a || b) 
goal: x.concat a ||| y.concat b = (x ||| y).concat (a || b)

full_name: BitVec.concat_and_concat
code: @[simp] theorem concat_and_concat (x y : BitVec w) (a b : Bool) :
    (concat x a) &&& (concat y b) = concat (x &&& y) (a && b) 
goal: x.concat a &&& y.concat b = (x &&& y).concat (a && b)

full_name: BitVec.concat_xor_concat
code: @[simp] theorem concat_xor_concat (x y : BitVec w) (a b : Bool) :
    (concat x a) ^^^ (concat y b) = concat (x ^^^ y) (xor a b) 
goal: x.concat a ^^^ y.concat b = (x ^^^ y).concat (xor a b)

full_name: BitVec.add_def
code: theorem add_def {n} (x y : BitVec n) : x + y = .ofNat n (x.toNat + y.toNat) 
goal: x + y = BitVec.ofNat n (x.toNat + y.toNat)

full_name: BitVec.toNat_add
code: /--
Definition of bitvector addition as a nat.
-/
@[simp, bv_toNat] theorem toNat_add (x y : BitVec w) : (x + y).toNat = (x.toNat + y.toNat) % 2^w 
goal: (x + y).toNat = (x.toNat + y.toNat) % 2 ^ w

full_name: BitVec.toFin_add
code: @[simp] theorem toFin_add (x y : BitVec w) : (x + y).toFin = toFin x + toFin y 
goal: (x + y).toFin = x.toFin + y.toFin

full_name: BitVec.ofFin_add
code: @[simp] theorem ofFin_add (x : Fin (2^n)) (y : BitVec n) :
  .ofFin x + y = .ofFin (x + y.toFin) 
goal: { toFin := x } + y = { toFin := x + y.toFin }

full_name: BitVec.add_ofFin
code: @[simp] theorem add_ofFin (x : BitVec n) (y : Fin (2^n)) :
  x + .ofFin y = .ofFin (x.toFin + y) 
goal: x + { toFin := y } = { toFin := x.toFin + y }

full_name: BitVec.ofNat_add
code: theorem ofNat_add {n} (x y : Nat) : BitVec.ofNat n (x + y) = BitVec.ofNat n x + BitVec.ofNat n y 
goal: BitVec.ofNat n (x + y) = BitVec.ofNat n x + BitVec.ofNat n y

full_name: BitVec.ofNat_add_ofNat
code: theorem ofNat_add_ofNat {n} (x y : Nat) : BitVec.ofNat n x + BitVec.ofNat n y = BitVec.ofNat n (x + y) 
goal: BitVec.ofNat n x + BitVec.ofNat n y = BitVec.ofNat n (x + y)

full_name: BitVec.add_assoc
code: protected theorem add_assoc (x y z : BitVec n) : x + y + z = x + (y + z) 
goal: x + y + z = x + (y + z)

full_name: BitVec.«instAssociativeHAdd_.lake»
code: instance : Std.Associative (α := BitVec n) (· + ·) 
goal: Std.Associative fun x x_1 => x + x_1

full_name: BitVec.add_comm
code: protected theorem add_comm (x y : BitVec n) : x + y = y + x 
goal: x + y = y + x

full_name: BitVec.«instCommutativeHAdd_.lake»
code: instance : Std.Commutative (α := BitVec n) (· + ·) 
goal: Std.Commutative fun x x_1 => x + x_1

full_name: BitVec.add_zero
code: @[simp] protected theorem add_zero (x : BitVec n) : x + 0#n = x 
goal: x + 0#n = x

full_name: BitVec.zero_add
code: @[simp] protected theorem zero_add (x : BitVec n) : 0#n + x = x 
goal: 0#n + x = x

full_name: BitVec.«instLawfulIdentityHAddOfNatOfNatNat_.lake»
code: instance : Std.LawfulIdentity (α := BitVec n) (· + ·) 0#n 
goal: Std.LawfulIdentity (fun x x_1 => x + x_1) 0#n

full_name: BitVec.truncate_add
code: theorem truncate_add (x y : BitVec w) (h : i ≤ w) :
    (x + y).truncate i = x.truncate i + y.truncate i 
goal: BitVec.truncate i (x + y) = BitVec.truncate i x + BitVec.truncate i y

full_name: BitVec.toInt_add
code: @[simp, bv_toNat] theorem toInt_add (x y : BitVec w) :
  (x + y).toInt = (x.toInt + y.toInt).bmod (2^w) 
goal: (x + y).toInt = (x.toInt + y.toInt).bmod (2 ^ w)

full_name: BitVec.ofInt_add
code: theorem ofInt_add {n} (x y : Int) : BitVec.ofInt n (x + y) =
    BitVec.ofInt n x + BitVec.ofInt n y 
goal: BitVec.ofInt n (x + y) = BitVec.ofInt n x + BitVec.ofInt n y

full_name: BitVec.sub_def
code: theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n ((2^n - y.toNat) + x.toNat) 
goal: x - y = BitVec.ofNat n (2 ^ n - y.toNat + x.toNat)

full_name: BitVec.toNat_sub
code: @[simp] theorem toNat_sub {n} (x y : BitVec n) :
    (x - y).toNat = (((2^n - y.toNat) + x.toNat) % 2^n) 
goal: (x - y).toNat = (2 ^ n - y.toNat + x.toNat) % 2 ^ n

full_name: BitVec.toNat_sub'
code: @[bv_toNat] theorem toNat_sub' {n} (x y : BitVec n) :
    (x - y).toNat = ((x.toNat + (2^n - y.toNat)) % 2^n) 
goal: (x - y).toNat = (x.toNat + (2 ^ n - y.toNat)) % 2 ^ n

full_name: BitVec.toFin_sub
code: @[simp] theorem toFin_sub (x y : BitVec n) : (x - y).toFin = toFin x - toFin y 
goal: (x - y).toFin = x.toFin - y.toFin

full_name: BitVec.ofFin_sub
code: @[simp] theorem ofFin_sub (x : Fin (2^n)) (y : BitVec n) : .ofFin x - y = .ofFin (x - y.toFin) 
goal: { toFin := x } - y = { toFin := x - y.toFin }

full_name: BitVec.sub_ofFin
code: @[simp] theorem sub_ofFin (x : BitVec n) (y : Fin (2^n)) : x - .ofFin y = .ofFin (x.toFin - y) 
goal: x - { toFin := y } = { toFin := x.toFin - y }

full_name: BitVec.ofNat_sub_ofNat
code: theorem ofNat_sub_ofNat {n} (x y : Nat) : BitVec.ofNat n x - BitVec.ofNat n y = .ofNat n ((2^n - y % 2^n) + x) 
goal: BitVec.ofNat n x - BitVec.ofNat n y = BitVec.ofNat n (2 ^ n - y % 2 ^ n + x)

full_name: BitVec.sub_zero
code: @[simp] protected theorem sub_zero (x : BitVec n) : x - 0#n = x 
goal: x - 0#n = x

full_name: BitVec.sub_self
code: @[simp] protected theorem sub_self (x : BitVec n) : x - x = 0#n 
goal: x - x = 0#n

full_name: BitVec.toNat_neg
code: @[simp, bv_toNat] theorem toNat_neg (x : BitVec n) : (- x).toNat = (2^n - x.toNat) % 2^n 
goal: (-x).toNat = (2 ^ n - x.toNat) % 2 ^ n

full_name: BitVec.toFin_neg
code: @[simp] theorem toFin_neg (x : BitVec n) :
    (-x).toFin = Fin.ofNat' (2^n - x.toNat) (Nat.two_pow_pos _) 
goal: (-x).toFin = Fin.ofNat' (2 ^ n - x.toNat) ⋯

full_name: BitVec.sub_toAdd
code: theorem sub_toAdd {n} (x y : BitVec n) : x - y = x + - y 
goal: x - y = x + -y

full_name: BitVec.neg_zero
code: @[simp] theorem neg_zero (n:Nat) : -BitVec.ofNat n 0 = BitVec.ofNat n 0 
goal: -0#n = 0#n

full_name: BitVec.add_sub_cancel
code: theorem add_sub_cancel (x y : BitVec w) : x + y - y = x 
goal: x + y - y = x

full_name: BitVec.sub_add_cancel
code: theorem sub_add_cancel (x y : BitVec w) : x - y + y = x 
goal: x - y + y = x

full_name: BitVec.eq_sub_iff_add_eq
code: theorem eq_sub_iff_add_eq {x y z : BitVec w} : x = z - y ↔ x + y = z 
goal: x = z - y ↔ x + y = z

full_name: BitVec.negOne_eq_allOnes
code: theorem negOne_eq_allOnes : -1#w = allOnes w 
goal: -1#w = BitVec.allOnes w

full_name: BitVec.neg_eq_not_add
code: theorem neg_eq_not_add (x : BitVec w) : -x = ~~~x + 1 
goal: -x = ~~~x + 1

full_name: BitVec.mul_def
code: theorem mul_def {n} {x y : BitVec n} : x * y = (ofFin <| x.toFin * y.toFin) 
goal: x * y = { toFin := x.toFin * y.toFin }

full_name: BitVec.toNat_mul
code: @[simp, bv_toNat] theorem toNat_mul (x y : BitVec n) : (x * y).toNat = (x.toNat * y.toNat) % 2 ^ n 
goal: (x * y).toNat = x.toNat * y.toNat % 2 ^ n

full_name: BitVec.toFin_mul
code: @[simp] theorem toFin_mul (x y : BitVec n) : (x * y).toFin = (x.toFin * y.toFin) 
goal: (x * y).toFin = x.toFin * y.toFin

full_name: BitVec.mul_comm
code: protected theorem mul_comm (x y : BitVec w) : x * y = y * x 
goal: x * y = y * x

full_name: BitVec.«instCommutativeHMul_.lake»
code: instance : Std.Commutative (fun (x y : BitVec w) => x * y) 
goal: Std.Commutative fun x y => x * y

full_name: BitVec.mul_assoc
code: protected theorem mul_assoc (x y z : BitVec w) : x * y * z = x * (y * z) 
goal: x * y * z = x * (y * z)

full_name: BitVec.«instAssociativeHMul_.lake»
code: instance : Std.Associative (fun (x y : BitVec w) => x * y) 
goal: Std.Associative fun x y => x * y

full_name: BitVec.mul_one
code: @[simp] protected theorem mul_one (x : BitVec w) : x * 1#w = x 
goal: x * 1#w = x

full_name: BitVec.one_mul
code: @[simp] protected theorem one_mul (x : BitVec w) : 1#w * x = x 
goal: 1#w * x = x

full_name: BitVec.«instLawfulCommIdentityHMulOfNatOfNatNat_.lake»
code: instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) 
goal: Std.LawfulCommIdentity (fun x y => x * y) 1#w

full_name: BitVec.toInt_mul
code: @[simp, bv_toNat] theorem toInt_mul (x y : BitVec w) :
  (x * y).toInt = (x.toInt * y.toInt).bmod (2^w) 
goal: (x * y).toInt = (x.toInt * y.toInt).bmod (2 ^ w)

full_name: BitVec.ofInt_mul
code: theorem ofInt_mul {n} (x y : Int) : BitVec.ofInt n (x * y) =
    BitVec.ofInt n x * BitVec.ofInt n y 
goal: BitVec.ofInt n (x * y) = BitVec.ofInt n x * BitVec.ofInt n y

full_name: BitVec.le_def
code: @[bv_toNat] theorem le_def (x y : BitVec n) :
  x ≤ y ↔ x.toNat ≤ y.toNat 
goal: x ≤ y ↔ x.toNat ≤ y.toNat

full_name: BitVec.le_ofFin
code: @[simp] theorem le_ofFin (x : BitVec n) (y : Fin (2^n)) :
  x ≤ BitVec.ofFin y ↔ x.toFin ≤ y 
goal: x ≤ { toFin := y } ↔ x.toFin ≤ y

full_name: BitVec.ofFin_le
code: @[simp] theorem ofFin_le (x : Fin (2^n)) (y : BitVec n) :
  BitVec.ofFin x ≤ y ↔ x ≤ y.toFin 
goal: { toFin := x } ≤ y ↔ x ≤ y.toFin

full_name: BitVec.ofNat_le_ofNat
code: @[simp] theorem ofNat_le_ofNat {n} (x y : Nat) : (BitVec.ofNat n x) ≤ (BitVec.ofNat n y) ↔ x % 2^n ≤ y % 2^n 
goal: BitVec.ofNat n x ≤ BitVec.ofNat n y ↔ x % 2 ^ n ≤ y % 2 ^ n

full_name: BitVec.lt_def
code: @[bv_toNat] theorem lt_def (x y : BitVec n) :
  x < y ↔ x.toNat < y.toNat 
goal: x < y ↔ x.toNat < y.toNat

full_name: BitVec.lt_ofFin
code: @[simp] theorem lt_ofFin (x : BitVec n) (y : Fin (2^n)) :
  x < BitVec.ofFin y ↔ x.toFin < y 
goal: x < { toFin := y } ↔ x.toFin < y

full_name: BitVec.ofFin_lt
code: @[simp] theorem ofFin_lt (x : Fin (2^n)) (y : BitVec n) :
  BitVec.ofFin x < y ↔ x < y.toFin 
goal: { toFin := x } < y ↔ x < y.toFin

full_name: BitVec.ofNat_lt_ofNat
code: @[simp] theorem ofNat_lt_ofNat {n} (x y : Nat) : BitVec.ofNat n x < BitVec.ofNat n y ↔ x % 2^n < y % 2^n 
goal: BitVec.ofNat n x < BitVec.ofNat n y ↔ x % 2 ^ n < y % 2 ^ n

full_name: BitVec.lt_of_le_ne
code: protected theorem lt_of_le_ne (x y : BitVec n) (h1 : x <= y) (h2 : ¬ x = y) : x < y 
goal: x < y

full_name: BitVec.intMax
code: /-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
def intMax (w : Nat) : BitVec w 
goal: BitVec w

full_name: BitVec.getLsb_intMax_eq
code: theorem getLsb_intMax_eq (w : Nat) : (intMax w).getLsb i = decide (i < w) 
goal: (BitVec.intMax w).getLsb i = decide (i < w)

full_name: BitVec.toNat_intMax_eq
code: theorem toNat_intMax_eq : (intMax w).toNat = 2^w - 1 
goal: (BitVec.intMax w).toNat = 2 ^ w - 1

full_name: BitVec.getMsb_ofBoolListBE
code: @[simp] theorem getMsb_ofBoolListBE : (ofBoolListBE bs).getMsb i = bs.getD i false 
goal: (BitVec.ofBoolListBE bs).getMsb i = bs.getD i false

full_name: BitVec.getLsb_ofBoolListBE
code: @[simp] theorem getLsb_ofBoolListBE :
    (ofBoolListBE bs).getLsb i = (decide (i < bs.length) && bs.getD (bs.length - 1 - i) false) 
goal: (BitVec.ofBoolListBE bs).getLsb i = (decide (i < bs.length) && bs.getD (bs.length - 1 - i) false)

full_name: BitVec.getLsb_ofBoolListLE
code: @[simp] theorem getLsb_ofBoolListLE : (ofBoolListLE bs).getLsb i = bs.getD i false 
goal: (BitVec.ofBoolListLE bs).getLsb i = bs.getD i false

full_name: BitVec.getMsb_ofBoolListLE
code: @[simp] theorem getMsb_ofBoolListLE :
    (ofBoolListLE bs).getMsb i = (decide (i < bs.length) && bs.getD (bs.length - 1 - i) false) 
goal: (BitVec.ofBoolListLE bs).getMsb i = (decide (i < bs.length) && bs.getD (bs.length - 1 - i) false)

full_name: BitVec.rotateLeft_mod_eq_rotateLeft
code: /-- rotateLeft is invariant under `mod` by the bitwidth. -/
@[simp]
theorem rotateLeft_mod_eq_rotateLeft {x : BitVec w} {r : Nat} :
    x.rotateLeft (r % w) = x.rotateLeft r 
goal: x.rotateLeft (r % w) = x.rotateLeft r

full_name: BitVec.rotateLeft_eq_rotateLeftAux_of_lt
code: /-- `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 
goal: x.rotateLeft r = x.rotateLeftAux r

full_name: BitVec.getLsb_rotateLeftAux_of_le
code: /--
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).getLsb ⟨i, i < 2⟩
= <3 2 1 0 | 6 5>.getLsb ⟨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 getLsb_rotateLeftAux_of_le {x : BitVec w} {r : Nat} {i : Nat} (hi : i < r) :
    (x.rotateLeftAux r).getLsb i = x.getLsb (w - r + i) 
goal: (x.rotateLeftAux r).getLsb i = x.getLsb (w - r + i)

full_name: BitVec.getLsb_rotateLeftAux_of_geq
code: /--
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).getLsb ⟨i, i ≥ 2⟩
= <3 2 1 0 | 6 5>.getLsb ⟨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 getLsb_rotateLeftAux_of_geq {x : BitVec w} {r : Nat} {i : Nat} (hi : i ≥ r) :
    (x.rotateLeftAux r).getLsb i = (decide (i < w) && x.getLsb (i - r)) 
goal: (x.rotateLeftAux r).getLsb i = (decide (i < w) && x.getLsb (i - r))

full_name: BitVec.getLsb_rotateLeft_of_le
code: /-- When `r < w`, we give a formula for `(x.rotateRight r).getLsb i`. -/
theorem getLsb_rotateLeft_of_le {x : BitVec w} {r i : Nat} (hr: r < w) :
    (x.rotateLeft r).getLsb i =
      cond (i < r)
      (x.getLsb (w - r + i))
      (decide (i < w) && x.getLsb (i - r)) 
goal: (x.rotateLeft r).getLsb i = bif decide (i < r) then x.getLsb (w - r + i) else decide (i < w) && x.getLsb (i - r)

full_name: BitVec.getLsb_rotateLeft
code: @[simp]
theorem getLsb_rotateLeft {x : BitVec w} {r i : Nat}  :
    (x.rotateLeft r).getLsb i =
      cond (i < r % w)
      (x.getLsb (w - (r % w) + i))
      (decide (i < w) && x.getLsb (i - (r % w))) 
goal: (x.rotateLeft r).getLsb i =
  bif decide (i < r % w) then x.getLsb (w - r % w + i) else decide (i < w) && x.getLsb (i - r % w)

full_name: BitVec.getLsb_rotateRightAux_of_le
code: /--
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).getLsb ⟨i, i ≤ 7 - 2⟩
= <1 0 | 6 5 4 3 2>.getLsb ⟨i, i ≤ 7 - 2⟩
= <6 5 4 3 2>.getLsb i
= <6 5 4 3 2 | 1 0>[i + 2]
-/
theorem getLsb_rotateRightAux_of_le {x : BitVec w} {r : Nat} {i : Nat} (hi : i < w - r) :
    (x.rotateRightAux r).getLsb i = x.getLsb (r + i) 
goal: (x.rotateRightAux r).getLsb i = x.getLsb (r + i)

full_name: BitVec.getLsb_rotateRightAux_of_geq
code: /--
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).getLsb ⟨i, i ≥ 7 - 2⟩
= <1 0 | 6 5 4 3 2>.getLsb ⟨i, i ≤ 7 - 2⟩
= <1 0>.getLsb (i - len(<6 5 4 3 2>)
= <6 5 4 3 2 | 1 0> (i - len<6 4 4 3 2>)
 -/
theorem getLsb_rotateRightAux_of_geq {x : BitVec w} {r : Nat} {i : Nat} (hi : i ≥ w - r) :
    (x.rotateRightAux r).getLsb i = (decide (i < w) && x.getLsb (i - (w - r))) 
goal: (x.rotateRightAux r).getLsb i = (decide (i < w) && x.getLsb (i - (w - r)))

full_name: BitVec.rotateRight_eq_rotateRightAux_of_lt
code: /-- `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 
goal: x.rotateRight r = x.rotateRightAux r

full_name: BitVec.rotateRight_mod_eq_rotateRight
code: /-- rotateRight is invariant under `mod` by the bitwidth. -/
@[simp]
theorem rotateRight_mod_eq_rotateRight {x : BitVec w} {r : Nat} :
    x.rotateRight (r % w) = x.rotateRight r 
goal: x.rotateRight (r % w) = x.rotateRight r

full_name: BitVec.getLsb_rotateRight_of_le
code: /-- When `r < w`, we give a formula for `(x.rotateRight r).getLsb i`. -/
theorem getLsb_rotateRight_of_le {x : BitVec w} {r i : Nat} (hr: r < w) :
    (x.rotateRight r).getLsb i =
      cond (i < w - r)
      (x.getLsb (r + i))
      (decide (i < w) && x.getLsb (i - (w - r))) 
goal: (x.rotateRight r).getLsb i = bif decide (i < w - r) then x.getLsb (r + i) else decide (i < w) && x.getLsb (i - (w - r))

full_name: BitVec.getLsb_rotateRight
code: @[simp]
theorem getLsb_rotateRight {x : BitVec w} {r i : Nat} :
    (x.rotateRight r).getLsb i =
      cond (i < w - (r % w))
      (x.getLsb ((r % w) + i))
      (decide (i < w) && x.getLsb (i - (w - (r % w)))) 
goal: (x.rotateRight r).getLsb i =
  bif decide (i < w - r % w) then x.getLsb (r % w + i) else decide (i < w) && x.getLsb (i - (w - r % w))

full_name: BitVec.toNat_twoPow
code: @[simp, bv_toNat]
theorem toNat_twoPow (w : Nat) (i : Nat) : (twoPow w i).toNat = 2^i % 2^w 
goal: (BitVec.twoPow w i).toNat = 2 ^ i % 2 ^ w

full_name: BitVec.getLsb_twoPow
code: @[simp]
theorem getLsb_twoPow (i j : Nat) : (twoPow w i).getLsb j = ((i < w) && (i = j)) 
goal: (BitVec.twoPow w i).getLsb j = (decide (i < w) && decide (i = j))

full_name: BitVec.and_twoPow_eq
code: theorem and_twoPow_eq (x : BitVec w) (i : Nat) :
    x &&& (twoPow w i) = if x.getLsb i then twoPow w i else 0#w 
goal: x &&& BitVec.twoPow w i = if x.getLsb i = true then BitVec.twoPow w i else 0#w

full_name: BitVec.mul_twoPow_eq_shiftLeft
code: @[simp]
theorem mul_twoPow_eq_shiftLeft (x : BitVec w) (i : Nat) :
    x * (twoPow w i) = x <<< i 
goal: x * BitVec.twoPow w i = x <<< i