use std::ops::ShlAssign;
use num_bigint::{BigInt, BigUint};
use num_traits::Zero;
use crate::sane::MAX_COMPUTATION_BITS;
pub(crate) trait ArbitraryPrecision: Clone + ShlAssign<usize> {}
impl ArbitraryPrecision for BigInt {}
impl ArbitraryPrecision for BigUint {}
pub(crate) fn shift_mantissa_chunked<M>(mantissa: &M, shift: &BigUint, chunk_limit: &BigUint) -> M
where
M: ArbitraryPrecision,
{
if shift > &BigUint::from(MAX_COMPUTATION_BITS) {
crate::detected_computable_would_exhaust_memory!("shift by extreme exponent");
}
let mut shifted = mantissa.clone();
let mut remaining = shift.clone();
let chunk_limit_usize = chunk_limit.try_into().unwrap_or(usize::MAX);
while remaining > BigUint::zero() {
let chunk_usize: usize = if &remaining > chunk_limit {
chunk_limit_usize
} else {
(&remaining).try_into().unwrap_or(usize::MAX)
};
#[allow(clippy::arithmetic_side_effects)] {
shifted <<= chunk_usize;
}
remaining -= BigUint::from(chunk_usize);
}
shifted
}
#[cfg(test)]
mod tests {
use super::*;
use num_bigint::BigInt;
#[test]
fn shift_mantissa_chunks_large_shift() {
let mantissa = BigInt::from(1);
let shift = BigUint::from(128u32);
let chunk_limit = BigUint::from(64u32);
let chunked = shift_mantissa_chunked::<BigInt>(&mantissa, &shift, &chunk_limit);
let expected = &mantissa << 128usize;
assert_eq!(chunked, expected);
}
#[test]
fn shift_zero_returns_original() {
let mantissa = BigInt::from(42);
let shift = BigUint::zero();
let chunk_limit = BigUint::from(64u32);
let result = shift_mantissa_chunked::<BigInt>(&mantissa, &shift, &chunk_limit);
assert_eq!(result, mantissa);
}
#[test]
fn shift_within_single_chunk() {
let mantissa = BigInt::from(1);
let shift = BigUint::from(10u32);
let chunk_limit = BigUint::from(64u32);
let result = shift_mantissa_chunked::<BigInt>(&mantissa, &shift, &chunk_limit);
assert_eq!(result, BigInt::from(1024)); }
}