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});