use crate::{
add,
arch::word::{SignedWord, Word},
div,
helper_macros::debug_assert_zero,
math,
memory::{self, Memory},
mul::{self, helpers},
shift,
Sign::{self, *},
};
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)
}
#[must_use]
pub fn add_signed_mul(
c: &mut [Word],
sign: Sign,
a: &[Word],
b: &[Word],
memory: &mut Memory,
) -> SignedWord {
assert!(a.len() >= b.len() && b.len() >= MIN_LEN && c.len() == a.len() + b.len());
helpers::add_signed_mul_split_into_chunks(
c,
sign,
a,
b,
b.len(),
memory,
add_signed_mul_same_len,
)
}
#[must_use]
pub fn add_signed_mul_same_len(
c: &mut [Word],
sign: Sign,
a: &[Word],
b: &[Word],
memory: &mut Memory,
) -> SignedWord {
let n = a.len();
debug_assert!(b.len() == n && c.len() == 2 * n);
debug_assert!(n >= MIN_LEN);
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 (b0, b12) = b.split_at(n3);
let (b1, b2) = b12.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];
debug_assert_zero!(mul::add_signed_mul_same_len(t1_short, Positive, a0, b0, &mut memory));
carry_c0 += add::add_signed_same_len_in_place(&mut c[..2 * n3], sign, t1_short);
carry_c2 += add::add_signed_in_place(&mut c[2 * n3..4 * n3 + 2], -sign, 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);
let (b_eval, mut memory) = memory.allocate_slice_copy_fill(n3 + 1, b0, 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);
b_eval[n3] = mul::add_mul_word_same_len_in_place(&mut b_eval[..n3], 2, b1);
b_eval[n3] += mul::add_mul_word_in_place(&mut b_eval[..n3], 4, b2);
debug_assert_zero!(mul::add_signed_mul_same_len(t1, Positive, a_eval, b_eval, &mut memory));
}
{
let (c_eval, mut memory) = memory.allocate_slice_fill(2 * n3 + 2, 0);
let c_short = &mut c_eval[..2 * n3_short];
debug_assert_zero!(mul::add_signed_mul_same_len(c_short, Positive, a2, b2, &mut memory));
carry_c2 += add::add_signed_in_place(&mut c[2 * n3..4 * n3 + 2], -sign, c_short);
carry += add::add_signed_same_len_in_place(&mut c[4 * n3..], sign, 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 mut value_neg1_sign;
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));
let (b02, mut memory) = memory.allocate_slice_copy_fill(n3 + 1, b0, 0);
b02[n3] = Word::from(add::add_in_place(&mut b02[..n3], b2));
b_eval.copy_from_slice(b02);
b_eval[n3] += Word::from(add::add_same_len_in_place(&mut b_eval[..n3], b1));
debug_assert_zero!(mul::add_signed_mul_same_len(t2, Positive, a_eval, b_eval, &mut memory));
carry_c1 += add::add_signed_in_place(&mut c[n3..3 * n3 + 2], sign, t2);
a_eval.copy_from_slice(a02);
value_neg1_sign = add::sub_in_place_with_sign(a_eval, a1);
b_eval.copy_from_slice(b02);
value_neg1_sign *= add::sub_in_place_with_sign(b_eval, b1);
}
let (c_eval, mut memory) = memory.allocate_slice_fill(2 * (n3 + 1), 0);
debug_assert_zero!(mul::add_signed_mul_same_len(c_eval, Positive, a_eval, b_eval, &mut memory));
debug_assert_zero!(add::add_signed_same_len_in_place(t2, value_neg1_sign, c_eval));
match value_neg1_sign {
Positive => debug_assert_zero!(mul::add_mul_word_same_len_in_place(t1, 2, c_eval)),
Negative => debug_assert_zero!(mul::sub_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 c[n3..3 * n3 + 2], -sign, t1);
carry_c3 += add::add_signed_same_len_in_place(&mut c[3 * n3..5 * n3 + 2], sign, t1);
carry_c2 += add::add_signed_same_len_in_place(&mut c[2 * n3..4 * n3 + 2], sign, t2);
carry_c3 += add::add_signed_same_len_in_place(&mut c[3 * n3..5 * n3 + 2], -sign, t2);
carry_c1 += add::add_signed_word_in_place(&mut c[2 * n3..3 * n3 + 2], carry_c0);
carry_c2 += add::add_signed_word_in_place(&mut c[3 * n3 + 2..4 * n3 + 2], carry_c1);
carry_c3 += add::add_signed_word_in_place(&mut c[4 * n3 + 2..5 * n3 + 2], carry_c2);
carry += add::add_signed_word_in_place(&mut c[5 * n3 + 2..], carry_c3);
debug_assert!(carry.abs() <= 1);
carry
}