Skip to content

Commit

Permalink
Add parallel safe prime generation
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdplm committed Nov 5, 2024
1 parent f0e5edf commit e4b1adb
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 4 deletions.
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ mod traits;
pub use presets::{generate_prime_with_rng, generate_safe_prime_with_rng, is_prime_with_rng, is_safe_prime_with_rng};
pub use traits::RandomPrimeWithRng;

#[cfg(feature = "rayon")]
pub use presets::par_generate_prime_with_rng;
#[cfg(feature = "default-rng")]
pub use presets::{generate_prime, generate_safe_prime, is_prime, is_safe_prime};
#[cfg(feature = "rayon")]
pub use presets::{par_generate_prime_with_rng, par_generate_safe_prime_with_rng};
66 changes: 64 additions & 2 deletions src/presets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,10 @@ pub fn generate_safe_prime_with_rng<T: Integer + RandomBits + RandomMod>(
}
}

/// Use [`rayon`] to parallelize the prime search.
///
/// Returns a random prime of size `bit_length` using the provided RNG.
///
/// Uses [`rayon`] to parallelize the prime search using `corecount/2` threads (but minimum 2).
///
/// Panics if `bit_length` is less than 2, or greater than the bit size of the target `Uint`.
///
/// Panics if the platform is unable to spawn threads.
Expand Down Expand Up @@ -134,6 +134,49 @@ where
}
}

/// Returns a random safe prime (that is, such that `(n - 1) / 2` is also prime)
/// of size `bit_length` using the provided RNG.
///
/// Uses [`rayon`] to parallelize the prime search using `corecount/2` threads (but minimum 2).
///
/// Panics if `bit_length` is less than 3, or greater than the bit size of the target `Uint`.
/// Panics if the platform is unable to spawn threads.
///
/// See [`is_prime_with_rng`] for details about the performed checks.
#[cfg(feature = "rayon")]
pub fn par_generate_safe_prime_with_rng<T>(rng: &mut (impl CryptoRngCore + Send + Sync + Clone), bit_length: u32) -> T
where
T: Integer + RandomBits + RandomMod,
{
if bit_length < 2 {
panic!("`bit_length` must be 2 or greater.");
}
let bit_length = NonZeroU32::new(bit_length).expect("`bit_length` should be non-zero");

// TODO(dp): decide how to set the threadcount.
let threadcount = core::cmp::max(2, num_cpus::get() / 2);
let threadpool = rayon::ThreadPoolBuilder::new()
.num_threads(threadcount)
.build()
.expect("If the platform can spawn threads, then this call will work.");
let start = random_odd_integer::<T>(rng, bit_length).get();
let sieve = Sieve::new(start, bit_length, true);

let prime = threadpool.install(|| {
sieve.par_bridge().find_any(|c| {
let mut rng = rng.clone();
is_safe_prime_with_rng(&mut rng, c)
})
});
match prime {
Some(prime) => prime,
None => {
drop(threadpool);
par_generate_safe_prime_with_rng(rng, bit_length.get())
}
}
}

/// Probabilistically checks if the given number is prime using the provided RNG.
///
/// Performed checks:
Expand Down Expand Up @@ -435,6 +478,25 @@ mod rayon_tests {
assert!(is_prime(&p));
}
}

#[test]
fn parallel_safe_prime_generation() {
for bit_length in (28..=128).step_by(10) {
let p: U128 = par_generate_safe_prime_with_rng(&mut OsRng, bit_length);
assert!(p.bits_vartime() == bit_length);
assert!(is_prime(&p));
}
}

#[test]
fn parallel_safe_prime_generation_boxed() {
for bit_length in (28..=128).step_by(10) {
let p: BoxedUint = par_generate_safe_prime_with_rng(&mut OsRng, bit_length);
assert!(p.bits_vartime() == bit_length);
assert!(p.to_words().len() == nlimbs!(bit_length));
assert!(is_prime(&p));
}
}
}

#[cfg(test)]
Expand Down

0 comments on commit e4b1adb

Please sign in to comment.