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}