mod nth;
mod sqrt;
use super::add::{SMALL, read_small, write_small};
use super::mul::{Operand, Product, STACK_LIMBS, WIDE, on_heap, on_stack, read_wide, to_limbs};
use super::{ByteOrder, Number};
use core::mem::MaybeUninit;
pub(super) use nth::quotient;
pub(super) use sqrt::sqrt_rem_u128;
impl Number {
#[track_caller]
pub fn isqrt(&self) -> Number {
if self.value.len() <= SMALL {
let x = unsafe { read_small(&self.value, self.byte_order) };
if self.negative && x != 0 {
negative_square_root();
}
let (s, _) = sqrt_rem_u128(x);
return small_number(self.byte_order, u128::from(s), false);
}
if self.value.len() <= WIDE {
return wide_square_root::<false>(self).0;
}
long_square_root::<false>(self).0
}
#[track_caller]
pub fn isqrt_rem(&self) -> (Number, Number) {
if self.value.len() <= SMALL {
let x = unsafe { read_small(&self.value, self.byte_order) };
if self.negative && x != 0 {
negative_square_root();
}
let (s, r) = sqrt_rem_u128(x);
let order = self.byte_order;
return (
small_number(order, u128::from(s), false),
small_number(order, r, false),
);
}
if self.value.len() <= WIDE {
return wide_square_root::<true>(self);
}
long_square_root::<true>(self)
}
#[track_caller]
pub fn nth_root(&self, n: u32) -> Number {
if n == 0 {
zero_degree();
}
if self.value.len() <= SMALL {
let x = unsafe { read_small(&self.value, self.byte_order) };
let negative = self.negative && x != 0;
if negative && n.is_multiple_of(2) {
imaginary_root();
}
return small_number(self.byte_order, small_root(x, n), negative);
}
long_root(self, n)
}
}
#[cold]
#[inline(never)]
#[track_caller]
fn negative_square_root() -> ! {
panic!("argument of integer square root cannot be negative")
}
#[cold]
#[inline(never)]
#[track_caller]
fn zero_degree() -> ! {
panic!("root degree n must be at least 1")
}
#[cold]
#[inline(never)]
#[track_caller]
fn imaginary_root() -> ! {
panic!("even roots of a negative are imaginary")
}
pub(super) fn small_number(order: ByteOrder, v: u128, negative: bool) -> Number {
let len = 16 - (v | 1).leading_zeros() as usize / 8;
let mut value = Vec::with_capacity(len);
unsafe {
write_small(value.as_mut_ptr(), order, v as u64, (v >> 64) as u64, len);
value.set_len(len);
}
Number {
value,
negative: negative && v != 0,
byte_order: order,
}
}
pub(super) fn small_root(x: u128, n: u32) -> u128 {
match n {
1 => return x,
2 => return u128::from(sqrt_rem_u128(x).0),
_ => {}
}
let bits = 128 - x.leading_zeros();
if bits <= n {
return u128::from(x != 0);
}
let estimate = if bits.div_ceil(n) <= ROUGH_BITS {
let top = (x << x.leading_zeros() >> 64) as u64;
rough_root(top, u64::from(bits), n)
} else {
let xf = ((x >> 64) as u64) as f64 * 18_446_744_073_709_551_616.0 + (x as u64) as f64;
if n == 3 {
xf.cbrt()
} else {
xf.powf(1.0 / f64::from(n))
}
};
let s = u128::from((estimate as u64).max(1));
let (low, high) = (power_at_most(s, n, x), power_at_most(s + 1, n, x));
if low && !high {
return s;
}
small_root_steps(x, n, s, low)
}
#[inline(always)]
fn power_at_most(s: u128, n: u32, x: u128) -> bool {
power_u128(s, n).is_some_and(|p| p <= x)
}
#[cold]
#[inline(never)]
fn small_root_steps(x: u128, n: u32, mut s: u128, low: bool) -> u128 {
let below = |s: u128| power_at_most(s, n, x);
if !low {
s -= 1;
while !below(s) {
s -= 1;
}
return s;
}
while below(s + 1) {
s += 1;
}
s
}
pub(super) const ROUGH_BITS: u32 = 18;
pub(super) fn rough_root(top: u64, bits: u64, n: u32) -> f64 {
let f = (top >> 11) as f64 * (1.0 / 4_503_599_627_370_496.0) - 1.0;
let horner = |c: &[f64], x: f64| c.iter().fold(0.0, |acc, &c| acc * x + c);
let log1p = horner(
&[
-0.025_123_203_286_102_475,
0.119_298_237_706_177_92,
-0.274_623_257_617_108_6,
0.455_527_088_061_034_7,
-0.717_557_872_422_149_5,
1.442_475_314_822_074_8,
2.123_740_891_505_907_2e-6,
],
f,
);
let l = ((bits - 1) as f64 + log1p) / f64::from(n);
let i = l.floor();
let exp = horner(
&[
0.001_895_107_290_998_152_7,
0.008_946_214_666_338_551,
0.055_863_282_659_265_955,
0.240_140_770_091_823_9,
0.693_154_620_003_301_9,
0.999_999_895_763_136,
],
l - i,
);
exp * f64::from_bits((i as u64 + 1023) << 52)
}
#[inline(always)]
fn power_u128(s: u128, n: u32) -> Option<u128> {
let bits = u64::from(128 - s.leading_zeros());
let n64 = u64::from(n);
if s.is_power_of_two() {
let shift = (bits - 1) * n64;
return if shift < 128 { Some(1 << shift) } else { None };
}
if bits * n64 <= 128 {
Some(s.wrapping_pow(n))
} else if bits.saturating_sub(1) * n64 >= 128 {
None
} else {
s.checked_pow(n)
}
}
#[inline(never)]
#[track_caller]
fn wide_square_root<const REM: bool>(a: &Number) -> (Number, Number) {
let order = a.byte_order;
let x = unsafe { read_wide(&a.value, a.byte_order) };
if a.negative && x != [0; 4] {
negative_square_root();
}
if x[2] == 0 && x[3] == 0 {
let (s, r) = sqrt_rem_u128(u128::from(x[1]) << 64 | u128::from(x[0]));
let rem = if REM {
small_number(order, r, false)
} else {
empty(order)
};
return (small_number(order, u128::from(s), false), rem);
}
let (s, r) = sqrt::sqrt_rem_wide::<REM>(x);
let rem = if REM {
unsafe { limbs_number(r.as_ptr(), 3, false, order) }
} else {
empty(order)
};
(small_number(order, s, false), rem)
}
#[inline(never)]
#[track_caller]
fn long_square_root<const REM: bool>(a: &Number) -> (Number, Number) {
let x = Operand::of(a);
let order = a.byte_order;
if a.negative && x.significant != 0 {
negative_square_root();
}
if x.significant <= SMALL {
let (s, r) = sqrt_rem_u128(unsafe { small_value(x) });
let rem = if REM {
small_number(order, r, false)
} else {
empty(order)
};
return (small_number(order, u128::from(s), false), rem);
}
let an = x.significant.div_ceil(8);
let tn = an.div_ceil(2);
let need =
even(an) + even(tn + 1) + if REM { even(tn + 1) } else { 0 } + sqrt::sqrtrem_itch(an);
let compute = |w: *mut MaybeUninit<u64>| {
let w: *mut u64 = w.cast();
unsafe {
let (m, s) = (w, w.add(even(an)));
let r = s.add(even(tn + 1));
let t = if REM { r.add(even(tn + 1)) } else { r };
to_limbs(x, m);
let rn = sqrt::sqrtrem::<REM>(s, r, m, an, t);
let root = limbs_number(s, tn, false, order);
let rem = if REM {
limbs_number(r, rn, false, order)
} else {
empty(order)
};
(root, rem)
}
};
if need <= LOCAL_LIMBS {
let mut local = [PAIR; LOCAL_LIMBS / 2];
compute(local.as_mut_ptr().cast())
} else if need <= STACK_LIMBS {
on_stack(compute)
} else {
on_heap(need, compute)
}
}
const LOCAL_LIMBS: usize = 64;
#[derive(Clone, Copy)]
#[repr(C, align(16))]
struct Pair([MaybeUninit<u64>; 2]);
const PAIR: Pair = Pair([MaybeUninit::uninit(); 2]);
#[inline(never)]
#[track_caller]
fn long_root(a: &Number, n: u32) -> Number {
let x = Operand::of(a);
let order = a.byte_order;
let negative = a.negative && x.significant != 0;
if negative && n.is_multiple_of(2) {
imaginary_root();
}
if x.significant <= SMALL {
let v = unsafe { small_value(x) };
return small_number(order, small_root(v, n), negative);
}
if n == 2 {
return a.isqrt();
}
let an = x.significant.div_ceil(8);
if 8 * x.significant as u64 <= u64::from(n) {
return small_number(order, 1, negative);
}
if n == 1 {
let compute = |w: *mut MaybeUninit<u64>| {
let w: *mut u64 = w.cast();
unsafe {
to_limbs(x, w);
limbs_number(w, an, negative, order)
}
};
return if an <= STACK_LIMBS {
on_stack(compute)
} else {
on_heap(an, compute)
};
}
let need = even(an) + nth::root_itch(an, n);
let compute = |w: *mut MaybeUninit<u64>| {
let w: *mut u64 = w.cast();
unsafe {
let (m, r) = (w, w.add(even(an)));
to_limbs(x, m);
let sn = nth::root(m, an, n, r);
limbs_number(r, sn, negative, order)
}
};
if need <= STACK_LIMBS {
on_stack(compute)
} else {
on_heap(need, compute)
}
}
unsafe fn small_value(x: Operand) -> u128 {
let mut l = [0u64; 2];
unsafe { to_limbs(x, l.as_mut_ptr()) };
u128::from(l[0]) | (u128::from(l[1]) << 64)
}
fn empty(order: ByteOrder) -> Number {
Number {
value: Vec::new(),
negative: false,
byte_order: order,
}
}
const fn even(n: usize) -> usize {
(n + 1) & !1
}
pub(super) unsafe fn limbs_number(
r: *const u64,
n: usize,
negative: bool,
order: ByteOrder,
) -> Number {
unsafe {
let mut top = n;
while top > 0 && *r.add(top - 1) == 0 {
top -= 1;
}
let product = if top == 0 {
Product {
r: core::ptr::null(),
len: 1,
negative: false,
}
} else {
let len = 8 * top - (*r.add(top - 1)).leading_zeros() as usize / 8;
Product { r, len, negative }
};
let mut v = Vec::with_capacity(product.len);
product.write(v.as_mut_ptr(), order);
v.set_len(product.len);
Number {
value: v,
negative: product.negative,
byte_order: order,
}
}
}
pub(super) unsafe fn lshift(dst: *mut u64, src: *const u64, n: usize, s: u32) -> u64 {
unsafe {
if s == 0 {
core::ptr::copy(src, dst, n);
return 0;
}
let out = *src.add(n - 1) >> (64 - s);
for i in (1..n).rev() {
*dst.add(i) = (*src.add(i) << s) | (*src.add(i - 1) >> (64 - s));
}
*dst = *src << s;
out
}
}
pub(super) unsafe fn rshift(dst: *mut u64, src: *const u64, n: usize, s: u32) {
debug_assert!(0 < s && s < 64);
unsafe {
for i in 0..n - 1 {
*dst.add(i) = (*src.add(i) >> s) | (*src.add(i + 1) << (64 - s));
}
*dst.add(n - 1) = *src.add(n - 1) >> s;
}
}
pub(super) unsafe fn mul_1(r: *mut u64, x: *const u64, n: usize, y: u64) -> u64 {
let mut carry = 0;
unsafe {
for i in 0..n {
let p = u128::from(*x.add(i)) * u128::from(y) + u128::from(carry);
*r.add(i) = p as u64;
carry = (p >> 64) as u64;
}
}
carry
}
pub(super) unsafe fn addmul_1(r: *mut u64, x: *const u64, n: usize, y: u64) -> u64 {
let mut carry = 0;
unsafe {
for i in 0..n {
let p =
u128::from(*x.add(i)) * u128::from(y) + u128::from(*r.add(i)) + u128::from(carry);
*r.add(i) = p as u64;
carry = (p >> 64) as u64;
}
}
carry
}