Skip to main content

binius_field/
packed_ghash.rs

1// Copyright 2024-2025 Irreducible Inc.
2// Copyright 2026 The Binius Developers
3
4use crate::{
5	BinaryField128bGhash,
6	arch::{
7		GhashInvert1x, GhashInvert2x, GhashInvert4x, GhashSquare1x, GhashSquare2x, GhashSquare4x,
8		GhashWideMul1x, GhashWideMul2x, GhashWideMul4x, M128, M256, M512, MulFromWideMul,
9		portable::packed_macros::{portable_macros::*, *},
10	},
11};
12
13define_packed_binary_field!(
14	PackedBinaryGhash1x128b,
15	BinaryField128bGhash,
16	M128,
17	(MulFromWideMul),
18	(GhashSquare1x),
19	(GhashInvert1x),
20	(GhashWideMul1x)
21);
22
23define_packed_binary_field!(
24	PackedBinaryGhash2x128b,
25	BinaryField128bGhash,
26	M256,
27	(MulFromWideMul),
28	(GhashSquare2x),
29	(GhashInvert2x),
30	(GhashWideMul2x)
31);
32
33define_packed_binary_field!(
34	PackedBinaryGhash4x128b,
35	BinaryField128bGhash,
36	M512,
37	(MulFromWideMul),
38	(GhashSquare4x),
39	(GhashInvert4x),
40	(GhashWideMul4x)
41);
42
43#[cfg(test)]
44mod test_utils {
45	/// Test if `mult_func` operation is a valid multiply operation on the given values for
46	/// all possible packed fields defined on 8-512 bits.
47	macro_rules! define_multiply_tests {
48		($mult_func:path, $constraint:ty) => {
49			$crate::packed_binary_field::test_utils::define_check_packed_mul!(
50				$mult_func,
51				$constraint
52			);
53
54			proptest! {
55				#[test]
56				fn test_mul_packed_128(a_val in any::<u128>(), b_val in any::<u128>()) {
57					TestMult::<$crate::PackedBinaryGhash1x128b>::test_mul(
58						a_val.into(),
59						b_val.into(),
60					);
61				}
62
63				#[test]
64				fn test_mul_packed_256(a_val in any::<[u128; 2]>(), b_val in any::<[u128; 2]>()) {
65					TestMult::<$crate::PackedBinaryGhash2x128b>::test_mul(
66						a_val.into(),
67						b_val.into(),
68					);
69				}
70
71				#[test]
72				fn test_mul_packed_512(a_val in any::<[u128; 4]>(), b_val in any::<[u128; 4]>()) {
73					TestMult::<$crate::PackedBinaryGhash4x128b>::test_mul(
74						a_val.into(),
75						b_val.into(),
76					);
77				}
78			}
79		};
80	}
81
82	/// Test if `square_func` operation is a valid square operation on the given value for
83	/// all possible packed fields.
84	macro_rules! define_square_tests {
85		($square_func:path, $constraint:ident) => {
86			$crate::packed_binary_field::test_utils::define_check_packed_square!(
87				$square_func,
88				$constraint
89			);
90
91			proptest! {
92				#[test]
93				fn test_square_packed_128(a_val in any::<u128>()) {
94					TestSquare::<$crate::PackedBinaryGhash1x128b>::test_square(a_val.into());
95				}
96
97				#[test]
98				fn test_square_packed_256(a_val in any::<[u128; 2]>()) {
99					TestSquare::<$crate::PackedBinaryGhash2x128b>::test_square(a_val.into());
100				}
101
102				#[test]
103				fn test_square_packed_512(a_val in any::<[u128; 4]>()) {
104					TestSquare::<$crate::PackedBinaryGhash4x128b>::test_square(a_val.into());
105				}
106			}
107		};
108	}
109
110	/// Test if `invert_func` operation is a valid invert operation on the given value for
111	/// all possible packed fields.
112	macro_rules! define_invert_tests {
113		($invert_func:path, $constraint:ident) => {
114			$crate::packed_binary_field::test_utils::define_check_packed_inverse!(
115				$invert_func,
116				$constraint
117			);
118
119			proptest! {
120				#[test]
121				fn test_invert_packed_128(a_val in any::<u128>()) {
122					TestInvert::<$crate::PackedBinaryGhash1x128b>::test_invert(a_val.into());
123				}
124
125				#[test]
126				fn test_invert_packed_256(a_val in any::<[u128; 2]>()) {
127					TestInvert::<$crate::PackedBinaryGhash2x128b>::test_invert(a_val.into());
128				}
129
130				#[test]
131				fn test_invert_packed_512(a_val in any::<[u128; 4]>()) {
132					TestInvert::<$crate::PackedBinaryGhash4x128b>::test_invert(a_val.into());
133				}
134			}
135		};
136	}
137
138	macro_rules! define_wide_mul_tests {
139		() => {
140			fn check_widening_correctness<P>(a: P::Underlier, b: P::Underlier)
141			where
142				P: $crate::PackedField<Scalar = $crate::BinaryField128bGhash>
143					+ $crate::WideMul
144					+ $crate::underlier::WithUnderlier,
145			{
146				let a = P::from_underlier(a);
147				let b = P::from_underlier(b);
148				let wide = P::wide_mul(a, b);
149				let reduced = P::reduce(wide);
150				assert_eq!(reduced, a * b);
151			}
152
153			fn check_widening_linearity<P>(
154				a1: P::Underlier,
155				b1: P::Underlier,
156				a2: P::Underlier,
157				b2: P::Underlier,
158			) where
159				P: $crate::PackedField<Scalar = $crate::BinaryField128bGhash>
160					+ $crate::WideMul
161					+ $crate::underlier::WithUnderlier,
162			{
163				let (a1, b1) = (P::from_underlier(a1), P::from_underlier(b1));
164				let (a2, b2) = (P::from_underlier(a2), P::from_underlier(b2));
165				let sum_reduced = P::reduce(P::wide_mul(a1, b1) + P::wide_mul(a2, b2));
166				assert_eq!(sum_reduced, a1 * b1 + a2 * b2);
167			}
168
169			proptest! {
170				#[test]
171				fn test_wide_mul_correctness_128(a in any::<u128>(), b in any::<u128>()) {
172					check_widening_correctness::<$crate::PackedBinaryGhash1x128b>(a.into(), b.into());
173				}
174
175				#[test]
176				fn test_wide_mul_correctness_256(a in any::<[u128; 2]>(), b in any::<[u128; 2]>()) {
177					check_widening_correctness::<$crate::PackedBinaryGhash2x128b>(a.into(), b.into());
178				}
179
180				#[test]
181				fn test_wide_mul_correctness_512(a in any::<[u128; 4]>(), b in any::<[u128; 4]>()) {
182					check_widening_correctness::<$crate::PackedBinaryGhash4x128b>(a.into(), b.into());
183				}
184
185				#[test]
186				fn test_wide_mul_linearity_128(
187					a1 in any::<u128>(), b1 in any::<u128>(),
188					a2 in any::<u128>(), b2 in any::<u128>(),
189				) {
190					check_widening_linearity::<$crate::PackedBinaryGhash1x128b>(
191						a1.into(), b1.into(), a2.into(), b2.into(),
192					);
193				}
194
195				#[test]
196				fn test_wide_mul_linearity_256(
197					a1 in any::<[u128; 2]>(), b1 in any::<[u128; 2]>(),
198					a2 in any::<[u128; 2]>(), b2 in any::<[u128; 2]>(),
199				) {
200					check_widening_linearity::<$crate::PackedBinaryGhash2x128b>(
201						a1.into(), b1.into(), a2.into(), b2.into(),
202					);
203				}
204
205				#[test]
206				fn test_wide_mul_linearity_512(
207					a1 in any::<[u128; 4]>(), b1 in any::<[u128; 4]>(),
208					a2 in any::<[u128; 4]>(), b2 in any::<[u128; 4]>(),
209				) {
210					check_widening_linearity::<$crate::PackedBinaryGhash4x128b>(
211						a1.into(), b1.into(), a2.into(), b2.into(),
212					);
213				}
214			}
215		};
216	}
217
218	pub(crate) use define_invert_tests;
219	pub(crate) use define_multiply_tests;
220	pub(crate) use define_square_tests;
221	pub(crate) use define_wide_mul_tests;
222}
223
224#[cfg(test)]
225mod tests {
226	use std::ops::Mul;
227
228	use proptest::{arbitrary::any, proptest};
229
230	use super::{
231		PackedBinaryGhash2x128b, PackedBinaryGhash4x128b,
232		test_utils::{
233			define_invert_tests, define_multiply_tests, define_square_tests, define_wide_mul_tests,
234		},
235	};
236	use crate::{
237		BinaryField128bGhash, PackedField,
238		arithmetic_traits::{InvertOrZero, Square},
239		underlier::WithUnderlier,
240	};
241
242	fn check_get_set<const WIDTH: usize, PT>(a: [u128; WIDTH], b: [u128; WIDTH])
243	where
244		PT: PackedField<Scalar = BinaryField128bGhash>
245			+ WithUnderlier<Underlier: From<[u128; WIDTH]>>,
246	{
247		let mut val = PT::from_underlier(a.into());
248		for i in 0..WIDTH {
249			assert_eq!(val.get(i), BinaryField128bGhash::from(a[i]));
250			val.set(i, BinaryField128bGhash::from(b[i]));
251			assert_eq!(val.get(i), BinaryField128bGhash::from(b[i]));
252		}
253	}
254
255	proptest! {
256		#[test]
257		fn test_get_set_256(a in any::<[u128; 2]>(), b in any::<[u128; 2]>()) {
258			check_get_set::<2, PackedBinaryGhash2x128b>(a, b);
259		}
260
261		#[test]
262		fn test_get_set_512(a in any::<[u128; 4]>(), b in any::<[u128; 4]>()) {
263			check_get_set::<4, PackedBinaryGhash4x128b>(a, b);
264		}
265	}
266
267	define_multiply_tests!(Mul::mul, PackedField);
268
269	define_square_tests!(Square::square, PackedField);
270
271	define_invert_tests!(InvertOrZero::invert_or_zero, PackedField);
272
273	define_wide_mul_tests!();
274
275	#[test]
276	fn test_wide_mul_zero_inputs() {
277		use super::PackedBinaryGhash1x128b as P;
278		use crate::{WideMul, field::FieldOps};
279
280		let zero = P::default();
281		let one = P::one();
282
283		assert_eq!(P::reduce(P::wide_mul(zero, zero)), zero);
284		assert_eq!(P::reduce(P::wide_mul(zero, one)), zero);
285		assert_eq!(P::reduce(P::wide_mul(one, zero)), zero);
286		assert_eq!(P::reduce(P::wide_mul(one, one)), one);
287
288		let wide_zero = <P as WideMul>::Output::default();
289		assert_eq!(P::reduce(wide_zero), zero);
290	}
291
292	#[test]
293	fn test_wide_mul_single_accumulation() {
294		use rand::{SeedableRng, rngs::StdRng};
295
296		use super::PackedBinaryGhash1x128b as P;
297		use crate::{Random, WideMul};
298
299		let mut rng = StdRng::seed_from_u64(77);
300		let a = P::random(&mut rng);
301		let b = P::random(&mut rng);
302
303		let wide = P::wide_mul(a, b);
304		let sum = wide + <P as WideMul>::Output::default();
305		assert_eq!(P::reduce(sum), a * b);
306	}
307}