use core::array;
use p3_binary_field::{Gf2, Ghash128};
use p3_field::{Field, PackedField, PackedValue, PrimeCharacteristicRing};
use proptest::prelude::*;
use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};
const fn nist_block(block: u128) -> Ghash128 {
Ghash128::from_le_bytes(block.reverse_bits().to_le_bytes())
}
#[test]
fn nist_gcm_aes128_example_2() {
let h = nist_block(0xb83b533708bf535d0aa6e52980d53b78);
let blocks = [
0x42831ec2217774244b7221b784d0d49c,
0xe3aa212f2c02a4e035c17e2329aca12e,
0x21d514b25466931c7d8f6a5aac84aa05,
0x1ba30b396a0aac973d58e091473f5985,
512,
];
let result = blocks
.into_iter()
.fold(Ghash128::ZERO, |acc, block| (acc + nist_block(block)) * h);
assert_eq!(result, nist_block(0x7f1b32b81b820d02614f8895ac1d4eac));
}
fn check_dot<P: PackedField<Scalar = Ghash128> + Eq, const N: usize>(seed: u64) {
let mut rng = SmallRng::seed_from_u64(seed);
let a: [P; N] = array::from_fn(|_| P::from_fn(|_| rng.random()));
let b: [P; N] = array::from_fn(|_| P::from_fn(|_| rng.random()));
let actual = P::dot_product(&a, &b);
for lane in 0..P::WIDTH {
let expected: Ghash128 = a
.iter()
.zip(&b)
.map(|(x, y)| x.as_slice()[lane] * y.as_slice()[lane])
.sum();
assert_eq!(actual.as_slice()[lane], expected);
}
}
fn check_dot_lengths<P: PackedField<Scalar = Ghash128> + Eq>(seed: u64) {
check_dot::<P, 0>(seed);
check_dot::<P, 1>(seed);
check_dot::<P, 3>(seed);
check_dot::<P, 16>(seed);
check_dot::<P, 65>(seed);
}
proptest! {
#[test]
fn dot_products_match_scalar_reduction(seed: u64) {
check_dot_lengths::<Ghash128>(seed);
check_dot_lengths::<<Ghash128 as Field>::Packing>(seed);
}
}
#[test]
fn dot_product_cancellation_and_extremes() {
type P = <Ghash128 as Field>::Packing;
let extremes = [0, 1, u128::MAX, 1 << 127, 1 << 64, u64::MAX as u128, 0x87];
for offset in 0..extremes.len() {
let a = P::from_fn(|i| {
Ghash128::from_le_bytes(extremes[(offset + i) % extremes.len()].to_le_bytes())
});
let b = P::from_fn(|i| {
Ghash128::from_le_bytes(extremes[(offset + i + 1) % extremes.len()].to_le_bytes())
});
assert_eq!(P::dot_product(&[a, a], &[b, b]), P::ZERO);
assert_eq!(P::dot_product(&[a, a, a], &[b, b, b]), a * b);
}
}
#[test]
fn packed_property_suite_supports_zero_in_small_fields() {
p3_field_testing::test_packed_vs_scalar_proptest::<Gf2>();
}