use crate::{
add,
arch::word::{SignedWord, Word},
div,
helper_macros::debug_assert_zero,
math,
memory::{self, Memory},
mul, shift, sqr,
Sign::*,
};
use alloc::alloc::Layout;
pub const MIN_LEN: usize = 16;
pub fn memory_requirement_up_to(n: usize) -> Layout {
let num_words = 4 * n + 13 * (math::ceil_log2(n) as usize);
memory::array_layout::<Word>(num_words)
}
pub fn square(b: &mut [Word], a: &[Word], memory: &mut Memory) {
let n = a.len();
debug_assert!(n >= MIN_LEN && b.len() == 2 * n);
let n3 = (n + 2) / 3;
let n3_short = n - 2 * n3;
let (a0, a12) = a.split_at(n3);
let (a1, a2) = a12.split_at(n3);
let mut carry: SignedWord = 0;
let mut carry_c0: SignedWord = 0; let mut carry_c1: SignedWord = 0; let mut carry_c2: SignedWord = 0; let mut carry_c3: SignedWord = 0;
let (t1, mut memory) = memory.allocate_slice_fill(2 * n3 + 2, 0);
{
let t1_short = &mut t1[..2 * n3];
sqr::sqr(t1_short, a0, &mut memory);
carry_c0 += add::add_signed_same_len_in_place(&mut b[..2 * n3], Positive, t1_short);
carry_c2 += add::add_signed_in_place(&mut b[2 * n3..4 * n3 + 2], Negative, t1_short);
t1[2 * n3] = mul::mul_word_in_place(t1_short, 3);
t1[2 * n3 + 1] = 0;
}
let (a_eval, mut memory) = memory.allocate_slice_copy_fill(n3 + 1, a0, 0);
{
a_eval[n3] = mul::add_mul_word_same_len_in_place(&mut a_eval[..n3], 2, a1);
a_eval[n3] += mul::add_mul_word_in_place(&mut a_eval[..n3], 4, a2);
let (v2, mut mem) = memory.allocate_slice_fill(2 * (n3 + 1), 0);
sqr::sqr(v2, a_eval, &mut mem);
debug_assert_zero!(add::add_signed_same_len_in_place(t1, Positive, v2));
}
{
let (c_eval, mut memory) = memory.allocate_slice_fill(2 * n3 + 2, 0);
let c_short = &mut c_eval[..2 * n3_short];
sqr::sqr(c_short, a2, &mut memory);
carry_c2 += add::add_signed_in_place(&mut b[2 * n3..4 * n3 + 2], Negative, c_short);
carry += add::add_signed_same_len_in_place(&mut b[4 * n3..], Positive, c_short);
c_eval[2 * n3_short] = mul::mul_word_in_place(c_short, 12);
debug_assert_zero!(add::sub_in_place(t1, &c_eval[..2 * n3_short + 1]));
}
let (t2, mut memory) = memory.allocate_slice_fill(2 * n3 + 2, 0);
{
let (a02, mut memory) = memory.allocate_slice_copy_fill(n3 + 1, a0, 0);
a02[n3] = Word::from(add::add_in_place(&mut a02[..n3], a2));
a_eval.copy_from_slice(a02);
a_eval[n3] += Word::from(add::add_same_len_in_place(&mut a_eval[..n3], a1));
sqr::sqr(t2, a_eval, &mut memory);
carry_c1 += add::add_signed_in_place(&mut b[n3..3 * n3 + 2], Positive, t2);
a_eval.copy_from_slice(a02);
let neg_sign = add::sub_in_place_with_sign(a_eval, a1);
if neg_sign == Negative {
a_eval[..n3].copy_from_slice(a1);
a_eval[n3] = 0;
debug_assert_zero!(add::sub_in_place(a_eval, a02));
}
}
let (c_eval, mut memory) = memory.allocate_slice_fill(2 * (n3 + 1), 0);
sqr::sqr(c_eval, a_eval, &mut memory);
debug_assert_zero!(add::add_signed_same_len_in_place(t2, Positive, c_eval));
debug_assert_zero!(mul::add_mul_word_same_len_in_place(t1, 2, c_eval));
let t1_rem = div::div_by_word_in_place(t1, 6);
let t2_rem = shift::shr_in_place(t2, 1);
assert_eq!(t1_rem, 0);
assert_eq!(t2_rem, 0);
carry_c1 += add::add_signed_same_len_in_place(&mut b[n3..3 * n3 + 2], Negative, t1);
carry_c3 += add::add_signed_same_len_in_place(&mut b[3 * n3..5 * n3 + 2], Positive, t1);
carry_c2 += add::add_signed_same_len_in_place(&mut b[2 * n3..4 * n3 + 2], Positive, t2);
carry_c3 += add::add_signed_same_len_in_place(&mut b[3 * n3..5 * n3 + 2], Negative, t2);
carry_c1 += add::add_signed_word_in_place(&mut b[2 * n3..3 * n3 + 2], carry_c0);
carry_c2 += add::add_signed_word_in_place(&mut b[3 * n3 + 2..4 * n3 + 2], carry_c1);
carry_c3 += add::add_signed_word_in_place(&mut b[4 * n3 + 2..5 * n3 + 2], carry_c2);
carry += add::add_signed_word_in_place(&mut b[5 * n3 + 2..], carry_c3);
debug_assert!(carry.abs() <= 1);
}