use num_traits::Zero;
use std::ops::{Add, AddAssign};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct WideLimbs<const N: usize>(pub [u64; N]);
impl<const N: usize> Default for WideLimbs<N> {
fn default() -> Self {
Self([0u64; N])
}
}
impl<const N: usize> AddAssign for WideLimbs<N> {
#[inline]
fn add_assign(&mut self, other: Self) {
let mut carry = 0u64;
for i in 0..N {
let (sum, c1) = self.0[i].overflowing_add(other.0[i]);
let (sum, c2) = sum.overflowing_add(carry);
self.0[i] = sum;
carry = (c1 as u64) + (c2 as u64);
}
}
}
impl<const N: usize> Add for WideLimbs<N> {
type Output = Self;
#[inline]
fn add(mut self, other: Self) -> Self {
self += other;
self
}
}
impl<const N: usize> Zero for WideLimbs<N> {
#[inline]
fn zero() -> Self {
Self([0u64; N])
}
#[inline]
fn is_zero(&self) -> bool {
self.0.iter().all(|&x| x == 0)
}
}
#[inline(always)]
pub(super) const fn gte<const N: usize>(a: &[u64; N], b: &[u64; N]) -> bool {
let mut i = N;
while i > 0 {
i -= 1;
if a[i] > b[i] {
return true;
}
if a[i] < b[i] {
return false;
}
}
true }
#[inline(always)]
pub(super) const fn add<const N: usize>(a: &[u64; N], b: &[u64; N]) -> ([u64; N], u64) {
let mut result = [0u64; N];
let mut carry = 0u128;
let mut i = 0;
while i < N {
let sum = (a[i] as u128) + (b[i] as u128) + carry;
result[i] = sum as u64;
carry = sum >> 64;
i += 1;
}
(result, carry as u64)
}
#[inline(always)]
pub(super) const fn sub<const N: usize>(a: &[u64; N], b: &[u64; N]) -> [u64; N] {
let mut result = [0u64; N];
let mut borrow = 0u64;
let mut i = 0;
while i < N {
let (diff, b1) = a[i].overflowing_sub(b[i]);
let (diff2, b2) = diff.overflowing_sub(borrow);
result[i] = diff2;
borrow = (b1 as u64) + (b2 as u64);
i += 1;
}
result
}
#[inline(always)]
pub(super) const fn shl<const N: usize>(a: &[u64; N]) -> [u64; N] {
let mut result = [0u64; N];
let mut carry = 0u64;
let mut i = 0;
while i < N {
let new_carry = a[i] >> 63;
result[i] = (a[i] << 1) | carry;
carry = new_carry;
i += 1;
}
result
}
#[inline(always)]
pub(super) const fn clz<const N: usize>(a: &[u64; N]) -> u32 {
let mut i = N;
let mut count = 0u32;
while i > 0 {
i -= 1;
if a[i] != 0 {
return count + a[i].leading_zeros();
}
count += 64;
}
count
}
#[inline(always)]
pub const fn mul_4_by_4(a: &[u64; 4], b: &[u64; 4]) -> [u64; 8] {
let mut result = [0u64; 8];
let mut i = 0;
while i < 4 {
let mut carry = 0u128;
let mut j = 0;
while j < 4 {
let prod = (a[i] as u128) * (b[j] as u128) + (result[i + j] as u128) + carry;
result[i + j] = prod as u64;
carry = prod >> 64;
j += 1;
}
result[i + 4] = carry as u64;
i += 1;
}
result
}
#[inline(always)]
pub(super) const fn gte_5_4(a: &[u64; 5], b: &[u64; 4]) -> bool {
if a[4] > 0 {
return true;
}
let mut i = 4;
while i > 0 {
i -= 1;
if a[i] > b[i] {
return true;
}
if a[i] < b[i] {
return false;
}
}
true }
#[inline(always)]
pub(super) const fn sub_5_4(a: &[u64; 5], b: &[u64; 4]) -> [u64; 5] {
let mut result = [0u64; 5];
let mut borrow = 0u64;
let mut i = 0;
while i < 4 {
let (diff, b1) = a[i].overflowing_sub(b[i]);
let (diff2, b2) = diff.overflowing_sub(borrow);
result[i] = diff2;
borrow = (b1 as u64) + (b2 as u64);
i += 1;
}
let (diff, _) = a[4].overflowing_sub(borrow);
result[4] = diff;
result
}
#[inline(always)]
pub(super) const fn reduce_8_mod_4(x: &[u64; 8], p: &[u64; 4]) -> [u64; 4] {
let p8: [u64; 8] = [p[0], p[1], p[2], p[3], 0, 0, 0, 0];
if !gte::<8>(x, &p8) {
return [x[0], x[1], x[2], x[3]];
}
let x_clz = clz::<8>(x);
let p_clz = clz::<8>(&p8);
if p_clz <= x_clz {
let result = sub::<8>(x, &p8);
return [result[0], result[1], result[2], result[3]];
}
let shift_bits = p_clz - x_clz;
let mut remainder = *x;
let mut shifted_p = p8;
let mut bits_to_shift = shift_bits;
while bits_to_shift > 0 {
shifted_p = shl::<8>(&shifted_p);
bits_to_shift -= 1;
}
let mut iterations = shift_bits + 1;
while iterations > 0 {
if gte::<8>(&remainder, &shifted_p) {
remainder = sub::<8>(&remainder, &shifted_p);
}
if iterations > 1 {
let mut i = 0;
while i < 7 {
shifted_p[i] = (shifted_p[i] >> 1) | (shifted_p[i + 1] << 63);
i += 1;
}
shifted_p[7] >>= 1;
}
iterations -= 1;
}
[remainder[0], remainder[1], remainder[2], remainder[3]]
}