use crate::natural::arithmetic::add::{
limbs_add_limb_to_out, limbs_add_same_length_to_out, limbs_slice_add_greater_in_place_left,
limbs_slice_add_limb_in_place, limbs_slice_add_same_length_in_place_left,
};
use crate::natural::arithmetic::add_mul::limbs_slice_add_mul_limb_same_length_in_place_left;
use crate::natural::arithmetic::div::limbs_div_divisor_of_limb_max_with_carry_in_place;
use crate::natural::arithmetic::div_exact::{
limbs_div_exact_3_in_place, limbs_div_exact_limb_in_place,
};
use crate::natural::arithmetic::mul::poly_eval::limbs_shl_and_add_same_length_in_place_left;
use crate::natural::arithmetic::mul::toom::BIT_CORRECTION;
use crate::natural::arithmetic::shl::limbs_shl_to_out;
use crate::natural::arithmetic::shr::limbs_slice_shr_in_place;
use crate::natural::arithmetic::sub::{
limbs_sub_greater_in_place_left, limbs_sub_limb_in_place, limbs_sub_same_length_in_place_left,
limbs_sub_same_length_in_place_right, limbs_sub_same_length_in_place_with_overlap,
limbs_sub_same_length_to_out,
};
use crate::natural::arithmetic::sub_mul::limbs_sub_mul_limb_same_length_in_place_left;
use crate::platform::{
Limb, AORSMUL_FASTER_2AORSLSH, AORSMUL_FASTER_3AORSLSH, AORSMUL_FASTER_AORS_2AORSLSH,
AORSMUL_FASTER_AORS_AORSLSH,
};
use malachite_base::num::arithmetic::traits::{
DivisibleByPowerOf2, Parity, WrappingAddAssign, WrappingSubAssign,
};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::slices::slice_test_zero;
use std::mem::swap;
pub(crate) fn limbs_mul_toom_interpolate_5_points(
c: &mut [Limb],
v_2: &mut [Limb],
v_neg_1: &mut [Limb],
k: usize,
two_r: usize,
v_neg_1_neg: bool,
mut v_inf_0: Limb,
) {
let two_k = k << 1;
let two_k_plus_1 = two_k + 1;
let four_k_plus_1 = two_k_plus_1 + two_k;
assert_eq!(v_neg_1.len(), two_k_plus_1);
assert!(two_r <= two_k);
let v_1 = &c[two_k..four_k_plus_1]; let v_2 = &mut v_2[..two_k_plus_1];
if v_neg_1_neg {
assert!(!limbs_slice_add_same_length_in_place_left(v_2, v_neg_1));
} else {
assert!(!limbs_sub_same_length_in_place_left(v_2, v_neg_1));
}
limbs_div_exact_3_in_place(v_2);
if v_neg_1_neg {
assert!(!limbs_slice_add_same_length_in_place_left(v_neg_1, v_1));
} else {
assert!(!limbs_sub_same_length_in_place_right(v_1, v_neg_1));
}
assert_eq!(limbs_slice_shr_in_place(v_neg_1, 1), 0);
let (c_lo, v_1) = c.split_at_mut(two_k);
if limbs_sub_same_length_in_place_left(&mut v_1[..two_k], c_lo) {
v_1[two_k].wrapping_sub_assign(1);
}
let v_1 = &mut v_1[..two_k_plus_1];
assert!(!limbs_sub_same_length_in_place_left(v_2, v_1));
assert_eq!(limbs_slice_shr_in_place(v_2, 1), 0);
assert!(!limbs_sub_same_length_in_place_left(v_1, v_neg_1));
let (c_lo, c_hi) = c.split_at_mut(3 * k + 1);
if limbs_slice_add_same_length_in_place_left(&mut c_lo[k..], v_neg_1) {
assert!(!limbs_slice_add_limb_in_place(
&mut c_hi[..two_r + k - 1],
1,
));
}
let v_inf = &mut c_hi[k - 1..two_r + k - 1];
let saved = v_inf[0]; v_inf[0] = v_inf_0; let mut carry = limbs_shl_to_out(v_neg_1, &v_inf[..two_r], 1);
if limbs_sub_same_length_in_place_left(&mut v_2[..two_r], &v_neg_1[..two_r]) {
carry += 1;
}
assert!(!limbs_sub_limb_in_place(&mut v_2[two_r..], carry));
if two_r > k + 1 {
let (c_lo, c_hi) = c[k << 2..].split_at_mut(k + 1);
if limbs_slice_add_same_length_in_place_left(c_lo, &v_2[k..]) {
assert!(!limbs_slice_add_limb_in_place(
&mut c_hi[..two_r - k - 1],
1,
));
}
} else {
assert!(!limbs_slice_add_same_length_in_place_left(
&mut c[k << 2..(k << 2) + two_r],
&v_2[k..k + two_r],
));
}
split_into_chunks_mut!(c, k << 1, [_unused, v_1], v_inf);
let carry = limbs_sub_same_length_in_place_left(&mut v_1[..two_r], &v_inf[..two_r]);
v_inf_0 = v_inf[0]; v_inf[0] = saved;
split_into_chunks_mut!(c, k, [_unused, c1], v1);
let v1 = &mut v1[..two_k_plus_1];
if carry {
assert!(!limbs_sub_limb_in_place(&mut v1[two_r..], 1)); }
if limbs_sub_same_length_in_place_left(c1, &v_2[..k]) {
assert!(!limbs_sub_limb_in_place(v1, 1));
}
let (c3, v_inf) = c[3 * k..].split_at_mut(k);
if limbs_slice_add_same_length_in_place_left(c3, &v_2[..k]) {
v_inf[0].wrapping_add_assign(1);
assert!(v_inf[0] >= 1); }
assert!(!limbs_slice_add_limb_in_place(&mut v_inf[..two_r], v_inf_0));
}
pub(crate) fn limbs_mul_toom_interpolate_6_points(
out: &mut [Limb],
n: usize,
n_high: usize,
w4_neg: bool,
w4: &mut [Limb],
w2_neg: bool,
w2: &mut [Limb],
w1: &mut [Limb],
) {
assert_ne!(n, 0);
let m = 2 * n + 1;
assert_ne!(n_high, 0);
assert!(n_high < m);
assert_eq!(w1.len(), m);
assert_eq!(w2.len(), m);
assert_eq!(w4.len(), m);
let (w5, w3) = out[..4 * n + 1].split_at_mut(n << 1);
if w2_neg {
limbs_slice_add_same_length_in_place_left(w2, w1);
} else {
limbs_sub_same_length_in_place_right(w1, w2);
}
limbs_slice_shr_in_place(w2, 2);
let (w1_last, w1_init) = w1.split_last_mut().unwrap();
if limbs_sub_same_length_in_place_left(w1_init, w5) {
w1_last.wrapping_sub_assign(1);
}
limbs_slice_shr_in_place(w1, 1);
limbs_sub_same_length_in_place_left(w1, w2);
limbs_slice_shr_in_place(w1, 1);
if w4_neg {
limbs_slice_add_same_length_in_place_left(w4, w3);
} else {
limbs_sub_same_length_in_place_right(w3, w4);
}
limbs_slice_shr_in_place(w4, 1);
limbs_sub_same_length_in_place_left(w2, w4);
limbs_div_exact_3_in_place(w2);
limbs_sub_same_length_in_place_left(w3, w4);
let (w3_last, w3_init) = w3.split_last_mut().unwrap();
if limbs_sub_same_length_in_place_left(w3_init, w5) {
w3_last.wrapping_sub_assign(1);
}
limbs_sub_same_length_in_place_left(w1, w3);
limbs_div_exact_3_in_place(w1);
let out = &mut out[n..];
let (out_lo, out_hi) = out.split_at_mut(m);
if limbs_slice_add_same_length_in_place_left(out_lo, w4) {
assert!(!limbs_slice_add_limb_in_place(&mut out_hi[..n], 1));
}
let out_hi = &out[n << 2..];
let mut carry = limbs_shl_to_out(w4, &out_hi[..n_high], 2);
let (w2_lo, w2_hi) = w2.split_at_mut(n_high);
if limbs_sub_same_length_in_place_left(w2_lo, &w4[..n_high]) {
carry += 1;
}
assert!(!limbs_sub_limb_in_place(w2_hi, carry));
let (w2_lo, w2_hi) = w2.split_at(n);
let (out_lo, out) = out.split_at_mut(n);
if limbs_sub_same_length_in_place_left(out_lo, w2_lo) {
assert!(!limbs_sub_limb_in_place(&mut out[..m], 1));
}
let (out_lo, out_hi) = out.split_at_mut(n << 1);
let carry = Limb::from(limbs_slice_add_same_length_in_place_left(
&mut out_lo[n..],
w2_lo,
));
let carry_1 = out_hi[0] + carry;
let (w2_hi_last, w2_hi_init) = w2_hi.split_last().unwrap();
let mut carry = *w2_hi_last;
let (w1_lo, w1_hi) = w1.split_at_mut(n);
if limbs_add_same_length_to_out(out_hi, w1_lo, w2_hi_init) {
carry += 1;
}
assert!(!limbs_slice_add_limb_in_place(w1_hi, carry));
let mut carry_2 = 0;
let (w1_last, w1_init) = w1.split_last().unwrap();
let w1_init = &w1_init[n..];
let out_hi = &mut out[3 * n..];
if n_high > n {
carry_2 = *w1_last;
if limbs_slice_add_same_length_in_place_left(&mut out_hi[..n], w1_init) {
carry_2.wrapping_add_assign(1);
}
} else if limbs_slice_add_same_length_in_place_left(&mut out_hi[..n_high], &w1_init[..n_high]) {
carry_2 = 1;
}
let out = &mut out[..3 * n + n_high];
let carry = limbs_sub_same_length_in_place_with_overlap(out, n << 1);
let out_high = out.last_mut().unwrap();
let embankment = out_high.wrapping_sub(1);
*out_high = 1;
let out = &mut out[n..];
if n_high > n {
if carry_1 > carry_2 {
assert!(!limbs_slice_add_limb_in_place(
&mut out[n..],
carry_1 - carry_2,
));
} else {
assert!(!limbs_sub_limb_in_place(&mut out[n..], carry_2 - carry_1));
}
if carry {
assert!(!limbs_sub_limb_in_place(&mut out[n_high..], 1));
}
assert!(!limbs_slice_add_limb_in_place(&mut out[3 * n..], carry_2));
} else {
assert!(!limbs_slice_add_limb_in_place(&mut out[n..], carry_1));
if carry {
carry_2.wrapping_add_assign(1);
}
assert!(!limbs_sub_limb_in_place(&mut out[n_high..], carry_2));
}
out.last_mut().unwrap().wrapping_add_assign(embankment);
}
const WANT_ASSERT: bool = true;
pub(crate) fn limbs_mul_toom_interpolate_7_points(
out: &mut [Limb],
n: usize,
n_high: usize,
w1_neg: bool,
w1: &mut [Limb],
w3_neg: bool,
w3: &mut [Limb],
w4: &mut [Limb],
w5: &mut [Limb],
scratch: &mut [Limb],
) {
let m = 2 * n + 1;
assert_ne!(n_high, 0);
assert!(n_high < m);
assert_eq!(w1.len(), m);
assert_eq!(w3.len(), m);
assert_eq!(w4.len(), m);
assert_eq!(w5.len(), m);
let (w0, remainder) = out.split_at_mut(n << 1);
let (w2, w6) = remainder.split_at_mut(n << 2);
let w2 = &mut w2[..m];
let w6 = &mut w6[..n_high];
limbs_slice_add_same_length_in_place_left(w5, w4);
if w1_neg {
limbs_slice_add_same_length_in_place_left(w1, w4);
} else {
limbs_sub_same_length_in_place_right(w4, w1);
}
assert!(w1[0].even());
limbs_slice_shr_in_place(w1, 1);
limbs_sub_greater_in_place_left(w4, w0);
limbs_sub_same_length_in_place_left(w4, w1);
assert!(w4[0].divisible_by_power_of_2(2));
limbs_slice_shr_in_place(w4, 2); scratch[n_high] = limbs_shl_to_out(scratch, w6, 4);
limbs_sub_greater_in_place_left(w4, &scratch[..=n_high]);
if w3_neg {
limbs_slice_add_same_length_in_place_left(w3, w2);
} else {
limbs_sub_same_length_in_place_right(w2, w3);
}
assert!(w3[0].even());
limbs_slice_shr_in_place(w3, 1);
limbs_sub_same_length_in_place_left(w2, w3);
limbs_sub_mul_limb_same_length_in_place_left(w5, w2, 65);
limbs_sub_greater_in_place_left(w2, w6);
limbs_sub_greater_in_place_left(w2, w0);
limbs_slice_add_mul_limb_same_length_in_place_left(w5, w2, 45);
assert!(w5[0].even());
limbs_slice_shr_in_place(w5, 1);
limbs_sub_same_length_in_place_left(w4, w2);
limbs_div_exact_3_in_place(w4);
limbs_sub_same_length_in_place_left(w2, w4);
limbs_sub_same_length_in_place_right(w5, w1);
limbs_shl_to_out(scratch, w3, 3);
limbs_sub_same_length_in_place_left(w5, &scratch[..m]);
limbs_div_exact_limb_in_place(w5, 9);
limbs_sub_same_length_in_place_left(w3, w5);
limbs_div_exact_limb_in_place(w1, 15);
limbs_slice_add_same_length_in_place_left(w1, w5);
assert!(w1[0].even());
limbs_slice_shr_in_place(w1, 1); limbs_sub_same_length_in_place_left(w5, w1);
let two_n = n << 1;
assert!(w1[two_n] < 2);
assert!(w2[two_n] < 3);
assert!(w3[two_n] < 4);
assert!(w4[two_n] < 3);
assert!(w5[two_n] < 2);
let (out_lo, out_hi) = out[n..].split_at_mut(m);
if limbs_slice_add_same_length_in_place_left(out_lo, w1) {
assert!(!limbs_slice_add_limb_in_place(&mut out_hi[..n], 1));
}
split_into_chunks_mut!(&mut out[3 * n..], n, [out_3, out_4, out_5], remainder);
let mut addend = out_4[0];
let (w3_lo, w3_hi) = w3.split_at_mut(n);
if limbs_slice_add_same_length_in_place_left(out_3, w3_lo) {
addend.wrapping_add_assign(1);
}
assert!(!limbs_slice_add_limb_in_place(w3_hi, addend));
let (w3_hi_last, w3_hi_init) = w3_hi.split_last_mut().unwrap();
let mut addend = *w3_hi_last;
let (w4_lo, w4_hi) = w4.split_at_mut(n);
if limbs_add_same_length_to_out(out_4, w3_hi_init, w4_lo) {
addend += 1;
}
assert!(!limbs_slice_add_limb_in_place(w4_hi, addend));
let (w4_last, w4_init) = w4_hi.split_last_mut().unwrap();
let mut addend = *w4_last;
let (w5_lo, w5_hi) = w5.split_at_mut(n);
if limbs_add_same_length_to_out(out_5, w4_init, w5_lo) {
addend += 1;
}
assert!(!limbs_slice_add_limb_in_place(w5_hi, addend));
if n_high > n + 1 {
assert!(!limbs_slice_add_greater_in_place_left(remainder, w5_hi));
} else {
let (w5_hi_lo, w5_hi_hi) = w5_hi.split_at_mut(n_high);
assert!(!limbs_slice_add_same_length_in_place_left(
&mut remainder[..n_high],
w5_hi_lo,
));
if WANT_ASSERT && n + n_high < m {
slice_test_zero(w5_hi_hi);
}
}
}
pub_test! {limbs_shl_and_sub_same_length(
xs: &mut [Limb],
ys: &[Limb],
shift: u64,
scratch: &mut [Limb],
) -> Limb {
let n = ys.len();
let mut carry = limbs_shl_to_out(scratch, ys, shift);
if limbs_sub_same_length_in_place_left(&mut xs[..n], &scratch[..n]) {
carry.wrapping_add_assign(1);
}
carry
}}
fn limbs_shl_and_sub(xs: &mut [Limb], ys: &[Limb], shift: u64, scratch: &mut [Limb]) {
let (ys_head, ys_tail) = ys.split_first().unwrap();
assert!(!limbs_sub_limb_in_place(xs, *ys_head >> shift));
let carry = limbs_shl_and_sub_same_length(xs, ys_tail, Limb::WIDTH - shift, scratch);
assert!(!limbs_sub_limb_in_place(&mut xs[ys.len() - 1..], carry));
}
fn limbs_shl_and_sub_special(
xs_init: &mut [Limb],
xs_last: &mut Limb,
ys: &[Limb],
shift: u64,
scratch: &mut [Limb],
) {
let (ys_head, ys_tail) = ys.split_first().unwrap();
if limbs_sub_limb_in_place(xs_init, *ys_head >> shift) {
*xs_last = xs_last.checked_sub(1).unwrap();
}
let carry = limbs_shl_and_sub_same_length(xs_init, ys_tail, Limb::WIDTH - shift, scratch);
if limbs_sub_limb_in_place(&mut xs_init[ys_tail.len()..], carry) {
*xs_last = xs_last.checked_sub(1).unwrap();
}
}
pub(crate) fn limbs_mul_toom_interpolate_8_points(
out: &mut [Limb],
n: usize,
s_plus_t: usize,
r3: &mut [Limb],
r7: &mut [Limb],
scratch: &mut [Limb],
) {
assert!(s_plus_t >= n);
let m = 3 * n + 1;
assert_eq!(r3.len(), m);
assert_eq!(r7.len(), m);
let (out, remainder) = out.split_at_mut(n << 1);
let (out_2, remainder) = remainder.split_at_mut(n);
let (r5, r1) = remainder.split_at_mut(n << 2);
let r1 = &mut r1[..s_plus_t];
let r5_lo = &mut r5[..m];
limbs_shl_and_sub(&mut r3[n..], out, 4, scratch);
let carry = limbs_shl_and_sub_same_length(r3, r1, 12, scratch);
assert!(!limbs_sub_limb_in_place(&mut r3[s_plus_t..], carry));
limbs_shl_and_sub(&mut r5_lo[n..], out, 2, scratch);
let carry = limbs_shl_and_sub_same_length(r5_lo, r1, 6, scratch);
assert!(!limbs_sub_limb_in_place(&mut r5_lo[s_plus_t..], carry));
let (r7_last, r7_init) = r7.split_last_mut().unwrap();
if limbs_sub_same_length_in_place_left(&mut r7_init[n..], out) {
r7_last.wrapping_sub_assign(1);
}
let (r7_lo, r7_hi) = r7.split_at_mut(s_plus_t);
if limbs_sub_same_length_in_place_left(r7_lo, r1) {
assert!(!limbs_sub_limb_in_place(r7_hi, 1));
}
assert!(!limbs_sub_same_length_in_place_left(r3, r5_lo));
assert_eq!(limbs_slice_shr_in_place(r3, 2), 0);
assert!(!limbs_sub_same_length_in_place_left(r5_lo, r7));
assert!(!limbs_sub_same_length_in_place_left(r3, r5_lo));
limbs_div_exact_limb_in_place(r3, 45);
limbs_div_exact_3_in_place(r5_lo);
assert_eq!(limbs_shl_and_sub_same_length(r5_lo, r3, 2, scratch), 0);
split_into_chunks_mut!(r5_lo, n, [r5_lo_0, r5_lo_1, r5_lo_2], r5_lo_3);
let (r7_lo, r7) = r7.split_at_mut(n);
let out = &mut out[n..];
let carry_1 = limbs_slice_add_same_length_in_place_left(out, r7_lo);
let carry_2 = limbs_sub_same_length_in_place_left(out, r5_lo_0);
if carry_1 && !carry_2 {
assert!(!limbs_slice_add_limb_in_place(r7, 1));
} else if !carry_1 && carry_2 {
assert!(!limbs_sub_limb_in_place(r7, 1));
}
let (r7_lo, r7_hi) = r7.split_at_mut(n);
if limbs_sub_same_length_to_out(out_2, r7_lo, r5_lo_1) {
assert!(!limbs_sub_limb_in_place(r7_hi, 1));
}
let (r3_lo, r3) = r3.split_at_mut(n);
if limbs_slice_add_same_length_in_place_left(r5_lo_2, r3_lo) {
r5_lo_3[0].wrapping_add_assign(1);
}
let carry_1 = limbs_slice_add_same_length_in_place_left(&mut r5_lo[..n + 1], r7_hi);
let (r5_lo_lo, r5_lo_hi) = r5_lo.split_at_mut(n << 1);
let carry_2 = limbs_sub_same_length_in_place_left(&mut r5_lo_lo[..n + 1], r5_lo_hi);
if carry_1 && !carry_2 {
assert!(!limbs_slice_add_limb_in_place(&mut r5_lo[n + 1..], 1));
} else if !carry_1 && carry_2 {
assert!(!limbs_sub_limb_in_place(&mut r5_lo[n + 1..], 1));
}
assert!(!limbs_sub_same_length_in_place_left(&mut r5_lo[n..], r3));
let r5_3n = r5[3 * n];
let (r3_lo, r3) = r3.split_at_mut(n);
if limbs_add_limb_to_out(&mut r5[3 * n..], r3_lo, r5_3n) {
assert!(!limbs_slice_add_limb_in_place(r3, 1));
}
let mut r3_n = r3[n];
let (r1_lo, r1_hi) = r1.split_at_mut(n);
if limbs_slice_add_same_length_in_place_left(r1_lo, &r3[..n]) {
r3_n.wrapping_add_assign(1);
}
if s_plus_t == n {
assert_eq!(r3_n, 0);
} else {
assert!(!limbs_slice_add_limb_in_place(r1_hi, r3_n));
}
}
fn limbs_div_255_in_place(xs: &mut [Limb]) {
limbs_div_divisor_of_limb_max_with_carry_in_place(xs, Limb::MAX / 255, 0);
}
fn limbs_aors_mul_or_two_sh_aors_helper(
xs: &mut [Limb],
ys: &[Limb],
s: Limb,
sign: bool,
s1: u64,
sign1: bool,
s2: u64,
sign2: bool,
scratch: &mut [Limb],
) {
if AORSMUL_FASTER_2AORSLSH {
if sign {
limbs_slice_add_mul_limb_same_length_in_place_left(xs, ys, s);
} else {
limbs_sub_mul_limb_same_length_in_place_left(xs, ys, s);
}
} else {
if sign1 {
limbs_shl_and_add_same_length_in_place_left(xs, ys, s1, scratch);
} else {
limbs_shl_and_sub_same_length(xs, ys, s1, scratch);
}
if sign2 {
limbs_shl_and_add_same_length_in_place_left(xs, ys, s2, scratch);
} else {
limbs_shl_and_sub_same_length(xs, ys, s2, scratch);
}
}
}
fn limbs_aors_mul_or_three_sh_aors_helper(
xs: &mut [Limb],
ys: &[Limb],
s: Limb,
s1: u64,
s2: u64,
s3: u64,
no_carry: bool,
scratch: &mut [Limb],
) {
if AORSMUL_FASTER_3AORSLSH {
let c = limbs_sub_mul_limb_same_length_in_place_left(xs, ys, s);
if no_carry {
assert_eq!(c, 0);
}
} else {
let c = limbs_shl_and_sub_same_length(xs, ys, s1, scratch);
if no_carry {
assert_eq!(c, 0);
}
let c = limbs_shl_and_sub_same_length(xs, ys, s2, scratch);
if no_carry {
assert_eq!(c, 0);
}
let c = limbs_shl_and_sub_same_length(xs, ys, s3, scratch);
if no_carry {
assert_eq!(c, 0);
}
}
}
fn limbs_aors_mul_or_aors_and_two_sh_aors_helper(
xs: &mut [Limb],
ys: &[Limb],
s: Limb,
s1: u64,
s2: u64,
scratch: &mut [Limb],
) {
if AORSMUL_FASTER_AORS_2AORSLSH {
assert_eq!(limbs_sub_mul_limb_same_length_in_place_left(xs, ys, s), 0);
} else {
assert!(!limbs_sub_same_length_in_place_left(xs, ys));
assert_eq!(
limbs_shl_and_add_same_length_in_place_left(xs, ys, s1, scratch),
0
);
assert_eq!(limbs_shl_and_sub_same_length(xs, ys, s2, scratch), 0);
}
}
fn limbs_aors_mul_or_aors_and_sh_aors_helper(
xs: &mut [Limb],
ys: &[Limb],
s: Limb,
sign1: bool,
s2: u64,
scratch: &mut [Limb],
) {
if AORSMUL_FASTER_AORS_AORSLSH {
limbs_sub_mul_limb_same_length_in_place_left(xs, ys, s);
} else {
if sign1 {
limbs_slice_add_same_length_in_place_left(xs, ys);
} else {
limbs_sub_same_length_in_place_left(xs, ys);
}
limbs_shl_and_sub_same_length(xs, ys, s2, scratch);
}
}
pub_crate_test! {limbs_mul_toom_interpolate_12_points<'a>(
out: &mut [Limb],
mut r1: &'a mut [Limb],
r3: &mut [Limb],
mut r5: &'a mut [Limb],
n: usize,
s_plus_t: usize,
half: bool,
mut scratch: &'a mut [Limb],
) {
let m = 3 * n + 1;
assert_eq!(r1.len(), m);
assert_eq!(r3.len(), m);
assert_eq!(r5.len(), m);
let (out_lo, remainder) = out.split_at_mut(3 * n);
let out_lo = &mut out_lo[..n << 1];
let (r4, r2) = remainder.split_at_mut(n << 2);
let r4 = &mut r4[..m];
if half {
let (r2, r0) = r2.split_at_mut(4 * n);
let r0 = &mut r0[..s_plus_t];
let (r3_lo, r3_hi) = r3.split_at_mut(s_plus_t);
if limbs_sub_same_length_in_place_left(r3_lo, r0) {
assert!(!limbs_sub_limb_in_place(r3_hi, 1));
}
let carry = limbs_shl_and_sub_same_length(r2, r0, 10, scratch);
assert!(!limbs_sub_limb_in_place(&mut r2[s_plus_t..m], carry));
limbs_shl_and_sub(r5, r0, 2, scratch);
let carry = limbs_shl_and_sub_same_length(r1, r0, 20, scratch);
assert!(!limbs_sub_limb_in_place(&mut r1[s_plus_t..], carry));
limbs_shl_and_sub(r4, r0, 4, scratch);
};
let r2 = &mut r2[..m];
let carry = limbs_shl_and_sub_same_length(&mut r4[n..], out_lo, 20, scratch);
r4.last_mut().unwrap().wrapping_sub_assign(carry);
limbs_shl_and_sub(&mut r1[n..], out_lo, 4, scratch);
assert!(!limbs_add_same_length_to_out(scratch, r1, r4));
limbs_sub_same_length_in_place_left(r4, r1); swap(&mut r1, &mut scratch);
let r1 = &mut r1[..m];
let carry = limbs_shl_and_sub_same_length(&mut r5[n..], out_lo, 10, scratch);
r5.last_mut().unwrap().wrapping_sub_assign(carry);
limbs_shl_and_sub(&mut r2[n..], out_lo, 2, scratch);
limbs_sub_same_length_to_out(scratch, r5, r2); assert!(!limbs_slice_add_same_length_in_place_left(r2, r5));
swap(&mut r5, &mut scratch);
let (r3_last, r3_init) = r3.split_last_mut().unwrap();
if limbs_sub_same_length_in_place_left(&mut r3_init[n..], out_lo) {
r3_last.wrapping_sub_assign(1);
}
limbs_aors_mul_or_aors_and_sh_aors_helper(r4, r5, 257, false, 8, scratch);
limbs_div_exact_limb_in_place(r4, 2835 << 2);
let r4_last = r4.last_mut().unwrap();
if r4_last.leading_zeros() < 3 {
*r4_last |= Limb::MAX << (Limb::WIDTH - 2);
}
limbs_aors_mul_or_two_sh_aors_helper(r5, r4, 60, true, 2, false, 6, true, scratch);
limbs_div_255_in_place(r5);
assert_eq!(limbs_shl_and_sub_same_length(r2, r3, 5, scratch), 0);
limbs_aors_mul_or_three_sh_aors_helper(r1, r2, 100, 6, 5, 2, true, scratch);
assert_eq!(limbs_shl_and_sub_same_length(r1, r3, 9, scratch), 0);
limbs_div_exact_limb_in_place(r1, 42525);
limbs_aors_mul_or_aors_and_two_sh_aors_helper(r2, r1, 225, 5, 8, scratch);
limbs_div_exact_limb_in_place(r2, 9 << 2);
assert!(!limbs_sub_same_length_in_place_left(r3, r2));
limbs_sub_same_length_in_place_right(r2, r4);
assert_eq!(limbs_slice_shr_in_place(r4, 1), 0);
assert!(!limbs_sub_same_length_in_place_left(r2, r4));
let r1 = &mut r1[..m];
limbs_slice_add_same_length_in_place_left(r5, r1);
assert_eq!(limbs_slice_shr_in_place(r5, 1), 0);
assert!(!limbs_sub_same_length_in_place_left(r3, r1));
assert!(!limbs_sub_same_length_in_place_left(r1, r5));
split_into_chunks_mut!(out, n, [_unused, out_1, out_2, out_3], out_4);
split_into_chunks_mut!(r5, n, [r5_0, r5_1], r5_2);
if limbs_slice_add_same_length_in_place_left(out_1, r5_0) {
if limbs_add_limb_to_out(out_2, r5_1, 1) {
assert!(!limbs_slice_add_limb_in_place(r5_2, 1));
}
} else {
out_2.copy_from_slice(r5_1);
}
let (r5_last, r5_2) = r5_2.split_last_mut().unwrap();
let mut carry = *r5_last;
if limbs_slice_add_same_length_in_place_left(out_3, r5_2) {
carry.wrapping_add_assign(1);
}
assert!(!limbs_slice_add_limb_in_place(
&mut out_4[..2 * n + 1],
carry,
));
split_into_chunks_mut!(out_4, n, [_unused, out_5, out_6, out_7], out_8);
split_into_chunks_mut!(r3, n, [r3_0, r3_1], r3_2);
if limbs_slice_add_same_length_in_place_left(out_5, r3_0) {
out_6[0].wrapping_add_assign(1);
}
let out_6_first = out_6[0];
if limbs_add_limb_to_out(out_6, r3_1, out_6_first) {
assert!(!limbs_slice_add_limb_in_place(r3_2, 1));
}
let (r3_last, r3_2) = r3_2.split_last_mut().unwrap();
let mut carry = *r3_last;
if limbs_slice_add_same_length_in_place_left(out_7, r3_2) {
carry.wrapping_add_assign(1);
}
assert!(!limbs_slice_add_limb_in_place(
&mut out_8[..2 * n + 1],
carry,
));
split_into_chunks_mut!(out_8, n, [_unused, out_9], out_10);
let (r1_0, r1_1) = r1.split_at_mut(n);
if limbs_slice_add_same_length_in_place_left(out_9, r1_0) {
out_10[0].wrapping_add_assign(1);
}
let out_10_first = out_10[0];
if half {
let (out_10, out_11) = out_10.split_at_mut(n);
let (r1_1, r1_2) = r1_1.split_at_mut(n);
if limbs_add_limb_to_out(out_10, r1_1, out_10_first) {
assert!(!limbs_slice_add_limb_in_place(r1_2, 1));
}
if s_plus_t > n {
let (out_11, out_12) = out_11.split_at_mut(n);
let (r1_last, r1_2) = r1_2.split_last_mut().unwrap();
let mut carry = *r1_last;
if limbs_slice_add_same_length_in_place_left(out_11, r1_2) {
carry.wrapping_add_assign(1);
}
assert!(!limbs_slice_add_limb_in_place(
&mut out_12[..s_plus_t - n],
carry,
));
} else {
assert!(!limbs_slice_add_same_length_in_place_left(
&mut out_11[..s_plus_t],
&r1_2[..s_plus_t],
));
}
} else {
assert!(!limbs_add_limb_to_out(
out_10,
&r1_1[..s_plus_t],
out_10_first,
));
}
}}
#[cfg(feature = "32_bit_limbs")]
const CORRECTED_WIDTH: u64 = 42 - Limb::WIDTH;
#[cfg(not(feature = "32_bit_limbs"))]
const CORRECTED_WIDTH: u64 = 42;
pub_crate_test! {limbs_mul_toom_interpolate_16_points<'a>(
out: &mut [Limb],
r1: &mut [Limb],
mut r3: &'a mut [Limb],
mut r5: &'a mut [Limb],
mut r7: &'a mut [Limb],
n: usize,
s_plus_t: usize,
half: bool,
mut scratch: &'a mut [Limb],
) {
let m = 3 * n + 1;
assert!(s_plus_t <= n << 1);
assert_eq!(r1.len(), m);
assert_eq!(r3.len(), m);
assert_eq!(r5.len(), m);
assert_eq!(r7.len(), m);
let (pp_lo, remainder) = out.split_at_mut(3 * n);
let pp_lo = &mut pp_lo[..n << 1];
split_into_chunks_mut!(remainder, n << 2, [r6, r4], r2);
let r4 = &mut r4[..m];
let r6 = &mut r6[..m];
if half {
let (r2, r0) = r2.split_at_mut(n << 2);
let r0 = &mut r0[..s_plus_t];
let r2 = &mut r2[..m];
let (r4_lo, r4_hi) = r4.split_at_mut(s_plus_t);
if limbs_sub_same_length_in_place_left(r4_lo, r0) {
assert!(!limbs_sub_limb_in_place(r4_hi, 1));
}
let carry = limbs_shl_and_sub_same_length(r3, r0, 14, scratch);
assert!(!limbs_sub_limb_in_place(&mut r3[s_plus_t..], carry));
limbs_shl_and_sub(r6, r0, 2, scratch);
let carry = limbs_shl_and_sub_same_length(r2, r0, 28, scratch);
assert!(!limbs_sub_limb_in_place(&mut r2[s_plus_t..], carry));
limbs_shl_and_sub(r5, r0, 4, scratch);
if BIT_CORRECTION {
let carry = limbs_shl_and_sub_same_length(&mut r1[1..], r0, CORRECTED_WIDTH, scratch);
limbs_sub_limb_in_place(&mut r1[s_plus_t + 1..], carry);
let r5_first = r5.first_mut().unwrap();
let carry = *r5_first;
*r5_first = 0x80;
limbs_shl_and_sub_special(r7, r5_first, r0, 6, scratch);
*r5_first = carry;
} else {
let carry = limbs_shl_and_sub_same_length(r1, r0, CORRECTED_WIDTH, scratch);
assert!(!limbs_sub_limb_in_place(&mut r1[s_plus_t..], carry));
limbs_shl_and_sub(r7, r0, 6, scratch);
}
}
let r2 = &mut r2[..m];
let (r5_last, r5_init) = r5[n..].split_last_mut().unwrap();
r5_last.wrapping_sub_assign(limbs_shl_and_sub_same_length(r5_init, pp_lo, 28, scratch));
limbs_shl_and_sub(&mut r2[n..], pp_lo, 4, scratch);
limbs_sub_same_length_to_out(scratch, r5, r2); assert!(!limbs_slice_add_same_length_in_place_left(r2, r5));
swap(&mut r5, &mut scratch);
let (r6_last, r6_init) = r6[n..].split_last_mut().unwrap();
r6_last.wrapping_sub_assign(limbs_shl_and_sub_same_length(r6_init, pp_lo, 14, scratch));
limbs_shl_and_sub(&mut r3[n..], pp_lo, 2, scratch);
assert!(!limbs_add_same_length_to_out(scratch, r3, r6));
limbs_sub_same_length_in_place_left(r6, r3); swap(&mut r3, &mut scratch);
let r1_hi = &mut r1[n..];
if BIT_CORRECTION {
limbs_shl_and_sub_same_length(&mut r7[n + 1..], pp_lo, CORRECTED_WIDTH, scratch);
let (pp_lo_first, pp_lo_tail) = pp_lo.split_first().unwrap();
assert!(!limbs_sub_limb_in_place(r1_hi, pp_lo_first >> 6));
let carry = limbs_shl_and_sub_same_length(r1_hi, pp_lo_tail, Limb::WIDTH - 6, scratch);
limbs_sub_limb_in_place(&mut r1_hi[2 * n - 1..], carry);
} else {
let carry = limbs_shl_and_sub_same_length(&mut r7[n..], pp_lo, CORRECTED_WIDTH, scratch);
r7.last_mut().unwrap().wrapping_sub_assign(carry);
limbs_shl_and_sub(r1_hi, pp_lo, 6, scratch);
}
limbs_sub_same_length_to_out(scratch, r7, r1);
limbs_slice_add_same_length_in_place_left(r1, r7);
swap(&mut r7, &mut scratch);
let (r4_last, r4_init) = r4[n..].split_last_mut().unwrap();
if limbs_sub_same_length_in_place_left(r4_init, pp_lo) {
r4_last.wrapping_sub_assign(1);
}
limbs_aors_mul_or_two_sh_aors_helper(r5, r6, 1028, false, 2, false, 10, false, scratch);
limbs_sub_mul_limb_same_length_in_place_left(r7, r5, 1300); limbs_aors_mul_or_three_sh_aors_helper(r7, r6, 1052688, 4, 12, 20, false, scratch);
limbs_div_exact_limb_in_place(r7, 188513325);
limbs_div_255_in_place(r7);
limbs_sub_mul_limb_same_length_in_place_left(r5, r7, 12567555);
limbs_div_exact_limb_in_place(r5, 2835 << 6);
let r5_last = r5.last_mut().unwrap();
if r5_last.leading_zeros() < 7 {
*r5_last |= Limb::MAX << (Limb::WIDTH - 6);
}
limbs_aors_mul_or_aors_and_sh_aors_helper(r6, r7, 4095, true, 12, scratch);
limbs_aors_mul_or_two_sh_aors_helper(r6, r5, 240, true, 8, true, 4, false, scratch);
limbs_div_exact_limb_in_place(r6, 255 << 2);
let r6_last = r6.last_mut().unwrap();
if r6_last.leading_zeros() < 3 {
*r6_last |= Limb::MAX << (Limb::WIDTH - 2);
}
assert_eq!(limbs_shl_and_sub_same_length(r3, r4, 7, scratch), 0);
assert_eq!(limbs_shl_and_sub_same_length(r2, r4, 13, scratch), 0);
assert_eq!(limbs_sub_mul_limb_same_length_in_place_left(r2, r3, 400), 0);
limbs_shl_and_sub_same_length(r1, r4, 19, scratch);
limbs_sub_mul_limb_same_length_in_place_left(r1, r2, 1428);
limbs_sub_mul_limb_same_length_in_place_left(r1, r3, 112896);
limbs_div_exact_limb_in_place(r1, 182712915);
limbs_div_255_in_place(r1);
assert_eq!(
limbs_sub_mul_limb_same_length_in_place_left(r2, r1, 15181425),
0
);
limbs_div_exact_limb_in_place(r2, 42525 << 4);
limbs_aors_mul_or_aors_and_two_sh_aors_helper(r3, r1, 3969, 7, 12, scratch);
assert_eq!(limbs_sub_mul_limb_same_length_in_place_left(r3, r2, 900), 0);
limbs_div_exact_limb_in_place(r3, 9 << 4);
assert!(!limbs_sub_same_length_in_place_left(r4, r1));
assert!(!limbs_sub_same_length_in_place_left(r4, r3));
assert!(!limbs_sub_same_length_in_place_left(r4, r2));
limbs_slice_add_same_length_in_place_left(r6, r2);
assert_eq!(limbs_slice_shr_in_place(r6, 1), 0);
assert!(!limbs_sub_greater_in_place_left(r2, r6));
limbs_sub_same_length_in_place_right(r3, r5);
assert_eq!(limbs_slice_shr_in_place(r5, 1), 0);
assert!(!limbs_sub_same_length_in_place_left(r3, r5));
limbs_slice_add_same_length_in_place_left(r7, r1);
assert_eq!(limbs_slice_shr_in_place(r7, 1), 0);
assert!(!limbs_sub_same_length_in_place_left(r1, r7));
split_into_chunks_mut!(out, n, [_unused, out_1, out_2, out_3], out_4);
split_into_chunks_mut!(r7, n, [r7_0, r7_1], r7_2);
if limbs_slice_add_same_length_in_place_left(out_1, r7_0) {
if limbs_add_limb_to_out(out_2, r7_1, 1) {
assert!(!limbs_slice_add_limb_in_place(r7_2, 1));
}
} else {
out_2.copy_from_slice(r7_1);
}
let (r7_last, r7_2) = r7_2.split_last_mut().unwrap();
let mut carry = *r7_last;
if limbs_slice_add_same_length_in_place_left(out_3, r7_2) {
carry.wrapping_add_assign(1);
}
assert!(!limbs_slice_add_limb_in_place(out_4, carry));
split_into_chunks_mut!(out_4, n, [_unused, out_5, out_6, out_7], out_8);
split_into_chunks_mut!(r5, n, [r5_0, r5_1], r5_2);
if limbs_slice_add_same_length_in_place_left(out_5, r5_0) {
out_6[0].wrapping_add_assign(1);
}
let out_6_first = out_6[0];
if limbs_add_limb_to_out(out_6, r5_1, out_6_first) {
assert!(!limbs_slice_add_limb_in_place(r5_2, 1));
}
let (r5_last, r5_2) = r5_2.split_last_mut().unwrap();
let mut carry = *r5_last;
if limbs_slice_add_same_length_in_place_left(out_7, r5_2) {
carry.wrapping_add_assign(1);
}
assert!(!limbs_slice_add_limb_in_place(out_8, carry));
split_into_chunks_mut!(out_8, n, [_unused, out_9, out_10, out_11], out_12);
split_into_chunks_mut!(r3, n, [r3_0, r3_1], r3_2);
if limbs_slice_add_same_length_in_place_left(out_9, r3_0) {
out_10[0].wrapping_add_assign(1);
}
let out_10_first = out_10[0];
if limbs_add_limb_to_out(out_10, r3_1, out_10_first) {
assert!(!limbs_slice_add_limb_in_place(r3_2, 1));
}
let (r3_last, r3_2) = r3_2.split_last_mut().unwrap();
let mut carry = *r3_last;
if limbs_slice_add_same_length_in_place_left(out_11, r3_2) {
carry.wrapping_add_assign(1);
}
assert!(!limbs_slice_add_limb_in_place(out_12, carry));
split_into_chunks_mut!(out_12, n, [_unused, out_13], out_14);
let (r1_0, r1_1) = r1.split_at_mut(n);
if limbs_slice_add_same_length_in_place_left(out_13, r1_0) {
out_14[0].wrapping_add_assign(1);
}
let out_14_first = out_14[0];
if half {
let (out_14, out_15) = out_14.split_at_mut(n);
let (r1_1, r1_2) = r1_1.split_at_mut(n);
if limbs_add_limb_to_out(out_14, r1_1, out_14_first) {
assert!(!limbs_slice_add_limb_in_place(r1_2, 1));
}
if s_plus_t > n {
let (out_15, out_16) = out_15.split_at_mut(n);
let (r1_last, r1_2) = r1_2.split_last_mut().unwrap();
let mut carry = *r1_last;
if limbs_slice_add_same_length_in_place_left(out_15, r1_2) {
carry.wrapping_add_assign(1);
}
assert!(!limbs_slice_add_limb_in_place(
&mut out_16[..s_plus_t - n],
carry,
));
} else {
assert!(!limbs_slice_add_same_length_in_place_left(
&mut out_15[..s_plus_t],
&r1_2[..s_plus_t],
));
}
} else {
assert!(!limbs_add_limb_to_out(
&mut out_14[..s_plus_t],
&r1_1[..s_plus_t],
out_14_first,
));
}
}}