use crate::algo_x_support::seed::cbrt_seed;
use crate::int::types::traits::BigInt;
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
#[inline]
fn icbrt_w_seeded<const W: usize>(n: Int<W>) -> Int<W> {
let bits = n.bit_length();
let mag = n.unsigned_abs();
let mut seed_limbs = [0u64; W];
cbrt_seed(mag.as_limbs(), bits, &mut seed_limbs);
let x0 = Int::<W>::from_mag_limbs(&seed_limbs, false);
let x0 = if x0 <= Int::<W>::ZERO { Int::<W>::ONE } else { x0 };
let three = Int::<W>::from_i128(3);
let mut x = x0;
loop {
let y = (x + x + n / (x * x)) / three;
if y >= x {
break x;
}
x = y;
}
}
#[inline]
#[must_use]
pub(crate) fn cbrt_native<const N: usize, const W: usize>(
raw: Int<N>,
pow10_2scale: Int<W>,
mode: RoundingMode,
) -> Int<N> {
if raw == Int::<N>::ZERO {
return Int::<N>::ZERO;
}
let zero = Int::<W>::ZERO;
let one = Int::<W>::ONE;
let widened: Int<W> = raw.resize_to::<Int<W>>();
let negative = widened < zero;
let mag = if negative { -widened } else { widened };
let n: Int<W> = mag * pow10_2scale;
let q = icbrt_w_seeded::<W>(n);
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::<Int<N>>()
}
#[inline]
#[must_use]
pub(crate) fn cbrt_native_d57s20(raw: Int<3>, mode: RoundingMode) -> Int<3> {
cbrt_native::<3, 6>(raw, const { crate::consts::pow10::dispatch_int::<6>(2 * 20) }, mode)
}
#[cfg(all(
test,
feature = "std",
any(
feature = "d57",
feature = "d76",
feature = "d115",
feature = "d153",
feature = "d230",
feature = "d307"
)
))]
mod tests {
use super::cbrt_native;
#[cfg(feature = "d57")]
use super::cbrt_native_d57s20;
use crate::algos::cbrt::cbrt_newton::cbrt_newton;
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
const SCALE: u32 = 20;
const ALL_MODES: [RoundingMode; 6] = [
RoundingMode::HalfToEven,
RoundingMode::HalfAwayFromZero,
RoundingMode::HalfTowardZero,
RoundingMode::Trunc,
RoundingMode::Floor,
RoundingMode::Ceiling,
];
fn check_cell<const N: usize, const W: usize>(scale: u32, raws: &[i128])
where
crate::int::types::compute_limbs::Limbs<N>: crate::int::types::compute_limbs::ComputeLimbs,
{
for &r in raws {
let raw = Int::<N>::from_i128(r);
for mode in ALL_MODES {
let got = cbrt_native::<N, W>(raw, Int::<W>::TEN.pow(2 * scale), mode);
let want = cbrt_newton::<N>(raw, scale, mode);
assert_eq!(got, want, "N={N} W={W} scale={scale} raw={r} mode={mode:?}");
}
}
}
#[cfg(feature = "d57")]
#[test]
fn cbrt_native_matches_generic_newton_d57_s20_all_modes() {
let raws: [i128; 11] = [
0,
1, 100_000_000_000_000_000_000, 150_000_000_000_000_000_000, -150_000_000_000_000_000_000, 800_000_000_000_000_000_000, -800_000_000_000_000_000_000, 2_700_000_000_000_000_000_000, 12_345_678_901_234_567_890, (1i128 << 90) | 0xBEEF, (1i128 << 120) | 0x1357, ];
check_cell::<3, 6>(SCALE, &raws);
}
#[cfg(feature = "d76")]
#[test]
fn cbrt_native_matches_generic_newton_d76_s35() {
let raws: [i128; 7] = [
0,
1,
-800_000_000_000_000_000_000,
800_000_000_000_000_000_000,
(1i128 << 100) | 0xBEEF,
-((1i128 << 120) | 0x1357),
i128::MAX,
];
check_cell::<4, 8>(35, &raws);
}
#[cfg(feature = "d153")]
#[test]
fn cbrt_native_matches_generic_newton_d153_s75() {
let raws: [i128; 5] = [
0,
1,
-((1i128 << 100) | 0xABCD),
(1i128 << 120) | 0x99,
i128::MAX,
];
check_cell::<8, 16>(75, &raws);
}
#[cfg(feature = "d57")]
#[test]
fn cbrt_native_zero_is_zero() {
for mode in ALL_MODES {
assert_eq!(cbrt_native_d57s20(Int::<3>::ZERO, mode), Int::<3>::ZERO, "mode {mode:?}");
}
}
#[cfg(feature = "d57")]
#[test]
fn cbrt_native_perfect_cube_eight_is_two() {
let raw = Int::<3>::from_i128(800_000_000_000_000_000_000);
let two = Int::<3>::from_i128(200_000_000_000_000_000_000);
for mode in ALL_MODES {
assert_eq!(cbrt_native_d57s20(raw, mode), two, "mode {mode:?}");
}
}
fn near_max<const N: usize>(neg: bool) -> Int<N> {
let mut mag = [0u64; N];
for m in mag.iter_mut() {
*m = u64::MAX;
}
mag[N - 1] = u64::MAX >> 1;
Int::<N>::from_mag_limbs(&mag, neg)
}
#[test]
fn cbrt_native_near_max_magnitude_all_cells() {
for &neg in &[false, true] {
for mode in ALL_MODES {
#[cfg(feature = "d57")]
assert_eq!(cbrt_native::<3, 6>(near_max::<3>(neg), Int::<6>::TEN.pow(2 * 20), mode), cbrt_newton::<3>(near_max::<3>(neg), 20, mode), "D57 neg={neg} mode {mode:?}");
#[cfg(feature = "d76")]
assert_eq!(cbrt_native::<4, 8>(near_max::<4>(neg), Int::<8>::TEN.pow(2 * 35), mode), cbrt_newton::<4>(near_max::<4>(neg), 35, mode), "D76 neg={neg} mode {mode:?}");
#[cfg(feature = "d115")]
assert_eq!(cbrt_native::<6, 12>(near_max::<6>(neg), Int::<12>::TEN.pow(2 * 57), mode), cbrt_newton::<6>(near_max::<6>(neg), 57, mode), "D115 neg={neg} mode {mode:?}");
#[cfg(feature = "d153")]
assert_eq!(cbrt_native::<8, 16>(near_max::<8>(neg), Int::<16>::TEN.pow(2 * 75), mode), cbrt_newton::<8>(near_max::<8>(neg), 75, mode), "D153s75 neg={neg} mode {mode:?}");
#[cfg(feature = "d153")]
assert_eq!(cbrt_native::<8, 16>(near_max::<8>(neg), Int::<16>::TEN.pow(2 * 76), mode), cbrt_newton::<8>(near_max::<8>(neg), 76, mode), "D153s76 neg={neg} mode {mode:?}");
#[cfg(feature = "d230")]
assert_eq!(cbrt_native::<12, 25>(near_max::<12>(neg), Int::<25>::TEN.pow(2 * 115), mode), cbrt_newton::<12>(near_max::<12>(neg), 115, mode), "D230 neg={neg} mode {mode:?}");
#[cfg(feature = "d307")]
assert_eq!(cbrt_native::<16, 32>(near_max::<16>(neg), Int::<32>::TEN.pow(2 * 150), mode), cbrt_newton::<16>(near_max::<16>(neg), 150, mode), "D307 neg={neg} mode {mode:?}");
}
}
}
}