use crate::{Byte, Integer, Uint};
use super::MAX_RADIX_POWERS;
use crate::integer::radix::assert_range;
use alloc::{string::String, vec::Vec};
#[inline]
const fn digit_to_str_byte(digit: u8) -> u8 {
if digit < 10 {
digit + b'0'
} else {
digit + b'a' - 10
}
}
macro_rules! impl_desc {
() => {
"Methods which convert integers to strings and lists of digits in a given radix (base)."
};
}
#[doc = concat!("(Unsigned integers only.) ", impl_desc!())]
impl<const N: usize, const B: usize, const OM: u8> Uint<N, B, OM> {
#[inline]
fn to_digits_le(self, radix: u32) -> Vec<u8> {
let mut digits = Vec::with_capacity(Self::BITS.div_ceil(radix.ilog2()) as usize); let mut current = self;
let radix_u64 = radix as u64;
let (max_pow, max_pow_exponent) = MAX_RADIX_POWERS[radix as usize];
loop {
let (q, mut r) = current.div_rem_u64(max_pow); if q.is_zero() {
while r != 0 {
digits.push((r % radix_u64) as u8);
r /= radix_u64;
}
return digits;
}
for _ in 0..max_pow_exponent {
digits.push((r % radix_u64) as u8); r /= radix_u64;
}
current = q;
}
}
#[inline]
fn to_inexact_bitwise_digits_le(self, radix: u32) -> Vec<u8> {
let bit_width = self.bit_width();
let radix_log2 = radix.ilog2();
let mask = u8::MAX >> (u8::BITS - radix_log2);
let mut digits = Vec::with_capacity(Self::BITS.div_ceil(radix_log2) as usize);
let num_non_zero_digits = bit_width.div_ceil(u128::BITS) as usize; let mut offset_bit_width = 0;
let mut carry_bits = 0;
let self_digits = self.to_digits::<u128>();
for i in 0..num_non_zero_digits {
let d = self_digits.get(i); let digit = (d << offset_bit_width) as u8 & mask; digits.push(digit | carry_bits);
let mut j = radix_log2 - offset_bit_width;
while j <= u128::BITS - radix_log2 {
let digit = (d >> j) as u8 & mask; digits.push(digit);
j += radix_log2;
}
offset_bit_width = radix_log2 - ((j + radix_log2) % u128::BITS);
carry_bits = (d >> j) as u8; }
if carry_bits != 0 {
digits.push(carry_bits);
}
while let Some(&0) = digits.last() {
digits.pop();
}
digits
}
#[inline]
fn to_exact_bitwise_digits_le(self, radix: u32) -> Vec<u8> {
let radix_log2 = radix.ilog2();
let mask = u8::MAX >> (u8::BITS - radix_log2);
let mut digits = Vec::with_capacity(Self::BITS.div_ceil(radix_log2) as usize);
debug_assert!(mask.trailing_ones() == radix_log2);
debug_assert!(mask.count_ones() == radix_log2); let num_non_zero_digits = self.bit_width().div_ceil(Byte::BITS) as usize;
let digits_per_big_digit = Byte::BITS / radix_log2;
for i in 0..num_non_zero_digits - 1 {
let mut d = self.bytes[i]; for _ in 0..digits_per_big_digit {
let digit = d & mask; digits.push(digit);
d >>= radix_log2;
}
}
let mut d = self.bytes[num_non_zero_digits - 1];
while d != 0 {
let digit = d & mask; digits.push(digit);
d >>= radix_log2;
}
digits
}
#[inline]
pub fn to_radix_be(&self, radix: u32) -> Vec<u8> {
let mut v = self.to_radix_le(radix);
v.reverse();
v
}
pub fn to_radix_le(&self, radix: u32) -> Vec<u8> {
assert_range!(radix, 256);
if self.is_zero() {
return vec![0];
}
match radix {
2 | 4 | 16 | 256 => {
self.to_exact_bitwise_digits_le(radix)
},
8 | 32 | 64 | 128 => {
self.to_inexact_bitwise_digits_le(radix)
},
10 => self.to_digits_le(10),
_ => self.to_digits_le(radix),
}
}
}
impl<const S: bool, const N: usize, const B: usize, const OM: u8> Integer<S, N, B, OM> {
#[inline]
pub fn to_str_radix(&self, radix: u32) -> String {
if self.is_negative_internal() {
return format!("-{}", self.unsigned_abs_internal().to_str_radix(radix));
}
assert_range!(radix, 36);
let mut out = self.force_sign::<false>().to_radix_be(radix);
for byte in out.iter_mut() {
*byte = digit_to_str_byte(*byte);
}
unsafe { String::from_utf8_unchecked(out) }
}
}
#[cfg(test)]
mod tests {
crate::test::test_all! {
testing integers;
crate::test::quickcheck_from_to_radix!(stest, str_radix, 36);
}
crate::test::test_all! {
testing unsigned;
crate::test::quickcheck_from_to_radix!(stest, radix_be, 256);
crate::test::quickcheck_from_to_radix!(stest, radix_le, 256);
}
}