ruint/algorithms/div/
small.rs

1//! Small division using reciprocals from [MG10].
2//!
3//! [MG10]: https://gmplib.org/~tege/division-paper.pdf
4
5// Following naming from paper.
6#![allow(clippy::many_single_char_names, clippy::similar_names)]
7// Truncation is intentional
8#![allow(clippy::cast_possible_truncation)]
9
10use super::reciprocal::{reciprocal, reciprocal_2};
11use crate::{
12    algorithms::DoubleWord,
13    utils::{unlikely, UncheckedSlice},
14};
15
16// The running time is 2.7 ns for [`div_2x1_mg10`] versus 18 ns for
17// [`div_2x1_ref`].
18pub use self::{div_2x1_mg10 as div_2x1, div_3x2_mg10 as div_3x2};
19
20/// ⚠️ Compute single limb division.
21#[doc = crate::algorithms::unstable_warning!()]
22#[inline(always)]
23#[track_caller]
24#[must_use]
25pub const fn div_1x1(numerator: u64, divisor: u64) -> (u64, u64) {
26    (numerator / divisor, numerator % divisor)
27}
28
29/// ⚠️ Compute single limb normalized division.
30#[doc = crate::algorithms::unstable_warning!()]
31/// The divisor must be normalized. See algorithm 7 from [MG10].
32///
33/// [MG10]: https://gmplib.org/~tege/division-paper.pdf
34#[inline(always)]
35pub fn div_nx1_normalized(u: &mut [u64], d: u64) -> u64 {
36    // OPT: Version with in-place shifting of `u`
37    debug_assert!(d >= (1 << 63));
38
39    let v = reciprocal(d);
40    let mut r: u64 = 0;
41    for u in u.iter_mut().rev() {
42        let n = u128::join(r, *u);
43        let (q, r0) = div_2x1(n, d, v);
44        *u = q;
45        r = r0;
46    }
47    r
48}
49
50/// ⚠️ Compute single limb division.
51#[doc = crate::algorithms::unstable_warning!()]
52/// The highest limb of `numerator` and `divisor` must be nonzero.
53/// The divisor does not need normalization.
54/// See algorithm 7 from [MG10].
55///
56/// [MG10]: https://gmplib.org/~tege/division-paper.pdf
57///
58/// # Panics
59///
60/// May panic if the above requirements are not met.
61#[inline(always)]
62pub fn div_nx1(limbs: &mut [u64], divisor: u64) -> u64 {
63    debug_assert!(divisor != 0);
64    debug_assert!(!limbs.is_empty());
65    debug_assert!(*limbs.last().unwrap() != 0);
66
67    let limbs = UncheckedSlice::wrap_mut(limbs);
68
69    // Normalize and compute reciprocal
70    let shift = divisor.leading_zeros();
71    if shift == 0 {
72        return div_nx1_normalized(limbs, divisor);
73    }
74    let divisor = divisor << shift;
75    let reciprocal = reciprocal(divisor);
76
77    let last = limbs[limbs.len() - 1];
78    let mut remainder = last >> (64 - shift);
79    for i in (1..limbs.len()).rev() {
80        // Shift limbs
81        let upper = limbs[i];
82        let lower = limbs[i - 1];
83        let u = (upper << shift) | (lower >> (64 - shift));
84
85        // Compute quotient
86        let n = u128::join(remainder, u);
87        let (q, r) = div_2x1(n, divisor, reciprocal);
88
89        // Store quotient
90        limbs[i] = q;
91        remainder = r;
92    }
93    // Compute last quotient
94    let first = limbs[0];
95    let n = u128::join(remainder, first << shift);
96    let (q, remainder) = div_2x1(n, divisor, reciprocal);
97    limbs[0] = q;
98
99    // Un-normalize remainder
100    remainder >> shift
101}
102
103/// ⚠️ Compute double limb normalized division.
104#[doc = crate::algorithms::unstable_warning!()]
105/// Requires `divisor` to be in the range $[2^{127}, 2^{128})$ (i.e.
106/// normalized). Same as [`div_nx1`] but using [`div_3x2`] internally.
107#[inline(always)]
108pub fn div_nx2_normalized(u: &mut [u64], d: u128) -> u128 {
109    // OPT: Version with in-place shifting of `u`
110    debug_assert!(d >= (1 << 127));
111
112    let v = reciprocal_2(d);
113    let mut remainder: u128 = 0;
114    for u in u.iter_mut().rev() {
115        let (q, r) = div_3x2(remainder, *u, d, v);
116        *u = q;
117        remainder = r;
118    }
119    remainder
120}
121
122/// ⚠️ Compute double limb division.
123#[doc = crate::algorithms::unstable_warning!()]
124/// Requires `divisor` to be in the range $[2^{64}, 2^{128})$. Same as
125/// [`div_nx2_normalized`] but does the shifting of the numerator inline.
126///
127/// # Panics
128///
129/// May panic if the above requirements are not met.
130#[inline(always)]
131pub fn div_nx2(limbs: &mut [u64], divisor: u128) -> u128 {
132    debug_assert!(divisor >= 1 << 64);
133    debug_assert!(!limbs.is_empty());
134    debug_assert!(*limbs.last().unwrap() != 0);
135
136    let limbs = UncheckedSlice::wrap_mut(limbs);
137
138    // Normalize and compute reciprocal
139    let shift = divisor.high().leading_zeros();
140    if shift == 0 {
141        return div_nx2_normalized(limbs, divisor);
142    }
143    let divisor = divisor << shift;
144    let reciprocal = reciprocal_2(divisor);
145
146    let last = limbs[limbs.len() - 1];
147    let mut remainder: u128 = u128::from(last >> (64 - shift));
148    for i in (1..limbs.len()).rev() {
149        // Shift limbs
150        let upper = limbs[i];
151        let lower = limbs[i - 1];
152        let u = (upper << shift) | (lower >> (64 - shift));
153
154        // Compute quotient
155        let (q, r) = div_3x2(remainder, u, divisor, reciprocal);
156
157        // Store quotient
158        limbs[i] = q;
159        remainder = r;
160    }
161    // Compute last quotient
162    let first = limbs[0];
163    let (q, remainder) = div_3x2(remainder, first << shift, divisor, reciprocal);
164    limbs[0] = q;
165
166    // Un-normalize remainder
167    remainder >> shift
168}
169
170/// ⚠️ Reference implementation for `div_2x1`.
171#[doc = crate::algorithms::unstable_warning!()]
172#[inline]
173#[must_use]
174pub fn div_2x1_ref(u: u128, d: u64) -> (u64, u64) {
175    debug_assert!(d >= (1 << 63));
176    debug_assert!((u >> 64) < u128::from(d));
177    let d = u128::from(d);
178    let q = (u / d) as u64;
179    let r = (u % d) as u64;
180    (q, r)
181}
182
183/// ⚠️ Computes the quotient and remainder of a `u128` divided by a `u64`.
184#[doc = crate::algorithms::unstable_warning!()]
185/// Requires
186/// * `u < d * 2**64`,
187/// * `d >= 2**63`, and
188/// * `v = reciprocal(d)`.
189///
190/// Implements algorithm 4 from [MG10].
191///
192/// [MG10]: https://gmplib.org/~tege/division-paper.pdf
193#[inline(always)]
194#[must_use]
195pub fn div_2x1_mg10(u: u128, d: u64, v: u64) -> (u64, u64) {
196    debug_assert!(d >= (1 << 63));
197    debug_assert!((u >> 64) < u128::from(d));
198    debug_assert_eq!(v, reciprocal(d));
199
200    let q = u + (u >> 64) * u128::from(v);
201    let q0 = q as u64;
202    let q1 = ((q >> 64) as u64).wrapping_add(1);
203    let r = (u as u64).wrapping_sub(q1.wrapping_mul(d));
204    let (q1, r) = if r > q0 {
205        (q1.wrapping_sub(1), r.wrapping_add(d))
206    } else {
207        (q1, r)
208    };
209    let (q1, r) = if unlikely(r >= d) {
210        (q1.wrapping_add(1), r.wrapping_sub(d))
211    } else {
212        (q1, r)
213    };
214    (q1, r)
215}
216
217/// ⚠️ Reference implementation for `div_3x2`.
218#[doc = crate::algorithms::unstable_warning!()]
219/// TODO: This implementation is off by one.
220#[inline]
221#[must_use]
222pub fn div_3x2_ref(n21: u128, n0: u64, d: u128) -> u64 {
223    debug_assert!(d >= (1 << 127));
224    debug_assert!(n21 < d);
225
226    let n2 = (n21 >> 64) as u64;
227    let n1 = n21 as u64;
228    let d1 = (d >> 64) as u64;
229    let d0 = d as u64;
230
231    if unlikely(n2 == d1) {
232        // From [n2 n1] < [d1 d0] and n2 = d1 it follows that n[1] < d[0].
233        debug_assert!(n1 < d0);
234        // We start by subtracting 2^64 times the divisor, resulting in a
235        // negative remainder. Depending on the result, we need to add back
236        // in one or two times the divisor to make the remainder positive.
237        // (It can not be more since the divisor is > 2^127 and the negated
238        // remainder is < 2^128.)
239        let neg_remainder = u128::from(d0).wrapping_sub((u128::from(n1) << 64) | u128::from(n0));
240        if neg_remainder > d {
241            0xffff_ffff_ffff_fffe_u64
242        } else {
243            0xffff_ffff_ffff_ffff_u64
244        }
245    } else {
246        // Compute quotient and remainder
247        let (mut q, mut r) = div_2x1_ref(n21, d1);
248
249        let t1 = u128::from(q) * u128::from(d0);
250        let t2 = (u128::from(n0) << 64) | u128::from(r);
251        if t1 > t2 {
252            q -= 1;
253            r = r.wrapping_add(d1);
254            let overflow = r < d1;
255            if !overflow {
256                let t1 = u128::from(q) * u128::from(d0);
257                let t2 = (u128::from(n0) << 64) | u128::from(r);
258                if t1 > t2 {
259                    q -= 1;
260                    // UNUSED: r += d[1];
261                }
262            }
263        }
264        q
265    }
266}
267
268/// ⚠️ Computes the quotient of a 192 bits divided by a normalized u128.
269#[doc = crate::algorithms::unstable_warning!()]
270/// Implements [MG10] algorithm 5.
271///
272/// [MG10]: https://gmplib.org/~tege/division-paper.pdf
273#[inline(always)]
274#[must_use]
275pub fn div_3x2_mg10(u21: u128, u0: u64, d: u128, v: u64) -> (u64, u128) {
276    debug_assert!(d >= (1 << 127));
277    debug_assert!(u21 < d);
278    debug_assert_eq!(v, reciprocal_2(d));
279
280    let q = u128::mul(u21.high(), v) + u21;
281    let r1 = u21.low().wrapping_sub(q.high().wrapping_mul(d.high()));
282    let t = u128::mul(d.low(), q.high());
283    let mut r = u128::join(r1, u0).wrapping_sub(t).wrapping_sub(d);
284    let mut q1 = q.high().wrapping_add(1);
285    if r.high() >= q.low() {
286        q1 = q1.wrapping_sub(1);
287        r = r.wrapping_add(d);
288    }
289    if unlikely(r >= d) {
290        q1 = q1.wrapping_add(1);
291        r = r.wrapping_sub(d);
292    }
293    (q1, r)
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299    use crate::algorithms::addmul;
300    use proptest::{
301        collection,
302        num::{u128, u64},
303        prop_assume, proptest,
304        strategy::Strategy,
305    };
306
307    #[test]
308    fn test_div_2x1_mg10() {
309        proptest!(|(q: u64, r: u64, mut d: u64)| {
310            let d = d | (1 << 63);
311            let r = r % d;
312            let n = u128::from(q) * u128::from(d) + u128::from(r);
313            let v = reciprocal(d);
314            assert_eq!(div_2x1_mg10(n, d, v), (q,r));
315        });
316    }
317
318    #[ignore = "TODO"]
319    #[test]
320    fn test_div_3x2_ref() {
321        proptest!(|(q: u64, r: u128, mut d: u128)| {
322            let d = d | (1 << 127);
323            let r = r % d;
324            let (n21, n0) = {
325                let d1 = (d >> 64) as u64;
326                let d0 = d as u64;
327                let r1 = (r >> 64) as u64;
328                let r0 = r as u64;
329                // n = q * d + r
330                let n10 = u128::from(q) * u128::from(d0) + u128::from(r0);
331                let n0 = n10 as u64;
332                let n21 = (n10 >> 64) + u128::from(q) * u128::from(d1) + u128::from(r1);
333                (n21, n0)
334            };
335            assert_eq!(div_3x2_ref(n21, n0, d), q);
336        });
337    }
338
339    #[test]
340    fn test_div_3x2_mg10() {
341        proptest!(|(q: u64, r: u128, mut d: u128)| {
342            let d = d | (1 << 127);
343            let r = r % d;
344            let (n21, n0) = {
345                let d1 = (d >> 64) as u64;
346                let d0 = d as u64;
347                let r1 = (r >> 64) as u64;
348                let r0 = r as u64;
349                // n = q * d + r
350                let n10 = u128::from(q) * u128::from(d0) + u128::from(r0);
351                let n0 = n10 as u64;
352                let n21 = (n10 >> 64) + u128::from(q) * u128::from(d1) + u128::from(r1);
353                (n21, n0)
354            };
355            let v = reciprocal_2(d);
356            assert_eq!(div_3x2_mg10(n21, n0, d, v), (q, r));
357        });
358    }
359
360    #[test]
361    fn test_div_nx1_normalized() {
362        let any_vec = collection::vec(u64::ANY, ..10);
363        proptest!(|(quotient in any_vec, mut divisor: u64, mut remainder: u64)| {
364            // Construct problem
365            divisor |= 1 << 63;
366            remainder %= divisor;
367            let mut numerator = vec![0; quotient.len() + 1];
368            numerator[0] = remainder;
369            addmul(&mut numerator, &quotient, &[divisor]);
370
371            // Test
372            let r = div_nx1_normalized(&mut numerator, divisor);
373            assert_eq!(&numerator[..quotient.len()], &quotient);
374            assert_eq!(r, remainder);
375        });
376    }
377
378    #[test]
379    fn test_div_nx1() {
380        let any_vec = collection::vec(u64::ANY, 1..10);
381        let divrem = (1..u64::MAX, u64::ANY).prop_map(|(d, r)| (d, r % d));
382        proptest!(|(quotient in any_vec,(divisor, remainder) in divrem)| {
383            // Construct problem
384            let mut numerator = vec![0; quotient.len() + 1];
385            numerator[0] = remainder;
386            addmul( &mut numerator, &quotient, &[divisor]);
387
388            // Trim numerator
389            while numerator.last() == Some(&0) {
390                numerator.pop();
391            }
392            prop_assume!(!numerator.is_empty());
393
394            // Test
395            let r = div_nx1(&mut numerator, divisor);
396            assert_eq!(&numerator[..quotient.len()], &quotient);
397            assert_eq!(r, remainder);
398        });
399    }
400
401    #[test]
402    fn test_div_nx2_normalized() {
403        let any_vec = collection::vec(u64::ANY, ..10);
404        let divrem = (1_u128 << 127.., u128::ANY).prop_map(|(d, r)| (d, r % d));
405        proptest!(|(quotient in any_vec, (divisor, remainder) in divrem)| {
406            // Construct problem
407            let mut numerator = vec![0; quotient.len() + 2];
408            numerator[0] = remainder.low();
409            numerator[1] = remainder.high();
410            addmul(&mut numerator, &quotient, &[divisor.low(), divisor.high()]);
411
412            // Test
413            let r = div_nx2_normalized(&mut numerator, divisor);
414            assert_eq!(&numerator[..quotient.len()], &quotient);
415            assert_eq!(r, remainder);
416        });
417    }
418
419    #[test]
420    fn test_div_nx2() {
421        let any_vec = collection::vec(u64::ANY, 2..10);
422        let divrem = (1..u128::MAX, u128::ANY).prop_map(|(d, r)| (d, r % d));
423        proptest!(|(quotient in any_vec,(divisor, remainder) in divrem)| {
424            // Construct problem
425            let mut numerator = vec![0; quotient.len() + 2];
426            numerator[0] = remainder.low();
427            numerator[1] = remainder.high();
428            addmul(&mut numerator, &quotient, &[divisor.low(), divisor.high()]);
429
430            // Trim numerator
431            while numerator.last() == Some(&0) {
432                numerator.pop();
433            }
434            prop_assume!(!numerator.is_empty());
435
436            // Test
437            let r = div_nx2(&mut numerator, divisor);
438            assert_eq!(&numerator[..quotient.len()], &quotient);
439            assert_eq!(r, remainder);
440        });
441    }
442}