Skip to main content

commonware_math/
algebra.rs

1//! Provides traits for algebraic operations.
2//!
3//! These traits are designed to lean on the existing Rust operations in [`std::ops`],
4//! so that the familiar `+`, `+=`, etc. operators can be used. The traits are also
5//! designed with performant implementations in mind, so implementations try to
6//! use methods which don't require copying unnecessarily.
7use commonware_parallel::Strategy as ParStrategy;
8use core::{
9    fmt::Debug,
10    ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign},
11};
12use rand_core::CryptoRngCore;
13
14/// Yield all the bits in a u64, from lowest to highest.
15fn yield_bits_le(x: u64) -> impl Iterator<Item = bool> {
16    (0..64).map(move |i| (x >> i) & 1 != 0)
17}
18
19/// Yield the bits in a u64, until they all become 0.
20fn yield_bits_le_until_zeroes(x: u64) -> impl Iterator<Item = bool> {
21    (0..64 - x.leading_zeros()).map(move |i| (x >> i) & 1 != 0)
22}
23
24/// Yield all of the bits in an array of u64s, in little endian order.
25fn yield_bits_le_arr(xs: &[u64]) -> impl Iterator<Item = bool> + use<'_> {
26    let (&last, start) = xs.split_last().unwrap_or((&0, &[]));
27    start
28        .iter()
29        .copied()
30        .flat_map(yield_bits_le)
31        .chain(yield_bits_le_until_zeroes(last))
32}
33
34/// Inner utility for [`Additive::scale`] and [`Ring::exp`].
35///
36/// The "double-and-add" / "square-and-multiply" algorithms work over an arbitrary
37/// monoid, i.e. something supporting:
38///
39/// 1. 1 : T
40/// 2. (<>) : T -> T -> T
41///
42/// We take these two operations, along with a helper for applying the operation
43/// to oneself, in order to make the algorithm generic.
44fn monoid_exp<T: Clone>(
45    zero: T,
46    op: impl Fn(&mut T, &T),
47    self_op: impl Fn(&mut T),
48    x: &T,
49    bits_le: &[u64],
50) -> T {
51    let mut acc = zero;
52    let mut w = x.clone();
53    for b in yield_bits_le_arr(bits_le) {
54        if b {
55            op(&mut acc, &w);
56        }
57        self_op(&mut w)
58    }
59    acc
60}
61
62/// Return `[1, base, base^2, ..., base^(len - 1)]`.
63pub fn powers<R: Ring>(base: &R, len: usize) -> impl Iterator<Item = R> + '_ {
64    (0..len).scan(R::one(), move |state, _| {
65        let out = state.clone();
66        *state *= base;
67        Some(out)
68    })
69}
70
71/// A basic trait we expect algebraic data structures to implement.
72///
73/// Types implementing this trait need to support:
74///
75/// 1. `T.clone()`,
76/// 2. `format!("{:?}", &T)`
77/// 2. `&T == &T`,
78/// 3. `&T != &T`.
79///
80/// In other words, being clonable, and comparable for equality.
81pub trait Object: Clone + Debug + PartialEq + Eq + Send + Sync {}
82
83/// A type that supports addition, subtraction, and negation.
84///
85/// For some type `T` implementing this trait, the following operations must be
86/// supported:
87///
88/// 1. `&mut T += &T`,
89/// 2. `T + &T`,
90/// 3. `&mut T -= &T`,
91/// 4. `T - &T`,
92/// 5. `-T`.
93///
94/// There are other combinations of borrowing that could be chosen, but these
95/// should be efficiently implementable, even for a "heavier" struct, e.g.
96/// a vector of values.
97///
98/// # Usage
99///
100///
101/// ```
102/// # use commonware_math::algebra::Additive;
103///
104/// // We use .clone() whenever ownership is needed.
105/// fn example<T: Additive>(mut x: T, y: T) {
106///     x += &y;
107///     x.clone() + &y;
108///     x -= &y;
109///     x.clone() - &y;
110///     -x.clone();
111///     T::zero();
112/// }
113/// ```
114pub trait Additive:
115    Object
116    + for<'a> AddAssign<&'a Self>
117    + for<'a> Add<&'a Self, Output = Self>
118    + for<'a> SubAssign<&'a Self>
119    + for<'a> Sub<&'a Self, Output = Self>
120    + Neg<Output = Self>
121{
122    /// The neutral element for addition.
123    fn zero() -> Self;
124
125    /// Add an element to itself.
126    ///
127    /// This has a default implementation involving a clone.
128    ///
129    /// This can be overriden if a more efficient implementation is available.
130    fn double(&mut self) {
131        *self += &self.clone();
132    }
133
134    /// Scale this number by a positive integer.
135    ///
136    /// To support arbitrary positive integers, we expect to see 64 bit limbs
137    /// in little endian order.
138    ///
139    /// For example, for a 256 bit integer, we expect a slice of 4 elements,
140    /// starting with the lowest 64 bits.
141    fn scale(&self, bits_le: &[u64]) -> Self {
142        monoid_exp(Self::zero(), |a, b| *a += b, |a| a.double(), self, bits_le)
143    }
144}
145
146/// A type that supports multiplication.
147///
148/// For some type `T` implementing this trait, the following operations must be
149/// supported:
150///
151/// 1. `&mut T *= &T`,
152/// 2. `T * &T`.
153///
154/// As with [`Additive`], the borrowing scheme is chosen to keep implementations
155/// efficient even for heavier structures.
156///
157/// # Usage
158///
159/// ```
160/// # use commonware_math::algebra::Multiplicative;
161///
162/// // We use .clone() whenever ownership is needed.
163/// fn example<T: Multiplicative>(mut x: T, y: T) {
164///     x *= &y;
165///     x.clone() * &y;
166/// }
167/// ```
168pub trait Multiplicative:
169    Object + for<'a> MulAssign<&'a Self> + for<'a> Mul<&'a Self, Output = Self>
170{
171    /// Multiply an element with itself.
172    ///
173    /// This has a default implementation involving a clone.
174    ///
175    /// This can be overriden for a specific type that's better.
176    fn square(&mut self) {
177        *self *= &self.clone();
178    }
179}
180
181/// A type which implements [`Additive`], and supports scaling by some other type.
182///
183/// Mathematically, this is a (right) `R`-module.
184///
185/// The following operations must be supported (in addition to [`Additive`]):
186/// 1. `T *= &R`,
187/// 2. `T * &R`
188///
189///
190/// # Usage
191///
192/// ```
193/// # use commonware_math::algebra::Space;
194///
195/// // We use .clone() whenever ownership is needed.
196/// fn example<R, T: Space<R>>(mut x: T, y: R) {
197///     x *= &y;
198///     x.clone() * &y;
199/// }
200/// ```
201pub trait Space<R>:
202    Additive + for<'a> MulAssign<&'a R> + for<'a> Mul<&'a R, Output = Self>
203{
204    /// Calculate `sum_i points[i] * scalars[i]`.
205    ///
206    /// There's a default implementation, but for many types, a more efficient
207    /// algorithm is possible.
208    ///
209    /// Both slices should be considered as padded with [`Additive::zero`] so
210    /// that they have the same length.
211    ///
212    /// For empty slices, the result should be [`Additive::zero`];
213    fn msm(points: &[Self], scalars: &[R], _strategy: &impl ParStrategy) -> Self {
214        msm_naive(points, scalars)
215    }
216}
217
218/// A naive implementation of [`Space::msm`].
219///
220/// This is what the trait does by default.
221///
222/// This is useful when implementing the trait, because for small inputs it
223/// might be worth just using the naive implementation, because faster
224/// algorithms have some overhead in terms of allocating space.
225pub fn msm_naive<R, K: Space<R>>(points: &[K], scalars: &[R]) -> K {
226    let mut out = K::zero();
227    for (s, p) in scalars.iter().zip(points.iter()) {
228        out += &(p.clone() * s);
229    }
230    out
231}
232
233impl<R: Additive + Multiplicative> Space<R> for R {}
234
235/// An instance of a mathematical Ring.
236///
237/// This combines [`Additive`] and [`Multiplicative`], and introduces a
238/// neutral element for multiplication, [`Ring::one`].
239pub trait Ring: Additive + Multiplicative {
240    /// The neutral element for multiplication.
241    ///
242    /// Multiplying by this element does nothing.
243    fn one() -> Self;
244
245    /// Exponentiate this number by a positive integer.
246    ///
247    /// To support arbitrary positive integers, we expect to see 64 bit limbs
248    /// in little endian order.
249    ///
250    /// For example, for a 256 bit integer, we expect a slice of 4 elements,
251    /// starting with the lowest 64 bits.
252    fn exp(&self, bits_le: &[u64]) -> Self {
253        monoid_exp(Self::one(), |a, b| *a *= b, |a| a.square(), self, bits_le)
254    }
255}
256
257/// An instance of a mathematical Field.
258///
259/// This inherits from [`Ring`], and requires the existence of multiplicative
260/// inverses as well.
261pub trait Field: Ring {
262    /// The multiplicative inverse of an element.
263    ///
264    /// For [`Additive::zero`], this should return [`Additive::zero`].
265    ///
266    /// For any other element `x`, this should return an element `y` such that
267    /// `x * y` is equal to [`Ring::one`].
268    fn inv(&self) -> Self;
269}
270
271/// A [`Field`] which supports operations allowing for efficient NTTs.
272///
273/// Fields implementing this trait must have characteristic not equal to 2
274/// (so that 2 is invertible), and must have a multiplicative group with
275/// sufficiently large 2-adic order.
276pub trait FieldNTT: Field {
277    /// The maximum (lg) of the power of two root of unity this fields supports.
278    const MAX_LG_ROOT_ORDER: u8;
279
280    /// A root of unity of order `2^lg`.
281    ///
282    /// In other words, for `r = root_of_unity(lg)`, `k = 2^lg` should be the
283    /// smallest power such that `r^k = 1`.
284    ///
285    /// This function should return `None` only for `lg > MAX_LG_ROOT_ORDER`.
286    fn root_of_unity(lg: u8) -> Option<Self>;
287
288    /// An element which is not a power of a root of unity.
289    ///
290    /// In other words, for any `lg`, `k`, this element should not equal
291    /// `Self::root_of_unity(lg)^k`.
292    fn coset_shift() -> Self;
293
294    fn coset_shift_inv() -> Self {
295        Self::coset_shift().inv()
296    }
297
298    /// Return the result of dividing this element by `2`.
299    ///
300    /// This is equivalent to `self * (1 + 1)^-1`, but is usually implementable
301    /// in a more efficient way.
302    fn div_2(&self) -> Self {
303        (Self::one() + &Self::one()).inv() * self
304    }
305}
306
307/// A group suitable for use in cryptography.
308///
309/// This is a cyclic group, with a specified generator.
310///
311/// The group is of prime order, and thus has an associated field of scalars.
312///
313/// This trait requires that this type implements [`Space`] over that field.
314pub trait CryptoGroup: Space<Self::Scalar> {
315    type Scalar: Field;
316
317    /// Return the generator point of this group.
318    fn generator() -> Self;
319}
320
321/// A [`CryptoGroup`] which supports obliviously sampling elements.
322///
323/// This capability is also often referred to as "hash to curve", in the
324/// context of Elliptic Curve Cryptography, but we use the term "group"
325/// to match the naming conventions for other traits.
326///
327/// Advanced protocols use this capability to create new generator elements
328/// whose discrete logarithm relative to other points is unknown.
329pub trait HashToGroup: CryptoGroup {
330    /// Hash a domain separator, and a message, returning a group element.
331    ///
332    /// This should return an element without knowing its discrete logarithm.
333    ///
334    /// In particular, hashing into a [`CryptoGroup::Scalar`], and then multiplying
335    /// that by [`CryptoGroup::generator`] DOES NOT work.
336    fn hash_to_group(domain_separator: &[u8], message: &[u8]) -> Self;
337
338    /// Convert randomness to a group element, without learning its discrete logarithm.
339    ///
340    /// This has a default implementation assuming 128 bits of collision security.
341    /// This works by generating 256 bits of randomness, and then passing that
342    /// to [`HashToGroup::hash_to_group`].
343    ///
344    /// If you have a more efficient implementation, or want more collision security,
345    /// override this method.
346    fn rand_to_group(mut rng: impl CryptoRngCore) -> Self {
347        let mut bytes = [0u8; 32];
348        rng.fill_bytes(&mut bytes);
349        Self::hash_to_group(&[], &bytes)
350    }
351}
352
353/// A trait for objects that can be randomly sampled.
354///
355/// The only stipulation about this sampling process is that the result
356/// should be indistinguishable from one sampled uniformly at random.
357///
358/// Beyond that, we don't assume that we don't learn other things about the
359/// object as the sampler.
360pub trait Random {
361    /// Sample an object uniformly at random.
362    fn random(rng: impl CryptoRngCore) -> Self;
363}
364
365#[cfg(any(test, feature = "arbitrary"))]
366pub mod test_suites {
367    //! A collection of property tests for algebraic types.
368    //!
369    //! Provides pre-canned test suites that verify algebraic laws hold for a given type.
370    //! For example, [`fuzz_additive`] checks:
371    //!
372    //! - `+=` is consistent with `+`
373    //! - Addition is commutative
374    //! - Addition is associative
375    //! - Zero is the neutral element
376    //! - Negation is the additive inverse
377    //!
378    //! These functions take `&mut Unstructured` so users can run the harness themselves.
379    //!
380    //! # Example
381    //!
382    //! ```
383    //! # use commonware_math::algebra::test_suites::*;
384    //! # use commonware_math::fields::goldilocks::F;
385    //! commonware_invariants::minifuzz::test(|u| fuzz_field::<F>(u));
386    //! ```
387    use super::*;
388    use arbitrary::{Arbitrary, Unstructured};
389
390    fn check_add_assign<T: Additive>(a: T, b: T) {
391        let mut acc = a.clone();
392        acc += &b;
393        assert_eq!(acc, a + &b, "+= does not match +");
394    }
395
396    fn check_add_commutes<T: Additive>(a: T, b: T) {
397        assert_eq!(a.clone() + &b, b + &a, "+ not commutative");
398    }
399
400    fn check_add_associates<T: Additive>(a: T, b: T, c: T) {
401        assert_eq!((a.clone() + &b) + &c, a + &(b + &c), "+ not associative");
402    }
403
404    fn check_add_zero<T: Additive>(a: T) {
405        assert_eq!(T::zero() + &a, a, "a + 0 != a");
406    }
407
408    fn check_add_neg_self<T: Additive>(a: T) {
409        let neg_a = -a.clone();
410        assert_eq!(T::zero(), a + &neg_a, "a - a != 0");
411    }
412
413    fn check_sub_vs_add_neg<T: Additive>(a: T, b: T) {
414        assert_eq!(a.clone() - &b, a + &-b, "a - b != a + (-b)");
415    }
416
417    fn check_sub_assign<T: Additive>(a: T, b: T) {
418        let mut acc = a.clone();
419        acc -= &b;
420        assert_eq!(acc, a - &b, "-= different from -");
421    }
422
423    /// Fuzz the [`Additive`] trait properties.
424    ///
425    /// Takes arbitrary data and checks that algebraic laws hold.
426    pub fn fuzz_additive<T: Additive + for<'a> Arbitrary<'a>>(
427        u: &mut Unstructured<'_>,
428    ) -> arbitrary::Result<()> {
429        let a: T = u.arbitrary()?;
430        let b: T = u.arbitrary()?;
431        let c: T = u.arbitrary()?;
432        check_add_assign(a.clone(), b.clone());
433        check_add_commutes(a.clone(), b.clone());
434        check_add_associates(a.clone(), b.clone(), c);
435        check_add_zero(a.clone());
436        check_add_neg_self(a.clone());
437        check_sub_vs_add_neg(a.clone(), b.clone());
438        check_sub_assign(a, b);
439        Ok(())
440    }
441
442    fn check_mul_assign<T: Multiplicative>(a: T, b: T) {
443        let mut acc = a.clone();
444        acc *= &b;
445        assert_eq!(acc, a * &b, "*= different from *");
446    }
447
448    fn check_mul_commutes<T: Multiplicative>(a: T, b: T) {
449        assert_eq!(a.clone() * &b, b * &a, "* not commutative");
450    }
451
452    fn check_mul_associative<T: Multiplicative>(a: T, b: T, c: T) {
453        assert_eq!((a.clone() * &b) * &c, a * &(b * &c), "* not associative");
454    }
455
456    /// Fuzz the [`Multiplicative`] trait properties.
457    ///
458    /// Takes arbitrary data and checks that algebraic laws hold.
459    pub fn fuzz_multiplicative<T: Multiplicative + for<'a> Arbitrary<'a>>(
460        u: &mut Unstructured<'_>,
461    ) -> arbitrary::Result<()> {
462        let a: T = u.arbitrary()?;
463        let b: T = u.arbitrary()?;
464        let c: T = u.arbitrary()?;
465        check_mul_assign(a.clone(), b.clone());
466        check_mul_commutes(a.clone(), b.clone());
467        check_mul_associative(a, b, c);
468        Ok(())
469    }
470
471    fn check_mul_one<T: Ring>(a: T) {
472        assert_eq!(T::one() * &a, a, "a * 1 != a");
473    }
474
475    fn check_mul_distributes<T: Ring>(a: T, b: T, c: T) {
476        assert_eq!(
477            (a.clone() + &b) * &c,
478            a * &c + &(b * &c),
479            "(a + b) * c != a * c + b * c"
480        );
481    }
482
483    /// Fuzz the [`Ring`] trait properties.
484    ///
485    /// Takes arbitrary data and checks that algebraic laws hold.
486    /// This also checks [`Additive`] and [`Multiplicative`] properties.
487    pub fn fuzz_ring<T: Ring + for<'a> Arbitrary<'a>>(
488        u: &mut Unstructured<'_>,
489    ) -> arbitrary::Result<()> {
490        fuzz_additive::<T>(u)?;
491        fuzz_multiplicative::<T>(u)?;
492        let a: T = u.arbitrary()?;
493        let b: T = u.arbitrary()?;
494        let c: T = u.arbitrary()?;
495        check_mul_one(a.clone());
496        check_mul_distributes(a, b, c);
497        Ok(())
498    }
499
500    fn check_inv<T: Field>(a: T) {
501        if a == T::zero() {
502            assert_eq!(T::zero(), a.inv(), "0.inv() != 0");
503        } else {
504            assert_eq!(a.inv() * &a, T::one(), "a * a.inv() != 1");
505        }
506    }
507
508    /// Fuzz the [`Field`] trait properties.
509    ///
510    /// Takes arbitrary data and checks that algebraic laws hold.
511    /// This also checks [`Ring`] properties.
512    pub fn fuzz_field<T: Field + for<'a> Arbitrary<'a>>(
513        u: &mut Unstructured<'_>,
514    ) -> arbitrary::Result<()> {
515        fuzz_ring::<T>(u)?;
516        let a: T = u.arbitrary()?;
517        check_inv(a);
518        Ok(())
519    }
520
521    fn check_scale_distributes<R, K: Space<R>>(a: K, b: K, x: R) {
522        assert_eq!((a.clone() + &b) * &x, a * &x + &(b * &x));
523    }
524
525    fn check_scale_assign<R, K: Space<R>>(a: K, b: R) {
526        let mut acc = a.clone();
527        acc *= &b;
528        assert_eq!(acc, a * &b);
529    }
530
531    fn check_msm_eq_naive<R, K: Space<R>>(points: &[K], scalars: &[R]) {
532        use commonware_parallel::Sequential;
533        assert_eq!(
534            msm_naive(points, scalars),
535            K::msm(points, scalars, &Sequential)
536        );
537    }
538
539    /// Fuzz the [`Space`] trait properties, assuming nothing about the scalar `R`.
540    ///
541    /// Takes arbitrary data and checks that algebraic laws hold.
542    pub fn fuzz_space<R: Debug + for<'a> Arbitrary<'a>, K: Space<R> + for<'a> Arbitrary<'a>>(
543        u: &mut Unstructured<'_>,
544    ) -> arbitrary::Result<()> {
545        let a: K = u.arbitrary()?;
546        let b: K = u.arbitrary()?;
547        let x: R = u.arbitrary()?;
548        check_scale_distributes(a.clone(), b, x);
549        let c: R = u.arbitrary()?;
550        check_scale_assign(a, c);
551        let len: usize = u.int_in_range(0..=16)?;
552        let points: Vec<K> = (0..len)
553            .map(|_| u.arbitrary())
554            .collect::<arbitrary::Result<_>>()?;
555        let scalars: Vec<R> = (0..len)
556            .map(|_| u.arbitrary())
557            .collect::<arbitrary::Result<_>>()?;
558        check_msm_eq_naive(&points, &scalars);
559        Ok(())
560    }
561
562    fn check_scale_compat<R: Multiplicative, K: Space<R>>(a: K, b: R, c: R) {
563        assert_eq!((a.clone() * &b) * &c, a * &(b * &c));
564    }
565
566    /// Fuzz the [`Space`] trait properties, assuming `R` is [`Multiplicative`].
567    ///
568    /// Takes arbitrary data and checks that algebraic laws hold.
569    /// This also checks base [`Space`] properties plus compatibility with multiplication.
570    pub fn fuzz_space_multiplicative<
571        R: Multiplicative + for<'a> Arbitrary<'a>,
572        K: Space<R> + for<'a> Arbitrary<'a>,
573    >(
574        u: &mut Unstructured<'_>,
575    ) -> arbitrary::Result<()> {
576        fuzz_space::<R, K>(u)?;
577        let a: K = u.arbitrary()?;
578        let b: R = u.arbitrary()?;
579        let c: R = u.arbitrary()?;
580        check_scale_compat(a, b, c);
581        Ok(())
582    }
583
584    fn check_scale_one<R: Ring, K: Space<R>>(a: K) {
585        assert_eq!(a.clone(), a * &R::one());
586    }
587
588    fn check_scale_zero<R: Ring, K: Space<R>>(a: K) {
589        assert_eq!(K::zero(), a * &R::zero());
590    }
591
592    /// Fuzz the [`Space`] trait properties, assuming `R` is a [`Ring`].
593    ///
594    /// Takes arbitrary data and checks that algebraic laws hold.
595    /// This also checks [`fuzz_space_multiplicative`] properties plus compatibility
596    /// with [`Ring::one()`] and [`Additive::zero()`].
597    pub fn fuzz_space_ring<R: Ring + for<'a> Arbitrary<'a>, K: Space<R> + for<'a> Arbitrary<'a>>(
598        u: &mut Unstructured<'_>,
599    ) -> arbitrary::Result<()> {
600        fuzz_space_multiplicative::<R, K>(u)?;
601        let a: K = u.arbitrary()?;
602        check_scale_one::<R, K>(a.clone());
603        check_scale_zero::<R, K>(a);
604        Ok(())
605    }
606
607    fn check_hash_to_group<G: HashToGroup>(data: [[u8; 4]; 4]) {
608        let (dst0, m0, dst1, m1) = (&data[0], &data[1], &data[2], &data[3]);
609        assert_eq!(
610            (dst0, m0) == (dst1, m1),
611            G::hash_to_group(dst0, m0) == G::hash_to_group(dst1, m1)
612        );
613    }
614
615    /// Fuzz the [`HashToGroup`] trait properties.
616    ///
617    /// Takes arbitrary data and checks that the hash function is deterministic.
618    /// This doesn't check any properties related to [`CryptoGroup`].
619    pub fn fuzz_hash_to_group<G: HashToGroup>(u: &mut Unstructured<'_>) -> arbitrary::Result<()> {
620        let data: [[u8; 4]; 4] = u.arbitrary()?;
621        check_hash_to_group::<G>(data);
622        Ok(())
623    }
624
625    fn check_root_of_unity_order<T: FieldNTT>(lg: u8) {
626        if lg > T::MAX_LG_ROOT_ORDER {
627            assert!(
628                T::root_of_unity(lg).is_none(),
629                "root_of_unity should be None for lg > MAX"
630            );
631            return;
632        }
633        let root = T::root_of_unity(lg).expect("root_of_unity should be Some for lg <= MAX");
634
635        let mut order = Vec::new();
636        let mut remaining = lg;
637        while remaining >= 64 {
638            order.push(0u64);
639            remaining -= 64;
640        }
641        order.push(1u64 << remaining);
642
643        assert_eq!(root.exp(&order), T::one(), "root^(2^lg) should equal 1");
644        if lg > 0 {
645            let last = order.len() - 1;
646            order[0] = order[0].wrapping_sub(1);
647            for i in 0..last {
648                if order[i] == u64::MAX {
649                    order[i + 1] = order[i + 1].wrapping_sub(1);
650                }
651            }
652            assert_ne!(
653                root.exp(&order),
654                T::one(),
655                "root^(2^lg - 1) should not equal 1"
656            );
657        }
658    }
659
660    fn check_div_2<T: FieldNTT>(a: T) {
661        let two = T::one() + &T::one();
662        assert_eq!(a.div_2() * &two, a, "div_2(a) * 2 should equal a");
663    }
664
665    fn check_coset_shift_inv<T: FieldNTT>() {
666        assert_eq!(
667            T::coset_shift() * &T::coset_shift_inv(),
668            T::one(),
669            "coset_shift * coset_shift_inv should equal 1"
670        );
671    }
672
673    /// Run the test suite for the [`FieldNTT`] trait.
674    ///
675    /// This will also run [`fuzz_field`].
676    pub fn fuzz_field_ntt<T: FieldNTT + for<'a> Arbitrary<'a>>(
677        u: &mut Unstructured<'_>,
678    ) -> arbitrary::Result<()> {
679        fuzz_field::<T>(u)?;
680
681        // Biased choice towards div_2 checks
682        match u.int_in_range(0u8..=9)? {
683            0 => {
684                check_coset_shift_inv::<T>();
685            }
686            1 => {
687                let lg = u.int_in_range(0..=T::MAX_LG_ROOT_ORDER + 1)?;
688                check_root_of_unity_order::<T>(lg);
689            }
690            _ => {
691                check_div_2(T::arbitrary(u)?);
692            }
693        }
694        Ok(())
695    }
696}
697
698commonware_macros::stability_scope!(ALPHA {
699    #[cfg(any(test, feature = "fuzz"))]
700    pub mod fuzz {
701        use super::*;
702        use crate::fields::goldilocks::F;
703        use arbitrary::{Arbitrary, Unstructured};
704        use commonware_parallel::Sequential;
705
706        #[derive(Debug, Arbitrary)]
707        pub enum Plan {
708            ExpOne(F),
709            ExpZero(F),
710            Exp(F, u32, u32),
711            PowersMatchesExp(F, u16),
712            ScaleOne(F),
713            ScaleZero(F),
714            Scale(F, u32, u32),
715            Msm2([F; 2], [F; 2]),
716        }
717
718        impl Plan {
719            pub fn run(self, _u: &mut Unstructured<'_>) -> arbitrary::Result<()> {
720                match self {
721                    Self::ExpOne(x) => {
722                        assert_eq!(x.exp(&[1]), x);
723                    }
724                    Self::ExpZero(x) => {
725                        assert_eq!(x.exp(&[]), F::one());
726                    }
727                    Self::Exp(x, a, b) => {
728                        let a = u64::from(a);
729                        let b = u64::from(b);
730                        assert_eq!(x.exp(&[a + b]), x.exp(&[a]) * x.exp(&[b]));
731                    }
732                    Self::PowersMatchesExp(base, index) => {
733                        let pow_i = powers(&base, usize::from(index) + 1)
734                            .last()
735                            .expect("len=index+1 guarantees at least one item");
736                        assert_eq!(pow_i, base.exp(&[u64::from(index)]));
737                    }
738                    Self::ScaleOne(x) => {
739                        assert_eq!(x.scale(&[1]), x);
740                    }
741                    Self::ScaleZero(x) => {
742                        assert_eq!(x.scale(&[]), F::zero());
743                    }
744                    Self::Scale(x, a, b) => {
745                        let a = u64::from(a);
746                        let b = u64::from(b);
747                        assert_eq!(x.scale(&[a + b]), x.scale(&[a]) + x.scale(&[b]));
748                    }
749                    Self::Msm2(a, b) => {
750                        assert_eq!(F::msm(&a, &b, &Sequential), a[0] * b[0] + a[1] * b[1]);
751                    }
752                }
753                Ok(())
754            }
755        }
756
757        #[test]
758        fn test_fuzz() {
759            commonware_invariants::minifuzz::test(|u| u.arbitrary::<Plan>()?.run(u));
760        }
761    }
762});