Skip to main content

binius_field/
binary_field.rs

1// Copyright 2023-2025 Irreducible Inc.
2// Copyright 2026 The Binius Developers
3
4use std::{
5	fmt::{Debug, Display, Formatter},
6	iter::{Product, Sum},
7	ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
8};
9
10use bytemuck::Zeroable;
11
12use super::{Underlier, UnderlierView, extension::ExtensionField};
13use crate::{Field, underlier::U1};
14
15/// A finite field with characteristic 2.
16pub trait BinaryField: ExtensionField<BinaryField1b> + UnderlierView<Underlier: Underlier> {
17	const N_BITS: usize = Self::ORDER_EXPONENT;
18
19	/// An element whose absolute trace is 1.
20	///
21	/// The absolute trace is the $\mathbb{F}_2$-linear map
22	///
23	/// $$\operatorname{Tr}(x) = \sum_{i=0}^{n-1} x^{2^i},$$
24	///
25	/// which lands in $\mathbb{F}_2$ and is surjective, so such an element always exists and
26	/// exactly half the field has trace 1. Which one is named here is arbitrary; each field picks
27	/// a single-bit element, the lowest one that qualifies.
28	///
29	/// The NTT's Gao-Mateer domain context seeds its basis with this: the descent
30	/// $\beta_i = \beta_{i+1}^2 + \beta_{i+1}$ reaches $\beta_0 = 1$ exactly when it starts from
31	/// an element of trace 1.
32	const TRACE_ONE_ELEMENT: Self;
33}
34
35/// Generates a binary field type over an underlier `$typ`.
36///
37/// `$gen` is the multiplicative generator and `$trace_one` an element of trace 1, both as raw
38/// underlier values.
39///
40/// The default form derives the field's arithmetic from its width-one packing.
41/// The `custom_arithmetic` form omits that, for a field that defines its own arithmetic.
42macro_rules! binary_field {
43	// Default: the field's arithmetic is its width-one packing's arithmetic.
44	($vis:vis $name:ident($typ:ty), $gen:expr, $trace_one:expr) => {
45		binary_field!(@base $vis $name($typ), $gen, $trace_one);
46		binary_field!(@arithmetic_via_packed $name, $typ);
47	};
48	// The field provides its own `Mul`/`Square`/`InvertOrZero`/`WideMul` separately.
49	(custom_arithmetic $vis:vis $name:ident($typ:ty), $gen:expr, $trace_one:expr) => {
50		binary_field!(@base $vis $name($typ), $gen, $trace_one);
51	};
52
53	(@base $vis:vis $name:ident($typ:ty), $gen:expr, $trace_one:expr) => {
54		#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Zeroable, bytemuck::TransparentWrapper)]
55		#[repr(transparent)]
56		$vis struct $name(pub(crate) $typ);
57
58		// NOTE: `new` is intentionally NOT generated here. Each field defines its own `new` so it
59		// can take an ergonomic constructor type independent of the underlier (e.g.
60		// `Ghash128b::new` takes `u128` even though its underlier is `M128`).
61		impl $name {
62			pub const fn val(self) -> $typ {
63				self.0
64			}
65		}
66
67		unsafe impl $crate::underlier::UnderlierView for $name {
68			type Underlier = $typ;
69		}
70
71		// Serialization forwards to the underlier.
72		impl binius_utils::SerializeBytes for $name {
73			fn serialize(&self, write_buf: impl binius_utils::bytes::BufMut) -> Result<(), binius_utils::SerializationError> {
74				self.0.serialize(write_buf)
75			}
76		}
77
78		impl binius_utils::DeserializeBytes for $name {
79			fn deserialize(read_buf: impl binius_utils::bytes::Buf) -> Result<Self, binius_utils::SerializationError> {
80				Ok(Self(binius_utils::DeserializeBytes::deserialize(read_buf)?))
81			}
82		}
83
84		impl binius_utils::FixedSizeSerializeBytes for $name {
85			const BYTE_SIZE: usize = <$typ as binius_utils::FixedSizeSerializeBytes>::BYTE_SIZE;
86		}
87
88		impl Neg for $name {
89			type Output = Self;
90
91			fn neg(self) -> Self::Output {
92				self
93			}
94		}
95
96		impl Add<Self> for $name {
97			type Output = Self;
98
99			#[allow(clippy::suspicious_arithmetic_impl)]
100			fn add(self, rhs: Self) -> Self::Output {
101				$name(self.0 ^ rhs.0)
102			}
103		}
104
105		impl Add<&Self> for $name {
106			type Output = Self;
107
108			#[allow(clippy::suspicious_arithmetic_impl)]
109			fn add(self, rhs: &Self) -> Self::Output {
110				$name(self.0 ^ rhs.0)
111			}
112		}
113
114		impl Sub<Self> for $name {
115			type Output = Self;
116
117			#[allow(clippy::suspicious_arithmetic_impl)]
118			fn sub(self, rhs: Self) -> Self::Output {
119				$name(self.0 ^ rhs.0)
120			}
121		}
122
123		impl Sub<&Self> for $name {
124			type Output = Self;
125
126			#[allow(clippy::suspicious_arithmetic_impl)]
127			fn sub(self, rhs: &Self) -> Self::Output {
128				$name(self.0 ^ rhs.0)
129			}
130		}
131
132		impl Mul<&Self> for $name {
133			type Output = Self;
134
135			fn mul(self, rhs: &Self) -> Self::Output {
136				self * *rhs
137			}
138		}
139
140		impl AddAssign<Self> for $name {
141			fn add_assign(&mut self, rhs: Self) {
142				*self = *self + rhs;
143			}
144		}
145
146		impl AddAssign<&Self> for $name {
147			fn add_assign(&mut self, rhs: &Self) {
148				*self = *self + *rhs;
149			}
150		}
151
152		impl SubAssign<Self> for $name {
153			fn sub_assign(&mut self, rhs: Self) {
154				*self = *self - rhs;
155			}
156		}
157
158		impl SubAssign<&Self> for $name {
159			fn sub_assign(&mut self, rhs: &Self) {
160				*self = *self - *rhs;
161			}
162		}
163
164		impl MulAssign<Self> for $name {
165			fn mul_assign(&mut self, rhs: Self) {
166				*self = *self * rhs;
167			}
168		}
169
170		impl MulAssign<&Self> for $name {
171			fn mul_assign(&mut self, rhs: &Self) {
172				*self = *self * rhs;
173			}
174		}
175
176		impl Sum<Self> for $name {
177			fn sum<I: Iterator<Item=Self>>(iter: I) -> Self {
178				iter.fold(Self::ZERO, |acc, x| acc + x)
179			}
180		}
181
182		impl<'a> Sum<&'a Self> for $name {
183			fn sum<I: Iterator<Item=&'a Self>>(iter: I) -> Self {
184				iter.fold(Self::ZERO, |acc, x| acc + x)
185			}
186		}
187
188		impl Product<Self> for $name {
189			fn product<I: Iterator<Item=Self>>(iter: I) -> Self {
190				iter.fold(Self::ONE, |acc, x| acc * x)
191			}
192		}
193
194		impl<'a> Product<&'a Self> for $name {
195			fn product<I: Iterator<Item=&'a Self>>(iter: I) -> Self {
196				iter.fold(Self::ONE, |acc, x| acc * x)
197			}
198		}
199
200
201		impl Field for $name {
202			const ZERO: Self = $name(<$typ as $crate::underlier::Underlier>::ZERO);
203			const ONE: Self = $name(<$typ as $crate::underlier::Underlier>::ONE);
204			const CHARACTERISTIC: usize = 2;
205			const ORDER_EXPONENT: usize = <$typ as $crate::underlier::Underlier>::BITS;
206			const MULTIPLICATIVE_GENERATOR: $name = $name($gen);
207
208			fn double(&self) -> Self {
209				Self::ZERO
210			}
211		}
212
213		// A field element divides into exactly one element of itself. This makes the field a
214		// degenerate packed field of width one (see the `PackedField` impl below).
215		impl $crate::Divisible<$name> for $name {
216			const LOG_N: usize = 0;
217
218			#[inline]
219			fn value_iter(value: Self) -> impl ExactSizeIterator<Item = $name> + Send + Clone {
220				std::iter::once(value)
221			}
222
223			#[inline]
224			fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = $name> + Send + Clone + '_ {
225				std::iter::once(*value)
226			}
227
228			#[inline]
229			fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = $name> + Send + Clone + '_ {
230				slice.iter().copied()
231			}
232
233			#[inline]
234			unsafe fn get_unchecked(&self, _index: usize) -> $name {
235				*self
236			}
237
238			#[inline]
239			unsafe fn set_unchecked(&mut self, _index: usize, val: $name) {
240				*self = val;
241			}
242
243			#[inline]
244			fn broadcast(val: $name) -> Self {
245				val
246			}
247
248			#[inline]
249			fn from_iter(mut iter: impl Iterator<Item = $name>) -> Self {
250				iter.next().unwrap_or(Self::ZERO)
251			}
252		}
253
254		// As a width-one packed field, a field element is masked by its single selector: kept
255		// when selected, otherwise zeroed. Uses the same underlier bitmask strategy as
256		// PackedPrimitiveType so the mask type and AND operation are consistent.
257		impl $crate::Maskable<$name> for $name {
258			type Mask = $typ;
259
260			#[inline]
261			fn make_mask(mut selectors: impl Iterator<Item = bool>) -> $typ {
262				<$typ as $crate::Divisible<_>>::broadcast(
263					$crate::underlier::U1::from(selectors.next().unwrap_or(false)),
264				)
265			}
266
267			#[inline]
268			fn select(&self, mask: &$typ) -> Self {
269				Self(self.0 & *mask)
270			}
271		}
272
273		impl $crate::PackedField for $name {
274			#[inline]
275			fn iter(&self) -> impl Iterator<Item = Self::Scalar> + Send + Clone + '_ {
276				std::iter::once(*self)
277			}
278
279			#[inline]
280			fn into_iter(self) -> impl Iterator<Item = Self::Scalar> + Send + Clone {
281				std::iter::once(self)
282			}
283
284			#[inline]
285			fn iter_slice(slice: &[Self]) -> impl Iterator<Item = Self::Scalar> + Send + Clone + '_ {
286				slice.iter().copied()
287			}
288
289			fn interleave(self, _other: Self, _log_block_len: usize) -> (Self, Self) {
290				panic!("cannot interleave when WIDTH = 1");
291			}
292
293			fn unzip(self, _other: Self, _log_block_len: usize) -> (Self, Self) {
294				panic!("cannot transpose when WIDTH = 1");
295			}
296
297			#[inline]
298			fn from_fn(mut f: impl FnMut(usize) -> Self::Scalar) -> Self {
299				f(0)
300			}
301
302			#[inline]
303			unsafe fn spread_unchecked(self, _log_block_len: usize, _block_idx: usize) -> Self {
304				self
305			}
306		}
307
308		impl ::rand::distr::Distribution<$name> for ::rand::distr::StandardUniform {
309			fn sample<R: ::rand::Rng + ?Sized>(&self, rng: &mut R) -> $name {
310				$name(::rand::distr::StandardUniform.sample(rng))
311			}
312		}
313
314		impl Display for $name {
315			fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
316				write!(f, "0x{repr:0>width$x}", repr=self.val(), width=Self::N_BITS.max(4) / 4)
317			}
318		}
319
320		impl Debug for $name {
321			fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
322				let structure_name = std::any::type_name::<$name>().split("::").last().expect("exist");
323
324				write!(f, "{}({})",structure_name, self)
325			}
326		}
327
328		impl BinaryField for $name {
329			const TRACE_ONE_ELEMENT: Self = $name($trace_one);
330		}
331
332		impl From<$typ> for $name {
333			fn from(val: $typ) -> Self {
334				return Self(val)
335			}
336		}
337
338		impl From<$name> for $typ {
339			fn from(val: $name) -> Self {
340				return val.0
341			}
342		}
343	};
344
345	// Each op lifts the underlier into the width-one packing, runs its arithmetic, and lowers back.
346	(@arithmetic_via_packed $name:ident, $typ:ty) => {
347		impl Mul<Self> for $name {
348			type Output = Self;
349
350			#[inline]
351			fn mul(self, rhs: Self) -> Self {
352				type P = $crate::packed_fields::primitive::PackedPrimitiveType<$typ, $name>;
353				Self((P::from_underlier(self.0) * P::from_underlier(rhs.0)).to_underlier())
354			}
355		}
356
357		impl $crate::arithmetic_traits::Square for $name {
358			#[inline]
359			fn square(self) -> Self {
360				type P = $crate::packed_fields::primitive::PackedPrimitiveType<$typ, $name>;
361				Self(
362					$crate::arithmetic_traits::Square::square(P::from_underlier(self.0)).to_underlier(),
363				)
364			}
365		}
366
367		impl $crate::arithmetic_traits::InvertOrZero for $name {
368			#[inline]
369			fn invert_or_zero(self) -> Self {
370				type P = $crate::packed_fields::primitive::PackedPrimitiveType<$typ, $name>;
371				Self(
372					$crate::arithmetic_traits::InvertOrZero::invert_or_zero(P::from_underlier(self.0))
373						.to_underlier(),
374				)
375			}
376		}
377
378		impl $crate::arithmetic_traits::WideMul for $name {
379			type Output = <$crate::packed_fields::primitive::PackedPrimitiveType<$typ, $name> as $crate::arithmetic_traits::WideMul>::Output;
380
381			#[inline]
382			fn wide_mul(a: Self, b: Self) -> Self::Output {
383				type P = $crate::packed_fields::primitive::PackedPrimitiveType<$typ, $name>;
384				<P as $crate::arithmetic_traits::WideMul>::wide_mul(
385					P::from_underlier(a.0),
386					P::from_underlier(b.0),
387				)
388			}
389
390			#[inline]
391			fn reduce(wide: Self::Output) -> Self {
392				type P = $crate::packed_fields::primitive::PackedPrimitiveType<$typ, $name>;
393				Self(<P as $crate::arithmetic_traits::WideMul>::reduce(wide).to_underlier())
394			}
395		}
396	};
397}
398
399pub(crate) use binary_field;
400
401macro_rules! impl_field_extension {
402	($subfield_name:ident($subfield_typ:ty) < @$log_degree:expr => $name:ident($typ:ty)) => {
403		impl TryFrom<$name> for $subfield_name {
404			type Error = ();
405
406			#[inline]
407			fn try_from(elem: $name) -> Result<Self, Self::Error> {
408				use $crate::{Divisible, underlier::Underlier};
409
410				// `elem` lies in the subfield iff every subfield-underlier limb above the
411				// least-significant one is zero (equivalent to `elem >> N_BITS == 0`).
412				let in_subfield = Divisible::<$subfield_typ>::ref_iter(&elem.0)
413					.skip(1)
414					.all(|limb| limb == <$subfield_typ as Underlier>::ZERO);
415				if in_subfield {
416					Ok($subfield_name(Divisible::<$subfield_typ>::get(&elem.0, 0)))
417				} else {
418					Err(())
419				}
420			}
421		}
422
423		impl From<$subfield_name> for $name {
424			#[inline]
425			fn from(elem: $subfield_name) -> Self {
426				$name(<$typ>::from(elem.val()))
427			}
428		}
429
430		impl Add<$subfield_name> for $name {
431			type Output = Self;
432
433			#[inline]
434			fn add(self, rhs: $subfield_name) -> Self::Output {
435				self + Self::from(rhs)
436			}
437		}
438
439		impl Sub<$subfield_name> for $name {
440			type Output = Self;
441
442			#[inline]
443			fn sub(self, rhs: $subfield_name) -> Self::Output {
444				self - Self::from(rhs)
445			}
446		}
447
448		// The subfield coordinates are literally `$typ`'s `$subfield_typ` limbs (see `basis`
449		// below), so multiplying by a subfield scalar is linear in each limb: reinterpret `self`
450		// as a `PackedPrimitiveType` of the subfield, broadcast-multiply by `rhs`, and cast back.
451		impl Mul<$subfield_name> for $name {
452			type Output = Self;
453
454			#[inline]
455			fn mul(self, rhs: $subfield_name) -> Self::Output {
456				type P =
457					$crate::packed_fields::primitive::PackedPrimitiveType<$typ, $subfield_name>;
458				Self((P::from_underlier(self.0) * P::broadcast(rhs)).to_underlier())
459			}
460		}
461
462		impl AddAssign<$subfield_name> for $name {
463			#[inline]
464			fn add_assign(&mut self, rhs: $subfield_name) {
465				*self = *self + rhs;
466			}
467		}
468
469		impl SubAssign<$subfield_name> for $name {
470			#[inline]
471			fn sub_assign(&mut self, rhs: $subfield_name) {
472				*self = *self - rhs;
473			}
474		}
475
476		impl MulAssign<$subfield_name> for $name {
477			#[inline]
478			fn mul_assign(&mut self, rhs: $subfield_name) {
479				*self = *self * rhs;
480			}
481		}
482
483		impl Add<$name> for $subfield_name {
484			type Output = $name;
485
486			#[inline]
487			fn add(self, rhs: $name) -> Self::Output {
488				rhs + self
489			}
490		}
491
492		impl Sub<$name> for $subfield_name {
493			type Output = $name;
494
495			#[allow(clippy::suspicious_arithmetic_impl)]
496			#[inline]
497			fn sub(self, rhs: $name) -> Self::Output {
498				rhs + self
499			}
500		}
501
502		impl Mul<$name> for $subfield_name {
503			type Output = $name;
504
505			#[inline]
506			fn mul(self, rhs: $name) -> Self::Output {
507				rhs * self
508			}
509		}
510
511		impl ExtensionField<$subfield_name> for $name {
512			const LOG_DEGREE: usize = $log_degree;
513
514			#[inline]
515			fn basis(i: usize) -> Self {
516				use $crate::{Divisible, underlier::Underlier};
517
518				assert!(
519					i < 1 << $log_degree,
520					"index {} out of range for degree {}",
521					i,
522					1 << $log_degree
523				);
524				// The `i`-th basis element sets subfield-underlier limb `i` to one, i.e. bit
525				// `i * N_BITS` (equivalent to `ONE << (i * N_BITS)`).
526				let mut underlier = <$typ as Underlier>::ZERO;
527				Divisible::<$subfield_typ>::set(
528					&mut underlier,
529					i,
530					<$subfield_typ as Underlier>::ONE,
531				);
532				Self(underlier)
533			}
534
535			#[inline]
536			fn from_bases_sparse(
537				base_elems: impl IntoIterator<Item = $subfield_name>,
538				log_stride: usize,
539			) -> Self {
540				use $crate::{Divisible, underlier::Underlier};
541
542				debug_assert!($name::N_BITS.is_power_of_two());
543				let shift_step = ($subfield_name::N_BITS << log_stride) & ($name::N_BITS - 1);
544				let mut underlier = <$typ as Underlier>::ZERO;
545				let mut shift = 0;
546
547				for elem in base_elems.into_iter() {
548					assert!(shift < $name::N_BITS, "too many base elements for extension degree");
549					// `shift` is a multiple of the subfield width, so it addresses limb
550					// `shift / N_BITS`; OR the element in (matching the previous `|= .. << shift`).
551					let limb = shift / $subfield_name::N_BITS;
552					let acc = Divisible::<$subfield_typ>::get(&underlier, limb) | elem.val();
553					Divisible::<$subfield_typ>::set(&mut underlier, limb, acc);
554					shift += shift_step;
555				}
556
557				Self(underlier)
558			}
559
560			#[inline]
561			fn iter_bases(&self) -> impl Iterator<Item = $subfield_name> {
562				use binius_utils::iter::IterExtensions;
563				use $crate::{Divisible, underlier::UnderlierView};
564
565				Divisible::<<$subfield_name as UnderlierView>::Underlier>::ref_iter(&self.0)
566					.map_skippable($subfield_name::from)
567			}
568
569			#[inline]
570			unsafe fn get_base_unchecked(&self, i: usize) -> $subfield_name {
571				use $crate::{Divisible, underlier::UnderlierView};
572				// Safety: the caller guarantees `i < Self::N` (over subfield elements).
573				unsafe {
574					$subfield_name::from_underlier(Divisible::<
575						<$subfield_name as UnderlierView>::Underlier,
576					>::get_unchecked(&self.to_underlier(), i))
577				}
578			}
579
580			#[inline]
581			fn square_transpose(values: &mut [Self]) {
582				crate::transpose::square_transforms_extension_field::<$subfield_name, Self>(values)
583			}
584		}
585	};
586}
587
588pub(crate) use impl_field_extension;
589
590// The trace over the prime field is the identity here, so `ONE` is the only trace-1 element.
591binary_field!(pub BinaryField1b(U1), U1::new(0x1), U1::new(0x1));
592
593impl BinaryField1b {
594	pub const fn new(value: U1) -> Self {
595		Self(value)
596	}
597}
598
599impl From<u8> for BinaryField1b {
600	#[inline]
601	fn from(val: u8) -> Self {
602		Self::new(U1::new(val))
603	}
604}
605
606impl From<BinaryField1b> for u8 {
607	#[inline]
608	fn from(value: BinaryField1b) -> Self {
609		value.val().into()
610	}
611}
612
613impl From<bool> for BinaryField1b {
614	#[inline]
615	fn from(value: bool) -> Self {
616		Self::from(U1::new_unchecked(value.into()))
617	}
618}
619
620#[cfg(test)]
621pub(crate) mod tests {
622	use binius_utils::{DeserializeBytes, SerializeBytes, bytes::BytesMut};
623	use proptest::prelude::*;
624
625	use super::BinaryField1b as BF1;
626	use crate::{
627		BinaryField, BinaryField1b, ExtensionField, Field, Ghash128b, GhashSq256b, Rijndael8b,
628		arithmetic_traits::InvertOrZero,
629	};
630
631	#[test]
632	fn test_gf2_add() {
633		assert_eq!(BF1::from(0) + BF1::from(0), BF1::from(0));
634		assert_eq!(BF1::from(0) + BF1::from(1), BF1::from(1));
635		assert_eq!(BF1::from(1) + BF1::from(0), BF1::from(1));
636		assert_eq!(BF1::from(1) + BF1::from(1), BF1::from(0));
637	}
638
639	#[test]
640	fn test_gf2_sub() {
641		assert_eq!(BF1::from(0) - BF1::from(0), BF1::from(0));
642		assert_eq!(BF1::from(0) - BF1::from(1), BF1::from(1));
643		assert_eq!(BF1::from(1) - BF1::from(0), BF1::from(1));
644		assert_eq!(BF1::from(1) - BF1::from(1), BF1::from(0));
645	}
646
647	#[test]
648	fn test_gf2_mul() {
649		assert_eq!(BF1::from(0) * BF1::from(0), BF1::from(0));
650		assert_eq!(BF1::from(0) * BF1::from(1), BF1::from(0));
651		assert_eq!(BF1::from(1) * BF1::from(0), BF1::from(0));
652		assert_eq!(BF1::from(1) * BF1::from(1), BF1::from(1));
653	}
654
655	pub(crate) fn is_binary_field_valid_generator<F: BinaryField>() -> bool {
656		// Binary fields should contain a multiplicative subgroup of size 2^n - 1
657		let mut order = if F::N_BITS == 128 {
658			u128::MAX
659		} else {
660			(1 << F::N_BITS) - 1
661		};
662
663		// Naive factorization of group order - represented as a multiset of prime factors
664		let mut factorization = Vec::new();
665
666		let mut prime = 2;
667		while prime * prime <= order {
668			while order.is_multiple_of(prime) {
669				order /= prime;
670				factorization.push(prime);
671			}
672
673			prime += if prime > 2 { 2 } else { 1 };
674		}
675
676		if order > 1 {
677			factorization.push(order);
678		}
679
680		// Iterate over all divisors (some may be tested several times if order is non-square-free)
681		for mask in 0..(1 << factorization.len()) {
682			let mut divisor = 1;
683
684			for (bit_index, &prime) in factorization.iter().enumerate() {
685				if (1 << bit_index) & mask != 0 {
686					divisor *= prime;
687				}
688			}
689
690			// Compute pow(generator, divisor) in log time
691			divisor = divisor.reverse_bits();
692
693			let mut pow_divisor = F::ONE;
694			while divisor > 0 {
695				pow_divisor *= pow_divisor;
696
697				if divisor & 1 != 0 {
698					pow_divisor *= F::MULTIPLICATIVE_GENERATOR;
699				}
700
701				divisor >>= 1;
702			}
703
704			// Generator invariant
705			let is_root_of_unity = pow_divisor == F::ONE;
706			let is_full_group = mask + 1 == 1 << factorization.len();
707
708			if is_root_of_unity && !is_full_group || !is_root_of_unity && is_full_group {
709				return false;
710			}
711		}
712
713		true
714	}
715
716	#[test]
717	fn test_multiplicative_generators() {
718		assert!(is_binary_field_valid_generator::<BinaryField1b>());
719		assert!(is_binary_field_valid_generator::<Rijndael8b>());
720		assert!(is_binary_field_valid_generator::<Ghash128b>());
721	}
722
723	/// The absolute trace $\operatorname{Tr}(x) = \sum_{i=0}^{n-1} x^{2^i}$, computed by repeated
724	/// squaring rather than by any property of the field's representation.
725	fn trace<F: BinaryField>(x: F) -> F {
726		let mut acc = F::ZERO;
727		let mut square = x;
728		for _ in 0..F::N_BITS {
729			acc += square;
730			square = square.square();
731		}
732		acc
733	}
734
735	/// Every field's declared element really has trace 1.
736	///
737	/// A wrong constant would not fail loudly on its own: the Gao-Mateer basis it seeds asserts
738	/// $\beta_0 = 1$, so it would surface as a panic deep inside NTT setup rather than here.
739	#[test]
740	fn test_trace_one_elements() {
741		fn check<F: BinaryField>() {
742			assert_eq!(trace(F::TRACE_ONE_ELEMENT), F::ONE);
743		}
744		check::<BinaryField1b>();
745		check::<Rijndael8b>();
746		check::<Ghash128b>();
747		check::<GhashSq256b>();
748	}
749
750	/// The trace lands in $\mathbb{F}_2$ for every element, not just the declared one. This pins
751	/// the helper above, so a `trace` that silently computed something else could not make the
752	/// previous test pass.
753	#[test]
754	fn test_trace_lands_in_the_prime_subfield() {
755		for value in 0..=u8::MAX {
756			let t = trace(Rijndael8b::new(value));
757			assert!(t == Rijndael8b::ZERO || t == Rijndael8b::ONE, "value {value:#04x}");
758		}
759	}
760
761	#[test]
762	fn test_field_degrees() {
763		assert_eq!(BinaryField1b::N_BITS, 1);
764		assert_eq!(Rijndael8b::N_BITS, 8);
765		assert_eq!(Ghash128b::N_BITS, 128);
766	}
767
768	#[test]
769	fn test_field_formatting() {
770		assert_eq!(format!("{}", BinaryField1b::from(1)), "0x1");
771		assert_eq!(format!("{}", Rijndael8b::from(3)), "0x03");
772		assert_eq!(format!("{}", Ghash128b::new(5)), "0x00000000000000000000000000000005");
773	}
774
775	#[test]
776	fn test_inverse_on_zero() {
777		assert!(BinaryField1b::ZERO.invert_or_zero().is_zero());
778		assert!(Rijndael8b::ZERO.invert_or_zero().is_zero());
779		assert!(Ghash128b::ZERO.invert_or_zero().is_zero());
780	}
781
782	proptest! {
783		#[test]
784		fn test_inverse_8b(val in 1u8..) {
785			let x = Rijndael8b::new(val);
786			// Safety: `val` is in `1..`, so `x` is non-zero.
787			let x_inverse = unsafe { x.invert() };
788			assert_eq!(x * x_inverse, Rijndael8b::ONE);
789		}
790
791		#[test]
792		fn test_inverse_128b(val in 1u128..) {
793			let x = Ghash128b::from(val);
794			// Safety: `val` is in `1..`, so `x` is non-zero.
795			let x_inverse = unsafe { x.invert() };
796			assert_eq!(x * x_inverse, Ghash128b::ONE);
797		}
798	}
799
800	/// Checks the `TryFrom` conversion narrowing an extension field element to its subfield:
801	/// elements embedded from the subfield must round-trip, and elements with a nonzero
802	/// coefficient outside the subfield must be rejected.
803	fn assert_subfield_extraction<FSub: Field, F: ExtensionField<FSub>>() {
804		assert_eq!(TryInto::<FSub>::try_into(F::from(FSub::ZERO)).ok(), Some(FSub::ZERO));
805
806		// `BinaryField1b` has a trivial multiplicative group, so for the three pairs with that
807		// subfield this sweeps `ONE` alone, which together with `ZERO` is already the whole field.
808		// Only `Ghash128b` in `GhashSq256b` walks non-trivial subfield values.
809		let mut elem = FSub::ONE;
810		for _ in 0..4 {
811			assert_eq!(TryInto::<FSub>::try_into(F::from(elem)).ok(), Some(elem));
812			elem *= FSub::MULTIPLICATIVE_GENERATOR;
813		}
814
815		// `basis(i)` for `i > 0` has a zero coefficient of `1` and a nonzero higher coefficient,
816		// so it lies outside the subfield - with or without a subfield part added on.
817		for i in 1..F::DEGREE {
818			assert!(TryInto::<FSub>::try_into(F::basis(i)).is_err());
819			assert!(TryInto::<FSub>::try_into(F::basis(i) + F::ONE).is_err());
820		}
821	}
822
823	#[test]
824	fn test_subfield_extraction() {
825		assert_subfield_extraction::<BinaryField1b, Ghash128b>();
826		assert_subfield_extraction::<BinaryField1b, Rijndael8b>();
827		assert_subfield_extraction::<Ghash128b, GhashSq256b>();
828		assert_subfield_extraction::<BinaryField1b, GhashSq256b>();
829	}
830
831	#[test]
832	fn test_serialization() {
833		let mut buffer = BytesMut::new();
834		let b1 = BinaryField1b::from(0x1);
835		let b8 = Rijndael8b::new(0x12);
836		let b128 = Ghash128b::new(0x147AD0369CF258BE8899AABBCCDDEEFF);
837
838		b1.serialize(&mut buffer).unwrap();
839		b8.serialize(&mut buffer).unwrap();
840		b128.serialize(&mut buffer).unwrap();
841
842		let mut read_buffer = buffer.freeze();
843
844		assert_eq!(BinaryField1b::deserialize(&mut read_buffer).unwrap(), b1);
845		assert_eq!(Rijndael8b::deserialize(&mut read_buffer).unwrap(), b8);
846		assert_eq!(Ghash128b::deserialize(&mut read_buffer).unwrap(), b128);
847	}
848}