use super::{biguint_from_vec, BigUint, ToBigUint};
use crate::big_digit::{self, BigDigit};
use crate::std_alloc::Vec;
use crate::ParseBigIntError;
#[cfg(has_try_from)]
use crate::TryFromBigIntError;
#[cfg(has_try_from)]
use core::convert::TryFrom;
use core::mem;
use core::str::FromStr;
use num_integer::Integer;
use num_traits::{FromPrimitive, Num, PrimInt, ToPrimitive, Zero};
fn fls<T: PrimInt>(v: T) -> u8 {
mem::size_of::<T>() as u8 * 8 - v.leading_zeros() as u8
}
fn ilog2<T: PrimInt>(v: T) -> u8 {
fls(v) - 1
}
impl FromStr for BigUint {
type Err = ParseBigIntError;
#[inline]
fn from_str(s: &str) -> Result<BigUint, ParseBigIntError> {
BigUint::from_str_radix(s, 10)
}
}
pub(super) fn from_bitwise_digits_le(v: &[u8], bits: u8) -> BigUint {
debug_assert!(!v.is_empty() && bits <= 8 && big_digit::BITS % bits == 0);
debug_assert!(v.iter().all(|&c| BigDigit::from(c) < (1 << bits)));
let digits_per_big_digit = big_digit::BITS / bits;
let data = v
.chunks(digits_per_big_digit.into())
.map(|chunk| {
chunk
.iter()
.rev()
.fold(0, |acc, &c| (acc << bits) | BigDigit::from(c))
})
.collect();
biguint_from_vec(data)
}
fn from_inexact_bitwise_digits_le(v: &[u8], bits: u8) -> BigUint {
debug_assert!(!v.is_empty() && bits <= 8 && big_digit::BITS % bits != 0);
debug_assert!(v.iter().all(|&c| BigDigit::from(c) < (1 << bits)));
let total_bits = (v.len() as u64).saturating_mul(bits.into());
let big_digits = Integer::div_ceil(&total_bits, &big_digit::BITS.into())
.to_usize()
.unwrap_or(core::usize::MAX);
let mut data = Vec::with_capacity(big_digits);
let mut d = 0;
let mut dbits = 0;
for &c in v {
d |= BigDigit::from(c) << dbits;
dbits += bits;
if dbits >= big_digit::BITS {
data.push(d);
dbits -= big_digit::BITS;
d = BigDigit::from(c) >> (bits - dbits);
}
}
if dbits > 0 {
debug_assert!(dbits < big_digit::BITS);
data.push(d as BigDigit);
}
biguint_from_vec(data)
}
pub(super) fn from_radix_be(buf: &[u8], radix: u32) -> Option<BigUint> {
assert!(2 <= radix && radix <= 256, "The radix must be within 2...256");
assert!(radix.is_power_of_two(), "The radix must be a power of 2");
if buf.is_empty() {
return Some(Zero::zero());
}
if radix != 256 && buf.iter().any(|&b| b >= radix as u8) {
return None;
}
let res = {
let bits = ilog2(radix);
let mut v = Vec::from(buf);
v.reverse();
if big_digit::BITS % bits == 0 {
from_bitwise_digits_le(&v, bits)
} else {
from_inexact_bitwise_digits_le(&v, bits)
}
};
Some(res)
}
pub(super) fn from_radix_le(buf: &[u8], radix: u32) -> Option<BigUint> {
assert!(2 <= radix && radix <= 256, "The radix must be within 2...256");
assert!(radix.is_power_of_two(), "The radix must be a power of 2");
if buf.is_empty() {
return Some(Zero::zero());
}
if radix != 256 && buf.iter().any(|&b| b >= radix as u8) {
return None;
}
let res = {
let bits = ilog2(radix);
if big_digit::BITS % bits == 0 {
from_bitwise_digits_le(buf, bits)
} else {
from_inexact_bitwise_digits_le(buf, bits)
}
};
Some(res)
}
impl Num for BigUint {
type FromStrRadixErr = ParseBigIntError;
fn from_str_radix(s: &str, radix: u32) -> Result<BigUint, ParseBigIntError> {
assert!(2 <= radix && radix <= 36, "The radix must be within 2...36");
assert!(radix.is_power_of_two(), "The radix must be a power of 2");
let mut s = s;
if s.starts_with('+') {
let tail = &s[1..];
if !tail.starts_with('+') {
s = tail
}
}
if s.is_empty() {
return Err(ParseBigIntError::empty());
}
if s.starts_with('_') {
return Err(ParseBigIntError::invalid());
}
let mut v = Vec::with_capacity(s.len());
for b in s.bytes() {
let d = match b {
b'0'..=b'9' => b - b'0',
b'a'..=b'z' => b - b'a' + 10,
b'A'..=b'Z' => b - b'A' + 10,
b'_' => continue,
_ => core::u8::MAX,
};
if d < radix as u8 {
v.push(d);
} else {
return Err(ParseBigIntError::invalid());
}
}
let res = {
let bits = ilog2(radix);
v.reverse();
if big_digit::BITS % bits == 0 {
from_bitwise_digits_le(&v, bits)
} else {
from_inexact_bitwise_digits_le(&v, bits)
}
};
Ok(res)
}
}
impl ToPrimitive for BigUint {
#[inline]
fn to_i64(&self) -> Option<i64> {
self.to_u64().as_ref().and_then(u64::to_i64)
}
#[inline]
fn to_i128(&self) -> Option<i128> {
self.to_u128().as_ref().and_then(u128::to_i128)
}
#[allow(clippy::useless_conversion)]
#[inline]
fn to_u64(&self) -> Option<u64> {
let mut ret: u64 = 0;
let mut bits = 0;
for i in self.data.iter() {
if bits >= 64 {
return None;
}
ret += u64::from(*i) << bits;
bits += big_digit::BITS;
}
Some(ret)
}
#[inline]
fn to_u128(&self) -> Option<u128> {
let mut ret: u128 = 0;
let mut bits = 0;
for i in self.data.iter() {
if bits >= 128 {
return None;
}
ret |= u128::from(*i) << bits;
bits += big_digit::BITS;
}
Some(ret)
}
}
macro_rules! impl_try_from_biguint {
($T:ty, $to_ty:path) => {
#[cfg(has_try_from)]
impl TryFrom<&BigUint> for $T {
type Error = TryFromBigIntError<()>;
#[inline]
fn try_from(value: &BigUint) -> Result<$T, TryFromBigIntError<()>> {
$to_ty(value).ok_or(TryFromBigIntError::new(()))
}
}
#[cfg(has_try_from)]
impl TryFrom<BigUint> for $T {
type Error = TryFromBigIntError<BigUint>;
#[inline]
fn try_from(value: BigUint) -> Result<$T, TryFromBigIntError<BigUint>> {
<$T>::try_from(&value).map_err(|_| TryFromBigIntError::new(value))
}
}
};
}
impl_try_from_biguint!(u8, ToPrimitive::to_u8);
impl_try_from_biguint!(u16, ToPrimitive::to_u16);
impl_try_from_biguint!(u32, ToPrimitive::to_u32);
impl_try_from_biguint!(u64, ToPrimitive::to_u64);
impl_try_from_biguint!(usize, ToPrimitive::to_usize);
impl_try_from_biguint!(u128, ToPrimitive::to_u128);
impl_try_from_biguint!(i8, ToPrimitive::to_i8);
impl_try_from_biguint!(i16, ToPrimitive::to_i16);
impl_try_from_biguint!(i32, ToPrimitive::to_i32);
impl_try_from_biguint!(i64, ToPrimitive::to_i64);
impl_try_from_biguint!(isize, ToPrimitive::to_isize);
impl_try_from_biguint!(i128, ToPrimitive::to_i128);
impl FromPrimitive for BigUint {
#[inline]
fn from_i64(n: i64) -> Option<BigUint> {
if n >= 0 {
Some(BigUint::from(n as u64))
} else {
None
}
}
#[inline]
fn from_i128(n: i128) -> Option<BigUint> {
if n >= 0 {
Some(BigUint::from(n as u128))
} else {
None
}
}
#[inline]
fn from_u64(n: u64) -> Option<BigUint> {
Some(BigUint::from(n))
}
#[inline]
fn from_u128(n: u128) -> Option<BigUint> {
Some(BigUint::from(n))
}
}
impl From<u64> for BigUint {
#[inline]
fn from(mut n: u64) -> Self {
let mut ret: BigUint = Zero::zero();
while n != 0 {
ret.data.push(n as BigDigit);
n = (n >> 1) >> (big_digit::BITS - 1);
}
ret
}
}
impl From<u128> for BigUint {
#[inline]
fn from(mut n: u128) -> Self {
let mut ret: BigUint = Zero::zero();
while n != 0 {
ret.data.push(n as BigDigit);
n >>= big_digit::BITS;
}
ret
}
}
macro_rules! impl_biguint_from_uint {
($T:ty) => {
impl From<$T> for BigUint {
#[inline]
fn from(n: $T) -> Self {
BigUint::from(n as u64)
}
}
};
}
impl_biguint_from_uint!(u8);
impl_biguint_from_uint!(u16);
impl_biguint_from_uint!(u32);
impl_biguint_from_uint!(usize);
macro_rules! impl_biguint_try_from_int {
($T:ty, $from_ty:path) => {
#[cfg(has_try_from)]
impl TryFrom<$T> for BigUint {
type Error = TryFromBigIntError<()>;
#[inline]
fn try_from(value: $T) -> Result<BigUint, TryFromBigIntError<()>> {
$from_ty(value).ok_or(TryFromBigIntError::new(()))
}
}
};
}
impl_biguint_try_from_int!(i8, FromPrimitive::from_i8);
impl_biguint_try_from_int!(i16, FromPrimitive::from_i16);
impl_biguint_try_from_int!(i32, FromPrimitive::from_i32);
impl_biguint_try_from_int!(i64, FromPrimitive::from_i64);
impl_biguint_try_from_int!(isize, FromPrimitive::from_isize);
impl_biguint_try_from_int!(i128, FromPrimitive::from_i128);
impl ToBigUint for BigUint {
#[inline]
fn to_biguint(&self) -> Option<BigUint> {
Some(self.clone())
}
}
macro_rules! impl_to_biguint {
($T:ty, $from_ty:path) => {
impl ToBigUint for $T {
#[inline]
fn to_biguint(&self) -> Option<BigUint> {
$from_ty(*self)
}
}
};
}
impl_to_biguint!(isize, FromPrimitive::from_isize);
impl_to_biguint!(i8, FromPrimitive::from_i8);
impl_to_biguint!(i16, FromPrimitive::from_i16);
impl_to_biguint!(i32, FromPrimitive::from_i32);
impl_to_biguint!(i64, FromPrimitive::from_i64);
impl_to_biguint!(i128, FromPrimitive::from_i128);
impl_to_biguint!(usize, FromPrimitive::from_usize);
impl_to_biguint!(u8, FromPrimitive::from_u8);
impl_to_biguint!(u16, FromPrimitive::from_u16);
impl_to_biguint!(u32, FromPrimitive::from_u32);
impl_to_biguint!(u64, FromPrimitive::from_u64);
impl_to_biguint!(u128, FromPrimitive::from_u128);
pub(super) fn to_bitwise_digits_le(u: &BigUint, bits: u8) -> Vec<u8> {
debug_assert!(!u.is_zero() && bits <= 8 && big_digit::BITS % bits == 0);
let last_i = u.data.len() - 1;
let mask: BigDigit = (1 << bits) - 1;
let digits_per_big_digit = big_digit::BITS / bits;
let digits = Integer::div_ceil(&u.bits(), &u64::from(bits))
.to_usize()
.unwrap_or(core::usize::MAX);
let mut res = Vec::with_capacity(digits);
for mut r in u.data[..last_i].iter().cloned() {
for _ in 0..digits_per_big_digit {
res.push((r & mask) as u8);
r >>= bits;
}
}
let mut r = u.data[last_i];
while r != 0 {
res.push((r & mask) as u8);
r >>= bits;
}
res
}
fn to_inexact_bitwise_digits_le(u: &BigUint, bits: u8) -> Vec<u8> {
debug_assert!(!u.is_zero() && bits <= 8 && big_digit::BITS % bits != 0);
let mask: BigDigit = (1 << bits) - 1;
let digits = Integer::div_ceil(&u.bits(), &u64::from(bits))
.to_usize()
.unwrap_or(core::usize::MAX);
let mut res = Vec::with_capacity(digits);
let mut r = 0;
let mut rbits = 0;
for c in &u.data {
r |= *c << rbits;
rbits += big_digit::BITS;
while rbits >= bits {
res.push((r & mask) as u8);
r >>= bits;
if rbits > big_digit::BITS {
r = *c >> (big_digit::BITS - (rbits - bits));
}
rbits -= bits;
}
}
if rbits != 0 {
res.push(r as u8);
}
while let Some(&0) = res.last() {
res.pop();
}
res
}
pub(super) fn to_radix_le(u: &BigUint, radix: u32) -> Vec<u8> {
assert!(radix.is_power_of_two(), "The radix must be a power of 2");
if u.is_zero() {
vec![0]
} else {
let bits = ilog2(radix);
if big_digit::BITS % bits == 0 {
to_bitwise_digits_le(u, bits)
} else {
to_inexact_bitwise_digits_le(u, bits)
}
}
}
pub(crate) fn to_str_radix_reversed(u: &BigUint, radix: u32) -> Vec<u8> {
assert!(2 <= radix && radix <= 36, "The radix must be within 2...36");
if u.is_zero() {
return vec![b'0'];
}
let mut res = to_radix_le(u, radix);
for r in &mut res {
debug_assert!(u32::from(*r) < radix);
if *r < 10 {
*r += b'0';
} else {
*r += b'a' - 10;
}
}
res
}