use crate::support::rounding::RoundingMode;
use crate::wide_int::{wide_cast, WideStorage};
#[cfg(feature = "std")]
#[inline]
fn libm_cbrt(x: f64) -> f64 {
x.cbrt()
}
#[cfg(not(feature = "std"))]
#[inline]
fn libm_cbrt(_x: f64) -> f64 {
1.0
}
#[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 = if cfg!(feature = "std") && sig_bits >= 8 {
let top_shift = sig_bits - 64.min(sig_bits);
let mag_for_top: W = n >> top_shift;
let top_u128: u128 = {
let mut buf = [0u128; 1];
mag_for_top.mag_into_u128(&mut buf);
buf[0]
};
let top_f = top_u128 as f64;
let seed_f64 = libm_cbrt(top_f);
let third = top_shift / 3;
let residue = top_shift % 3; let factor: f64 = match residue {
1 => 1.2599210498948732, 2 => 1.5874010519681994, _ => 1.0,
};
let scaled_f64 = seed_f64 * factor;
let truncated = scaled_f64 as u128;
let frac_nonzero = (truncated as f64) != scaled_f64;
let seed_int: u128 = truncated
.saturating_add(if frac_nonzero { 1 } else { 0 })
.saturating_add(1);
let mut seed_w: W = wide_cast::<u128, W>(seed_int);
if third > 0 {
seed_w = seed_w << third;
}
if seed_w == zero {
one
} else {
seed_w
}
} else {
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);