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