thruput_u256/div.rs
1//! Division and remainder.
2//!
3//! [`U256::div_rem_small`] is a separate function rather than a fast path
4//! inside [`U256::div_rem`], so a contract that only divides by constants (or
5//! formats a decimal) links the cheap routine and not Knuth D. Measured by
6//! `scripts/bigint-bench.sh -b u256 -o "div div_small"`: linking `div` adds
7//! 2,572 bytes of rv32 .text where `div_small` adds 680, so the general path
8//! costs 1,892 on top of the small one. (It was 240 more before the Knuth D
9//! loop called `div_rem_2by1_normalized` directly, and far worse when the
10//! quotient estimate went through `__udivdi3` -- see the estimate below.)
11//!
12//! Knuth D is written once as a macro and instantiated at two dividend widths
13//! -- eight limbs for the `U256 / U256` operators, sixteen for the
14//! full-precision `mul_div` family, whose dividend is a 512-bit product. A
15//! single shared function over the wider buffers was measured and rejected;
16//! [`knuth_d`] has the numbers.
17
18use crate::{LIMBS, U256};
19use core::mem::MaybeUninit;
20
21/// The number of 32-bit limbs in a full-precision (512-bit) product.
22pub(crate) const WIDE_LIMBS: usize = 2 * LIMBS;
23
24/// The base of the limb representation, as a `u64` for intermediate work.
25const B: u64 = 1 << 32;
26
27/// Half-limb base, for the 64-by-32 divide below.
28const HB: u32 = 1 << 16;
29
30/// Divides `(hi, lo)` by `d`. Requires `hi < d` and `d != 0`; both hold at
31/// every call site.
32///
33/// Hacker's Delight's `divlu` -- schoolbook base 2^16, so every divide is a
34/// native 32-bit `DIVU`. The obvious `(hi:lo as u64) / d` spelling is not:
35/// `DIVU` is XLEN-wide, so on rv32 it lowers to `__udivdi3`/`__umoddi3`, one
36/// libcall per limb. (Verified by disassembly; it emitted zero `divu`.)
37#[inline]
38const fn div_rem_2by1(hi: u32, lo: u32, d: u32) -> (u32, u32) {
39 debug_assert!(hi < d);
40
41 // Normalise so the divisor's high bit is set, which is what bounds the
42 // half-digit estimates in the core.
43 let s = d.leading_zeros();
44 let un32 = if s == 0 {
45 hi
46 } else {
47 (hi << s) | (lo >> (32 - s))
48 };
49 let (q, rem) = div_rem_2by1_normalized(un32, lo << s, d << s);
50 (q, rem >> s)
51}
52
53/// The core of [`div_rem_2by1`], for a divisor whose high bit is already set.
54/// Requires `hi < d`.
55///
56/// Split out so the Knuth D loop, which normalises its divisor once up front,
57/// does not re-derive `s == 0` per quotient digit. LLVM cannot prove it --
58/// known-bits does not carry `x << clz(x)` through the array stores -- so the
59/// wrapper's selects and shifts would otherwise ride along in the hot loop as
60/// dead work.
61#[inline]
62const fn div_rem_2by1_normalized(hi: u32, lo: u32, d: u32) -> (u32, u32) {
63 debug_assert!(hi < d);
64 debug_assert!(d >> 31 == 1);
65
66 let vn1 = d >> 16;
67 let vn0 = d & 0xffff;
68
69 let un1 = lo >> 16;
70 let un0 = lo & 0xffff;
71
72 // `rhat < 2^16` is held by the `break`, and `q1 * vn0` is only reached once
73 // `q1 < 2^16`, so neither overflows.
74 let mut q1 = hi / vn1;
75 let mut rhat = hi - q1 * vn1;
76 while q1 >= HB || q1 * vn0 > (rhat << 16) | un1 {
77 q1 -= 1;
78 rhat += vn1;
79 if rhat >= HB {
80 break;
81 }
82 }
83
84 // `un21` fits in 32 bits but `hi * 2^16` does not; the wrapping is
85 // deliberate and lands on the right answer.
86 let un21 = hi
87 .wrapping_mul(HB)
88 .wrapping_add(un1)
89 .wrapping_sub(q1.wrapping_mul(d));
90
91 let mut q0 = un21 / vn1;
92 rhat = un21 - q0 * vn1;
93 while q0 >= HB || q0 * vn0 > (rhat << 16) | un0 {
94 q0 -= 1;
95 rhat += vn1;
96 if rhat >= HB {
97 break;
98 }
99 }
100
101 let rem = un21
102 .wrapping_mul(HB)
103 .wrapping_add(un0)
104 .wrapping_sub(q0.wrapping_mul(d));
105
106 (q1 * HB + q0, rem)
107}
108
109impl U256 {
110 /// Divides by a 32-bit value, returning the quotient and remainder.
111 ///
112 /// # Panics
113 ///
114 /// If `rhs` is zero.
115 #[inline]
116 pub const fn div_rem_small(self, rhs: u32) -> (Self, u32) {
117 self.div_rem_small_from(rhs, 0)
118 }
119
120 /// [`div_rem_small`](Self::div_rem_small), resuming from a partial
121 /// remainder rather than from zero.
122 ///
123 /// Chaining two calls divides a 512-bit value by a 32-bit one: the high
124 /// half's remainder seeds the low half. That keeps the `dn == 1` case of
125 /// [`div_rem_512`] on this cheap routine instead of widening the Knuth D
126 /// buffers to reach it.
127 ///
128 /// # Panics
129 ///
130 /// If `rhs` is zero. `rem` must already be below `rhs`, which every
131 /// caller satisfies by construction (it is a remainder of `rhs`).
132 #[inline]
133 pub(crate) const fn div_rem_small_from(
134 self,
135 rhs: u32,
136 rem: u32,
137 ) -> (Self, u32) {
138 if rhs == 0 {
139 panic!("attempt to divide by zero");
140 }
141 debug_assert!(rem < rhs);
142
143 let mut q = [0u32; LIMBS];
144 let mut rem = rem;
145
146 let mut i = LIMBS;
147 while i > 0 {
148 i -= 1;
149 // `rem` is a remainder, so `rem < rhs` holds on every iteration,
150 // which is exactly `div_rem_2by1`'s precondition.
151 let (qi, r) = div_rem_2by1(rem, self.limbs[i], rhs);
152 q[i] = qi;
153 rem = r;
154 }
155
156 (Self::from_limbs(q), rem)
157 }
158
159 /// Divides by another `U256`, returning the quotient and remainder.
160 ///
161 /// # Panics
162 ///
163 /// If `rhs` is zero, matching the primitive integers (which panic on a
164 /// zero divisor regardless of `overflow-checks`).
165 pub const fn div_rem(self, rhs: Self) -> (Self, Self) {
166 match self.checked_div_rem(rhs) {
167 Some(pair) => pair,
168 None => panic!("attempt to divide by zero"),
169 }
170 }
171
172 /// Divides by another `U256`, returning `None` if `rhs` is zero.
173 pub const fn checked_div_rem(self, rhs: Self) -> Option<(Self, Self)> {
174 let dn = rhs.limb_len();
175 if dn == 0 {
176 return None;
177 }
178 let nn = self.limb_len();
179
180 // Divisor larger than the dividend: quotient zero, remainder self.
181 if nn < dn {
182 return Some((Self::ZERO, self));
183 }
184
185 // Separate function, not an inlined copy, so a contract using only
186 // `div_rem_small` never links the code below.
187 if dn == 1 {
188 let (q, r) = self.div_rem_small(rhs.limbs[0]);
189 return Some((q, Self::from_u32(r)));
190 }
191
192 let (q, r) = div_rem_knuth(&self.limbs, nn, &rhs.limbs, dn);
193 Some((Self::from_limbs(q), r))
194 }
195 /// Division, returning `None` if `rhs` is zero.
196 #[inline]
197 pub const fn checked_div(self, rhs: Self) -> Option<Self> {
198 match self.checked_div_rem(rhs) {
199 Some((q, _)) => Some(q),
200 None => None,
201 }
202 }
203
204 /// Remainder, returning `None` if `rhs` is zero.
205 #[inline]
206 pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
207 match self.checked_div_rem(rhs) {
208 Some((_, r)) => Some(r),
209 None => None,
210 }
211 }
212}
213
214/// The low [`LIMBS`] limbs of a full-precision limb array.
215#[inline]
216pub(crate) const fn low_limbs(wide: &[u32; WIDE_LIMBS]) -> [u32; LIMBS] {
217 let mut out = [0u32; LIMBS];
218 let mut i = 0;
219 while i < LIMBS {
220 out[i] = wide[i];
221 i += 1;
222 }
223 out
224}
225
226/// Generates a Knuth algorithm D (Hacker's Delight `divmnu`) specialised to a
227/// dividend of `$n` limbs, base 2^32.
228///
229/// The generated function divides the `nn`-limb value `u` by the `dn`-limb
230/// value `v`, returning the quotient's limbs and the remainder. The quotient
231/// occupies the low `nn - dn + 1` limbs of the returned array; the rest are
232/// zero.
233///
234/// It requires `2 <= dn <= LIMBS`, `dn <= nn <= $n`, `u[nn - 1] != 0` and
235/// `v[dn - 1] != 0`. Every caller establishes these from a `limb_len`.
236///
237/// # Why a macro
238///
239/// Two widths need this: the `U256 / U256` operators divide eight limbs, and
240/// [`div_rem_512`] divides the sixteen of a full-precision product. One body,
241/// two instantiations, each sized to its own dividend.
242///
243/// The obvious alternative -- one function over the wider buffers, called by
244/// both, since every loop is bounded by the runtime `nn`/`dn` anyway -- was
245/// written first and measured second. It loses.
246/// `scripts/bigint-bench.sh -b u256 -o "div div_small mul_div_u128"`, rv32,
247/// marginal over the `none` calibration build:
248///
249/// | | `div` .text | `div` gas | `mul_div_u128` .text | `mul_div_u128` gas |
250/// |---|---|---|---|---|
251/// | before this change | 2,640 | 963.05 | 3,036 | 1735.05 |
252/// | one shared function | 2,576 | 1221.05 | 2,996 | 1798.05 |
253/// | two instantiations | 2,572 | 972.05 | 3,012 | 1712.05 |
254///
255/// Sharing one function buys 64 bytes and costs **+258 gas per division, 27% on
256/// the bench's operands**. Division is the hot path of the contracts this
257/// crate exists for, so that is the wrong way round.
258/// Specialising gets the bytes anyway (68 below the pre-change baseline) for
259/// +0.9% gas, and leaves `mul_div_u128` both smaller and faster than before.
260///
261/// Read the 27% as a worst case, not a constant. The overhead is per call, so
262/// it is a larger fraction of a shallow division than a deep one, and the
263/// bench's operands make `div` shallow: its divisor is 201 bits, seven of
264/// eight limbs, so Knuth D runs its quotient loop twice out of a possible
265/// seven. It is also not purely fixed -- the same change cost `mul_div_u128`
266/// only 63 gas through the same `checked_div_rem`. Why the two call shapes
267/// differ by that much was not isolated; only the totals above were measured,
268/// and the mechanism is a guess this comment declines to make. What holds
269/// across every row is the sign: specialised is never worse than shared.
270///
271/// # The scratch buffers
272///
273/// Four arrays, and only two of them want zeros:
274///
275/// - `q` and `r` **are** the answer. The quotient occupies `nn - dn + 1` limbs
276/// and the remainder `dn`, but both are returned full width, so their high
277/// limbs genuinely are zero and something has to write them. They stay
278/// `[u32; _]`, zero-initialised.
279/// - `vn` and `un` are scratch. The normalisation loops fill `vn[0..dn]` and
280/// `un[0..=nn]` before anything reads them, and nothing ever reads above
281/// that, so zeroing them was dead work. They are `MaybeUninit` -- the
282/// `SAFETY` note above the function states the two invariants every read
283/// leans on.
284///
285/// LLVM could not remove that dead half on its own. The dynamic `vn[dn - 1]`
286/// and `un[j + dn]` indexing pins both arrays to the stack, out of reach of
287/// the dead-store elimination a scalarised array would have got, so the
288/// zeroing was real stores in the artifact: 40 `sw zero` over the `none`
289/// calibration build, which is all four arrays written out in full.
290///
291/// The safe way to reach them -- write every slot unconditionally, so a later
292/// store dominates each element -- was tried first, on `vn`, and does not
293/// work. `MaybeUninit` does. rv32, marginal over `none`:
294///
295/// | | `div` .text | `div` gas | `sw zero` |
296/// |---|---|---|---|
297/// | zero-init, then write `0..dn` | 2,572 | 972.05 | 40 |
298/// | write all `LIMBS` unconditionally | 2,924 | 947.05 | 40 |
299/// | `MaybeUninit` | **2,360** | **928.05** | **25** |
300///
301/// The full-width write keeps every zero store and costs 352 bytes to save 25
302/// gas -- backwards where the 64 KiB artifact cap binds. `MaybeUninit` removes
303/// 15 of the 40 and is smaller *and* faster on every caller:
304///
305/// | | .text | gas |
306/// |---|---|---|
307/// | `div` | 2,572 -> 2,360 (**-8.2%**) | 972.05 -> 928.05 (**-4.5%**) |
308/// | `mul_div_u128` | 3,012 -> 2,788 (**-7.4%**) | 1712.05 -> 1628.05 (**-4.9%**) |
309/// | `mul_div` | 3,992 -> 3,892 (**-2.5%**) | 4712.05 -> 4534.05 (**-3.8%**) |
310///
311/// This is the crate's only discretionary `unsafe`, so it is the one place
312/// Miri earns its keep: `.github/workflows/miri.yml` runs this crate's suite
313/// under it, and the tests thin their random sweeps under `cfg!(miri)` rather
314/// than skipping any shape.
315///
316/// Being a `const fn` was never the obstacle, incidentally -- `MaybeUninit`
317/// works in one on stable, compile-time evaluation of a partially initialised
318/// array included, so long as the uninitialised tail is never read.
319///
320/// `#[inline]` on the generated function was measured too, on the theory that
321/// it would restore the pre-split shape exactly: it costs 156 bytes on `div`
322/// to save 2 gas, and makes `mul_div_u128` both bigger and slower. Not taken.
323///
324/// What the split costs instead is now measured too, by an op that calls both
325/// paths in one kernel so the artifact carries both instantiations:
326///
327/// | | .text | gas |
328/// |---|---|---|
329/// | `mul_div` alone | 3,892 | 4534.05 |
330/// | `div` alone | 2,360 | 928.05 |
331/// | both in one artifact | 6,088 | 5468.05 |
332///
333/// So adding `/` to a contract that already links `mul_div` costs **2,196
334/// bytes** -- against 2,360 standalone, meaning barely 160 bytes are shared
335/// and the two Knuth D copies are all but fully duplicated. Sharing one
336/// function would have saved most of that ~2.2 KB, and cost 27% of every
337/// division's gas. Both halves of that trade are numbers.
338///
339/// (An earlier version of this comment guessed the second copy at "the 1,892
340/// bytes one copy costs over `div_small`". Measuring it said otherwise, by
341/// about 20% -- which is why it is a measurement now.)
342macro_rules! knuth_d {
343 ($name:ident, $n:expr) => {
344 // `vn` and `un` are `MaybeUninit` scratch, so every read of them is
345 // `unsafe`. Two facts, established here once, make all of those reads
346 // sound; the `SAFETY` comments below say which one each leans on.
347 //
348 // I1. `vn[0..dn]` is initialised. The normalisation loop writes every
349 // index below `dn` before anything reads one, and every later read
350 // is at `dn - 1`, at `dn - 2` (`dn >= 2`), or at an `i < dn`.
351 //
352 // I2. `un[0..=nn]` is initialised. Its loop writes `nn` and then every
353 // index below it. Every later read is at `i + j`, `j + dn`,
354 // `j + dn - 1`, `j + dn - 2` -- all at most `nn`, since `j` stops
355 // at `nn - dn` and `i` at `dn - 1` -- or at `i` and `i + 1` in the
356 // remainder loop, where `i < dn` and `dn <= nn`.
357 //
358 // Nothing reads above those bounds, which is the whole reason the
359 // tails are left uninitialised: see "Why the buffers are zeroed".
360 const fn $name(
361 u: &[u32; $n],
362 nn: usize,
363 v: &[u32; LIMBS],
364 dn: usize,
365 ) -> ([u32; $n], U256) {
366 debug_assert!(dn >= 2);
367 debug_assert!(nn >= dn);
368 debug_assert!(nn <= $n);
369
370 let s = v[dn - 1].leading_zeros();
371
372 // Normalise the divisor so its top limb has its high bit set, which is
373 // what bounds the quotient-digit estimate below. Establishes I1.
374 let mut vn: [MaybeUninit<u32>; LIMBS] =
375 [MaybeUninit::uninit(); LIMBS];
376 let mut i = dn;
377 while i > 0 {
378 i -= 1;
379 let lo = if i == 0 || s == 0 {
380 0
381 } else {
382 v[i - 1] >> (32 - s)
383 };
384 vn[i] = MaybeUninit::new((v[i] << s) | lo);
385 }
386
387 // Normalised dividend, one limb wider so the estimate always has two
388 // limbs to read. Establishes I2.
389 let mut un: [MaybeUninit<u32>; $n + 1] =
390 [MaybeUninit::uninit(); $n + 1];
391 un[nn] = MaybeUninit::new(if s == 0 {
392 0
393 } else {
394 u[nn - 1] >> (32 - s)
395 });
396 let mut i = nn;
397 while i > 0 {
398 i -= 1;
399 let lo = if i == 0 || s == 0 {
400 0
401 } else {
402 u[i - 1] >> (32 - s)
403 };
404 un[i] = MaybeUninit::new((u[i] << s) | lo);
405 }
406
407 let mut q = [0u32; $n];
408
409 let mut j = nn - dn + 1;
410 while j > 0 {
411 j -= 1;
412
413 // Estimate one quotient limb from the top two dividend limbs.
414 //
415 // `div_rem_2by1_normalized` wants `hi < dv`, while Knuth allows
416 // equality (the running remainder is below the divisor, so
417 // `hi <= dv`). Equality means the true digit is `B + lo / dv`, at
418 // least `B`; step D3 clamps that to `B - 1`, which is still an
419 // upper bound on the real quotient digit, and the correction below
420 // plus the add-back take it the rest of the way. Spelling the
421 // common branch as `div_rem_2by1_normalized` -- `dv` is the
422 // normalised divisor's top limb, so its high bit is set -- keeps
423 // the whole loop on native 32-bit `DIVU`s instead of one
424 // `__udivdi3` per quotient digit, and skips the wrapper's
425 // re-normalisation prologue.
426 // SAFETY: I2 -- `j + dn <= nn`, so both are initialised.
427 let hi = unsafe { un[j + dn].assume_init() };
428 let lo = unsafe { un[j + dn - 1].assume_init() };
429 // SAFETY: I1 -- `dn >= 2`, so `dn - 1 < dn`.
430 let dv = unsafe { vn[dn - 1].assume_init() };
431
432 let (mut qhat, mut rhat) = if hi < dv {
433 let (qh, rh) = div_rem_2by1_normalized(hi, lo, dv);
434 (qh as u64, rh as u64)
435 } else {
436 // `hi <= dv` (above), so only equality reaches here, and the
437 // Knuth rhat `num - (B - 1) * dv` collapses: `num` is
438 // `dv * B + lo`, so subtracting `(B - 1) * dv` leaves
439 // `dv + lo`.
440 debug_assert!(hi == dv);
441 (B - 1, (dv as u64) + (lo as u64))
442 };
443
444 // `rhat >= B` makes the test below vacuous -- `(rhat << 32) | ..`
445 // exceeds any `qhat * vn[dn - 2]` -- and the shift would drop the
446 // bits that make that true, so it guards the loop rather than only
447 // breaking out of it. `qhat < B` now holds by construction, so the
448 // estimate no longer needs its own bound check.
449 // SAFETY: I1 and I2 -- `dn - 2 < dn` (`dn >= 2`) and
450 // `j + dn - 2 < nn`.
451 let vn_second = unsafe { vn[dn - 2].assume_init() };
452 let un_third = unsafe { un[j + dn - 2].assume_init() };
453 while rhat < B
454 && qhat * (vn_second as u64)
455 > (rhat << 32) | (un_third as u64)
456 {
457 qhat -= 1;
458 rhat += dv as u64;
459 }
460
461 // Multiply and subtract.
462 let mut k: i64 = 0;
463 let mut i = 0;
464 while i < dn {
465 // SAFETY: I1 -- `i < dn`.
466 let vi = unsafe { vn[i].assume_init() };
467 // SAFETY: I2 -- `i + j <= nn - 1`.
468 let uij = unsafe { un[i + j].assume_init() };
469 let p = qhat * (vi as u64);
470 let t = (uij as i64) - k - ((p & 0xffff_ffff) as i64);
471 un[i + j] = MaybeUninit::new(t as u32);
472 k = ((p >> 32) as i64) - (t >> 32);
473 i += 1;
474 }
475 // SAFETY: I2 -- `j + dn <= nn`.
476 let t = (unsafe { un[j + dn].assume_init() } as i64) - k;
477 un[j + dn] = MaybeUninit::new(t as u32);
478
479 q[j] = qhat as u32;
480
481 // Estimate one too large (probability ~2/2^32): add the divisor
482 // back. Rare enough that the tests need a wide sweep to hit it.
483 if t < 0 {
484 q[j] -= 1;
485 let mut carry = 0u64;
486 let mut i = 0;
487 while i < dn {
488 // SAFETY: I1 and I2 -- `i < dn` and `i + j <= nn - 1`.
489 let vi = unsafe { vn[i].assume_init() };
490 let uij = unsafe { un[i + j].assume_init() };
491 let sum = (uij as u64) + (vi as u64) + carry;
492 un[i + j] = MaybeUninit::new(sum as u32);
493 carry = sum >> 32;
494 i += 1;
495 }
496 // SAFETY: I2 -- `j + dn <= nn`.
497 let top = unsafe { un[j + dn].assume_init() };
498 un[j + dn] = MaybeUninit::new(
499 (top as u64).wrapping_add(carry) as u32,
500 );
501 }
502 }
503
504 // Undo the normalisation shift on the remainder.
505 let mut r = [0u32; LIMBS];
506 let mut i = 0;
507 while i < dn {
508 // SAFETY: I2 -- `i < dn` and `dn <= nn`, so `i + 1 <= nn`.
509 let lo = unsafe { un[i].assume_init() };
510 let next = unsafe { un[i + 1].assume_init() };
511 let hi = if s == 0 { 0 } else { next << (32 - s) };
512 r[i] = (lo >> s) | hi;
513 i += 1;
514 }
515
516 (q, U256::from_limbs(r))
517 }
518 };
519}
520
521knuth_d!(div_rem_knuth, LIMBS);
522knuth_d!(div_rem_knuth_wide, WIDE_LIMBS);
523
524/// Divides the 512-bit value `hi * 2^256 + lo` by `d`, returning the
525/// quotient's sixteen limbs and the remainder. `None` if `d` is zero.
526///
527/// The quotient genuinely needs 512 bits when `d <= hi`; callers that require
528/// a `U256` answer check the upper half themselves. The remainder is below
529/// `d`, so it always fits.
530pub(crate) const fn div_rem_512(
531 hi: U256,
532 lo: U256,
533 d: U256,
534) -> Option<([u32; WIDE_LIMBS], U256)> {
535 let dn = d.limb_len();
536 if dn == 0 {
537 return None;
538 }
539
540 // Same split as the narrow path: dividing by a single limb stays on
541 // `div_rem_small`, seeded across the two halves, rather than widening
542 // Knuth D's buffers to reach a case it does not need.
543 if dn == 1 {
544 let divisor = d.limbs[0];
545 let (qhi, rem) = hi.div_rem_small(divisor);
546 let (qlo, rem) = lo.div_rem_small_from(divisor, rem);
547 let mut q = [0u32; WIDE_LIMBS];
548 let mut i = 0;
549 while i < LIMBS {
550 q[i] = qlo.limbs[i];
551 q[i + LIMBS] = qhi.limbs[i];
552 i += 1;
553 }
554 return Some((q, U256::from_u32(rem)));
555 }
556
557 let mut u = [0u32; WIDE_LIMBS];
558 let mut i = 0;
559 while i < LIMBS {
560 u[i] = lo.limbs[i];
561 u[i + LIMBS] = hi.limbs[i];
562 i += 1;
563 }
564
565 let mut nn = WIDE_LIMBS;
566 while nn > 0 && u[nn - 1] == 0 {
567 nn -= 1;
568 }
569 // `dn <= LIMBS`, so a dividend with fewer significant limbs than the
570 // divisor has an empty high half, and the remainder is just `lo`.
571 if nn < dn {
572 return Some(([0u32; WIDE_LIMBS], lo));
573 }
574
575 let (q, r) = div_rem_knuth_wide(&u, nn, &d.limbs, dn);
576 Some((q, r))
577}
578
579/// Computes `a * b / d` exactly, without the intermediate product needing to
580/// fit in 128 bits.
581///
582/// This is the case that sends contract authors looking for a 256-bit type in
583/// the first place: two 128-bit amounts multiplied and scaled back down. The
584/// `U256` never escapes into caller code.
585///
586/// # Panics
587///
588/// If `d` is zero, or if the quotient does not fit in a `u128`. The overflow
589/// panic is unconditional rather than `overflow-checks`-gated: this is an
590/// explicit API rather than an operator, and silently wrapping the result of a
591/// deliberate exact-arithmetic call has no primitive behaviour to match.
592#[inline]
593pub fn mul_div_u128(a: u128, b: u128, d: u128) -> u128 {
594 match checked_mul_div_u128(a, b, d) {
595 Some(v) => v,
596 None if d == 0 => panic!("attempt to divide by zero"),
597 None => panic!("mul_div_u128 quotient exceeds u128"),
598 }
599}
600
601/// Computes `a * b / d` exactly, returning `None` if `d` is zero or the
602/// quotient does not fit in a `u128`.
603#[inline]
604pub fn checked_mul_div_u128(a: u128, b: u128, d: u128) -> Option<u128> {
605 let (q, _) =
606 U256::widening_mul_u128(a, b).checked_div_rem(U256::from(d))?;
607 u128::try_from(q).ok()
608}
609
610#[cfg(test)]
611mod tests {
612 use super::*;
613
614 /// The spelling `div_rem_2by1` replaced -- correct and obvious, just a
615 /// libcall on rv32.
616 fn reference(hi: u32, lo: u32, d: u32) -> (u32, u32) {
617 let n = ((hi as u64) << 32) | (lo as u64);
618 ((n / d as u64) as u32, (n % d as u64) as u32)
619 }
620
621 fn check(hi: u32, lo: u32, d: u32) {
622 assert!(hi < d, "precondition");
623 assert_eq!(
624 div_rem_2by1(hi, lo, d),
625 reference(hi, lo, d),
626 "({hi}, {lo}) / {d}"
627 );
628 // A normalised divisor also satisfies the core's stricter
629 // precondition, so pin the core against the oracle directly rather
630 // than only through the wrapper's `s == 0` path.
631 if d >> 31 == 1 {
632 assert_eq!(
633 div_rem_2by1_normalized(hi, lo, d),
634 reference(hi, lo, d),
635 "normalized ({hi}, {lo}) / {d}"
636 );
637 }
638 }
639
640 #[test]
641 fn div_rem_2by1_matches_u64_on_boundaries() {
642 let interesting = [
643 1u32,
644 2,
645 3,
646 10,
647 1_000_000_000,
648 0xffff,
649 0x1_0000,
650 0x8000_0000,
651 0xffff_fffe,
652 u32::MAX,
653 ];
654 let los = [0u32, 1, 0xffff, 0x1_0000, 0x8000_0000, u32::MAX];
655
656 for d in interesting {
657 for lo in los {
658 // hi must stay below d.
659 for hi in [0, d / 2, d.saturating_sub(1)] {
660 if hi < d {
661 check(hi, lo, d);
662 }
663 }
664 }
665 }
666 }
667
668 #[test]
669 fn div_rem_2by1_matches_u64_on_random_inputs() {
670 // Deterministic xorshift so a failure reproduces exactly.
671 let mut x = 0x2545_f491_4f6c_dd1du64;
672 let mut next = move || {
673 x ^= x << 13;
674 x ^= x >> 7;
675 x ^= x << 17;
676 x
677 };
678
679 for _ in 0..200_000 {
680 let d = (next() as u32).max(1);
681 let hi = (next() as u32) % d;
682 let lo = next() as u32;
683 check(hi, lo, d);
684 }
685 }
686
687 /// Divisors with a set high bit skip normalisation (`s == 0`), which is
688 /// the branch where a `>> (32 - s)` would be undefined.
689 #[test]
690 fn div_rem_2by1_handles_unnormalised_and_normalised_divisors() {
691 let mut x = 0x9e37_79b9_7f4a_7c15u64;
692 let mut next = move || {
693 x ^= x << 13;
694 x ^= x >> 7;
695 x ^= x << 17;
696 x
697 };
698
699 for _ in 0..50_000 {
700 // Already normalised: high bit set, so s == 0.
701 let d = (next() as u32) | 0x8000_0000;
702 check((next() as u32) % d, next() as u32, d);
703
704 // Maximally unnormalised: s == 31.
705 check(0, next() as u32, 1);
706 }
707 }
708}