use crate::support::rounding::RoundingMode;
use crate::wide_int::WideStorage;
#[inline]
#[must_use]
pub(crate) fn cbrt<S, W>(raw: S, scale: u32, mode: RoundingMode) -> S
where
S: WideStorage,
W: WideStorage,
{
if raw == S::ZERO {
return S::ZERO;
}
let zero = W::ZERO;
let one = W::ONE;
let three = W::ONE + W::ONE + W::ONE;
let ten = W::TEN;
let widened: W = raw.resize_to::<W>();
let negative = widened < zero;
let mag = if negative { -widened } else { widened };
let n: W = mag * ten.pow(2 * scale);
let sig_bits = W::BITS - n.leading_zeros();
let mut x = one << sig_bits.div_ceil(3);
loop {
let y = (x + x + n / (x * x)) / three;
if y >= x {
break;
}
x = y;
}
let q = x;
let eight_n = n << 3u32;
let t = q + q + one;
let cube = t * t * t;
let halfway_geq = eight_n >= cube;
let halfway_gt = eight_n > cube;
let tie = halfway_geq && !halfway_gt;
let two_q = q + q;
let eight_q_cubed = if q == zero { zero } else { two_q * two_q * two_q };
let residual_nonzero = eight_n > eight_q_cubed;
let q_is_odd = (q % (one + one)) != zero;
let bump = match mode {
RoundingMode::HalfToEven => halfway_gt || (tie && q_is_odd),
RoundingMode::HalfAwayFromZero => halfway_geq,
RoundingMode::HalfTowardZero => halfway_gt,
RoundingMode::Trunc => false,
RoundingMode::Floor => negative && residual_nonzero,
RoundingMode::Ceiling => !negative && residual_nonzero,
};
let q = if bump { q + one } else { q };
let signed = if negative { -q } else { q };
signed.resize_to::<S>()
}
macro_rules! decl_cbrt_kernel_shim {
($name:ident, $Storage:ty, $CbrtWide:ty) => {
#[inline]
#[must_use]
pub(crate) fn $name(raw: $Storage, scale: u32, mode: RoundingMode) -> $Storage {
cbrt::<$Storage, $CbrtWide>(raw, scale, mode)
}
};
}
#[cfg(any(feature = "d57", feature = "wide"))]
decl_cbrt_kernel_shim!(cbrt_d57, crate::wide_int::Int192, crate::wide_int::Int768);
#[cfg(any(feature = "d76", feature = "wide"))]
decl_cbrt_kernel_shim!(cbrt_d76, crate::wide_int::Int256, crate::wide_int::Int1024);
#[cfg(any(feature = "d115", feature = "wide"))]
decl_cbrt_kernel_shim!(cbrt_d115, crate::wide_int::Int384, crate::wide_int::Int1536);
#[cfg(any(feature = "d153", feature = "wide"))]
decl_cbrt_kernel_shim!(cbrt_d153, crate::wide_int::Int512, crate::wide_int::Int2048);
#[cfg(any(feature = "d230", feature = "wide"))]
decl_cbrt_kernel_shim!(cbrt_d230, crate::wide_int::Int768, crate::wide_int::Int3072);
#[cfg(any(feature = "d307", feature = "wide", feature = "x-wide"))]
decl_cbrt_kernel_shim!(cbrt_d307, crate::wide_int::Int1024, crate::wide_int::Int4096);
#[cfg(any(feature = "d462", feature = "x-wide"))]
decl_cbrt_kernel_shim!(cbrt_d462, crate::wide_int::Int1536, crate::wide_int::Int6144);
#[cfg(any(feature = "d616", feature = "x-wide"))]
decl_cbrt_kernel_shim!(cbrt_d616, crate::wide_int::Int2048, crate::wide_int::Int8192);
#[cfg(any(feature = "d924", feature = "xx-wide"))]
decl_cbrt_kernel_shim!(cbrt_d924, crate::wide_int::Int3072, crate::wide_int::Int12288);
#[cfg(any(feature = "d1232", feature = "xx-wide"))]
decl_cbrt_kernel_shim!(cbrt_d1232, crate::wide_int::Int4096, crate::wide_int::Int16384);