mod binomial;
mod odd;
mod primes;
mod product;
use super::mul::Product;
use super::root::{lshift, small_number};
use super::{ByteOrder, Number};
type Limbs = Vec<u64>;
const FACTORIALS: [u128; 35] = {
let mut t = [1u128; 35];
let mut i = 1;
while i < 35 {
t[i] = t[i - 1] * i as u128;
i += 1;
}
t
};
impl Number {
pub fn factorial(n: u64, byte_order: ByteOrder) -> Number {
if n < FACTORIALS.len() as u64 {
return small_number(byte_order, FACTORIALS[n as usize], false);
}
long_factorial(n, byte_order)
}
pub fn binomial(n: u64, k: u64, byte_order: ByteOrder) -> Number {
if k > n {
return small_number(byte_order, 0, false);
}
let k = k.min(n - k);
if let Some(c) = binomial::small(n, k) {
return small_number(byte_order, c, false);
}
long_binomial(n, k, byte_order)
}
}
#[inline(never)]
fn long_factorial(n: u64, order: ByteOrder) -> Number {
let shift = n - u64::from(n.count_ones());
if n < odd::SWING {
let mut x = [0u64; odd::SMALL_LIMBS + 1];
let mut small = [0u64; odd::SMALL_LIMBS];
let len = odd::small_odd_factorial(n, &mut small);
x[..len].copy_from_slice(&small[..len]);
return shifted_number(&mut x, len, shift, order);
}
let mut x = odd::odd_factorial(n);
let len = x.len();
x.push(0);
shifted_number(&mut x, len, shift, order)
}
#[inline(never)]
fn long_binomial(n: u64, k: u64, order: ByteOrder) -> Number {
binomial::long(n, k, |x, len, shift| shifted_number(x, len, shift, order))
}
fn shifted_number(x: &mut [u64], mut len: usize, shift: u64, order: ByteOrder) -> Number {
let zeros = usize::try_from(shift / 8).expect("capacity overflow");
let bits = (shift % 8) as u32;
if bits != 0 {
let out = unsafe { lshift(x.as_mut_ptr(), x.as_ptr(), len, bits) };
if out != 0 {
x[len] = out;
len += 1;
}
}
let top = x[len - 1];
let bytes = 8 * len - top.leading_zeros() as usize / 8;
let total = bytes.checked_add(zeros).expect("capacity overflow");
let p = Product {
r: x.as_ptr(),
len: bytes,
negative: false,
};
let mut v = Vec::with_capacity(total);
unsafe {
let dst = v.as_mut_ptr();
if order == ByteOrder::BigEndian {
p.write(dst, order);
core::ptr::write_bytes(dst.add(bytes), 0, zeros);
} else {
core::ptr::write_bytes(dst, 0, zeros);
p.write(dst.add(zeros), order);
}
v.set_len(total);
}
Number {
value: v,
negative: false,
byte_order: order,
}
}