Skip to main content

binius_field/
packed_binary_field.rs

1// Copyright 2023-2025 Irreducible Inc.
2// Copyright 2026 The Binius Developers
3
4use std::ops::Mul;
5
6use crate::{
7	BinaryField1b,
8	arch::{M128, M256, M512, PackedPrimitiveType},
9	arithmetic_traits::{InvertOrZero, Square, WideMul},
10	underlier::{U1, U2, U4, UnderlierType},
11};
12
13// Type aliases for the `BinaryField1b` packings. The underlier determines the width; `M128`/`M256`/
14// `M512` resolve to the architecture-appropriate type (SIMD where available, scaled otherwise).
15pub type PackedBinaryField1x1b = PackedPrimitiveType<U1, BinaryField1b>;
16pub type PackedBinaryField2x1b = PackedPrimitiveType<U2, BinaryField1b>;
17pub type PackedBinaryField4x1b = PackedPrimitiveType<U4, BinaryField1b>;
18pub type PackedBinaryField8x1b = PackedPrimitiveType<u8, BinaryField1b>;
19pub type PackedBinaryField16x1b = PackedPrimitiveType<u16, BinaryField1b>;
20pub type PackedBinaryField32x1b = PackedPrimitiveType<u32, BinaryField1b>;
21pub type PackedBinaryField64x1b = PackedPrimitiveType<u64, BinaryField1b>;
22pub type PackedBinaryField128x1b = PackedPrimitiveType<M128, BinaryField1b>;
23pub type PackedBinaryField256x1b = PackedPrimitiveType<M256, BinaryField1b>;
24pub type PackedBinaryField512x1b = PackedPrimitiveType<M512, BinaryField1b>;
25
26// Every `BinaryField1b` packing shares the same arithmetic, which is available for any underlier:
27// addition is bitwise XOR (provided generically for all `PackedPrimitiveType` in `packed.rs`) and
28// multiplication is bitwise AND. Squaring and inversion are the identity, since `0` and `1` are
29// each their own square and inverse. A single blanket impl over `U` therefore replaces the
30// per-type definitions that the `define_packed_binary_field` macro used to generate.
31impl<U: UnderlierType> Mul for PackedPrimitiveType<U, BinaryField1b> {
32	type Output = Self;
33
34	#[inline]
35	#[allow(clippy::suspicious_arithmetic_impl)]
36	fn mul(self, rhs: Self) -> Self {
37		(self.0 & rhs.0).into()
38	}
39}
40
41impl<U: UnderlierType> Square for PackedPrimitiveType<U, BinaryField1b> {
42	#[inline]
43	fn square(self) -> Self {
44		self
45	}
46}
47
48impl<U: UnderlierType> InvertOrZero for PackedPrimitiveType<U, BinaryField1b> {
49	#[inline]
50	fn invert_or_zero(self) -> Self {
51		self
52	}
53}
54
55impl<U: UnderlierType> WideMul for PackedPrimitiveType<U, BinaryField1b> {
56	type Output = Self;
57
58	#[inline]
59	fn wide_mul(a: Self, b: Self) -> Self {
60		a * b
61	}
62
63	#[inline]
64	fn reduce(wide: Self) -> Self {
65		wide
66	}
67}
68
69/// Common code to test different multiply, square and invert implementations
70#[cfg(test)]
71pub mod test_utils {
72	use crate::{
73		PackedField,
74		underlier::{U1, U2, U4, WithUnderlier},
75	};
76
77	pub struct Unit;
78
79	impl From<U1> for Unit {
80		fn from(_: U1) -> Self {
81			Self
82		}
83	}
84
85	impl From<U2> for Unit {
86		fn from(_: U2) -> Self {
87			Self
88		}
89	}
90
91	impl From<U4> for Unit {
92		fn from(_: U4) -> Self {
93			Self
94		}
95	}
96
97	impl From<u8> for Unit {
98		fn from(_: u8) -> Self {
99			Self
100		}
101	}
102
103	impl From<u16> for Unit {
104		fn from(_: u16) -> Self {
105			Self
106		}
107	}
108
109	impl From<u32> for Unit {
110		fn from(_: u32) -> Self {
111			Self
112		}
113	}
114
115	impl From<u64> for Unit {
116		fn from(_: u64) -> Self {
117			Self
118		}
119	}
120
121	impl From<u128> for Unit {
122		fn from(_: u128) -> Self {
123			Self
124		}
125	}
126
127	impl From<[u128; 2]> for Unit {
128		fn from(_: [u128; 2]) -> Self {
129			Self
130		}
131	}
132
133	impl From<[u128; 4]> for Unit {
134		fn from(_: [u128; 4]) -> Self {
135			Self
136		}
137	}
138
139	/// We use such helper macros to run tests only for the
140	/// types that implement the `constraint` trait.
141	/// The idea is inspired by `impls` trait.
142	macro_rules! define_check_packed_mul {
143		($mult_func:path, $constraint:path) => {
144			#[allow(unused)]
145			trait TestMulTrait<T> {
146				fn test_mul(_a: T, _b: T) {}
147			}
148
149			impl<T> TestMulTrait<$crate::packed_binary_field::test_utils::Unit> for T {}
150
151			struct TestMult<T>(std::marker::PhantomData<T>);
152
153			impl<T: $constraint + PackedField + $crate::underlier::WithUnderlier> TestMult<T> {
154				#[allow(unused)]
155				fn test_mul(
156					a: <T as $crate::underlier::WithUnderlier>::Underlier,
157					b: <T as $crate::underlier::WithUnderlier>::Underlier,
158				) {
159					let a = T::from_underlier(a);
160					let b = T::from_underlier(b);
161
162					let c = $mult_func(a, b);
163					for i in 0..T::WIDTH {
164						assert_eq!(c.get(i), a.get(i) * b.get(i));
165					}
166				}
167			}
168		};
169	}
170
171	pub(crate) use define_check_packed_mul;
172
173	macro_rules! define_check_packed_square {
174		($square_func:path, $constraint:path) => {
175			#[allow(unused)]
176			trait TestSquareTrait<T> {
177				fn test_square(_a: T) {}
178			}
179
180			impl<T> TestSquareTrait<$crate::packed_binary_field::test_utils::Unit> for T {}
181
182			struct TestSquare<T>(std::marker::PhantomData<T>);
183
184			impl<T: $constraint + PackedField + $crate::underlier::WithUnderlier> TestSquare<T> {
185				#[allow(unused)]
186				fn test_square(a: <T as $crate::underlier::WithUnderlier>::Underlier) {
187					let a = T::from_underlier(a);
188
189					let c = $square_func(a);
190					for i in 0..T::WIDTH {
191						assert_eq!(c.get(i), a.get(i) * a.get(i));
192					}
193				}
194			}
195		};
196	}
197
198	pub(crate) use define_check_packed_square;
199
200	macro_rules! define_check_packed_inverse {
201		($invert_func:path, $constraint:path) => {
202			#[allow(unused)]
203			trait TestInvertTrait<T> {
204				fn test_invert(_a: T) {}
205			}
206
207			impl<T> TestInvertTrait<$crate::packed_binary_field::test_utils::Unit> for T {}
208
209			struct TestInvert<T>(std::marker::PhantomData<T>);
210
211			#[allow(unused)]
212			impl<T: $constraint + PackedField + $crate::underlier::WithUnderlier> TestInvert<T> {
213				fn test_invert(a: <T as $crate::underlier::WithUnderlier>::Underlier) {
214					use crate::Field;
215
216					let a = T::from_underlier(a);
217
218					let c = $invert_func(a);
219					for i in 0..T::WIDTH {
220						assert!(
221							(c.get(i).is_zero().into()
222								&& a.get(i).is_zero().into()
223								&& c.get(i).is_zero().into())
224								|| T::Scalar::ONE == a.get(i) * c.get(i)
225						);
226					}
227				}
228			}
229		};
230	}
231
232	pub(crate) use define_check_packed_inverse;
233
234	/// Test if `mult_func` operation is a valid multiply operation on the given values for
235	/// all possible packed fields defined on u128.
236	macro_rules! define_multiply_tests {
237		($mult_func:path, $constraint:path) => {
238			$crate::packed_binary_field::test_utils::define_check_packed_mul!(
239				$mult_func,
240				$constraint
241			);
242
243			proptest::proptest! {
244				#[test]
245				fn test_mul_packed_8(a_val in proptest::prelude::any::<u8>(), b_val in proptest::prelude::any::<u8>()) {
246					use $crate::PackedBinaryField8x1b;
247					use $crate::PackedAESBinaryField1x8b;
248
249					TestMult::<PackedBinaryField8x1b>::test_mul(a_val.into(), b_val.into());
250					TestMult::<PackedAESBinaryField1x8b>::test_mul(a_val.into(), b_val.into());
251				}
252
253				#[test]
254				fn test_mul_packed_16(a_val in proptest::prelude::any::<u16>(), b_val in proptest::prelude::any::<u16>()) {
255					use $crate::PackedBinaryField16x1b;
256
257					TestMult::<PackedBinaryField16x1b>::test_mul(a_val.into(), b_val.into());
258				}
259
260				#[test]
261				fn test_mul_packed_32(a_val in proptest::prelude::any::<u32>(), b_val in proptest::prelude::any::<u32>()) {
262					use $crate::PackedBinaryField32x1b;
263
264					TestMult::<PackedBinaryField32x1b>::test_mul(a_val.into(), b_val.into());
265				}
266
267				#[test]
268				fn test_mul_packed_64(a_val in proptest::prelude::any::<u64>(), b_val in proptest::prelude::any::<u64>()) {
269					use $crate::PackedBinaryField64x1b;
270
271					TestMult::<PackedBinaryField64x1b>::test_mul(a_val.into(), b_val.into());
272				}
273
274				#[test]
275				fn test_mul_packed_128(a_val in proptest::prelude::any::<u128>(), b_val in proptest::prelude::any::<u128>()) {
276					use $crate::PackedBinaryField128x1b;
277					use $crate::PackedAESBinaryField16x8b;
278					use $crate::PackedBinaryGhash1x128b;
279
280					TestMult::<PackedBinaryField128x1b>::test_mul(a_val.into(), b_val.into());
281					TestMult::<PackedAESBinaryField16x8b>::test_mul(a_val.into(), b_val.into());
282					TestMult::<PackedBinaryGhash1x128b>::test_mul(a_val.into(), b_val.into());
283				}
284
285				#[test]
286				fn test_mul_packed_256(a_val in proptest::prelude::any::<[u128; 2]>(), b_val in proptest::prelude::any::<[u128; 2]>()) {
287					use $crate::PackedBinaryField256x1b;
288					use $crate::PackedAESBinaryField32x8b;
289					use $crate::PackedBinaryGhash2x128b;
290
291					TestMult::<PackedBinaryField256x1b>::test_mul(a_val.into(), b_val.into());
292					TestMult::<PackedAESBinaryField32x8b>::test_mul(a_val.into(), b_val.into());
293					TestMult::<PackedBinaryGhash2x128b>::test_mul(a_val.into(), b_val.into());
294				}
295
296				#[test]
297				fn test_mul_packed_512(a_val in proptest::prelude::any::<[u128; 4]>(), b_val in proptest::prelude::any::<[u128; 4]>()) {
298					use $crate::PackedBinaryField512x1b;
299					use $crate::PackedAESBinaryField64x8b;
300					use $crate::PackedBinaryGhash4x128b;
301
302					TestMult::<PackedBinaryField512x1b>::test_mul(a_val.into(), b_val.into());
303					TestMult::<PackedAESBinaryField64x8b>::test_mul(a_val.into(), b_val.into());
304					TestMult::<PackedBinaryGhash4x128b>::test_mul(a_val.into(), b_val.into());
305				}
306			}
307		};
308	}
309
310	/// Test if `square_func` operation is a valid square operation on the given value for
311	/// all possible packed fields.
312	macro_rules! define_square_tests {
313		($square_func:path, $constraint:path) => {
314			$crate::packed_binary_field::test_utils::define_check_packed_square!(
315				$square_func,
316				$constraint
317			);
318
319			proptest::proptest! {
320				#[test]
321				fn test_square_packed_8(a_val in proptest::prelude::any::<u8>()) {
322					use $crate::PackedBinaryField8x1b;
323					use $crate::PackedAESBinaryField1x8b;
324
325					TestSquare::<PackedBinaryField8x1b>::test_square(a_val.into());
326					TestSquare::<PackedAESBinaryField1x8b>::test_square(a_val.into());
327				}
328
329				#[test]
330				fn test_square_packed_16(a_val in proptest::prelude::any::<u16>()) {
331					use $crate::PackedBinaryField16x1b;
332
333					TestSquare::<PackedBinaryField16x1b>::test_square(a_val.into());
334				}
335
336				#[test]
337				fn test_square_packed_32(a_val in proptest::prelude::any::<u32>()) {
338					use $crate::PackedBinaryField32x1b;
339
340					TestSquare::<PackedBinaryField32x1b>::test_square(a_val.into());
341				}
342
343				#[test]
344				fn test_square_packed_64(a_val in proptest::prelude::any::<u64>()) {
345					use $crate::PackedBinaryField64x1b;
346
347					TestSquare::<PackedBinaryField64x1b>::test_square(a_val.into());
348				}
349
350				#[test]
351				fn test_square_packed_128(a_val in proptest::prelude::any::<u128>()) {
352					use $crate::PackedBinaryField128x1b;
353					use $crate::PackedAESBinaryField16x8b;
354					use $crate::PackedBinaryGhash1x128b;
355
356					TestSquare::<PackedBinaryField128x1b>::test_square(a_val.into());
357					TestSquare::<PackedAESBinaryField16x8b>::test_square(a_val.into());
358					TestSquare::<PackedBinaryGhash1x128b>::test_square(a_val.into());
359				}
360
361				#[test]
362				fn test_square_packed_256(a_val in proptest::prelude::any::<[u128; 2]>()) {
363					use $crate::PackedBinaryField256x1b;
364					use $crate::PackedAESBinaryField32x8b;
365					use $crate::PackedBinaryGhash2x128b;
366
367					TestSquare::<PackedBinaryField256x1b>::test_square(a_val.into());
368					TestSquare::<PackedAESBinaryField32x8b>::test_square(a_val.into());
369					TestSquare::<PackedBinaryGhash2x128b>::test_square(a_val.into());
370				}
371
372				#[test]
373				fn test_square_packed_512(a_val in proptest::prelude::any::<[u128; 4]>()) {
374					use $crate::PackedBinaryField512x1b;
375					use $crate::PackedAESBinaryField64x8b;
376					use $crate::PackedBinaryGhash4x128b;
377
378					TestSquare::<PackedBinaryField512x1b>::test_square(a_val.into());
379					TestSquare::<PackedAESBinaryField64x8b>::test_square(a_val.into());
380					TestSquare::<PackedBinaryGhash4x128b>::test_square(a_val.into());
381				}
382			}
383		};
384	}
385
386	/// Test if `invert_func` operation is a valid invert operation on the given value for
387	/// all possible packed fields.
388	macro_rules! define_invert_tests {
389		($invert_func:path, $constraint:path) => {
390			$crate::packed_binary_field::test_utils::define_check_packed_inverse!(
391				$invert_func,
392				$constraint
393			);
394
395			proptest::proptest! {
396				#[test]
397				fn test_invert_packed_8(a_val in proptest::prelude::any::<u8>()) {
398					use $crate::PackedBinaryField8x1b;
399					use $crate::PackedAESBinaryField1x8b;
400
401					TestInvert::<PackedBinaryField8x1b>::test_invert(a_val.into());
402					TestInvert::<PackedAESBinaryField1x8b>::test_invert(a_val.into());
403				}
404
405				#[test]
406				fn test_invert_packed_16(a_val in proptest::prelude::any::<u16>()) {
407					use $crate::PackedBinaryField16x1b;
408
409					TestInvert::<PackedBinaryField16x1b>::test_invert(a_val.into());
410				}
411
412				#[test]
413				fn test_invert_packed_32(a_val in proptest::prelude::any::<u32>()) {
414					use $crate::PackedBinaryField32x1b;
415
416					TestInvert::<PackedBinaryField32x1b>::test_invert(a_val.into());
417				}
418
419				#[test]
420				fn test_invert_packed_64(a_val in proptest::prelude::any::<u64>()) {
421					use $crate::PackedBinaryField64x1b;
422
423					TestInvert::<PackedBinaryField64x1b>::test_invert(a_val.into());
424				}
425
426				#[test]
427				fn test_invert_packed_128(a_val in proptest::prelude::any::<u128>()) {
428					use $crate::PackedBinaryField128x1b;
429					use $crate::PackedAESBinaryField16x8b;
430					use $crate::PackedBinaryGhash1x128b;
431
432					TestInvert::<PackedBinaryField128x1b>::test_invert(a_val.into());
433					TestInvert::<PackedAESBinaryField16x8b>::test_invert(a_val.into());
434					TestInvert::<PackedBinaryGhash1x128b>::test_invert(a_val.into());
435				}
436
437				#[test]
438				fn test_invert_packed_256(a_val in proptest::prelude::any::<[u128; 2]>()) {
439					use $crate::PackedBinaryField256x1b;
440					use $crate::PackedAESBinaryField32x8b;
441					use $crate::PackedBinaryGhash2x128b;
442
443					TestInvert::<PackedBinaryField256x1b>::test_invert(a_val.into());
444					TestInvert::<PackedAESBinaryField32x8b>::test_invert(a_val.into());
445					TestInvert::<PackedBinaryGhash2x128b>::test_invert(a_val.into());
446				}
447
448				#[test]
449				fn test_invert_packed_512(a_val in proptest::prelude::any::<[u128; 4]>()) {
450					use $crate::PackedBinaryField512x1b;
451					use $crate::PackedAESBinaryField64x8b;
452					use $crate::PackedBinaryGhash4x128b;
453
454					TestInvert::<PackedBinaryField512x1b>::test_invert(a_val.into());
455					TestInvert::<PackedAESBinaryField64x8b>::test_invert(a_val.into());
456					TestInvert::<PackedBinaryGhash4x128b>::test_invert(a_val.into());
457				}
458			}
459		};
460	}
461
462	pub(crate) use define_invert_tests;
463	pub(crate) use define_multiply_tests;
464	pub(crate) use define_square_tests;
465
466	pub fn check_interleave<P: PackedField + WithUnderlier>(
467		lhs: P::Underlier,
468		rhs: P::Underlier,
469		log_block_len: usize,
470	) {
471		let lhs = P::from_underlier(lhs);
472		let rhs = P::from_underlier(rhs);
473		let (a, b) = lhs.interleave(rhs, log_block_len);
474		let block_len = 1 << log_block_len;
475		for i in (0..P::WIDTH).step_by(block_len * 2) {
476			for j in 0..block_len {
477				assert_eq!(a.get(i + j), lhs.get(i + j));
478				assert_eq!(a.get(i + j + block_len), rhs.get(i + j));
479
480				assert_eq!(b.get(i + j), lhs.get(i + j + block_len));
481				assert_eq!(b.get(i + j + block_len), rhs.get(i + j + block_len));
482			}
483		}
484	}
485
486	pub fn check_interleave_all_heights<P: PackedField + WithUnderlier>(
487		lhs: P::Underlier,
488		rhs: P::Underlier,
489	) {
490		for log_block_len in 0..P::LOG_WIDTH {
491			check_interleave::<P>(lhs, rhs, log_block_len);
492		}
493	}
494
495	pub fn check_unzip<P: PackedField + WithUnderlier>(
496		lhs: P::Underlier,
497		rhs: P::Underlier,
498		log_block_len: usize,
499	) {
500		let lhs = P::from_underlier(lhs);
501		let rhs = P::from_underlier(rhs);
502		let block_len = 1 << log_block_len;
503		let (a, b) = lhs.unzip(rhs, log_block_len);
504		for i in (0..P::WIDTH / 2).step_by(block_len) {
505			for j in 0..block_len {
506				assert_eq!(
507					a.get(i + j),
508					lhs.get(2 * i + j),
509					"i: {}, j: {}, log_block_len: {}, P: {:?}",
510					i,
511					j,
512					log_block_len,
513					P::zero()
514				);
515				assert_eq!(
516					b.get(i + j),
517					lhs.get(2 * i + j + block_len),
518					"i: {}, j: {}, log_block_len: {}, P: {:?}",
519					i,
520					j,
521					log_block_len,
522					P::zero()
523				);
524			}
525		}
526
527		for i in (0..P::WIDTH / 2).step_by(block_len) {
528			for j in 0..block_len {
529				assert_eq!(
530					a.get(i + j + P::WIDTH / 2),
531					rhs.get(2 * i + j),
532					"i: {}, j: {}, log_block_len: {}, P: {:?}",
533					i,
534					j,
535					log_block_len,
536					P::zero()
537				);
538				assert_eq!(b.get(i + j + P::WIDTH / 2), rhs.get(2 * i + j + block_len));
539			}
540		}
541	}
542
543	pub fn check_transpose_all_heights<P: PackedField + WithUnderlier>(
544		lhs: P::Underlier,
545		rhs: P::Underlier,
546	) {
547		for log_block_len in 0..P::LOG_WIDTH {
548			check_unzip::<P>(lhs, rhs, log_block_len);
549		}
550	}
551}
552
553#[cfg(test)]
554mod tests {
555	use std::{fmt::Debug, iter::repeat_with, ops::Mul};
556
557	use binius_utils::{
558		DeserializeBytes, FixedSizeSerializeBytes, SerializeBytes, bytes::BytesMut,
559	};
560	use proptest::prelude::*;
561	use rand::prelude::*;
562	use test_utils::check_interleave_all_heights;
563
564	use super::{
565		test_utils::{define_invert_tests, define_multiply_tests, define_square_tests},
566		*,
567	};
568	use crate::{
569		Divisible, PackedAESBinaryField1x8b, PackedAESBinaryField16x8b, PackedAESBinaryField32x8b,
570		PackedAESBinaryField64x8b, PackedBinaryGhash1x128b, PackedBinaryGhash2x128b,
571		PackedBinaryGhash4x128b, PackedField, Random,
572		arithmetic_traits::{InvertOrZero, Square},
573		test_utils::check_transpose_all_heights,
574		underlier::{U2, U4},
575	};
576
577	fn test_add_packed<P: PackedField + From<u128>>(a_val: u128, b_val: u128) {
578		let a = P::from(a_val);
579		let b = P::from(b_val);
580		let c = a + b;
581		for i in 0..P::WIDTH {
582			assert_eq!(c.get(i), a.get(i) + b.get(i));
583		}
584	}
585
586	fn test_mul_packed<P: PackedField>(a: P, b: P) {
587		let c = a * b;
588		for i in 0..P::WIDTH {
589			assert_eq!(c.get(i), a.get(i) * b.get(i));
590		}
591	}
592
593	fn test_mul_packed_random<P: PackedField>() {
594		let mut rng = StdRng::seed_from_u64(0);
595		test_mul_packed(P::random(&mut rng), P::random(&mut rng))
596	}
597
598	fn test_set_then_get<P: PackedField>() {
599		let mut rng = StdRng::seed_from_u64(0);
600		let mut elem = P::random(&mut rng);
601
602		let scalars = repeat_with(|| P::Scalar::random(&mut rng))
603			.take(P::WIDTH)
604			.collect::<Vec<_>>();
605
606		for (i, val) in scalars.iter().enumerate() {
607			elem.set(i, *val);
608		}
609		for (i, val) in scalars.iter().enumerate() {
610			assert_eq!(elem.get(i), *val);
611		}
612	}
613
614	fn test_serialize_then_deserialize<P: PackedField + DeserializeBytes + SerializeBytes>() {
615		let mut buffer = BytesMut::new();
616		let mut rng = StdRng::seed_from_u64(0);
617		let packed = P::random(&mut rng);
618		packed.serialize(&mut buffer).unwrap();
619
620		let mut read_buffer = buffer.freeze();
621
622		assert_eq!(P::deserialize(&mut read_buffer).unwrap(), packed);
623	}
624
625	#[test]
626	fn test_set_then_get_128b() {
627		test_set_then_get::<PackedBinaryGhash1x128b>();
628		test_set_then_get::<PackedBinaryGhash2x128b>();
629		test_set_then_get::<PackedBinaryGhash4x128b>();
630	}
631
632	#[test]
633	fn test_serialize_then_deserialize_128b() {
634		test_serialize_then_deserialize::<PackedBinaryGhash1x128b>();
635		test_serialize_then_deserialize::<PackedBinaryGhash2x128b>();
636		test_serialize_then_deserialize::<PackedBinaryGhash4x128b>();
637	}
638
639	#[test]
640	fn test_serialize_deserialize_different_packing_width() {
641		let mut rng = StdRng::seed_from_u64(0);
642
643		let packed0 = PackedBinaryGhash1x128b::random(&mut rng);
644		let packed1 = PackedBinaryGhash1x128b::random(&mut rng);
645
646		let mut buffer = BytesMut::new();
647		packed0.serialize(&mut buffer).unwrap();
648		packed1.serialize(&mut buffer).unwrap();
649
650		let mut read_buffer = buffer.freeze();
651		let packed01 = PackedBinaryGhash2x128b::deserialize(&mut read_buffer).unwrap();
652
653		assert!(
654			packed01
655				.iter()
656				.zip([packed0, packed1])
657				.all(|(x, y)| x == y.get(0))
658		);
659	}
660
661	// TODO: Generate lots more proptests using macros
662	proptest! {
663		#[test]
664		fn test_add_packed_128x1b(a_val in any::<u128>(), b_val in any::<u128>()) {
665			test_add_packed::<PackedBinaryField128x1b>(a_val, b_val)
666		}
667
668		#[test]
669		fn test_add_packed_16x8b(a_val in any::<u128>(), b_val in any::<u128>()) {
670			test_add_packed::<PackedAESBinaryField16x8b>(a_val, b_val)
671		}
672
673		#[test]
674		fn test_add_packed_1x128b(a_val in any::<u128>(), b_val in any::<u128>()) {
675			test_add_packed::<PackedBinaryGhash1x128b>(a_val, b_val)
676		}
677	}
678
679	#[test]
680	fn test_mul_packed_256x1b() {
681		test_mul_packed_random::<PackedBinaryField256x1b>()
682	}
683
684	#[test]
685	fn test_mul_packed_32x8b() {
686		test_mul_packed_random::<PackedAESBinaryField32x8b>()
687	}
688
689	#[test]
690	fn test_mul_packed_2x128b() {
691		test_mul_packed_random::<PackedBinaryGhash2x128b>()
692	}
693
694	#[test]
695	fn test_iter_size_hint() {
696		assert_valid_iterator_with_exact_size_hint::<PackedBinaryField128x1b>();
697	}
698
699	fn assert_valid_iterator_with_exact_size_hint<P: PackedField>() {
700		assert_eq!(P::default().iter().size_hint(), (P::WIDTH, Some(P::WIDTH)));
701		assert_eq!(P::default().into_iter().size_hint(), (P::WIDTH, Some(P::WIDTH)));
702		assert_eq!(P::default().iter().count(), P::WIDTH);
703		assert_eq!(P::default().into_iter().count(), P::WIDTH);
704	}
705
706	define_multiply_tests!(Mul::mul, PackedField);
707
708	define_square_tests!(Square::square, PackedField);
709
710	define_invert_tests!(InvertOrZero::invert_or_zero, PackedField);
711
712	proptest! {
713		#[test]
714		fn test_interleave_2b(a_val in 0u8..3, b_val in 0u8..3) {
715			check_interleave_all_heights::<PackedBinaryField2x1b>(U2::new(a_val), U2::new(b_val));
716		}
717
718		#[test]
719		fn test_interleave_4b(a_val in 0u8..16, b_val in 0u8..16) {
720			check_interleave_all_heights::<PackedBinaryField4x1b>(U4::new(a_val), U4::new(b_val));
721		}
722
723		#[test]
724		fn test_interleave_8b(a_val in 0u8.., b_val in 0u8..) {
725			check_interleave_all_heights::<PackedBinaryField8x1b>(a_val, b_val);
726			check_interleave_all_heights::<PackedAESBinaryField1x8b>(a_val, b_val);
727		}
728
729		#[test]
730		fn test_interleave_16b(a_val in 0u16.., b_val in 0u16..) {
731			check_interleave_all_heights::<PackedBinaryField16x1b>(a_val, b_val);
732		}
733
734		#[test]
735		fn test_interleave_32b(a_val in 0u32.., b_val in 0u32..) {
736			check_interleave_all_heights::<PackedBinaryField32x1b>(a_val, b_val);
737		}
738
739		#[test]
740		fn test_interleave_64b(a_val in 0u64.., b_val in 0u64..) {
741			check_interleave_all_heights::<PackedBinaryField64x1b>(a_val, b_val);
742		}
743
744		#[test]
745		#[allow(clippy::useless_conversion)] // this warning depends on the target platform
746		fn test_interleave_128b(a_val in 0u128.., b_val in 0u128..) {
747			check_interleave_all_heights::<PackedBinaryField128x1b>(a_val.into(), b_val.into());
748			check_interleave_all_heights::<PackedAESBinaryField16x8b>(a_val.into(), b_val.into());
749			check_interleave_all_heights::<PackedBinaryGhash1x128b>(a_val.into(), b_val.into());
750		}
751
752		#[test]
753		fn test_interleave_256b(a_val in any::<[u128; 2]>(), b_val in any::<[u128; 2]>()) {
754			check_interleave_all_heights::<PackedBinaryField256x1b>(a_val.into(), b_val.into());
755			check_interleave_all_heights::<PackedAESBinaryField32x8b>(a_val.into(), b_val.into());
756			check_interleave_all_heights::<PackedBinaryGhash2x128b>(a_val.into(), b_val.into());
757		}
758
759		#[test]
760		fn test_interleave_512b(a_val in any::<[u128; 4]>(), b_val in any::<[u128; 4]>()) {
761			check_interleave_all_heights::<PackedBinaryField512x1b>(a_val.into(), b_val.into());
762			check_interleave_all_heights::<PackedAESBinaryField64x8b>(a_val.into(), b_val.into());
763			check_interleave_all_heights::<PackedBinaryGhash4x128b>(a_val.into(), b_val.into());
764		}
765
766		#[test]
767		fn check_transpose_2b(a_val in 0u8..3, b_val in 0u8..3) {
768			check_transpose_all_heights::<PackedBinaryField2x1b>(U2::new(a_val), U2::new(b_val));
769		}
770
771		#[test]
772		fn check_transpose_4b(a_val in 0u8..16, b_val in 0u8..16) {
773			check_transpose_all_heights::<PackedBinaryField4x1b>(U4::new(a_val), U4::new(b_val));
774		}
775
776		#[test]
777		fn check_transpose_8b(a_val in 0u8.., b_val in 0u8..) {
778			check_transpose_all_heights::<PackedBinaryField8x1b>(a_val, b_val);
779			check_transpose_all_heights::<PackedAESBinaryField1x8b>(a_val, b_val);
780		}
781
782		#[test]
783		fn check_transpose_16b(a_val in 0u16.., b_val in 0u16..) {
784			check_transpose_all_heights::<PackedBinaryField16x1b>(a_val, b_val);
785		}
786
787		#[test]
788		fn check_transpose_32b(a_val in 0u32.., b_val in 0u32..) {
789			check_transpose_all_heights::<PackedBinaryField32x1b>(a_val, b_val);
790		}
791
792		#[test]
793		fn check_transpose_64b(a_val in 0u64.., b_val in 0u64..) {
794			check_transpose_all_heights::<PackedBinaryField64x1b>(a_val, b_val);
795		}
796
797		#[test]
798		#[allow(clippy::useless_conversion)] // this warning depends on the target platform
799		fn check_transpose_128b(a_val in 0u128.., b_val in 0u128..) {
800			check_transpose_all_heights::<PackedBinaryField128x1b>(a_val.into(), b_val.into());
801			check_transpose_all_heights::<PackedAESBinaryField16x8b>(a_val.into(), b_val.into());
802			check_transpose_all_heights::<PackedBinaryGhash1x128b>(a_val.into(), b_val.into());
803		}
804
805		#[test]
806		fn check_transpose_256b(a_val in any::<[u128; 2]>(), b_val in any::<[u128; 2]>()) {
807			check_transpose_all_heights::<PackedBinaryField256x1b>(a_val.into(), b_val.into());
808			check_transpose_all_heights::<PackedAESBinaryField32x8b>(a_val.into(), b_val.into());
809			check_transpose_all_heights::<PackedBinaryGhash2x128b>(a_val.into(), b_val.into());
810		}
811
812		#[test]
813		fn check_transpose_512b(a_val in any::<[u128; 4]>(), b_val in any::<[u128; 4]>()) {
814			check_transpose_all_heights::<PackedBinaryField512x1b>(a_val.into(), b_val.into());
815			check_transpose_all_heights::<PackedAESBinaryField64x8b>(a_val.into(), b_val.into());
816			check_transpose_all_heights::<PackedBinaryGhash4x128b>(a_val.into(), b_val.into());
817		}
818	}
819
820	// The generic `SerializeBytes`/`DeserializeBytes` impls on `PackedPrimitiveType` round-trip
821	// across both integer underliers and (where applicable) SIMD underliers.
822	#[test]
823	fn test_serialize_roundtrip() {
824		fn check_roundtrip<
825			P: PackedField + SerializeBytes + DeserializeBytes + PartialEq + Debug,
826		>(
827			rng: &mut StdRng,
828		) {
829			let value = P::random(rng);
830			let mut buf = BytesMut::new();
831			value.serialize(&mut buf).unwrap();
832			let deserialized = P::deserialize(buf.freeze()).unwrap();
833			assert_eq!(value, deserialized);
834		}
835
836		let mut rng = StdRng::seed_from_u64(0);
837		check_roundtrip::<PackedBinaryField8x1b>(&mut rng);
838		check_roundtrip::<PackedBinaryField64x1b>(&mut rng);
839		check_roundtrip::<PackedBinaryField128x1b>(&mut rng);
840		check_roundtrip::<PackedBinaryGhash1x128b>(&mut rng);
841	}
842
843	// `FixedSizeSerializeBytes` propagates from the underlier. Integer-backed packed fields (here,
844	// `u8`- and `u64`-backed on every arch) report the underlier's byte size.
845	#[test]
846	fn test_fixed_size_byte_size() {
847		assert_eq!(<PackedBinaryField8x1b as FixedSizeSerializeBytes>::BYTE_SIZE, 1);
848		assert_eq!(<PackedBinaryField64x1b as FixedSizeSerializeBytes>::BYTE_SIZE, 8);
849	}
850}