pub(crate) const MAX_POWERS: usize = 128;
#[inline]
pub(crate) fn poly_dot_powers_128(values: impl ExactSizeIterator<Item = u128>) -> u128 {
assert!(
values.len() <= MAX_POWERS,
"a combination covers at most {MAX_POWERS} values"
);
let (mut low, mut high) = (0u128, 0u128);
for (k, value) in values.enumerate() {
low ^= value << k;
high ^= (value >> 1) >> (127 - k);
}
super::reduce_128(low, high)
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use proptest::prelude::*;
use super::*;
use crate::clmul::poly_mul_128;
const X: u128 = 2;
fn reference(values: &[u128]) -> u128 {
let mut power = 1u128;
let mut sum = 0u128;
for &value in values {
sum ^= poly_mul_128(value, power);
power = poly_mul_128(power, X);
}
sum
}
#[test]
fn an_empty_run_combines_to_zero() {
assert_eq!(poly_dot_powers_128([].into_iter()), 0);
}
#[test]
fn a_single_value_is_returned_unchanged() {
assert_eq!(poly_dot_powers_128([0x1234u128].into_iter()), 0x1234);
}
#[test]
fn the_shift_path_matches_the_multiplying_reference_at_the_boundaries() {
let extremes = [0u128, 1, 1 << 127, u128::MAX];
for value in extremes {
let run = alloc::vec![value; MAX_POWERS];
assert_eq!(
poly_dot_powers_128(run.iter().copied()),
reference(&run),
"value={value:#x}"
);
}
}
#[test]
#[should_panic(expected = "a combination covers at most")]
fn a_run_past_the_accumulator_is_refused() {
let run = alloc::vec![1u128; MAX_POWERS + 1];
poly_dot_powers_128(run.iter().copied());
}
proptest! {
#[test]
fn the_shift_path_matches_the_multiplying_reference(
values in prop::collection::vec(any::<u128>(), 0..=MAX_POWERS),
) {
prop_assert_eq!(
poly_dot_powers_128(values.iter().copied()),
reference(&values),
);
}
#[test]
fn the_combination_is_additive_in_the_values(
left in prop::collection::vec(any::<u128>(), 1..=32),
seed: u64,
) {
let right = left
.iter()
.enumerate()
.map(|(index, value)| value ^ seed.wrapping_mul(index as u64 + 1) as u128)
.collect::<Vec<_>>();
let sum = left
.iter()
.zip(&right)
.map(|(a, b)| a ^ b)
.collect::<Vec<_>>();
prop_assert_eq!(
poly_dot_powers_128(left.iter().copied())
^ poly_dot_powers_128(right.iter().copied()),
poly_dot_powers_128(sum.iter().copied()),
);
}
}
}