ruint/algorithms/div/
knuth.rs

1//! Knuth division
2
3use super::{reciprocal::reciprocal_2, small::div_3x2, DoubleWord};
4use crate::{
5    algorithms::{add::carrying_add_n, mul::submul_nx1},
6    utils::{likely, unlikely, UncheckedSlice},
7};
8
9/// ⚠️ In-place Knuth normalized long division with reciprocals.
10#[doc = crate::algorithms::unstable_warning!()]
11/// # Conditions of Use
12///
13/// * The highest (most-significant) bit of the divisor MUST be set.
14/// * The `divisor` and `numerator` MUST each be at least two limbs.
15/// * `numerator` MUST contain at at least as many elements as `divisor`.
16///
17/// # Panics
18///
19/// May panic if any condition of use is violated.
20#[inline(always)]
21#[allow(clippy::many_single_char_names)]
22pub fn div_nxm_normalized(numerator: &mut [u64], divisor: &[u64]) {
23    debug_assert!(divisor.len() >= 2);
24    debug_assert!(numerator.len() >= divisor.len());
25    debug_assert!(*divisor.last().unwrap() >= (1 << 63));
26
27    let numerator = UncheckedSlice::wrap_mut(numerator);
28    let divisor = UncheckedSlice::wrap(divisor);
29
30    let n = divisor.len();
31    let m = numerator.len() - n - 1;
32
33    // Compute the divisor double limb and reciprocal
34    let d = u128::join(divisor[n - 1], divisor[n - 2]);
35    let v = reciprocal_2(d);
36
37    // Compute the quotient one limb at a time.
38    for j in (0..=m).rev() {
39        // Fetch the first three limbs of the numerator.
40        let n21 = u128::join(numerator[j + n], numerator[j + n - 1]);
41        let n0 = numerator[j + n - 2];
42        debug_assert!(n21 <= d);
43
44        // Overflow case
45        if unlikely(n21 == d) {
46            let q = u64::MAX;
47            let _carry = submul_nx1(&mut numerator[j..j + n], divisor, q);
48            numerator[j + n] = q;
49            continue;
50        }
51
52        // Calculate 3x2 approximate quotient word.
53        // By using 3x2 limbs we get a quotient that is very likely correct
54        // and at most one too large. In the process we also get the first
55        // two remainder limbs.
56        let (mut q, r) = div_3x2(n21, n0, d, v);
57
58        // Subtract the quotient times the divisor from the remainder.
59        // We already have the highest two limbs, so we can reduce the
60        // computation. We still need to carry propagate into these limbs.
61        let borrow = submul_nx1(&mut numerator[j..j + n - 2], &divisor[..n - 2], q);
62        let (r, borrow) = r.overflowing_sub(u128::from(borrow));
63        numerator[j + n - 2] = r.low();
64        numerator[j + n - 1] = r.high();
65
66        // If we have a carry then the quotient was one too large.
67        // We correct by decrementing the quotient and adding one divisor back.
68        if unlikely(borrow) {
69            q = q.wrapping_sub(1);
70            let carry = carrying_add_n(&mut numerator[j..j + n], &divisor[..n], false);
71            // Expect carry because we flip sign back to positive.
72            debug_assert!(carry);
73        }
74
75        // Store quotient in the unused bits of numerator
76        numerator[j + n] = q;
77    }
78}
79
80/// ⚠️ In-place Knuth long division with implicit normalization and reciprocals.
81#[doc = crate::algorithms::unstable_warning!()]
82/// # Conditions of use:
83///
84/// * `divisor` MUST NOT be empty.
85/// * The highest (most-significant) limb of `divisor` MUST be non-zero.
86/// * `divisor` and `numerator` MUST contain at least three limbs.
87/// * `numerator` MUST contain at at least as many elements as `divisor`.
88///
89/// # Panics
90///
91/// May panic if any condition of use is violated.
92#[inline(always)]
93#[allow(clippy::many_single_char_names)]
94pub fn div_nxm(numerator: &mut [u64], divisor: &mut [u64]) {
95    debug_assert!(divisor.len() >= 3);
96    assume!(numerator.len() >= divisor.len()); // Elides the check in `copy_within`.
97    debug_assert!(*divisor.last().unwrap() >= 1);
98
99    let numerator = UncheckedSlice::wrap_mut(numerator);
100    let divisor = UncheckedSlice::wrap_mut(divisor);
101
102    let n = divisor.len();
103    let m = numerator.len() - n;
104
105    // Compute normalized divisor double-word and reciprocal.
106    // TODO: Delegate to div_nxm_normalized if normalized.
107    let (d, shift) = {
108        let d = u128::join(divisor[n - 1], divisor[n - 2]);
109        let shift = d.high().leading_zeros();
110        (
111            if shift == 0 {
112                d
113            } else {
114                (d << shift) | u128::from(divisor[n - 3] >> (64 - shift))
115            },
116            shift,
117        )
118    };
119    debug_assert!(d >= 1 << 127);
120    let v = reciprocal_2(d);
121
122    // Compute the quotient one limb at a time.
123    let mut q_high = 0;
124    #[allow(clippy::range_plus_one)] // Inclusive ranges optimize worse.
125    for j in (0..m + 1).rev() {
126        // Fetch the first three limbs of the shifted numerator starting at `j + n`.
127        let (n21, n0) = {
128            let n2 = numerator.get(j + n).copied().unwrap_or_default();
129            let n21 = u128::join(n2, numerator[j + n - 1]);
130            let n0 = numerator[j + n - 2];
131            if shift == 0 {
132                (n21, n0)
133            } else {
134                (
135                    (n21 << shift) | u128::from(n0 >> (64 - shift)),
136                    (n0 << shift) | (numerator[j + n - 3] >> (64 - shift)),
137                )
138            }
139        };
140        debug_assert!(n21 <= d);
141
142        // Compute the quotient
143        let q = if likely(n21 < d) {
144            // Calculate 3x2 approximate quotient word.
145            // By using 3x2 limbs we get a quotient that is very likely correct
146            // and at most one too large. In the process we also get the first
147            // two remainder limbs.
148            let (mut q, r) = div_3x2(n21, n0, d, v);
149
150            if q != 0 {
151                // Subtract the quotient times the divisor from the remainder.
152                // We already have the highest 128 bit, so we can reduce the
153                // computation. We still need to carry propagate into these limbs.
154                let borrow = if shift == 0 {
155                    let borrow = submul_nx1(&mut numerator[j..j + n - 2], &divisor[..n - 2], q);
156                    let (r, borrow) = r.overflowing_sub(u128::from(borrow));
157                    numerator[j + n - 2] = r.low();
158                    numerator[j + n - 1] = r.high();
159                    borrow
160                } else {
161                    // OPT: Can we re-use `r` here somehow? The problem is we can not just
162                    // shift the `r` or `borrow` because we need to accurately reproduce
163                    // the remainder and carry in the middle of a limb.
164                    let borrow = submul_nx1(&mut numerator[j..j + n], divisor, q);
165                    let n2 = numerator.get(j + n).copied().unwrap_or_default();
166                    borrow != n2
167                };
168
169                // If we have a carry then the quotient was one too large.
170                // We correct by decrementing the quotient and adding one divisor back.
171                if unlikely(borrow) {
172                    q = q.wrapping_sub(1);
173                    let carry = carrying_add_n(&mut numerator[j..j + n], &divisor[..n], false);
174                    // Expect carry because we flip sign back to positive.
175                    debug_assert!(carry);
176                }
177            }
178            q
179        } else {
180            // Overflow case
181            let q = u64::MAX;
182            let _carry = submul_nx1(&mut numerator[j..j + n], divisor, q);
183            q
184        };
185
186        // Store the quotient in the processed bits of numerator plus `q_high`.
187        if j + n < numerator.len() {
188            numerator[j + n] = q;
189        } else {
190            q_high = q;
191        }
192    }
193
194    // Copy remainder to `divisor` and `quotient` to numerator.
195    divisor.copy_from_slice(&numerator[..n]);
196    numerator.copy_within(n.., 0);
197    numerator[m] = q_high;
198    numerator[m + 1..].fill(0);
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204    use crate::algorithms::{addmul, borrowing_sub_n, cmp};
205    use core::cmp::Ordering;
206    use proptest::{
207        collection, num, proptest,
208        strategy::{Just, Strategy},
209    };
210
211    #[allow(unused_imports)]
212    use alloc::vec::Vec;
213
214    // Basic test without exceptional paths
215    #[test]
216    fn test_divrem_8by4() {
217        let mut numerator = [
218            0x3000000000000000,
219            0xd4e15e75fd4e6516,
220            0x593a70aa5daf127b,
221            0xff0a216ae9c215f1,
222            0xa78c7ad6fea10429,
223            0x18276b093f5d1dac,
224            0xfe2e0bccb9e6d8b3,
225            0x1bebfb3bc05d9347,
226        ];
227        let divisor = [
228            0x800000000000000,
229            0x580c0c40583c55b5,
230            0x6b16b3fb5bd85ed3,
231            0xcc958c207ce3c96f,
232        ];
233        let expected_quotient = [
234            0x9128464e61d6b5b3_u64,
235            0xd9eea4fc30c5ac6c_u64,
236            0x944a2d832d5a6a08_u64,
237            0x22f06722e8d883b1_u64,
238        ];
239        let expected_remainder = [
240            0x9800000000000000,
241            0x70efd2d3f528c8d9,
242            0x6dad759fcd6af14a,
243            0x5fe38801c609f277,
244        ];
245        div_nxm_normalized(&mut numerator, &divisor);
246        let (remainder, quotient) = numerator.split_at(divisor.len());
247        assert_eq!(remainder, expected_remainder);
248        assert_eq!(quotient, expected_quotient);
249    }
250
251    // Test case that forces the `unlikely(borrow)` branch.
252    #[test]
253    fn test_div_rollback() {
254        let mut numerator = [
255            0x1656178c14142000,
256            0x821415dfe9e81612,
257            0x1616561616161616,
258            0x96000016820016,
259        ];
260        let divisor = [0x1415dfe9e8161414, 0x1656161616161682, 0x9600001682001616];
261        let expected_quotient = [0xffffffffffffff];
262        let expected_remainder = [0x166bf775fc2a3414, 0x1656161616161680, 0x9600001682001616];
263        div_nxm_normalized(&mut numerator, &divisor);
264        let (remainder, quotient) = numerator.split_at(divisor.len());
265        assert_eq!(remainder, expected_remainder);
266        assert_eq!(quotient, expected_quotient);
267    }
268
269    // Test case that forces the `unlikely(borrow)` branch.
270    #[test]
271    fn test_div_rollback_2() {
272        let mut numerator = [
273            0x100100000,
274            0x81000,
275            0x1000000000000000,
276            0x0,
277            0x0,
278            0xfffff00000000000,
279            0xffffffffffffffff,
280            0xdfffffffffffff,
281        ];
282        let divisor = [
283            0xfffffffffff00000,
284            0xffffffffffffffff,
285            0xfffffffffffff3ff,
286            0xffffffffffffffff,
287            0xdfffffffffffffff,
288        ];
289        let expected_quotient = [0xffffedb6db6db6e9, 0xffffffffffffffff, 0xffffffffffffff];
290        let expected_remainder = [
291            0xdb6db6dc6ea00000,
292            0x80ffe,
293            0xf2492492492ec00,
294            0x1000,
295            0x2000000000000000,
296        ];
297        div_nxm_normalized(&mut numerator, &divisor);
298        let (remainder, quotient) = numerator.split_at(divisor.len());
299        assert_eq!(quotient, expected_quotient);
300        assert_eq!(remainder, expected_remainder);
301    }
302
303    #[test]
304    fn test_div_overflow() {
305        let mut numerator = [0xb200000000000002, 0x1, 0x0, 0xfdffffff00000000];
306        let divisor = [0x10002, 0x0, 0xfdffffff00000000];
307        let expected_quotient = [0xffffffffffffffff];
308        let expected_remainder = [0xb200000000010004, 0xfffffffffffeffff, 0xfdfffffeffffffff];
309        div_nxm_normalized(&mut numerator, &divisor);
310        let (remainder, quotient) = numerator.split_at(divisor.len());
311        assert_eq!(quotient, expected_quotient);
312        assert_eq!(remainder, expected_remainder);
313    }
314
315    // Proptest without forced exceptional paths
316    #[test]
317    fn test_div_nxm_normalized() {
318        let quotient = collection::vec(num::u64::ANY, 1..10);
319        let divisor = collection::vec(num::u64::ANY, 2..10).prop_map(|mut vec| {
320            *vec.last_mut().unwrap() |= 1 << 63;
321            vec
322        });
323        let dr = divisor.prop_flat_map(|divisor| {
324            let d = divisor.clone();
325            let remainder =
326                collection::vec(num::u64::ANY, divisor.len()).prop_map(move |mut vec| {
327                    if cmp(&vec, &d) != Ordering::Less {
328                        let borrow = borrowing_sub_n(&mut vec, &d, false);
329                        assert!(!borrow);
330                    }
331                    vec
332                });
333            (Just(divisor), remainder)
334        });
335        proptest!(|(quotient in quotient, (divisor, remainder) in dr)| {
336            let mut numerator: Vec<u64> = vec![0; divisor.len() + quotient.len()];
337            numerator[..remainder.len()].copy_from_slice(&remainder);
338            addmul(&mut numerator, quotient.as_slice(), divisor.as_slice());
339
340            div_nxm_normalized(numerator.as_mut_slice(), divisor.as_slice());
341            let (r, q) = numerator.split_at(divisor.len());
342            assert_eq!(r, remainder);
343            assert_eq!(q, quotient);
344        });
345    }
346
347    // Basic test without exceptional paths (with shift == 0)
348    #[test]
349    fn test_div_nxm_8by4_noshift() {
350        let mut numerator = [
351            0x3000000000000000,
352            0xd4e15e75fd4e6516,
353            0x593a70aa5daf127b,
354            0xff0a216ae9c215f1,
355            0xa78c7ad6fea10429,
356            0x18276b093f5d1dac,
357            0xfe2e0bccb9e6d8b3,
358            0x1bebfb3bc05d9347,
359        ];
360        let mut divisor = [
361            0x800000000000000,
362            0x580c0c40583c55b5,
363            0x6b16b3fb5bd85ed3,
364            0xcc958c207ce3c96f,
365        ];
366        let quotient = [
367            0x9128464e61d6b5b3,
368            0xd9eea4fc30c5ac6c,
369            0x944a2d832d5a6a08,
370            0x22f06722e8d883b1,
371        ];
372        let remainder = [
373            0x9800000000000000,
374            0x70efd2d3f528c8d9,
375            0x6dad759fcd6af14a,
376            0x5fe38801c609f277,
377        ];
378        div_nxm(&mut numerator, &mut divisor);
379        assert_eq!(&numerator[..quotient.len()], quotient);
380        assert_eq!(divisor, remainder);
381    }
382
383    // Basic test without exceptional paths (with shift > 0)
384    #[test]
385    fn test_div_nxm_8by4_shift() {
386        let mut numerator = [
387            0xc59c28364a491d22,
388            0x1ab240e2a2a91050,
389            0xe497baaf4e4b16cb,
390            0xd21643d231c590d6,
391            0xda918cd26803c7f1,
392            0xb445074f770b5261,
393            0x37aff2aa32059516,
394            0x3cf254c1,
395        ];
396        let mut divisor = [
397            0xc91e935375a97723,
398            0x86a9ded3044ec12b,
399            0xc7d2c4d3b53bff51,
400            0x6ef0530d,
401        ];
402        let quotient = [
403            0x456b1581ef1a759a,
404            0x88702c90bbe2ef3c,
405            0xff8492ee85dec642,
406            0x8ca39da4ca785f36,
407        ];
408        let remainder = [
409            0x82c3522848567314,
410            0xeaba6edb18db568e,
411            0x18f16cfde22dcefe,
412            0x11296685,
413        ];
414        div_nxm(&mut numerator, &mut divisor);
415        assert_eq!(&numerator[..quotient.len()], quotient);
416        assert_eq!(divisor, remainder);
417    }
418
419    // Basic test without exceptional paths (with q_high > 0)
420    #[test]
421    fn test_div_nxm_8by4_q_high() {
422        let mut numerator = [
423            0x39ea76324ed952cc,
424            0x89b7a0d30e2df1be,
425            0x7011596e8b3f301f,
426            0x11930a89eca68640,
427            0x36a34eca4f73d0e4,
428            0x86d53c52b1108c90,
429            0x6093338b7b667e03,
430        ];
431        let mut divisor = [
432            0x439702d44a8f62a4,
433            0x8dfa6ea7fc41f689,
434            0xc79723ff4dd060e0,
435            0x7d13e204,
436        ];
437        let quotient = [
438            0x181cecbb64efa48b,
439            0x1e97056793a15125,
440            0xe8145d63cd312d02,
441            0xc5a9aced,
442        ];
443        let remainder = [
444            0x682e10f8d0b1b3c0,
445            0xbf46c8b0e5ac8a62,
446            0x5abe292d53acf085,
447            0x699fc911,
448        ];
449        div_nxm(&mut numerator, &mut divisor);
450        assert_eq!(&numerator[..quotient.len()], quotient);
451        assert_eq!(divisor, remainder);
452    }
453
454    // Basic test with numerator and divisor the same size.
455    #[test]
456    fn test_div_nxm_4by4() {
457        let mut numerator = [
458            0x55a8f128f187ecee,
459            0xe059a1f3fe52e559,
460            0x570ab3b2aac5c5d9,
461            0xf7ea0c73b80ddca1,
462        ];
463        let mut divisor = [
464            0x6c8cd670adcae7da,
465            0x458d6428c7fd36d3,
466            0x4a73ad64cc703a1d,
467            0x33bf790f92ed51fe,
468        ];
469        let quotient = [0x4];
470        let remainder = [
471            0xa37597663a5c4d86,
472            0xca241150de5e0a0b,
473            0x2d3bfe1f7904dd64,
474            0x28ec28356c5894a8,
475        ];
476        div_nxm(&mut numerator, &mut divisor);
477        assert_eq!(&numerator[..quotient.len()], quotient);
478        assert_eq!(divisor, remainder);
479    }
480
481    #[test]
482    fn test_div_nxm_4by3() {
483        let mut numerator = [
484            0x8000000000000000,
485            0x8000000000000000,
486            0x8000000000000000,
487            0x8000000000000001,
488        ];
489        let mut divisor = [0x8000000000000000, 0x8000000000000000, 0x8000000000000000];
490        let quotient = [0x1, 0x1];
491        let remainder = [0x0, 0x8000000000000000, 0x7fffffffffffffff];
492        div_nxm(&mut numerator, &mut divisor);
493        assert_eq!(&numerator[..quotient.len()], quotient);
494        assert_eq!(divisor, remainder);
495    }
496
497    // Proptest without forced exceptional paths
498    #[test]
499    fn test_div_nxm() {
500        let quotient = collection::vec(num::u64::ANY, 1..10);
501        let divisor = collection::vec(num::u64::ANY, 3..10);
502        let dr = divisor.prop_flat_map(|divisor| {
503            let d = divisor.clone();
504            let remainder =
505                collection::vec(num::u64::ANY, divisor.len()).prop_map(move |mut vec| {
506                    *vec.last_mut().unwrap() %= d.last().unwrap();
507                    vec
508                });
509            (Just(divisor), remainder)
510        });
511        proptest!(|(quotient in quotient, (mut divisor, remainder) in dr)| {
512            let mut numerator: Vec<u64> = vec![0; divisor.len() + quotient.len()];
513            numerator[..remainder.len()].copy_from_slice(&remainder);
514            addmul(&mut numerator, quotient.as_slice(), divisor.as_slice());
515
516            div_nxm(numerator.as_mut_slice(), divisor.as_mut_slice());
517            assert_eq!(&numerator[..quotient.len()], quotient);
518            assert_eq!(divisor, remainder);
519            assert!(numerator[quotient.len()..].iter().all(|&x| x == 0));
520        });
521    }
522}