@@ -478,12 +478,29 @@ func CloneGenerators(LeftGenerators, RightGenerators []*mathlib.G1) ([]*mathlib.
478478 return leftGen , rightGen
479479}
480480
481- // ComputeSVector computes the s vector and its entry-wise inverse of s as detailed below:
482- // b(i,j) = 1 if (logn - j)^{th} bit of i-1 is 1, else its -1
483- // s[i] = \prod_{j=1}^{\log n} x_j^{bits(i,j)}
484- // sInv[i] = s[i].Inverse
485- // Input: n, challenges = [x_1,\ldots,x_{\log n}]
486- // Returns (s, sInv) where sInv[i] = s[i]^{-1}, computed via batch inversion.
481+ // ComputeSVector computes the s vector and its entry-wise inverse using
482+ // a dual-butterfly (doubling) recurrence in O(n) multiplications.
483+ //
484+ // Each entry is defined as:
485+ //
486+ // s[i] = ∏_{r=0}^{k-1} (bit(i, k-1-r) == 1 ? x_r : x_r^{-1})
487+ // sInv[i] = s[i]^{-1}
488+ //
489+ // where k = log₂(n) and x_r = challenges[r].
490+ //
491+ // The butterfly builds both vectors simultaneously:
492+ //
493+ // Round r: for each existing entry i in [0, 2^r):
494+ // s[i + 2^r] = s[i] · x_{k-1-r} (bit set → challenge)
495+ // s[i] = s[i] · x_{k-1-r}^{-1} (bit unset → inverse)
496+ // sInv[i + 2^r] = sInv[i] · x_{k-1-r}^{-1} (swapped)
497+ // sInv[i] = sInv[i] · x_{k-1-r} (swapped)
498+ //
499+ // This replaces the previous O(n·log n) nested-loop implementation and
500+ // eliminates the final BatchInverse call for sInv.
501+ //
502+ // Input: n, challenges = [x_0, …, x_{k-1}] where n = 2^k.
503+ // Returns (s, sInv) where sInv[i] = s[i]^{-1}.
487504func ComputeSVector (n int , challenges []* mathlib.Zr , curve * mathlib.Curve ) ([]* mathlib.Zr , []* mathlib.Zr ) {
488505 log2n := len (challenges )
489506
@@ -492,40 +509,36 @@ func ComputeSVector(n int, challenges []*mathlib.Zr, curve *mathlib.Curve) ([]*m
492509 panic ("n must equal 2^(number of challenges)" )
493510 }
494511
495- // Precompute challenge inverses (log2n inversions instead of O(n*log2n))
512+ // Precompute challenge inverses: O(log n) with a single field inversion.
496513 challengeInvs := math .BatchInverse (challenges , curve )
497514
515+ // Pre-allocate both vectors. Entries [1..n-1] are initialised to zero
516+ // so that ModMulInPlace can write into them without prior allocation.
498517 s := make ([]* mathlib.Zr , n )
499-
500- for i := 1 ; i <= n ; i ++ {
501- // Start with s_i = 1
502- si := math .One (curve )
503-
504- // Compute product over j=1 to log2(n).
505- // At round j the generator fold splits on bit (log2n - j) of the
506- // 0-based index: first half (bit=0) picks up x_j^{-1}, second
507- // half (bit=1) picks up x_j — matching reduceGenerators.
508- for j := 1 ; j <= log2n ; j ++ {
509- bitPosition := log2n - j
510- iMinus1 := i - 1
511- bitIsSet := (iMinus1 >> bitPosition ) & 1
512-
513- var factor * mathlib.Zr
514- if bitIsSet == 1 {
515- // second half at round j => x_j
516- factor = challenges [j - 1 ]
517- } else {
518- // first half at round j => x_j^{-1}
519- factor = challengeInvs [j - 1 ]
520- }
521-
522- // Multiply: s_i *= factor
523- si = curve .ModMul (si , factor , curve .GroupOrder )
518+ sInv := make ([]* mathlib.Zr , n )
519+ s [0 ] = math .One (curve )
520+ sInv [0 ] = math .One (curve )
521+ for i := 1 ; i < n ; i ++ {
522+ s [i ] = curve .NewZrFromInt (0 )
523+ sInv [i ] = curve .NewZrFromInt (0 )
524+ }
525+
526+ // Dual butterfly: O(n) total multiplications.
527+ for r := range log2n {
528+ halfLen := 1 << r
529+ c := challenges [log2n - 1 - r ]
530+ cInv := challengeInvs [log2n - 1 - r ]
531+ for i := range halfLen {
532+ // s: bit set → challenge, bit unset → inverse.
533+ // Compute s[i+halfLen] BEFORE mutating s[i].
534+ curve .ModMulInPlace (s [i + halfLen ], s [i ], c , curve .GroupOrder )
535+ s [i ] = curve .ModMul (s [i ], cInv , curve .GroupOrder )
536+
537+ // sInv: factors are swapped relative to s.
538+ curve .ModMulInPlace (sInv [i + halfLen ], sInv [i ], cInv , curve .GroupOrder )
539+ sInv [i ] = curve .ModMul (sInv [i ], c , curve .GroupOrder )
524540 }
525-
526- s [i - 1 ] = si
527541 }
528- sInv := math .BatchInverse (s , curve )
529542
530543 return s , sInv
531544}
0 commit comments