binius_field/
extension.rs

1// Copyright 2023-2025 Irreducible Inc.
2
3use std::{
4	iter,
5	ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign},
6};
7
8use super::{Field, error::Error};
9
10pub trait ExtensionField<F: Field>:
11	Field
12	+ From<F>
13	+ TryInto<F>
14	+ Add<F, Output = Self>
15	+ Sub<F, Output = Self>
16	+ Mul<F, Output = Self>
17	+ AddAssign<F>
18	+ SubAssign<F>
19	+ MulAssign<F>
20{
21	/// Base-2 logarithm of the extension degree.
22	const LOG_DEGREE: usize;
23
24	/// Extension degree.
25	///
26	/// `DEGREE` is guaranteed to equal `2^LOG_DEGREE`.
27	const DEGREE: usize = 1 << Self::LOG_DEGREE;
28
29	/// For `0 <= i < DEGREE`, returns `i`-th basis field element.
30	///
31	/// ## Pre-conditions
32	///
33	/// * `i` must be in the range [0, `Self::DEGREE`).
34	#[inline]
35	fn basis(i: usize) -> Self {
36		Self::basis_checked(i).expect("pre-condition: 0 <= i < DEGREE")
37	}
38
39	/// For `0 <= i < DEGREE`, returns `i`-th basis field element.
40	///
41	/// This is the same operation as [`Self::basis`], but it returns an error result instead of
42	/// panicking if the index is out of range.
43	///
44	/// ## Throws
45	///
46	/// * `Error::ExtensionDegreeMismatch` unless `i` is in the range [0, `Self::DEGREE`).
47	fn basis_checked(i: usize) -> Result<Self, Error>;
48
49	/// Create an extension field element from a slice of base field elements in order
50	/// consistent with `basis(i)` return values.
51	/// Potentially faster than taking an inner product with a vector of basis elements.
52	#[inline]
53	fn from_bases(base_elems: impl IntoIterator<Item = F>) -> Result<Self, Error> {
54		Self::from_bases_sparse(base_elems, 0)
55	}
56
57	/// A specialized version of `from_bases` which assumes that only base field
58	/// elements with indices dividing `2^log_stride` can be nonzero.
59	///
60	/// `base_elems` should have length at most `ceil(DEGREE / 2^LOG_STRIDE)`. Note that
61	/// [`ExtensionField::from_bases`] is a special case of `from_bases_sparse` with `log_stride =
62	/// 0`.
63	fn from_bases_sparse(
64		base_elems: impl IntoIterator<Item = F>,
65		log_stride: usize,
66	) -> Result<Self, Error>;
67
68	/// Iterator over base field elements.
69	fn iter_bases(&self) -> impl Iterator<Item = F>;
70
71	/// Convert into an iterator over base field elements.
72	fn into_iter_bases(self) -> impl Iterator<Item = F>;
73
74	/// Returns the i-th base field element.
75	#[inline]
76	fn get_base(&self, i: usize) -> F {
77		assert!(i < Self::DEGREE, "index out of bounds");
78		unsafe { self.get_base_unchecked(i) }
79	}
80
81	/// Returns the i-th base field element without bounds checking.
82	///
83	/// # Safety
84	/// `i` must be less than `DEGREE`.
85	unsafe fn get_base_unchecked(&self, i: usize) -> F;
86}
87
88impl<F: Field> ExtensionField<F> for F {
89	const LOG_DEGREE: usize = 0;
90
91	#[inline(always)]
92	fn basis_checked(i: usize) -> Result<Self, Error> {
93		if i != 0 {
94			return Err(Error::ExtensionDegreeMismatch);
95		}
96		Ok(Self::ONE)
97	}
98
99	#[inline(always)]
100	fn from_bases_sparse(
101		base_elems: impl IntoIterator<Item = F>,
102		log_stride: usize,
103	) -> Result<Self, Error> {
104		let mut base_elems = base_elems.into_iter();
105		if log_stride != 0 {
106			return Err(Error::ExtensionDegreeMismatch);
107		}
108
109		match base_elems.next() {
110			Some(elem) => Ok(elem),
111			None => Ok(Self::ZERO),
112		}
113	}
114
115	#[inline(always)]
116	fn iter_bases(&self) -> impl Iterator<Item = F> {
117		iter::once(*self)
118	}
119
120	#[inline(always)]
121	fn into_iter_bases(self) -> impl Iterator<Item = F> {
122		iter::once(self)
123	}
124
125	#[inline(always)]
126	unsafe fn get_base_unchecked(&self, i: usize) -> F {
127		debug_assert_eq!(i, 0);
128		*self
129	}
130}