Skip to main content

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}