use crate::int::types::BigInt;
use crate::support::error::ConvertError;
use crate::support::rounding::{should_bump, RoundingMode};
#[inline]
fn rescale_bigint<T: BigInt>(value: T, s_from: u32, s_to: u32, mode: RoundingMode) -> Option<T> {
if s_to == s_from {
return Some(value);
}
if s_to > s_from {
let shift = s_to - s_from;
let multiplier = T::TEN.checked_pow(shift)?;
return value.checked_mul(multiplier);
}
let shift = s_from - s_to;
let divisor = match T::TEN.checked_pow(shift) {
Some(d) => d,
None => return Some(round_when_quotient_zero(value, mode)),
};
let (quotient, remainder) = value.div_rem(divisor);
if remainder == T::ZERO {
return Some(quotient);
}
let abs_rem = magnitude(remainder);
let abs_div = magnitude(divisor);
let cmp_r = abs_rem.cmp(&(abs_div - abs_rem));
let q_is_odd = quotient.bit(0);
let result_positive = (value < T::ZERO) == (divisor < T::ZERO);
if should_bump(mode, cmp_r, q_is_odd, result_positive) {
if result_positive {
Some(quotient + T::ONE)
} else {
Some(quotient - T::ONE)
}
} else {
Some(quotient)
}
}
#[inline]
fn magnitude<T: BigInt>(value: T) -> T {
if value < T::ZERO {
T::ZERO - value
} else {
value
}
}
#[inline]
fn round_when_quotient_zero<T: BigInt>(value: T, mode: RoundingMode) -> T {
if value == T::ZERO {
return T::ZERO;
}
let result_positive = value > T::ZERO;
let cmp_r = core::cmp::Ordering::Less;
if should_bump(mode, cmp_r, false, result_positive) {
if result_positive {
T::ONE
} else {
T::ZERO - T::ONE
}
} else {
T::ZERO
}
}
#[inline]
pub(crate) fn convert_magnitude<Src, Dst>(
src: Src,
s_from: u32,
s_to: u32,
mode: RoundingMode,
) -> Result<Dst, ConvertError>
where
Src: BigInt,
Dst: BigInt,
{
if Dst::LIMBS >= Src::LIMBS {
let widened: Dst = src.resize_to::<Dst>();
rescale_bigint(widened, s_from, s_to, mode).ok_or(ConvertError::Overflow)
} else {
let rescaled: Src = rescale_bigint(src, s_from, s_to, mode).ok_or(ConvertError::Overflow)?;
let narrowed: Dst = rescaled.resize_to::<Dst>();
let back: Src = narrowed.resize_to::<Src>();
if back == rescaled {
Ok(narrowed)
} else {
Err(ConvertError::Overflow)
}
}
}