#[cfg(target_pointer_width = "16")]
type UDoubleSize = u32;
#[cfg(target_pointer_width = "32")]
type UDoubleSize = u64;
#[cfg(target_pointer_width = "64")]
type UDoubleSize = u128;
c0nst::c0nst! {
pub c0nst trait CarrylessMul: Sized {
type Output;
fn carryless_mul(self, rhs: Self) -> Self::Output;
}
}
macro_rules! carryless_mul_impl {
($($t:ty)*) => {$(
c0nst::c0nst! {
c0nst impl CarrylessMul for $t {
type Output = $t;
#[inline]
fn carryless_mul(self, rhs: Self) -> $t {
let mut result: $t = 0;
let mut i = 0u32;
while i < <$t>::BITS {
let mask = ((rhs >> i) & 1).wrapping_neg();
result ^= (self << i) & mask;
i += 1;
}
result
}
}
}
)*};
}
carryless_mul_impl!(usize u8 u16 u32 u64 u128);
c0nst::c0nst! {
pub c0nst trait WideningCarrylessMul: Sized {
type Wide;
fn widening_carryless_mul(self, rhs: Self) -> Self::Wide;
}
}
macro_rules! widening_carryless_mul_impl {
($($t:ty => $w:ty;)*) => {$(
c0nst::c0nst! {
c0nst impl WideningCarrylessMul for $t {
type Wide = $w;
#[inline]
fn widening_carryless_mul(self, rhs: Self) -> $w {
let wide = self as $w;
let mut result: $w = 0;
let mut i = 0u32;
while i < <$t>::BITS {
let mask = (((rhs >> i) & 1) as $w).wrapping_neg();
result ^= (wide << i) & mask;
i += 1;
}
result
}
}
}
)*};
}
widening_carryless_mul_impl! {
u8 => u16;
u16 => u32;
u32 => u64;
u64 => u128;
}
c0nst::c0nst! {
pub c0nst trait CarryingCarrylessMul: Sized {
type Output;
fn carrying_carryless_mul(self, rhs: Self, carry: Self) -> (Self::Output, Self::Output);
}
}
macro_rules! carrying_carryless_mul_impl {
($($t:ty => $w:ty;)*) => {$(
c0nst::c0nst! {
c0nst impl CarryingCarrylessMul for $t {
type Output = $t;
#[inline]
fn carrying_carryless_mul(self, rhs: Self, carry: Self) -> ($t, $t) {
let a = self as $w;
let mut p: $w = 0;
let mut i = 0u32;
while i < <$t>::BITS {
let mask = (((rhs >> i) & 1) as $w).wrapping_neg();
p ^= (a << i) & mask;
i += 1;
}
((p as $t) ^ carry, (p >> <$t>::BITS) as $t)
}
}
}
)*};
}
carrying_carryless_mul_impl! {
u8 => u16;
u16 => u32;
u32 => u64;
u64 => u128;
usize => UDoubleSize;
}
const fn wide_clmul_u64(a: u64, b: u64) -> u128 {
let a = a as u128;
let mut p: u128 = 0;
let mut i = 0u32;
while i < 64 {
let mask = (((b >> i) & 1) as u128).wrapping_neg();
p ^= (a << i) & mask;
i += 1;
}
p
}
c0nst::c0nst! {
c0nst impl CarryingCarrylessMul for u128 {
type Output = u128;
#[inline]
fn carrying_carryless_mul(self, rhs: Self, carry: Self) -> (u128, u128) {
let x0 = self as u64;
let x1 = (self >> 64) as u64;
let y0 = rhs as u64;
let y1 = (rhs >> 64) as u64;
let z0 = wide_clmul_u64(x0, y0);
let z2 = wide_clmul_u64(x1, y1);
let z3 = wide_clmul_u64(x0 ^ x1, y0 ^ y1);
let z1 = z3 ^ z0 ^ z2;
let lo = z0 ^ (z1 << 64);
let hi = z2 ^ (z1 >> 64);
(lo ^ carry, hi)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn carrying_carryless() {
for a in [0u8, 1, 3, 0x80, 0xAB, 0xFF] {
for b in [0u8, 1, 3, 0x80, 0xCD, 0xFF] {
let wide = WideningCarrylessMul::widening_carryless_mul(a, b);
let (lo, hi) = CarryingCarrylessMul::carrying_carryless_mul(a, b, 0x5A);
assert_eq!(lo, (wide as u8) ^ 0x5A);
assert_eq!(hi, (wide >> 8) as u8);
}
}
fn reference(a: u128, b: u128) -> (u128, u128) {
let (mut lo, mut hi) = (0u128, 0u128);
for i in 0..128u32 {
if (b >> i) & 1 == 1 {
lo ^= a.wrapping_shl(i);
if i > 0 {
hi ^= a >> (128 - i);
}
}
}
(lo, hi)
}
let vals = [
0u128,
1,
u128::MAX,
0x0123_4567_89ab_cdef_fedc_ba98_7654_3210,
1 << 127,
0xdead_beef,
];
for &a in &vals {
for &b in &vals {
let (lo, hi) = CarryingCarrylessMul::carrying_carryless_mul(a, b, 0);
assert_eq!((lo, hi), reference(a, b), "{a:#x} {b:#x}");
}
}
let (lo0, hi0) = CarryingCarrylessMul::carrying_carryless_mul(u128::MAX, u128::MAX, 0);
let (lo1, hi1) = CarryingCarrylessMul::carrying_carryless_mul(u128::MAX, u128::MAX, 0xFF);
assert_eq!((lo0 ^ 0xFF, hi0), (lo1, hi1));
}
#[test]
fn carryless() {
assert_eq!(CarrylessMul::carryless_mul(0b11u8, 0b11), 0b101);
assert_eq!(CarrylessMul::carryless_mul(0x80u8, 0x80), 0); assert_eq!(
WideningCarrylessMul::widening_carryless_mul(0x80u8, 0x80),
0x4000u16
);
assert_eq!(CarrylessMul::carryless_mul(0b1011u32, 0b100), 0b101100);
}
}