use super::super::mul::limbs::{add_n, mul, mul_itch};
use super::kernels::row;
use super::window::{Registers, Ring};
pub(super) const MULLO_DC: usize = 40;
pub(in super::super) fn mullo_itch(n: usize) -> usize {
if n < MULLO_DC {
0
} else {
let low = split(n);
2 * n + mul_itch(low, low).max(mullo_itch(n - low))
}
}
#[inline]
fn split(n: usize) -> usize {
let high = if n < 120 * 36 / 25 {
n * 11 / 36
} else if n < 224 * 40 / 31 {
n * 9 / 40
} else {
n * 7 / 39
};
n - high
}
pub(in super::super) unsafe fn mullo(
r: *mut u64,
x: *const u64,
y: *const u64,
n: usize,
s: *mut u64,
) {
unsafe {
if n < MULLO_DC {
mullo_basecase(r, x, y, n);
} else {
mullo_dc(r, x, y, n, s);
}
}
}
unsafe fn mullo_basecase(r: *mut u64, x: *const u64, y: *const u64, n: usize) {
unsafe {
core::ptr::write_bytes(r, 0, n);
for j in 0..n {
row(r.add(j), x, n - j, *y.add(j));
}
}
}
unsafe fn mullo_dc(r: *mut u64, x: *const u64, y: *const u64, n: usize, s: *mut u64) {
let low = split(n);
let high = n - low;
unsafe {
let rest = s.add(2 * n);
mul(s, x, low, y, low, rest);
core::ptr::copy_nonoverlapping(s, r, n);
mullo(s.add(n), x.add(low), y, high, rest);
add_n(r.add(low), r.add(low), s.add(n), high);
mullo(s.add(n), x, y.add(low), high, rest);
add_n(r.add(low), r.add(low), s.add(n), high);
}
}
pub(super) struct Low {
pub(super) n: usize,
}
impl Low {
pub(super) fn itch(n: usize) -> usize {
n + mullo_itch(n)
}
}
impl Ring for Low {
fn n(&self) -> usize {
self.n
}
unsafe fn sqr(&self, r: *mut u64, a: *const u64, t: *mut u64) {
unsafe {
mullo(t, a, a, self.n, t.add(self.n));
core::ptr::copy_nonoverlapping(t, r, self.n);
}
}
unsafe fn mul(&self, r: *mut u64, a: *const u64, b: *const u64, t: *mut u64) {
unsafe {
mullo(t, a, b, self.n, t.add(self.n));
core::ptr::copy_nonoverlapping(t, r, self.n);
}
}
}
pub(super) struct Low2;
impl Registers for Low2 {
type V = u128;
#[inline(always)]
fn sqr(&self, a: u128) -> u128 {
a.wrapping_mul(a)
}
#[inline(always)]
fn mul(&self, a: u128, b: u128) -> u128 {
a.wrapping_mul(b)
}
}
pub(super) struct LowN<const N: usize>;
impl<const N: usize> Registers for LowN<N> {
type V = [u64; N];
#[inline(always)]
fn sqr(&self, a: [u64; N]) -> [u64; N] {
self.mul(a, a)
}
#[inline(always)]
fn mul(&self, a: [u64; N], b: [u64; N]) -> [u64; N] {
let mut r = [0u64; N];
for i in 0..N {
let mut carry = 0u64;
for j in 0..N - i {
let t =
u128::from(a[i]) * u128::from(b[j]) + u128::from(r[i + j]) + u128::from(carry);
r[i + j] = t as u64;
carry = (t >> 64) as u64;
}
}
r
}
}