Zulip Chat Archive
Stream: general
Topic: Arithmetic issue when upgrading to v4.24
Adrian Palacios (Oct 15 2025 at 17:35):
I'm in the process of upgrading our Lean project from v4.23.0 to v4.24.0. The last error I need to solve is an omega case as follows:
cases h₁ : decide ((Int.ofNat (18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616)) % Int.ofNat (2 ^ 64) < (Int.ofNat (2 ^ 64) + 1) / 2) == true
case false =>
simp only [beq_eq_false_iff_ne, ne_eq] at h₁
simp only [decide_eq_true_eq, Int.ofNat_eq_coe] at h₁
omega
where the omega tactic yields:
omega could not prove the goal:
a possible counterexample may satisfy the constraints
0 ≤ c - 18446744073709551616*d ≤ 18446744073709551615
1 ≤ 18446744073709551616*b + c - 18446744073709551616*d ≤ 9223372036854775808
2 ≤ a ≤ 9223372036854775807
where
a := ↑n₂
b := ↑(18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616) / 18446744073709551616
c := ↑9223372036854775808 / ↑n₂
d := ↑(9223372036854775808 / n₂) / 18446744073709551616
The tactic state before omega looks like this:
bv₁ bv₂ : BitVec 64
n₂ : Nat
h₀ : 2 * n₂ < 2 ^ 64
h : 1 < n₂
h₁ : ¬↑(18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616) % ↑(2 ^ 64) < (↑(2 ^ 64) + 1) / 2
⊢ (if
↑↑(Fin.Internal.ofNat 18446744073709551616 ⋯
(18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616)) %
↑18446744073709551616 <
(↑18446744073709551616 + 1) / 2 then
↑↑(Fin.Internal.ofNat 18446744073709551616 ⋯
(18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616)) %
↑18446744073709551616
else
↑↑(Fin.Internal.ofNat 18446744073709551616 ⋯
(18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616)) %
↑18446744073709551616 -
↑18446744073709551616) <
9223372036854775807
I know that using simp only [Fin.Internal.ofNat] or simp solves the problem, but neither are acceptable to me (Fin.Internal.ofNat is internal, and we stick to simp only in our project). What's a clean way to solve this error? A few more things I've tried:
- using
norm_castbut it's hitting a recursion error - some combinations of conversions but I'm not familiar with
Finnumbers so they don't simplify in most cases
Eric Wieser (Oct 15 2025 at 17:42):
Can you make a #mwe?
Kevin Buzzard (Oct 15 2025 at 17:57):
Does grind happen to do it?
Robin Arnez (Oct 15 2025 at 18:06):
I think the first problem is how Fin.Internal.ofNat even appeared in the first place; did you unfold BitVec.ofNat? In that case, try to instead use its API (e.g. BitVec.toFin_ofNat, BitVec.toNat_ofNat)
Adrian Palacios (Oct 15 2025 at 18:53):
@Eric Wieser I don't know how to make one. This is embedded in a long theorem proof and I assumed we were missing something obvious in this particular case.
@Kevin Buzzard grind doesn't work neither:
`grind` failed
case grind.1.1
bv₁ bv₂ : BitVec 64
n₂ : Nat
h₀ : 2 * n₂ ≤ 18446744073709551615
h : 2 ≤ n₂
h₁ : -1 * (↑(18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616) % 18446744073709551616) +
9223372036854775808 ≤
0
h_1 : (-1 *
if
↑↑(Fin.Internal.ofNat 18446744073709551616 ⋯
(18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616)) %
18446744073709551616 +
-9223372036854775807 ≤
0 then
↑↑(Fin.Internal.ofNat 18446744073709551616 ⋯
(18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616)) %
18446744073709551616
else
↑↑(Fin.Internal.ofNat 18446744073709551616 ⋯
(18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616)) %
18446744073709551616 +
-18446744073709551616) +
9223372036854775807 ≤
0
h_2 : ↑↑(Fin.Internal.ofNat 18446744073709551616 ⋯ (18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616)) %
18446744073709551616 +
-9223372036854775807 ≤
0
h_3 : 9223372036854775808 / ↑n₂ % 18446744073709551616 + -18446744073709551616 ≤ 0
⊢ False
Kevin Buzzard (Oct 15 2025 at 18:56):
Try using extract_goal for a #mwe .
Adrian Palacios (Oct 15 2025 at 19:20):
@Kevin Buzzard my IDE says unknown tactic when using extract_goal.
@Robin Arnez we do use BitVec.ofNat earlier in the proof so I'm trying using BitVec APIs in a few places to see if it makes a difference (no luck so far). Here's this entire case before the omega:
case inr h₁ =>
subst h₁
simp only [BitVec.neg, Int64.MAX, BitVec.udiv_eq, BitVec.udiv_def]
simp only [Nat.reducePow, toNat_intMin, Nat.add_one_sub_one, Nat.reduceMod, toNat_ofNat,
gt_iff_lt]
simp only [BitVec.ofNat, Nat.reducePow, toInt_ofFin]
simp only [BitVec.msb_eq_false_iff_two_mul_lt] at h₀
replace h : 1 < bv₂.toNat := by
simp only [BitVec.toInt, h₀, ↓reduceIte] at h
simp only [← Int.ofNat_lt, Int.cast_ofNat_Int, h]
generalize bv₂.toNat = n₂ at *
simp only [Int.bmod_def]
cases h₁ : decide ((Int.ofNat (18446744073709551616 - 9223372036854775808 / n₂ % 18446744073709551616)) % Int.ofNat (2 ^ 64) < (Int.ofNat (2 ^ 64) + 1) / 2) == true
case false =>
simp only [beq_eq_false_iff_ne, ne_eq] at h₁
simp only [decide_eq_true_eq, Int.ofNat_eq_coe] at h₁
Robin Arnez (Oct 15 2025 at 19:21):
can you tell me the goal before the simp only [BitVec.ofNat, Nat.reducePow, toInt_ofFin]
Kevin Buzzard (Oct 15 2025 at 19:47):
extract_goal is a Mathlib tactic: import Mathlib or import Mathlib.Tactic.ExtractGoal. It can be very useful in situations like this; maybe it should be upstreamed?
Adrian Palacios (Oct 15 2025 at 19:49):
@Robin Arnez yes, it's this:
bv₁ bv₂ : BitVec 64
h : 1 < bv₂.toInt
h₀ : bv₂.msb = false
⊢ (BitVec.ofNat 64 (18446744073709551616 - 9223372036854775808 / bv₂.toNat % 18446744073709551616)).toInt <
9223372036854775807
Robin Arnez (Oct 15 2025 at 19:52):
Try simp only [BitVec.toInt_ofNat', Nat.reducePow] instead of that one line
Adrian Palacios (Oct 15 2025 at 21:13):
@Robin Arnez that worked, thanks so much!
@Kevin Buzzard oh, that makes sense. We don't depend on Mathlib in our project (the Lean formalization of Cedar), but extract_goal sounds generic enough that could be upstreamed.
Last updated: Dec 20 2025 at 21:32 UTC