Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Machine-Prime, simplified nearest_prime #84

Merged
merged 1 commit into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ serde = { version = "1.0", default-features = false, features = ["derive"], opti
serde_arrays = { version = "0.1.0", optional = true }
zerocopy = { version = "0.8", default-features = false, features = ["derive"], optional = true }
rkyv = { version = "0.8", default-features = false, optional = true }
# no-std constant time primality testing, low memory variant
machine-prime = { version="1.3.*", features = ["small"], optional = true}

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
Expand All @@ -33,6 +35,10 @@ zerocopy = ["dep:zerocopy"]
# Derives the `Serialize`, `Deserialize`, and `Archive` traits from the [`rkyv`](https://crates.io/crates/rkyv) crate for the `Primes` struct.
rkyv = ["dep:rkyv"]

# Calls the Machine-Prime library for much faster primality testing
fastprime = ["dep:machine-prime"]
JSorngard marked this conversation as resolved.
Show resolved Hide resolved


[package.metadata.docs.rs]
# Document all features.
all-features = true
Expand Down
13 changes: 12 additions & 1 deletion src/check.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! This module contains an implementation of a deterministic Miller-Rabin primality test

#[cfg(not(feature="fastprime"))]
use crate::integer_math::{mod_mul, mod_pow};

/// Returns whether `n` is prime.
Expand All @@ -17,6 +17,14 @@ use crate::integer_math::{mod_mul, mod_pow};
/// ```
#[must_use]
pub const fn is_prime(n: u64) -> bool {

#[cfg(feature="fastprime")]
{
machine_prime::is_prime(n)
}

#[cfg(not(feature="fastprime"))]
{
// Since we know the maximum size of the numbers we test against
// we can use the fact that there are known perfect bases
// in order to make the test both fast and deterministic.
Expand Down Expand Up @@ -75,9 +83,12 @@ pub const fn is_prime(n: u64) -> bool {
}

true

} // end conditional compilation block
}

/// Performs a Miller-Rabin test with the witness k.
#[cfg(not(feature="fastprime"))]
const fn miller_test(mut d: u64, n: u64, k: u64) -> bool {
let mut x = mod_pow(k, d, n);
if x == 1 || x == n - 1 {
Expand Down
2 changes: 2 additions & 0 deletions src/integer_math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub const fn isqrt(n: u64) -> u64 {

/// Calculates (`base` ^ `exp`) mod `modulo` without overflow.
#[must_use]
#[cfg(not(feature="fastprime"))]
pub const fn mod_pow(mut base: u64, mut exp: u64, modulo: u64) -> u64 {
let mut res = 1;

Expand All @@ -52,6 +53,7 @@ pub const fn mod_pow(mut base: u64, mut exp: u64, modulo: u64) -> u64 {

/// Calculates (`a` * `b`) mod `modulo` without overflow.
#[must_use]
#[cfg(not(feature="fastprime"))]
pub const fn mod_mul(a: u64, b: u64, modulo: u64) -> u64 {
((a as u128 * b as u128) % modulo as u128) as u64
}
Expand Down
47 changes: 29 additions & 18 deletions src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,27 @@

use crate::is_prime;


// Generalised function for nearest search by incrementing/decrementing by 1
// Any attempt at optimising this would be largely pointless since the largest prime gap under 2^64 is only 1550
// And is_prime's trial division already eliminates most of those
const fn bounded_search(mut n: u64, stride: u64) -> Option<u64>{
JSorngard marked this conversation as resolved.
Show resolved Hide resolved

loop{

// Addition over Z/2^64, aka regular addition under optimisation flags
n= n.wrapping_add(stride);
// If either condition is met then we started either below or above the smallest or largest prime respectively
// Any two values from 2^64-58 to 1 would also work
if n == 0u64 || n == u64::MAX{
return None
}

if is_prime(n){
return Some(n)
}
}
}
/// Returns the largest prime smaller than `n` if there is one.
///
/// Scans for primes downwards from the input with [`is_prime`].
Expand All @@ -24,24 +45,10 @@ use crate::is_prime;
/// assert_eq!(NO_SUCH, None);
/// ```
#[must_use = "the function only returns a new value and does not modify its input"]
pub const fn previous_prime(mut n: u64) -> Option<u64> {
if n <= 2 {
None
} else if n == 3 {
Some(2)
} else {
n -= 1;
pub const fn previous_prime(n: u64) -> Option<u64> {

if n % 2 == 0 {
n -= 1;
}

while !is_prime(n) {
n -= 2;
}

Some(n)
}
// Adding by 2^64-1 over Z/2^64 is equivalent to subtracting by 1
bounded_search(n,u64::MAX)
}

/// Returns the smallest prime greater than `n` if there is one that
Expand All @@ -67,7 +74,10 @@ pub const fn previous_prime(mut n: u64) -> Option<u64> {
/// assert_eq!(NO_SUCH, None);
/// ```
#[must_use = "the function only returns a new value and does not modify its input"]
pub const fn next_prime(mut n: u64) -> Option<u64> {
pub const fn next_prime(n: u64) -> Option<u64> {

bounded_search(n,1)
/*
// The largest prime smaller than u64::MAX
if n >= 18_446_744_073_709_551_557 {
None
Expand All @@ -86,4 +96,5 @@ pub const fn next_prime(mut n: u64) -> Option<u64> {

Some(n)
}
*/
}
Loading