Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
YaoJGalteland
left a comment
There was a problem hiding this comment.
The constant overflow issue fix is correct and the tests are solid. The main concerns is: Exp still wasting 14 multiplications for trivial constant exponents. For example, Exp(x, Zero()) and Exp(x, One()) has no fast paths.
Clauded some analysis (see below), looks pretty promising. Does that logic make sense to you?
The current implementation uses a fixed 4-bit window over all 64 bits, always:
| Component | Count |
|---|---|
Table building (table[2..15]) |
14 MulMod |
| Squarings (16 windows × 4) | 64 Mul |
| Table-lookup multiplications (16 windows × 1) | 16 Mul |
| Total | 94 |
This is constant regardless of the exponent value.
Cost: (bitLen(exp) − 1) squarings + (popcount(exp) − 1) multiplications.
Cost comparison (Goldilocks, 64-bit field)
| Exponent | Before | After (sq + mul) | Savings | Ratio |
|---|---|---|---|---|
exp = 0 |
0 | 0 (0 + 0) | 0 | — |
exp = 1 |
94 | 0 (0 + 0) | 94 | ∞ |
exp = 2 |
94 | 1 (1 + 0) | 93 | 94× |
exp = 3 |
94 | 2 (1 + 1) | 92 | 47× |
exp = 7 |
94 | 4 (2 + 2) | 90 | 23.5× |
exp = 255 |
94 | 14 (7 + 7) | 80 | 6.7× |
exp = 2³² (Frobenius) |
94 | 32 (32 + 0) | 62 | 2.9× |
exp = 2⁶³ |
94 | 63 (63 + 0) | 31 | 1.5× |
exp = (p−1)/2 (Legendre) |
94 | 93 (62 + 31) | 1 | ~1× |
exp = p−1 (dense) |
94 | 94 (63 + 31) | 0 | 1× |
The worst case for binary s-a-m is a maximally dense exponent (p−1, all bits
set) where it ties with the windowed method. The windowed method's advantage is
for variable exponents, where it amortizes multiplications across 4-bit
windows. For constant exponents that advantage is lost since zero windows are
known at compile time.
The practically important cases (Frobenius, Legendre symbol, small exponents)
all fall in the 2×–∞ improvement range.
Yup, good observation. Will implement in separate PR (ExpConst and ExpBits) |
Description
Issue found by Diligence fuzzer. Thanks @wuestholz for reporting. Agent summary of the issue (and human-confirmed independently):
Implemented regression tests for particular case, also added constant fast path handling to other methods as well.
Type of change
How has this been tested?
Added regression tests and constant path tests.
Checklist:
golangci-lintdoes not output errors locallyNote
Medium Risk
Touches core non-native arithmetic paths (
Mul*,Div,Inverse,Sqrt) and changes when hints/deferred checks are emitted, so incorrect constant detection/mod arithmetic could alter constraint generation, though changes are gated to compile-time constants and covered by new regressions.Overview
Fixes a small-field emulation invariant where operations could return constant-valued elements with non-zero
overflow, later triggering a panic inReduce(e.g., duringExp(x, 1)compilation).Adds compile-time constant fast paths for
Mul/MulNoReduce(includingmulModwhenp == nil) plusDiv,Inverse, andSqrt, computing results viabig.Intmod arithmetic and returningnewConstElementwithout hints/deferred checks or overflow growth. New unit/regression tests cover constant fast paths across small/large fields and the fuzz-foundExp(x,1)/Mul(1,1)compilation regressions.Written by Cursor Bugbot for commit 5e2ece3. This will update automatically on new commits. Configure here.