pub const P: u64 = 0xffff_ffff_0000_0001;
#[inline(always)]
pub const fn montyred(x: u128) -> u64 {
let xl = x as u64;
let xh = (x >> 64) as u64;
let (a, e) = xl.overflowing_add(xl << 32);
let b = a.wrapping_sub(a >> 32).wrapping_sub(e as u64);
let (r, c) = xh.overflowing_sub(b);
r.wrapping_sub((1 + !P) * c as u64)
}
#[inline(always)]
pub const fn bfe_add_raw(a: u64, b: u64) -> u64 {
let (x1, c1) = a.overflowing_sub(P - b);
if c1 {
x1.wrapping_add(P)
} else {
x1
}
}
#[inline(always)]
#[cfg(test)]
pub const fn bfe_mul_raw(a: u64, b: u64) -> u64 {
montyred((a as u128) * (b as u128))
}
#[inline(always)]
pub fn split_and_lookup(x: u64, lut: &[u8; 256]) -> u64 {
let mut bytes = x.to_le_bytes();
bytes[0] = lut[bytes[0] as usize];
bytes[1] = lut[bytes[1] as usize];
bytes[2] = lut[bytes[2] as usize];
bytes[3] = lut[bytes[3] as usize];
bytes[4] = lut[bytes[4] as usize];
bytes[5] = lut[bytes[5] as usize];
bytes[6] = lut[bytes[6] as usize];
bytes[7] = lut[bytes[7] as usize];
u64::from_le_bytes(bytes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn montyred_zero() {
assert_eq!(montyred(0), 0);
}
#[test]
fn bfe_add_below_p_is_canonical() {
let mont_3 = montyred((3u128) * (0xffff_fffe_0000_0001u128));
let mont_5 = montyred((5u128) * (0xffff_fffe_0000_0001u128));
let mont_8 = montyred((8u128) * (0xffff_fffe_0000_0001u128));
assert_eq!(bfe_add_raw(mont_3, mont_5), mont_8);
}
#[test]
fn bfe_mul_one_is_identity() {
const R2: u128 = 0xffff_fffe_0000_0001;
let mont_1 = montyred(R2);
let mont_42 = montyred(42u128 * R2);
assert_eq!(bfe_mul_raw(mont_42, mont_1), mont_42);
}
}