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