use crate::integer_polynomial::arithmetic::coefficient::PolynomialCoefficient;
use crate::integer_polynomial::arithmetic::mul::karatsuba::{
add_shifted_rev, revbin_in, revbin_out,
};
use crate::integer_polynomial::arithmetic::vec::{vec_add, vec_sub_assign};
use alloc::vec;
use core::borrow::Borrow;
use malachite_base::num::arithmetic::traits::{CeilingLogBase2, PowerOf2};
use malachite_base::num::conversion::traits::ExactFrom;
fn square_karatsuba_recursive<C: PolynomialCoefficient, T: Borrow<C>>(
out: &mut [C],
xs: &[T],
temp: &mut [C],
bits: u64,
) {
let length = usize::power_of_2(bits);
let m = length >> 1;
if length == 1 {
out[0] = xs[0].borrow().square_ref();
out[1] = C::ZERO;
return;
}
let (sums, temp) = temp.split_at_mut(length);
vec_add(&mut sums[..m], &xs[..m], &xs[m..length]);
let (out_lo, out_hi) = out.split_at_mut(length);
square_karatsuba_recursive(out_lo, &xs[..m], temp, bits - 1);
square_karatsuba_recursive(out_hi, &sums[..m], temp, bits - 1);
square_karatsuba_recursive(sums, &xs[m..], temp, bits - 1);
vec_sub_assign(&mut out_hi[..length], out_lo);
vec_sub_assign(&mut out_hi[..length], sums);
add_shifted_rev(out_lo, sums, bits);
}
crate_test_fn! {square_to_out_karatsuba<C: PolynomialCoefficient>(out: &mut [C], xs: &[C]) {
let len = xs.len();
assert_ne!(len, 0);
if len == 1 {
out[0] = xs[0].square_ref();
return;
}
let loglen = u64::exact_from(len).ceiling_log_base_2();
let length = usize::power_of_2(loglen);
let zero = C::ZERO;
let mut rev = vec![&zero; length];
let mut scratch = vec![C::ZERO; length << 2];
let (rev_out, temp) = scratch.split_at_mut(length << 1);
revbin_in(&mut rev, xs, loglen);
square_karatsuba_recursive(rev_out, &rev, temp, loglen);
revbin_out(out, rev_out, loglen + 1);
}}