Skip to content

Commit

Permalink
chore: bump Std, changes for leanprover-community/batteries#366 (#8700)
Browse files Browse the repository at this point in the history
Notably leanprover-community/batteries#366 changed the definition of `testBit` (to something equivalent) when upstreaming it, which broke a handful of proofs.

Other conflicting changes in Std, resolved for now by priming the mathlib name:

* `Std.BitVec.adc`: the type was changed from `BitVec (n + 1)` to `Bool × BitVec w`
* `Nat.mul_add_mod`: the type was changed from `(a * b + c) % b = c % b` to `(b * a + c) % b = c % b`

[Zulip thread](https://leanprover.zulipchat.com/#narrow/stream/348111-std4/topic/Mathlib.20bump.20for.20.22Operations.20for.20bit.20representation.20of.20.2E.2E.2E.22/near/404801443)



Co-authored-by: Scott Morrison <scott.morrison@gmail.com>
Co-authored-by: Alex Keizer <alex@keizer.dev>
Co-authored-by: Eric Wieser <wieser.eric@gmail.com>
  • Loading branch information
4 people committed Nov 30, 2023
1 parent d3855b8 commit 1c6d0ce
Show file tree
Hide file tree
Showing 11 changed files with 69 additions and 150 deletions.
8 changes: 5 additions & 3 deletions Mathlib/Data/BitVec/Defs.lean
Expand Up @@ -58,9 +58,11 @@ namespace Std.BitVec
/-! ### Arithmetic operators -/

#align bitvec.neg Std.BitVec.neg
/-- Add with carry (no overflow) -/
def adc {n} (x y : BitVec n) (c : Bool) : BitVec (n+1) :=
ofFin (x.toNat + y.toNat + c.toNat)
/-- Add with carry (no overflow)
See also `Std.BitVec.adc`, which stores the carry bit separately. -/
def adc' {n} (x y : BitVec n) (c : Bool) : BitVec (n+1) :=
let a := x.adc y c; .cons a.1 a.2
#align bitvec.adc Std.BitVec.adc

#align bitvec.add Std.BitVec.add
Expand Down
37 changes: 9 additions & 28 deletions Mathlib/Data/BitVec/Lemmas.lean
Expand Up @@ -31,20 +31,10 @@ theorem toNat_inj {x y : BitVec w} : x.toNat = y.toNat ↔ x = y :=
theorem toNat_lt_toNat {x y : BitVec w} : x.toNat < y.toNat ↔ x < y :=
Iff.rfl

@[simp]
lemma ofNat_eq_mod_two_pow (n : Nat) : (BitVec.ofNat w n).toNat = n % 2^w := rfl

lemma toNat_ofNat {m} (h : m < 2^w) : (BitVec.ofNat w m).toNat = m := Fin.val_cast_of_lt h

@[simp]
lemma toNat_ofFin (x : Fin (2^w)) : (ofFin x).toNat = x.val := rfl
attribute [simp] toNat_ofNat toNat_ofFin

theorem toNat_append (msbs : BitVec w) (lsbs : BitVec v) :
(msbs ++ lsbs).toNat = msbs.toNat <<< v ||| lsbs.toNat := by
rcases msbs with ⟨msbs, hm⟩
rcases lsbs with ⟨lsbs, hl⟩
simp only [HAppend.hAppend, append, toNat_ofFin]
rw [toNat_ofNat (Nat.add_comm w v ▸ append_lt hl hm)]
lemma toNat_ofNat_of_lt {m} (h : m < 2^w) : (BitVec.ofNat w m).toNat = m := by
simp only [toNat_ofNat, mod_eq_of_lt h]

#noalign bitvec.bits_to_nat_to_bool

Expand All @@ -60,14 +50,9 @@ lemma extractLsb_eq {w : ℕ} (hi lo : ℕ) (a : BitVec w) :

theorem toNat_extractLsb' {i j} {x : BitVec w} :
(extractLsb' i j x).toNat = x.toNat / 2 ^ i % (2 ^ j) := by
simp only [extractLsb', ofNat_eq_mod_two_pow, shiftRight_eq_div_pow]

theorem getLsb_eq_testBit {i} {x : BitVec w} : getLsb x i = x.toNat.testBit i := by
simp only [getLsb, Nat.shiftLeft_eq, one_mul, Nat.and_two_pow]
cases' testBit (BitVec.toNat x) i
<;> simp [pos_iff_ne_zero.mp (two_pow_pos i)]
simp only [extractLsb', toNat_ofNat, shiftRight_eq_div_pow]

theorem ofFin_val {n : ℕ} (i : Fin <| 2 ^ n) : (ofFin i).toNat = i.val := by
theorem ofFin_val {n : ℕ} (i : Fin <| 2 ^ n) : (ofFin i).toNat = i.val :=
rfl
#align bitvec.of_fin_val Std.BitVec.ofFin_val

Expand All @@ -91,16 +76,12 @@ theorem decide_addLsb_mod_two {x b} : decide (addLsb x b % 2 = 1) = b := by
simp [addLsb]
#align bitvec.to_bool_add_lsb_mod_two Std.BitVec.decide_addLsb_mod_two

@[simp]
lemma ofNat_toNat (x : BitVec w) : BitVec.ofNat w x.toNat = x := by
rcases x with ⟨x⟩
simp [BitVec.ofNat]
apply Fin.cast_val_eq_self x
#align bitvec.of_nat_to_nat Std.BitVec.ofNat_toNat
lemma ofNat_toNat' (x : BitVec w) : (x.toNat)#w = x := by
rw [ofNat_toNat, truncate_eq]

lemma ofNat_toNat' (x : BitVec w) (h : w = v):
lemma ofNat_toNat_of_eq (x : BitVec w) (h : w = v):
BitVec.ofNat v x.toNat = x.cast h := by
cases h; rw [ofNat_toNat, cast_eq]
cases h; rw [ofNat_toNat', cast_eq]

theorem toFin_val {n : ℕ} (v : BitVec n) : (toFin v : ℕ) = v.toNat := by
rfl
Expand Down
16 changes: 0 additions & 16 deletions Mathlib/Data/Bool/Basic.lean
Expand Up @@ -47,14 +47,7 @@ theorem of_decide_iff {p : Prop} [Decidable p] : decide p ↔ p :=
coe_decide p
#align bool.of_to_bool_iff Bool.of_decide_iff

@[simp]
theorem true_eq_decide_iff {p : Prop} [Decidable p] : true = decide p ↔ p :=
eq_comm.trans of_decide_iff
#align bool.tt_eq_to_bool_iff Bool.true_eq_decide_iff

@[simp]
theorem false_eq_decide_iff {p : Prop} [Decidable p] : false = decide p ↔ ¬p :=
eq_comm.trans (decide_false_iff _)
#align bool.ff_eq_to_bool_iff Bool.false_eq_decide_iff

theorem decide_not (p : Prop) [Decidable p] : (decide ¬p) = !(decide p) := by
Expand Down Expand Up @@ -308,22 +301,13 @@ theorem right_le_or : ∀ x y : Bool, y ≤ (x || y) := by decide
theorem or_le : ∀ {x y z}, x ≤ z → y ≤ z → (x || y) ≤ z := by decide
#align bool.bor_le Bool.or_le

/-- convert a `Bool` to a `ℕ`, `false -> 0`, `true -> 1` -/
def toNat (b : Bool) : Nat :=
cond b 1 0
#align bool.to_nat Bool.toNat

lemma toNat_le_one (b : Bool) : b.toNat ≤ 1 := by
cases b <;> decide

/-- convert a `ℕ` to a `Bool`, `0 -> false`, everything else -> `true` -/
def ofNat (n : Nat) : Bool :=
decide (n ≠ 0)
#align bool.of_nat Bool.ofNat

@[simp] lemma toNat_true : toNat true = 1 := rfl
@[simp] lemma toNat_false : toNat false = 0 := rfl

@[simp] lemma toNat_beq_zero (b : Bool) : (b.toNat == 0) = !b := by cases b <;> rfl
@[simp] lemma toNat_bne_zero (b : Bool) : (b.toNat != 0) = b := by simp [bne]
@[simp] lemma toNat_beq_one (b : Bool) : (b.toNat == 1) = b := by cases b <;> rfl
Expand Down
10 changes: 5 additions & 5 deletions Mathlib/Data/Int/Bitwise.lean
Expand Up @@ -407,15 +407,15 @@ attribute [local simp] Int.zero_div

theorem shiftLeft_add : ∀ (m : ℤ) (n : ℕ) (k : ℤ), m <<< (n + k) = (m <<< (n : ℤ)) <<< k
| (m : ℕ), n, (k : ℕ) =>
congr_arg ofNat (by simp [Nat.pow_add, mul_assoc])
congr_arg ofNat (by simp [Nat.shiftLeft_eq, Nat.pow_add, mul_assoc])
| -[m+1], n, (k : ℕ) => congr_arg negSucc (Nat.shiftLeft'_add _ _ _ _)
| (m : ℕ), n, -[k+1] =>
subNatNat_elim n k.succ (fun n k i => (↑m) <<< i = (Nat.shiftLeft' false m n) >>> k)
(fun (i n : ℕ) =>
by dsimp; simp [- Nat.shiftLeft_eq, ← Nat.shiftLeft_sub _ , add_tsub_cancel_left])
by dsimp; simp [← Nat.shiftLeft_sub _ , add_tsub_cancel_left])
fun i n => by
dsimp
simp [- Nat.shiftLeft_eq, Nat.shiftLeft_zero, Nat.shiftRight_add, ← Nat.shiftLeft_sub]
simp [Nat.shiftLeft_zero, Nat.shiftRight_add, ← Nat.shiftLeft_sub]
rfl
| -[m+1], n, -[k+1] =>
subNatNat_elim n k.succ
Expand All @@ -433,7 +433,7 @@ theorem shiftLeft_sub (m : ℤ) (n : ℕ) (k : ℤ) : m <<< (n - k) = (m <<< (n
#align int.shiftl_sub Int.shiftLeft_sub

theorem shiftLeft_eq_mul_pow : ∀ (m : ℤ) (n : ℕ), m <<< (n : ℤ) = m * (2 ^ n : ℕ)
| (m : ℕ), _ => congr_arg ((↑) : ℕ → ℤ) (by simp)
| (m : ℕ), _ => congr_arg ((↑) : ℕ → ℤ) (by simp [Nat.shiftLeft_eq])
| -[_+1], _ => @congr_arg ℕ ℤ _ _ (fun i => -i) (Nat.shiftLeft'_tt_eq_mul_pow _ _)
#align int.shiftl_eq_mul_pow Int.shiftLeft_eq_mul_pow

Expand All @@ -445,7 +445,7 @@ theorem shiftRight_eq_div_pow : ∀ (m : ℤ) (n : ℕ), m >>> (n : ℤ) = m / (
#align int.shiftr_eq_div_pow Int.shiftRight_eq_div_pow

theorem one_shiftLeft (n : ℕ) : 1 <<< (n : ℤ) = (2 ^ n : ℕ) :=
congr_arg ((↑) : ℕ → ℤ) (by simp)
congr_arg ((↑) : ℕ → ℤ) (by simp [Nat.shiftLeft_eq])
#align int.one_shiftl Int.one_shiftLeft

@[simp]
Expand Down
7 changes: 4 additions & 3 deletions Mathlib/Data/Nat/Basic.lean
Expand Up @@ -838,11 +838,12 @@ theorem lt_mul_div_succ (m : ℕ) {n : ℕ} (n0 : 0 < n) : m < n * (m / n + 1) :
exact lt_succ_self _
#align nat.lt_mul_div_succ Nat.lt_mul_div_succ

theorem mul_add_mod (a b c : ℕ) : (a * b + c) % b = c % b := by simp [Nat.add_mod]
#align nat.mul_add_mod Nat.mul_add_mod
-- TODO: Std4 claimed this name but flipped the order of multiplication
theorem mul_add_mod' (a b c : ℕ) : (a * b + c) % b = c % b := by rw [mul_comm, Nat.mul_add_mod]
#align nat.mul_add_mod Nat.mul_add_mod'

theorem mul_add_mod_of_lt {a b c : ℕ} (h : c < b) : (a * b + c) % b = c := by
rw [Nat.mul_add_mod, Nat.mod_eq_of_lt h]
rw [Nat.mul_add_mod', Nat.mod_eq_of_lt h]
#align nat.mul_add_mod_of_lt Nat.mul_add_mod_of_lt

theorem pred_eq_self_iff {n : ℕ} : n.pred = n ↔ n = 0 := by
Expand Down
71 changes: 17 additions & 54 deletions Mathlib/Data/Nat/Bitwise.lean
Expand Up @@ -125,15 +125,7 @@ theorem xor_bit : ∀ a m b n, bit a m ^^^ bit b n = bit (bne a b) (m ^^^ n) :=
bitwise_bit
#align nat.lxor_bit Nat.xor_bit

@[simp]
theorem testBit_bitwise {f : Bool → Bool → Bool} (h : f false false = false) (m n k) :
testBit (bitwise f m n) k = f (testBit m k) (testBit n k) := by
induction' k with k ih generalizing m n
<;> cases' m using bitCasesOn with a m
<;> cases' n using bitCasesOn with b n
<;> rw [bitwise_bit h]
· simp [testBit_zero]
· simp [testBit_succ, ih]
attribute [simp] Nat.testBit_bitwise
#align nat.test_bit_bitwise Nat.testBit_bitwise

@[simp]
Expand All @@ -151,9 +143,7 @@ theorem testBit_ldiff : ∀ m n k, testBit (ldiff m n) k = (testBit m k && not (
testBit_bitwise rfl
#align nat.test_bit_ldiff Nat.testBit_ldiff

@[simp]
theorem testBit_xor : ∀ m n k, testBit (m ^^^ n) k = bne (testBit m k) (testBit n k) :=
testBit_bitwise rfl
attribute [simp] testBit_xor
#align nat.test_bit_lxor Nat.testBit_xor

end
Expand Down Expand Up @@ -216,31 +206,19 @@ theorem zero_of_testBit_eq_false {n : ℕ} (h : ∀ i, testBit n i = false) : n
theorem testBit_eq_false_of_lt {n i} (h : n < 2 ^ i) : n.testBit i = false := by
simp [testBit, shiftRight_eq_div_pow, Nat.div_eq_of_lt h]

@[simp]
theorem zero_testBit (i : ℕ) : testBit 0 i = false := by
simp only [testBit, zero_shiftRight, bodd_zero]
#align nat.zero_test_bit Nat.zero_testBit

/-- The ith bit is the ith element of `n.bits`. -/
theorem testBit_eq_inth (n i : ℕ) : n.testBit i = n.bits.getI i := by
induction' i with i ih generalizing n
· simp [testBit, bodd_eq_bits_head, List.getI_zero_eq_headI]
· simp only [testBit, zero_eq, shiftRight_zero, and_one_is_mod, mod_two_of_bodd,
bodd_eq_bits_head, List.getI_zero_eq_headI]
cases List.headI (bits n) <;> rfl
conv_lhs => rw [← bit_decomp n]
rw [testBit_succ, ih n.div2, div2_bits_eq_tail]
cases n.bits <;> simp
#align nat.test_bit_eq_inth Nat.testBit_eq_inth

/-- Bitwise extensionality: Two numbers agree if they agree at every bit position. -/
theorem eq_of_testBit_eq {n m : ℕ} (h : ∀ i, testBit n i = testBit m i) : n = m := by
induction' n using Nat.binaryRec with b n hn generalizing m
· simp only [zero_testBit] at h
exact (zero_of_testBit_eq_false fun i => (h i).symm).symm
induction' m using Nat.binaryRec with b' m
· simp only [zero_testBit] at h
exact zero_of_testBit_eq_false h
suffices h' : n = m by
rw [h', show b = b' by simpa using h 0]
exact hn fun i => by convert h (i + 1) using 1 <;> rw [testBit_succ]
#align nat.eq_of_test_bit_eq Nat.eq_of_testBit_eq

theorem exists_most_significant_bit {n : ℕ} (h : n ≠ 0) :
Expand Down Expand Up @@ -294,20 +272,20 @@ theorem lt_of_testBit {n m : ℕ} (i : ℕ) (hn : testBit n i = false) (hm : tes

@[simp]
theorem testBit_two_pow_self (n : ℕ) : testBit (2 ^ n) n = true := by
rw [testBit, shiftRight_eq_div_pow, Nat.div_self (pow_pos (α := ℕ) zero_lt_two n), bodd_one]
rw [testBit, shiftRight_eq_div_pow, Nat.div_self (pow_pos (α := ℕ) zero_lt_two n)]
simp
#align nat.test_bit_two_pow_self Nat.testBit_two_pow_self

theorem testBit_two_pow_of_ne {n m : ℕ} (hm : n ≠ m) : testBit (2 ^ n) m = false := by
rw [testBit, shiftRight_eq_div_pow]
cases' hm.lt_or_lt with hm hm
· rw [Nat.div_eq_of_lt, bodd_zero]
exact Nat.pow_lt_pow_of_lt_right one_lt_two hm
· rw [Nat.div_eq_of_lt]
· simp
· exact Nat.pow_lt_pow_of_lt_right one_lt_two hm
· rw [pow_div hm.le zero_lt_two, ← tsub_add_cancel_of_le (succ_le_of_lt <| tsub_pos_of_lt hm)]
-- Porting note: XXX why does this make it work?
rw [(rfl : succ 0 = 1)]
simp only [ge_iff_le, tsub_le_iff_right, pow_succ, bodd_mul,
Bool.and_eq_false_eq_eq_false_or_eq_false, or_true]
exact Or.inr rfl
simp [pow_succ, and_one_is_mod, mul_mod_left]
#align nat.test_bit_two_pow_of_ne Nat.testBit_two_pow_of_ne

theorem testBit_two_pow (n m : ℕ) : testBit (2 ^ n) m = (n = m) := by
Expand Down Expand Up @@ -371,25 +349,10 @@ theorem zero_xor (n : ℕ) : 0 ^^^ n = n := by simp [HXor.hXor, Xor.xor, xor]
theorem xor_zero (n : ℕ) : n ^^^ 0 = n := by simp [HXor.hXor, Xor.xor, xor]
#align nat.lxor_zero Nat.xor_zero

@[simp]
theorem zero_land (n : ℕ) : 0 &&& n = 0 := by
simp only [HAnd.hAnd, AndOp.and, land, bitwise_zero_left, ite_false, Bool.and_true];
#align nat.zero_land Nat.zero_land

@[simp]
theorem land_zero (n : ℕ) : n &&& 0 = 0 := by
simp only [HAnd.hAnd, AndOp.and, land, bitwise_zero_right, ite_false, Bool.and_false]
#align nat.land_zero Nat.land_zero

@[simp]
theorem zero_lor (n : ℕ) : 0 ||| n = n := by
simp only [HOr.hOr, OrOp.or, lor, bitwise_zero_left, ite_true, Bool.or_true]
#align nat.zero_lor Nat.zero_lor

@[simp]
theorem lor_zero (n : ℕ) : n ||| 0 = n := by
simp only [HOr.hOr, OrOp.or, lor, bitwise_zero_right, ite_true, Bool.or_false]
#align nat.lor_zero Nat.lor_zero
#align nat.zero_land Nat.zero_and
#align nat.land_zero Nat.and_zero
#align nat.zero_lor Nat.zero_or
#align nat.lor_zero Nat.or_zero


/-- Proving associativity of bitwise operations in general essentially boils down to a huge case
Expand Down Expand Up @@ -487,11 +450,11 @@ theorem xor_trichotomy {a b c : ℕ} (h : a ≠ b ^^^ c) :
all_goals
exact lt_of_testBit i (by
-- Porting note: this was originally `simp [h, hi]`
rw [Nat.testBit_xor, h, Bool.bne_eq_xor, Bool.true_xor,hi]
rw [Nat.testBit_xor, h, Bool.xor, Bool.true_xor, hi]
rfl
) h fun j hj => by
-- Porting note: this was originally `simp [hi' _ hj]`
rw [Nat.testBit_xor, hi' _ hj, Bool.bne_eq_xor, Bool.xor_false, eq_self_iff_true]
rw [Nat.testBit_xor, hi' _ hj, Bool.xor, Bool.xor_false, eq_self_iff_true]
trivial
#align nat.lxor_trichotomy Nat.xor_trichotomy

Expand Down
5 changes: 0 additions & 5 deletions Mathlib/Data/Nat/Order/Lemmas.lean
Expand Up @@ -223,11 +223,6 @@ theorem le_of_lt_add_of_dvd (h : a < b + n) : n ∣ a → n ∣ b → a ≤ b :=
exact mul_le_mul_left' (lt_succ_iff.1 <| lt_of_mul_lt_mul_left h bot_le) _
#align nat.le_of_lt_add_of_dvd Nat.le_of_lt_add_of_dvd

@[simp]
theorem mod_div_self (m n : ℕ) : m % n / n = 0 := by
cases n
· exact (m % 0).div_zero
· case succ n => exact Nat.div_eq_of_lt (m.mod_lt n.succ_pos)
#align nat.mod_div_self Nat.mod_div_self

/-- `n` is not divisible by `a` iff it is between `a * k` and `a * (k + 1)` for some `k`. -/
Expand Down
5 changes: 1 addition & 4 deletions Mathlib/Data/Nat/Size.lean
Expand Up @@ -32,10 +32,7 @@ theorem shiftLeft'_tt_eq_mul_pow (m) : ∀ n, shiftLeft' true m n + 1 = (m + 1)
end

#align nat.one_shiftl Nat.one_shiftLeft

theorem zero_shiftLeft (n) : 0 <<< n = 0 := by simp
#align nat.zero_shiftl Nat.zero_shiftLeft

#align nat.shiftr_eq_div_pow Nat.shiftRight_eq_div_pow

theorem shiftLeft'_ne_zero_left (b) {m} (h : m ≠ 0) (n) : shiftLeft' b m n ≠ 0 := by
Expand Down Expand Up @@ -118,7 +115,7 @@ theorem lt_size_self (n : ℕ) : n < 2 ^ size n := by
by_cases h : bit b n = 0
· apply this h
rw [size_bit h, shiftLeft_succ, shiftLeft_eq, one_mul, ← bit0_val]
exact bit_lt_bit0 _ (by simpa [shiftRight_eq_div_pow] using IH)
exact bit_lt_bit0 _ (by simpa [shiftLeft_eq, shiftRight_eq_div_pow] using IH)
#align nat.lt_size_self Nat.lt_size_self

theorem size_le {m n : ℕ} : size m ≤ n ↔ m < 2 ^ n :=
Expand Down
29 changes: 10 additions & 19 deletions Mathlib/Data/Num/Lemmas.lean
Expand Up @@ -970,28 +970,19 @@ theorem castNum_shiftRight (m : Num) (n : Nat) : ↑(m >>> n) = (m : ℕ) >>> (n
@[simp]
theorem castNum_testBit (m n) : testBit m n = Nat.testBit m n := by
-- Porting note: `unfold` → `dsimp only`
cases m <;> dsimp only [testBit, Nat.testBit]
cases m <;> dsimp only [testBit]
case zero =>
change false = Nat.bodd (0 >>> n)
rw [Nat.zero_shiftRight]
rfl
rw [show (Num.zero : Nat) = 0 from rfl, Nat.zero_testBit]
case pos m =>
induction' n with n IH generalizing m <;> cases' m with m m <;> dsimp only [PosNum.testBit]
rw [cast_pos]
induction' n with n IH generalizing m <;> cases' m with m m
<;> dsimp only [PosNum.testBit, Nat.zero_eq]
· rfl
· exact (Nat.bodd_bit _ _).symm
· exact (Nat.bodd_bit _ _).symm
· change false = Nat.bodd (1 >>> (n + 1))
rw [add_comm, Nat.shiftRight_add]
change false = Nat.bodd (0 >>> n)
rw [Nat.zero_shiftRight]; rfl
· change PosNum.testBit m n = Nat.bodd ((Nat.bit true m) >>> (n + 1))
rw [add_comm, Nat.shiftRight_add]
simp only [Nat.shiftRight_succ, Nat.shiftRight_zero, ← Nat.div2_val, Nat.div2_bit]
apply IH
· change PosNum.testBit m n = Nat.bodd ((Nat.bit false m) >>> (n + 1))
rw [add_comm, Nat.shiftRight_add]
simp only [Nat.shiftRight_succ, Nat.shiftRight_zero, ← Nat.div2_val, Nat.div2_bit]
apply IH
· rw [PosNum.cast_bit1, ← Nat.bit_true, Nat.testBit_zero]
· rw [PosNum.cast_bit0, ← Nat.bit_false, Nat.testBit_zero]
· rw [PosNum.cast_one', ← bit1_zero, ← Nat.bit_true, Nat.testBit_succ, Nat.zero_testBit]
· rw [PosNum.cast_bit1, ← Nat.bit_true, Nat.testBit_succ, IH]
· rw [PosNum.cast_bit0, ← Nat.bit_false, Nat.testBit_succ, IH]
#align num.test_bit_to_nat Num.castNum_testBit

end Num
Expand Down

0 comments on commit 1c6d0ce

Please sign in to comment.