Skip to main content

num_bigint/biguint/
convert.rs

1// This uses stdlib features higher than the MSRV
2#![allow(clippy::manual_range_contains)] // 1.35
3
4use super::{biguint_from_vec, fls, ilog2, BigUint, ToBigUint};
5
6use super::addition::add2;
7use super::division::{div_rem_digit, FAST_DIV_WIDE};
8use super::multiplication::mac_with_carry;
9
10use crate::big_digit::{self, BigDigit, BigDigits};
11use crate::ParseBigIntError;
12use crate::TryFromBigIntError;
13
14use alloc::vec::Vec;
15use core::cmp::Ordering::{Equal, Greater, Less};
16use core::convert::TryFrom;
17use core::str::FromStr;
18use num_integer::Integer;
19use num_traits::float::FloatCore;
20use num_traits::{FromPrimitive, Num, ToPrimitive, Zero};
21
22impl FromStr for BigUint {
23    type Err = ParseBigIntError;
24
25    #[inline]
26    fn from_str(s: &str) -> Result<BigUint, ParseBigIntError> {
27        BigUint::from_str_radix(s, 10)
28    }
29}
30
31// Convert from a power of two radix (bits == ilog2(radix)) where bits evenly divides
32// BigDigit::BITS
33pub(super) fn from_bitwise_digits_le(v: &[u8], bits: u8) -> BigUint {
34    debug_assert!(!v.is_empty() && bits <= 8 && big_digit::BITS % bits == 0);
35    debug_assert!(v.iter().all(|&c| BigDigit::from(c) < (1 << bits)));
36
37    let digits_per_big_digit = big_digit::BITS / bits;
38
39    let data = v
40        .chunks(digits_per_big_digit.into())
41        .map(|chunk| {
42            chunk
43                .iter()
44                .rev()
45                .fold(0, |acc, &c| (acc << bits) | BigDigit::from(c))
46        })
47        .collect();
48
49    biguint_from_vec(data)
50}
51
52// Convert from a power of two radix (bits == ilog2(radix)) where bits doesn't evenly divide
53// BigDigit::BITS
54fn from_inexact_bitwise_digits_le(v: &[u8], bits: u8) -> BigUint {
55    debug_assert!(!v.is_empty() && bits <= 8 && big_digit::BITS % bits != 0);
56    debug_assert!(v.iter().all(|&c| BigDigit::from(c) < (1 << bits)));
57
58    let total_bits = (v.len() as u64).saturating_mul(bits.into());
59    let big_digits = Integer::div_ceil(&total_bits, &big_digit::BITS.into())
60        .to_usize()
61        .unwrap_or(usize::MAX);
62    let mut data = Vec::with_capacity(big_digits);
63
64    let mut d = 0;
65    let mut dbits = 0; // number of bits we currently have in d
66
67    // walk v accumululating bits in d; whenever we accumulate big_digit::BITS in d, spit out a
68    // big_digit:
69    for &c in v {
70        d |= BigDigit::from(c) << dbits;
71        dbits += bits;
72
73        if dbits >= big_digit::BITS {
74            data.push(d);
75            dbits -= big_digit::BITS;
76            // if dbits was > big_digit::BITS, we dropped some of the bits in c (they couldn't fit
77            // in d) - grab the bits we lost here:
78            d = BigDigit::from(c) >> (bits - dbits);
79        }
80    }
81
82    if dbits > 0 {
83        debug_assert!(dbits < big_digit::BITS);
84        data.push(d as BigDigit);
85    }
86
87    biguint_from_vec(data)
88}
89
90// Read little-endian radix digits
91fn from_radix_digits_be(v: &[u8], radix: u32) -> BigUint {
92    debug_assert!(!v.is_empty() && !radix.is_power_of_two());
93    debug_assert!(v.iter().all(|&c| u32::from(c) < radix));
94
95    // Estimate how big the result will be, so we can pre-allocate it.
96    #[cfg(feature = "std")]
97    let big_digits = {
98        let radix_log2 = f64::from(radix).log2();
99        let bits = radix_log2 * v.len() as f64;
100        (bits / big_digit::BITS as f64).ceil()
101    };
102    #[cfg(not(feature = "std"))]
103    let big_digits = {
104        let radix_log2 = ilog2(radix.next_power_of_two()) as usize;
105        let bits = radix_log2 * v.len();
106        (bits / big_digit::BITS as usize) + 1
107    };
108
109    let mut data = Vec::with_capacity(big_digits.to_usize().unwrap_or(0));
110
111    let (base, power) = get_radix_base(radix);
112    let radix = radix as BigDigit;
113
114    let r = v.len() % power;
115    let i = if r == 0 { power } else { r };
116    let (head, tail) = v.split_at(i);
117
118    let first = head
119        .iter()
120        .fold(0, |acc, &d| acc * radix + BigDigit::from(d));
121    data.push(first);
122
123    debug_assert!(tail.len() % power == 0);
124    for chunk in tail.chunks(power) {
125        if data.last() != Some(&0) {
126            data.push(0);
127        }
128
129        let mut carry = 0;
130        for d in data.iter_mut() {
131            *d = mac_with_carry(0, *d, base, &mut carry);
132        }
133        debug_assert!(carry == 0);
134
135        let n = chunk
136            .iter()
137            .fold(0, |acc, &d| acc * radix + BigDigit::from(d));
138        add2(&mut data, &[n]);
139    }
140
141    biguint_from_vec(data)
142}
143
144pub(super) fn from_radix_be(buf: &[u8], radix: u32) -> Option<BigUint> {
145    assert!(
146        2 <= radix && radix <= 256,
147        "The radix must be within 2...256"
148    );
149
150    if buf.is_empty() {
151        return Some(BigUint::ZERO);
152    }
153
154    if radix != 256 && buf.iter().any(|&b| b >= radix as u8) {
155        return None;
156    }
157
158    let res = if radix.is_power_of_two() {
159        // Powers of two can use bitwise masks and shifting instead of multiplication
160        let bits = ilog2(radix);
161        let mut v = Vec::from(buf);
162        v.reverse();
163        if big_digit::BITS % bits == 0 {
164            from_bitwise_digits_le(&v, bits)
165        } else {
166            from_inexact_bitwise_digits_le(&v, bits)
167        }
168    } else {
169        from_radix_digits_be(buf, radix)
170    };
171
172    Some(res)
173}
174
175pub(super) fn from_radix_le(buf: &[u8], radix: u32) -> Option<BigUint> {
176    assert!(
177        2 <= radix && radix <= 256,
178        "The radix must be within 2...256"
179    );
180
181    if buf.is_empty() {
182        return Some(BigUint::ZERO);
183    }
184
185    if radix != 256 && buf.iter().any(|&b| b >= radix as u8) {
186        return None;
187    }
188
189    let res = if radix.is_power_of_two() {
190        // Powers of two can use bitwise masks and shifting instead of multiplication
191        let bits = ilog2(radix);
192        if big_digit::BITS % bits == 0 {
193            from_bitwise_digits_le(buf, bits)
194        } else {
195            from_inexact_bitwise_digits_le(buf, bits)
196        }
197    } else {
198        let mut v = Vec::from(buf);
199        v.reverse();
200        from_radix_digits_be(&v, radix)
201    };
202
203    Some(res)
204}
205
206impl Num for BigUint {
207    type FromStrRadixErr = ParseBigIntError;
208
209    /// Creates and initializes a [`BigUint`].
210    fn from_str_radix(s: &str, radix: u32) -> Result<BigUint, ParseBigIntError> {
211        assert!(2 <= radix && radix <= 36, "The radix must be within 2...36");
212        let mut s = s;
213        if let Some(tail) = s.strip_prefix('+') {
214            if !tail.starts_with('+') {
215                s = tail
216            }
217        }
218
219        if s.is_empty() {
220            return Err(ParseBigIntError::empty());
221        }
222
223        if s.starts_with('_') {
224            // Must lead with a real digit!
225            return Err(ParseBigIntError::invalid());
226        }
227
228        // First normalize all characters to plain digit values
229        let mut v = Vec::with_capacity(s.len());
230        for b in s.bytes() {
231            let d = match b {
232                b'0'..=b'9' => b - b'0',
233                b'a'..=b'z' => b - b'a' + 10,
234                b'A'..=b'Z' => b - b'A' + 10,
235                b'_' => continue,
236                _ => u8::MAX,
237            };
238            if d < radix as u8 {
239                v.push(d);
240            } else {
241                return Err(ParseBigIntError::invalid());
242            }
243        }
244
245        let res = if radix.is_power_of_two() {
246            // Powers of two can use bitwise masks and shifting instead of multiplication
247            let bits = ilog2(radix);
248            v.reverse();
249            if big_digit::BITS % bits == 0 {
250                from_bitwise_digits_le(&v, bits)
251            } else {
252                from_inexact_bitwise_digits_le(&v, bits)
253            }
254        } else {
255            from_radix_digits_be(&v, radix)
256        };
257        Ok(res)
258    }
259}
260
261fn high_bits_to_u64(v: &BigUint) -> u64 {
262    match v.data.len() {
263        0 => 0,
264        1 => {
265            // XXX Conversion is useless if already 64-bit.
266            #[allow(clippy::useless_conversion)]
267            let v0 = u64::from(v.data[0]);
268            v0
269        }
270        _ => {
271            let mut bits = v.bits();
272            let mut ret = 0u64;
273            let mut ret_bits = 0;
274
275            for d in v.data.iter().rev() {
276                let digit_bits = (bits - 1) % u64::from(big_digit::BITS) + 1;
277                let bits_want = Ord::min(64 - ret_bits, digit_bits);
278
279                if bits_want != 0 {
280                    if bits_want != 64 {
281                        ret <<= bits_want;
282                    }
283                    // XXX Conversion is useless if already 64-bit.
284                    #[allow(clippy::useless_conversion)]
285                    let d0 = u64::from(*d) >> (digit_bits - bits_want);
286                    ret |= d0;
287                }
288
289                // Implement round-to-odd: If any lower bits are 1, set LSB to 1
290                // so that rounding again to floating point value using
291                // nearest-ties-to-even is correct.
292                //
293                // See: https://en.wikipedia.org/wiki/Rounding#Rounding_to_prepare_for_shorter_precision
294
295                if digit_bits - bits_want != 0 {
296                    // XXX Conversion is useless if already 64-bit.
297                    #[allow(clippy::useless_conversion)]
298                    let masked = u64::from(*d) << (64 - (digit_bits - bits_want) as u32);
299                    ret |= (masked != 0) as u64;
300                }
301
302                ret_bits += bits_want;
303                bits -= bits_want;
304            }
305
306            ret
307        }
308    }
309}
310
311impl ToPrimitive for BigUint {
312    #[inline]
313    fn to_i64(&self) -> Option<i64> {
314        self.to_u64().as_ref().and_then(u64::to_i64)
315    }
316
317    #[inline]
318    fn to_i128(&self) -> Option<i128> {
319        self.to_u128().as_ref().and_then(u128::to_i128)
320    }
321
322    #[allow(clippy::useless_conversion)]
323    #[inline]
324    fn to_u64(&self) -> Option<u64> {
325        let mut ret: u64 = 0;
326        let mut bits = 0;
327
328        for i in self.data.iter() {
329            if bits >= 64 {
330                return None;
331            }
332
333            // XXX Conversion is useless if already 64-bit.
334            ret += u64::from(*i) << bits;
335            bits += big_digit::BITS;
336        }
337
338        Some(ret)
339    }
340
341    #[inline]
342    fn to_u128(&self) -> Option<u128> {
343        let mut ret: u128 = 0;
344        let mut bits = 0;
345
346        for i in self.data.iter() {
347            if bits >= 128 {
348                return None;
349            }
350
351            ret |= u128::from(*i) << bits;
352            bits += big_digit::BITS;
353        }
354
355        Some(ret)
356    }
357
358    #[inline]
359    fn to_f32(&self) -> Option<f32> {
360        let mantissa = high_bits_to_u64(self);
361        let exponent = self.bits() - u64::from(fls(mantissa));
362
363        if exponent > f32::MAX_EXP as u64 {
364            Some(f32::INFINITY)
365        } else {
366            Some((mantissa as f32) * 2.0f32.powi(exponent as i32))
367        }
368    }
369
370    #[inline]
371    fn to_f64(&self) -> Option<f64> {
372        let mantissa = high_bits_to_u64(self);
373        let exponent = self.bits() - u64::from(fls(mantissa));
374
375        if exponent > f64::MAX_EXP as u64 {
376            Some(f64::INFINITY)
377        } else {
378            Some((mantissa as f64) * 2.0f64.powi(exponent as i32))
379        }
380    }
381}
382
383macro_rules! impl_try_from_biguint {
384    ($T:ty, $to_ty:path) => {
385        impl TryFrom<&BigUint> for $T {
386            type Error = TryFromBigIntError<()>;
387
388            #[inline]
389            fn try_from(value: &BigUint) -> Result<$T, TryFromBigIntError<()>> {
390                $to_ty(value).ok_or(TryFromBigIntError::new(()))
391            }
392        }
393
394        impl TryFrom<BigUint> for $T {
395            type Error = TryFromBigIntError<BigUint>;
396
397            #[inline]
398            fn try_from(value: BigUint) -> Result<$T, TryFromBigIntError<BigUint>> {
399                <$T>::try_from(&value).map_err(|_| TryFromBigIntError::new(value))
400            }
401        }
402    };
403}
404
405impl_try_from_biguint!(u8, ToPrimitive::to_u8);
406impl_try_from_biguint!(u16, ToPrimitive::to_u16);
407impl_try_from_biguint!(u32, ToPrimitive::to_u32);
408impl_try_from_biguint!(u64, ToPrimitive::to_u64);
409impl_try_from_biguint!(usize, ToPrimitive::to_usize);
410impl_try_from_biguint!(u128, ToPrimitive::to_u128);
411
412impl_try_from_biguint!(i8, ToPrimitive::to_i8);
413impl_try_from_biguint!(i16, ToPrimitive::to_i16);
414impl_try_from_biguint!(i32, ToPrimitive::to_i32);
415impl_try_from_biguint!(i64, ToPrimitive::to_i64);
416impl_try_from_biguint!(isize, ToPrimitive::to_isize);
417impl_try_from_biguint!(i128, ToPrimitive::to_i128);
418
419impl FromPrimitive for BigUint {
420    #[inline]
421    fn from_i64(n: i64) -> Option<BigUint> {
422        if n >= 0 {
423            Some(BigUint::from(n as u64))
424        } else {
425            None
426        }
427    }
428
429    #[inline]
430    fn from_i128(n: i128) -> Option<BigUint> {
431        if n >= 0 {
432            Some(BigUint::from(n as u128))
433        } else {
434            None
435        }
436    }
437
438    #[inline]
439    fn from_u64(n: u64) -> Option<BigUint> {
440        Some(BigUint::from(n))
441    }
442
443    #[inline]
444    fn from_u128(n: u128) -> Option<BigUint> {
445        Some(BigUint::from(n))
446    }
447
448    #[inline]
449    fn from_f64(mut n: f64) -> Option<BigUint> {
450        // handle NAN, INFINITY, NEG_INFINITY
451        if !n.is_finite() {
452            return None;
453        }
454
455        // match the rounding of casting from float to int
456        n = n.trunc();
457
458        // handle 0.x, -0.x
459        if n.is_zero() {
460            return Some(Self::ZERO);
461        }
462
463        let (mantissa, exponent, sign) = FloatCore::integer_decode(n);
464
465        if sign == -1 {
466            return None;
467        }
468
469        let mut ret = BigUint::from(mantissa);
470        match exponent.cmp(&0) {
471            Greater => ret <<= exponent as usize,
472            Equal => {}
473            Less => ret >>= (-exponent) as usize,
474        }
475        Some(ret)
476    }
477}
478
479impl From<u8> for BigUint {
480    #[inline]
481    fn from(n: u8) -> Self {
482        BigUint::from(u32::from(n))
483    }
484}
485
486impl From<u16> for BigUint {
487    #[inline]
488    fn from(n: u16) -> Self {
489        BigUint::from(u32::from(n))
490    }
491}
492
493impl From<u32> for BigUint {
494    #[inline]
495    fn from(n: u32) -> Self {
496        BigUint {
497            data: BigDigits::from_digit(n as BigDigit),
498        }
499    }
500}
501
502impl From<u64> for BigUint {
503    #[inline]
504    fn from(n: u64) -> Self {
505        cfg_digit_expr!(
506            return if n > big_digit::MAX as u64 {
507                BigUint::new(vec![n as BigDigit, (n >> big_digit::BITS) as BigDigit])
508            } else {
509                BigUint {
510                    data: BigDigits::from_digit(n as BigDigit),
511                }
512            },
513            return BigUint {
514                data: BigDigits::from_digit(n),
515            }
516        );
517    }
518}
519
520impl From<u128> for BigUint {
521    #[inline]
522    fn from(mut n: u128) -> Self {
523        let mut ret: BigUint = Self::ZERO;
524
525        while n != 0 {
526            ret.data.push(n as BigDigit);
527            n >>= big_digit::BITS;
528        }
529
530        ret
531    }
532}
533
534impl From<usize> for BigUint {
535    #[inline]
536    fn from(n: usize) -> Self {
537        BigUint::from(n as crate::UsizePromotion)
538    }
539}
540
541macro_rules! impl_biguint_try_from_int {
542    ($T:ty, $from_ty:path) => {
543        impl TryFrom<$T> for BigUint {
544            type Error = TryFromBigIntError<()>;
545
546            #[inline]
547            fn try_from(value: $T) -> Result<BigUint, TryFromBigIntError<()>> {
548                $from_ty(value).ok_or(TryFromBigIntError::new(()))
549            }
550        }
551    };
552}
553
554impl_biguint_try_from_int!(i8, FromPrimitive::from_i8);
555impl_biguint_try_from_int!(i16, FromPrimitive::from_i16);
556impl_biguint_try_from_int!(i32, FromPrimitive::from_i32);
557impl_biguint_try_from_int!(i64, FromPrimitive::from_i64);
558impl_biguint_try_from_int!(isize, FromPrimitive::from_isize);
559impl_biguint_try_from_int!(i128, FromPrimitive::from_i128);
560
561impl ToBigUint for BigUint {
562    #[inline]
563    fn to_biguint(&self) -> Option<BigUint> {
564        Some(self.clone())
565    }
566}
567
568macro_rules! impl_to_biguint {
569    ($T:ty, $from_ty:path) => {
570        impl ToBigUint for $T {
571            #[inline]
572            fn to_biguint(&self) -> Option<BigUint> {
573                $from_ty(*self)
574            }
575        }
576    };
577}
578
579impl_to_biguint!(isize, FromPrimitive::from_isize);
580impl_to_biguint!(i8, FromPrimitive::from_i8);
581impl_to_biguint!(i16, FromPrimitive::from_i16);
582impl_to_biguint!(i32, FromPrimitive::from_i32);
583impl_to_biguint!(i64, FromPrimitive::from_i64);
584impl_to_biguint!(i128, FromPrimitive::from_i128);
585
586impl_to_biguint!(usize, FromPrimitive::from_usize);
587impl_to_biguint!(u8, FromPrimitive::from_u8);
588impl_to_biguint!(u16, FromPrimitive::from_u16);
589impl_to_biguint!(u32, FromPrimitive::from_u32);
590impl_to_biguint!(u64, FromPrimitive::from_u64);
591impl_to_biguint!(u128, FromPrimitive::from_u128);
592
593impl_to_biguint!(f32, FromPrimitive::from_f32);
594impl_to_biguint!(f64, FromPrimitive::from_f64);
595
596impl From<bool> for BigUint {
597    fn from(x: bool) -> Self {
598        if x {
599            Self::ONE
600        } else {
601            Self::ZERO
602        }
603    }
604}
605
606// Extract bitwise digits that evenly divide BigDigit
607pub(super) fn to_bitwise_digits_le(u: &BigUint, bits: u8) -> Vec<u8> {
608    debug_assert!(!u.is_zero() && bits <= 8 && big_digit::BITS % bits == 0);
609
610    let last_i = u.data.len() - 1;
611    let mask: BigDigit = (1 << bits) - 1;
612    let digits_per_big_digit = big_digit::BITS / bits;
613    let digits = Integer::div_ceil(&u.bits(), &u64::from(bits))
614        .to_usize()
615        .unwrap_or(usize::MAX);
616    let mut res = Vec::with_capacity(digits);
617
618    for mut r in u.data[..last_i].iter().cloned() {
619        for _ in 0..digits_per_big_digit {
620            res.push((r & mask) as u8);
621            r >>= bits;
622        }
623    }
624
625    let mut r = u.data[last_i];
626    while r != 0 {
627        res.push((r & mask) as u8);
628        r >>= bits;
629    }
630
631    res
632}
633
634// Extract bitwise digits that don't evenly divide BigDigit
635fn to_inexact_bitwise_digits_le(u: &BigUint, bits: u8) -> Vec<u8> {
636    debug_assert!(!u.is_zero() && bits <= 8 && big_digit::BITS % bits != 0);
637
638    let mask: BigDigit = (1 << bits) - 1;
639    let digits = Integer::div_ceil(&u.bits(), &u64::from(bits))
640        .to_usize()
641        .unwrap_or(usize::MAX);
642    let mut res = Vec::with_capacity(digits);
643
644    let mut r = 0;
645    let mut rbits = 0;
646
647    for c in &*u.data {
648        r |= *c << rbits;
649        rbits += big_digit::BITS;
650
651        while rbits >= bits {
652            res.push((r & mask) as u8);
653            r >>= bits;
654
655            // r had more bits than it could fit - grab the bits we lost
656            if rbits > big_digit::BITS {
657                r = *c >> (big_digit::BITS - (rbits - bits));
658            }
659
660            rbits -= bits;
661        }
662    }
663
664    if rbits != 0 {
665        res.push(r as u8);
666    }
667
668    while let Some(&0) = res.last() {
669        res.pop();
670    }
671
672    res
673}
674
675// Extract little-endian radix digits
676#[inline(always)] // forced inline to get const-prop for radix=10
677pub(super) fn to_radix_digits_le(u: &BigUint, radix: u32) -> Vec<u8> {
678    debug_assert!(!u.is_zero() && !radix.is_power_of_two());
679
680    #[cfg(feature = "std")]
681    let radix_digits = {
682        let radix_log2 = f64::from(radix).log2();
683        ((u.bits() as f64) / radix_log2).ceil()
684    };
685    #[cfg(not(feature = "std"))]
686    let radix_digits = {
687        let radix_log2 = ilog2(radix) as usize;
688        ((u.bits() as usize) / radix_log2) + 1
689    };
690
691    // Estimate how big the result will be, so we can pre-allocate it.
692    let mut res = Vec::with_capacity(radix_digits.to_usize().unwrap_or(0));
693
694    let digits = u.clone();
695
696    // X86 DIV can quickly divide by a full digit, otherwise we choose a divisor
697    // that's suitable for `div_half` to avoid slow `DoubleBigDigit` division.
698    let (base, power) = if FAST_DIV_WIDE {
699        get_radix_base(radix)
700    } else {
701        get_half_radix_base(radix)
702    };
703    let radix = radix as BigDigit;
704
705    // For very large numbers, the O(n²) loop of repeated `div_rem_digit` dominates the
706    // performance. We can mitigate this by dividing into chunks of a larger base first.
707    // The threshold for this was chosen by anecdotal performance measurements to
708    // approximate where this starts to make a noticeable difference.
709    if digits.data.len() >= 32 {
710        let mut big_bases = Vec::with_capacity(32);
711        big_bases.push((BigUint::from(base), power));
712
713        loop {
714            let (big_base, power) = big_bases.last().unwrap();
715            if big_base.data.len() > digits.data.len() / 2 + 1 {
716                break;
717            }
718            let next_big_base = big_base * big_base;
719            let next_power = *power * 2;
720            big_bases.push((next_big_base, next_power));
721        }
722
723        to_radix_digits_le_divide_and_conquer(
724            digits,
725            base,
726            power,
727            &big_bases,
728            big_bases.len() - 1,
729            &mut res,
730            radix,
731        );
732        while res.last() == Some(&0) {
733            res.pop();
734        }
735        return res;
736    }
737
738    to_radix_digits_le_small(digits, base, power, &mut res, radix);
739
740    res
741}
742
743// Extract little-endian radix digits for small numbers
744#[inline(always)] // forced inline to get const-prop for radix=10
745fn to_radix_digits_le_small(
746    mut digits: BigUint,
747    base: BigDigit,
748    power: usize,
749    res: &mut Vec<u8>,
750    radix: BigDigit,
751) {
752    while digits.data.len() > 1 {
753        let (q, mut r) = div_rem_digit(digits, base);
754        for _ in 0..power {
755            res.push((r % radix) as u8);
756            r /= radix;
757        }
758        digits = q;
759    }
760
761    let mut r = digits.data[0];
762    while r != 0 {
763        res.push((r % radix) as u8);
764        r /= radix;
765    }
766}
767
768fn to_radix_digits_le_divide_and_conquer(
769    number: BigUint,
770    base: BigDigit,
771    power: usize,
772    big_bases: &[(BigUint, usize)],
773    k: usize,
774    res: &mut Vec<u8>,
775    radix: BigDigit,
776) {
777    let &(ref big_base, result_len) = &big_bases[k];
778    if number.data.len() < 8 {
779        let prev_res_len = res.len();
780        if !number.is_zero() {
781            to_radix_digits_le_small(number, base, power, res, radix);
782        }
783        while res.len() < prev_res_len + result_len * 2 {
784            res.push(0);
785        }
786        return;
787    }
788    // number always has two digits in the big base
789    let (digit_1, digit_2) = number.div_rem(big_base);
790    assert!(&digit_1 < big_base);
791    assert!(&digit_2 < big_base);
792    to_radix_digits_le_divide_and_conquer(digit_2, base, power, big_bases, k - 1, res, radix);
793    to_radix_digits_le_divide_and_conquer(digit_1, base, power, big_bases, k - 1, res, radix);
794}
795
796pub(super) fn to_radix_le(u: &BigUint, radix: u32) -> Vec<u8> {
797    if u.is_zero() {
798        vec![0]
799    } else if radix.is_power_of_two() {
800        // Powers of two can use bitwise masks and shifting instead of division
801        let bits = ilog2(radix);
802        if big_digit::BITS % bits == 0 {
803            to_bitwise_digits_le(u, bits)
804        } else {
805            to_inexact_bitwise_digits_le(u, bits)
806        }
807    } else if radix == 10 {
808        // 10 is so common that it's worth separating out for const-propagation.
809        // Optimizers can often turn constant division into a faster multiplication.
810        to_radix_digits_le(u, 10)
811    } else {
812        to_radix_digits_le(u, radix)
813    }
814}
815
816pub(crate) fn to_str_radix_reversed(u: &BigUint, radix: u32) -> Vec<u8> {
817    assert!(2 <= radix && radix <= 36, "The radix must be within 2...36");
818
819    if u.is_zero() {
820        return vec![b'0'];
821    }
822
823    let mut res = to_radix_le(u, radix);
824
825    // Now convert everything to ASCII digits.
826    for r in &mut res {
827        debug_assert!(u32::from(*r) < radix);
828        if *r < 10 {
829            *r += b'0';
830        } else {
831            *r += b'a' - 10;
832        }
833    }
834    res
835}
836
837/// Returns the greatest power of the radix for the `BigDigit` bit size
838#[inline]
839fn get_radix_base(radix: u32) -> (BigDigit, usize) {
840    static BASES: [(BigDigit, usize); 257] = generate_radix_bases(big_digit::MAX);
841    debug_assert!(!radix.is_power_of_two());
842    debug_assert!((3..256).contains(&radix));
843    BASES[radix as usize]
844}
845
846/// Returns the greatest power of the radix for half the `BigDigit` bit size
847#[inline]
848fn get_half_radix_base(radix: u32) -> (BigDigit, usize) {
849    static BASES: [(BigDigit, usize); 257] = generate_radix_bases(big_digit::HALF);
850    debug_assert!(!radix.is_power_of_two());
851    debug_assert!((3..256).contains(&radix));
852    BASES[radix as usize]
853}
854
855/// Generate tables of the greatest power of each radix that is less that the given maximum. These
856/// are returned from `get_radix_base` to batch the multiplication/division of radix conversions on
857/// full [`BigUint`] values, operating on primitive integers as much as possible.
858///
859/// e.g. BASES_16[3] = (59049, 10) // 3¹⁰ fits in u16, but 3¹¹ is too big
860///      BASES_32[3] = (3486784401, 20)
861///      BASES_64[3] = (12157665459056928801, 40)
862///
863/// Powers of two are not included, just zeroed, as they're implemented with shifts.
864const fn generate_radix_bases(max: BigDigit) -> [(BigDigit, usize); 257] {
865    let mut bases = [(0, 0); 257];
866
867    let mut radix: BigDigit = 3;
868    while radix < 256 {
869        if !radix.is_power_of_two() {
870            let mut power = 1;
871            let mut base = radix;
872
873            while let Some(b) = base.checked_mul(radix) {
874                if b > max {
875                    break;
876                }
877                base = b;
878                power += 1;
879            }
880            bases[radix as usize] = (base, power)
881        }
882        radix += 1;
883    }
884
885    bases
886}
887
888#[test]
889fn test_radix_bases() {
890    for radix in 3u32..256 {
891        if !radix.is_power_of_two() {
892            let (base, power) = get_radix_base(radix);
893            let radix = BigDigit::from(radix);
894            let power = u32::try_from(power).unwrap();
895            assert_eq!(base, radix.pow(power));
896            assert!(radix.checked_pow(power + 1).is_none());
897        }
898    }
899}
900
901#[test]
902fn test_half_radix_bases() {
903    for radix in 3u32..256 {
904        if !radix.is_power_of_two() {
905            let (base, power) = get_half_radix_base(radix);
906            let radix = BigDigit::from(radix);
907            let power = u32::try_from(power).unwrap();
908            assert_eq!(base, radix.pow(power));
909            assert!(radix.pow(power + 1) > big_digit::HALF);
910        }
911    }
912}