use crate::int::types::traits::BigInt;
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
#[inline]
fn round_and_narrow<const N: usize, const W: usize>(
q: Int<W>,
n: Int<W>,
negative: bool,
mode: RoundingMode,
) -> Int<N> {
let zero = Int::<W>::ZERO;
let one = Int::<W>::ONE;
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>>()
}
#[cfg(feature = "std")]
#[inline]
fn icbrt_w_f64_full<const W: usize>(n: Int<W>) -> Int<W> {
let seed_f64 = crate::algo_x_support::seed::cbrt_seed_f64_full(n.as_f64());
let seed = Int::<W>::from_f64(seed_f64);
let x0 = if seed <= Int::<W>::ZERO { Int::<W>::ONE } else { seed };
let three = Int::<W>::from_i128(3);
let mut x = (x0 + x0 + n / (x0 * x0)) / three;
if x <= Int::<W>::ZERO {
x = Int::<W>::ONE;
}
loop {
let y = (x + x + n / (x * x)) / three;
if y >= x {
break x;
}
x = y;
}
}
#[inline]
fn icbrt_w_shipped_seed<const W: usize>(n: Int<W>) -> Int<W> {
let bits = n.bit_length();
let mag = n.unsigned_abs();
let mut seed_limbs = [0u64; W];
crate::algo_x_support::seed::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_fast_a<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 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;
#[cfg(feature = "std")]
let q = if n.bit_length() <= 1020 {
icbrt_w_f64_full::<W>(n)
} else {
icbrt_w_shipped_seed::<W>(n)
};
#[cfg(not(feature = "std"))]
let q = icbrt_w_shipped_seed::<W>(n);
round_and_narrow::<N, W>(q, n, negative, mode)
}
#[inline]
fn icbrt_w_tight_topbits<const W: usize>(n: Int<W>) -> Int<W> {
let bits = n.bit_length();
let mag = n.unsigned_abs();
let mut seed_limbs = [0u64; W];
crate::algo_x_support::seed::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 + x0 + n / (x0 * x0)) / three;
if x <= Int::<W>::ZERO {
x = Int::<W>::ONE;
}
loop {
let y = (x + x + n / (x * x)) / three;
if y >= x {
break x;
}
x = y;
}
}
#[inline]
#[must_use]
pub(crate) fn cbrt_native_fast_b<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 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_tight_topbits::<W>(n);
round_and_narrow::<N, W>(q, n, negative, 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_fast_a, cbrt_native_fast_b};
use crate::algos::cbrt::cbrt_native::cbrt_native;
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
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]) {
let pow = Int::<W>::TEN.pow(2 * scale);
for &r in raws {
let raw = Int::<N>::from_i128(r);
for mode in ALL_MODES {
let want = cbrt_native::<N, W>(raw, pow, mode);
let got_a = cbrt_native_fast_a::<N, W>(raw, pow, mode);
let got_b = cbrt_native_fast_b::<N, W>(raw, pow, mode);
assert_eq!(got_a, want, "A: N={N} W={W} scale={scale} raw={r} mode={mode:?}");
assert_eq!(got_b, want, "B: N={N} W={W} scale={scale} raw={r} 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)
}
#[cfg(feature = "d57")]
#[test]
fn fast_candidates_match_native_d57_s20() {
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>(20, &raws);
}
#[cfg(any(
feature = "d76",
feature = "d115",
feature = "d153",
feature = "d230",
feature = "d307"
))]
#[test]
fn fast_candidates_match_native_other_cells() {
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,
];
#[cfg(feature = "d76")]
check_cell::<4, 8>(35, &raws);
#[cfg(feature = "d115")]
check_cell::<6, 12>(57, &raws);
#[cfg(feature = "d153")]
check_cell::<8, 16>(75, &raws);
#[cfg(feature = "d153")]
check_cell::<8, 16>(76, &raws);
#[cfg(feature = "d230")]
check_cell::<12, 25>(115, &raws);
#[cfg(feature = "d307")]
check_cell::<16, 32>(150, &raws);
}
#[cfg(any(
feature = "d57",
feature = "d76",
feature = "d115",
feature = "d153"
))]
#[test]
fn fast_a_routed_3n_widths_near_max() {
for &neg in &[false, true] {
for mode in ALL_MODES {
macro_rules! chk {
($n:literal, $w:literal, $($s:literal),+) => {{
$(
let pow = Int::<$w>::TEN.pow(2 * $s);
let raw = near_max::<$n>(neg);
let want = cbrt_native::<$n, $w>(raw, pow, mode);
assert_eq!(cbrt_native_fast_a::<$n, $w>(raw, pow, mode), want, "A 3N N={} W={} s={} neg={neg} mode={mode:?}", $n, $w, $s);
)+
}};
}
#[cfg(feature = "d57")]
chk!(3, 9, 0, 20, 28, 57);
#[cfg(feature = "d76")]
chk!(4, 12, 0, 20, 35, 76);
#[cfg(feature = "d115")]
chk!(6, 18, 0, 25, 57, 115);
#[cfg(feature = "d153")]
chk!(8, 24, 0, 25, 75, 153);
}
}
}
#[test]
fn fast_candidates_match_native_near_max_all_cells() {
for &neg in &[false, true] {
for mode in ALL_MODES {
macro_rules! chk {
($n:literal, $w:literal, $s:literal) => {{
let pow = Int::<$w>::TEN.pow(2 * $s);
let raw = near_max::<$n>(neg);
let want = cbrt_native::<$n, $w>(raw, pow, mode);
assert_eq!(cbrt_native_fast_a::<$n, $w>(raw, pow, mode), want, "A near_max N={} neg={neg} mode={mode:?}", $n);
assert_eq!(cbrt_native_fast_b::<$n, $w>(raw, pow, mode), want, "B near_max N={} neg={neg} mode={mode:?}", $n);
}};
}
#[cfg(feature = "d57")]
chk!(3, 6, 20);
#[cfg(feature = "d76")]
chk!(4, 8, 35);
#[cfg(feature = "d115")]
chk!(6, 12, 57);
#[cfg(feature = "d153")]
chk!(8, 16, 75);
#[cfg(feature = "d153")]
chk!(8, 16, 76);
#[cfg(feature = "d230")]
chk!(12, 25, 115);
#[cfg(feature = "d307")]
chk!(16, 32, 150);
}
}
}
}