11use super :: addition:: __add2;
22use super :: shift:: biguint_shl;
3- use super :: { cmp_slice, ilog2 , BigUint } ;
3+ use super :: { cmp_slice, BigUint } ;
44
55use crate :: big_digit:: { self , BigDigit , BigDigits , DoubleBigDigit , BITS } ;
66use crate :: UsizePromotion ;
@@ -193,29 +193,36 @@ fn div_rem_cow(u: Cow<'_, BigUint>, d: Cow<'_, BigUint>) -> (BigUint, BigUint) {
193193 Greater => { } // Do nothing
194194 }
195195
196+ // Larger operands use the algorithm from Burnikel and Ziegler,
197+ // which has its own similar shift-normalization strategy.
198+ if ( u. data . len ( ) > BURNIKEL_ZIEGLER_THRESHOLD * 2 )
199+ && ( d. data . len ( ) > BURNIKEL_ZIEGLER_THRESHOLD )
200+ {
201+ return div_rem_burnikel_ziegler ( u, d) ;
202+ }
203+
196204 // This algorithm is from Knuth, TAOCP vol 2 section 4.3, algorithm D:
197205 //
198206 // First, normalize the arguments so the highest bit in the highest digit of the divisor is
199207 // set: the main loop uses the highest digit of the divisor for generating guesses, so we
200208 // want it to be the largest number we can efficiently divide by.
201209 //
202210 let shift = d. data . last ( ) . unwrap ( ) . leading_zeros ( ) ;
203-
204211 if shift == 0 {
205212 // no need to clone d
206- div_rem_core ( u, & d)
213+ div_rem_core ( u. into_owned ( ) , & d)
207214 } else {
208215 let u = biguint_shl ( u, shift) ;
209216 let d = biguint_shl ( d, shift) ;
210- let ( q, r) = div_rem_core ( Cow :: Owned ( u ) , & d) ;
217+ let ( q, r) = div_rem_core ( u , & d) ;
211218 // renormalize the remainder
212219 ( q, r >> shift)
213220 }
214221}
215222
216223const BURNIKEL_ZIEGLER_THRESHOLD : usize = 64 ;
217224
218- /// This algorithm is from Burnikel and Ziegler, "Fast Recursive Division", Algorithm 1 .
225+ /// This algorithm is from Burnikel and Ziegler, "Fast Recursive Division".
219226/// It is a recursive algorithm that divides the dividend and divisor into blocks of digits
220227/// and uses a divide-and-conquer approach to find the quotient.
221228///
@@ -224,7 +231,7 @@ const BURNIKEL_ZIEGLER_THRESHOLD: usize = 64;
224231/// Time complexity of this algorithm is the same as the algorithm used for the multiplication.
225232///
226233/// link: https://pure.mpg.de/rest/items/item_1819444_4/component/file_2599480/content
227- fn div_rem_burnikel_ziegler ( u : & BigUint , d : & BigUint ) -> ( BigUint , BigUint ) {
234+ fn div_rem_burnikel_ziegler ( a : Cow < ' _ , BigUint > , b : Cow < ' _ , BigUint > ) -> ( BigUint , BigUint ) {
228235 fn divide_biguint ( mut b : BigUint , level : usize ) -> ( BigUint , BigUint ) {
229236 if b. data . len ( ) <= level {
230237 return ( BigUint :: ZERO , b) ;
@@ -255,28 +262,13 @@ fn div_rem_burnikel_ziegler(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) {
255262 b : BigUint ,
256263 level : usize ,
257264 ) -> ( BigUint , BigUint ) {
258- // A precondition of this function is that q fits into a single digit.
265+ // The quotient must fit into a single digit, i.e. A < βⁿB .
259266 debug_assert ! ( ah < b) ;
267+ // The divisor must also have its MSB set, i.e. βⁿ/2 ≤ B < βⁿ.
268+ debug_assert_eq ! ( normalizing_shift_amount( & b, level) , 0 ) ;
260269 if level <= BURNIKEL_ZIEGLER_THRESHOLD {
261270 return div_rem ( concat_biguint ( & ah, al, level) , b) ;
262271 }
263- let shift = normalizing_shift_amount ( & b, level) ;
264- if shift != 0 {
265- let b = b << shift;
266- let ( ah, al) = divide_biguint ( concat_biguint ( & ah, al, level) << shift, level) ;
267- let ( q, r) = div_two_digit_by_one_normalized ( ah, al, b, level) ;
268- ( q, r >> shift)
269- } else {
270- div_two_digit_by_one_normalized ( ah, al, b, level)
271- }
272- }
273-
274- fn div_two_digit_by_one_normalized (
275- ah : BigUint ,
276- al : BigUint ,
277- b : BigUint ,
278- level : usize ,
279- ) -> ( BigUint , BigUint ) {
280272 let level = level / 2 ;
281273 let ( a1, a2) = divide_biguint ( ah, level) ;
282274 let ( a3, a4) = divide_biguint ( al, level) ;
@@ -325,28 +317,59 @@ fn div_rem_burnikel_ziegler(u: &BigUint, d: &BigUint) -> (BigUint, BigUint) {
325317 ( q, r - d)
326318 }
327319
328- let mut level = 1 << ilog2 ( u. data . len ( ) ) ;
329- if d. data . len ( ) > level {
330- level *= 2 ;
320+ fn div_blocks ( a : & BigUint , b : & BigUint , level : usize ) -> ( BigUint , BigUint ) {
321+ let mut a_blocks = a. data . chunks ( level) ;
322+
323+ // If the most-significant block is already less than b,
324+ // then it serves as the initial remainder.
325+ let mut r = BigUint :: ZERO ;
326+ if let Some ( ah) = a_blocks. clone ( ) . next_back ( ) {
327+ if cmp_slice ( ah, & b. data ) == Less {
328+ _ = a_blocks. next_back ( ) ;
329+ // NB: assuming a is normalized, then so is r
330+ r = BigUint {
331+ data : BigDigits :: from_slice ( ah) ,
332+ } ;
333+ }
334+ }
335+
336+ let mut q_data = vec ! [ 0 ; a_blocks. len( ) * level] ;
337+ let q_blocks = q_data. chunks_mut ( level) ;
338+
339+ // Iterate block-by-block like long division
340+ for ( q, ai) in q_blocks. zip ( a_blocks) . rev ( ) {
341+ let mut ai = BigUint {
342+ data : BigDigits :: from_slice ( ai) ,
343+ } ;
344+ ai. data . normalize ( ) ;
345+ let ( qi, ri) = div_two_digit_by_one ( r, ai, b. clone ( ) , level) ;
346+ q[ ..qi. data . len ( ) ] . copy_from_slice ( & qi. data ) ;
347+ r = ri;
348+ }
349+
350+ let mut q = BigUint {
351+ data : BigDigits :: from_vec ( q_data) ,
352+ } ;
353+ q. data . normalize ( ) ;
354+ ( q, r)
331355 }
332- let ( u1, u2) = divide_biguint ( u. clone ( ) , level) ;
333- if & u1 >= d {
334- div_two_digit_by_one ( BigUint :: ZERO , u. clone ( ) , d. clone ( ) , level * 2 )
356+
357+ // The block size is the minimum 2ᵏ that fits the divisor.
358+ let level = b. data . len ( ) . next_power_of_two ( ) ;
359+ let shift = normalizing_shift_amount ( & b, level) ;
360+ if shift == 0 {
361+ div_blocks ( & a, & b, level)
335362 } else {
336- div_two_digit_by_one ( u1, u2, d. clone ( ) , level)
363+ let a = biguint_shl ( a, shift) ;
364+ let b = biguint_shl ( b, shift) ;
365+ let ( q, r) = div_blocks ( & a, & b, level) ;
366+ ( q, r >> shift)
337367 }
338368}
339369
340370/// An implementation of the base division algorithm.
341371/// Knuth, TAOCP vol 2 section 4.3.1, algorithm D, with an improvement from exercises 19-21.
342- fn div_rem_core ( a : Cow < ' _ , BigUint > , b : & BigUint ) -> ( BigUint , BigUint ) {
343- if ( a. data . len ( ) > BURNIKEL_ZIEGLER_THRESHOLD * 2 )
344- && ( b. data . len ( ) > BURNIKEL_ZIEGLER_THRESHOLD )
345- {
346- return div_rem_burnikel_ziegler ( & a, b) ;
347- }
348-
349- let mut a = a. into_owned ( ) ;
372+ fn div_rem_core ( mut a : BigUint , b : & BigUint ) -> ( BigUint , BigUint ) {
350373 let b = & * b. data ;
351374 debug_assert ! ( a. data. len( ) >= b. len( ) && b. len( ) > 1 ) ;
352375 debug_assert ! ( b. last( ) . unwrap( ) . leading_zeros( ) == 0 ) ;
0 commit comments