From c7d0dfd4bd13641ae1276aeeccd83c1b06bad42c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 26 Dec 2024 16:52:02 +0100 Subject: [PATCH 1/6] Add back the `fast_test` feature --- Cargo.lock | 7 +++ Cargo.toml | 5 ++ src/check.rs | 109 ++++++++++++++++++++++++-------------------- src/integer_math.rs | 2 + 4 files changed, 74 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41339e4..da6f6e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,6 +104,7 @@ name = "const-primes" version = "0.10.1" dependencies = [ "criterion", + "machine-prime", "rand", "rkyv", "serde", @@ -248,6 +249,12 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "machine-prime" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ced57f9093501cfed04a9deca0dcb6f36475fa8f00495a6d99823a75d9f714" + [[package]] name = "memchr" version = "2.7.4" diff --git a/Cargo.toml b/Cargo.toml index dedc64e..867b51f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ documentation = "https://docs.rs/const-primes" rust-version = "1.81.0" [dependencies] +machine-prime = { version = "1.3", optional = true, default-features = false, features = ["tiny"] } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } serde_arrays = { version = "0.1.0", optional = true } zerocopy = { version = "0.8", default-features = false, features = ["derive"], optional = true } @@ -23,6 +24,10 @@ rand = { version = "0.8", default-features = false, features = ["small_rng"] } serde_json = "1.0" [features] + +# Significantly speed up primality testing by depending on the [`machine-prime`](https://crates.io/crates/machine_prime) crate. +fast_test = ["dep:machine-prime"] + # Derives the `Serialize` and `Deserialize` traits from the [`serde`](https://crates.io/crates/serde) crate for the `Primes` struct, as well as a few others. # Uses the [`serde_arrays`](https://crates.io/crates/serde_arrays) crate to do this, and that crate uses the standard library. serde = ["dep:serde", "dep:serde_arrays"] diff --git a/src/check.rs b/src/check.rs index 9998ef9..e9ca33f 100644 --- a/src/check.rs +++ b/src/check.rs @@ -1,4 +1,6 @@ //! This module contains an implementation of a deterministic Miller-Rabin primality test + +#[cfg(not(feature = "fast_test"))] use crate::integer_math::{mod_mul, mod_pow}; /// Returns whether `n` is prime. @@ -17,66 +19,75 @@ use crate::integer_math::{mod_mul, mod_pow}; /// ``` #[must_use] pub const fn is_prime(n: u64) -> bool { - // 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. - // This list of witnesses was taken from - // . - const NUM_BASES: usize = 11; - const WITNESSES: [(u64, &[u64]); NUM_BASES] = [ - (2_046, &[2]), - (1_373_652, &[2, 3]), - (9_080_190, &[31, 73]), - (25_326_000, &[2, 3, 5]), - (4_759_123_140, &[2, 7, 61]), - (1_112_004_669_632, &[2, 13, 23, 1_662_803]), - (2_152_302_898_746, &[2, 3, 5, 7, 11]), - (3_474_749_660_382, &[2, 3, 5, 7, 11, 13]), - (341_550_071_728_320, &[2, 3, 5, 7, 11, 13, 17]), - (3_825_123_056_546_413_050, &[2, 3, 5, 7, 11, 13, 17, 19, 23]), - (u64::MAX, &[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]), - ]; - - if n == 2 || n == 3 { - return true; - } else if n <= 1 || n % 2 == 0 || n % 3 == 0 { - return false; + #[cfg(feature = "fast_test")] + { + machine_prime::is_prime(n) } - // Use a small wheel to check up to log2(n). - // This keeps the complexity at O(log(n)). - let mut candidate_factor = 5; - let trial_limit = n.ilog2() as u64; - while candidate_factor <= trial_limit { - if n % candidate_factor == 0 || n % (candidate_factor + 2) == 0 { + #[cfg(not(feature = "fast_test"))] + { + // 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. + // This list of witnesses was taken from + // . + const NUM_BASES: usize = 11; + const WITNESSES: [(u64, &[u64]); NUM_BASES] = [ + (2_046, &[2]), + (1_373_652, &[2, 3]), + (9_080_190, &[31, 73]), + (25_326_000, &[2, 3, 5]), + (4_759_123_140, &[2, 7, 61]), + (1_112_004_669_632, &[2, 13, 23, 1_662_803]), + (2_152_302_898_746, &[2, 3, 5, 7, 11]), + (3_474_749_660_382, &[2, 3, 5, 7, 11, 13]), + (341_550_071_728_320, &[2, 3, 5, 7, 11, 13, 17]), + (3_825_123_056_546_413_050, &[2, 3, 5, 7, 11, 13, 17, 19, 23]), + (u64::MAX, &[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]), + ]; + + if n == 2 || n == 3 { + return true; + } else if n <= 1 || n % 2 == 0 || n % 3 == 0 { return false; } - candidate_factor += 6; - } - // Find r such that n = 2^d * r + 1 for some r >= 1 - let mut d = n - 1; - while d % 2 == 0 { - d >>= 1; - } + // Use a small wheel to check up to log2(n). + // This keeps the complexity at O(log(n)). + let mut candidate_factor = 5; + let trial_limit = n.ilog2() as u64; + while candidate_factor <= trial_limit { + if n % candidate_factor == 0 || n % (candidate_factor + 2) == 0 { + return false; + } + candidate_factor += 6; + } - let mut i = 0; - while i < NUM_BASES && WITNESSES[i].0 < n { - i += 1; - } - let witnesses = WITNESSES[i].1; + // Find r such that n = 2^d * r + 1 for some r >= 1 + let mut d = n - 1; + while d % 2 == 0 { + d >>= 1; + } - let mut i = 0; - while i < witnesses.len() && witnesses[i] < n { - if !miller_test(d, n, witnesses[i]) { - return false; + let mut i = 0; + while i < NUM_BASES && WITNESSES[i].0 < n { + i += 1; } - i += 1; - } + let witnesses = WITNESSES[i].1; - true + let mut i = 0; + while i < witnesses.len() && witnesses[i] < n { + if !miller_test(d, n, witnesses[i]) { + return false; + } + i += 1; + } + + true + } } +#[cfg(not(feature = "fast_test"))] /// Performs a Miller-Rabin test with the witness k. const fn miller_test(mut d: u64, n: u64, k: u64) -> bool { let mut x = mod_pow(k, d, n); diff --git a/src/integer_math.rs b/src/integer_math.rs index 665f02a..9bc788b 100644 --- a/src/integer_math.rs +++ b/src/integer_math.rs @@ -32,6 +32,7 @@ pub const fn isqrt(n: u64) -> u64 { } } +#[cfg(not(feature = "fast_test"))] /// Calculates (`base` ^ `exp`) mod `modulo` without overflow. #[must_use] pub const fn mod_pow(mut base: u64, mut exp: u64, modulo: u64) -> u64 { @@ -50,6 +51,7 @@ pub const fn mod_pow(mut base: u64, mut exp: u64, modulo: u64) -> u64 { res } +#[cfg(not(feature = "fast_test"))] /// Calculates (`a` * `b`) mod `modulo` without overflow. #[must_use] pub const fn mod_mul(a: u64, b: u64, modulo: u64) -> u64 { From edef4a6c823bcc0aa46c1e4691e6a8616231a487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Dec 2024 21:18:24 +0100 Subject: [PATCH 2/6] Switch to small feature from tiny --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 867b51f..f744929 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.rs/const-primes" rust-version = "1.81.0" [dependencies] -machine-prime = { version = "1.3", optional = true, default-features = false, features = ["tiny"] } +machine-prime = { version = "1.3", optional = true, default-features = false, features = ["small"] } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } serde_arrays = { version = "0.1.0", optional = true } zerocopy = { version = "0.8", default-features = false, features = ["derive"], optional = true } From 63365685b44a2be6e8eabaa2cce708a48be7a46c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 Jan 2025 11:49:06 +0100 Subject: [PATCH 3/6] update machine-prime --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da6f6e5..6b22b5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,9 +251,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "machine-prime" -version = "1.3.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31ced57f9093501cfed04a9deca0dcb6f36475fa8f00495a6d99823a75d9f714" +checksum = "ffe65c4413533b22f1cbbe02d5ce739996bc7a19f12e441846767aa4c18fd812" [[package]] name = "memchr" diff --git a/Cargo.toml b/Cargo.toml index f744929..0fe5710 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.rs/const-primes" rust-version = "1.81.0" [dependencies] -machine-prime = { version = "1.3", optional = true, default-features = false, features = ["small"] } +machine-prime = { version = "1.5", optional = true, default-features = false, features = ["lucas"] } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } serde_arrays = { version = "0.1.0", optional = true } zerocopy = { version = "0.8", default-features = false, features = ["derive"], optional = true } From 6a12c56fd454e6eb73b35088b01a0fa5ce84c487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 Jan 2025 22:23:26 +0100 Subject: [PATCH 4/6] Document feature --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- README.md | 2 ++ src/lib.rs | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b22b5d..e0603e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,9 +251,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "machine-prime" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe65c4413533b22f1cbbe02d5ce739996bc7a19f12e441846767aa4c18fd812" +checksum = "b66a0fa1bff0724c15528f46a9c37554c37b598e24976917f32b246c8f947a8b" [[package]] name = "memchr" diff --git a/Cargo.toml b/Cargo.toml index 0fe5710..3f22a13 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.rs/const-primes" rust-version = "1.81.0" [dependencies] -machine-prime = { version = "1.5", optional = true, default-features = false, features = ["lucas"] } +machine-prime = { version = "1.5.1", optional = true, default-features = false, features = ["lucas"] } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } serde_arrays = { version = "0.1.0", optional = true } zerocopy = { version = "0.8", default-features = false, features = ["derive"], optional = true } diff --git a/README.md b/README.md index 069fb8e..bbfe517 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,8 @@ and more! ## Features +`fast_test`: significantly speed up primality testing by depending on [`machine-prime`](https://crates.io/crates/machine_prime). + `serde`: derives the `Serialize` and `Deserialize` traits from [`serde`](https://crates.io/crates/serde) for the `Primes` struct, as well as a few others. Uses the [`serde_arrays`](https://crates.io/crates/serde_arrays) diff --git a/src/lib.rs b/src/lib.rs index d9a5f73..c1e139f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,7 +91,9 @@ //! //! # Features //! -//! `serde`: derives the [`Serialize`](serde::Serialize) and [`Deserialize`](serde::Deserialize) traits from the [`serde`] crte for the [`Primes`] struct, as well as a few others. +//! `fast_test`: Significantly speed up the [`is_prime`] function by depending on the [`machine_prime`] crate. +//! +//! `serde`: derives the [`Serialize`](serde::Serialize) and [`Deserialize`](serde::Deserialize) traits from the [`serde`] crate for the [`Primes`] struct, as well as a few others. //! Uses the [`serde_arrays`](https://docs.rs/serde_arrays/0.1.0) crate to do this, and that crate uses the standard library. //! //! `zerocopy`: derives the [`IntoBytes`](zerocopy::IntoBytes) trait from the [`zerocopy`] crate for the [`Primes`] struct. From a9bd9ff39f022587f9436f87e86748019aae73ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 Jan 2025 23:20:04 +0100 Subject: [PATCH 5/6] Update machine prime and add more documentation --- CHANGELOG.md | 4 ++++ Cargo.lock | 4 ++-- Cargo.toml | 2 +- src/lib.rs | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f02d604..284bd64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ This file contains the changes to the crate since version 0.4.8. +## 0.10.2 + +- Re-added the `fast_test` feature, as the feature issue has been resolved. + ## 0.10.1 - Removed reference to no longer existing feature from docstring of `is_prime`. diff --git a/Cargo.lock b/Cargo.lock index e0603e2..eb9e5a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,9 +251,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "machine-prime" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66a0fa1bff0724c15528f46a9c37554c37b598e24976917f32b246c8f947a8b" +checksum = "f62ebabb004f1a0d8cb02e21ed2456828d4a65b44b0f0b7de29847cee5d7c3a3" [[package]] name = "memchr" diff --git a/Cargo.toml b/Cargo.toml index 3f22a13..55f4be0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ documentation = "https://docs.rs/const-primes" rust-version = "1.81.0" [dependencies] -machine-prime = { version = "1.5.1", optional = true, default-features = false, features = ["lucas"] } +machine-prime = { version = "1.5.2", optional = true, default-features = false, features = ["lucas"] } serde = { version = "1.0", default-features = false, features = ["derive"], optional = true } serde_arrays = { version = "0.1.0", optional = true } zerocopy = { version = "0.8", default-features = false, features = ["derive"], optional = true } diff --git a/src/lib.rs b/src/lib.rs index c1e139f..ca12660 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,7 +92,7 @@ //! # Features //! //! `fast_test`: Significantly speed up the [`is_prime`] function by depending on the [`machine_prime`] crate. -//! +//! //! `serde`: derives the [`Serialize`](serde::Serialize) and [`Deserialize`](serde::Deserialize) traits from the [`serde`] crate for the [`Primes`] struct, as well as a few others. //! Uses the [`serde_arrays`](https://docs.rs/serde_arrays/0.1.0) crate to do this, and that crate uses the standard library. //! From 64a0e702a9032375fafb8ce6981cfda8368a3a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 Jan 2025 23:20:28 +0100 Subject: [PATCH 6/6] Bump version number --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb9e5a5..dc847f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,7 +101,7 @@ checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" [[package]] name = "const-primes" -version = "0.10.1" +version = "0.10.2" dependencies = [ "criterion", "machine-prime", diff --git a/Cargo.toml b/Cargo.toml index 55f4be0..3f6dc96 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "const-primes" authors = ["Johanna Sörngård "] -version = "0.10.1" +version = "0.10.2" edition = "2021" license = "MIT OR Apache-2.0" keywords = ["const", "primes", "no_std", "prime-numbers"]