awint_core/logic/
div.rs

1use awint_internals::*;
2use const_fn::const_fn;
3
4use crate::Bits;
5
6/// # Division
7///
8/// These operations are not inplace unlike many other functions in this crate,
9/// because extra mutable space is needed in order to avoid allocation.
10///
11/// Note that signed divisions can overflow when `duo.is_imin()` and
12/// `div.is_umax()` (negative one in signed interpretation). The overflow
13/// results in `quo.is_imin()` and `rem.is_zero()`.
14///
15/// Note about terminology: we like short three letter shorthands, but run into
16/// a problem where the first three letters of "divide", "dividend", and
17/// "divisor" all clash with each other. Additionally, the standard Rust
18/// terminology for a function returning a quotient is things such as
19/// `i64::wrapping_div`, which should have been named `i64::wrapping_quo`
20/// instead. Here, we choose to type out "divide" in full whenever the operation
21/// involves both quotients and remainders. We don't use "num" or "den", because
22/// it may cause confusion later if an `awint` crate gains rational number
23/// capabilities. We use "quo" for quotient and "rem" for remainder. We use
24/// "div" for divisor. That still leaves a name clash with dividend, so we
25/// choose to use the shorthand "duo". This originates from the fact that for
26/// inplace division operations (which this crate does not have for performance
27/// purposes and avoiding allocation), the dividend is often subtracted from in
28/// the internal algorithms until it becomes the remainder, so that it serves
29/// two purposes.
30impl Bits {
31    /// Unsigned-divides `self` by `div`, sets `self` to the quotient, and
32    /// returns the remainder. Returns `None` if `div == 0`.
33    #[const_fn(cfg(feature = "const_support"))]
34    #[must_use]
35    pub const fn digit_udivide_inplace_(&mut self, div: Digit) -> Option<Digit> {
36        if div == 0 {
37            return None
38        }
39        // Note: we cannot have a carry-in because the quotient could otherwise
40        // overflow; `rem` needs to start as 0 and it would cause signature problems
41        // anyway.
42        let mut rem = 0;
43        const_for!(i in {0..self.total_digits()}.rev() {
44            // Safety: we checked that `self.bw() == duo.bw()`
45            let y = unsafe {self.get_unchecked(i)};
46            let tmp = dd_division((y, rem), (div, 0));
47            rem = tmp.1.0;
48            *unsafe {self.get_unchecked_mut(i)} = tmp.0.0;
49        });
50        Some(rem)
51    }
52
53    // Unsigned-divides `duo` by `div`, sets `self` to the quotient, and
54    // returns the remainder. Returns `None` if `self.bw() != duo.bw()` or
55    // `div == 0`.
56    #[const_fn(cfg(feature = "const_support"))]
57    #[must_use]
58    pub const fn digit_udivide_(&mut self, duo: &Self, div: Digit) -> Option<Digit> {
59        if div == 0 || self.bw() != duo.bw() {
60            return None
61        }
62        let mut rem = 0;
63        const_for!(i in {0..self.total_digits()}.rev() {
64            // Safety: we checked that `self.bw() == duo.bw()`
65            let y = unsafe {duo.get_unchecked(i)};
66            let tmp = dd_division((y, rem), (div, 0));
67            rem = tmp.1.0;
68            *unsafe {self.get_unchecked_mut(i)} = tmp.0.0;
69        });
70        Some(rem)
71    }
72
73    /// This function is factored out to keep code size  of the division
74    /// functions from exploding
75    ///
76    /// # Safety
77    ///
78    /// Assumptions: The bitwidths all match, `div.lz() > duo.lz()`, `div.lz() -
79    /// duo.lz() < BITS`, and there are is at least `BITS * 2` bits worth of
80    /// significant bits in `duo`.
81    #[const_fn(cfg(feature = "const_support"))]
82    pub(crate) const unsafe fn two_possibility_algorithm(
83        quo: &mut Self,
84        rem: &mut Self,
85        duo: &Self,
86        div: &Self,
87    ) {
88        debug_assert!(div.lz() > duo.lz());
89        debug_assert!((div.lz() - duo.lz()) < BITS);
90        debug_assert!((duo.sig()) >= (BITS * 2));
91        let i = duo.sig() - (BITS * 2);
92        let duo_sig_dd = duo.get_double_digit(i);
93        let div_sig_dd = div.get_double_digit(i);
94        // Because `lz_diff < BITS`, the quotient will fit in one `Digit`
95        let mut small_quo = dd_division(duo_sig_dd, div_sig_dd).0 .0;
96        // using `rem` as a temporary
97        rem.copy_(div).unwrap();
98        let uof = rem.digit_cin_mul_(0, small_quo);
99        rem.rsb_(duo).unwrap();
100        if (uof != 0) || rem.msb() {
101            rem.add_(div).unwrap();
102            small_quo -= 1;
103        }
104        quo.digit_(small_quo);
105    }
106
107    /// Unsigned-divides `duo` by `div` and assigns the quotient to `quo` and
108    /// remainder to `rem`. Returns `None` if any bitwidths are not equal or
109    /// `div.is_zero()`.
110    #[const_fn(cfg(feature = "const_support"))]
111    #[must_use]
112    pub const fn udivide(quo: &mut Self, rem: &mut Self, duo: &Self, div: &Self) -> Option<()> {
113        // prevent any potential problems with the assumptions that many subroutines
114        // make
115        quo.assert_cleared_unused_bits();
116        rem.assert_cleared_unused_bits();
117        duo.assert_cleared_unused_bits();
118        div.assert_cleared_unused_bits();
119        let w = quo.bw();
120        if div.is_zero() || w != rem.bw() || w != duo.bw() || w != div.bw() {
121            return None
122        }
123        // This is a version of the "trifecta" division algorithm adapted for bigints.
124        // See https://github.com/AaronKutch/specialized-div-rem for better documentation.
125
126        let len = quo.total_digits();
127        let mut duo_lz = duo.lz();
128        let div_lz = div.lz();
129
130        // quotient is 0 or 1 branch
131        if div_lz <= duo_lz {
132            if duo.uge(div).unwrap() {
133                quo.uone_();
134                rem.copy_(duo).unwrap();
135                rem.sub_(div).unwrap();
136            } else {
137                quo.zero_();
138                rem.copy_(duo).unwrap();
139            }
140            return Some(())
141        }
142
143        // small division branch
144        if (w - duo_lz) <= BITS {
145            let tmp_duo = duo.to_digit();
146            let tmp_div = div.to_digit();
147            quo.digit_(tmp_duo.wrapping_div(tmp_div));
148            rem.digit_(tmp_duo.wrapping_rem(tmp_div));
149            return Some(())
150        }
151
152        // double digit division branch. This is needed or else some branches below
153        // cannot rely on there being at least two digits of significant bits.
154        if (w - duo_lz) <= BITS * 2 {
155            unsafe {
156                let tmp = dd_division(
157                    (duo.first(), duo.get_unchecked(1)),
158                    (div.first(), div.get_unchecked(1)),
159                );
160                // using `digit_` to make sure other digits are zeroed
161                quo.digit_(tmp.0 .0);
162                *quo.get_unchecked_mut(1) = tmp.0 .1;
163                rem.digit_(tmp.1 .0);
164                *rem.get_unchecked_mut(1) = tmp.1 .1;
165            }
166            return Some(())
167        }
168
169        // TODO optimize for `trailing_zeros`. This function needs optimization in
170        // general such as using subslices more aggressively, need internal functions
171        // that handle differing lengths.
172
173        // short division branch
174        if w - div_lz <= BITS {
175            let tmp = quo.digit_udivide_(duo, div.to_digit()).unwrap();
176            rem.digit_(tmp);
177            return Some(())
178        }
179
180        // Two possibility division algorithm branch
181        let lz_diff = div_lz - duo_lz;
182        if lz_diff < BITS {
183            // Safety: we checked for bitwidth equality, the quotient is 0 or 1 branch makes
184            // sure `div.lz() > duo.lz()`, we just checked that `lz_diff < BITS`, and the
185            // double digit division branch makes sure there are at least `BITS*2`
186            // significant bits
187            unsafe {
188                Bits::two_possibility_algorithm(quo, rem, duo, div);
189            }
190            return Some(())
191        }
192
193        // We make a slight deviation from the original trifecta algorithm: instead of
194        // finding the quotient partial by dividing the `BITS*2` msbs of `duo` by the
195        // `BITS` msbs of `div`, we use `BITS*2 - 1` msbs of `duo` and `BITS` msbs of
196        // `div`. This is because the original partial could require `BITS + 1`
197        // significant bits (consider (2^(BITS*2) - 1) / (2^(BITS - 1) + 1)). This is
198        // possible to handle, but causes more problems than it is worth as seen from an
199        // earlier implementation in the `apint` crate.
200
201        let div_extra = w - div_lz - BITS;
202        let div_sig_d = div.get_digit(div_extra);
203        let div_sig_d_add1 = widen_add(div_sig_d, 1, 0);
204        quo.zero_();
205        // use `rem` as "duo" from now on
206        rem.copy_(duo).unwrap();
207        loop {
208            let duo_extra = w - duo_lz - (BITS * 2) + 1;
209            // using `<` instead of `<=` because of the change to `duo_extra`
210            if div_extra < duo_extra {
211                // Undersubtracting long division step
212
213                // `get_dd_unchecked` will not work, e.x. w = 192 and duo_lz = 0, it will
214                // attempt to access an imaginary zero bit beyond the bitwidth
215                let duo_sig_dd = unsafe {
216                    let digits = digits_u(duo_extra);
217                    let bits = extra_u(duo_extra);
218                    if bits == 0 {
219                        (rem.get_unchecked(digits), rem.get_unchecked(digits + 1))
220                    } else {
221                        let mid = rem.get_unchecked(digits + 1);
222                        let last = if digits + 2 == len {
223                            0
224                        } else {
225                            rem.get_unchecked(digits + 2)
226                        };
227                        (
228                            (rem.get_unchecked(digits) >> bits) | (mid << (BITS - bits)),
229                            (mid >> bits) | (last << (BITS - bits)),
230                        )
231                    }
232                };
233                let quo_part = dd_division(duo_sig_dd, div_sig_d_add1).0 .0;
234                let extra_shl = duo_extra - div_extra;
235                let shl_bits = extra_u(extra_shl);
236                let shl_digits = digits_u(extra_shl);
237
238                // Addition of `quo_part << extra_shl` to the quotient.
239                let (carry, next) = unsafe {
240                    if shl_bits == 0 {
241                        let tmp = widen_add(quo.get_unchecked(shl_digits), quo_part, 0);
242                        *quo.get_unchecked_mut(shl_digits) = tmp.0;
243                        (tmp.1 != 0, shl_digits + 1)
244                    } else {
245                        let tmp0 =
246                            widen_add(quo.get_unchecked(shl_digits), quo_part << shl_bits, 0);
247                        *quo.get_unchecked_mut(shl_digits) = tmp0.0;
248                        let tmp1 = widen_add(
249                            quo.get_unchecked(shl_digits + 1),
250                            quo_part >> (BITS - shl_bits),
251                            tmp0.1,
252                        );
253                        *quo.get_unchecked_mut(shl_digits + 1) = tmp1.0;
254                        (tmp1.1 != 0, shl_digits + 2)
255                    }
256                };
257                unsafe {
258                    subdigits_mut!(quo, { next..len }, subquo, {
259                        subquo.inc_(carry);
260                    });
261                }
262
263                // Subtraction of `(div * quo_part) << extra_shl` from duo. Requires three
264                // different carries.
265
266                // carry for bits that wrap across digit boundaries when `<< shl_bits` is
267                // applied
268                let mut wrap_carry = 0;
269                // the multiplication carry
270                let mut mul_carry = 0;
271                // subtraction carry starting with the two's complement increment
272                let mut add_carry = 1;
273                unsafe {
274                    if shl_bits == 0 {
275                        // subdigit shift unneeded
276                        const_for!(i in {shl_digits..len} {
277                            let tmp1 = widen_mul_add(
278                                div.get_unchecked(i - shl_digits),
279                                quo_part,
280                                mul_carry
281                            );
282                            mul_carry = tmp1.1;
283                            // notice that `tmp1` is inverted for subtraction
284                            let tmp2 = widen_add(!tmp1.0, rem.get_unchecked(i), add_carry);
285                            add_carry = tmp2.1;
286                            *rem.get_unchecked_mut(i) = tmp2.0;
287                        });
288                    } else {
289                        const_for!(i in {shl_digits..len} {
290                            let tmp0 = wrap_carry | (div.get_unchecked(i - shl_digits) << shl_bits);
291                            wrap_carry = div.get_unchecked(i - shl_digits) >> (BITS - shl_bits);
292                            let tmp1 = widen_mul_add(tmp0, quo_part, mul_carry);
293                            mul_carry = tmp1.1;
294                            let tmp2 = widen_add(!tmp1.0, rem.get_unchecked(i), add_carry);
295                            add_carry = tmp2.1;
296                            *rem.get_unchecked_mut(i) = tmp2.0;
297                        });
298                    }
299                }
300            } else {
301                // Two possibility algorithm
302                let i = w - duo_lz - (BITS * 2);
303                let duo_sig_dd = rem.get_double_digit(i);
304                let div_sig_dd = div.get_double_digit(i);
305                // Because `lz_diff < BITS`, the quotient will fit in one `Digit`
306                let mut small_quo = dd_division(duo_sig_dd, div_sig_dd).0 .0;
307                // subtract `div*small_quo` from `rem` inplace
308                let mut mul_carry = 0;
309                let mut add_carry = 1;
310                unsafe {
311                    const_for!(i in {0..len} {
312                        let tmp0 = widen_mul_add(div.get_unchecked(i), small_quo, mul_carry);
313                        mul_carry = tmp0.1;
314                        let tmp1 = widen_add(!tmp0.0, rem.get_unchecked(i), add_carry);
315                        add_carry = tmp1.1;
316                        *rem.get_unchecked_mut(i) = tmp1.0;
317                    });
318                }
319                if rem.msb() {
320                    rem.add_(div).unwrap();
321                    small_quo -= 1;
322                }
323                // add `quo_add` to `quo`
324                let tmp = widen_add(quo.first(), small_quo, 0);
325                *quo.first_mut() = tmp.0;
326                unsafe {
327                    subdigits_mut!(quo, { 1..len }, subquo, {
328                        subquo.inc_(tmp.1 != 0);
329                    });
330                }
331                return Some(())
332            }
333
334            duo_lz = rem.lz();
335
336            if div_lz <= duo_lz {
337                // quotient can have 0 or 1 added to it
338                if div_lz == duo_lz && div.ule(rem).unwrap() {
339                    quo.inc_(true);
340                    rem.sub_(div).unwrap();
341                }
342                return Some(())
343            }
344
345            // duo fits in two digits. Only possible if `div` fits into two digits, but it
346            // is not worth it to unroll further
347            if (w - duo_lz) <= (BITS * 2) {
348                unsafe {
349                    let tmp = dd_division(
350                        (rem.first(), rem.get_unchecked(1)),
351                        (div.first(), div.get_unchecked(1)),
352                    );
353                    let tmp0 = widen_add(quo.first(), tmp.0 .0, 0);
354
355                    *quo.first_mut() = tmp0.0;
356                    // tmp.0.1 is zero, just handle the carry now
357                    subdigits_mut!(quo, { 1..len }, subquo, {
358                        subquo.inc_(tmp0.1 != 0);
359                    });
360                    // using `digit_` to make sure other digits are zeroed
361                    rem.digit_(tmp.1 .0);
362                    *rem.get_unchecked_mut(1) = tmp.1 .1;
363                }
364                return Some(())
365            }
366        }
367    }
368
369    /// Signed-divides `duo` by `div` and assigns the quotient to `quo` and
370    /// remainder to `rem`. Returns `None` if any bitwidths are not equal or
371    /// `div.is_zero()`. `duo` and `div` are marked mutable but their values are
372    /// not changed by this function.
373    #[const_fn(cfg(feature = "const_support"))]
374    #[must_use]
375    pub const fn idivide(
376        quo: &mut Self,
377        rem: &mut Self,
378        duo: &mut Self,
379        div: &mut Self,
380    ) -> Option<()> {
381        let w = quo.bw();
382        if div.is_zero() || w != rem.bw() || w != duo.bw() || w != div.bw() {
383            return None
384        }
385        let duo_msb = duo.msb();
386        let div_msb = div.msb();
387        duo.neg_(duo_msb);
388        div.neg_(div_msb);
389        Bits::udivide(quo, rem, duo, div).unwrap();
390        duo.neg_(duo_msb);
391        rem.neg_(duo_msb);
392        div.neg_(div_msb);
393        quo.neg_(duo_msb != div_msb);
394        Some(())
395    }
396}