@@ -51,6 +51,13 @@ type Field[T FieldParams] struct {
5151 checker frontend.Rangechecker
5252
5353 deferredChecks []deferredChecker
54+
55+ // smallFieldMode indicates that the emulated field is small enough that
56+ // products fit in the native field and we can use scalar batched verification
57+ // instead of polynomial identity testing. This provides significant constraint
58+ // reduction for small field emulation (e.g., KoalaBear on BLS12-377).
59+ smallFieldMode bool
60+ smallFieldModeOnce sync.Once
5461}
5562
5663type ctxKey [T FieldParams ] struct {}
@@ -310,3 +317,46 @@ func sum[T constraints.Ordered](a ...T) T {
310317 }
311318 return m
312319}
320+
321+ // useSmallFieldOptimization returns true if we can use the small field
322+ // optimization for multiplication. The optimization is possible when:
323+ // - NbLimbs == 1 (emulated field fits in a single native limb)
324+ // - 2 * modBits + margin < nativeBits - 2 (products fit with margin for batching)
325+ //
326+ // When these conditions are met, we can use scalar batched verification instead
327+ // of polynomial identity testing, which significantly reduces constraint counts.
328+ func (f * Field [T ]) useSmallFieldOptimization () bool {
329+ f .smallFieldModeOnce .Do (func () {
330+ // Small field optimization only works when NbLimbs == 1
331+ if f .fParams .NbLimbs () != 1 {
332+ f .smallFieldMode = false
333+ return
334+ }
335+
336+ // Small field optimization doesn't work when we're already using extension field
337+ // for multiplication checks (native field is small)
338+ if f .extensionApi != nil {
339+ f .smallFieldMode = false
340+ return
341+ }
342+
343+ // Check that products fit in the native field with margin for batching.
344+ // We need: 2 * modBits + batchingMargin < nativeBits - 2
345+ // The margin accounts for:
346+ // - γ^i scaling factors in the batched sum
347+ // - Multiple terms being summed together
348+ // We use 32 bits margin which allows for batching millions of operations.
349+ modBits := uint (f .fParams .Modulus ().BitLen ())
350+ nativeBits := uint (f .api .Compiler ().FieldBitLen ())
351+ const batchingMargin = 32
352+
353+ f .smallFieldMode = 2 * modBits + batchingMargin < nativeBits - 2
354+ if f .smallFieldMode {
355+ f .log .Debug ().
356+ Uint ("modBits" , modBits ).
357+ Uint ("nativeBits" , nativeBits ).
358+ Msg ("using small field optimization for emulated multiplication" )
359+ }
360+ })
361+ return f .smallFieldMode
362+ }
0 commit comments