Skip to main content

fp/
fp_ext.rs

1//! Generic extension field  $\mathbb{F}_{p^M} = \mathbb{F}_p\[x\] / (f(x))$.
2//!
3//! # Overview
4//!
5//! Given a prime $p$ and a monic irreducible polynomial $f$ of degree
6//! $M$ over $\\mathbb{F}\_{p}$, the extension field $\mathbb{F}_{{p^M}} =
7//! \mathbb{F}_p\[x\] / (f(x))$ is the set of polynomials of degree
8//! $M$ with coefficients in $\mathbb{F}_p$, where arithmetic is done
9//! modulo $f$.
10//!
11//! This module provides a single generic type [`FpExt<MOD, LIMBS, M,
12//! N, P, TSCONTS>`] that covers *any* such extension.  The
13//! irreducible polynomial is supplied via the zero-size marker trait
14//! [`IrreduciblePoly`].
15//!
16//! # Example
17//!
18//! ```
19//! use crypto_bigint::{const_prime_monty_params, Uint};
20//! use fp::field_ops::FieldOps;
21//! use fp::fp_element::FpElement;
22//! use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants};
23//!
24//! /* Setup the underlying prime field */
25//! const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2);
26//! type Fp19 = FpElement<Fp19Mod, 1>;
27//!
28//! fn fp(n: u64) -> Fp19 {
29//!    Fp19::from_u64(n)
30//! }
31//!
32//! /* Setput the irreducible polynomial */
33//! struct QuadPoly;
34//! impl IrreduciblePoly<Fp19Mod, 1, 2> for QuadPoly {
35//!     fn modulus() -> [Fp19; 2] {
36//!         [Fp19::one(), Fp19::zero()]
37//!     }
38//! }
39//!
40//! /* Setup the Tonelli--Shanks constants */
41//! struct TSQuad;
42//! impl TonelliShanksConstants<Fp19Mod, 1, 2, 1> for TSQuad {
43//!     // p^2 - 1
44//!     const ORDER: Uint<1> = Uint::<1>::from_u64(360);
45//!     // (p^2 - 1) / 2
46//!     const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180);
47//!     // p^2 - 1 = 2^S * T with T odd
48//!     const S: u64 = 3;
49//!     const T: Uint<1> = Uint::<1>::from_u64(45);
50//!     // (T - 1) / 2
51//!     const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22);
52//!     // 2^(S - 1)
53//!     const TWOSM1: Uint<1> = Uint::<1>::from_u64(4);
54//!     // 2^S root of unity
55//!     fn root_of_unity() -> [FpElement<Fp19Mod, 1>; 2] {
56//!         [Fp19::from_u64(3), Fp19::from_u64(3)]
57//!     }
58//! }
59//!
60//! /* Define the extension field */
61//! type F19_2 = FpExt<Fp19Mod, 1, 2, 1, QuadPoly, TSQuad>;
62//!
63//! /* Some standard tests */
64//! let a = F19_2::new([fp(3), fp(2)]);
65//! let ainv = F19_2::new([fp(9), fp(13)]);
66//! let asquare = F19_2::new([fp(5), fp(12)]);
67//! assert_eq!(a.invert().unwrap(), ainv);
68//! assert_eq!(a.square(), asquare);
69//! assert_eq!(asquare.sqrt().unwrap().square(), asquare);
70//! ```
71//!
72//! # Representation
73//!
74//! An element $a \in \mathbb{F}\_{p^M}$ is stored as exactly $M$
75//! base-field coefficients so that `coeffs = [a_0, a_1, ...,
76//! a_{M-1}]` cooresponds to
77//! $$
78//! a_0 + a_1 x + ... + a_{M-1}x^{M-1} \pmod{ f(x) }
79//! $$
80//!
81//! # Operations and costs
82//!
83//! | Operation   | Algorithm                        | Base-field cost          |
84//! |-------------|----------------------------------|--------------------------|
85//! | Add / Sub   | Coefficient-wise                 | $M$ adds                 |
86//! | Negate      | Coefficient-wise                 | $M$ negs                 |
87//! | Double      | Coefficient-wise                 | $M$ doubles              |
88//! | Multiply    | Schoolbook + reduction mod f     | $M^2$ muls + $M^2$ adds  |
89//! | Square      | Same as multiply (self * self)   | $M^2$ muls + $M^2$ adds  |
90//! | Invert      | Polynomial extended GCD          | $O(M^2)$                 |
91//! | Frobenius   | self^p  via square-and-multiply  | $O(M^2 \log p)$          |
92//! | Norm        | Product of M Galois conjugates   | $O(M^3 \log p)$          |
93//! | Trace       | Sum of M Galois conjugates       | $O(M^2 \log p)$          |
94
95use core::ops::{Add, Mul, Neg, Sub};
96use std::marker::PhantomData;
97
98use crate::field_ops::{FieldFromRepr, FieldOps, FieldRandom};
99use crate::fp_element::FpElement;
100use crypto_bigint::{modular::ConstPrimeMontyParams, Uint};
101use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
102
103// ===========================================================================
104// IrreduciblePoly — one thing callers need to implement for a new field
105// ===========================================================================
106
107/// Supplies the monic irreducible polynomial
108/// $$f(x) = x^M + c_{M-1} x^{M-1} + ... + c_1 x + c_0$$
109/// that defines the extension  $\mathbb{F}_p^M = \mathbb{F}_p\[x\] / (f(x))$.
110///
111/// # Convention
112///
113/// `modulus()` returns the **non-leading** coefficients in ascending degree order:
114/// `[c_0, c_1, ..., c_{M-1}]`.
115/// The leading coefficient 1 (coefficient of $x^M$) is implicit.
116///
117/// # Example: $\mathbb{F}_{19^2}$
118///
119/// The polynomial $f(x) = x^2 + 1$ is irreducible over $\mathbb{F}_{19}$.
120/// ```
121/// # use crypto_bigint::{const_prime_monty_params, Uint};
122/// # use fp::fp_element::FpElement;
123/// # use fp::field_ops::FieldOps;
124/// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants};
125/// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2);
126/// type Fp19 = FpElement<Fp19Mod, 1>;
127/// struct MyQuadPoly;
128/// impl IrreduciblePoly<Fp19Mod, 1, 2> for MyQuadPoly {
129///     fn modulus() -> [FpElement<Fp19Mod, 1>; 2] {
130///         // f(x) = x^2 + 0x + 1 -> [1, 0]
131///         [FpElement::one(), FpElement::zero()]
132///     }
133/// }
134/// ```
135///
136/// # Example: $\mathbb{F}_{19^3}$
137///
138/// Note that $f(x) = x^3 + 17$ is irreducible over $\mathbb{F}_{19}$.
139/// ```
140/// # use crypto_bigint::{const_prime_monty_params, Uint};
141/// # use fp::fp_element::FpElement;
142/// # use fp::field_ops::FieldOps;
143/// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants};
144/// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2);
145/// type Fp19 = FpElement<Fp19Mod, 1>;
146/// struct MyCubicPoly;
147/// impl IrreduciblePoly<Fp19Mod, 1, 3> for MyCubicPoly {
148///     fn modulus() -> [FpElement<Fp19Mod, 1>; 3] {
149///         // f(x) = x^3 + 0x^2 + 0x + 17  ->  [17, 0, 0]
150///         [FpElement::from_u64(17), FpElement::zero(), FpElement::zero()]
151///     }
152/// }
153/// ```
154pub trait IrreduciblePoly<MOD, const LIMBS: usize, const M: usize>: 'static
155where
156    MOD: ConstPrimeMontyParams<LIMBS>,
157{
158    /// Non-leading coefficients `[c_0, c_1, ..., c_{M-1}]` of the defining polynomial.
159    fn modulus() -> [FpElement<MOD, LIMBS>; M];
160}
161
162/*
163===========================================================================
164TonelliShanksConstants - The only other thing users have to
165implement for a new field
166===========================================================================
167*/
168
169/// Supplies the constants for Tonelli--Shanks algorithm.
170///
171/// The `usize` constant `N` in the trait definition should be chosen
172/// so that $p^M$ fits inside a `Uint<N>`. Required constants are
173/// listed below all are derived by writing $p^M - 1 = 2^S T$ for some
174/// odd $T$.
175///
176/// # Example: $\mathbb{F}_{19^2}$
177/// Note that we have a factorisation $19^2 - 1 = 2^3 \cdot 45$
178/// ```
179/// # use crypto_bigint::{const_prime_monty_params, Uint};
180/// # use fp::fp_element::FpElement;
181/// # use fp::fp_ext::{FpExt, IrreduciblePoly, TonelliShanksConstants};
182/// const_prime_monty_params!(Fp19Mod, Uint<1>, "0000000000000013", 2);
183/// type Fp19 = FpElement<Fp19Mod, 1>;
184/// struct TSQuad;
185/// impl TonelliShanksConstants<Fp19Mod, 1, 2, 1> for TSQuad {
186///     // p^2 - 1
187///     const ORDER: Uint<1> = Uint::<1>::from_u64(360);
188///     // (p^2 - 1) / 2
189///     const HALF_ORDER: Uint<1> = Uint::<1>::from_u64(180);
190///     // p^2 - 1 = 2^S * T with T odd
191///     const S: u64 = 3;
192///     const T: Uint<1> = Uint::<1>::from_u64(45);
193///     // (T - 1) / 2
194///     const PROJENATOR_EXP: Uint<1> = Uint::<1>::from_u64(22);
195///     // 2^(S - 1)
196///     const TWOSM1: Uint<1> = Uint::<1>::from_u64(4);
197///     // 2^S root of unity
198///     fn root_of_unity() -> [FpElement<Fp19Mod, 1>; 2] {
199///         [Fp19::from_u64(3), Fp19::from_u64(3)]
200///     }
201/// }
202/// ```
203///
204/// # Todo
205///
206/// - [ ] Compute these constants at compile time to bury this from
207///   the end user.
208pub trait TonelliShanksConstants<MOD, const LIMBS: usize, const M: usize, const N: usize>:
209    'static
210where
211    MOD: ConstPrimeMontyParams<LIMBS>,
212{
213    /// Multiplicative group order $p^M - 1$
214    const ORDER: Uint<N>;
215
216    /// Half mult group order $(p^M - 1) / 2$
217    const HALF_ORDER: Uint<N>;
218
219    /// Write $p^M - 1 = 2^S T$ with $T$ odd
220    const S: u64;
221
222    /// Write $p^M - 1 = 2^S T$ with $T$ odd
223    const T: Uint<N>;
224
225    /// Constant $2^{S - 1}$
226    const TWOSM1: Uint<N>;
227
228    /// Projenator exponent of the TS algorithm this is $(T - 1) / 2$
229    const PROJENATOR_EXP: Uint<N>;
230
231    /// $2^S$ root of unity
232    fn root_of_unity() -> [FpElement<MOD, LIMBS>; M];
233}
234
235// ===========================================================================
236// FpExt — element of Fp^M
237// ===========================================================================
238
239/// An element of the extension field $\mathbb{F}_{p^M} =
240/// \mathbb{F}_p\[x\] / (f(x))$.
241///
242/// `P` is a zero-size marker type implementing [`IrreduciblePoly`].
243/// `M` is the extension degree (number of base-field coefficients stored).
244/// `N` is the number limbs needed to store p^M
245pub struct FpExt<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS>
246where
247    MOD: ConstPrimeMontyParams<LIMBS>,
248    P: IrreduciblePoly<MOD, LIMBS, M>,
249    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
250{
251    /// Coefficients in ascending degree, that is `coeffs[i]` is
252    /// the coefficient of $x^i$ (zero indexed).
253    pub coeffs: [FpElement<MOD, LIMBS>; M],
254    _phantom: PhantomData<P>,
255    _order: PhantomData<TSCONSTS>,
256}
257
258// ---------------------------------------------------------------------------
259// Constructors
260// ---------------------------------------------------------------------------
261
262impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS>
263    FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
264where
265    MOD: ConstPrimeMontyParams<LIMBS>,
266    P: IrreduciblePoly<MOD, LIMBS, M>,
267    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
268{
269    /// Construct from a coefficient array `[a_0, ..., a_{M-1}]`.
270    pub fn new(coeffs: [FpElement<MOD, LIMBS>; M]) -> Self {
271        Self {
272            coeffs,
273            _phantom: PhantomData,
274            _order: PhantomData,
275        }
276    }
277
278    /// Embed a base-field element as $a + 0x + ... + 0x^{M-1}$.
279    pub fn from_base(a: FpElement<MOD, LIMBS>) -> Self {
280        let mut coeffs = std::array::from_fn(|_| FpElement::zero());
281        coeffs[0] = a;
282        Self::new(coeffs)
283    }
284
285    /// Return the coefficient of $x^i$.
286    pub fn coeff(&self, i: usize) -> &FpElement<MOD, LIMBS> {
287        &self.coeffs[i]
288    }
289}
290
291// ---------------------------------------------------------------------------
292// Clone / Copy / PartialEq / Eq / Debug
293// (manual impls so we don't over-constrain the bounds the way #[derive] would)
294// ---------------------------------------------------------------------------
295
296impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> Clone
297    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
298where
299    MOD: ConstPrimeMontyParams<LIMBS>,
300    P: IrreduciblePoly<MOD, LIMBS, M>,
301    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
302{
303    fn clone(&self) -> Self {
304        Self {
305            coeffs: self.coeffs.clone(),
306            _phantom: PhantomData,
307            _order: PhantomData,
308        }
309    }
310}
311
312// FpElement is Copy (derives it), so [FpElement; M] is Copy too.
313impl<MOD, const LIMBS: usize, const M: usize, P, const N: usize, TSCONSTS> Copy
314    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
315where
316    MOD: ConstPrimeMontyParams<LIMBS>,
317    P: IrreduciblePoly<MOD, LIMBS, M>,
318    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
319    [FpElement<MOD, LIMBS>; M]: Copy,
320{
321}
322
323impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> PartialEq
324    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
325where
326    MOD: ConstPrimeMontyParams<LIMBS>,
327    P: IrreduciblePoly<MOD, LIMBS, M>,
328    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
329{
330    fn eq(&self, other: &Self) -> bool {
331        self.coeffs
332            .iter()
333            .zip(other.coeffs.iter())
334            .all(|(a, b)| a == b)
335    }
336}
337
338impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> Eq
339    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
340where
341    MOD: ConstPrimeMontyParams<LIMBS>,
342    P: IrreduciblePoly<MOD, LIMBS, M>,
343    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
344{
345}
346
347impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> std::fmt::Debug
348    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
349where
350    MOD: ConstPrimeMontyParams<LIMBS>,
351    P: IrreduciblePoly<MOD, LIMBS, M>,
352    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
353    FpElement<MOD, LIMBS>: std::fmt::Debug,
354{
355    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356        write!(f, "FpExt{:?}", self.coeffs.as_ref())
357    }
358}
359
360// ---------------------------------------------------------------------------
361// Pretty Display — polynomial form over Fp
362// ---------------------------------------------------------------------------
363
364/// Shows the element as  $a_0 + a_1 x + a_2 x^2 + ...$  with zero
365/// coefficients suppressed.  The zero element prints as `0`.
366impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> std::fmt::Display
367    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
368where
369    MOD: ConstPrimeMontyParams<LIMBS>,
370    P: IrreduciblePoly<MOD, LIMBS, M>,
371    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
372    FpElement<MOD, LIMBS>: std::fmt::Display,
373{
374    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
375        let mut terms = Vec::new();
376
377        for (i, coeff) in self.coeffs.iter().enumerate() {
378            if bool::from(coeff.is_zero()) {
379                continue;
380            }
381
382            let is_one = bool::from(coeff.is_one());
383
384            match i {
385                0 => terms.push(format!("{coeff}")),
386                1 if is_one => terms.push("x".to_string()),
387                1 => terms.push(format!("{coeff}*x")),
388                _ if is_one => terms.push(format!("x^{i}")),
389                _ => terms.push(format!("{coeff}*x^{i}")),
390            }
391        }
392
393        if terms.is_empty() {
394            write!(f, "0")
395        } else {
396            write!(f, "{}", terms.join(" + "))
397        }
398    }
399}
400
401// ---------------------------------------------------------------------------
402// CtOption functionalities
403// ---------------------------------------------------------------------------
404
405impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> Default
406    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
407where
408    MOD: ConstPrimeMontyParams<LIMBS>,
409    P: IrreduciblePoly<MOD, LIMBS, M>,
410    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
411{
412    fn default() -> Self {
413        Self::zero()
414    }
415}
416
417impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> ConditionallySelectable
418    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
419where
420    MOD: ConstPrimeMontyParams<LIMBS>,
421    P: IrreduciblePoly<MOD, LIMBS, M>,
422    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
423{
424    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
425        let mut res_coeffs = [FpElement::zero(); M];
426        for i in 0..M {
427            res_coeffs[i] = FpElement::conditional_select(&a.coeffs[i], &b.coeffs[i], choice);
428        }
429        Self::new(res_coeffs)
430    }
431
432    fn conditional_assign(&mut self, other: &Self, choice: Choice) {
433        for i in 0..M {
434            self.coeffs[i].conditional_assign(&other.coeffs[i], choice);
435        }
436    }
437
438    fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) {
439        for i in 0..M {
440            FpElement::conditional_swap(&mut a.coeffs[i], &mut b.coeffs[i], choice);
441        }
442    }
443}
444
445impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> ConstantTimeEq
446    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
447where
448    MOD: ConstPrimeMontyParams<LIMBS>,
449    P: IrreduciblePoly<MOD, LIMBS, M>,
450    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
451{
452    fn ct_eq(&self, other: &Self) -> Choice {
453        let mut acc = Choice::from(1u8);
454        for i in 0..M {
455            acc &= self.coeffs[i].ct_eq(&other.coeffs[i]);
456        }
457        acc
458    }
459
460    fn ct_ne(&self, other: &Self) -> Choice {
461        !self.ct_eq(other)
462    }
463}
464
465// ===========================================================================
466// Private polynomial helpers
467//
468// All intermediate polynomial arithmetic uses Vec<FpElement> so that we
469// never need arithmetic on const generic bounds (e.g. [T; M+M-1]), which is
470// not yet stable in Rust.  The conversion back to [T; M] happens only at the
471// boundary (poly_reduce).
472// ===========================================================================
473
474type Poly<MOD, const LIMBS: usize> = Vec<FpElement<MOD, LIMBS>>;
475
476/// Coefficient-wise addition.  Output length = max(|a|, |b|).
477#[allow(dead_code)]
478fn poly_add<MOD, const LIMBS: usize>(
479    a: &[FpElement<MOD, LIMBS>],
480    b: &[FpElement<MOD, LIMBS>],
481) -> Poly<MOD, LIMBS>
482where
483    MOD: ConstPrimeMontyParams<LIMBS>,
484{
485    let len = a.len().max(b.len());
486    (0..len)
487        .map(|i| {
488            let ai = a.get(i).cloned().unwrap_or_else(|| FpElement::zero());
489            let bi = b.get(i).cloned().unwrap_or_else(|| FpElement::zero());
490            FieldOps::add(&ai, &bi)
491        })
492        .collect()
493}
494
495/// Coefficient-wise subtraction.
496fn poly_sub<MOD, const LIMBS: usize>(
497    a: &[FpElement<MOD, LIMBS>],
498    b: &[FpElement<MOD, LIMBS>],
499) -> Poly<MOD, LIMBS>
500where
501    MOD: ConstPrimeMontyParams<LIMBS>,
502{
503    let len = a.len().max(b.len());
504    (0..len)
505        .map(|i| {
506            let ai = a.get(i).cloned().unwrap_or_else(|| FpElement::zero());
507            let bi = b.get(i).cloned().unwrap_or_else(|| FpElement::zero());
508            FieldOps::sub(&ai, &bi)
509        })
510        .collect()
511}
512
513/// Multiply every coefficient by a scalar.
514fn poly_scale<MOD, const LIMBS: usize>(
515    a: &[FpElement<MOD, LIMBS>],
516    s: &FpElement<MOD, LIMBS>,
517) -> Poly<MOD, LIMBS>
518where
519    MOD: ConstPrimeMontyParams<LIMBS>,
520{
521    a.iter().map(|ai| FieldOps::mul(ai, s)).collect()
522}
523
524/// Schoolbook polynomial multiplication.  Output degree = deg(a) + deg(b).
525fn poly_mul<MOD, const LIMBS: usize>(
526    a: &[FpElement<MOD, LIMBS>],
527    b: &[FpElement<MOD, LIMBS>],
528) -> Poly<MOD, LIMBS>
529where
530    MOD: ConstPrimeMontyParams<LIMBS>,
531{
532    if a.is_empty() || b.is_empty() {
533        return vec![];
534    }
535    let mut out = vec![FpElement::zero(); a.len() + b.len() - 1];
536    for (i, ai) in a.iter().enumerate() {
537        for (j, bj) in b.iter().enumerate() {
538            let t = FieldOps::mul(ai, bj);
539            out[i + j] = FieldOps::add(&out[i + j], &t);
540        }
541    }
542    out
543}
544
545/// Remove trailing zero coefficients (i.e. normalise to the canonical form
546/// where the leading coefficient is non-zero, or the empty Vec for the zero poly).
547fn poly_normalize<MOD, const LIMBS: usize>(mut a: Poly<MOD, LIMBS>) -> Poly<MOD, LIMBS>
548where
549    MOD: ConstPrimeMontyParams<LIMBS>,
550{
551    while a.last().map_or(false, |c| bool::from(c.is_zero())) {
552        a.pop();
553    }
554    a
555}
556
557/// Polynomial long division.  Returns `(quotient, remainder)` such that
558/// `a = quotient * b + remainder`  with  `deg(remainder) < deg(b)`.
559///
560/// Requires b ≠ 0 and its leading coefficient invertible (always true over Fp).
561fn poly_divmod<MOD, const LIMBS: usize>(
562    a: &[FpElement<MOD, LIMBS>],
563    b: &[FpElement<MOD, LIMBS>],
564) -> (Poly<MOD, LIMBS>, Poly<MOD, LIMBS>)
565where
566    MOD: ConstPrimeMontyParams<LIMBS>,
567{
568    let b = poly_normalize(b.to_vec());
569    assert!(!b.is_empty(), "poly_divmod: divisor is zero");
570
571    let mut rem = poly_normalize(a.to_vec());
572
573    if rem.len() < b.len() {
574        return (vec![], rem);
575    }
576
577    let b_lead_inv = b
578        .last()
579        .unwrap()
580        .invert()
581        .expect("poly_divmod: leading coefficient of b is not invertible");
582
583    let out_len = rem.len() - b.len() + 1;
584    let mut quot = vec![FpElement::zero(); out_len];
585
586    // Process from the highest-degree term of the quotient down to degree 0.
587    for i in (0..out_len).rev() {
588        // The current highest remaining degree is i + deg(b).
589        let rem_deg = i + b.len() - 1;
590        let coeff = FieldOps::mul(&rem[rem_deg], &b_lead_inv);
591        if bool::from(coeff.is_zero()) {
592            continue;
593        }
594        quot[i] = coeff.clone();
595        for (j, bj) in b.iter().enumerate() {
596            let t = FieldOps::mul(&coeff, bj);
597            rem[i + j] = FieldOps::sub(&rem[i + j], &t);
598        }
599    }
600
601    (quot, poly_normalize(rem))
602}
603
604/// Reduce polynomial $a$ modulo the irreducible $f(x) = x^M + \sum_j c_j x^j$.
605///
606/// Uses the substitution rule:
607/// $$x^M \equiv − (c_0 + c_1 x + ... + c_{M-1} x^{M-1})$$
608///
609/// Sweeps from the highest degree of `a` down to `M`, eliminating one
610/// term per step.  Each step costs `M` multiplications and `M`
611/// additions in $\mathbb{F}_p$. Note that $c_i$ is `modulus[i]` below.
612fn poly_reduce<MOD, const LIMBS: usize, const M: usize>(
613    a: Vec<FpElement<MOD, LIMBS>>,
614    modulus: &[FpElement<MOD, LIMBS>; M],
615) -> [FpElement<MOD, LIMBS>; M]
616where
617    MOD: ConstPrimeMontyParams<LIMBS>,
618{
619    let mut a = a;
620    // Ensure we have at least M slots.
621    while a.len() < M {
622        a.push(FpElement::zero());
623    }
624
625    // Eliminate all terms of degree >= M, from the top down.
626    for i in (M..a.len()).rev() {
627        let lead = a[i].clone();
628        if bool::from(lead.is_zero()) {
629            continue;
630        }
631        // x^i = x^{i−M}  x^M  \equiv  −x^{i−M}  Σ_j modulus[j]x^j
632        // → subtract leadmodulus[j] from position (i−M+j) for each j.
633        for j in 0..M {
634            let t = FieldOps::mul(&lead, &modulus[j]);
635            a[i - M + j] = FieldOps::sub(&a[i - M + j], &t);
636        }
637        a[i] = FpElement::zero();
638    }
639
640    // Copy the first M coefficients into a fixed-size array.
641    std::array::from_fn(|i| {
642        if i < a.len() {
643            a[i].clone()
644        } else {
645            FpElement::zero()
646        }
647    })
648}
649
650/// Polynomial extended GCD over Fp.
651///
652/// Returns `(g, s)` satisfying  `as \equiv g  (mod b)`,  where `g = gcd(a, b)`.
653///
654/// When `b` is irreducible and `a` is not a multiple of `b`, `g` is a nonzero
655/// constant (units are the GCD of coprime polynomials over a field), so
656/// `a⁻¹ \equiv s  g⁻¹  (mod b)`.
657///
658/// Uses the standard iterative Euclidean algorithm:
659/// ```text
660/// (r₀, s₀) = (a, 1)
661/// (r₁, s₁) = (b, 0)
662/// while r₁ ≠ 0:
663///     q         = r₀ / r₁
664///     (r₀, r₁) = (r₁, r₀ − qr₁)
665///     (s₀, s₁) = (s₁, s₀ − qs₁)
666/// return (r₀, s₀)
667/// ```
668fn poly_ext_gcd<MOD, const LIMBS: usize>(
669    a: Poly<MOD, LIMBS>,
670    b: Poly<MOD, LIMBS>,
671) -> (Poly<MOD, LIMBS>, Poly<MOD, LIMBS>)
672// (gcd, s)
673where
674    MOD: ConstPrimeMontyParams<LIMBS>,
675{
676    let mut r0 = poly_normalize(a);
677    let mut r1 = poly_normalize(b);
678    let mut s0: Poly<MOD, LIMBS> = vec![FpElement::one()]; // 1
679    let mut s1: Poly<MOD, LIMBS> = vec![]; // 0
680
681    while !r1.is_empty() {
682        let (q, r) = poly_divmod(&r0, &r1);
683        // s_new = s0 − qs1
684        let q_s1 = poly_mul(&q, &s1);
685        let s_new = poly_normalize(poly_sub(&s0, &q_s1));
686        s0 = s1;
687        s1 = s_new;
688        r0 = r1;
689        r1 = poly_normalize(r);
690    }
691
692    (r0, s0)
693}
694
695// ===========================================================================
696// Operator overloads (delegate to the FieldOps methods below)
697// ===========================================================================
698
699impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> Add
700    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
701where
702    MOD: ConstPrimeMontyParams<LIMBS>,
703    P: IrreduciblePoly<MOD, LIMBS, M>,
704    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
705{
706    type Output = Self;
707    fn add(self, rhs: Self) -> Self {
708        FieldOps::add(&self, &rhs)
709    }
710}
711
712impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> Sub
713    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
714where
715    MOD: ConstPrimeMontyParams<LIMBS>,
716    P: IrreduciblePoly<MOD, LIMBS, M>,
717    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
718{
719    type Output = Self;
720    fn sub(self, rhs: Self) -> Self {
721        FieldOps::sub(&self, &rhs)
722    }
723}
724
725impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> Mul
726    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
727where
728    MOD: ConstPrimeMontyParams<LIMBS>,
729    P: IrreduciblePoly<MOD, LIMBS, M>,
730    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
731{
732    type Output = Self;
733    fn mul(self, rhs: Self) -> Self {
734        FieldOps::mul(&self, &rhs)
735    }
736}
737
738impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> Neg
739    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
740where
741    MOD: ConstPrimeMontyParams<LIMBS>,
742    P: IrreduciblePoly<MOD, LIMBS, M>,
743    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
744{
745    type Output = Self;
746    fn neg(self) -> Self {
747        FieldOps::negate(&self)
748    }
749}
750
751// ===========================================================================
752// Helper functions for Tonelli-Shanks algorithm
753// ===========================================================================
754
755/// The loop part of the Tonelli--Shanks algorithm
756///
757/// Takes as input a (reference to an) element `x` of FpM and the
758/// projenator `w = x^((t-1)/2)` for `x`. The function then modifies
759/// `x` and return the final value sqrt(x) if `x` is a quadratic
760/// residue in FpM
761///
762/// # Arguments
763///
764/// * `x` - The value to take the sqrt of (type: &mut FpExt)
765/// * `w` - The projenator for x (type: &FpExt)
766///
767/// # Note
768///
769/// Most of this is directly taken from the Fp case at
770/// <https://github.com/SamFrengley/ff-sqrtratio/blob/8e5aa6f934f32d9b9cff56177d9943a2effcd390/ff_derive/src/lib.rs>
771/// under an MIT liscence.
772fn ts_loop<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS>(
773    x: &mut FpExt<MOD, LIMBS, M, N, P, TSCONSTS>,
774    w: &FpExt<MOD, LIMBS, M, N, P, TSCONSTS>,
775) where
776    MOD: ConstPrimeMontyParams<LIMBS>,
777    P: IrreduciblePoly<MOD, LIMBS, M>,
778    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
779{
780    let mut v = TSCONSTS::S as u32;
781    *x = x.mul(*w);
782    let mut b = x.mul(*w);
783    let mut z = FpExt::new(TSCONSTS::root_of_unity());
784
785    for max_v in (1..=TSCONSTS::S as u32).rev() {
786        let mut k = 1u32;
787        let mut tmp = b.square();
788        let mut j_less_than_v: Choice = 1.into();
789
790        for j in 2..max_v {
791            let tmp_is_one = tmp.ct_eq(&FpExt::one());
792            let squared = FpExt::conditional_select(&tmp, &z, tmp_is_one).square();
793            tmp = FpExt::conditional_select(&squared, &tmp, tmp_is_one);
794            let new_z = FpExt::conditional_select(&z, &squared, tmp_is_one);
795
796            j_less_than_v &= !j.ct_eq(&v);
797
798            k = u32::conditional_select(&j, &k, tmp_is_one);
799            z = FpExt::conditional_select(&z, &new_z, j_less_than_v);
800        }
801
802        let result = x.mul(z);
803        *x = FpExt::conditional_select(&result, x, b.ct_eq(&FpExt::one()));
804        z = z.square();
805        b = b.mul(z);
806        v = k;
807    }
808}
809
810// ===========================================================================
811// FieldOps implementation
812// ===========================================================================
813
814impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> FieldOps
815    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
816where
817    MOD: ConstPrimeMontyParams<LIMBS>,
818    P: IrreduciblePoly<MOD, LIMBS, M>,
819    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
820{
821    // --- Identity elements --------------------------------------------------
822
823    fn zero() -> Self {
824        Self::new(std::array::from_fn(|_| FpElement::zero()))
825    }
826
827    fn one() -> Self {
828        let mut c: [FpElement<MOD, LIMBS>; M] = std::array::from_fn(|_| FpElement::zero());
829        c[0] = FpElement::one();
830        Self::new(c)
831    }
832
833    fn from_u64(x: u64) -> Self {
834        Self::from_base(FpElement::<MOD, LIMBS>::from_u64(x))
835    }
836
837    // --- Predicates ---------------------------------------------------------
838
839    fn is_zero(&self) -> Choice {
840        self.ct_eq(&Self::zero())
841    }
842
843    fn is_one(&self) -> Choice {
844        Self::ct_eq(self, &Self::one())
845    }
846
847    // --- Core arithmetic ----------------------------------------------------
848
849    fn negate(&self) -> Self {
850        Self::new(std::array::from_fn(|i| self.coeffs[i].negate()))
851    }
852
853    fn add(&self, rhs: &Self) -> Self {
854        Self::new(std::array::from_fn(|i| {
855            FieldOps::add(&self.coeffs[i], &rhs.coeffs[i])
856        }))
857    }
858
859    fn sub(&self, rhs: &Self) -> Self {
860        Self::new(std::array::from_fn(|i| {
861            FieldOps::sub(&self.coeffs[i], &rhs.coeffs[i])
862        }))
863    }
864
865    /// Schoolbook multiplication followed by reduction modulo $f(x)$.
866    ///
867    /// Product has degree $\leq 2M−2$, then each high-degree term is
868    /// replaced using $x^M \equiv −\sum_j \texttt{modulus}\[j\] x^j$
869    /// until all degrees are below $M$.
870    fn mul(&self, rhs: &Self) -> Self {
871        let product = poly_mul(&self.coeffs, &rhs.coeffs);
872        Self::new(poly_reduce(product, &P::modulus()))
873    }
874
875    fn square(&self) -> Self {
876        // Calls our own FieldOps::mul, not Mul::mul (operator), to avoid the
877        // supertrait ambiguity documented in field_ops.rs.
878        <Self as FieldOps>::mul(self, self)
879    }
880
881    fn double(&self) -> Self {
882        Self::new(std::array::from_fn(|i| self.coeffs[i].double()))
883    }
884
885    // --- Inversion ----------------------------------------------------------
886
887    /// Inversion via polynomial extended GCD.
888    ///
889    /// Finds $s$ such that $\texttt{self} \times s \equiv 1 \pmod{f}$
890    /// by computing $\gcd(\texttt{self}, f) = g$ (a nonzero constant
891    /// if `self` is nonzero) and then returning $s g^{-1} \pmod{f}$.
892    fn invert(&self) -> CtOption<Self> {
893        let is_invertible = !self.is_zero();
894
895        // Build the full irreducible polynomial as a Vec:
896        // f = [c_0, c_1, ..., c_{M-1}, 1]  (coefficients in ascending degree)
897        let mut f: Poly<MOD, LIMBS> = P::modulus().iter().cloned().collect();
898        f.push(FpElement::one()); // leading x^M term
899
900        let a: Poly<MOD, LIMBS> = self.coeffs.iter().cloned().collect();
901        let (gcd, s) = poly_ext_gcd(a, f);
902
903        // gcd is a nonzero constant in Fp (since f is irreducible and a ≠ 0).
904        let g0 = gcd.into_iter().next().unwrap_or(FpElement::zero()); // the constant term = gcd value
905        let g_inv = g0.invert().unwrap_or(FpElement::zero()); // invert in Fp
906
907        // self⁻¹ = s(x)  g⁻¹  reduced mod f
908        let s_scaled = poly_scale(&s, &g_inv);
909        CtOption::new(
910            Self::new(poly_reduce(s_scaled, &P::modulus())),
911            is_invertible,
912        )
913    }
914
915    // --- Frobenius ----------------------------------------------------------
916
917    /// `φ_p(a) = a^p` — the p-power Frobenius endomorphism.
918    ///
919    /// Computed via square-and-multiply using the characteristic p retrieved
920    /// from the base field.
921    fn frobenius(&self) -> Self {
922        let p = FpElement::<MOD, LIMBS>::characteristic();
923        <Self as FieldOps>::pow(self, &p)
924    }
925
926    // --- Norm and trace -----------------------------------------------------
927
928    /// `N_{Fp^M/Fp}(a) = ∏_{i=0}^{M-1} φ_p^i(a)` — product of all Galois conjugates.
929    ///
930    /// The result lies in Fp (all higher coefficients are 0), but is returned
931    /// embedded in Fp^M for uniformity with the [`FieldOps`] signature.
932    fn norm(&self) -> Self {
933        let mut result = self.clone();
934        let mut conj = self.frobenius();
935        for _ in 1..M {
936            result = <Self as FieldOps>::mul(&result, &conj);
937            conj = conj.frobenius();
938        }
939        result
940    }
941
942    /// `Tr_{Fp^M/Fp}(a) = Σ_{i=0}^{M-1} φ_p^i(a)` — sum of all Galois conjugates.
943    ///
944    /// Like `norm`, the result lies in Fp but is returned embedded in Fp^M.
945    fn trace(&self) -> Self {
946        let mut result = self.clone();
947        let mut conj = self.frobenius();
948        for _ in 1..M {
949            result = <Self as FieldOps>::add(&result, &conj);
950            conj = conj.frobenius();
951        }
952        result
953    }
954
955    // --- Square root --------------------------------------------------------
956
957    /// Tonelli--Shanks squareroot algorithm
958    ///
959    /// Implementation of the Tonelli--Shanks square root algorithm. Requires
960    /// only a factorisation as $p^M - 1 = 2^K N$ so can compute this at
961    /// compile time by truncating zeros.
962    ///
963    /// # Arguments
964    ///
965    /// * `&self` - An element of Fp^M (type: &self)
966    ///
967    /// # Returns
968    ///
969    /// `self^(1/2)` a choice of squareroot (type: Self)
970    fn sqrt(&self) -> CtOption<Self> {
971        let mut x = *self;
972        let exp = TSCONSTS::PROJENATOR_EXP;
973        let exp_limbs = exp.as_limbs().map(|limb| limb.0);
974        // TODO: generate the addition chain for this specific constant
975        // this is constant time since exp_limbs is always(!) the same
976        let w = x.pow_vartime(&exp_limbs);
977        ts_loop(&mut x, &w);
978        CtOption::new(
979            x,
980            x.mul(x).ct_eq(self), // Only return Some if it's the square root.
981        )
982    }
983
984    /// Inverse and sqrt in one exponentiation
985    ///
986    /// Computes the inverse and squareroot of `self` in one
987    /// exponentiation using the tricks in Scott's article
988    ///
989    /// # Arguments
990    ///
991    /// * `&self` - An element of Fp^M (type: &self)
992    ///
993    /// # Returns
994    ///
995    /// `(myinv, mysqrt)` which is `self.invert()` and `self.sqrt()`
996    /// (type: `CtOption<Self>`, `CtOption<Self>`)
997    fn inverse_and_sqrt(&self) -> (CtOption<Self>, CtOption<Self>) {
998        let is_invertible = !self.is_zero();
999
1000        let mut mysqrt = *self;
1001        let exp = TSCONSTS::PROJENATOR_EXP;
1002        let exp_limbs = exp.as_limbs().map(|limb| limb.0);
1003        // TODO: generate the addition chain for this specific constant
1004        // this is constant time since exp_limbs is always(!) the same
1005        let w = mysqrt.pow_vartime(&exp_limbs);
1006        ts_loop(&mut mysqrt, &w);
1007
1008        let e0 = TSCONSTS::TWOSM1;
1009        let e0_limbs = e0.as_limbs().map(|limb| limb.0);
1010        let e1 = e0.sub(Uint::<N>::from_u64(1));
1011        let e1_limbs = e1.as_limbs().map(|limb| limb.0);
1012
1013        // Compute x^(2^(S - 1) - 1) * (x * w^4)^(2^(S - 1))
1014        let myinv = self
1015            .pow_vartime(&e1_limbs)
1016            .mul((self.mul(&w.pow_vartime(&[4 as u64]))).pow_vartime(&e0_limbs));
1017
1018        (
1019            CtOption::new(myinv, is_invertible),
1020            CtOption::new(
1021                mysqrt,
1022                mysqrt.mul(mysqrt).ct_eq(self), // Only return Some if it's the square root.
1023            ),
1024        )
1025    }
1026
1027    /// Inverse of squareroot of `self` in 1 exponentiation
1028    ///
1029    /// Computes 1/sqrt(self) using the trick from Mike Scott's
1030    /// "Tricks of the trade" article Section 2
1031    /// <https://eprint.iacr.org/2020/1497>
1032    ///
1033    /// # Arguments
1034    ///
1035    /// * `&self` - Description of &self (type: self)
1036    ///
1037    /// # Returns
1038    ///
1039    /// The inverse of the squareroot of `self` (type: `CtOption<Self>`)
1040    fn inv_sqrt(&self) -> CtOption<Self> {
1041        let (inv, sqrt) = self.inverse_and_sqrt();
1042        inv.and_then(|a| sqrt.map(|b| a * b))
1043    }
1044
1045    /// Inverse of `self` and squareroot of `rhs` in 1 exponentiation
1046    ///
1047    /// Computes `1/self` and `rhs.sqrt()` simulaineously using the
1048    /// trick from Mike Scott's "Tricks of the trade" article Section
1049    /// 2 <https://eprint.iacr.org/2020/1497>
1050    ///
1051    /// # Returns
1052    ///
1053    /// The inverse of `self` and square root fo `rhs`. Theq former is
1054    /// none if and only if `self` is nonzero and the latter is not
1055    /// none if and only if there exists a squareroot of `rhs` in FpM
1056    /// (type: (`CtOption<Self>`, `CtOption<Self>`))
1057    fn invertme_sqrtother(&self, rhs: &Self) -> (CtOption<Self>, CtOption<Self>) {
1058        let is_invertible = !self.is_zero();
1059
1060        let x = self.mul(self).mul(*rhs);
1061        let (myinv, mysqrt) = x.inverse_and_sqrt();
1062
1063        let myinv_value = myinv.unwrap_or(Self::zero());
1064        let mysqrt_value = mysqrt.unwrap_or(Self::zero());
1065        let inv_value = self.mul(rhs).mul(myinv_value);
1066        let sqrt_value = inv_value.mul(mysqrt_value);
1067
1068        let inv_is_some = is_invertible & myinv.is_some();
1069
1070        let sqrt_is_some = inv_is_some & mysqrt.is_some() & (sqrt_value.mul(sqrt_value)).ct_eq(rhs);
1071
1072        (
1073            CtOption::new(inv_value, inv_is_some),
1074            CtOption::new(sqrt_value, sqrt_is_some),
1075        )
1076    }
1077
1078    /// Computes the squareroot of a ratio `self/rhs`
1079    ///
1080    /// Computes `sqrt(self/rhs)` in one exponentiation using the
1081    /// trick from Mike Scott's "Tricks of the trade" article Section
1082    /// 2 <https://eprint.iacr.org/2020/1497>
1083    ///
1084    /// # Arguments
1085    ///
1086    /// * `&self` - Element of FpM (type: self)
1087    /// * `rhs` - Element of FpM (type: &Self)
1088    ///
1089    /// # Returns
1090    ///
1091    /// The squareroot of the ratio `self/rhs` is not none if and only
1092    /// if `rhs` is invertible and the ratio has an FpM squareroot
1093    /// (type: `CtOption<Self>`)
1094    fn sqrt_ratio(&self, rhs: &Self) -> CtOption<Self> {
1095        let x = self.mul(&self.mul(self)).mul(*rhs);
1096        let (myinv, mysqrt) = x.inverse_and_sqrt();
1097
1098        let myinv_value = myinv.unwrap_or(Self::zero());
1099        let mysqrt_value = mysqrt.unwrap_or(Self::zero());
1100        let ans_value = self.mul(self).mul(myinv_value).mul(mysqrt_value);
1101
1102        let inv_is_some = myinv.is_some();
1103        let ans_is_some =
1104            inv_is_some & mysqrt.is_some() & (mysqrt_value.mul(mysqrt_value)).ct_eq(&x);
1105
1106        CtOption::new(ans_value, ans_is_some)
1107    }
1108
1109    /// a is a QR in Fp^M iff a^{(p^M-1)/2} = 1.
1110    ///
1111    /// Implements the "Legendre symbol" which is 1 if and only if we
1112    /// have a quadratic residue in FpM
1113    /// WARNING: Not constant time if `self` is zero
1114    ///
1115    /// # Arguments
1116    ///
1117    /// * `&self` - Element of FpM (type: self)
1118    ///
1119    /// # Returns
1120    ///
1121    /// Either `0` if `&self` is `0`, `1` if `&self` is a QR or `-1` if
1122    /// `&self` is not a QR. (type: i8)
1123    fn legendre(&self) -> i8 {
1124        let exp = TSCONSTS::HALF_ORDER;
1125        let exp_limbs = exp.as_limbs().map(|limb| limb.0);
1126        let symb = self.pow(&exp_limbs); // note, this is constant time since exp is constant
1127
1128        let ret = i8::conditional_select(&-1, &1, symb.is_one());
1129        i8::conditional_select(&0, &ret, !symb.is_zero())
1130    }
1131
1132    // --- Utilities ----------------------------------------------------------
1133
1134    fn characteristic() -> Vec<u64> {
1135        // Same prime p as the base field.
1136        FpElement::<MOD, LIMBS>::characteristic()
1137    }
1138
1139    /// Degree of Fp^M over the prime subfield Fp.
1140    fn degree() -> u32 {
1141        M as u32
1142    }
1143}
1144
1145// ===========================================================================
1146// Cryptographically secure random sampling
1147// ===========================================================================
1148
1149impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS> FieldRandom
1150    for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
1151where
1152    MOD: ConstPrimeMontyParams<LIMBS>,
1153    P: IrreduciblePoly<MOD, LIMBS, M>,
1154    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
1155{
1156    /// Sample a uniformly random element of Fp^M by drawing each
1157    /// coefficient independently from Fp.
1158    fn random(rng: &mut (impl rand::CryptoRng + rand::Rng)) -> Self {
1159        let coeffs = std::array::from_fn(|_| FpElement::<MOD, LIMBS>::random(rng));
1160        Self::new(coeffs)
1161    }
1162}
1163
1164impl<MOD, const LIMBS: usize, const M: usize, const N: usize, P, TSCONSTS>
1165FieldFromRepr for FpExt<MOD, LIMBS, M, N, P, TSCONSTS>
1166where
1167    MOD: ConstPrimeMontyParams<LIMBS>,
1168    P: IrreduciblePoly<MOD, LIMBS, M>,
1169    TSCONSTS: TonelliShanksConstants<MOD, LIMBS, M, N>,
1170{
1171    type Repr = [FpElement<MOD, LIMBS>; M];
1172
1173    fn from_repr(x: Self::Repr) -> Self {
1174        Self::new(x)
1175    }
1176}