feat: add BitVec.toInt_sdiv plus corresponding BitVec theory (#7565)

This PR adds `BitVec.toInt_sdiv` plus a lot of related bitvector theory
around divisions.

Coauthored-by: Markus Himmel <markus@lean-fro.org>
This commit is contained in:
Tobias Grosser 2025-03-26 14:20:15 +00:00 committed by GitHub
parent b2da85971d
commit 149b6423f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 363 additions and 4 deletions

View file

@ -1371,6 +1371,261 @@ theorem eq_iff_eq_of_inv (f : α → BitVec w) (g : BitVec w → α) (h : ∀ x,
have := congrArg g h'
simpa [h] using this
@[simp]
theorem ne_intMin_of_lt_of_msb_false {x : BitVec w} (hw : 0 < w) (hx : x.msb = false) :
x ≠ intMin w := by
have := toNat_lt_of_msb_false hx
simp [toNat_eq, Nat.two_pow_pred_mod_two_pow hw]
omega
@[simp]
theorem ne_zero_of_msb_true {x : BitVec w} (hx : x.msb = true) :
x ≠ 0#w := by
have := Nat.two_pow_pos (w-1)
have := le_toNat_of_msb_true hx
simp [toNat_eq]
omega
@[simp]
theorem msb_neg_of_ne_intMin_of_ne_zero {x : BitVec w} (h : x ≠ intMin w) (h' : x ≠ 0#w) :
(-x).msb = !x.msb := by
simp only [msb_neg, bool_to_prop]
simp [h, h']
@[simp]
theorem udiv_intMin_of_msb_false {x : BitVec w} (h : x.msb = false) :
x / intMin w = 0#w := by
by_cases hw : w = 0
· subst hw
decide +revert
have wpos : 0 < w := by omega
have := Nat.two_pow_pos (w-1)
simp [toNat_eq, wpos]
rw [Nat.div_eq_zero_iff_lt (by omega)]
exact toNat_lt_of_msb_false h
theorem sdiv_intMin {x : BitVec w} :
x.sdiv (intMin w) = if x = intMin w then 1#w else 0#w := by
by_cases hw : w = 0
· subst hw
decide +revert
have wpos : 0 < w := by omega
by_cases h : x = intMin w
· subst h
simp
omega
· simp only [sdiv_eq, msb_intMin, show 0 < w by omega, h]
have := Nat.two_pow_pos (w-1)
by_cases hx : x.msb
· simp [msb_neg_of_ne_intMin_of_ne_zero (by simp [h])
(BitVec.ne_zero_of_msb_true hx), hx]
· simp [hx]
theorem sdiv_neg {x y : BitVec w} (h : y ≠ intMin w) :
x.sdiv (-y) = -(x.sdiv y) := by
by_cases h' : y = 0#w
· subst h'
simp
· simp only [BitVec.sdiv, msb_neg_of_ne_intMin_of_ne_zero h (by simp [h'])]
cases x.msb <;> cases y.msb <;> simp
theorem neg_sdiv {x y : BitVec w} (h : x ≠ intMin w) :
(-x).sdiv y = -(x.sdiv y) := by
by_cases hx0 : x = 0#w
· subst hx0
simp
· simp only [BitVec.sdiv, msb_neg_of_ne_intMin_of_ne_zero h (by simp [hx0])]
cases x.msb <;> cases y.msb <;> simp
theorem neg_sdiv_neg {x y : BitVec w} (h : x ≠ intMin w) :
(-x).sdiv (-y) = x.sdiv y := by
by_cases h' : y = intMin w
· subst h'
simp [sdiv_intMin, neg_intMin]
· by_cases hy0 : y = 0#w
· subst hy0
simp
· by_cases hx0 : x = 0#w
· subst hx0
simp
· simp only [BitVec.sdiv,
msb_neg_of_ne_intMin_of_ne_zero h (by simp [hx0]),
msb_neg_of_ne_intMin_of_ne_zero h' (by simp [hy0])]
cases x.msb <;> cases y.msb <;> simp
theorem intMin_eq_neg_two_pow : intMin w = BitVec.ofInt w (-2 ^ (w - 1)) := by
apply BitVec.eq_of_toInt_eq
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; simp [BitVec.toInt_zero_length]) (fun hw => ?_)
rw [BitVec.toInt_intMin_of_pos hw, BitVec.toInt_ofInt_eq_self hw (Int.le_refl _)]
have := Nat.two_pow_pos (w - 1)
norm_cast
omega
theorem toInt_intMin_eq_bmod : (intMin w).toInt = (-2 ^ (w - 1)).bmod (2 ^ w) := by
rw [intMin_eq_neg_two_pow, toInt_ofInt]
@[simp]
theorem toInt_bmod_cancel(b : BitVec w) : b.toInt.bmod (2 ^ w) = b.toInt := by
rw [toInt_eq_toNat_bmod, Int.bmod_bmod]
theorem sdiv_ne_intMin_of_ne_intMin {x y : BitVec w} (h : x ≠ intMin w) :
x.sdiv y ≠ intMin w := by
by_cases hw : w = 0
· subst hw
simp [BitVec.eq_nil x] at h
contradiction
simp only [sdiv, udiv_eq, neg_eq]
by_cases hx : x.msb <;> by_cases hy : y.msb
<;> simp only [hx, hy, neg_ne_intMin_inj]
<;> simp only [Bool.not_eq_true] at hx hy
<;> apply ne_intMin_of_lt_of_msb_false (by omega)
<;> rw [msb_udiv]
<;> try simp only [hx, Bool.false_and]
· simp [h, ne_zero_of_msb_true, hx]
· simp [h, ne_zero_of_msb_true, hx]
theorem toInt_eq_neg_toNat_neg_of_msb_true {x : BitVec w} (h : x.msb = true) :
x.toInt = -((-x).toNat) := by
simp only [toInt_eq_msb_cond, h, ↓reduceIte, toNat_neg, Int.ofNat_emod]
norm_cast
rw [Nat.mod_eq_of_lt]
· omega
· have := @BitVec.isLt w x
have ne_zero := ne_zero_of_msb_true h
simp only [ne_eq, toNat_eq, toNat_ofNat, zero_mod] at ne_zero
omega
theorem toInt_eq_neg_toNat_neg_of_nonpos {x : BitVec w} (h : x = 0#w x.msb = true) :
x.toInt = -((-x).toNat) := by
cases h
case inl h' =>
simp [h']
case inr h' =>
simp [toInt_eq_neg_toNat_neg_of_msb_true h']
theorem intMin_udiv_eq_intMin_iff (x : BitVec w) :
intMin w / x = intMin w ↔ x = 1#w := by
by_cases hw : w = 0; subst hw; decide +revert
by_cases hx : x = 1#w; subst hx; simp
have wpos : 0 < w := by omega
have : 0 ≤ (2 ^ (w - 1) / x.toNat) := by simp
have := Nat.two_pow_pos (w - 1)
constructor
· intro h
rw [← toInt_inj, toInt_eq_msb_cond] at h
have : (intMin w / x).msb = false := by simp [msb_udiv, msb_intMin, wpos, hx]
simp [this, wpos, toInt_intMin] at h
omega
· intro h
subst h
simp
theorem intMin_udiv_ne_zero_of_ne_zero {b : BitVec w} (hb : b.msb = false) (hb0 : b ≠ 0#w) :
intMin w / b ≠ 0#w := by
by_cases hw : w = 0; subst hw; decide +revert
have wpos : 0 < w := by omega
simp [toNat_eq] at hb0
have := @Nat.div_eq_zero_iff_lt b.toNat (2 ^ (w-1)) (by omega)
have := toNat_lt_of_msb_false hb
simp [toNat_eq, wpos]
omega
theorem toInt_sdiv_of_ne_or_ne (a b : BitVec w) (h : a ≠ intMin w b ≠ -1#w) :
(a.sdiv b).toInt = a.toInt.tdiv b.toInt := by
by_cases hw0 : w = 0; subst hw0; decide +revert
by_cases hw1 : w = 1; subst hw1; decide +revert
by_cases ha0 : a = 0#w; subst ha0; simp
by_cases hb0 : b = 0#w; subst hb0; simp
by_cases hb1 : b = 1#w; subst hb1; simp [show 1 < w by omega]
have wpos : 0 < w := by omega
have := Nat.two_pow_pos (w - 1)
by_cases hbintMin : b = intMin w
· simp only [ne_eq, Decidable.not_not] at hbintMin
subst hbintMin
have toIntA_lt := @BitVec.toInt_lt w a; norm_cast at toIntA_lt
have le_toIntA := @BitVec.le_toInt w a; norm_cast at le_toIntA
simp only [sdiv_intMin, h, ↓reduceIte, toInt_zero, toInt_intMin, wpos,
Nat.two_pow_pred_mod_two_pow, Int.tdiv_neg]
· by_cases ha_intMin : a = intMin w
· simp only [ha_intMin, ↓reduceIte, show 1 < w by omega, toInt_one, toInt_intMin, wpos,
Nat.two_pow_pred_mod_two_pow, Int.neg_tdiv, Int.neg_neg]
rw [Int.tdiv_self (by omega)]
· by_cases ha_nonneg : 0 ≤ a.toInt
· simp [Int.tdiv_eq_zero_of_lt ha_nonneg (by norm_cast at *), ha_intMin]
· simp only [ne_eq, ← toInt_inj, toInt_intMin, wpos, Nat.two_pow_pred_mod_two_pow] at h
rw [← Int.neg_tdiv, Int.tdiv_eq_zero_of_lt (by omega)]
· simp [ha_intMin]
· simp [wpos, ← toInt_ne, toInt_intMin] at ha_intMin
omega
· by_cases ha : a.msb <;> by_cases hb : b.msb
<;> simp only [not_eq_true] at ha hb
· simp only [sdiv_eq, ha, hb, udiv_eq]
rw [toInt_eq_neg_toNat_neg_of_nonpos (x := a) (by simp [ha]),
toInt_eq_neg_toNat_neg_of_nonpos (x := b) (by simp [hb]),
Int.neg_tdiv_neg, Int.tdiv_eq_ediv_of_nonneg (by omega)]
rw [toInt_eq_toNat_of_msb]
· rfl
· by_cases ha_intMin : a = intMin w
· simp only [ha_intMin, ne_eq, not_true_eq_false, _root_.false_or] at h
simp [msb_udiv, neg_eq_iff_eq_neg, h]
· simp [msb_udiv, ha_intMin, ha]
· have sdiv_toInt_of_msb_true_of_msb_false :
(a.sdiv b).toInt = -((-a).toNat / b.toNat) := by
simp only [sdiv_eq, ha, hb, udiv_eq]
rw [toInt_eq_neg_toNat_neg_of_nonpos]
· rw [neg_neg, toNat_udiv, toNat_neg, Int.ofNat_emod, Int.neg_inj]
norm_cast
· rw [neg_eq_zero_iff]
by_cases h' : -a / b = 0#w
· simp [h']
· by_cases ha_intMin : a = intMin w
· have ry := (intMin_udiv_eq_intMin_iff b).mp
simp only [hb1, imp_false] at ry
simp [msb_udiv, ha_intMin, hb1, ry, intMin_udiv_ne_zero_of_ne_zero, hb, hb0]
· have := @BitVec.ne_intMin_of_lt_of_msb_false w ((-a) / b) wpos (by simp [ha, ha0, ha_intMin])
simp [msb_neg, h', this, ha, ha_intMin]
rw [toInt_eq_toNat_of_msb hb, toInt_eq_neg_toNat_neg_of_msb_true ha, Int.neg_tdiv,
Int.tdiv_eq_ediv_of_nonneg (by omega), sdiv_toInt_of_msb_true_of_msb_false]
· rw [← @BitVec.neg_neg w (a.sdiv b), ← sdiv_neg hbintMin]
have hmb : (-b).msb = false := by simp [hbintMin, hb]
rw [toInt_neg_of_ne_intMin]
· simp [sdiv, ha, hmb]
rw [toInt_udiv_of_msb ha, toInt_eq_toNat_of_msb ha]
rw [toInt_eq_neg_toNat_neg_of_msb_true hb, Int.tdiv_neg, Int.tdiv_eq_ediv_of_nonneg (by omega)]
· apply sdiv_ne_intMin_of_ne_intMin
apply ne_intMin_of_lt_of_msb_false (by omega) ha
· rw [sdiv, Int.tdiv_cases, udiv_eq, neg_eq, if_pos (toInt_nonneg_of_msb_false ha),
if_pos (toInt_nonneg_of_msb_false hb), ha, hb, toInt_udiv_of_msb ha,
toInt_eq_toNat_of_msb ha, toInt_eq_toNat_of_msb hb]
theorem intMin_sdiv_neg_one : (intMin w).sdiv (-1#w) = intMin w := by
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; exact Subsingleton.elim _ _) (fun hw => ?_)
apply BitVec.eq_of_toNat_eq
rw [sdiv]
simp [msb_intMin, hw, negOne_eq_allOnes, msb_allOnes]
have : 2 ≤ 2 ^ w := Nat.pow_one 2 ▸ (Nat.pow_le_pow_iff_right (by omega)).2 (by omega)
rw [Nat.sub_sub_self (by omega), Nat.mod_eq_of_lt, Nat.div_one]
omega
theorem toInt_sdiv (a b : BitVec w) : (a.sdiv b).toInt = (a.toInt.tdiv b.toInt).bmod (2 ^ w) := by
by_cases h : a = intMin w ∧ b = -1#w
· rcases h with ⟨rfl, rfl⟩
rw [BitVec.intMin_sdiv_neg_one]
refine (Nat.eq_zero_or_pos w).elim (by rintro rfl; simp [toInt_of_zero_length]) (fun hw => ?_)
rw [toInt_intMin_of_pos hw, negOne_eq_allOnes, toInt_allOnes, if_pos hw, Int.tdiv_neg,
Int.tdiv_one, Int.neg_neg, Int.bmod_eq_neg (Int.pow_nonneg (by omega))]
conv => lhs; rw [(by omega: w = (w - 1) + 1)]
simp [Nat.pow_succ, Int.natCast_pow, Int.mul_comm]
· rw [← toInt_bmod_cancel]
rw [BitVec.toInt_sdiv_of_ne_or_ne _ _ (by simpa only [Classical.not_and_iff_not_or_not] using h)]
/-! ### Lemmas that use Bitblasting circuits -/
theorem add_sub_comm {x y : BitVec w} : x + y - z = x - z + y := by

View file

@ -391,6 +391,15 @@ theorem getLsbD_ofNat (n : Nat) (x : Nat) (i : Nat) :
simp only [toNat_eq, toNat_ofNat, Nat.zero_mod] at h
rw [Nat.mod_eq_of_lt (by omega)]
@[simp] 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
by_cases h : w = 0
· subst h
simp [BitVec.eq_nil x] at h
have := x.isLt
rw [Nat.mod_eq_of_lt]
omega
@[simp] 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)
@ -647,6 +656,34 @@ theorem eq_zero_or_eq_one (a : BitVec 1) : a = 0#1 a = 1#1 := by
theorem toInt_zero {w : Nat} : (0#w).toInt = 0 := by
simp [BitVec.toInt, show 0 < 2^w by exact Nat.two_pow_pos w]
@[simp]
theorem toNat_one (h : 0 < w) : (1#w : BitVec w).toNat = 1 := by
simp
omega
@[simp]
theorem toInt_one (h : 1 < w) : (1#w : BitVec w).toInt = 1 := by
rw [toInt_eq_toNat_of_msb, toNat_one (by omega)]
· simp
· simp
omega
@[simp] 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, ge_iff_le, decide_eq_true_eq] at h
have := Nat.two_pow_pos (w-1)
exact sub_toNat_mod_cancel_of_toNat (by omega)
theorem toNat_lt_of_msb_false {w : Nat} {x : BitVec w} (h : x.msb = false) : x.toNat < 2 ^ (w - 1) := by
have rt := @msb_eq_decide w x
simp only [ge_iff_le, false_eq_decide_iff, Nat.not_le, h] at rt
omega
theorem le_toNat_of_msb_true {w : Nat} {x : BitVec w} (h : x.msb = true) : 2 ^ (w - 1) ≤ x.toNat := by
have rt := @msb_eq_decide w x
simp only [h, ge_iff_le, true_eq_decide_iff] at rt
omega
/--
`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.
@ -972,7 +1009,7 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
(extractLsb hi lo x).toNat = (x.toNat >>> lo) % 2^(hi-lo+1) := rfl
@[simp] theorem toInt_extractLsb' {s m : Nat} {x : BitVec n} :
(extractLsb' s m x).toInt = ((x.toNat >>> s) : Int).bmod (2 ^ m) := by
(extractLsb' s m x).toInt = ((x.toNat >>> s) : Int).bmod (2 ^ m) := by
simp [extractLsb', toInt_ofNat]
@[simp] theorem toInt_extractLsb {hi lo : Nat} {x : BitVec n} :
@ -1179,6 +1216,11 @@ theorem extractLsb'_eq_zero {x : BitVec w} {start : Nat} :
simp
omega
theorem msb_allOnes (hw : 0 < w) : (allOnes w).msb = true := by
rw [msb_eq_true_iff_two_mul_ge, toNat_allOnes]
have : 2 ≤ 2 ^ w := Nat.pow_one 2 ▸ (Nat.pow_le_pow_iff_right (by omega)).2 (by omega)
omega
/-! ### or -/
@[simp] theorem toNat_or (x y : BitVec v) :
@ -3161,7 +3203,7 @@ theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n ((2^n - y.toNat) + x.toN
simp [toInt_eq_toNat_bmod, @Int.ofNat_sub y.toNat (2 ^ w) (by omega)]
theorem toInt_sub_toInt_lt_twoPow_iff {x y : BitVec w} :
(x.toInt - y.toInt < - 2 ^ (w - 1))
(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]
@ -3177,7 +3219,7 @@ theorem toInt_sub_toInt_lt_twoPow_iff {x y : BitVec w} :
omega
theorem twoPow_le_toInt_sub_toInt_iff {x y : BitVec w} :
(2 ^ (w - 1) ≤ x.toInt - y.toInt)
(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]
@ -3301,6 +3343,14 @@ theorem neg_neg {x : BitVec w} : - - x = x := by
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
@ -3736,7 +3786,8 @@ theorem msb_udiv (x y : BitVec w) :
≤ 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} (h : x.msb = false) (y : BitVec w) :
@[simp]
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]
@ -4610,6 +4661,17 @@ The RHS is zero in case `w = 0` which is modeled by wrapping the expression in `
theorem toNat_intMin : (intMin w).toNat = 2 ^ (w - 1) % 2 ^ w := by
simp [intMin]
@[simp]
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]
omega
· simp [h]
/--
The RHS is zero in case `w = 0` which is modeled by wrapping the expression in `... % 2 ^ w`.
-/
@ -4652,6 +4714,14 @@ theorem neg_intMin {w : Nat} : -intMin w = intMin w := by
· simp only [Nat.not_lt, Nat.le_zero_eq] at h
simp [bitvec_to_nat, h]
@[simp]
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]
@[simp]
theorem abs_intMin {w : Nat} : (intMin w).abs = intMin w := by
simp [BitVec.abs, bitvec_to_nat]

View file

@ -732,6 +732,22 @@ theorem neg_ediv {a b : Int} : (-a) / b = -(a / b) - if b a then 0 else b.si
· have := emod_lt_of_neg a hb
omega
theorem tdiv_cases (n m : Int) : n.tdiv m =
if 0 ≤ n then
if 0 ≤ m then n / m else -(n / (-m))
else
if 0 ≤ m then -((-n) / m) else (-n) / (-m) := by
split <;> rename_i hn
· split <;> rename_i hm
· rw [Int.tdiv_eq_ediv_of_nonneg hn]
· rw [Int.tdiv_eq_ediv_of_nonneg hn]
simp
· split <;> rename_i hm
· rw [Int.tdiv_eq_ediv, Int.neg_ediv]
simp [hn, Int.neg_sub, Int.add_comm]
· rw [Int.tdiv_eq_ediv, Int.neg_ediv, Int.ediv_neg]
simp [hn, Int.sub_eq_add_neg, apply_ite Neg.neg]
theorem neg_emod {a b : Int} : (-a) % b = if b a then 0 else b.natAbs - (a % b) := by
rw [emod_def, emod_def, neg_ediv, Int.mul_sub, Int.mul_neg]
split <;> rename_i h
@ -2269,6 +2285,14 @@ theorem bmod_lt {x : Int} {m : Nat} (h : 0 < m) : bmod x m < (m + 1) / 2 := by
theorem bmod_eq_emod_of_lt {x : Int} {m : Nat} (hx : x % m < (m + 1) / 2) : bmod x m = x % m := by
simp [bmod, hx]
theorem bmod_eq_neg {n : Nat} {m : Int} (hm : 0 ≤ m) (hn : n = 2 * m) : m.bmod n = -m := by
by_cases h : m = 0
· subst h; simp
· rw [Int.bmod_def, hn, if_neg]
· rw [Int.emod_eq_of_lt hm] <;> omega
· simp only [Int.not_lt]
rw [Int.emod_eq_of_lt hm] <;> omega
theorem bmod_le {x : Int} {m : Nat} (h : 0 < m) : bmod x m ≤ (m - 1) / 2 := by
refine lt_add_one_iff.mp ?_
calc

View file

@ -17,6 +17,16 @@ protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
rw [Int.mul_comm, Int.pow_succ]
protected theorem pow_pos {n : Int} {m : Nat} : 0 < n → 0 < n ^ m := by
induction m with
| zero => simp
| succ m ih => exact fun h => Int.mul_pos (ih h) h
protected theorem pow_nonneg {n : Int} {m : Nat} : 0 ≤ n → 0 ≤ n ^ m := by
induction m with
| zero => simp
| succ m ih => exact fun h => Int.mul_nonneg (ih h) h
@[deprecated Nat.pow_le_pow_left (since := "2025-02-17")]
abbrev pow_le_pow_of_le_left := @Nat.pow_le_pow_left