@@ -12,7 +12,7 @@ library ModexpBarrett {
1212 bytes memory base ,
1313 bytes memory exponent ,
1414 bytes memory modulus
15- ) internal view returns (bytes memory result ) {
15+ ) internal pure returns (bytes memory result ) {
1616 uint256 modLen = modulus.length ;
1717 if (modLen == 0 ) return new bytes (0 );
1818
@@ -46,10 +46,10 @@ library ModexpBarrett {
4646 uint256 k = (modLen + 31 ) / 32 ;
4747
4848 uint256 [] memory n = _bytesToLimbs (modulus, k);
49- uint256 [] memory a = _reduceBase (base, modulus , k);
49+ uint256 [] memory a = _reduceBase (base, n , k);
5050
5151 // Barrett constant: mu = floor(2^(512k) / n), has k+1 limbs
52- uint256 [] memory mu = _computeBarrettConstant (n, k, modulus );
52+ uint256 [] memory mu = _computeBarrettConstant (n, k);
5353
5454 // one = 1 as k-limb number (used as initial accumulator)
5555 uint256 [] memory r = new uint256 [](k);
@@ -144,83 +144,31 @@ library ModexpBarrett {
144144 }
145145 }
146146
147- // ── Precompile wrapper ────────────────────────────────────────────
148-
149- function _callPrecompile (bytes memory b , bytes memory e , bytes memory m )
150- private view returns (bytes memory result )
151- {
152- uint256 modLen = m.length ;
153- result = new bytes (modLen);
154- bytes memory input = abi.encodePacked (
155- uint256 (b.length ), uint256 (e.length ), uint256 (modLen), b, e, m
156- );
157- assembly {
158- let ok := staticcall (gas (), 0x05 , add (input, 0x20 ), mload (input), add (result, 0x20 ), modLen)
159- if iszero (ok) { revert (0 , 0 ) }
160- }
161- }
162-
163- function _reduceBase (bytes memory base , bytes memory modulus , uint256 k )
164- private view returns (uint256 [] memory )
147+ /// @dev Reduces base mod n via schoolbook division remainder.
148+ function _reduceBase (bytes memory base , uint256 [] memory n , uint256 k )
149+ private pure returns (uint256 [] memory )
165150 {
166- return _bytesToLimbs (_callPrecompile (base, hex "01 " , modulus), k);
167- }
168-
169- function _uint256ToMinBytes (uint256 val ) private pure returns (bytes memory ) {
170- if (val == 0 ) return hex "00 " ;
171- uint256 byteLen = 0 ;
172- uint256 tmp = val;
173- while (tmp > 0 ) {
174- byteLen++ ;
175- tmp >>= 8 ;
176- }
177- bytes memory result = new bytes (byteLen);
178- unchecked {
179- for (uint256 i = 0 ; i < byteLen; i++ ) {
180- result[byteLen - 1 - i] = bytes1 (uint8 (val));
181- val >>= 8 ;
182- }
183- }
184- return result;
151+ uint256 baseLen = base.length ;
152+ if (baseLen == 0 ) return new uint256 [](k);
153+ uint256 baseK = (baseLen + 31 ) / 32 ;
154+ if (baseK < k) baseK = k;
155+ uint256 [] memory baseLimbs = _bytesToLimbs (base, baseK);
156+ (, uint256 [] memory rem ) = _schoolbookDiv (baseLimbs, baseK, n, k);
157+ return rem;
185158 }
186159
187160 // ── Barrett constant computation ──────────────────────────────────
188161
189162 /// @dev Computes mu = floor(2^(512k) / n).
190- function _computeBarrettConstant (uint256 [] memory n , uint256 k , bytes memory modulus )
191- private view returns (uint256 [] memory )
163+ function _computeBarrettConstant (uint256 [] memory n , uint256 k )
164+ private pure returns (uint256 [] memory )
192165 {
193- // r = 2^(512k) mod n via precompile
194- uint256 expVal = 512 * k;
195- bytes memory expBytes = _uint256ToMinBytes (expVal);
196- bytes memory base2 = hex "02 " ;
197- uint256 [] memory r = _bytesToLimbs (_callPrecompile (base2, expBytes, modulus), k);
198-
199- // dividend = 2^(512k) - r, which is exactly divisible by n
200- // dividend has 2k+1 limbs: limbs 0..k-1 = two's complement of r, limb 2k = 1
166+ // Construct 2^(512k) as a (2k+1)-limb number: all zeros except limb[2k] = 1
201167 uint256 dLen = 2 * k + 1 ;
202168 uint256 [] memory dividend = new uint256 [](dLen);
203-
204- // Compute -r mod 2^(256k), i.e., two's complement
205- assembly {
206- let divP := add (dividend, 0x20 )
207- let rP := add (r, 0x20 )
208- let borrow := 0
209- for { let i := 0 } lt (i, k) { i := add (i, 1 ) } {
210- let ri := mload (add (rP, mul (i, 0x20 )))
211- let d := sub (sub (0 , ri), borrow)
212- borrow := or (gt (ri, 0 ), and (iszero (ri), borrow))
213- mstore (add (divP, mul (i, 0x20 )), d)
214- }
215- for { let i := k } lt (i, mul (2 , k)) { i := add (i, 1 ) } {
216- let d := sub (0 , borrow)
217- mstore (add (divP, mul (i, 0x20 )), d)
218- }
219- mstore (add (divP, mul (mul (2 , k), 0x20 )), sub (1 , borrow))
220- }
221-
222- // mu = dividend / n via schoolbook division
223- return _schoolbookDiv (dividend, dLen, n, k);
169+ dividend[2 * k] = 1 ;
170+ (uint256 [] memory mu ,) = _schoolbookDiv (dividend, dLen, n, k);
171+ return mu;
224172 }
225173
226174 // ── Division helpers ──────────────────────────────────────────────
@@ -277,15 +225,17 @@ library ModexpBarrett {
277225 uint256 dLen ,
278226 uint256 [] memory divisor ,
279227 uint256 k
280- ) private pure returns (uint256 [] memory quotient ) {
228+ ) private pure returns (uint256 [] memory quotient , uint256 [] memory rem ) {
229+ rem = new uint256 [](k);
230+
281231 // Find actual length of dividend (strip leading zero limbs)
282232 uint256 m = dLen;
283233 while (m > 0 && dividend[m - 1 ] == 0 ) {
284234 m-- ;
285235 }
286236 if (m == 0 ) {
287237 quotient = new uint256 [](1 );
288- return quotient;
238+ return ( quotient, rem) ;
289239 }
290240
291241 // Find effective number of significant limbs in divisor
@@ -305,13 +255,15 @@ library ModexpBarrett {
305255 (q, remainder) = _div512by256 (remainder, dividend[i], d);
306256 quotient[i] = q;
307257 }
308- return quotient;
258+ rem[0 ] = remainder;
259+ return (quotient, rem);
309260 }
310261
311262 // Multi-limb divisor: Knuth Algorithm D (using kEff significant limbs)
312263 if (m < kEff) {
313264 quotient = new uint256 [](1 );
314- return quotient;
265+ for (uint256 i = 0 ; i < m; i++ ) rem[i] = dividend[i];
266+ return (quotient, rem);
315267 }
316268 uint256 numQlimbs = m - kEff + 1 ;
317269 quotient = new uint256 [](numQlimbs);
@@ -454,7 +406,19 @@ library ModexpBarrett {
454406 quotient[jj] = qHat;
455407 }
456408
457- return quotient;
409+ // Extract remainder from u[0..kEff-1] and denormalize (shift right)
410+ if (shift > 0 ) {
411+ for (uint256 i = 0 ; i < kEff; i++ ) {
412+ rem[i] = u[i] >> shift;
413+ if (i + 1 < kEff) {
414+ rem[i] |= u[i + 1 ] << (256 - shift);
415+ }
416+ }
417+ } else {
418+ for (uint256 i = 0 ; i < kEff; i++ ) {
419+ rem[i] = u[i];
420+ }
421+ }
458422 }
459423
460424 // ── Schoolbook multiplication ─────────────────────────────────────
0 commit comments