From 2070cb6cc2a8167b7b9ac3622e6ac82f2001a275 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 31 Jan 2026 22:51:24 +0000 Subject: [PATCH 1/3] Convert `inv` unit tests to property-based tests using `proptest`. This change replaces the manual `inv` unit test in `crates/fhe-math/src/zq/mod.rs` with a property-based test. The new test: - Uses `valid_moduli` to test general moduli. - Uses `prime_moduli` (a new strategy) to ensure the original prime cases are still covered and to test `inv` success cases. - Verifies that `inv` returns `None` for composite moduli or zero input. - Verifies that `inv` returns the correct inverse for prime moduli. - Includes debug assertions to verify panic behavior on invalid inputs (only when p is prime). Co-authored-by: tlepoint <1345502+tlepoint@users.noreply.github.com> --- crates/fhe-math/src/zq/mod.rs | 62 +++++++++++++++++------------------ 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/crates/fhe-math/src/zq/mod.rs b/crates/fhe-math/src/zq/mod.rs index a8c9dcbb..c2e3ed63 100644 --- a/crates/fhe-math/src/zq/mod.rs +++ b/crates/fhe-math/src/zq/mod.rs @@ -803,6 +803,7 @@ impl Modulus { #[cfg(test)] mod tests { use super::{Modulus, primes}; + use fhe_util::is_prime; use itertools::{Itertools, izip}; use proptest::collection::vec as prop_vec; use proptest::prelude::{BoxedStrategy, Just, Strategy, any}; @@ -814,6 +815,14 @@ mod tests { any::().prop_filter_map("filter invalid moduli", |p| Modulus::new(p).ok()) } + fn prime_moduli() -> BoxedStrategy { + proptest::sample::select(vec![ + 2u64, 3, 17, 1987, 4611686018326724609, + ]) + .prop_map(|p| Modulus::new(p).unwrap()) + .boxed() + } + fn vecs() -> BoxedStrategy<(Vec, Vec)> { prop_vec(any::(), 1..100) .prop_flat_map(|vec| { @@ -1093,6 +1102,27 @@ mod tests { let c = p.deserialize_vec(&b); prop_assert_eq!(a, c); } + + #[test] + fn inv(p in prop_oneof![valid_moduli().boxed(), prime_moduli()], mut a: u64) { + a = p.reduce(a); + let b = p.inv(a); + + if !is_prime(*p) || a == 0 { + prop_assert!(b.is_none()); + } else { + prop_assert!(b.is_some()); + prop_assert_eq!(p.mul(a, b.unwrap()), 1); + } + + #[cfg(debug_assertions)] + { + if is_prime(*p) { + prop_assert!(std::panic::catch_unwind(|| p.inv(*p)).is_err()); + prop_assert!(std::panic::catch_unwind(|| p.inv(*p << 1)).is_err()); + } + } + } } // TODO: Make a proptest. @@ -1167,36 +1197,4 @@ mod tests { } } - // TODO: Make a proptest. - #[test] - fn inv() { - let ntests = 100; - let mut rng = rand::rng(); - - for p in [2u64, 3, 17, 1987, 4611686018326724609] { - let q = Modulus::new(p).unwrap(); - - assert!(q.inv(0).is_none()); - assert_eq!(q.inv(1).unwrap(), 1); - assert_eq!(q.inv(p - 1).unwrap(), p - 1); - - #[cfg(debug_assertions)] - { - assert!(std::panic::catch_unwind(|| q.inv(p)).is_err()); - assert!(std::panic::catch_unwind(|| q.inv(p << 1)).is_err()); - } - - for _ in 0..ntests { - let a = rng.next_u64() % p; - let b = q.inv(a); - - if a == 0 { - assert!(b.is_none()) - } else { - assert!(b.is_some()); - assert_eq!(q.mul(a, b.unwrap()), 1) - } - } - } - } } From b97cfc833322b3ba941cca622878ed65ade9402f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 31 Jan 2026 23:12:34 +0000 Subject: [PATCH 2/3] Convert `inv` unit tests to property-based tests using `proptest`. This change replaces the manual `inv` unit test in `crates/fhe-math/src/zq/mod.rs` with a property-based test. The new test: - Uses `valid_moduli` to test general moduli. - Uses `prime_moduli` (a new strategy) to ensure the original prime cases are still covered and to test `inv` success cases. - Verifies that `inv` returns `None` for composite moduli or zero input. - Verifies that `inv` returns the correct inverse for prime moduli. - Includes debug assertions to verify panic behavior on invalid inputs (only when p is prime). Co-authored-by: tlepoint <1345502+tlepoint@users.noreply.github.com> --- crates/fhe-math/src/zq/mod.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/fhe-math/src/zq/mod.rs b/crates/fhe-math/src/zq/mod.rs index c2e3ed63..56af28ea 100644 --- a/crates/fhe-math/src/zq/mod.rs +++ b/crates/fhe-math/src/zq/mod.rs @@ -816,11 +816,9 @@ mod tests { } fn prime_moduli() -> BoxedStrategy { - proptest::sample::select(vec![ - 2u64, 3, 17, 1987, 4611686018326724609, - ]) - .prop_map(|p| Modulus::new(p).unwrap()) - .boxed() + proptest::sample::select(vec![2u64, 3, 17, 1987, 4611686018326724609]) + .prop_map(|p| Modulus::new(p).unwrap()) + .boxed() } fn vecs() -> BoxedStrategy<(Vec, Vec)> { @@ -1196,5 +1194,4 @@ mod tests { } } } - } From 5813f7bdd538dddb3d606d02aa7a26ef19297700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tancr=C3=A8de=20Lepoint?= Date: Sun, 1 Feb 2026 09:42:15 -0500 Subject: [PATCH 3/3] Clippy --- crates/fhe-math/src/zq/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fhe-math/src/zq/mod.rs b/crates/fhe-math/src/zq/mod.rs index c84b0c30..62c1a8e9 100644 --- a/crates/fhe-math/src/zq/mod.rs +++ b/crates/fhe-math/src/zq/mod.rs @@ -807,7 +807,7 @@ mod tests { use itertools::{Itertools, izip}; use proptest::collection::vec as prop_vec; use proptest::prelude::{BoxedStrategy, Just, Strategy, any}; - use rand::{RngCore, rng}; + use rand::rng; // Utility functions for the proptests.