use super::super::div::limbs::{bdiv_q, binvert_limb};
use super::super::root::quotient;
use super::Limbs;
use super::odd::{ODD_FACTORIALS, SMALL_LIMBS, SWING, odd_factorial, small_odd_factorial};
use super::primes::{Remainders, Sieve};
use super::product::{Packer, product, product_into};
use core::mem::MaybeUninit;
pub(super) const SMALL_N: [u64; 64] = [
18_446_744_073_709_551_615,
12_686_161_381_664,
9_506_325_305,
132_496_420,
7_910_345,
1_080_110,
246_703,
79_266,
32_301,
15_633,
8602,
5222,
3424,
2386,
1748,
1333,
1052,
854,
710,
603,
521,
457,
406,
365,
332,
305,
282,
262,
246,
232,
220,
209,
200,
192,
185,
179,
174,
169,
164,
161,
157,
154,
151,
149,
147,
145,
143,
141,
140,
139,
137,
136,
136,
135,
134,
134,
133,
133,
132,
132,
132,
132,
131,
131,
];
const INVERSES: [u128; 66] = {
let mut t = [1u128; 66];
let mut odd = 1u128;
let mut i = 1;
while i < 66 {
let f = (i as u128) >> (i as u128).trailing_zeros();
odd = odd.wrapping_mul(f);
let mut x = odd;
let mut step = 0;
while step < 6 {
x = x.wrapping_mul(2u128.wrapping_sub(odd.wrapping_mul(x)));
step += 1;
}
t[i] = x;
i += 1;
}
t
};
const BY_PRIMES_K: u64 = 100;
fn by_primes_for(n: u64, k: u64) -> bool {
k > BY_PRIMES_K && u128::from(k) * u128::from(k) > 25 * u128::from(n)
}
const HENSEL_LIMIT: usize = 1 << 18;
fn twos(n: u64, k: u64) -> u64 {
u64::from(k.count_ones() + (n - k).count_ones() - n.count_ones())
}
#[inline(always)]
fn odd(x: u64) -> u64 {
x >> x.trailing_zeros()
}
#[inline]
pub(super) fn small(n: u64, k: u64) -> Option<u128> {
match k {
0 => Some(1),
1 => Some(u128::from(n)),
2..=65 if n <= SMALL_N[k as usize - 2] => {
let mut p = 1u128;
for i in 0..k {
p = p.wrapping_mul(u128::from(odd(n - i)));
}
Some(p.wrapping_mul(INVERSES[k as usize]) << twos(n, k))
}
_ => None,
}
}
pub(super) fn long<R>(n: u64, k: u64, place: impl FnOnce(&mut [u64], usize, u64) -> R) -> R {
let shift = twos(n, k);
if by_primes_for(n, k) {
let mut x = by_primes(n, k);
let len = x.len();
x.push(0);
return place(&mut x, len, shift);
}
if k < SWING {
return small_by_division(n, k, |x, len| place(x, len, shift));
}
let mut x = by_division(n, k);
let len = x.len();
x.push(0);
place(&mut x, len, shift)
}
fn small_by_division<R>(n: u64, k: u64, place: impl FnOnce(&mut [u64], usize) -> R) -> R {
const LIMBS: usize = SWING as usize;
let mut f = [MaybeUninit::<u64>::uninit(); LIMBS];
let mut nf = 0;
let limit = u64::MAX / n;
let mut prod = 1u64;
for x in n - k + 1..=n {
let o = odd(x);
if prod > limit {
f[nf] = MaybeUninit::new(prod);
nf += 1;
prod = o;
} else {
prod *= o;
}
}
f[nf] = MaybeUninit::new(prod);
nf += 1;
let mut p = [MaybeUninit::<u64>::uninit(); LIMBS + 1];
let mut halves = [MaybeUninit::<u64>::uninit(); 2 * LIMBS + 64];
let mut scratch = Vec::new();
let p = p.as_mut_ptr().cast::<u64>();
let pn = unsafe {
let f = core::slice::from_raw_parts(f.as_ptr().cast::<u64>(), nf);
product_into(f, p, halves.as_mut_ptr().cast(), &mut scratch)
};
if k < ODD_FACTORIALS.len() as u64 {
let x = unsafe {
*p.add(pn) = 0;
core::slice::from_raw_parts_mut(p, pn + 1)
};
let len = divide_by_limb(&mut x[..pn], ODD_FACTORIALS[k as usize]);
return place(x, len);
}
let mut d = [0u64; SMALL_LIMBS];
let dn = small_odd_factorial(k, &mut d);
let qn = pn - dn + 1;
let mut q = [MaybeUninit::<u64>::uninit(); LIMBS + 1];
let q = q.as_mut_ptr().cast::<u64>();
unsafe {
bdiv_q(q, p, qn, d.as_ptr(), dn, binvert_limb(d[0]));
let mut len = qn;
while *q.add(len - 1) == 0 {
len -= 1;
}
*q.add(qn) = 0;
place(core::slice::from_raw_parts_mut(q, qn + 1), len)
}
}
fn by_division(n: u64, k: u64) -> Limbs {
let mut packer = Packer::new(n, k as usize / 2 + 2);
for f in n - k + 1..=n {
packer.push(odd(f));
}
let mut p = product(&packer.finish());
exact_quotient(&mut p, &odd_factorial(k))
}
fn divide_by_limb(x: &mut [u64], d: u64) -> usize {
let v = binvert_limb(d);
let mut borrow = 0u64;
for limb in x.iter_mut() {
let (t, b) = limb.overflowing_sub(borrow);
let q = t.wrapping_mul(v);
*limb = q;
borrow = ((u128::from(q) * u128::from(d)) >> 64) as u64 + u64::from(b);
}
debug_assert!(borrow == 0, "the division was not exact");
let mut len = x.len();
while len > 1 && x[len - 1] == 0 {
len -= 1;
}
len
}
fn exact_quotient(x: &mut Limbs, d: &[u64]) -> Limbs {
let (xn, dn) = (x.len(), d.len());
let qn = xn - dn + 1;
let m = qn.min(dn);
if qn * m - m * m / 2 >= HENSEL_LIMIT {
return quotient(x, d);
}
let mut q = Vec::with_capacity(qn + 1);
q.resize(qn, 0);
unsafe {
bdiv_q(
q.as_mut_ptr(),
x.as_mut_ptr(),
qn,
d.as_ptr(),
dn,
binvert_limb(d[0]),
);
}
while q.last() == Some(&0) {
q.pop();
}
q
}
fn by_primes(n: u64, k: u64) -> Limbs {
let sieve = Sieve::new(n);
let mut packer = Packer::new(n, (n / 64) as usize + 4);
let root = super::super::root::sqrt_rem_u128(u128::from(n)).0;
sieve.each(3, root, |p| {
let (mut a, mut b) = (n, k);
let mut carry = 0;
let mut f = 1;
while a > 0 {
let (da, db) = (a % p, b % p);
carry = u64::from(da < db + carry);
if carry == 1 {
f *= p;
}
a /= p;
b /= p;
}
packer.push(f);
});
let (mut rn, mut rk) = (Remainders::of(n), Remainders::of(k));
sieve.each(root + 1, n / 2, |p| {
if rn.of_divisor(p) < rk.of_divisor(p) {
packer.push(p);
}
});
sieve.each((n - k + 1).max(n / 2 + 1), n, |p| packer.push(p));
product(&packer.finish())
}