p3_field/extension/mod.rs
1use core::iter;
2use core::marker::PhantomData;
3
4use crate::field::Field;
5use crate::{Algebra, ExtensionField, PackedField};
6
7mod binomial_extension;
8mod complex;
9mod cubic_extension;
10mod packed_binomial_extension;
11mod packed_cubic_extension;
12mod packed_quintic_extension;
13mod quintic_extension;
14
15use alloc::vec::Vec;
16
17pub use binomial_extension::*;
18pub use complex::*;
19pub use cubic_extension::{CubicTrinomialExtensionField, cubic_square, trinomial_cubic_mul};
20pub use packed_binomial_extension::*;
21pub use packed_cubic_extension::PackedCubicTrinomialExtensionField;
22pub use packed_quintic_extension::PackedQuinticTrinomialExtensionField;
23pub use quintic_extension::{
24 QuinticTrinomialExtensionField, quintic_square, trinomial_quintic_mul,
25};
26
27// ---------------------------------------------------------------------------
28// Shape-parameterized algebra for extension fields
29// ---------------------------------------------------------------------------
30//
31// Extension-ring arithmetic (mul / square / add / sub / base_mul) is exposed
32// via a single trait `ExtensionAlgebra<F, D, Shape>`, parameterized by:
33// - the base field `F`,
34// - the extension degree `D`,
35// - a marker `Shape: ExtensionShape` selecting the reducing polynomial.
36//
37// The three supported shapes are:
38// - `Binomial<F>` — `F[X] / (X^D - W)`, degree-generic, `W = F::W`.
39// - `CubicTrinomial` — `F[X] / (X^3 - X - 1)`.
40// - `QuinticTrinomial`— `F[X] / (X^5 + X^2 - 1)`.
41//
42// SIMD-packed types override `ext_mul` / `ext_square` with optimized kernels;
43// the other three methods have sensible coefficient-wise defaults.
44
45/// Sealed marker for the reducing polynomial shape of an extension field.
46///
47/// The three concrete shapes — [`Binomial`], [`CubicTrinomial`], and
48/// [`QuinticTrinomial`] — correspond to the reducers
49/// `X^D - W`, `X^3 - X - 1`, and `X^5 + X^2 - 1` respectively.
50pub trait ExtensionShape: 'static + sealed::Sealed {}
51
52mod sealed {
53 pub trait Sealed {}
54}
55
56/// Marker for the binomial reducer `X^D - W` (degree-generic).
57#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
58pub struct Binomial<F>(PhantomData<F>);
59
60/// Marker for the trinomial reducer `X^3 - X - 1`.
61#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
62pub struct CubicTrinomial;
63
64/// Marker for the trinomial reducer `X^5 + X^2 - 1`.
65#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
66pub struct QuinticTrinomial;
67
68impl<F: Field> sealed::Sealed for Binomial<F> {}
69impl sealed::Sealed for CubicTrinomial {}
70impl sealed::Sealed for QuinticTrinomial {}
71
72impl<F: Field> ExtensionShape for Binomial<F> {}
73impl ExtensionShape for CubicTrinomial {}
74impl ExtensionShape for QuinticTrinomial {}
75
76/// Unified extension-field representation.
77///
78/// An `ExtField<F, D, Shape, A>` represents an element of a degree-`D` extension
79/// of the base field `F`, with the reducing polynomial determined by `Shape`.
80/// The `A` parameter is the algebra over `F` storing each coefficient — usually
81/// `F` itself for scalar elements, or `F::Packing` for SIMD-packed elements.
82///
83/// The three public-facing extension types — [`BinomialExtensionField`],
84/// [`CubicTrinomialExtensionField`], [`QuinticTrinomialExtensionField`] — are
85/// type aliases over this struct with their respective `Shape`s.
86#[derive(
87 Copy, Clone, Eq, PartialEq, Hash, Debug, serde::Serialize, serde::Deserialize, PartialOrd, Ord,
88)]
89#[repr(transparent)] // Needed to make various casts safe.
90#[must_use]
91pub struct ExtField<F, const D: usize, Shape, A = F> {
92 #[serde(
93 with = "p3_util::array_serialization",
94 bound(
95 serialize = "A: serde::Serialize",
96 deserialize = "A: serde::Deserialize<'de>"
97 )
98 )]
99 pub(crate) value: [A; D],
100
101 _phantom: PhantomData<(F, Shape)>,
102}
103
104impl<F, const D: usize, Shape, A> ExtField<F, D, Shape, A> {
105 /// Create an extension field element from an array of base/algebra elements.
106 ///
107 /// Any array is accepted. No reduction is required since each entry is
108 /// already a valid element of the algebra over `F`.
109 ///
110 /// # Panics
111 /// Panics (at compile time) if `D <= 1`. A degree-0 or degree-1 "extension"
112 /// is degenerate — use `F` directly instead.
113 #[inline]
114 pub const fn new(value: [A; D]) -> Self {
115 const { assert!(D > 1) }
116 Self {
117 value,
118 _phantom: PhantomData,
119 }
120 }
121}
122
123/// Unified packed extension-field representation (SIMD-lane parallel).
124///
125/// `PackedExtField<F, PF, D, Shape>` stores `D` packed coefficients (one per
126/// SIMD lane), representing the `WIDTH` extension-field elements that fit in
127/// one SIMD register simultaneously.
128///
129/// The three public-facing packed types — [`PackedBinomialExtensionField`],
130/// [`PackedCubicTrinomialExtensionField`], [`PackedQuinticTrinomialExtensionField`]
131/// — are type aliases over this struct with their respective `Shape`s.
132#[derive(
133 Copy, Clone, Eq, PartialEq, Hash, Debug, serde::Serialize, serde::Deserialize, PartialOrd, Ord,
134)]
135#[repr(transparent)]
136#[must_use]
137pub struct PackedExtField<F: Field, PF: PackedField<Scalar = F>, const D: usize, Shape> {
138 #[serde(
139 with = "p3_util::array_serialization",
140 bound(
141 serialize = "PF: serde::Serialize",
142 deserialize = "PF: serde::Deserialize<'de>"
143 )
144 )]
145 pub(crate) value: [PF; D],
146 _phantom: PhantomData<Shape>,
147}
148
149impl<F: Field, PF: PackedField<Scalar = F>, const D: usize, Shape> PackedExtField<F, PF, D, Shape> {
150 /// Create a packed extension-field element from an array of packed coefficients.
151 #[inline]
152 pub const fn new(value: [PF; D]) -> Self {
153 Self {
154 value,
155 _phantom: PhantomData,
156 }
157 }
158}
159
160// ---------------------------------------------------------------------------
161// Shape-agnostic impls on the unified struct
162// ---------------------------------------------------------------------------
163//
164// These traits — `Default`, `From<A>`, `From<[A; D]>`, `BasedVectorSpace`,
165// `Packable`, `Distribution` — have the same implementation across every
166// shape. They live here on the unified struct rather than being triplicated
167// across the per-shape files.
168
169impl<F: Field, A: Algebra<F>, const D: usize, Shape: ExtensionShape> Default
170 for ExtField<F, D, Shape, A>
171{
172 #[inline]
173 fn default() -> Self {
174 Self::new(core::array::from_fn(|_| A::ZERO))
175 }
176}
177
178impl<F: Field, A: Algebra<F>, const D: usize, Shape: ExtensionShape> From<A>
179 for ExtField<F, D, Shape, A>
180{
181 #[inline]
182 fn from(x: A) -> Self {
183 Self::new(crate::field_to_array(x))
184 }
185}
186
187impl<F, A, const D: usize, Shape: ExtensionShape> From<[A; D]> for ExtField<F, D, Shape, A> {
188 #[inline]
189 fn from(x: [A; D]) -> Self {
190 Self::new(x)
191 }
192}
193
194impl<F, const D: usize, Shape> crate::Packable for ExtField<F, D, Shape>
195where
196 F: Field + ExtensionAlgebra<F, D, Shape>,
197 Shape: ExtensionShape + Copy + Eq + core::hash::Hash + Send + Sync,
198{
199}
200
201impl<F, A: Algebra<F>, const D: usize, Shape: ExtensionShape + Clone> crate::BasedVectorSpace<A>
202 for ExtField<F, D, Shape, A>
203where
204 F: Field + ExtensionAlgebra<F, D, Shape>,
205{
206 const DIMENSION: usize = D;
207
208 #[inline]
209 fn as_basis_coefficients_slice(&self) -> &[A] {
210 &self.value
211 }
212
213 #[inline]
214 fn from_basis_coefficients_fn<Fn: FnMut(usize) -> A>(f: Fn) -> Self {
215 Self::new(core::array::from_fn(f))
216 }
217
218 #[inline]
219 fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = A>>(mut iter: I) -> Option<Self> {
220 // The unwrap is safe as we just checked the length of iter.
221 (iter.len() == D).then(|| Self::new(core::array::from_fn(|_| iter.next().unwrap())))
222 }
223
224 #[inline]
225 fn flatten_to_base(vec: Vec<Self>) -> Vec<A> {
226 // Safety: `Self` is `#[repr(transparent)]` over `[A; D]`.
227 unsafe { p3_util::flatten_to_base::<A, Self>(vec) }
228 }
229
230 #[inline]
231 fn reconstitute_from_base(vec: Vec<A>) -> Vec<Self> {
232 // Safety: `Self` is `#[repr(transparent)]` over `[A; D]`.
233 unsafe { p3_util::reconstitute_from_base::<A, Self>(vec) }
234 }
235}
236
237impl<F, const D: usize, Shape: ExtensionShape> rand::distr::Distribution<ExtField<F, D, Shape>>
238 for rand::distr::StandardUniform
239where
240 F: Field + ExtensionAlgebra<F, D, Shape>,
241 Self: rand::distr::Distribution<F>,
242{
243 #[inline]
244 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> ExtField<F, D, Shape> {
245 ExtField::new(core::array::from_fn(|_| self.sample(rng)))
246 }
247}
248
249/// Algebra over `F` that supports degree-`D` extension arithmetic with a given reducer `Shape`.
250pub trait ExtensionAlgebra<F: Field, const D: usize, Shape: ExtensionShape>: Algebra<F> {
251 /// Multiplication in the algebra extension ring.
252 fn ext_mul(a: &[Self; D], b: &[Self; D], res: &mut [Self; D]);
253
254 /// Squaring in the algebra extension ring.
255 ///
256 /// Override when a dedicated symmetry-exploiting kernel beats a general multiply.
257 #[inline]
258 fn ext_square(a: &[Self; D], res: &mut [Self; D]) {
259 Self::ext_mul(a, a, res);
260 }
261
262 /// Coefficient-wise addition.
263 #[inline]
264 #[must_use]
265 fn ext_add(a: &[Self; D], b: &[Self; D]) -> [Self; D] {
266 vector_add(a, b)
267 }
268
269 /// Coefficient-wise subtraction.
270 #[inline]
271 #[must_use]
272 fn ext_sub(a: &[Self; D], b: &[Self; D]) -> [Self; D] {
273 vector_sub(a, b)
274 }
275
276 /// Multiply an extension element by a base-field scalar.
277 #[inline]
278 #[must_use]
279 fn ext_base_mul(lhs: [Self; D], rhs: Self) -> [Self; D] {
280 lhs.map(|x| x * rhs.dup())
281 }
282}
283
284/// Trait for fields that support binomial extension of the form `F[X]/(X^D - W)`.
285///
286/// A type implementing this trait can define a degree-`D` extension field using an
287/// irreducible binomial polynomial `X^D - W`, where `W` is a nonzero constant in the base field.
288///
289/// This is used to construct extension fields with efficient arithmetic.
290pub trait BinomiallyExtendable<const D: usize>:
291 Field + ExtensionAlgebra<Self, D, Binomial<Self>>
292{
293 /// The constant coefficient `W` in the binomial `X^D - W`.
294 const W: Self;
295
296 /// A `D`-th root of unity derived from `W`.
297 ///
298 /// This is `W^((n - 1)/D)`, where `n` is the order of the field.
299 /// Valid only when `n = kD + 1` for some `k`.
300 const DTH_ROOT: Self;
301
302 /// A generator for the extension field, expressed as a degree-`D` polynomial.
303 ///
304 /// This is an array of size `D`, where each entry is a base field element.
305 const EXT_GENERATOR: [Self; D];
306}
307
308/// Trait for extension fields that support Frobenius automorphisms.
309///
310/// The Frobenius automorphism is a field map `x ↦ x^n`,
311/// where `n` is the order of the base field.
312///
313/// This map is an automorphism of the field that fixes the base field.
314pub trait HasFrobenius<F: Field>: ExtensionField<F> {
315 /// Apply the Frobenius automorphism once.
316 ///
317 /// Equivalent to raising the element to the `n`th power.
318 #[must_use]
319 fn frobenius(&self) -> Self;
320
321 /// Apply the Frobenius automorphism `count` times.
322 ///
323 /// Equivalent to raising to the `n^count` power.
324 #[must_use]
325 fn repeated_frobenius(&self, count: usize) -> Self;
326
327 /// Computes the pseudo inverse of the given field element.
328 ///
329 /// Returns `0` if `self == 0`, and `1/self` otherwise.
330 /// In other words, returns `self^(n^D - 2)` where `D` is the extension degree.
331 #[must_use]
332 fn pseudo_inv(&self) -> Self;
333
334 /// Returns the full Galois orbit of the element under Frobenius.
335 ///
336 /// This is the sequence `[x, x^n, x^{n^2}, ..., x^{n^{D-1}}]`,
337 /// where `D` is the extension degree.
338 #[must_use]
339 fn galois_orbit(self) -> Vec<Self> {
340 iter::successors(Some(self), |x| Some(x.frobenius()))
341 .take(Self::DIMENSION)
342 .collect()
343 }
344}
345
346/// Trait for binomial extensions that support a two-adic subgroup generator.
347pub trait HasTwoAdicBinomialExtension<const D: usize>: BinomiallyExtendable<D> {
348 /// Two-adicity of the multiplicative group of the extension field.
349 ///
350 /// This is the number of times 2 divides the order of the field minus 1.
351 const EXT_TWO_ADICITY: usize;
352
353 /// Returns a two-adic generator for the extension field.
354 ///
355 /// This is used to generate the 2^bits-th roots of unity in the extension field.
356 ///
357 /// # Panics
358 /// Panics if `bits > EXT_TWO_ADICITY`.
359 #[must_use]
360 fn ext_two_adic_generator(bits: usize) -> [Self; D];
361}
362
363/// Trait for fields that support a degree-3 extension using the trinomial `X^3 - X - 1`.
364///
365/// Implement only when `X^3 - X - 1` is irreducible over the base field.
366pub trait CubicTrinomialExtendable: Field + ExtensionAlgebra<Self, 3, CubicTrinomial> {
367 /// Linear map for the Frobenius automorphism on `Σ a_i X^i` in the power basis `(1, X, X^2)`.
368 ///
369 /// Row `i` contains the coefficients of the image of `X^i` under Frobenius (the first row
370 /// includes the fixed contribution from `a_0`).
371 const FROBENIUS_MATRIX: [[Self; 3]; 3];
372
373 /// A generator of the multiplicative group of `F_{p^3}^*`, as polynomial coefficients.
374 const EXT_GENERATOR: [Self; 3];
375}
376
377/// Trait for cubic trinomial extensions that expose two-adic subgroup generators.
378pub trait HasTwoAdicCubicExtension: CubicTrinomialExtendable {
379 /// Two-adicity of `p^3 - 1`.
380 const EXT_TWO_ADICITY: usize;
381
382 /// Two-adic generator for the extension field; `bits` must be at most `EXT_TWO_ADICITY`.
383 #[must_use]
384 fn ext_two_adic_generator(bits: usize) -> [Self; 3];
385}
386
387/// Trait for fields that support a degree-5 extension using the trinomial `X^5 + X^2 - 1`.
388///
389/// This trait should only be implemented for fields where `X^5 + X^2 - 1` is irreducible.
390/// The implementor must verify irreducibility for their specific field.
391pub trait QuinticTrinomialExtendable: Field + ExtensionAlgebra<Self, 5, QuinticTrinomial> {
392 /// Frobenius coefficients for the quintic extension.
393 ///
394 /// `FROBENIUS_COEFFS[k]` represents `X^{(k+1)*p} mod (X^5 + X^2 - 1)` as a polynomial
395 /// with coefficients `[c_0, c_1, c_2, c_3, c_4]` where `X^{(k+1)*p} = Σ c_i * X^i`.
396 ///
397 /// These precomputed values enable efficient Frobenius automorphism computation.
398 const FROBENIUS_COEFFS: [[Self; 5]; 4];
399
400 /// A generator for the multiplicative group of the extension field `F_{p^5}*`.
401 ///
402 /// Represented as polynomial coefficients `[g_0, g_1, g_2, g_3, g_4]`.
403 const EXT_GENERATOR: [Self; 5];
404}
405
406/// Trait for quintic extensions that support two-adic subgroup generators.
407pub trait HasTwoAdicQuinticExtension: QuinticTrinomialExtendable {
408 /// Two-adicity of the multiplicative group order `p^5 - 1`.
409 const EXT_TWO_ADICITY: usize;
410
411 /// Returns a two-adic generator for the specified bit count.
412 ///
413 /// # Panics
414 /// Panics if `bits > EXT_TWO_ADICITY`.
415 #[must_use]
416 fn ext_two_adic_generator(bits: usize) -> [Self; 5];
417}