use super::{MODULUS, MU};
use crate::{
arithmetic::util::{adc, mac, sbb},
U256,
};
#[inline]
#[allow(clippy::too_many_arguments)]
pub(super) const fn barrett_reduce(lo: U256, hi: U256) -> U256 {
let lo = lo.as_words();
let hi = hi.as_words();
let a0 = lo[0];
let a1 = lo[1];
let a2 = lo[2];
let a3 = lo[3];
let a4 = hi[0];
let a5 = hi[1];
let a6 = hi[2];
let a7 = hi[3];
let q1: [u64; 5] = [a3, a4, a5, a6, a7];
let q3 = q1_times_mu_shift_five(&q1);
let r1: [u64; 5] = [a0, a1, a2, a3, a4];
let r2: [u64; 5] = q3_times_n_keep_five(&q3);
let r: [u64; 5] = sub_inner_five(r1, r2);
let r = subtract_n_if_necessary(r[0], r[1], r[2], r[3], r[4]);
let r = subtract_n_if_necessary(r[0], r[1], r[2], r[3], r[4]);
U256::from_words([r[0], r[1], r[2], r[3]])
}
const fn q1_times_mu_shift_five(q1: &[u64; 5]) -> [u64; 5] {
let (_w0, carry) = mac(0, q1[0], MU[0], 0);
let (w1, carry) = mac(0, q1[0], MU[1], carry);
let (w2, carry) = mac(0, q1[0], MU[2], carry);
let (w3, carry) = mac(0, q1[0], MU[3], carry);
let (w4, w5) = mac(0, q1[0], MU[4], carry);
let (_w1, carry) = mac(w1, q1[1], MU[0], 0);
let (w2, carry) = mac(w2, q1[1], MU[1], carry);
let (w3, carry) = mac(w3, q1[1], MU[2], carry);
let (w4, carry) = mac(w4, q1[1], MU[3], carry);
let (w5, w6) = mac(w5, q1[1], MU[4], carry);
let (_w2, carry) = mac(w2, q1[2], MU[0], 0);
let (w3, carry) = mac(w3, q1[2], MU[1], carry);
let (w4, carry) = mac(w4, q1[2], MU[2], carry);
let (w5, carry) = mac(w5, q1[2], MU[3], carry);
let (w6, w7) = mac(w6, q1[2], MU[4], carry);
let (_w3, carry) = mac(w3, q1[3], MU[0], 0);
let (w4, carry) = mac(w4, q1[3], MU[1], carry);
let (w5, carry) = mac(w5, q1[3], MU[2], carry);
let (w6, carry) = mac(w6, q1[3], MU[3], carry);
let (w7, w8) = mac(w7, q1[3], MU[4], carry);
let (_w4, carry) = mac(w4, q1[4], MU[0], 0);
let (w5, carry) = mac(w5, q1[4], MU[1], carry);
let (w6, carry) = mac(w6, q1[4], MU[2], carry);
let (w7, carry) = mac(w7, q1[4], MU[3], carry);
let (w8, w9) = mac(w8, q1[4], MU[4], carry);
[w5, w6, w7, w8, w9]
}
const fn q3_times_n_keep_five(q3: &[u64; 5]) -> [u64; 5] {
let modulus = MODULUS.as_words();
let (w0, carry) = mac(0, q3[0], modulus[0], 0);
let (w1, carry) = mac(0, q3[0], modulus[1], carry);
let (w2, carry) = mac(0, q3[0], modulus[2], carry);
let (w3, carry) = mac(0, q3[0], modulus[3], carry);
let (w4, _) = mac(0, q3[0], 0, carry);
let (w1, carry) = mac(w1, q3[1], modulus[0], 0);
let (w2, carry) = mac(w2, q3[1], modulus[1], carry);
let (w3, carry) = mac(w3, q3[1], modulus[2], carry);
let (w4, _) = mac(w4, q3[1], modulus[3], carry);
let (w2, carry) = mac(w2, q3[2], modulus[0], 0);
let (w3, carry) = mac(w3, q3[2], modulus[1], carry);
let (w4, _) = mac(w4, q3[2], modulus[2], carry);
let (w3, carry) = mac(w3, q3[3], modulus[0], 0);
let (w4, _) = mac(w4, q3[3], modulus[1], carry);
let (w4, _) = mac(w4, q3[4], modulus[0], 0);
[w0, w1, w2, w3, w4]
}
#[inline]
#[allow(clippy::too_many_arguments)]
const fn sub_inner_five(l: [u64; 5], r: [u64; 5]) -> [u64; 5] {
let (w0, borrow) = sbb(l[0], r[0], 0);
let (w1, borrow) = sbb(l[1], r[1], borrow);
let (w2, borrow) = sbb(l[2], r[2], borrow);
let (w3, borrow) = sbb(l[3], r[3], borrow);
let (w4, _borrow) = sbb(l[4], r[4], borrow);
[w0, w1, w2, w3, w4]
}
#[inline]
#[allow(clippy::too_many_arguments)]
const fn subtract_n_if_necessary(r0: u64, r1: u64, r2: u64, r3: u64, r4: u64) -> [u64; 5] {
let modulus = MODULUS.as_words();
let (w0, borrow) = sbb(r0, modulus[0], 0);
let (w1, borrow) = sbb(r1, modulus[1], borrow);
let (w2, borrow) = sbb(r2, modulus[2], borrow);
let (w3, borrow) = sbb(r3, modulus[3], borrow);
let (w4, borrow) = sbb(r4, 0, borrow);
let (w0, carry) = adc(w0, modulus[0] & borrow, 0);
let (w1, carry) = adc(w1, modulus[1] & borrow, carry);
let (w2, carry) = adc(w2, modulus[2] & borrow, carry);
let (w3, carry) = adc(w3, modulus[3] & borrow, carry);
let (w4, _carry) = adc(w4, 0, carry);
[w0, w1, w2, w3, w4]
}