From 36792b50656b3bf8d8582fc6695a97bb6a12e123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 23 Oct 2023 15:52:37 +0200 Subject: [PATCH 001/212] Add function largest_primes_below --- src/lib.rs | 113 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 39 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9ec4b9c..a00879e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -181,6 +181,79 @@ pub const fn primes() -> [Underlying; N] { primes } +/// Returns the `N` largest primes below the given upper limit. +/// # Example +/// ``` +/// # use const_primes::largest_primes_below; +/// const P: [u64; 10] = largest_primes_below(100); +/// assert_eq!(P, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); +/// ``` +pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; N] { + // This will be used to sieve all upper ranges. + let base_sieve: [bool; N] = are_prime(); + let mut primes = [0; N]; + let mut total_primes_found = 0; + 'generate: while total_primes_found < N { + let upper_sieve = sieve_segment(&base_sieve, upper_limit); + let mut i = 0; + while i < N { + let j = N - 1 - i; + if upper_sieve[j] { + primes[N - total_primes_found - 1] = upper_limit - 1 - i as u64; + total_primes_found += 1; + if total_primes_found >= N { + break 'generate; + } + } + i += 1; + } + upper_limit -= N as u64; + } + + primes +} + +/// Uses the primalities in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. +const fn sieve_segment(base_sieve: &[bool; N], upper_limit: u64) -> [bool; N] { + let mut segment_sieve = [true; N]; + + let lower_limit = upper_limit - N as u64; + + // In case 0 and/or 1 are included in the upper sieve we need to treat them as a special case + // since they are not multiples of any prime in `base_sieve` even though they are not primes. + if lower_limit == 0 && N > 1 { + segment_sieve[0] = false; + segment_sieve[1] = false; + } else if lower_limit == 1 && N > 0 { + segment_sieve[0] = false; + } + + let mut i = 0; + while i < N { + if base_sieve[i] { + let prime = i as u64; + + // Find the smallest multiple of the prime larger than or equal to `lower_limit`. + let mut composite = (lower_limit / prime) * prime; + if composite < lower_limit { + composite += prime; + } + if composite == prime { + composite += prime; + } + + // Sieve all numbers in the segment that are multiples of the prime. + while composite < upper_limit { + segment_sieve[(composite - lower_limit) as usize] = false; + composite += prime; + } + } + i += 1; + } + + segment_sieve +} + /// Returns an array of size `N` that indicates which of the integers in `[upper_limit - N, upper_limit)` are prime, /// or in other words: the value at a given index represents whether `index + upper_limit - N` is prime. /// @@ -248,45 +321,7 @@ pub const fn are_prime_below(upper_limit: u64) -> [bool; N] { return base_sieve; } - // Otherwise we use the base sieve to sieve the upper range - let mut segment_sieve = [true; N]; - - let lower_limit = upper_limit - n64; - - // In case 0 and/or 1 are included in the upper sieve we need to treat them as a special case - // since they are not multiples of any prime in `base_sieve` even though they are not primes. - if lower_limit == 0 && N > 1 { - segment_sieve[0] = false; - segment_sieve[1] = false; - } else if lower_limit == 1 && N > 0 { - segment_sieve[0] = false; - } - - // For all the found primes - let mut i = 0; - while i < N { - if base_sieve[i] { - let prime = i as u64; - - // Find the smallest multiple of the prime larger than or equal to `lower_limit`. - let mut composite = (lower_limit / prime) * prime; - if composite < lower_limit { - composite += prime; - } - if composite == prime { - composite += prime; - } - - // Sieve all numbers in the segment that are multiples of the prime. - while composite < upper_limit { - segment_sieve[(composite - lower_limit) as usize] = false; - composite += prime; - } - } - i += 1; - } - - segment_sieve + sieve_segment(&base_sieve, upper_limit) } /// Returns an array of size `N` where the value at a given index indicates whether the index is prime. From 10c4744f97d0ae53ded22098d3e078bc56936b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 23 Oct 2023 15:59:24 +0200 Subject: [PATCH 002/212] Add unit test to largest_primes_below --- src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a00879e..3281c47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -650,6 +650,22 @@ mod test { test_to_n!(0, 1, 2, 3, 4, 5, 10, 100, 1000, 10000); } + #[test] + fn check_largest_primes_below() { + macro_rules! test_n_below_100 { + ($($n:expr),+) => { + $( + { + println!("{}", $n); + assert_eq!(PRECOMPUTED_PRIMES[25-$n..25], largest_primes_below::<$n>(100).map(|i| i as u32)); + } + )+ + }; + } + + test_n_below_100!(10, 15); + } + #[test] fn check_next_prime() { for i in 1..PRECOMPUTED_PRIMES.len() - 1 { From c09b1438aad5642bc908f639392d6c96b2307b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 23 Oct 2023 16:04:48 +0200 Subject: [PATCH 003/212] Add early panic in largest_primes_below --- src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 3281c47..f4df7d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -188,7 +188,16 @@ pub const fn primes() -> [Underlying; N] { /// const P: [u64; 10] = largest_primes_below(100); /// assert_eq!(P, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); /// ``` +/// # Panics +/// Panics if `upper_limit` is not in the range `[N, N^2]`. pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; N] { + let n64 = N as u64; + assert!(upper_limit >= n64, "`upper_limit` must be at least `N`"); + match (n64).checked_mul(n64) { + Some(prod) => assert!(upper_limit <= prod, "`upper_limit` must be below `N^2`"), + None => panic!("`N^2` must fit in a `u64`"), + } + // This will be used to sieve all upper ranges. let base_sieve: [bool; N] = are_prime(); let mut primes = [0; N]; From e3d874984e64ddb2ea08377165da6f44242ab020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 23 Oct 2023 16:08:07 +0200 Subject: [PATCH 004/212] Clarify sieve_segment docstring --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f4df7d7..162489a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -222,7 +222,7 @@ pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; primes } -/// Uses the primalities in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. +/// Uses the primalities of the first `N` integers in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. const fn sieve_segment(base_sieve: &[bool; N], upper_limit: u64) -> [bool; N] { let mut segment_sieve = [true; N]; From d57e5bd618774a90d9fc35ae9eccd84221e9c2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 23 Oct 2023 17:21:05 +0200 Subject: [PATCH 005/212] Discovered failing test --- src/lib.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 162489a..d9d4f8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -200,15 +200,16 @@ pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; // This will be used to sieve all upper ranges. let base_sieve: [bool; N] = are_prime(); - let mut primes = [0; N]; - let mut total_primes_found = 0; - 'generate: while total_primes_found < N { - let upper_sieve = sieve_segment(&base_sieve, upper_limit); - let mut i = 0; + let mut primes: [u64; N] = [0; N]; + let mut total_primes_found: usize = 0; + 'generate: while total_primes_found < N && upper_limit > 1 { + let upper_sieve: [bool; N] = sieve_segment(&base_sieve, upper_limit); + let mut smallest_found_prime = upper_limit; + let mut i: usize = 0; while i < N { - let j = N - 1 - i; - if upper_sieve[j] { - primes[N - total_primes_found - 1] = upper_limit - 1 - i as u64; + if upper_sieve[N - 1 - i] { + smallest_found_prime = upper_limit - 1 - i as u64; + primes[N - total_primes_found - 1] = smallest_found_prime; total_primes_found += 1; if total_primes_found >= N { break 'generate; @@ -216,7 +217,7 @@ pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; } i += 1; } - upper_limit -= N as u64; + upper_limit = smallest_found_prime; } primes @@ -666,13 +667,14 @@ mod test { $( { println!("{}", $n); - assert_eq!(PRECOMPUTED_PRIMES[25-$n..25], largest_primes_below::<$n>(100).map(|i| i as u32)); + const P: [u64; $n] = largest_primes_below(100); + assert_eq!(PRECOMPUTED_PRIMES[25-$n..25], P.map(|i|i as u32)); } )+ }; } - test_n_below_100!(10, 15); + test_n_below_100!(10, 15, 20, 100); } #[test] From 2311f8acf8839dff6a58b062316719c008823a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 24 Oct 2023 14:47:12 +0200 Subject: [PATCH 006/212] Fix longrunning test --- src/lib.rs | 27 ++++++++++++++++++++++----- src/wrapper.rs | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d9d4f8d..8a4a322 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,7 +182,11 @@ pub const fn primes() -> [Underlying; N] { } /// Returns the `N` largest primes below the given upper limit. +/// The return array fills from the end until either it is full or there are no more primes. +/// If the primes run out before the array is filled the first elements will have a value of zero. +/// /// # Example +/// /// ``` /// # use const_primes::largest_primes_below; /// const P: [u64; 10] = largest_primes_below(100); @@ -198,16 +202,27 @@ pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; None => panic!("`N^2` must fit in a `u64`"), } + let mut primes: [u64; N] = [0; N]; + + // If the user requested only a single prime we just scan for it with `is_prime`. + if N == 1 { + if let Some(p) = largest_prime_leq(upper_limit - 1) { + primes[0] = p; + } + return primes; + } + // This will be used to sieve all upper ranges. let base_sieve: [bool; N] = are_prime(); - let mut primes: [u64; N] = [0; N]; + let mut total_primes_found: usize = 0; - 'generate: while total_primes_found < N && upper_limit > 1 { + 'generate: while total_primes_found < N && upper_limit > 2 { let upper_sieve: [bool; N] = sieve_segment(&base_sieve, upper_limit); - let mut smallest_found_prime = upper_limit; + let mut smallest_found_prime = primes[N - total_primes_found - 1]; let mut i: usize = 0; while i < N { - if upper_sieve[N - 1 - i] { + let j = N - 1 - i; + if upper_sieve[j] { smallest_found_prime = upper_limit - 1 - i as u64; primes[N - total_primes_found - 1] = smallest_found_prime; total_primes_found += 1; @@ -674,7 +689,9 @@ mod test { }; } - test_n_below_100!(10, 15, 20, 100); + test_n_below_100!(10, 15, 20); + + assert_eq!([0, 0, 0, 0, 2, 3, 5, 7], largest_primes_below(10)); } #[test] diff --git a/src/wrapper.rs b/src/wrapper.rs index 3537ef0..5d85db5 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -527,7 +527,7 @@ mod test { } #[test] - fn verity_as_slice() { + fn verify_as_slice() { const N: usize = 10; const P: Primes = Primes::new(); const A: [Underlying; N] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]; From 3ac94ac43827cd4cac4a5c9c35634bf1e6bcb753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 24 Oct 2023 15:39:20 +0200 Subject: [PATCH 007/212] Add more examples to docstring of largest_primes_below --- src/lib.rs | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8a4a322..2433b38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,23 +182,53 @@ pub const fn primes() -> [Underlying; N] { } /// Returns the `N` largest primes below the given upper limit. +/// /// The return array fills from the end until either it is full or there are no more primes. /// If the primes run out before the array is filled the first elements will have a value of zero. /// /// # Example +/// Basic usage +/// ``` +/// # use const_primes::largest_primes_below; +/// const PRIMES: [u64; 10] = largest_primes_below(100); /// +/// assert_eq!(PRIMES, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); +/// ``` +/// Compute larger primes without starting from zero /// ``` /// # use const_primes::largest_primes_below; -/// const P: [u64; 10] = largest_primes_below(100); -/// assert_eq!(P, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); +/// const N: usize = 2237; +/// const BIG_PRIMES: [u64; N] = largest_primes_below(5_000_035); +/// +/// assert_eq!(&BIG_PRIMES[..3], &[4_965_731, 4_965_739, 4_965_743]); +/// assert_eq!(&BIG_PRIMES[N - 3..], &[4_999_963, 4_999_999, 5_000_011]); +/// ``` +/// If there are not enough primes to fill the requested array, the first +/// elements will have a value of zero. +/// ``` +/// # use const_primes::largest_primes_below; +/// const PRIMES: [u64; 9] = largest_primes_below(10); +/// +/// assert_eq!(PRIMES, [0, 0, 0, 0, 0, 2, 3, 5, 7]); /// ``` /// # Panics -/// Panics if `upper_limit` is not in the range `[N, N^2]`. +/// Panics if `upper_limit` is not in the range `(N, N^2]`. +/// ```compile_fail +/// # use const_primes::largest_primes_below; +/// const PRIMES: [u64; 5] = largest_primes_below(5); +/// ``` +/// ```compile_fail +/// # use const_primes::largest_primes_below; +/// const PRIMES: [u64; 5] = largest_primes_below(26); +/// ``` pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; N] { let n64 = N as u64; - assert!(upper_limit >= n64, "`upper_limit` must be at least `N`"); + assert!(upper_limit > n64, "`upper_limit` must be larger than `N`"); match (n64).checked_mul(n64) { - Some(prod) => assert!(upper_limit <= prod, "`upper_limit` must be below `N^2`"), + Some(prod) => assert!( + upper_limit <= prod, + "`upper_limit` must be less than or equal to `N^2`" + ), None => panic!("`N^2` must fit in a `u64`"), } @@ -214,7 +244,6 @@ pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; // This will be used to sieve all upper ranges. let base_sieve: [bool; N] = are_prime(); - let mut total_primes_found: usize = 0; 'generate: while total_primes_found < N && upper_limit > 2 { let upper_sieve: [bool; N] = sieve_segment(&base_sieve, upper_limit); @@ -691,7 +720,7 @@ mod test { test_n_below_100!(10, 15, 20); - assert_eq!([0, 0, 0, 0, 2, 3, 5, 7], largest_primes_below(10)); + assert_eq!([0, 0, 0, 0, 0, 2, 3, 5, 7], largest_primes_below(10)); } #[test] From f1749449ca73823b0972f4730eeeef726bb5b374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 24 Oct 2023 15:44:39 +0200 Subject: [PATCH 008/212] Remove N=1 special case, as that can not succeed. Add test for smallest possible result. --- src/lib.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2433b38..88f5bd2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -234,14 +234,6 @@ pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; let mut primes: [u64; N] = [0; N]; - // If the user requested only a single prime we just scan for it with `is_prime`. - if N == 1 { - if let Some(p) = largest_prime_leq(upper_limit - 1) { - primes[0] = p; - } - return primes; - } - // This will be used to sieve all upper ranges. let base_sieve: [bool; N] = are_prime(); let mut total_primes_found: usize = 0; @@ -721,6 +713,8 @@ mod test { test_n_below_100!(10, 15, 20); assert_eq!([0, 0, 0, 0, 0, 2, 3, 5, 7], largest_primes_below(10)); + + assert_eq!([0, 2], largest_primes_below(3)); } #[test] From 7a5f293002caad7a506db86f430477845e77f18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 24 Oct 2023 15:52:32 +0200 Subject: [PATCH 009/212] Increase upper_limit in primes_larger_than docstring --- src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 88f5bd2..5dda15e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -197,11 +197,12 @@ pub const fn primes() -> [Underlying; N] { /// Compute larger primes without starting from zero /// ``` /// # use const_primes::largest_primes_below; -/// const N: usize = 2237; -/// const BIG_PRIMES: [u64; N] = largest_primes_below(5_000_035); +/// const N: usize = 70711; +/// # #[allow(long_running_const_eval)] +/// const BIG_PRIMES: [u64; N] = largest_primes_below(5_000_000_030); /// -/// assert_eq!(&BIG_PRIMES[..3], &[4_965_731, 4_965_739, 4_965_743]); -/// assert_eq!(&BIG_PRIMES[N - 3..], &[4_999_963, 4_999_999, 5_000_011]); +/// assert_eq!(&BIG_PRIMES[..3], &[4_998_417_421, 4_998_417_427, 4_998_417_443]); +/// assert_eq!(&BIG_PRIMES[N - 3..], &[4_999_999_903, 4_999_999_937, 5_000_000_029]); /// ``` /// If there are not enough primes to fill the requested array, the first /// elements will have a value of zero. From cea6fa853fd5076c3d9ca5d2b8aea85d80509080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 24 Oct 2023 16:01:56 +0200 Subject: [PATCH 010/212] Add note about compile error in const contexts to largest_primes_below --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5dda15e..57e2df3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -213,7 +213,8 @@ pub const fn primes() -> [Underlying; N] { /// assert_eq!(PRIMES, [0, 0, 0, 0, 0, 2, 3, 5, 7]); /// ``` /// # Panics -/// Panics if `upper_limit` is not in the range `(N, N^2]`. +/// Panics if `upper_limit` is not in the range `(N, N^2]`. This is a compile error +/// in const contexts /// ```compile_fail /// # use const_primes::largest_primes_below; /// const PRIMES: [u64; 5] = largest_primes_below(5); From 3275ca2a1322680f74c2f7b2665e4a79ca7e56e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 24 Oct 2023 16:05:17 +0200 Subject: [PATCH 011/212] use : before code examples --- src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 57e2df3..d5c5254 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -185,16 +185,19 @@ pub const fn primes() -> [Underlying; N] { /// /// The return array fills from the end until either it is full or there are no more primes. /// If the primes run out before the array is filled the first elements will have a value of zero. +/// +/// Due to the limitations on memory allocation in `const` contexts the value of `N` +/// must satisfy the bounds `N < upper_limit <= N^2`. /// /// # Example -/// Basic usage +/// Basic usage: /// ``` /// # use const_primes::largest_primes_below; /// const PRIMES: [u64; 10] = largest_primes_below(100); /// /// assert_eq!(PRIMES, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); /// ``` -/// Compute larger primes without starting from zero +/// Compute larger primes without starting from zero: /// ``` /// # use const_primes::largest_primes_below; /// const N: usize = 70711; @@ -205,7 +208,7 @@ pub const fn primes() -> [Underlying; N] { /// assert_eq!(&BIG_PRIMES[N - 3..], &[4_999_999_903, 4_999_999_937, 5_000_000_029]); /// ``` /// If there are not enough primes to fill the requested array, the first -/// elements will have a value of zero. +/// elements will have a value of zero: /// ``` /// # use const_primes::largest_primes_below; /// const PRIMES: [u64; 9] = largest_primes_below(10); @@ -214,7 +217,7 @@ pub const fn primes() -> [Underlying; N] { /// ``` /// # Panics /// Panics if `upper_limit` is not in the range `(N, N^2]`. This is a compile error -/// in const contexts +/// in const contexts: /// ```compile_fail /// # use const_primes::largest_primes_below; /// const PRIMES: [u64; 5] = largest_primes_below(5); From e1e019636ec5a23bcd5f5c3bb2de86df97ebe30a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 24 Oct 2023 17:02:30 +0200 Subject: [PATCH 012/212] Add some comments --- src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d5c5254..fd8a468 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,18 +241,24 @@ pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; // This will be used to sieve all upper ranges. let base_sieve: [bool; N] = are_prime(); + let mut total_primes_found: usize = 0; 'generate: while total_primes_found < N && upper_limit > 2 { + // This is the smallest prime we have found so far. + let mut smallest_found_prime = primes[N - 1 - total_primes_found]; + // Sieve for primes in the segment. let upper_sieve: [bool; N] = sieve_segment(&base_sieve, upper_limit); - let mut smallest_found_prime = primes[N - total_primes_found - 1]; + let mut i: usize = 0; while i < N { - let j = N - 1 - i; - if upper_sieve[j] { + // Iterate backwards through the upper sieve. + if upper_sieve[N - 1 - i] { smallest_found_prime = upper_limit - 1 - i as u64; - primes[N - total_primes_found - 1] = smallest_found_prime; + // Write every found prime to the primes array. + primes[N - 1 - total_primes_found] = smallest_found_prime; total_primes_found += 1; if total_primes_found >= N { + // If we have found enough primes we stop sieving. break 'generate; } } From 190a4054bb836eb3f0dbf7d7476ec3b1eec6b96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 25 Oct 2023 16:10:29 +0200 Subject: [PATCH 013/212] Grouped into submodules --- src/generation.rs | 253 +++++++++++++++++++++++++ src/lib.rs | 470 +++------------------------------------------- src/next_prime.rs | 73 +++++++ src/sieve.rs | 159 ++++++++++++++++ 4 files changed, 515 insertions(+), 440 deletions(-) create mode 100644 src/generation.rs create mode 100644 src/next_prime.rs create mode 100644 src/sieve.rs diff --git a/src/generation.rs b/src/generation.rs new file mode 100644 index 0000000..ed4ef7e --- /dev/null +++ b/src/generation.rs @@ -0,0 +1,253 @@ +use crate::{are_prime, sieve::sieve_segment, Underlying}; + +/// Returns the `N` first prime numbers. +/// +/// [`Primes`] might be relevant for you if you intend to later use these prime numbers for related computations. +/// +/// Uses a [segmented sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve). +/// +/// # Example +/// ``` +/// # use const_primes::primes; +/// const PRIMES: [u32; 10] = primes(); +/// assert_eq!(PRIMES, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); +/// ``` +/// # Panics +/// Panics if a computed prime overflows a `u32`. This will result in a compile error in a const context. +#[must_use = "the function only returns a new value"] +pub const fn primes() -> [Underlying; N] { + if N == 0 { + return [0; N]; + } else if N == 1 { + return [2; N]; + } else if N == 2 { + let mut primes = [0; N]; + primes[0] = 2; + primes[1] = 3; + return primes; + } + + // This is a segmented sieve that runs until it has found enough primes. + + // This array is the output in the end + let mut primes = [0; N]; + // This keeps track of how many primes we've found so far. + let mut prime_count = 0; + + // Sieve the first primes below N + let mut sieve: [bool; N] = are_prime(); + + // Count how many primes we found + // and store them in the final array + let mut number = 0; + while number < N { + if sieve[number] { + primes[prime_count] = number as Underlying; + prime_count += 1; + } + + number += 1; + } + + // For every segment of N numbers + let mut low = N - 1; + let mut high = 2 * N - 1; + 'generate: while prime_count < N { + // reset the sieve for the segment + sieve = [true; N]; + let mut i = 0; + + // and repeat for each prime found so far: + while i < prime_count { + let prime = primes[i] as usize; + + // Find the smallest composite in the current segment, + let mut composite = (low / prime) * prime; + if composite < low { + composite += prime; + } + + // and sieve all numbers in the segment that are multiples of the prime. + while composite < high { + sieve[composite - low] = false; + composite += prime; + } + + i += 1; + } + + // Move the found primes into the final array + i = low; + while i < high { + if sieve[i - low] { + primes[prime_count] = i as Underlying; + prime_count += 1; + // and stop the generation of primes if we're done. + if prime_count == N { + break 'generate; + } + } + i += 1; + } + + // Update low and high for the next segment + low += N; + high += N; + } + + primes +} + +/// Returns the `N` largest primes below the given upper limit. +/// +/// The return array fills from the end until either it is full or there are no more primes. +/// If the primes run out before the array is filled the first elements will have a value of zero. +/// +/// Due to the limitations on memory allocation in `const` contexts the value of `N` +/// must satisfy the bounds `N < upper_limit <= N^2`. +/// +/// # Example +/// Basic usage: +/// ``` +/// # use const_primes::primes_below; +/// const PRIMES: [u64; 10] = primes_below(100); +/// +/// assert_eq!(PRIMES, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); +/// ``` +/// Compute larger primes without starting from zero: +/// ``` +/// # use const_primes::primes_below; +/// const N: usize = 70711; +/// # #[allow(long_running_const_eval)] +/// const BIG_PRIMES: [u64; N] = primes_below(5_000_000_030); +/// +/// assert_eq!(&BIG_PRIMES[..3], &[4_998_417_421, 4_998_417_427, 4_998_417_443]); +/// assert_eq!(&BIG_PRIMES[N - 3..], &[4_999_999_903, 4_999_999_937, 5_000_000_029]); +/// ``` +/// If there are not enough primes to fill the requested array, the first +/// elements will have a value of zero: +/// ``` +/// # use const_primes::primes_below; +/// const PRIMES: [u64; 9] = primes_below(10); +/// +/// assert_eq!(PRIMES, [0, 0, 0, 0, 0, 2, 3, 5, 7]); +/// ``` +/// # Panics +/// Panics if `upper_limit` is not in the range `(N, N^2]`. This is a compile error +/// in const contexts: +/// ```compile_fail +/// # use const_primes::primes_below; +/// const PRIMES: [u64; 5] = primes_below(5); +/// ``` +/// ```compile_fail +/// # use const_primes::primes_below; +/// const PRIMES: [u64; 5] = primes_below(26); +/// ``` +pub const fn primes_below(mut upper_limit: u64) -> [u64; N] { + let n64 = N as u64; + assert!(upper_limit > n64, "`upper_limit` must be larger than `N`"); + match (n64).checked_mul(n64) { + Some(prod) => assert!( + upper_limit <= prod, + "`upper_limit` must be less than or equal to `N^2`" + ), + None => panic!("`N^2` must fit in a `u64`"), + } + + let mut primes: [u64; N] = [0; N]; + + // This will be used to sieve all upper ranges. + let base_sieve: [bool; N] = are_prime(); + + let mut total_primes_found: usize = 0; + 'generate: while total_primes_found < N && upper_limit > 2 { + // This is the smallest prime we have found so far. + let mut smallest_found_prime = primes[N - 1 - total_primes_found]; + // Sieve for primes in the segment. + let upper_sieve: [bool; N] = sieve_segment(&base_sieve, upper_limit); + + let mut i: usize = 0; + while i < N { + // Iterate backwards through the upper sieve. + if upper_sieve[N - 1 - i] { + smallest_found_prime = upper_limit - 1 - i as u64; + // Write every found prime to the primes array. + primes[N - 1 - total_primes_found] = smallest_found_prime; + total_primes_found += 1; + if total_primes_found >= N { + // If we have found enough primes we stop sieving. + break 'generate; + } + } + i += 1; + } + upper_limit = smallest_found_prime; + } + + primes +} + +/// Returns an array of the `N` smallest primes greater than or equal to `lower_limit`. +pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { + let n64 = N as u64; + if n64.checked_mul(n64).is_none() { + panic!("`N^2` must fit in a `u64`"); + } + + let mut primes = [0; N]; + let base_sieve: [bool; N] = are_prime(); + let mut total_found_primes = 0; + 'generate: while total_found_primes < N && lower_limit + n64 <= n64 * n64 { + let mut largest_found_prime = primes[total_found_primes]; + let upper_sieve = sieve_segment(&base_sieve, lower_limit + n64); + let mut i = 0; + // Move the found primes into the output vector. + while i < N { + if upper_sieve[i] { + largest_found_prime = lower_limit + i as u64; + primes[total_found_primes] = largest_found_prime; + total_found_primes += 1; + if total_found_primes >= N { + break 'generate; + } + } + i += 1; + } + lower_limit = largest_found_prime + 1; + } + primes +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn sanity_check_primes_geq() { + { + const P: [u64; 5] = primes_geq(10); + assert_eq!(P, [11, 13, 17, 19, 23]); + } + { + const P: [u64; 5] = primes_geq(0); + assert_eq!(P, [2, 3, 5, 7, 11]); + } + { + const P: [u64; 0] = primes_geq(0); + assert_eq!(P, []); + } + { + const P: [u64; 1] = primes_geq(0); + assert_eq!(P, [0]); + } + } + + #[test] + fn check_primes_geq_large() { + const N: usize = 71_000; + #[allow(long_running_const_eval)] + const P: [u64; N] = primes_geq(5_000_000_030); + assert_eq!(P[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); + assert_eq!(P[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); + } +} diff --git a/src/lib.rs b/src/lib.rs index fd8a468..0dd372c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,358 +76,20 @@ // This is used since there is currently no way to be generic over types that can do arithmetic at compile time. type Underlying = u32; +mod generation; mod imath; mod miller_rabin; +mod next_prime; +mod sieve; mod wrapper; + +pub use generation::{primes, primes_below, primes_geq}; use imath::isqrt; pub use miller_rabin::is_prime; +pub use next_prime::{largest_prime_leq, smallest_prime_geq}; +pub use sieve::{are_prime, are_prime_below}; pub use wrapper::Primes; -/// Returns the `N` first prime numbers. -/// -/// [`Primes`] might be relevant for you if you intend to later use these prime numbers for related computations. -/// -/// Uses a [segmented sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve). -/// -/// # Example -/// ``` -/// # use const_primes::primes; -/// const PRIMES: [u32; 10] = primes(); -/// assert_eq!(PRIMES, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); -/// ``` -/// # Panics -/// Panics if a computed prime overflows a `u32`. This will result in a compile error in a const context. -#[must_use = "the function only returns a new value"] -pub const fn primes() -> [Underlying; N] { - if N == 0 { - return [0; N]; - } else if N == 1 { - return [2; N]; - } else if N == 2 { - let mut primes = [0; N]; - primes[0] = 2; - primes[1] = 3; - return primes; - } - - // This is a segmented sieve that runs until it has found enough primes. - - // This array is the output in the end - let mut primes = [0; N]; - // This keeps track of how many primes we've found so far. - let mut prime_count = 0; - - // Sieve the first primes below N - let mut sieve: [bool; N] = are_prime(); - - // Count how many primes we found - // and store them in the final array - let mut number = 0; - while number < N { - if sieve[number] { - primes[prime_count] = number as Underlying; - prime_count += 1; - } - - number += 1; - } - - // For every segment of N numbers - let mut low = N - 1; - let mut high = 2 * N - 1; - 'generate: while prime_count < N { - // reset the sieve for the segment - sieve = [true; N]; - let mut i = 0; - - // and repeat for each prime found so far: - while i < prime_count { - let prime = primes[i] as usize; - - // Find the smallest composite in the current segment, - let mut composite = (low / prime) * prime; - if composite < low { - composite += prime; - } - - // and sieve all numbers in the segment that are multiples of the prime. - while composite < high { - sieve[composite - low] = false; - composite += prime; - } - - i += 1; - } - - // Move the found primes into the final array - i = low; - while i < high { - if sieve[i - low] { - primes[prime_count] = i as Underlying; - prime_count += 1; - // and stop the generation of primes if we're done. - if prime_count == N { - break 'generate; - } - } - i += 1; - } - - // Update low and high for the next segment - low += N; - high += N; - } - - primes -} - -/// Returns the `N` largest primes below the given upper limit. -/// -/// The return array fills from the end until either it is full or there are no more primes. -/// If the primes run out before the array is filled the first elements will have a value of zero. -/// -/// Due to the limitations on memory allocation in `const` contexts the value of `N` -/// must satisfy the bounds `N < upper_limit <= N^2`. -/// -/// # Example -/// Basic usage: -/// ``` -/// # use const_primes::largest_primes_below; -/// const PRIMES: [u64; 10] = largest_primes_below(100); -/// -/// assert_eq!(PRIMES, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); -/// ``` -/// Compute larger primes without starting from zero: -/// ``` -/// # use const_primes::largest_primes_below; -/// const N: usize = 70711; -/// # #[allow(long_running_const_eval)] -/// const BIG_PRIMES: [u64; N] = largest_primes_below(5_000_000_030); -/// -/// assert_eq!(&BIG_PRIMES[..3], &[4_998_417_421, 4_998_417_427, 4_998_417_443]); -/// assert_eq!(&BIG_PRIMES[N - 3..], &[4_999_999_903, 4_999_999_937, 5_000_000_029]); -/// ``` -/// If there are not enough primes to fill the requested array, the first -/// elements will have a value of zero: -/// ``` -/// # use const_primes::largest_primes_below; -/// const PRIMES: [u64; 9] = largest_primes_below(10); -/// -/// assert_eq!(PRIMES, [0, 0, 0, 0, 0, 2, 3, 5, 7]); -/// ``` -/// # Panics -/// Panics if `upper_limit` is not in the range `(N, N^2]`. This is a compile error -/// in const contexts: -/// ```compile_fail -/// # use const_primes::largest_primes_below; -/// const PRIMES: [u64; 5] = largest_primes_below(5); -/// ``` -/// ```compile_fail -/// # use const_primes::largest_primes_below; -/// const PRIMES: [u64; 5] = largest_primes_below(26); -/// ``` -pub const fn largest_primes_below(mut upper_limit: u64) -> [u64; N] { - let n64 = N as u64; - assert!(upper_limit > n64, "`upper_limit` must be larger than `N`"); - match (n64).checked_mul(n64) { - Some(prod) => assert!( - upper_limit <= prod, - "`upper_limit` must be less than or equal to `N^2`" - ), - None => panic!("`N^2` must fit in a `u64`"), - } - - let mut primes: [u64; N] = [0; N]; - - // This will be used to sieve all upper ranges. - let base_sieve: [bool; N] = are_prime(); - - let mut total_primes_found: usize = 0; - 'generate: while total_primes_found < N && upper_limit > 2 { - // This is the smallest prime we have found so far. - let mut smallest_found_prime = primes[N - 1 - total_primes_found]; - // Sieve for primes in the segment. - let upper_sieve: [bool; N] = sieve_segment(&base_sieve, upper_limit); - - let mut i: usize = 0; - while i < N { - // Iterate backwards through the upper sieve. - if upper_sieve[N - 1 - i] { - smallest_found_prime = upper_limit - 1 - i as u64; - // Write every found prime to the primes array. - primes[N - 1 - total_primes_found] = smallest_found_prime; - total_primes_found += 1; - if total_primes_found >= N { - // If we have found enough primes we stop sieving. - break 'generate; - } - } - i += 1; - } - upper_limit = smallest_found_prime; - } - - primes -} - -/// Uses the primalities of the first `N` integers in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. -const fn sieve_segment(base_sieve: &[bool; N], upper_limit: u64) -> [bool; N] { - let mut segment_sieve = [true; N]; - - let lower_limit = upper_limit - N as u64; - - // In case 0 and/or 1 are included in the upper sieve we need to treat them as a special case - // since they are not multiples of any prime in `base_sieve` even though they are not primes. - if lower_limit == 0 && N > 1 { - segment_sieve[0] = false; - segment_sieve[1] = false; - } else if lower_limit == 1 && N > 0 { - segment_sieve[0] = false; - } - - let mut i = 0; - while i < N { - if base_sieve[i] { - let prime = i as u64; - - // Find the smallest multiple of the prime larger than or equal to `lower_limit`. - let mut composite = (lower_limit / prime) * prime; - if composite < lower_limit { - composite += prime; - } - if composite == prime { - composite += prime; - } - - // Sieve all numbers in the segment that are multiples of the prime. - while composite < upper_limit { - segment_sieve[(composite - lower_limit) as usize] = false; - composite += prime; - } - } - i += 1; - } - - segment_sieve -} - -/// Returns an array of size `N` that indicates which of the integers in `[upper_limit - N, upper_limit)` are prime, -/// or in other words: the value at a given index represents whether `index + upper_limit - N` is prime. -/// -/// If you just want the prime status of the first `N` integers, see [`are_prime`]. -/// -/// Uses a sieve of Eratosthenes to sieve the first `N` integers -/// and then uses the result to sieve the output range if needed. -/// -/// # Examples -/// Basic usage -/// ``` -/// # use const_primes::are_prime_below; -/// const PRIME_STATUSES: [bool; 10] = are_prime_below(30); -/// -/// assert_eq!( -/// PRIME_STATUSES, -/// // 20 21 22 23 24 25 26 27 28 29 -/// [false, false, false, true, false, false, false, false, false, true], -/// ); -/// ``` -/// Sieve limited ranges of very large values -/// ``` -/// # use const_primes::are_prime_below; -/// const BIG_NUMBER: u64 = 5_000_000_031; -/// const CEIL_SQRT_BIG_NUMBER: usize = 70711; -/// const BIG_PRIME_STATUSES: [bool; CEIL_SQRT_BIG_NUMBER] = are_prime_below(BIG_NUMBER); -/// assert_eq!( -/// BIG_PRIME_STATUSES[CEIL_SQRT_BIG_NUMBER - 3..], -/// // 5_000_000_028 5_000_000_029 5_000_000_030 -/// [false, true, false], -/// ); -/// ``` -/// -/// # Panics -/// Panics if `upper_limit` is not in the range `[N, N^2]`, or if `N^2` overflows a `u64`. -/// These are compile errors in const contexts. -/// ```compile_fail -/// # use const_primes::are_prime_below; -/// const PRIME_STATUSES: [bool; 5] = are_prime_below(26); -/// ``` -/// ```compile_fail -/// # use const_primes::are_prime_below; -/// const PRIME_STATUSES: [bool; 5] = are_prime_below(4); -/// ``` -#[must_use = "the function returns a new value and does not modify its input"] -pub const fn are_prime_below(upper_limit: u64) -> [bool; N] { - let n64 = N as u64; - - // Since panics are compile time errors in const contexts - // we check all the preconditions here and panic early. - match n64.checked_mul(n64) { - Some(prod) => assert!( - upper_limit <= prod, - "`upper_limit` must be smaller than or equal to `N`^2" - ), - None => panic!("`N`^2 must fit in a `u64`"), - } - assert!(upper_limit >= n64, "`upper_limit` must be at least `N`"); - - // Use a normal sieve of Eratosthenes for the first N numbers. - let base_sieve: [bool; N] = are_prime(); - - if upper_limit == n64 { - // If we are not interested in sieving a larger range we can just return early. - return base_sieve; - } - - sieve_segment(&base_sieve, upper_limit) -} - -/// Returns an array of size `N` where the value at a given index indicates whether the index is prime. -/// -/// Uses a sieve of Eratosthenes. -/// -/// # Example -/// ``` -/// # use const_primes::are_prime; -/// const PRIMALITY: [bool; 10] = are_prime(); -/// // 0 1 2 3 4 5 6 7 8 9 -/// assert_eq!(PRIMALITY, [false, false, true, true, false, true, false, true, false, false]); -/// ``` -#[must_use = "the function only returns a new value"] -pub const fn are_prime() -> [bool; N] { - let mut sieve = [true; N]; - if N > 0 { - sieve[0] = false; - } - if N > 1 { - sieve[1] = false; - } - - let mut number: usize = 2; - let bound = isqrt(N as u64); - // For all numbers up to and including sqrt(n): - while (number as u64) <= bound { - if sieve[number] { - // If a number is prime we enumerate all multiples of it - // starting from its square, - let Some(mut composite) = number.checked_mul(number) else { - break; - }; - - // and mark them as not prime. - while composite < N { - sieve[composite] = false; - composite = match composite.checked_add(number) { - Some(sum) => sum, - None => break, - }; - } - } - number += 1; - } - - sieve -} - /// Returns the value of the [Möbius function](https://en.wikipedia.org/wiki/M%C3%B6bius_function). /// /// This function is @@ -490,78 +152,6 @@ pub const fn moebius(mut x: u64) -> i8 { } } -/// Returns the largest prime smaller than or equal to `n` if there is one. -/// -/// Scans for primes downwards from the input with [`is_prime`]. -/// -/// # Examples -/// ``` -/// # use const_primes::largest_prime_leq; -/// const LPLEQ: Option = largest_prime_leq(400); -/// assert_eq!(LPLEQ, Some(397)); -/// ``` -/// There's no prime smaller than or equal to one -/// ``` -/// # use const_primes::largest_prime_leq; -/// const NOSUCH: Option = largest_prime_leq(1); -/// assert!(NOSUCH.is_none()); -/// ``` -#[must_use = "the function only returns a new value and does not modify its input"] -pub const fn largest_prime_leq(mut n: u64) -> Option { - if n == 0 || n == 1 { - None - } else if n == 2 { - Some(2) - } else { - if n % 2 == 0 { - n -= 1; - } - - while !is_prime(n) { - n -= 2; - } - - Some(n) - } -} - -/// Returns the smallest prime greater than or equal to `n` if there is one that -/// can be represented by a `u64`. -/// -/// Scans for primes upwards from the input with [`is_prime`]. -/// -/// # Example -/// ``` -/// # use const_primes::smallest_prime_geq; -/// const SPGEQ: Option = smallest_prime_geq(400); -/// assert_eq!(SPGEQ, Some(401)); -/// ``` -/// Primes larger than 18446744073709551557 can not be represented by a `u64` -/// ``` -/// # use const_primes::smallest_prime_geq; -/// const NOSUCH: Option = smallest_prime_geq(18_446_744_073_709_551_558); -/// assert!(NOSUCH.is_none()); -/// ``` -#[must_use = "the function only returns a new value and does not modify its input"] -pub const fn smallest_prime_geq(mut n: u64) -> Option { - // The largest prime smaller than 2^64 - if n > 18_446_744_073_709_551_557 { - None - } else if n <= 2 { - Some(2) - } else { - if n % 2 == 0 { - n += 1; - } - - while !is_prime(n) { - n += 2; - } - - Some(n) - } -} - /// Returns an array of size `N` where the value at a given index is how many primes are less than or equal to the index. /// /// Sieves primes with [`are_prime`] and then counts them. @@ -672,25 +262,6 @@ mod test { assert_eq!(moebius(u32::MAX.into()), -1); } - #[test] - fn check_are_prime_below() { - macro_rules! test_n_below_100 { - ($($n:expr),+) => { - $( - println!("{}", $n); - assert_eq!(&PRIMALITIES[100-$n..], are_prime_below::<$n>(100)); - )+ - }; - } - test_n_below_100!( - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, - 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99 - ); - } - #[test] fn verify_primes() { macro_rules! test_to_n { @@ -708,13 +279,13 @@ mod test { } #[test] - fn check_largest_primes_below() { + fn check_primes_below() { macro_rules! test_n_below_100 { ($($n:expr),+) => { $( { println!("{}", $n); - const P: [u64; $n] = largest_primes_below(100); + const P: [u64; $n] = primes_below(100); assert_eq!(PRECOMPUTED_PRIMES[25-$n..25], P.map(|i|i as u32)); } )+ @@ -723,9 +294,28 @@ mod test { test_n_below_100!(10, 15, 20); - assert_eq!([0, 0, 0, 0, 0, 2, 3, 5, 7], largest_primes_below(10)); + assert_eq!([0, 0, 0, 0, 0, 2, 3, 5, 7], primes_below(10)); + + assert_eq!([0, 2], primes_below(3)); + } - assert_eq!([0, 2], largest_primes_below(3)); + #[test] + fn check_are_prime_below() { + macro_rules! test_n_below_100 { + ($($n:expr),+) => { + $( + println!("{}", $n); + assert_eq!(&PRIMALITIES[100-$n..], are_prime_below::<$n>(100)); + )+ + }; + } + test_n_below_100!( + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99 + ); } #[test] diff --git a/src/next_prime.rs b/src/next_prime.rs new file mode 100644 index 0000000..c6d0116 --- /dev/null +++ b/src/next_prime.rs @@ -0,0 +1,73 @@ +use crate::is_prime; + +/// Returns the largest prime smaller than or equal to `n` if there is one. +/// +/// Scans for primes downwards from the input with [`is_prime`]. +/// +/// # Examples +/// ``` +/// # use const_primes::largest_prime_leq; +/// const LPLEQ: Option = largest_prime_leq(400); +/// assert_eq!(LPLEQ, Some(397)); +/// ``` +/// There's no prime smaller than or equal to one +/// ``` +/// # use const_primes::largest_prime_leq; +/// const NOSUCH: Option = largest_prime_leq(1); +/// assert!(NOSUCH.is_none()); +/// ``` +#[must_use = "the function only returns a new value and does not modify its input"] +pub const fn largest_prime_leq(mut n: u64) -> Option { + if n == 0 || n == 1 { + None + } else if n == 2 { + Some(2) + } else { + if n % 2 == 0 { + n -= 1; + } + + while !is_prime(n) { + n -= 2; + } + + Some(n) + } +} + +/// Returns the smallest prime greater than or equal to `n` if there is one that +/// can be represented by a `u64`. +/// +/// Scans for primes upwards from the input with [`is_prime`]. +/// +/// # Example +/// ``` +/// # use const_primes::smallest_prime_geq; +/// const SPGEQ: Option = smallest_prime_geq(400); +/// assert_eq!(SPGEQ, Some(401)); +/// ``` +/// Primes larger than 18446744073709551557 can not be represented by a `u64` +/// ``` +/// # use const_primes::smallest_prime_geq; +/// const NOSUCH: Option = smallest_prime_geq(18_446_744_073_709_551_558); +/// assert!(NOSUCH.is_none()); +/// ``` +#[must_use = "the function only returns a new value and does not modify its input"] +pub const fn smallest_prime_geq(mut n: u64) -> Option { + // The largest prime smaller than 2^64 + if n > 18_446_744_073_709_551_557 { + None + } else if n <= 2 { + Some(2) + } else { + if n % 2 == 0 { + n += 1; + } + + while !is_prime(n) { + n += 2; + } + + Some(n) + } +} diff --git a/src/sieve.rs b/src/sieve.rs new file mode 100644 index 0000000..0cef8e5 --- /dev/null +++ b/src/sieve.rs @@ -0,0 +1,159 @@ +use crate::isqrt; + +/// Uses the primalities of the first `N` integers in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. +pub const fn sieve_segment(base_sieve: &[bool; N], upper_limit: u64) -> [bool; N] { + let mut segment_sieve = [true; N]; + + let lower_limit = upper_limit - N as u64; + + // In case 0 and/or 1 are included in the upper sieve we need to treat them as a special case + // since they are not multiples of any prime in `base_sieve` even though they are not primes. + if lower_limit == 0 && N > 1 { + segment_sieve[0] = false; + segment_sieve[1] = false; + } else if lower_limit == 1 && N > 0 { + segment_sieve[0] = false; + } + + let mut i = 0; + while i < N { + if base_sieve[i] { + let prime = i as u64; + + // Find the smallest multiple of the prime larger than or equal to `lower_limit`. + let mut composite = (lower_limit / prime) * prime; + if composite < lower_limit { + composite += prime; + } + if composite == prime { + composite += prime; + } + + // Sieve all numbers in the segment that are multiples of the prime. + while composite < upper_limit { + segment_sieve[(composite - lower_limit) as usize] = false; + composite += prime; + } + } + i += 1; + } + + segment_sieve +} + +/// Returns an array of size `N` that indicates which of the integers in `[upper_limit - N, upper_limit)` are prime, +/// or in other words: the value at a given index represents whether `index + upper_limit - N` is prime. +/// +/// If you just want the prime status of the first `N` integers, see [`are_prime`]. +/// +/// Uses a sieve of Eratosthenes to sieve the first `N` integers +/// and then uses the result to sieve the output range if needed. +/// +/// # Examples +/// Basic usage +/// ``` +/// # use const_primes::are_prime_below; +/// const PRIME_STATUSES: [bool; 10] = are_prime_below(30); +/// +/// assert_eq!( +/// PRIME_STATUSES, +/// // 20 21 22 23 24 25 26 27 28 29 +/// [false, false, false, true, false, false, false, false, false, true], +/// ); +/// ``` +/// Sieve limited ranges of very large values +/// ``` +/// # use const_primes::are_prime_below; +/// const BIG_NUMBER: u64 = 5_000_000_031; +/// const CEIL_SQRT_BIG_NUMBER: usize = 70711; +/// const BIG_PRIME_STATUSES: [bool; CEIL_SQRT_BIG_NUMBER] = are_prime_below(BIG_NUMBER); +/// assert_eq!( +/// BIG_PRIME_STATUSES[CEIL_SQRT_BIG_NUMBER - 3..], +/// // 5_000_000_028 5_000_000_029 5_000_000_030 +/// [false, true, false], +/// ); +/// ``` +/// +/// # Panics +/// Panics if `upper_limit` is not in the range `[N, N^2]`, or if `N^2` overflows a `u64`. +/// These are compile errors in const contexts. +/// ```compile_fail +/// # use const_primes::are_prime_below; +/// const PRIME_STATUSES: [bool; 5] = are_prime_below(26); +/// ``` +/// ```compile_fail +/// # use const_primes::are_prime_below; +/// const PRIME_STATUSES: [bool; 5] = are_prime_below(4); +/// ``` +#[must_use = "the function returns a new value and does not modify its input"] +pub const fn are_prime_below(upper_limit: u64) -> [bool; N] { + let n64 = N as u64; + + // Since panics are compile time errors in const contexts + // we check all the preconditions here and panic early. + match n64.checked_mul(n64) { + Some(prod) => assert!( + upper_limit <= prod, + "`upper_limit` must be smaller than or equal to `N`^2" + ), + None => panic!("`N`^2 must fit in a `u64`"), + } + assert!(upper_limit >= n64, "`upper_limit` must be at least `N`"); + + // Use a normal sieve of Eratosthenes for the first N numbers. + let base_sieve: [bool; N] = are_prime(); + + if upper_limit == n64 { + // If we are not interested in sieving a larger range we can just return early. + return base_sieve; + } + + sieve_segment(&base_sieve, upper_limit) +} + +/// Returns an array of size `N` where the value at a given index indicates whether the index is prime. +/// +/// Uses a sieve of Eratosthenes. +/// +/// # Example +/// ``` +/// # use const_primes::are_prime; +/// const PRIMALITY: [bool; 10] = are_prime(); +/// // 0 1 2 3 4 5 6 7 8 9 +/// assert_eq!(PRIMALITY, [false, false, true, true, false, true, false, true, false, false]); +/// ``` +#[must_use = "the function only returns a new value"] +pub const fn are_prime() -> [bool; N] { + let mut sieve = [true; N]; + if N > 0 { + sieve[0] = false; + } + if N > 1 { + sieve[1] = false; + } + + let mut number: usize = 2; + let bound = isqrt(N as u64); + // For all numbers up to and including sqrt(n): + while (number as u64) <= bound { + if sieve[number] { + // If a number is prime we enumerate all multiples of it + // starting from its square, + let Some(mut composite) = number.checked_mul(number) else { + break; + }; + + // and mark them as not prime. + while composite < N { + sieve[composite] = false; + composite = match composite.checked_add(number) { + Some(sum) => sum, + None => break, + }; + } + } + number += 1; + } + + sieve +} From 07a592d3740808fd4ecd9e187d906a92cecb6c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 25 Oct 2023 16:41:05 +0200 Subject: [PATCH 014/212] Begin unifying names, complete `primes_greater_than_or_equal_to` --- src/generation.rs | 69 +++++++++++++++++++++++++++++------------------ src/lib.rs | 10 +++---- 2 files changed, 48 insertions(+), 31 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index ed4ef7e..49f7696 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -109,17 +109,17 @@ pub const fn primes() -> [Underlying; N] { /// # Example /// Basic usage: /// ``` -/// # use const_primes::primes_below; -/// const PRIMES: [u64; 10] = primes_below(100); +/// # use const_primes::primes_less_than; +/// const PRIMES: [u64; 10] = primes_less_than(100); /// /// assert_eq!(PRIMES, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::primes_below; +/// # use const_primes::primes_less_than; /// const N: usize = 70711; /// # #[allow(long_running_const_eval)] -/// const BIG_PRIMES: [u64; N] = primes_below(5_000_000_030); +/// const BIG_PRIMES: [u64; N] = primes_less_than(5_000_000_030); /// /// assert_eq!(&BIG_PRIMES[..3], &[4_998_417_421, 4_998_417_427, 4_998_417_443]); /// assert_eq!(&BIG_PRIMES[N - 3..], &[4_999_999_903, 4_999_999_937, 5_000_000_029]); @@ -127,8 +127,8 @@ pub const fn primes() -> [Underlying; N] { /// If there are not enough primes to fill the requested array, the first /// elements will have a value of zero: /// ``` -/// # use const_primes::primes_below; -/// const PRIMES: [u64; 9] = primes_below(10); +/// # use const_primes::primes_less_than; +/// const PRIMES: [u64; 9] = primes_less_than(10); /// /// assert_eq!(PRIMES, [0, 0, 0, 0, 0, 2, 3, 5, 7]); /// ``` @@ -136,14 +136,14 @@ pub const fn primes() -> [Underlying; N] { /// Panics if `upper_limit` is not in the range `(N, N^2]`. This is a compile error /// in const contexts: /// ```compile_fail -/// # use const_primes::primes_below; -/// const PRIMES: [u64; 5] = primes_below(5); +/// # use const_primes::primes_less_than; +/// const PRIMES: [u64; 5] = primes_less_than(5); /// ``` /// ```compile_fail -/// # use const_primes::primes_below; -/// const PRIMES: [u64; 5] = primes_below(26); +/// # use const_primes::primes_less_than; +/// const PRIMES: [u64; 5] = primes_less_than(26); /// ``` -pub const fn primes_below(mut upper_limit: u64) -> [u64; N] { +pub const fn primes_less_than(mut upper_limit: u64) -> [u64; N] { let n64 = N as u64; assert!(upper_limit > n64, "`upper_limit` must be larger than `N`"); match (n64).checked_mul(n64) { @@ -188,7 +188,33 @@ pub const fn primes_below(mut upper_limit: u64) -> [u64; N] { } /// Returns an array of the `N` smallest primes greater than or equal to `lower_limit`. -pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { +/// +/// This function will fill the output array from index 0 and stop generating primes if they exceed `N^2`. +/// In that case the remaining elements of the output array will be 0. +/// +/// # Example +/// Basic usage: +/// ``` +/// # use const_primes::primes_greater_than_or_equal_to; +/// const PRIMES: [u64; 5] = primes_greater_than_or_equal_to(10); +/// assert_eq!(PRIMES, [11, 13, 17, 19, 23]); +/// ``` +/// Compute larger primes without starting from zero: +/// ``` +/// # use const_primes::primes_greater_than_or_equal_to; +/// const N: usize = 71_000; +/// # #[allow(long_running_const_eval)] +/// const P: [u64; N] = primes_greater_than_or_equal_to(5_000_000_030); +/// assert_eq!(P[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); +/// assert_eq!(P[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); +/// ``` +/// Only primes smaller than or equal to `N^2` will be generated: +/// ``` +/// # use const_primes::primes_greater_than_or_equal_to; +/// const PRIMES: [u64; 3] = primes_greater_than_or_equal_to(5); +/// assert_eq!(PRIMES, [5, 7, 0]); +/// ``` +pub const fn primes_greater_than_or_equal_to(mut lower_limit: u64) -> [u64; N] { let n64 = N as u64; if n64.checked_mul(n64).is_none() { panic!("`N^2` must fit in a `u64`"); @@ -223,31 +249,22 @@ mod test { use super::*; #[test] - fn sanity_check_primes_geq() { + fn sanity_check_primes_greater_than_or_equal_to() { { - const P: [u64; 5] = primes_geq(10); + const P: [u64; 5] = primes_greater_than_or_equal_to(10); assert_eq!(P, [11, 13, 17, 19, 23]); } { - const P: [u64; 5] = primes_geq(0); + const P: [u64; 5] = primes_greater_than_or_equal_to(0); assert_eq!(P, [2, 3, 5, 7, 11]); } { - const P: [u64; 0] = primes_geq(0); + const P: [u64; 0] = primes_greater_than_or_equal_to(0); assert_eq!(P, []); } { - const P: [u64; 1] = primes_geq(0); + const P: [u64; 1] = primes_greater_than_or_equal_to(0); assert_eq!(P, [0]); } } - - #[test] - fn check_primes_geq_large() { - const N: usize = 71_000; - #[allow(long_running_const_eval)] - const P: [u64; N] = primes_geq(5_000_000_030); - assert_eq!(P[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); - assert_eq!(P[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); - } } diff --git a/src/lib.rs b/src/lib.rs index 0dd372c..fecd3a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,7 @@ mod next_prime; mod sieve; mod wrapper; -pub use generation::{primes, primes_below, primes_geq}; +pub use generation::{primes_less_than, primes, primes_greater_than_or_equal_to}; use imath::isqrt; pub use miller_rabin::is_prime; pub use next_prime::{largest_prime_leq, smallest_prime_geq}; @@ -279,13 +279,13 @@ mod test { } #[test] - fn check_primes_below() { + fn check_primes_less_than() { macro_rules! test_n_below_100 { ($($n:expr),+) => { $( { println!("{}", $n); - const P: [u64; $n] = primes_below(100); + const P: [u64; $n] = primes_less_than(100); assert_eq!(PRECOMPUTED_PRIMES[25-$n..25], P.map(|i|i as u32)); } )+ @@ -294,9 +294,9 @@ mod test { test_n_below_100!(10, 15, 20); - assert_eq!([0, 0, 0, 0, 0, 2, 3, 5, 7], primes_below(10)); + assert_eq!([0, 0, 0, 0, 0, 2, 3, 5, 7], primes_less_than(10)); - assert_eq!([0, 2], primes_below(3)); + assert_eq!([0, 2], primes_less_than(3)); } #[test] From 2670aa5b829e631e0386834e0dd7b95898e4edc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 25 Oct 2023 16:42:21 +0200 Subject: [PATCH 015/212] limit sieve_segment to the crate --- src/sieve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sieve.rs b/src/sieve.rs index 0cef8e5..5ba6ba9 100644 --- a/src/sieve.rs +++ b/src/sieve.rs @@ -1,7 +1,7 @@ use crate::isqrt; /// Uses the primalities of the first `N` integers in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. -pub const fn sieve_segment(base_sieve: &[bool; N], upper_limit: u64) -> [bool; N] { +pub(crate) const fn sieve_segment(base_sieve: &[bool; N], upper_limit: u64) -> [bool; N] { let mut segment_sieve = [true; N]; let lower_limit = upper_limit - N as u64; From 727fc55a4d43d58ea0064c2d7cdd201aefb3eb7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 25 Oct 2023 17:00:41 +0200 Subject: [PATCH 016/212] Finalized the naming convention for specifying output range --- benches/prime_benches.rs | 4 ++-- src/generation.rs | 18 +++++++-------- src/lib.rs | 48 +++++++++++++++++++++------------------- src/next_prime.rs | 20 ++++++++--------- src/sieve.rs | 33 ++++++++++++++------------- 5 files changed, 64 insertions(+), 59 deletions(-) diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index 29eaa95..26e9eac 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,4 +1,4 @@ -use const_primes::{are_prime, is_prime, moebius, primes}; +use const_primes::{is_prime, moebius, primes, sieve_numbers}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use rand::prelude::*; use std::hint::black_box; @@ -21,7 +21,7 @@ fn benchmarks(c: &mut Criterion) { }); c.bench_function("sieve 10000 integers", |b| { - b.iter(|| black_box(are_prime::<10_000>())) + b.iter(|| black_box(sieve_numbers::<10_000>())) }); let ints: Vec<_> = (1..1_000_000).map(|n| n).collect(); diff --git a/src/generation.rs b/src/generation.rs index 49f7696..f759158 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -1,8 +1,8 @@ -use crate::{are_prime, sieve::sieve_segment, Underlying}; +use crate::{sieve::sieve_segment, sieve_numbers, Underlying}; /// Returns the `N` first prime numbers. /// -/// [`Primes`] might be relevant for you if you intend to later use these prime numbers for related computations. +/// [`Primes`](crate::Primes) might be relevant for you if you intend to later use these prime numbers for related computations. /// /// Uses a [segmented sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve). /// @@ -35,7 +35,7 @@ pub const fn primes() -> [Underlying; N] { let mut prime_count = 0; // Sieve the first primes below N - let mut sieve: [bool; N] = are_prime(); + let mut sieve: [bool; N] = sieve_numbers(); // Count how many primes we found // and store them in the final array @@ -98,7 +98,7 @@ pub const fn primes() -> [Underlying; N] { primes } -/// Returns the `N` largest primes below the given upper limit. +/// Returns the `N` largest primes less than `upper_limit`. /// /// The return array fills from the end until either it is full or there are no more primes. /// If the primes run out before the array is filled the first elements will have a value of zero. @@ -157,7 +157,7 @@ pub const fn primes_less_than(mut upper_limit: u64) -> [u64; N] let mut primes: [u64; N] = [0; N]; // This will be used to sieve all upper ranges. - let base_sieve: [bool; N] = are_prime(); + let base_sieve: [bool; N] = sieve_numbers(); let mut total_primes_found: usize = 0; 'generate: while total_primes_found < N && upper_limit > 2 { @@ -187,11 +187,11 @@ pub const fn primes_less_than(mut upper_limit: u64) -> [u64; N] primes } -/// Returns an array of the `N` smallest primes greater than or equal to `lower_limit`. -/// +/// Returns the `N` smallest primes greater than or equal to `lower_limit`. +/// /// This function will fill the output array from index 0 and stop generating primes if they exceed `N^2`. /// In that case the remaining elements of the output array will be 0. -/// +/// /// # Example /// Basic usage: /// ``` @@ -221,7 +221,7 @@ pub const fn primes_greater_than_or_equal_to(mut lower_limit: u6 } let mut primes = [0; N]; - let base_sieve: [bool; N] = are_prime(); + let base_sieve: [bool; N] = sieve_numbers(); let mut total_found_primes = 0; 'generate: while total_found_primes < N && lower_limit + n64 <= n64 * n64 { let mut largest_found_prime = primes[total_found_primes]; diff --git a/src/lib.rs b/src/lib.rs index fecd3a0..ead88ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,19 +50,19 @@ //! const CHECK: bool = is_prime(18_446_744_073_709_551_557); //! assert!(CHECK); //! ``` -//! or [`are_prime`] to compute the prime status of the `N` first integers, +//! or [`sieve_numbers`] to compute the prime status of the `N` first integers, //! ``` -//! # use const_primes::are_prime; +//! # use const_primes::sieve_numbers; //! const N: usize = 10; -//! const PRIME_STATUS: [bool; N] = are_prime(); +//! const PRIME_STATUS: [bool; N] = sieve_numbers(); //! // 0 1 2 3 4 5 6 7 8 9 //! assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, false, false]); //! ``` -//! or [`are_prime_below`] to compute the prime status of the `N` largest integers below a given value, +//! or [`sieve_numbers_less_than`] to compute the prime status of the `N` largest integers below a given value, //! ``` -//! # use const_primes::are_prime_below; +//! # use const_primes::sieve_numbers_less_than; //! const N: usize = 70711; -//! const BIG_PRIME_STATUS: [bool; N] = are_prime_below(5_000_000_031); +//! const BIG_PRIME_STATUS: [bool; N] = sieve_numbers_less_than(5_000_000_031); //! // 5_000_000_028 5_000_000_029 5_000_000_030 //! assert_eq!(BIG_PRIME_STATUS[N - 3..], [false, true, false]); //! ``` @@ -83,11 +83,13 @@ mod next_prime; mod sieve; mod wrapper; -pub use generation::{primes_less_than, primes, primes_greater_than_or_equal_to}; +pub use generation::{primes, primes_greater_than_or_equal_to, primes_less_than}; use imath::isqrt; pub use miller_rabin::is_prime; -pub use next_prime::{largest_prime_leq, smallest_prime_geq}; -pub use sieve::{are_prime, are_prime_below}; +pub use next_prime::{ + largest_prime_less_than_or_equal_to, smallest_prime_greater_than_or_equal_to, +}; +pub use sieve::{sieve_numbers, sieve_numbers_less_than}; pub use wrapper::Primes; /// Returns the value of the [Möbius function](https://en.wikipedia.org/wiki/M%C3%B6bius_function). @@ -154,7 +156,7 @@ pub const fn moebius(mut x: u64) -> i8 { /// Returns an array of size `N` where the value at a given index is how many primes are less than or equal to the index. /// -/// Sieves primes with [`are_prime`] and then counts them. +/// Sieves primes with [`sieve_numbers`] and then counts them. /// /// # Example /// Basic usage @@ -170,7 +172,7 @@ pub const fn prime_counts() -> [usize; N] { return counts; } counts[2] = 1; - let prime_status: [bool; N] = are_prime(); + let prime_status: [bool; N] = sieve_numbers(); let mut count = 1; let mut i = 3; while i < N { @@ -203,12 +205,12 @@ mod test { } #[test] - fn check_are_prime() { + fn check_sieve_numbers() { macro_rules! test_to { ($($n:expr),+) => { $( println!("{}", $n); - assert_eq!(&PRIMALITIES[..$n], are_prime::<$n>()); + assert_eq!(&PRIMALITIES[..$n], sieve_numbers::<$n>()); )+ }; } @@ -300,12 +302,12 @@ mod test { } #[test] - fn check_are_prime_below() { + fn check_sieve_numbers_less_than() { macro_rules! test_n_below_100 { ($($n:expr),+) => { $( println!("{}", $n); - assert_eq!(&PRIMALITIES[100-$n..], are_prime_below::<$n>(100)); + assert_eq!(&PRIMALITIES[100-$n..], sieve_numbers_less_than::<$n>(100)); )+ }; } @@ -322,21 +324,21 @@ mod test { fn check_next_prime() { for i in 1..PRECOMPUTED_PRIMES.len() - 1 { assert_eq!( - smallest_prime_geq(PRECOMPUTED_PRIMES[i] as u64 + 1), + smallest_prime_greater_than_or_equal_to(PRECOMPUTED_PRIMES[i] as u64 + 1), Some(PRECOMPUTED_PRIMES[i + 1] as u64) ); assert_eq!( - largest_prime_leq(PRECOMPUTED_PRIMES[i] as u64 - 1), + largest_prime_less_than_or_equal_to(PRECOMPUTED_PRIMES[i] as u64 - 1), Some(PRECOMPUTED_PRIMES[i - 1] as u64) ); } - assert_eq!(smallest_prime_geq(0), Some(2)); - assert_eq!(smallest_prime_geq(1), Some(2)); - assert_eq!(smallest_prime_geq(2), Some(2)); - assert_eq!(largest_prime_leq(0), None); - assert_eq!(largest_prime_leq(1), None); - assert_eq!(largest_prime_leq(2), Some(2)); + assert_eq!(smallest_prime_greater_than_or_equal_to(0), Some(2)); + assert_eq!(smallest_prime_greater_than_or_equal_to(1), Some(2)); + assert_eq!(smallest_prime_greater_than_or_equal_to(2), Some(2)); + assert_eq!(largest_prime_less_than_or_equal_to(0), None); + assert_eq!(largest_prime_less_than_or_equal_to(1), None); + assert_eq!(largest_prime_less_than_or_equal_to(2), Some(2)); } #[rustfmt::skip] diff --git a/src/next_prime.rs b/src/next_prime.rs index c6d0116..da6d479 100644 --- a/src/next_prime.rs +++ b/src/next_prime.rs @@ -6,18 +6,18 @@ use crate::is_prime; /// /// # Examples /// ``` -/// # use const_primes::largest_prime_leq; -/// const LPLEQ: Option = largest_prime_leq(400); +/// # use const_primes::largest_prime_less_than_or_equal_to; +/// const LPLEQ: Option = largest_prime_less_than_or_equal_to(400); /// assert_eq!(LPLEQ, Some(397)); /// ``` /// There's no prime smaller than or equal to one /// ``` -/// # use const_primes::largest_prime_leq; -/// const NOSUCH: Option = largest_prime_leq(1); +/// # use const_primes::largest_prime_less_than_or_equal_to; +/// const NOSUCH: Option = largest_prime_less_than_or_equal_to(1); /// assert!(NOSUCH.is_none()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn largest_prime_leq(mut n: u64) -> Option { +pub const fn largest_prime_less_than_or_equal_to(mut n: u64) -> Option { if n == 0 || n == 1 { None } else if n == 2 { @@ -42,18 +42,18 @@ pub const fn largest_prime_leq(mut n: u64) -> Option { /// /// # Example /// ``` -/// # use const_primes::smallest_prime_geq; -/// const SPGEQ: Option = smallest_prime_geq(400); +/// # use const_primes::smallest_prime_greater_than_or_equal_to; +/// const SPGEQ: Option = smallest_prime_greater_than_or_equal_to(400); /// assert_eq!(SPGEQ, Some(401)); /// ``` /// Primes larger than 18446744073709551557 can not be represented by a `u64` /// ``` -/// # use const_primes::smallest_prime_geq; -/// const NOSUCH: Option = smallest_prime_geq(18_446_744_073_709_551_558); +/// # use const_primes::smallest_prime_greater_than_or_equal_to; +/// const NOSUCH: Option = smallest_prime_greater_than_or_equal_to(18_446_744_073_709_551_558); /// assert!(NOSUCH.is_none()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn smallest_prime_geq(mut n: u64) -> Option { +pub const fn smallest_prime_greater_than_or_equal_to(mut n: u64) -> Option { // The largest prime smaller than 2^64 if n > 18_446_744_073_709_551_557 { None diff --git a/src/sieve.rs b/src/sieve.rs index 5ba6ba9..af7619f 100644 --- a/src/sieve.rs +++ b/src/sieve.rs @@ -1,7 +1,10 @@ use crate::isqrt; /// Uses the primalities of the first `N` integers in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. -pub(crate) const fn sieve_segment(base_sieve: &[bool; N], upper_limit: u64) -> [bool; N] { +pub(crate) const fn sieve_segment( + base_sieve: &[bool; N], + upper_limit: u64, +) -> [bool; N] { let mut segment_sieve = [true; N]; let lower_limit = upper_limit - N as u64; @@ -44,7 +47,7 @@ pub(crate) const fn sieve_segment(base_sieve: &[bool; N], upper_ /// Returns an array of size `N` that indicates which of the integers in `[upper_limit - N, upper_limit)` are prime, /// or in other words: the value at a given index represents whether `index + upper_limit - N` is prime. /// -/// If you just want the prime status of the first `N` integers, see [`are_prime`]. +/// If you just want the prime status of the first `N` integers, see [`sieve_numbers`]. /// /// Uses a sieve of Eratosthenes to sieve the first `N` integers /// and then uses the result to sieve the output range if needed. @@ -52,8 +55,8 @@ pub(crate) const fn sieve_segment(base_sieve: &[bool; N], upper_ /// # Examples /// Basic usage /// ``` -/// # use const_primes::are_prime_below; -/// const PRIME_STATUSES: [bool; 10] = are_prime_below(30); +/// # use const_primes::sieve_numbers_less_than; +/// const PRIME_STATUSES: [bool; 10] = sieve_numbers_less_than(30); /// /// assert_eq!( /// PRIME_STATUSES, @@ -63,10 +66,10 @@ pub(crate) const fn sieve_segment(base_sieve: &[bool; N], upper_ /// ``` /// Sieve limited ranges of very large values /// ``` -/// # use const_primes::are_prime_below; +/// # use const_primes::sieve_numbers_less_than; /// const BIG_NUMBER: u64 = 5_000_000_031; /// const CEIL_SQRT_BIG_NUMBER: usize = 70711; -/// const BIG_PRIME_STATUSES: [bool; CEIL_SQRT_BIG_NUMBER] = are_prime_below(BIG_NUMBER); +/// const BIG_PRIME_STATUSES: [bool; CEIL_SQRT_BIG_NUMBER] = sieve_numbers_less_than(BIG_NUMBER); /// assert_eq!( /// BIG_PRIME_STATUSES[CEIL_SQRT_BIG_NUMBER - 3..], /// // 5_000_000_028 5_000_000_029 5_000_000_030 @@ -78,15 +81,15 @@ pub(crate) const fn sieve_segment(base_sieve: &[bool; N], upper_ /// Panics if `upper_limit` is not in the range `[N, N^2]`, or if `N^2` overflows a `u64`. /// These are compile errors in const contexts. /// ```compile_fail -/// # use const_primes::are_prime_below; -/// const PRIME_STATUSES: [bool; 5] = are_prime_below(26); +/// # use const_primes::sieve_numbers_less_than; +/// const PRIME_STATUSES: [bool; 5] = sieve_numbers_less_than(26); /// ``` /// ```compile_fail -/// # use const_primes::are_prime_below; -/// const PRIME_STATUSES: [bool; 5] = are_prime_below(4); +/// # use const_primes::sieve_numbers_less_than; +/// const PRIME_STATUSES: [bool; 5] = sieve_numbers_less_than(4); /// ``` #[must_use = "the function returns a new value and does not modify its input"] -pub const fn are_prime_below(upper_limit: u64) -> [bool; N] { +pub const fn sieve_numbers_less_than(upper_limit: u64) -> [bool; N] { let n64 = N as u64; // Since panics are compile time errors in const contexts @@ -101,7 +104,7 @@ pub const fn are_prime_below(upper_limit: u64) -> [bool; N] { assert!(upper_limit >= n64, "`upper_limit` must be at least `N`"); // Use a normal sieve of Eratosthenes for the first N numbers. - let base_sieve: [bool; N] = are_prime(); + let base_sieve: [bool; N] = sieve_numbers(); if upper_limit == n64 { // If we are not interested in sieving a larger range we can just return early. @@ -117,13 +120,13 @@ pub const fn are_prime_below(upper_limit: u64) -> [bool; N] { /// /// # Example /// ``` -/// # use const_primes::are_prime; -/// const PRIMALITY: [bool; 10] = are_prime(); +/// # use const_primes::sieve_numbers; +/// const PRIMALITY: [bool; 10] = sieve_numbers(); /// // 0 1 2 3 4 5 6 7 8 9 /// assert_eq!(PRIMALITY, [false, false, true, true, false, true, false, true, false, false]); /// ``` #[must_use = "the function only returns a new value"] -pub const fn are_prime() -> [bool; N] { +pub const fn sieve_numbers() -> [bool; N] { let mut sieve = [true; N]; if N > 0 { sieve[0] = false; From ad8de4126f043b64d1f439dba5196dad489a0bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 25 Oct 2023 17:09:15 +0200 Subject: [PATCH 017/212] rename generation module to generate --- src/{generation.rs => generate.rs} | 0 src/lib.rs | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{generation.rs => generate.rs} (100%) diff --git a/src/generation.rs b/src/generate.rs similarity index 100% rename from src/generation.rs rename to src/generate.rs diff --git a/src/lib.rs b/src/lib.rs index ead88ce..5eb2295 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,14 +76,14 @@ // This is used since there is currently no way to be generic over types that can do arithmetic at compile time. type Underlying = u32; -mod generation; +mod generate; mod imath; mod miller_rabin; mod next_prime; mod sieve; mod wrapper; -pub use generation::{primes, primes_greater_than_or_equal_to, primes_less_than}; +pub use generate::{primes, primes_greater_than_or_equal_to, primes_less_than}; use imath::isqrt; pub use miller_rabin::is_prime; pub use next_prime::{ From 9cf3f12e0f6645fcf40b6ea01cdafbc3f5ada4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 25 Oct 2023 17:36:23 +0200 Subject: [PATCH 018/212] Add sieve_numbers_greater_than_or_equal_to --- src/lib.rs | 2 +- src/sieve.rs | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5eb2295..3dc640f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,7 @@ pub use miller_rabin::is_prime; pub use next_prime::{ largest_prime_less_than_or_equal_to, smallest_prime_greater_than_or_equal_to, }; -pub use sieve::{sieve_numbers, sieve_numbers_less_than}; +pub use sieve::{sieve_numbers, sieve_numbers_greater_than_or_equal_to, sieve_numbers_less_than}; pub use wrapper::Primes; /// Returns the value of the [Möbius function](https://en.wikipedia.org/wiki/M%C3%B6bius_function). diff --git a/src/sieve.rs b/src/sieve.rs index af7619f..21afb1f 100644 --- a/src/sieve.rs +++ b/src/sieve.rs @@ -78,8 +78,7 @@ pub(crate) const fn sieve_segment( /// ``` /// /// # Panics -/// Panics if `upper_limit` is not in the range `[N, N^2]`, or if `N^2` overflows a `u64`. -/// These are compile errors in const contexts. +/// Panics if `upper_limit` is not in the range `[N, N^2]`. In const contexts these are compile errors: /// ```compile_fail /// # use const_primes::sieve_numbers_less_than; /// const PRIME_STATUSES: [bool; 5] = sieve_numbers_less_than(26); @@ -160,3 +159,49 @@ pub const fn sieve_numbers() -> [bool; N] { sieve } + +/// Returns the prime status of the `N` smallest integers greater than or equal to `lower_limit`. +/// +/// # Example +/// Basic usage: +/// ``` +/// # use const_primes::sieve_numbers_greater_than_or_equal_to; +/// const PRIME_STATUS: [bool; 5] = sieve_numbers_greater_than_or_equal_to(10); +/// // 10 11 12 13 14 +/// assert_eq!(PRIME_STATUS, [false, true, false, true, false]); +/// ``` +/// # Panics +/// Panics if `N + lower_limit` is larger than `N^2`. In const contexts this is a compile error: +/// ```compile_fail +/// # use const_primes::sieve_numbers_greater_than_or_equal_to; +/// const P: [bool; 5] = sieve_numbers_greater_than_or_equal_to(21); +/// ``` +pub const fn sieve_numbers_greater_than_or_equal_to(lower_limit: u64) -> [bool; N] { + let n64 = N as u64; + + // Since panics are compile time errors in const contexts + // we check all the preconditions here and panic early. + let upper_limit = if let Some(sum) = n64.checked_add(lower_limit) { + sum + } else { + panic!("`N + lower_limit` must fit in a `u64`") + }; + if let Some(n_sqr) = n64.checked_mul(n64) { + assert!( + upper_limit <= n_sqr, + "`lower_limit + N` must be less than or equal to `N^2`" + ); + } else { + panic!("`N^2` must fit in a `u64`") + } + + let base_sieve: [bool; N] = sieve_numbers(); + + // If `lower_limit` is zero the upper range is the same as what we already sieved, + // so we return early. + if lower_limit == 0 { + return base_sieve; + } + + sieve_segment(&base_sieve, upper_limit) +} From e98b84959d0db954443eb07b2c3c5f03536be534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 26 Oct 2023 17:31:09 +0200 Subject: [PATCH 019/212] Remove the moebius function as it is out of scope for the crate --- benches/prime_benches.rs | 11 +----- src/lib.rs | 77 ---------------------------------------- src/sieve.rs | 6 ++-- 3 files changed, 4 insertions(+), 90 deletions(-) diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index 26e9eac..cd2c50f 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,4 +1,4 @@ -use const_primes::{is_prime, moebius, primes, sieve_numbers}; +use const_primes::{is_prime, primes, sieve_numbers}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use rand::prelude::*; use std::hint::black_box; @@ -23,15 +23,6 @@ fn benchmarks(c: &mut Criterion) { c.bench_function("sieve 10000 integers", |b| { b.iter(|| black_box(sieve_numbers::<10_000>())) }); - - let ints: Vec<_> = (1..1_000_000).map(|n| n).collect(); - c.bench_function("möbius of first 1e6 integers", |b| { - b.iter(|| { - for &i in &ints { - black_box(moebius(i)); - } - }) - }); } criterion_group!(benches, benchmarks); diff --git a/src/lib.rs b/src/lib.rs index 3dc640f..3a2246c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,68 +92,6 @@ pub use next_prime::{ pub use sieve::{sieve_numbers, sieve_numbers_greater_than_or_equal_to, sieve_numbers_less_than}; pub use wrapper::Primes; -/// Returns the value of the [Möbius function](https://en.wikipedia.org/wiki/M%C3%B6bius_function). -/// -/// This function is -/// - 1 if `x` is a square-free integer with an even number of prime factors, -/// - -1 if `x` is a square-free integer with an odd number of prime factors, -/// - 0 if `x` has a squared prime factor. -/// -/// Uses a small wheel to check prime factors up to `√x` and exits early if -/// there is a squared factor. -/// -/// # Example -/// ``` -/// # use const_primes::moebius; -/// const N: u64 = 1001; -/// const MÖBIUS1001: i8 = moebius(N); -/// assert_eq!(MÖBIUS1001, -1); -/// ``` -// I have added this code to Rosettacode: https://www.rosettacode.org/wiki/M%C3%B6bius_function#Rust -// as of the writing of this comment. -pub const fn moebius(mut x: u64) -> i8 { - let mut prime_count: u64 = 0; - - /// If x is divisible by the any of the given factors this macro counts the factor and divides it out. - /// It then returns zero if x is still divisible by the factor. - macro_rules! handle_factors { - ($($factor:expr),+) => { - $( - if x % $factor == 0 { - x /= $factor; - prime_count += 1; - // If x is still divisible by the factor that means x has a - // square or more prime factor, and we return 0. - if x % $factor == 0 { return 0; } - } - )+ - }; - } - - // Handle 2 and 3 separately, since the wheel will not find these factors. - handle_factors!(2, 3); - - // Handle the remaining factors <= √x with the wheel. - let mut i = 5; - let bound = isqrt(x); - while i <= bound { - handle_factors!(i, i + 2); - i += 6; - } - - // There can exist one prime factor larger than √x, - // in that case we can check if x is still larger than one, and then count it. - if x > 1 { - prime_count += 1; - } - - if prime_count % 2 == 0 { - 1 - } else { - -1 - } -} - /// Returns an array of size `N` where the value at a given index is how many primes are less than or equal to the index. /// /// Sieves primes with [`sieve_numbers`] and then counts them. @@ -249,21 +187,6 @@ mod test { test_prime_counts_up_to!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 50, 100); } - #[test] - fn check_möbius() { - #[rustfmt::skip] - const TEST_CASES: [i8; 51] = [0, 1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0, 1, 1, -1, 0, 0, 1, 0, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 0, -1, -1, -1, 0, 0, 1, -1, 0, 0, 0]; - for (n, ans) in TEST_CASES.into_iter().enumerate() { - assert_eq!(moebius(n as u64), ans); - } - #[rustfmt::skip] - const BIG_TEST_CASES: [i8; 51] = [0, -1, -1, 1, 0, -1, 1, 1, 0, -1, -1, 1, 0, -1, 0, -1, 0, 0, 1, -1, 0, -1, -1, -1, 0, 0, 0, 1, 0, 0, -1, -1, 0, -1, -1, 0, 0, 1, -1, -1, 0, 1, 1, 1, 0, -1, 1, 1, 0, -1, 0]; - for (n, ans) in BIG_TEST_CASES.into_iter().enumerate() { - assert_eq!(moebius(n as u64 + 1000), ans); - } - assert_eq!(moebius(u32::MAX.into()), -1); - } - #[test] fn verify_primes() { macro_rules! test_to_n { diff --git a/src/sieve.rs b/src/sieve.rs index 21afb1f..b1a4acd 100644 --- a/src/sieve.rs +++ b/src/sieve.rs @@ -161,7 +161,7 @@ pub const fn sieve_numbers() -> [bool; N] { } /// Returns the prime status of the `N` smallest integers greater than or equal to `lower_limit`. -/// +/// /// # Example /// Basic usage: /// ``` @@ -194,9 +194,9 @@ pub const fn sieve_numbers_greater_than_or_equal_to(lower_limit: } else { panic!("`N^2` must fit in a `u64`") } - + let base_sieve: [bool; N] = sieve_numbers(); - + // If `lower_limit` is zero the upper range is the same as what we already sieved, // so we return early. if lower_limit == 0 { From 2f98d59795e9215e88a75570da467dc9cb770098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 09:57:35 +0200 Subject: [PATCH 020/212] Remove panics section that is not needed --- src/generate.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/generate.rs b/src/generate.rs index f759158..e889161 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -12,8 +12,6 @@ use crate::{sieve::sieve_segment, sieve_numbers, Underlying}; /// const PRIMES: [u32; 10] = primes(); /// assert_eq!(PRIMES, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); /// ``` -/// # Panics -/// Panics if a computed prime overflows a `u32`. This will result in a compile error in a const context. #[must_use = "the function only returns a new value"] pub const fn primes() -> [Underlying; N] { if N == 0 { @@ -83,7 +81,7 @@ pub const fn primes() -> [Underlying; N] { primes[prime_count] = i as Underlying; prime_count += 1; // and stop the generation of primes if we're done. - if prime_count == N { + if prime_count >= N { break 'generate; } } From 97654518a362aca8e23144c4af8dded2adb4b648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 10:07:05 +0200 Subject: [PATCH 021/212] Remove '_numbers' from the names of sieving functions --- benches/prime_benches.rs | 4 +-- src/{generate.rs => generation.rs} | 8 +++--- src/lib.rs | 30 +++++++++++----------- src/{sieve.rs => sieving.rs} | 40 +++++++++++++++--------------- 4 files changed, 41 insertions(+), 41 deletions(-) rename src/{generate.rs => generation.rs} (97%) rename src/{sieve.rs => sieving.rs} (83%) diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index cd2c50f..f750a79 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,4 +1,4 @@ -use const_primes::{is_prime, primes, sieve_numbers}; +use const_primes::{is_prime, primes, sieve}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use rand::prelude::*; use std::hint::black_box; @@ -21,7 +21,7 @@ fn benchmarks(c: &mut Criterion) { }); c.bench_function("sieve 10000 integers", |b| { - b.iter(|| black_box(sieve_numbers::<10_000>())) + b.iter(|| black_box(sieve::<10_000>())) }); } diff --git a/src/generate.rs b/src/generation.rs similarity index 97% rename from src/generate.rs rename to src/generation.rs index e889161..40dadd7 100644 --- a/src/generate.rs +++ b/src/generation.rs @@ -1,4 +1,4 @@ -use crate::{sieve::sieve_segment, sieve_numbers, Underlying}; +use crate::{sieve, sieving::sieve_segment, Underlying}; /// Returns the `N` first prime numbers. /// @@ -33,7 +33,7 @@ pub const fn primes() -> [Underlying; N] { let mut prime_count = 0; // Sieve the first primes below N - let mut sieve: [bool; N] = sieve_numbers(); + let mut sieve: [bool; N] = sieve(); // Count how many primes we found // and store them in the final array @@ -155,7 +155,7 @@ pub const fn primes_less_than(mut upper_limit: u64) -> [u64; N] let mut primes: [u64; N] = [0; N]; // This will be used to sieve all upper ranges. - let base_sieve: [bool; N] = sieve_numbers(); + let base_sieve: [bool; N] = sieve(); let mut total_primes_found: usize = 0; 'generate: while total_primes_found < N && upper_limit > 2 { @@ -219,7 +219,7 @@ pub const fn primes_greater_than_or_equal_to(mut lower_limit: u6 } let mut primes = [0; N]; - let base_sieve: [bool; N] = sieve_numbers(); + let base_sieve: [bool; N] = sieve(); let mut total_found_primes = 0; 'generate: while total_found_primes < N && lower_limit + n64 <= n64 * n64 { let mut largest_found_prime = primes[total_found_primes]; diff --git a/src/lib.rs b/src/lib.rs index 3a2246c..cea24f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,19 +50,19 @@ //! const CHECK: bool = is_prime(18_446_744_073_709_551_557); //! assert!(CHECK); //! ``` -//! or [`sieve_numbers`] to compute the prime status of the `N` first integers, +//! or [`sieve`] to compute the prime status of the `N` first integers, //! ``` -//! # use const_primes::sieve_numbers; +//! # use const_primes::sieve; //! const N: usize = 10; -//! const PRIME_STATUS: [bool; N] = sieve_numbers(); +//! const PRIME_STATUS: [bool; N] = sieve(); //! // 0 1 2 3 4 5 6 7 8 9 //! assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, false, false]); //! ``` -//! or [`sieve_numbers_less_than`] to compute the prime status of the `N` largest integers below a given value, +//! or [`sieve_less_than`] to compute the prime status of the `N` largest integers below a given value, //! ``` -//! # use const_primes::sieve_numbers_less_than; +//! # use const_primes::sieve_less_than; //! const N: usize = 70711; -//! const BIG_PRIME_STATUS: [bool; N] = sieve_numbers_less_than(5_000_000_031); +//! const BIG_PRIME_STATUS: [bool; N] = sieve_less_than(5_000_000_031); //! // 5_000_000_028 5_000_000_029 5_000_000_030 //! assert_eq!(BIG_PRIME_STATUS[N - 3..], [false, true, false]); //! ``` @@ -76,25 +76,25 @@ // This is used since there is currently no way to be generic over types that can do arithmetic at compile time. type Underlying = u32; -mod generate; +mod generation; mod imath; mod miller_rabin; mod next_prime; -mod sieve; +mod sieving; mod wrapper; -pub use generate::{primes, primes_greater_than_or_equal_to, primes_less_than}; +pub use generation::{primes, primes_greater_than_or_equal_to, primes_less_than}; use imath::isqrt; pub use miller_rabin::is_prime; pub use next_prime::{ largest_prime_less_than_or_equal_to, smallest_prime_greater_than_or_equal_to, }; -pub use sieve::{sieve_numbers, sieve_numbers_greater_than_or_equal_to, sieve_numbers_less_than}; +pub use sieving::{sieve, sieve_greater_than_or_equal_to, sieve_less_than}; pub use wrapper::Primes; /// Returns an array of size `N` where the value at a given index is how many primes are less than or equal to the index. /// -/// Sieves primes with [`sieve_numbers`] and then counts them. +/// Sieves primes with [`sieve`] and then counts them. /// /// # Example /// Basic usage @@ -110,7 +110,7 @@ pub const fn prime_counts() -> [usize; N] { return counts; } counts[2] = 1; - let prime_status: [bool; N] = sieve_numbers(); + let prime_status: [bool; N] = sieve(); let mut count = 1; let mut i = 3; while i < N { @@ -143,12 +143,12 @@ mod test { } #[test] - fn check_sieve_numbers() { + fn check_sieve() { macro_rules! test_to { ($($n:expr),+) => { $( println!("{}", $n); - assert_eq!(&PRIMALITIES[..$n], sieve_numbers::<$n>()); + assert_eq!(&PRIMALITIES[..$n], sieve::<$n>()); )+ }; } @@ -230,7 +230,7 @@ mod test { ($($n:expr),+) => { $( println!("{}", $n); - assert_eq!(&PRIMALITIES[100-$n..], sieve_numbers_less_than::<$n>(100)); + assert_eq!(&PRIMALITIES[100-$n..], sieve_less_than::<$n>(100)); )+ }; } diff --git a/src/sieve.rs b/src/sieving.rs similarity index 83% rename from src/sieve.rs rename to src/sieving.rs index b1a4acd..10b477d 100644 --- a/src/sieve.rs +++ b/src/sieving.rs @@ -47,7 +47,7 @@ pub(crate) const fn sieve_segment( /// Returns an array of size `N` that indicates which of the integers in `[upper_limit - N, upper_limit)` are prime, /// or in other words: the value at a given index represents whether `index + upper_limit - N` is prime. /// -/// If you just want the prime status of the first `N` integers, see [`sieve_numbers`]. +/// If you just want the prime status of the first `N` integers, see [`sieve`]. /// /// Uses a sieve of Eratosthenes to sieve the first `N` integers /// and then uses the result to sieve the output range if needed. @@ -55,8 +55,8 @@ pub(crate) const fn sieve_segment( /// # Examples /// Basic usage /// ``` -/// # use const_primes::sieve_numbers_less_than; -/// const PRIME_STATUSES: [bool; 10] = sieve_numbers_less_than(30); +/// # use const_primes::sieve_less_than; +/// const PRIME_STATUSES: [bool; 10] = sieve_less_than(30); /// /// assert_eq!( /// PRIME_STATUSES, @@ -66,10 +66,10 @@ pub(crate) const fn sieve_segment( /// ``` /// Sieve limited ranges of very large values /// ``` -/// # use const_primes::sieve_numbers_less_than; +/// # use const_primes::sieve_less_than; /// const BIG_NUMBER: u64 = 5_000_000_031; /// const CEIL_SQRT_BIG_NUMBER: usize = 70711; -/// const BIG_PRIME_STATUSES: [bool; CEIL_SQRT_BIG_NUMBER] = sieve_numbers_less_than(BIG_NUMBER); +/// const BIG_PRIME_STATUSES: [bool; CEIL_SQRT_BIG_NUMBER] = sieve_less_than(BIG_NUMBER); /// assert_eq!( /// BIG_PRIME_STATUSES[CEIL_SQRT_BIG_NUMBER - 3..], /// // 5_000_000_028 5_000_000_029 5_000_000_030 @@ -80,15 +80,15 @@ pub(crate) const fn sieve_segment( /// # Panics /// Panics if `upper_limit` is not in the range `[N, N^2]`. In const contexts these are compile errors: /// ```compile_fail -/// # use const_primes::sieve_numbers_less_than; -/// const PRIME_STATUSES: [bool; 5] = sieve_numbers_less_than(26); +/// # use const_primes::sieve_less_than; +/// const PRIME_STATUSES: [bool; 5] = sieve_less_than(26); /// ``` /// ```compile_fail -/// # use const_primes::sieve_numbers_less_than; -/// const PRIME_STATUSES: [bool; 5] = sieve_numbers_less_than(4); +/// # use const_primes::sieve_less_than; +/// const PRIME_STATUSES: [bool; 5] = sieve_less_than(4); /// ``` #[must_use = "the function returns a new value and does not modify its input"] -pub const fn sieve_numbers_less_than(upper_limit: u64) -> [bool; N] { +pub const fn sieve_less_than(upper_limit: u64) -> [bool; N] { let n64 = N as u64; // Since panics are compile time errors in const contexts @@ -103,7 +103,7 @@ pub const fn sieve_numbers_less_than(upper_limit: u64) -> [bool; assert!(upper_limit >= n64, "`upper_limit` must be at least `N`"); // Use a normal sieve of Eratosthenes for the first N numbers. - let base_sieve: [bool; N] = sieve_numbers(); + let base_sieve: [bool; N] = sieve(); if upper_limit == n64 { // If we are not interested in sieving a larger range we can just return early. @@ -119,13 +119,13 @@ pub const fn sieve_numbers_less_than(upper_limit: u64) -> [bool; /// /// # Example /// ``` -/// # use const_primes::sieve_numbers; -/// const PRIMALITY: [bool; 10] = sieve_numbers(); +/// # use const_primes::sieve; +/// const PRIMALITY: [bool; 10] = sieve(); /// // 0 1 2 3 4 5 6 7 8 9 /// assert_eq!(PRIMALITY, [false, false, true, true, false, true, false, true, false, false]); /// ``` #[must_use = "the function only returns a new value"] -pub const fn sieve_numbers() -> [bool; N] { +pub const fn sieve() -> [bool; N] { let mut sieve = [true; N]; if N > 0 { sieve[0] = false; @@ -165,18 +165,18 @@ pub const fn sieve_numbers() -> [bool; N] { /// # Example /// Basic usage: /// ``` -/// # use const_primes::sieve_numbers_greater_than_or_equal_to; -/// const PRIME_STATUS: [bool; 5] = sieve_numbers_greater_than_or_equal_to(10); +/// # use const_primes::sieve_greater_than_or_equal_to; +/// const PRIME_STATUS: [bool; 5] = sieve_greater_than_or_equal_to(10); /// // 10 11 12 13 14 /// assert_eq!(PRIME_STATUS, [false, true, false, true, false]); /// ``` /// # Panics /// Panics if `N + lower_limit` is larger than `N^2`. In const contexts this is a compile error: /// ```compile_fail -/// # use const_primes::sieve_numbers_greater_than_or_equal_to; -/// const P: [bool; 5] = sieve_numbers_greater_than_or_equal_to(21); +/// # use const_primes::sieve_greater_than_or_equal_to; +/// const P: [bool; 5] = sieve_greater_than_or_equal_to(21); /// ``` -pub const fn sieve_numbers_greater_than_or_equal_to(lower_limit: u64) -> [bool; N] { +pub const fn sieve_greater_than_or_equal_to(lower_limit: u64) -> [bool; N] { let n64 = N as u64; // Since panics are compile time errors in const contexts @@ -195,7 +195,7 @@ pub const fn sieve_numbers_greater_than_or_equal_to(lower_limit: panic!("`N^2` must fit in a `u64`") } - let base_sieve: [bool; N] = sieve_numbers(); + let base_sieve: [bool; N] = sieve(); // If `lower_limit` is zero the upper range is the same as what we already sieved, // so we return early. From 5ee65ba78fd154be051671a3464177e5ae125830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 10:13:59 +0200 Subject: [PATCH 022/212] Add test for sieve_geq --- src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index cea24f4..67f502d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -225,7 +225,7 @@ mod test { } #[test] - fn check_sieve_numbers_less_than() { + fn check_sieve_less_than() { macro_rules! test_n_below_100 { ($($n:expr),+) => { $( @@ -243,6 +243,19 @@ mod test { ); } + #[test] + fn check_sieve_geq() { + macro_rules! test_n_geq_10 { + ($($n:expr),+) => { + $( + println!("{}", $n); + assert_eq!(&PRIMALITIES[10..10+$n], sieve_greater_than_or_equal_to::<$n>(10)); + )+ + }; + } + test_n_geq_10!(4, 5, 10, 20, 30, 40, 50); + } + #[test] fn check_next_prime() { for i in 1..PRECOMPUTED_PRIMES.len() - 1 { From 22ce04bc4def74d18b0277ed772a3f08c6b56fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 10:19:30 +0200 Subject: [PATCH 023/212] Make some tests use the const eval --- src/lib.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 67f502d..3408fb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -147,8 +147,10 @@ mod test { macro_rules! test_to { ($($n:expr),+) => { $( - println!("{}", $n); - assert_eq!(&PRIMALITIES[..$n], sieve::<$n>()); + { + const P: [bool; $n] = sieve(); + assert_eq!(&PRIMALITIES[..$n], P); + } )+ }; } @@ -209,7 +211,6 @@ mod test { ($($n:expr),+) => { $( { - println!("{}", $n); const P: [u64; $n] = primes_less_than(100); assert_eq!(PRECOMPUTED_PRIMES[25-$n..25], P.map(|i|i as u32)); } @@ -229,8 +230,10 @@ mod test { macro_rules! test_n_below_100 { ($($n:expr),+) => { $( - println!("{}", $n); - assert_eq!(&PRIMALITIES[100-$n..], sieve_less_than::<$n>(100)); + { + const P: [bool; $n] = sieve_less_than(100); + assert_eq!(&PRIMALITIES[100-$n..], P); + } )+ }; } @@ -248,8 +251,10 @@ mod test { macro_rules! test_n_geq_10 { ($($n:expr),+) => { $( - println!("{}", $n); - assert_eq!(&PRIMALITIES[10..10+$n], sieve_greater_than_or_equal_to::<$n>(10)); + { + const P: [bool; $n] = sieve_greater_than_or_equal_to(10); + assert_eq!(&PRIMALITIES[10..10+$n], P); + } )+ }; } From ba516ec7afad7741ff2ad5ca4bddd8274c3129a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 10:29:50 +0200 Subject: [PATCH 024/212] Add example of sieve_geq to crate docstring --- src/lib.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3408fb9..74ed8ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,13 +58,16 @@ //! // 0 1 2 3 4 5 6 7 8 9 //! assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, false, false]); //! ``` -//! or [`sieve_less_than`] to compute the prime status of the `N` largest integers below a given value, +//! or [`sieve_less_than`] and [`sieve_greater_than_or_equal_to`] to compute the prime status of the integers below or above a given value, //! ``` -//! # use const_primes::sieve_less_than; -//! const N: usize = 70711; -//! const BIG_PRIME_STATUS: [bool; N] = sieve_less_than(5_000_000_031); -//! // 5_000_000_028 5_000_000_029 5_000_000_030 -//! assert_eq!(BIG_PRIME_STATUS[N - 3..], [false, true, false]); +//! # use const_primes::{sieve_less_than, sieve_greater_than_or_equal_to}; +//! const N: usize = 70800; +//! const LOW_BIG_PRIME_STATUS: [bool; N] = sieve_less_than(5_000_000_031); +//! const HIGH_BIG_PRIME_STATUS: [bool; N] = sieve_greater_than_or_equal_to(5_000_000_031); +//! // 5_000_000_028 5_000_000_029 5_000_000_030 +//! assert_eq!(LOW_BIG_PRIME_STATUS[N - 3..], [false, true, false]); +//! // 5_000_000_031 5_000_000_032 5_000_000_033 +//! assert_eq!(HIGH_BIG_PRIME_STATUS[..3], [false, false, false]); //! ``` //! and more! From 64b41c0d7afdbba176070d41f200892b51a1e22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 10:33:56 +0200 Subject: [PATCH 025/212] Add example of sieve_geq to README --- README.md | 17 ++++++++++------- src/lib.rs | 12 ++++++------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index ce4b1cd..86888c7 100644 --- a/README.md +++ b/README.md @@ -48,19 +48,22 @@ Use `is_prime` to test whether a given number is prime const CHECK: bool = is_prime(18_446_744_073_709_551_557); assert!(CHECK); ``` -or `are_prime` to compute the prime status of the `N` first integers, +or `sieve` to compute the prime status of the `N` first integers, ```rust const N: usize = 10; -const PRIME_STATUS: [bool; N] = are_prime(); +const PRIME_STATUS: [bool; N] = sieve(); // 0 1 2 3 4 5 6 7 8 9 assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, false, false]); ``` -or `are_prime_below` to compute the prime status of the `N` largest integers below a given value, +or `sieve_less_than` and `sieve_greater_than_or_equal_to` to compute the prime status of the integers below or above a given value, ```rust -const N: usize = 70711; -const BIG_PRIME_STATUS: [bool; N] = are_prime_below(5_000_000_031); -// 5_000_000_028 5_000_000_029 5_000_000_030 -assert_eq!(BIG_PRIME_STATUS[N - 3..], [false, true, false]); +const N: usize = 70800; +const PRIME_STATUS_BELOW: [bool; N] = sieve_less_than(5_000_000_031); +const PRIME_STATUS_ABOVE: [bool; N] = sieve_greater_than_or_equal_to(5_000_000_031); +// 5_000_000_028 5_000_000_029 5_000_000_030 +assert_eq!(PRIME_STATUS_BELOW[N - 3..], [false, true, false]); +// 5_000_000_031 5_000_000_032 5_000_000_033 +assert_eq!(PRIME_STATUS_ABOVE[..3], [false, false, false]); ``` and more! diff --git a/src/lib.rs b/src/lib.rs index 74ed8ed..961ee04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,12 +62,12 @@ //! ``` //! # use const_primes::{sieve_less_than, sieve_greater_than_or_equal_to}; //! const N: usize = 70800; -//! const LOW_BIG_PRIME_STATUS: [bool; N] = sieve_less_than(5_000_000_031); -//! const HIGH_BIG_PRIME_STATUS: [bool; N] = sieve_greater_than_or_equal_to(5_000_000_031); -//! // 5_000_000_028 5_000_000_029 5_000_000_030 -//! assert_eq!(LOW_BIG_PRIME_STATUS[N - 3..], [false, true, false]); -//! // 5_000_000_031 5_000_000_032 5_000_000_033 -//! assert_eq!(HIGH_BIG_PRIME_STATUS[..3], [false, false, false]); +//! const PRIME_STATUS_BELOW: [bool; N] = sieve_less_than(5_000_000_031); +//! const PRIME_STATUS_ABOVE: [bool; N] = sieve_greater_than_or_equal_to(5_000_000_031); +//! // 5_000_000_028 5_000_000_029 5_000_000_030 +//! assert_eq!(PRIME_STATUS_BELOW[N - 3..], [false, true, false]); +//! // 5_000_000_031 5_000_000_032 5_000_000_033 +//! assert_eq!(PRIME_STATUS_ABOVE[..3], [false, false, false]); //! ``` //! and more! From fd8567e5524ee33e240e47f675a4f9670782d9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 10:48:14 +0200 Subject: [PATCH 026/212] Add benchmark to all prime generation functions --- benches/prime_benches.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index f750a79..2df5b2a 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,12 +1,21 @@ -use const_primes::{is_prime, primes, sieve}; +use const_primes::{is_prime, primes, primes_greater_than_or_equal_to, primes_less_than, sieve}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use rand::prelude::*; use std::hint::black_box; fn benchmarks(c: &mut Criterion) { - c.bench_function("generate 10000 primes", |b| { - b.iter(|| black_box(primes::<10_000>())) - }); + { + let mut prime_generation = c.benchmark_group("prime generation"); + prime_generation.bench_function("first 10000 primes", |b| { + b.iter(|| black_box(primes::<10_000>())) + }); + prime_generation.bench_function("10000 primes < 100000000", |b| { + b.iter(|| black_box(primes_less_than::<10000>(100000000))) + }); + prime_generation.bench_function("10000 primes >= 99990000", |b| { + b.iter(|| black_box(primes_greater_than_or_equal_to::<10000>(99990000))) + }); + } c.bench_function("is_prime on random numbers", |b| { b.iter_batched( From ae2c7bff5f655b7542b3ff63e5c10c90294c92fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 10:52:05 +0200 Subject: [PATCH 027/212] Add benchmark to all sieve functions --- benches/prime_benches.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index 2df5b2a..9d1109e 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,4 +1,4 @@ -use const_primes::{is_prime, primes, primes_greater_than_or_equal_to, primes_less_than, sieve}; +use const_primes::{is_prime, primes, primes_greater_than_or_equal_to, primes_less_than, sieve, sieve_less_than, sieve_greater_than_or_equal_to}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use rand::prelude::*; use std::hint::black_box; @@ -17,7 +17,7 @@ fn benchmarks(c: &mut Criterion) { }); } - c.bench_function("is_prime on random numbers", |b| { + c.bench_function("is_prime on 10000 random numbers", |b| { b.iter_batched( || (0..10_000).map(|_| random()).collect::>(), |data| { @@ -29,9 +29,18 @@ fn benchmarks(c: &mut Criterion) { ) }); - c.bench_function("sieve 10000 integers", |b| { - b.iter(|| black_box(sieve::<10_000>())) - }); + { + let mut sieving = c.benchmark_group("prime sieving"); + sieving.bench_function("first 10000 integers", |b| { + b.iter(|| black_box(sieve::<10_000>())) + }); + sieving.bench_function("10000 integers < 100000000", |b| { + b.iter(|| black_box(sieve_less_than::<10_000>(100000000))) + }); + sieving.bench_function("10000 integers >= 99990000", |b| { + b.iter(|| black_box(sieve_greater_than_or_equal_to::<10_000>(99990000))) + }); + } } criterion_group!(benches, benchmarks); From 011aa7b8ef62fbee916d1229f77758a08aeaba1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 11:01:42 +0200 Subject: [PATCH 028/212] Name benchmark groups programmatically --- benches/prime_benches.rs | 60 +++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index 9d1109e..2a5ca5f 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,44 +1,54 @@ -use const_primes::{is_prime, primes, primes_greater_than_or_equal_to, primes_less_than, sieve, sieve_less_than, sieve_greater_than_or_equal_to}; -use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; +use const_primes::{ + is_prime, primes, primes_greater_than_or_equal_to, primes_less_than, sieve, + sieve_greater_than_or_equal_to, sieve_less_than, +}; +use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; use rand::prelude::*; use std::hint::black_box; fn benchmarks(c: &mut Criterion) { { + const N: usize = 10_000; let mut prime_generation = c.benchmark_group("prime generation"); - prime_generation.bench_function("first 10000 primes", |b| { - b.iter(|| black_box(primes::<10_000>())) + prime_generation.bench_function(format!("first {N} primes"), |b| { + b.iter(|| black_box(primes::())) }); - prime_generation.bench_function("10000 primes < 100000000", |b| { - b.iter(|| black_box(primes_less_than::<10000>(100000000))) + prime_generation.bench_function(format!("{N} primes < 100000000"), |b| { + b.iter(|| black_box(primes_less_than::(100000000))) }); - prime_generation.bench_function("10000 primes >= 99990000", |b| { - b.iter(|| black_box(primes_greater_than_or_equal_to::<10000>(99990000))) + prime_generation.bench_function(format!("{N} primes >= 99990000"), |b| { + b.iter(|| black_box(primes_greater_than_or_equal_to::(99990000))) }); } - c.bench_function("is_prime on 10000 random numbers", |b| { - b.iter_batched( - || (0..10_000).map(|_| random()).collect::>(), - |data| { - for number in data.iter() { - black_box(is_prime(*number)); - } - }, - BatchSize::SmallInput, - ) - }); + { + const N: u64 = 10_000; + let mut primality_testing = c.benchmark_group("primality testing"); + primality_testing.throughput(Throughput::Elements(N)); + primality_testing.bench_function(format!("is_prime on {N} random numbers"), |b| { + b.iter_batched( + || (0..N).map(|_| random()).collect::>(), + |data| { + for number in data.iter() { + black_box(is_prime(*number)); + } + }, + BatchSize::SmallInput, + ) + }); + } { + const N: usize = 10_000; let mut sieving = c.benchmark_group("prime sieving"); - sieving.bench_function("first 10000 integers", |b| { - b.iter(|| black_box(sieve::<10_000>())) + sieving.bench_function(format!("first {N} integers"), |b| { + b.iter(|| black_box(sieve::())) }); - sieving.bench_function("10000 integers < 100000000", |b| { - b.iter(|| black_box(sieve_less_than::<10_000>(100000000))) + sieving.bench_function(format!("{N} integers < 100000000"), |b| { + b.iter(|| black_box(sieve_less_than::(100000000))) }); - sieving.bench_function("10000 integers >= 99990000", |b| { - b.iter(|| black_box(sieve_greater_than_or_equal_to::<10_000>(99990000))) + sieving.bench_function(format!("{N} integers >= 99990000"), |b| { + b.iter(|| black_box(sieve_greater_than_or_equal_to::(99990000))) }); } } From 9023e5da5d34803590161df0d2d64948859dbc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 12:24:09 +0200 Subject: [PATCH 029/212] Add runtime tests to some functions so that codecov can see it --- src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 961ee04..b1ff8ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -153,6 +153,7 @@ mod test { { const P: [bool; $n] = sieve(); assert_eq!(&PRIMALITIES[..$n], P); + assert_eq!(&PRIMALITIES[..$n], sieve::<$n>()); } )+ }; @@ -182,8 +183,8 @@ mod test { i += 1; } } - assert_eq!(PRIME_COUNTS, counts); + assert_eq!(counts, prime_counts()); } )+ }; @@ -198,8 +199,9 @@ mod test { ($($n:expr),+) => { $( { - const PRIMES: [Underlying; $n] = $crate::primes(); + const PRIMES: [Underlying; $n] = primes(); assert_eq!(PRIMES, PRECOMPUTED_PRIMES[..$n]); + assert_eq!(PRECOMPUTED_PRIMES[..$n], primes::<$n>()); } )+ }; @@ -216,6 +218,10 @@ mod test { { const P: [u64; $n] = primes_less_than(100); assert_eq!(PRECOMPUTED_PRIMES[25-$n..25], P.map(|i|i as u32)); + assert_eq!( + PRECOMPUTED_PRIMES[25-$n..25], + primes_less_than::<$n>(100).into_iter().map(|i|i as u32).collect::>() + ); } )+ }; @@ -236,6 +242,7 @@ mod test { { const P: [bool; $n] = sieve_less_than(100); assert_eq!(&PRIMALITIES[100-$n..], P); + assert_eq!(&PRIMALITIES[100-$n..], sieve_less_than::<$n>(100)); } )+ }; @@ -257,6 +264,7 @@ mod test { { const P: [bool; $n] = sieve_greater_than_or_equal_to(10); assert_eq!(&PRIMALITIES[10..10+$n], P); + assert_eq!(&PRIMALITIES[10..10+$n], sieve_greater_than_or_equal_to::<$n>(10)); } )+ }; From 821475d04a22b29a751581f1c462f47189572f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 12:28:57 +0200 Subject: [PATCH 030/212] Add test of None path to smallest_prime_geq --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index b1ff8ce..224c3f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -285,6 +285,7 @@ mod test { ); } + assert_eq!(smallest_prime_greater_than_or_equal_to(18_446_744_073_709_551_558), None); assert_eq!(smallest_prime_greater_than_or_equal_to(0), Some(2)); assert_eq!(smallest_prime_greater_than_or_equal_to(1), Some(2)); assert_eq!(smallest_prime_greater_than_or_equal_to(2), Some(2)); From 5e335dd0e8eb5447549c6b3a55563c6d95dd7b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 12:40:18 +0200 Subject: [PATCH 031/212] cargo fmt --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 224c3f9..d14ffa7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -285,7 +285,10 @@ mod test { ); } - assert_eq!(smallest_prime_greater_than_or_equal_to(18_446_744_073_709_551_558), None); + assert_eq!( + smallest_prime_greater_than_or_equal_to(18_446_744_073_709_551_558), + None + ); assert_eq!(smallest_prime_greater_than_or_equal_to(0), Some(2)); assert_eq!(smallest_prime_greater_than_or_equal_to(1), Some(2)); assert_eq!(smallest_prime_greater_than_or_equal_to(2), Some(2)); From 6dfe3a31707b469cc725aecf41a98818c87871c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 12:40:49 +0200 Subject: [PATCH 032/212] Correct sieve_segment behaviour when the requested segment starts at zero, and add a test for it --- src/sieving.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/sieving.rs b/src/sieving.rs index 10b477d..16c0703 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -9,12 +9,12 @@ pub(crate) const fn sieve_segment( let lower_limit = upper_limit - N as u64; - // In case 0 and/or 1 are included in the upper sieve we need to treat them as a special case - // since they are not multiples of any prime in `base_sieve` even though they are not primes. if lower_limit == 0 && N > 1 { - segment_sieve[0] = false; - segment_sieve[1] = false; + // If the lower limit is 0 we can just return the base sieve. + return *base_sieve; } else if lower_limit == 1 && N > 0 { + // In case 1 is included in the upper sieve we need to treat it as a special case + // since it's not a multiple of any prime in `base_sieve` even though it's not prime. segment_sieve[0] = false; } @@ -205,3 +205,16 @@ pub const fn sieve_greater_than_or_equal_to(lower_limit: u64) -> sieve_segment(&base_sieve, upper_limit) } + +#[cfg(test)] +mod test { + use super::{sieve, sieve_segment}; + + #[test] + fn test_consistency_of_sieve_segment() { + const P: [bool; 10] = sieve_segment(&sieve(), 10); + const PP: [bool; 10] = sieve_segment(&sieve(), 11); + assert_eq!(P, sieve()); + assert_eq!(PP, sieve::<11>()[1..]); + } +} From 015a3a89c08cc77fb9716193b44a1580317c1f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 13:13:45 +0200 Subject: [PATCH 033/212] make next_prime functions always jump ahead --- src/lib.rs | 23 +++++++++++------------ src/next_prime.rs | 40 ++++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d14ffa7..34e8f70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,9 +89,7 @@ mod wrapper; pub use generation::{primes, primes_greater_than_or_equal_to, primes_less_than}; use imath::isqrt; pub use miller_rabin::is_prime; -pub use next_prime::{ - largest_prime_less_than_or_equal_to, smallest_prime_greater_than_or_equal_to, -}; +pub use next_prime::{largest_prime_less_than, smallest_prime_greater_than}; pub use sieving::{sieve, sieve_greater_than_or_equal_to, sieve_less_than}; pub use wrapper::Primes; @@ -276,25 +274,26 @@ mod test { fn check_next_prime() { for i in 1..PRECOMPUTED_PRIMES.len() - 1 { assert_eq!( - smallest_prime_greater_than_or_equal_to(PRECOMPUTED_PRIMES[i] as u64 + 1), + smallest_prime_greater_than(PRECOMPUTED_PRIMES[i] as u64), Some(PRECOMPUTED_PRIMES[i + 1] as u64) ); assert_eq!( - largest_prime_less_than_or_equal_to(PRECOMPUTED_PRIMES[i] as u64 - 1), + largest_prime_less_than(PRECOMPUTED_PRIMES[i] as u64), Some(PRECOMPUTED_PRIMES[i - 1] as u64) ); } assert_eq!( - smallest_prime_greater_than_or_equal_to(18_446_744_073_709_551_558), + smallest_prime_greater_than(18_446_744_073_709_551_558), None ); - assert_eq!(smallest_prime_greater_than_or_equal_to(0), Some(2)); - assert_eq!(smallest_prime_greater_than_or_equal_to(1), Some(2)); - assert_eq!(smallest_prime_greater_than_or_equal_to(2), Some(2)); - assert_eq!(largest_prime_less_than_or_equal_to(0), None); - assert_eq!(largest_prime_less_than_or_equal_to(1), None); - assert_eq!(largest_prime_less_than_or_equal_to(2), Some(2)); + assert_eq!(smallest_prime_greater_than(0), Some(2)); + assert_eq!(smallest_prime_greater_than(1), Some(2)); + assert_eq!(smallest_prime_greater_than(2), Some(3)); + assert_eq!(largest_prime_less_than(0), None); + assert_eq!(largest_prime_less_than(1), None); + assert_eq!(largest_prime_less_than(2), None); + assert_eq!(largest_prime_less_than(3), Some(2)); } #[rustfmt::skip] diff --git a/src/next_prime.rs b/src/next_prime.rs index da6d479..1037d57 100644 --- a/src/next_prime.rs +++ b/src/next_prime.rs @@ -1,28 +1,30 @@ use crate::is_prime; -/// Returns the largest prime smaller than or equal to `n` if there is one. +/// Returns the largest prime smaller than `n` if there is one. /// /// Scans for primes downwards from the input with [`is_prime`]. /// /// # Examples /// ``` -/// # use const_primes::largest_prime_less_than_or_equal_to; -/// const LPLEQ: Option = largest_prime_less_than_or_equal_to(400); +/// # use const_primes::largest_prime_less_than; +/// const LPLEQ: Option = largest_prime_less_than(400); /// assert_eq!(LPLEQ, Some(397)); /// ``` -/// There's no prime smaller than or equal to one +/// There's no prime smaller than two /// ``` -/// # use const_primes::largest_prime_less_than_or_equal_to; -/// const NOSUCH: Option = largest_prime_less_than_or_equal_to(1); +/// # use const_primes::largest_prime_less_than; +/// const NOSUCH: Option = largest_prime_less_than(2); /// assert!(NOSUCH.is_none()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn largest_prime_less_than_or_equal_to(mut n: u64) -> Option { - if n == 0 || n == 1 { +pub const fn largest_prime_less_than(mut n: u64) -> Option { + if n <= 2 { None - } else if n == 2 { + } else if n == 3 { Some(2) } else { + n -= 1; + if n % 2 == 0 { n -= 1; } @@ -35,31 +37,33 @@ pub const fn largest_prime_less_than_or_equal_to(mut n: u64) -> Option { } } -/// Returns the smallest prime greater than or equal to `n` if there is one that +/// Returns the smallest prime greater than `n` if there is one that /// can be represented by a `u64`. /// /// Scans for primes upwards from the input with [`is_prime`]. /// /// # Example /// ``` -/// # use const_primes::smallest_prime_greater_than_or_equal_to; -/// const SPGEQ: Option = smallest_prime_greater_than_or_equal_to(400); +/// # use const_primes::smallest_prime_greater_than; +/// const SPGEQ: Option = smallest_prime_greater_than(400); /// assert_eq!(SPGEQ, Some(401)); /// ``` /// Primes larger than 18446744073709551557 can not be represented by a `u64` /// ``` -/// # use const_primes::smallest_prime_greater_than_or_equal_to; -/// const NOSUCH: Option = smallest_prime_greater_than_or_equal_to(18_446_744_073_709_551_558); +/// # use const_primes::smallest_prime_greater_than; +/// const NOSUCH: Option = smallest_prime_greater_than(18_446_744_073_709_551_557); /// assert!(NOSUCH.is_none()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn smallest_prime_greater_than_or_equal_to(mut n: u64) -> Option { - // The largest prime smaller than 2^64 - if n > 18_446_744_073_709_551_557 { +pub const fn smallest_prime_greater_than(mut n: u64) -> Option { + // The largest prime smaller than u64::MAX + if n >= 18_446_744_073_709_551_557 { None - } else if n <= 2 { + } else if n <= 1 { Some(2) } else { + n += 1; + if n % 2 == 0 { n += 1; } From 8bfad4c050ccfc5c0d77d22528871a48a27d1f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 13:21:44 +0200 Subject: [PATCH 034/212] Improve docstrings of next_prime functions --- src/next_prime.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/next_prime.rs b/src/next_prime.rs index 1037d57..e55d341 100644 --- a/src/next_prime.rs +++ b/src/next_prime.rs @@ -5,12 +5,13 @@ use crate::is_prime; /// Scans for primes downwards from the input with [`is_prime`]. /// /// # Examples +/// Basic usage: /// ``` /// # use const_primes::largest_prime_less_than; /// const LPLEQ: Option = largest_prime_less_than(400); /// assert_eq!(LPLEQ, Some(397)); /// ``` -/// There's no prime smaller than two +/// There's no prime smaller than two: /// ``` /// # use const_primes::largest_prime_less_than; /// const NOSUCH: Option = largest_prime_less_than(2); @@ -43,12 +44,13 @@ pub const fn largest_prime_less_than(mut n: u64) -> Option { /// Scans for primes upwards from the input with [`is_prime`]. /// /// # Example +/// Basic usage: /// ``` /// # use const_primes::smallest_prime_greater_than; /// const SPGEQ: Option = smallest_prime_greater_than(400); /// assert_eq!(SPGEQ, Some(401)); /// ``` -/// Primes larger than 18446744073709551557 can not be represented by a `u64` +/// Primes larger than 18446744073709551557 can not be represented by a `u64`: /// ``` /// # use const_primes::smallest_prime_greater_than; /// const NOSUCH: Option = smallest_prime_greater_than(18_446_744_073_709_551_557); From 5b5f9ebcc5687301eecd5e85364921da81142fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 14:32:45 +0200 Subject: [PATCH 035/212] Shorten names --- benches/prime_benches.rs | 13 ++++----- src/generation.rs | 46 ++++++++++++++++---------------- src/lib.rs | 57 +++++++++++++++++++--------------------- src/next_prime.rs | 20 +++++++------- src/sieving.rs | 28 ++++++++++---------- 5 files changed, 79 insertions(+), 85 deletions(-) diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index 2a5ca5f..fe8fa80 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,7 +1,4 @@ -use const_primes::{ - is_prime, primes, primes_greater_than_or_equal_to, primes_less_than, sieve, - sieve_greater_than_or_equal_to, sieve_less_than, -}; +use const_primes::{is_prime, primes, primes_geq, primes_lt, sieve, sieve_geq, sieve_lt}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; use rand::prelude::*; use std::hint::black_box; @@ -14,10 +11,10 @@ fn benchmarks(c: &mut Criterion) { b.iter(|| black_box(primes::())) }); prime_generation.bench_function(format!("{N} primes < 100000000"), |b| { - b.iter(|| black_box(primes_less_than::(100000000))) + b.iter(|| black_box(primes_lt::(100000000))) }); prime_generation.bench_function(format!("{N} primes >= 99990000"), |b| { - b.iter(|| black_box(primes_greater_than_or_equal_to::(99990000))) + b.iter(|| black_box(primes_geq::(99990000))) }); } @@ -45,10 +42,10 @@ fn benchmarks(c: &mut Criterion) { b.iter(|| black_box(sieve::())) }); sieving.bench_function(format!("{N} integers < 100000000"), |b| { - b.iter(|| black_box(sieve_less_than::(100000000))) + b.iter(|| black_box(sieve_lt::(100000000))) }); sieving.bench_function(format!("{N} integers >= 99990000"), |b| { - b.iter(|| black_box(sieve_greater_than_or_equal_to::(99990000))) + b.iter(|| black_box(sieve_geq::(99990000))) }); } } diff --git a/src/generation.rs b/src/generation.rs index 40dadd7..65c81ff 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -107,17 +107,17 @@ pub const fn primes() -> [Underlying; N] { /// # Example /// Basic usage: /// ``` -/// # use const_primes::primes_less_than; -/// const PRIMES: [u64; 10] = primes_less_than(100); +/// # use const_primes::primes_lt; +/// const PRIMES: [u64; 10] = primes_lt(100); /// /// assert_eq!(PRIMES, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::primes_less_than; +/// # use const_primes::primes_lt; /// const N: usize = 70711; /// # #[allow(long_running_const_eval)] -/// const BIG_PRIMES: [u64; N] = primes_less_than(5_000_000_030); +/// const BIG_PRIMES: [u64; N] = primes_lt(5_000_000_030); /// /// assert_eq!(&BIG_PRIMES[..3], &[4_998_417_421, 4_998_417_427, 4_998_417_443]); /// assert_eq!(&BIG_PRIMES[N - 3..], &[4_999_999_903, 4_999_999_937, 5_000_000_029]); @@ -125,8 +125,8 @@ pub const fn primes() -> [Underlying; N] { /// If there are not enough primes to fill the requested array, the first /// elements will have a value of zero: /// ``` -/// # use const_primes::primes_less_than; -/// const PRIMES: [u64; 9] = primes_less_than(10); +/// # use const_primes::primes_lt; +/// const PRIMES: [u64; 9] = primes_lt(10); /// /// assert_eq!(PRIMES, [0, 0, 0, 0, 0, 2, 3, 5, 7]); /// ``` @@ -134,14 +134,14 @@ pub const fn primes() -> [Underlying; N] { /// Panics if `upper_limit` is not in the range `(N, N^2]`. This is a compile error /// in const contexts: /// ```compile_fail -/// # use const_primes::primes_less_than; -/// const PRIMES: [u64; 5] = primes_less_than(5); +/// # use const_primes::primes_lt; +/// const PRIMES: [u64; 5] = primes_lt(5); /// ``` /// ```compile_fail -/// # use const_primes::primes_less_than; -/// const PRIMES: [u64; 5] = primes_less_than(26); +/// # use const_primes::primes_lt; +/// const PRIMES: [u64; 5] = primes_lt(26); /// ``` -pub const fn primes_less_than(mut upper_limit: u64) -> [u64; N] { +pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { let n64 = N as u64; assert!(upper_limit > n64, "`upper_limit` must be larger than `N`"); match (n64).checked_mul(n64) { @@ -193,26 +193,26 @@ pub const fn primes_less_than(mut upper_limit: u64) -> [u64; N] /// # Example /// Basic usage: /// ``` -/// # use const_primes::primes_greater_than_or_equal_to; -/// const PRIMES: [u64; 5] = primes_greater_than_or_equal_to(10); +/// # use const_primes::primes_geq; +/// const PRIMES: [u64; 5] = primes_geq(10); /// assert_eq!(PRIMES, [11, 13, 17, 19, 23]); /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::primes_greater_than_or_equal_to; +/// # use const_primes::primes_geq; /// const N: usize = 71_000; /// # #[allow(long_running_const_eval)] -/// const P: [u64; N] = primes_greater_than_or_equal_to(5_000_000_030); +/// const P: [u64; N] = primes_geq(5_000_000_030); /// assert_eq!(P[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); /// assert_eq!(P[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); /// ``` /// Only primes smaller than or equal to `N^2` will be generated: /// ``` -/// # use const_primes::primes_greater_than_or_equal_to; -/// const PRIMES: [u64; 3] = primes_greater_than_or_equal_to(5); +/// # use const_primes::primes_geq; +/// const PRIMES: [u64; 3] = primes_geq(5); /// assert_eq!(PRIMES, [5, 7, 0]); /// ``` -pub const fn primes_greater_than_or_equal_to(mut lower_limit: u64) -> [u64; N] { +pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { let n64 = N as u64; if n64.checked_mul(n64).is_none() { panic!("`N^2` must fit in a `u64`"); @@ -247,21 +247,21 @@ mod test { use super::*; #[test] - fn sanity_check_primes_greater_than_or_equal_to() { + fn sanity_check_primes_geq() { { - const P: [u64; 5] = primes_greater_than_or_equal_to(10); + const P: [u64; 5] = primes_geq(10); assert_eq!(P, [11, 13, 17, 19, 23]); } { - const P: [u64; 5] = primes_greater_than_or_equal_to(0); + const P: [u64; 5] = primes_geq(0); assert_eq!(P, [2, 3, 5, 7, 11]); } { - const P: [u64; 0] = primes_greater_than_or_equal_to(0); + const P: [u64; 0] = primes_geq(0); assert_eq!(P, []); } { - const P: [u64; 1] = primes_greater_than_or_equal_to(0); + const P: [u64; 1] = primes_geq(0); assert_eq!(P, [0]); } } diff --git a/src/lib.rs b/src/lib.rs index 34e8f70..ae9c4a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,12 +58,12 @@ //! // 0 1 2 3 4 5 6 7 8 9 //! assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, false, false]); //! ``` -//! or [`sieve_less_than`] and [`sieve_greater_than_or_equal_to`] to compute the prime status of the integers below or above a given value, +//! or [`sieve_lt`] and [`sieve_geq`] to compute the prime status of the integers below or above a given value, //! ``` -//! # use const_primes::{sieve_less_than, sieve_greater_than_or_equal_to}; +//! # use const_primes::{sieve_lt, sieve_geq}; //! const N: usize = 70800; -//! const PRIME_STATUS_BELOW: [bool; N] = sieve_less_than(5_000_000_031); -//! const PRIME_STATUS_ABOVE: [bool; N] = sieve_greater_than_or_equal_to(5_000_000_031); +//! const PRIME_STATUS_BELOW: [bool; N] = sieve_lt(5_000_000_031); +//! const PRIME_STATUS_ABOVE: [bool; N] = sieve_geq(5_000_000_031); //! // 5_000_000_028 5_000_000_029 5_000_000_030 //! assert_eq!(PRIME_STATUS_BELOW[N - 3..], [false, true, false]); //! // 5_000_000_031 5_000_000_032 5_000_000_033 @@ -86,11 +86,11 @@ mod next_prime; mod sieving; mod wrapper; -pub use generation::{primes, primes_greater_than_or_equal_to, primes_less_than}; +pub use generation::{primes, primes_geq, primes_lt}; use imath::isqrt; pub use miller_rabin::is_prime; -pub use next_prime::{largest_prime_less_than, smallest_prime_greater_than}; -pub use sieving::{sieve, sieve_greater_than_or_equal_to, sieve_less_than}; +pub use next_prime::{largest_prime_lt, smallest_prime_gt}; +pub use sieving::{sieve, sieve_geq, sieve_lt}; pub use wrapper::Primes; /// Returns an array of size `N` where the value at a given index is how many primes are less than or equal to the index. @@ -209,16 +209,16 @@ mod test { } #[test] - fn check_primes_less_than() { + fn check_primes_lt() { macro_rules! test_n_below_100 { ($($n:expr),+) => { $( { - const P: [u64; $n] = primes_less_than(100); + const P: [u64; $n] = primes_lt(100); assert_eq!(PRECOMPUTED_PRIMES[25-$n..25], P.map(|i|i as u32)); assert_eq!( PRECOMPUTED_PRIMES[25-$n..25], - primes_less_than::<$n>(100).into_iter().map(|i|i as u32).collect::>() + primes_lt::<$n>(100).into_iter().map(|i|i as u32).collect::>() ); } )+ @@ -227,20 +227,20 @@ mod test { test_n_below_100!(10, 15, 20); - assert_eq!([0, 0, 0, 0, 0, 2, 3, 5, 7], primes_less_than(10)); + assert_eq!([0, 0, 0, 0, 0, 2, 3, 5, 7], primes_lt(10)); - assert_eq!([0, 2], primes_less_than(3)); + assert_eq!([0, 2], primes_lt(3)); } #[test] - fn check_sieve_less_than() { + fn check_sieve_lt() { macro_rules! test_n_below_100 { ($($n:expr),+) => { $( { - const P: [bool; $n] = sieve_less_than(100); + const P: [bool; $n] = sieve_lt(100); assert_eq!(&PRIMALITIES[100-$n..], P); - assert_eq!(&PRIMALITIES[100-$n..], sieve_less_than::<$n>(100)); + assert_eq!(&PRIMALITIES[100-$n..], sieve_lt::<$n>(100)); } )+ }; @@ -260,9 +260,9 @@ mod test { ($($n:expr),+) => { $( { - const P: [bool; $n] = sieve_greater_than_or_equal_to(10); + const P: [bool; $n] = sieve_geq(10); assert_eq!(&PRIMALITIES[10..10+$n], P); - assert_eq!(&PRIMALITIES[10..10+$n], sieve_greater_than_or_equal_to::<$n>(10)); + assert_eq!(&PRIMALITIES[10..10+$n], sieve_geq::<$n>(10)); } )+ }; @@ -274,26 +274,23 @@ mod test { fn check_next_prime() { for i in 1..PRECOMPUTED_PRIMES.len() - 1 { assert_eq!( - smallest_prime_greater_than(PRECOMPUTED_PRIMES[i] as u64), + smallest_prime_gt(PRECOMPUTED_PRIMES[i] as u64), Some(PRECOMPUTED_PRIMES[i + 1] as u64) ); assert_eq!( - largest_prime_less_than(PRECOMPUTED_PRIMES[i] as u64), + largest_prime_lt(PRECOMPUTED_PRIMES[i] as u64), Some(PRECOMPUTED_PRIMES[i - 1] as u64) ); } - assert_eq!( - smallest_prime_greater_than(18_446_744_073_709_551_558), - None - ); - assert_eq!(smallest_prime_greater_than(0), Some(2)); - assert_eq!(smallest_prime_greater_than(1), Some(2)); - assert_eq!(smallest_prime_greater_than(2), Some(3)); - assert_eq!(largest_prime_less_than(0), None); - assert_eq!(largest_prime_less_than(1), None); - assert_eq!(largest_prime_less_than(2), None); - assert_eq!(largest_prime_less_than(3), Some(2)); + assert_eq!(smallest_prime_gt(18_446_744_073_709_551_558), None); + assert_eq!(smallest_prime_gt(0), Some(2)); + assert_eq!(smallest_prime_gt(1), Some(2)); + assert_eq!(smallest_prime_gt(2), Some(3)); + assert_eq!(largest_prime_lt(0), None); + assert_eq!(largest_prime_lt(1), None); + assert_eq!(largest_prime_lt(2), None); + assert_eq!(largest_prime_lt(3), Some(2)); } #[rustfmt::skip] diff --git a/src/next_prime.rs b/src/next_prime.rs index e55d341..84bfda5 100644 --- a/src/next_prime.rs +++ b/src/next_prime.rs @@ -7,18 +7,18 @@ use crate::is_prime; /// # Examples /// Basic usage: /// ``` -/// # use const_primes::largest_prime_less_than; -/// const LPLEQ: Option = largest_prime_less_than(400); +/// # use const_primes::largest_prime_lt; +/// const LPLEQ: Option = largest_prime_lt(400); /// assert_eq!(LPLEQ, Some(397)); /// ``` /// There's no prime smaller than two: /// ``` -/// # use const_primes::largest_prime_less_than; -/// const NOSUCH: Option = largest_prime_less_than(2); +/// # use const_primes::largest_prime_lt; +/// const NOSUCH: Option = largest_prime_lt(2); /// assert!(NOSUCH.is_none()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn largest_prime_less_than(mut n: u64) -> Option { +pub const fn largest_prime_lt(mut n: u64) -> Option { if n <= 2 { None } else if n == 3 { @@ -46,18 +46,18 @@ pub const fn largest_prime_less_than(mut n: u64) -> Option { /// # Example /// Basic usage: /// ``` -/// # use const_primes::smallest_prime_greater_than; -/// const SPGEQ: Option = smallest_prime_greater_than(400); +/// # use const_primes::smallest_prime_gt; +/// const SPGEQ: Option = smallest_prime_gt(400); /// assert_eq!(SPGEQ, Some(401)); /// ``` /// Primes larger than 18446744073709551557 can not be represented by a `u64`: /// ``` -/// # use const_primes::smallest_prime_greater_than; -/// const NOSUCH: Option = smallest_prime_greater_than(18_446_744_073_709_551_557); +/// # use const_primes::smallest_prime_gt; +/// const NOSUCH: Option = smallest_prime_gt(18_446_744_073_709_551_557); /// assert!(NOSUCH.is_none()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn smallest_prime_greater_than(mut n: u64) -> Option { +pub const fn smallest_prime_gt(mut n: u64) -> Option { // The largest prime smaller than u64::MAX if n >= 18_446_744_073_709_551_557 { None diff --git a/src/sieving.rs b/src/sieving.rs index 16c0703..22a0982 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -55,8 +55,8 @@ pub(crate) const fn sieve_segment( /// # Examples /// Basic usage /// ``` -/// # use const_primes::sieve_less_than; -/// const PRIME_STATUSES: [bool; 10] = sieve_less_than(30); +/// # use const_primes::sieve_lt; +/// const PRIME_STATUSES: [bool; 10] = sieve_lt(30); /// /// assert_eq!( /// PRIME_STATUSES, @@ -66,10 +66,10 @@ pub(crate) const fn sieve_segment( /// ``` /// Sieve limited ranges of very large values /// ``` -/// # use const_primes::sieve_less_than; +/// # use const_primes::sieve_lt; /// const BIG_NUMBER: u64 = 5_000_000_031; /// const CEIL_SQRT_BIG_NUMBER: usize = 70711; -/// const BIG_PRIME_STATUSES: [bool; CEIL_SQRT_BIG_NUMBER] = sieve_less_than(BIG_NUMBER); +/// const BIG_PRIME_STATUSES: [bool; CEIL_SQRT_BIG_NUMBER] = sieve_lt(BIG_NUMBER); /// assert_eq!( /// BIG_PRIME_STATUSES[CEIL_SQRT_BIG_NUMBER - 3..], /// // 5_000_000_028 5_000_000_029 5_000_000_030 @@ -80,15 +80,15 @@ pub(crate) const fn sieve_segment( /// # Panics /// Panics if `upper_limit` is not in the range `[N, N^2]`. In const contexts these are compile errors: /// ```compile_fail -/// # use const_primes::sieve_less_than; -/// const PRIME_STATUSES: [bool; 5] = sieve_less_than(26); +/// # use const_primes::sieve_lt; +/// const PRIME_STATUSES: [bool; 5] = sieve_lt(26); /// ``` /// ```compile_fail -/// # use const_primes::sieve_less_than; -/// const PRIME_STATUSES: [bool; 5] = sieve_less_than(4); +/// # use const_primes::sieve_lt; +/// const PRIME_STATUSES: [bool; 5] = sieve_lt(4); /// ``` #[must_use = "the function returns a new value and does not modify its input"] -pub const fn sieve_less_than(upper_limit: u64) -> [bool; N] { +pub const fn sieve_lt(upper_limit: u64) -> [bool; N] { let n64 = N as u64; // Since panics are compile time errors in const contexts @@ -165,18 +165,18 @@ pub const fn sieve() -> [bool; N] { /// # Example /// Basic usage: /// ``` -/// # use const_primes::sieve_greater_than_or_equal_to; -/// const PRIME_STATUS: [bool; 5] = sieve_greater_than_or_equal_to(10); +/// # use const_primes::sieve_geq; +/// const PRIME_STATUS: [bool; 5] = sieve_geq(10); /// // 10 11 12 13 14 /// assert_eq!(PRIME_STATUS, [false, true, false, true, false]); /// ``` /// # Panics /// Panics if `N + lower_limit` is larger than `N^2`. In const contexts this is a compile error: /// ```compile_fail -/// # use const_primes::sieve_greater_than_or_equal_to; -/// const P: [bool; 5] = sieve_greater_than_or_equal_to(21); +/// # use const_primes::sieve_geq; +/// const P: [bool; 5] = sieve_geq(21); /// ``` -pub const fn sieve_greater_than_or_equal_to(lower_limit: u64) -> [bool; N] { +pub const fn sieve_geq(lower_limit: u64) -> [bool; N] { let n64 = N as u64; // Since panics are compile time errors in const contexts From 6f69dda0ab2a1b0273a7fe0908b58ef8ef217fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 15:01:01 +0200 Subject: [PATCH 036/212] Add panics section to primes_geq docstring --- src/generation.rs | 12 +++++++++++- src/lib.rs | 37 +++++++++++++++++++++++++------------ 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 65c81ff..391dca0 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -104,6 +104,8 @@ pub const fn primes() -> [Underlying; N] { /// Due to the limitations on memory allocation in `const` contexts the value of `N` /// must satisfy the bounds `N < upper_limit <= N^2`. /// +/// If you want to compute primes that are larger than the input, take a look at [`primes_geq`]. +/// /// # Example /// Basic usage: /// ``` @@ -190,6 +192,8 @@ pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { /// This function will fill the output array from index 0 and stop generating primes if they exceed `N^2`. /// In that case the remaining elements of the output array will be 0. /// +/// If you want to compute primes smaller than the input, take a look at [`primes_lt`]. +/// /// # Example /// Basic usage: /// ``` @@ -206,12 +210,18 @@ pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { /// assert_eq!(P[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); /// assert_eq!(P[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); /// ``` -/// Only primes smaller than or equal to `N^2` will be generated: +/// Only primes smaller than to `N^2` will be generated: /// ``` /// # use const_primes::primes_geq; /// const PRIMES: [u64; 3] = primes_geq(5); /// assert_eq!(PRIMES, [5, 7, 0]); /// ``` +/// # Panics +/// Panics if `N^2` does not fit in a `u64`. +/// ```compile_fail +/// # use const_primes::primes_geq; +/// const P: [u64; u32::MAX as usize + 1] = primes_geq(0); +/// ``` pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { let n64 = N as u64; if n64.checked_mul(n64).is_none() { diff --git a/src/lib.rs b/src/lib.rs index ae9c4a0..0994631 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,30 @@ //! # use const_primes::Primes; //! const PRIMES: Primes<0> = Primes::new(); //! ``` +//! +//! ## Arbitrary ranges +//! +//! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] +//! that can be used to work with ranges that don't start at zero. +//! ``` +//! # use const_primes::{sieve_lt, primes_geq}; +//! const N: usize = 70722; +//! # #[allow(long_running_const_eval)] +//! const PRIMES_GEQ: [u64; N] = primes_geq(5_000_000_031); +//! +//! assert_eq!(PRIMES_GEQ[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); +//! ``` +//! ``` +//! # use const_primes::{sieve_lt, primes_geq}; +//! const N: usize = 70711; +//! const PRIME_STATUS_LT: [bool; N] = sieve_lt(5_000_000_031); +//! // 5_000_000_028 5_000_000_029 5_000_000_030 +//! assert_eq!(PRIME_STATUS_LT[N - 3..], [false, true, false]); +//! ``` +//! Unfortunately the output array must be large enough to contain the prime sieve, which scales with +//! the square root of largest relavant number, which is why the examples use a size of over 70000 even though +//! they're only interested in three numbers. +//! //! ## Other functionality //! //! Use [`is_prime`] to test whether a given number is prime, @@ -50,7 +74,7 @@ //! const CHECK: bool = is_prime(18_446_744_073_709_551_557); //! assert!(CHECK); //! ``` -//! or [`sieve`] to compute the prime status of the `N` first integers, +//! or [`sieve`] to compute the prime status of the `N` first integers. //! ``` //! # use const_primes::sieve; //! const N: usize = 10; @@ -58,17 +82,6 @@ //! // 0 1 2 3 4 5 6 7 8 9 //! assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, false, false]); //! ``` -//! or [`sieve_lt`] and [`sieve_geq`] to compute the prime status of the integers below or above a given value, -//! ``` -//! # use const_primes::{sieve_lt, sieve_geq}; -//! const N: usize = 70800; -//! const PRIME_STATUS_BELOW: [bool; N] = sieve_lt(5_000_000_031); -//! const PRIME_STATUS_ABOVE: [bool; N] = sieve_geq(5_000_000_031); -//! // 5_000_000_028 5_000_000_029 5_000_000_030 -//! assert_eq!(PRIME_STATUS_BELOW[N - 3..], [false, true, false]); -//! // 5_000_000_031 5_000_000_032 5_000_000_033 -//! assert_eq!(PRIME_STATUS_ABOVE[..3], [false, false, false]); -//! ``` //! and more! #![forbid(unsafe_code)] From f591acb30ce254b33964067d12a0aa325a9c3380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 15:09:56 +0200 Subject: [PATCH 037/212] Clean up panics section of primes_lt --- src/generation.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 391dca0..6a3295c 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -101,9 +101,6 @@ pub const fn primes() -> [Underlying; N] { /// The return array fills from the end until either it is full or there are no more primes. /// If the primes run out before the array is filled the first elements will have a value of zero. /// -/// Due to the limitations on memory allocation in `const` contexts the value of `N` -/// must satisfy the bounds `N < upper_limit <= N^2`. -/// /// If you want to compute primes that are larger than the input, take a look at [`primes_geq`]. /// /// # Example @@ -133,11 +130,11 @@ pub const fn primes() -> [Underlying; N] { /// assert_eq!(PRIMES, [0, 0, 0, 0, 0, 2, 3, 5, 7]); /// ``` /// # Panics -/// Panics if `upper_limit` is not in the range `(N, N^2]`. This is a compile error +/// Panics if `N^2` does not fit in a `u64` or if `upper_limit` is larger than `N^2`. This is a compile error /// in const contexts: /// ```compile_fail /// # use const_primes::primes_lt; -/// const PRIMES: [u64; 5] = primes_lt(5); +/// const PRIMES: [u64; u32::MAX as usize + 1] = primes_lt(100); /// ``` /// ```compile_fail /// # use const_primes::primes_lt; @@ -145,7 +142,6 @@ pub const fn primes() -> [Underlying; N] { /// ``` pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { let n64 = N as u64; - assert!(upper_limit > n64, "`upper_limit` must be larger than `N`"); match (n64).checked_mul(n64) { Some(prod) => assert!( upper_limit <= prod, From 64e581911e64c99ca50d1c2fecd92d449ce3311c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 15:13:49 +0200 Subject: [PATCH 038/212] Add comments to compile_fail exampels --- src/generation.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index 6a3295c..0ab08ed 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -134,10 +134,12 @@ pub const fn primes() -> [Underlying; N] { /// in const contexts: /// ```compile_fail /// # use const_primes::primes_lt; +/// // N is too large /// const PRIMES: [u64; u32::MAX as usize + 1] = primes_lt(100); /// ``` /// ```compile_fail /// # use const_primes::primes_lt; +/// // N upper_limit > N^2 /// const PRIMES: [u64; 5] = primes_lt(26); /// ``` pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { From 92a92f99ee4e7410186c3adaf82783b2e581bdcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 16:02:48 +0200 Subject: [PATCH 039/212] Add primality verification to primes_geq test --- src/generation.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index 0ab08ed..48a9a02 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -252,6 +252,8 @@ pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { #[cfg(test)] mod test { + use crate::is_prime; + use super::*; #[test] @@ -272,5 +274,11 @@ mod test { const P: [u64; 1] = primes_geq(0); assert_eq!(P, [0]); } + for prime in primes_geq::<2_000>(3_998_000) { + if prime == 0 { + break; + } + assert!(is_prime(prime)); + } } } From 41c3a7dddb665aecb7f599d550dc721feed1b860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 17:00:09 +0200 Subject: [PATCH 040/212] Ensured that potential false positives are ignored --- src/generation.rs | 5 +++++ src/sieving.rs | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 48a9a02..4d7b3fe 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -237,9 +237,14 @@ pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { while i < N { if upper_sieve[i] { largest_found_prime = lower_limit + i as u64; + if largest_found_prime >= n64 * n64 { + // We do not know if this is actually a prime + break 'generate; + } primes[total_found_primes] = largest_found_prime; total_found_primes += 1; if total_found_primes >= N { + // We've found enough primes break 'generate; } } diff --git a/src/sieving.rs b/src/sieving.rs index 22a0982..e9923d7 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -171,10 +171,10 @@ pub const fn sieve() -> [bool; N] { /// assert_eq!(PRIME_STATUS, [false, true, false, true, false]); /// ``` /// # Panics -/// Panics if `N + lower_limit` is larger than `N^2`. In const contexts this is a compile error: +/// Panics if `N + lower_limit` is larger than or equal to `N^2`. In const contexts this is a compile error: /// ```compile_fail /// # use const_primes::sieve_geq; -/// const P: [bool; 5] = sieve_geq(21); +/// const P: [bool; 5] = sieve_geq(20); /// ``` pub const fn sieve_geq(lower_limit: u64) -> [bool; N] { let n64 = N as u64; @@ -188,7 +188,7 @@ pub const fn sieve_geq(lower_limit: u64) -> [bool; N] { }; if let Some(n_sqr) = n64.checked_mul(n64) { assert!( - upper_limit <= n_sqr, + upper_limit < n_sqr, "`lower_limit + N` must be less than or equal to `N^2`" ); } else { From 52cbd6d06523940b755d90b488a6de2ecc7a4b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 17:04:19 +0200 Subject: [PATCH 041/212] Motivate early exit --- src/generation.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index 4d7b3fe..02c05bb 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -239,6 +239,8 @@ pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { largest_found_prime = lower_limit + i as u64; if largest_found_prime >= n64 * n64 { // We do not know if this is actually a prime + // since the base sieve does not contain information about + // the prime status of N. break 'generate; } primes[total_found_primes] = largest_found_prime; From d939966f54c74212e5bdf74a32de626d9014a80b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 17:13:23 +0200 Subject: [PATCH 042/212] Remove now redundant check --- src/generation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 02c05bb..aca87b9 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -229,7 +229,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { let mut primes = [0; N]; let base_sieve: [bool; N] = sieve(); let mut total_found_primes = 0; - 'generate: while total_found_primes < N && lower_limit + n64 <= n64 * n64 { + 'generate: while total_found_primes < N { let mut largest_found_prime = primes[total_found_primes]; let upper_sieve = sieve_segment(&base_sieve, lower_limit + n64); let mut i = 0; @@ -240,7 +240,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { if largest_found_prime >= n64 * n64 { // We do not know if this is actually a prime // since the base sieve does not contain information about - // the prime status of N. + // the prime status of numbers larger than or equal to N. break 'generate; } primes[total_found_primes] = largest_found_prime; From 737aa022b97a3baabf2e1ee109e923bff52be228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 17:14:40 +0200 Subject: [PATCH 043/212] grammar --- src/generation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index aca87b9..c8b51f8 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -208,7 +208,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { /// assert_eq!(P[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); /// assert_eq!(P[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); /// ``` -/// Only primes smaller than to `N^2` will be generated: +/// Only primes smaller than `N^2` will be generated: /// ``` /// # use const_primes::primes_geq; /// const PRIMES: [u64; 3] = primes_geq(5); From 76c948037b5d5895285ecec069ee6471f40d8b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 17:21:36 +0200 Subject: [PATCH 044/212] move backticks in panic massage of sieve_lt --- src/sieving.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sieving.rs b/src/sieving.rs index e9923d7..616ab4c 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -96,9 +96,9 @@ pub const fn sieve_lt(upper_limit: u64) -> [bool; N] { match n64.checked_mul(n64) { Some(prod) => assert!( upper_limit <= prod, - "`upper_limit` must be smaller than or equal to `N`^2" + "`upper_limit` must be smaller than or equal to `N^2`" ), - None => panic!("`N`^2 must fit in a `u64`"), + None => panic!("`N^2` must fit in a `u64`"), } assert!(upper_limit >= n64, "`upper_limit` must be at least `N`"); From 24025e2d866856a5967ea93ceb08e3294c7899ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 17:24:12 +0200 Subject: [PATCH 045/212] Add note about assumtions in sieve_segment --- src/sieving.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sieving.rs b/src/sieving.rs index 616ab4c..4d30eb3 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -1,6 +1,8 @@ use crate::isqrt; /// Uses the primalities of the first `N` integers in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. +/// Assumes that the base sieve contains the prime status of the `N` fist integers. The output is only meaningful +/// for the numbers below `N^2`. pub(crate) const fn sieve_segment( base_sieve: &[bool; N], upper_limit: u64, From 13a0371a5e3ceac2eee81c359d03730f3c69a756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 18:51:11 +0200 Subject: [PATCH 046/212] Remove unused imports in crate docstring examples --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0994631..1b1da4e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,7 @@ //! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] //! that can be used to work with ranges that don't start at zero. //! ``` -//! # use const_primes::{sieve_lt, primes_geq}; +//! # use const_primes::primes_geq; //! const N: usize = 70722; //! # #[allow(long_running_const_eval)] //! const PRIMES_GEQ: [u64; N] = primes_geq(5_000_000_031); @@ -56,7 +56,7 @@ //! assert_eq!(PRIMES_GEQ[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); //! ``` //! ``` -//! # use const_primes::{sieve_lt, primes_geq}; +//! # use const_primes::sieve_lt; //! const N: usize = 70711; //! const PRIME_STATUS_LT: [bool; N] = sieve_lt(5_000_000_031); //! // 5_000_000_028 5_000_000_029 5_000_000_030 From 2e8272c66fd4a5d9fd3ddf4e7aa5c6ff5ac289ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 18:59:36 +0200 Subject: [PATCH 047/212] Add #[must_use] --- src/generation.rs | 2 ++ src/sieving.rs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index c8b51f8..3bcbaf5 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -142,6 +142,7 @@ pub const fn primes() -> [Underlying; N] { /// // N upper_limit > N^2 /// const PRIMES: [u64; 5] = primes_lt(26); /// ``` +#[must_use = "the function only returns a new value and does not modify its input"] pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { let n64 = N as u64; match (n64).checked_mul(n64) { @@ -220,6 +221,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { /// # use const_primes::primes_geq; /// const P: [u64; u32::MAX as usize + 1] = primes_geq(0); /// ``` +#[must_use = "the function only returns a new value and does not modify its input"] pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { let n64 = N as u64; if n64.checked_mul(n64).is_none() { diff --git a/src/sieving.rs b/src/sieving.rs index 4d30eb3..97e1e69 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -3,6 +3,7 @@ use crate::isqrt; /// Uses the primalities of the first `N` integers in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. /// Assumes that the base sieve contains the prime status of the `N` fist integers. The output is only meaningful /// for the numbers below `N^2`. +#[must_use = "the function only returns a new value and does not modify its inputs"] pub(crate) const fn sieve_segment( base_sieve: &[bool; N], upper_limit: u64, @@ -89,7 +90,7 @@ pub(crate) const fn sieve_segment( /// # use const_primes::sieve_lt; /// const PRIME_STATUSES: [bool; 5] = sieve_lt(4); /// ``` -#[must_use = "the function returns a new value and does not modify its input"] +#[must_use = "the function only returns a new value and does not modify its input"] pub const fn sieve_lt(upper_limit: u64) -> [bool; N] { let n64 = N as u64; @@ -178,6 +179,7 @@ pub const fn sieve() -> [bool; N] { /// # use const_primes::sieve_geq; /// const P: [bool; 5] = sieve_geq(20); /// ``` +#[must_use = "the function only returns a new value and does not modify its input"] pub const fn sieve_geq(lower_limit: u64) -> [bool; N] { let n64 = N as u64; From 9e715e126ca411e36805520063fd6c17964d2171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 21:57:56 +0200 Subject: [PATCH 048/212] rename next_prime module to other_prime --- src/lib.rs | 24 ++++++++++++------------ src/{next_prime.rs => other_prime.rs} | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) rename src/{next_prime.rs => other_prime.rs} (72%) diff --git a/src/lib.rs b/src/lib.rs index 1b1da4e..80c58ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,14 +95,14 @@ type Underlying = u32; mod generation; mod imath; mod miller_rabin; -mod next_prime; +mod other_prime; mod sieving; mod wrapper; pub use generation::{primes, primes_geq, primes_lt}; use imath::isqrt; pub use miller_rabin::is_prime; -pub use next_prime::{largest_prime_lt, smallest_prime_gt}; +pub use other_prime::{next_prime, previous_prime}; pub use sieving::{sieve, sieve_geq, sieve_lt}; pub use wrapper::Primes; @@ -287,23 +287,23 @@ mod test { fn check_next_prime() { for i in 1..PRECOMPUTED_PRIMES.len() - 1 { assert_eq!( - smallest_prime_gt(PRECOMPUTED_PRIMES[i] as u64), + next_prime(PRECOMPUTED_PRIMES[i] as u64), Some(PRECOMPUTED_PRIMES[i + 1] as u64) ); assert_eq!( - largest_prime_lt(PRECOMPUTED_PRIMES[i] as u64), + previous_prime(PRECOMPUTED_PRIMES[i] as u64), Some(PRECOMPUTED_PRIMES[i - 1] as u64) ); } - assert_eq!(smallest_prime_gt(18_446_744_073_709_551_558), None); - assert_eq!(smallest_prime_gt(0), Some(2)); - assert_eq!(smallest_prime_gt(1), Some(2)); - assert_eq!(smallest_prime_gt(2), Some(3)); - assert_eq!(largest_prime_lt(0), None); - assert_eq!(largest_prime_lt(1), None); - assert_eq!(largest_prime_lt(2), None); - assert_eq!(largest_prime_lt(3), Some(2)); + assert_eq!(next_prime(18_446_744_073_709_551_558), None); + assert_eq!(next_prime(0), Some(2)); + assert_eq!(next_prime(1), Some(2)); + assert_eq!(next_prime(2), Some(3)); + assert_eq!(previous_prime(0), None); + assert_eq!(previous_prime(1), None); + assert_eq!(previous_prime(2), None); + assert_eq!(previous_prime(3), Some(2)); } #[rustfmt::skip] diff --git a/src/next_prime.rs b/src/other_prime.rs similarity index 72% rename from src/next_prime.rs rename to src/other_prime.rs index 84bfda5..b37690a 100644 --- a/src/next_prime.rs +++ b/src/other_prime.rs @@ -7,18 +7,18 @@ use crate::is_prime; /// # Examples /// Basic usage: /// ``` -/// # use const_primes::largest_prime_lt; -/// const LPLEQ: Option = largest_prime_lt(400); +/// # use const_primes::previous_prime; +/// const LPLEQ: Option = previous_prime(400); /// assert_eq!(LPLEQ, Some(397)); /// ``` /// There's no prime smaller than two: /// ``` -/// # use const_primes::largest_prime_lt; -/// const NOSUCH: Option = largest_prime_lt(2); +/// # use const_primes::previous_prime; +/// const NOSUCH: Option = previous_prime(2); /// assert!(NOSUCH.is_none()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn largest_prime_lt(mut n: u64) -> Option { +pub const fn previous_prime(mut n: u64) -> Option { if n <= 2 { None } else if n == 3 { @@ -46,18 +46,18 @@ pub const fn largest_prime_lt(mut n: u64) -> Option { /// # Example /// Basic usage: /// ``` -/// # use const_primes::smallest_prime_gt; -/// const SPGEQ: Option = smallest_prime_gt(400); +/// # use const_primes::next_prime; +/// const SPGEQ: Option = next_prime(400); /// assert_eq!(SPGEQ, Some(401)); /// ``` /// Primes larger than 18446744073709551557 can not be represented by a `u64`: /// ``` -/// # use const_primes::smallest_prime_gt; -/// const NOSUCH: Option = smallest_prime_gt(18_446_744_073_709_551_557); +/// # use const_primes::next_prime; +/// const NOSUCH: Option = next_prime(18_446_744_073_709_551_557); /// assert!(NOSUCH.is_none()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn smallest_prime_gt(mut n: u64) -> Option { +pub const fn next_prime(mut n: u64) -> Option { // The largest prime smaller than u64::MAX if n >= 18_446_744_073_709_551_557 { None From 1a4d8efa3eee93f61c324b26de9d7e8228f08f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 27 Oct 2023 22:18:17 +0200 Subject: [PATCH 049/212] Update documentation string of crate and README --- README.md | 54 +++++++++++++++++++++++++++++++++++------------------- src/lib.rs | 35 ++++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 86888c7..e10b91a 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,13 @@ A crate for generating and working with prime numbers in const contexts. `#![no_std]` compatible. ## Examples -Generate arrays of prime numbers with the function `primes` which uses a [segmented sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve). +Generate arrays of prime numbers with the function `primes` which uses a [segmented sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve): ```rust const PRIMES: [u32; 10] = primes(); assert_eq!(PRIMES[5], 13); assert_eq!(PRIMES, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); ``` -or with the type `Primes` which ensures that a non-zero number of primes are generated: +or with the wrapping type [`Primes`]: ```rust const PRIMES: Primes<10> = Primes::new(); assert_eq!(PRIMES[5], 13); @@ -40,30 +40,46 @@ assert_eq!(PRIMES_LEQ_100, Some(25)); assert!(CACHE.is_prime(1000).is_none()); assert!(CACHE.count_primes_leq(1000).is_none()); ``` -Creating a `Primes<0>` is a compile fail in const contexts and a panic otherwise. - -### Other functionality -Use `is_prime` to test whether a given number is prime -```rust -const CHECK: bool = is_prime(18_446_744_073_709_551_557); -assert!(CHECK); -``` -or `sieve` to compute the prime status of the `N` first integers, +Sieve a range of numbers for their prime status with `sieve`: ```rust const N: usize = 10; const PRIME_STATUS: [bool; N] = sieve(); // 0 1 2 3 4 5 6 7 8 9 assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, false, false]); +``` + +## Arbitrary ranges +The crate provides prime generation and sieving functions with suffixes, e.g. `primes_geq` and `sieve_lt` +that can be used to work with ranges that don't start at zero. +```rust +const N: usize = 70722; +const PRIMES_GEQ: [u64; N] = primes_geq(5_000_000_031); +assert_eq!(PRIMES_GEQ[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); ``` -or `sieve_less_than` and `sieve_greater_than_or_equal_to` to compute the prime status of the integers below or above a given value, ```rust -const N: usize = 70800; -const PRIME_STATUS_BELOW: [bool; N] = sieve_less_than(5_000_000_031); -const PRIME_STATUS_ABOVE: [bool; N] = sieve_greater_than_or_equal_to(5_000_000_031); -// 5_000_000_028 5_000_000_029 5_000_000_030 -assert_eq!(PRIME_STATUS_BELOW[N - 3..], [false, true, false]); -// 5_000_000_031 5_000_000_032 5_000_000_033 -assert_eq!(PRIME_STATUS_ABOVE[..3], [false, false, false]); +const N: usize = 70711; +const PRIME_STATUS_LT: [bool; N] = sieve_lt(5_000_000_031); +// 5_000_000_028 5_000_000_029 5_000_000_030 +assert_eq!(PRIME_STATUS_LT[N - 3..], [false, true, false]); +``` +Unfortunately the output array must be large enough to contain the prime sieve, which scales with +the square root of largest relavant number, which is why the examples use a size of over 70000 even though +they're only interested in three numbers. +## Other functionality +Use `is_prime` to test whether a given number is prime: +```rust +const CHECK: bool = is_prime(18_446_744_073_709_551_557); +assert!(CHECK); +``` +Find the next or previous prime numbers with `next_prime` and `previous_prime` if they exist: +```rust +const NEXT: Option = next_prime(25); +const PREV: Option = previous_prime(25); +const NOSUCH: Option = previous_prime(2); + +assert_eq!(NEXT, Some(29)); +assert_eq!(PREV, Some(23)); +assert_eq!(NOSUCH, None); ``` and more! diff --git a/src/lib.rs b/src/lib.rs index 80c58ea..bc3b199 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,21 +4,21 @@ //! //! # Examples //! Generate arrays of prime numbers with the function [`primes`] which uses a -//! [segmented sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve). +//! [segmented sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve): //! ``` //! use const_primes::primes; //! const PRIMES: [u32; 10] = primes(); //! assert_eq!(PRIMES[5], 13); //! assert_eq!(PRIMES, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); //! ``` -//! or with the type [`Primes`], which ensures that a non-zero number of primes are generated +//! or with the wrapping type [`Primes`]: //! ``` //! use const_primes::Primes; //! const PRIMES: Primes<10> = Primes::new(); //! assert_eq!(PRIMES[5], 13); //! assert_eq!(PRIMES, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); //! ``` -//! It also lets you reuse it as a cache of primes for related computations +//! which lets you reuse it as a cache of primes for related computations: //! ``` //! # use const_primes::Primes; //! const CACHE: Primes<100> = Primes::new(); @@ -37,12 +37,14 @@ //! assert!(CACHE.is_prime(1000).is_none()); //! assert!(CACHE.count_primes_leq(1000).is_none()); //! ``` -//! Creating a `Primes<0>` is a compile fail in const contexts and a panic otherwise. -//! ```compile_fail -//! # use const_primes::Primes; -//! const PRIMES: Primes<0> = Primes::new(); +//! Sieve a range of numbers for their prime status with [`sieve`]: +//! ``` +//! # use const_primes::sieve; +//! const N: usize = 10; +//! const PRIME_STATUS: [bool; N] = sieve(); +//! // 0 1 2 3 4 5 6 7 8 9 +//! assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, false, false]); //! ``` -//! //! ## Arbitrary ranges //! //! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] @@ -68,19 +70,22 @@ //! //! ## Other functionality //! -//! Use [`is_prime`] to test whether a given number is prime, +//! Use [`is_prime`] to test whether a given number is prime: //! ``` //! # use const_primes::is_prime; //! const CHECK: bool = is_prime(18_446_744_073_709_551_557); //! assert!(CHECK); //! ``` -//! or [`sieve`] to compute the prime status of the `N` first integers. +//! Find the next or previous prime numbers with [`next_prime`] and [`previous_prime`] if they exist: //! ``` -//! # use const_primes::sieve; -//! const N: usize = 10; -//! const PRIME_STATUS: [bool; N] = sieve(); -//! // 0 1 2 3 4 5 6 7 8 9 -//! assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, false, false]); +//! # use const_primes::{next_prime, previous_prime}; +//! const NEXT: Option = next_prime(25); +//! const PREV: Option = previous_prime(25); +//! const NOSUCH: Option = previous_prime(2); +//! +//! assert_eq!(NEXT, Some(29)); +//! assert_eq!(PREV, Some(23)); +//! assert_eq!(NOSUCH, None); //! ``` //! and more! From e4f7e43f1f3f97a22be6ece52d3e93d2946df2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 15:45:27 +0100 Subject: [PATCH 050/212] Add array_section module --- src/array_section.rs | 132 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 src/array_section.rs diff --git a/src/array_section.rs b/src/array_section.rs new file mode 100644 index 0000000..b9ed260 --- /dev/null +++ b/src/array_section.rs @@ -0,0 +1,132 @@ +use core::ops::Range; + +/// An array where only a section of the data may be viewed, +/// as the other data may be e.g. not uphold some invariant. +#[derive(Debug)] +pub struct RestrictedArray { + start: usize, + end: usize, + array: [T; N], +} + +impl Clone for RestrictedArray { + fn clone(&self) -> Self { + Self { + start: self.start, + end: self.end, + array: self.array.clone(), + } + } +} + +impl Copy for RestrictedArray {} + +impl> PartialEq> for RestrictedArray { + /// This method tests for `self` and `other` values to be equal, and is used by `==`. + /// Only compares the *unrestricted* part of `self` against the *unrestricted* part of `other`. + fn eq(&self, other: &RestrictedArray) -> bool { + self.as_slice() == other.as_slice() + } +} + +impl RestrictedArray { + /// Restrict a given array so that only elements within the given range are viewable. + /// + /// # Panics + /// Panics if the range of indices is out of bounds of the array. + pub const fn new(sub_range: Range, array: [T; N]) -> Self { + assert!( + sub_range.start < N && sub_range.end <= N, + "the sub-range must be in bounds" + ); + + if sub_range.start > sub_range.end { + Self { + start: 0, + end: 0, + array, + } + } else { + Self { + start: sub_range.start, + end: sub_range.end, + array, + } + } + } + + /// Returns a reference to the full underlying array. + pub const fn as_full_array(&self) -> &[T; N] { + &self.array + } + + /// Converts `self` into the full underlying array. + pub fn into_full_array(self) -> [T; N] { + self.array + } + + /// Returns the unrestricted part of the array as a slice. + pub const fn as_slice(&self) -> &[T] { + let (_, tail) = self.array.split_at(self.start); + tail.split_at(self.end - self.start).0 + } + + /// Returns the length of the unrestricted part of the array. + pub const fn len(&self) -> usize { + self.end - self.start + } + + /// Returns whether the unrestricted part is empty. + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns whether there are parts of the array that are restricted. + /// If this is `false` it is completely fine to call [`as_full_array`](RestrictedArray::as_full_array) + /// or [`into_full_array`](RestrictedArray::into_full_array). + pub const fn is_restricted(&self) -> bool { + self.len() == N + } + + /// Returns an iterator over the unrestricted section. + pub fn iter(&self) -> core::slice::Iter<'_, T> { + self.as_slice().iter() + } +} + +impl core::ops::Index for RestrictedArray { + type Output = T; + fn index(&self, index: usize) -> &Self::Output { + let i = match self.start.checked_add(index) { + Some(sum) => sum, + None => panic!("index overflowed"), + }; + + if i >= self.end { + panic!("index was {i} when len was {}", self.end - self.start); + } + + &self.array[i] + } +} + +impl IntoIterator for RestrictedArray { + type IntoIter = core::iter::Take>>; + type Item = <[T; N] as IntoIterator>::Item; + fn into_iter(self) -> Self::IntoIter { + let start = self.start; + let len = self.len(); + self.array.into_iter().skip(start).take(len) + } +} + +impl PartialEq for RestrictedArray +where + U: PartialEq<[T]>, +{ + /// This method tests for `self` and `other` values to be equal, and is used by `==`. + /// Only compares the *unrestricted* part of `self` against `other`. + fn eq(&self, other: &U) -> bool { + other == self.as_slice() + } +} From 53c5e81b22232a7f69b686cd7cb6d5cc6aaa0f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 15:55:16 +0100 Subject: [PATCH 051/212] Use a newtype for RestrictedArray IntoIter --- src/array_section.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index b9ed260..e353731 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -1,4 +1,4 @@ -use core::ops::Range; +use core::{iter::FusedIterator, ops::Range}; /// An array where only a section of the data may be viewed, /// as the other data may be e.g. not uphold some invariant. @@ -110,13 +110,35 @@ impl core::ops::Index for RestrictedArray { } } +pub struct RestrictedArrayIntoIter( + core::iter::Take>>, +); + +impl Iterator for RestrictedArrayIntoIter { + type Item = T; + fn next(&mut self) -> Option { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } +} +impl FusedIterator for RestrictedArrayIntoIter {} +impl ExactSizeIterator for RestrictedArrayIntoIter {} +impl DoubleEndedIterator for RestrictedArrayIntoIter { + fn next_back(&mut self) -> Option { + self.0.next_back() + } +} + impl IntoIterator for RestrictedArray { - type IntoIter = core::iter::Take>>; - type Item = <[T; N] as IntoIterator>::Item; + type IntoIter = RestrictedArrayIntoIter; + type Item = as Iterator>::Item; fn into_iter(self) -> Self::IntoIter { let start = self.start; let len = self.len(); - self.array.into_iter().skip(start).take(len) + RestrictedArrayIntoIter(self.array.into_iter().skip(start).take(len)) } } From 8b1add357eea9c63635769825e5202d70443d620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 15:56:05 +0100 Subject: [PATCH 052/212] Simplify docstring --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index e353731..d9ee956 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -30,7 +30,7 @@ impl> PartialEq> for Restr } impl RestrictedArray { - /// Restrict a given array so that only elements within the given range are viewable. + /// Restrict an array so that only elements within the given range are viewable. /// /// # Panics /// Panics if the range of indices is out of bounds of the array. From 86c840bce3ec2eb370772478c4f9f27a5defdef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 15:56:25 +0100 Subject: [PATCH 053/212] grammar --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index d9ee956..be36100 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -1,7 +1,7 @@ use core::{iter::FusedIterator, ops::Range}; /// An array where only a section of the data may be viewed, -/// as the other data may be e.g. not uphold some invariant. +/// as the other data may e.g. not uphold some invariant. #[derive(Debug)] pub struct RestrictedArray { start: usize, From b6b6be749d507b4a276732fcaf332be166e5ba32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 15:57:49 +0100 Subject: [PATCH 054/212] Add conditional impl of Eq --- src/array_section.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/array_section.rs b/src/array_section.rs index be36100..af42fd4 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -29,6 +29,8 @@ impl> PartialEq> for Restr } } +impl Eq for RestrictedArray {} + impl RestrictedArray { /// Restrict an array so that only elements within the given range are viewable. /// From 1a70333df93c0ade2e3629a581052c9cfb7af4a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 16:02:54 +0100 Subject: [PATCH 055/212] rename array_section to restricted_array --- src/{array_section.rs => restricted_array.rs} | 1 + 1 file changed, 1 insertion(+) rename src/{array_section.rs => restricted_array.rs} (98%) diff --git a/src/array_section.rs b/src/restricted_array.rs similarity index 98% rename from src/array_section.rs rename to src/restricted_array.rs index af42fd4..2529dd8 100644 --- a/src/array_section.rs +++ b/src/restricted_array.rs @@ -112,6 +112,7 @@ impl core::ops::Index for RestrictedArray { } } +/// Created by the [`IntoIterator`] trait implementation on [`RestrictedArray`]. pub struct RestrictedArrayIntoIter( core::iter::Take>>, ); From 823f7d236064e32f119d3c8c385376b33bc566d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 16:08:26 +0100 Subject: [PATCH 056/212] Add restricted_array to the crate as a module --- src/lib.rs | 1 + src/restricted_array.rs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index bc3b199..d73172a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,6 +97,7 @@ // This is used since there is currently no way to be generic over types that can do arithmetic at compile time. type Underlying = u32; +pub mod restricted_array; mod generation; mod imath; mod miller_rabin; diff --git a/src/restricted_array.rs b/src/restricted_array.rs index 2529dd8..017cb62 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -73,6 +73,17 @@ impl RestrictedArray { tail.split_at(self.end - self.start).0 } + /// Returns the index of the first element of the underlying array that's not restricted. + pub const fn start(&self) -> usize { + self.start + } + + /// Returns the index of the first element of the underlying array that is + /// restricted again after the end of the unrestricted part. + pub const fn end(&self) -> usize { + self.end + } + /// Returns the length of the unrestricted part of the array. pub const fn len(&self) -> usize { self.end - self.start From 48f7986821db6e34a6a99721f947d51a12170f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 16:09:12 +0100 Subject: [PATCH 057/212] cargo fmt --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d73172a..a14a0da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,11 +97,11 @@ // This is used since there is currently no way to be generic over types that can do arithmetic at compile time. type Underlying = u32; -pub mod restricted_array; mod generation; mod imath; mod miller_rabin; mod other_prime; +pub mod restricted_array; mod sieving; mod wrapper; From 94a1c2ee22a3fb8f9825e15929456e12e7f0e8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 16:13:24 +0100 Subject: [PATCH 058/212] Change order of generic paramters to match array --- src/restricted_array.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index 017cb62..aefe596 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -3,13 +3,13 @@ use core::{iter::FusedIterator, ops::Range}; /// An array where only a section of the data may be viewed, /// as the other data may e.g. not uphold some invariant. #[derive(Debug)] -pub struct RestrictedArray { +pub struct RestrictedArray { start: usize, end: usize, array: [T; N], } -impl Clone for RestrictedArray { +impl Clone for RestrictedArray { fn clone(&self) -> Self { Self { start: self.start, @@ -19,19 +19,19 @@ impl Clone for RestrictedArray { } } -impl Copy for RestrictedArray {} +impl Copy for RestrictedArray {} -impl> PartialEq> for RestrictedArray { +impl> PartialEq> for RestrictedArray { /// This method tests for `self` and `other` values to be equal, and is used by `==`. /// Only compares the *unrestricted* part of `self` against the *unrestricted* part of `other`. - fn eq(&self, other: &RestrictedArray) -> bool { + fn eq(&self, other: &RestrictedArray) -> bool { self.as_slice() == other.as_slice() } } -impl Eq for RestrictedArray {} +impl Eq for RestrictedArray {} -impl RestrictedArray { +impl RestrictedArray { /// Restrict an array so that only elements within the given range are viewable. /// /// # Panics @@ -107,7 +107,7 @@ impl RestrictedArray { } } -impl core::ops::Index for RestrictedArray { +impl core::ops::Index for RestrictedArray { type Output = T; fn index(&self, index: usize) -> &Self::Output { let i = match self.start.checked_add(index) { @@ -146,7 +146,7 @@ impl DoubleEndedIterator for RestrictedArrayIntoIter { } } -impl IntoIterator for RestrictedArray { +impl IntoIterator for RestrictedArray { type IntoIter = RestrictedArrayIntoIter; type Item = as Iterator>::Item; fn into_iter(self) -> Self::IntoIter { @@ -156,7 +156,7 @@ impl IntoIterator for RestrictedArray { } } -impl PartialEq for RestrictedArray +impl PartialEq for RestrictedArray where U: PartialEq<[T]>, { From cd29f38717ff0d1008ea07f35465ad218f4180c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 29 Oct 2023 18:21:13 +0100 Subject: [PATCH 059/212] PARTIAL COMMIT, primes_geq fails some tests --- benches/prime_benches.rs | 2 +- src/{generation.rs => generation/mod.rs} | 74 +++++---- src/generation/segmented_generation_result.rs | 148 ++++++++++++++++++ src/lib.rs | 20 ++- src/restricted_array.rs | 22 ++- 5 files changed, 222 insertions(+), 44 deletions(-) rename src/{generation.rs => generation/mod.rs} (76%) create mode 100644 src/generation/segmented_generation_result.rs diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index fe8fa80..a4604b3 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,4 +1,4 @@ -use const_primes::{is_prime, primes, primes_geq, primes_lt, sieve, sieve_geq, sieve_lt}; +use const_primes::{is_prime, primes, generation::{primes_geq, primes_lt}, sieve, sieve_geq, sieve_lt}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; use rand::prelude::*; use std::hint::black_box; diff --git a/src/generation.rs b/src/generation/mod.rs similarity index 76% rename from src/generation.rs rename to src/generation/mod.rs index 3bcbaf5..bcf62cd 100644 --- a/src/generation.rs +++ b/src/generation/mod.rs @@ -1,5 +1,8 @@ -use crate::{sieve, sieving::sieve_segment, Underlying}; +use crate::{restricted_array::RestrictedArray, sieve, sieving::sieve_segment, Underlying}; +pub mod segmented_generation_result; + +pub use segmented_generation_result::SegmentedGenerationResult; /// Returns the `N` first prime numbers. /// /// [`Primes`](crate::Primes) might be relevant for you if you intend to later use these prime numbers for related computations. @@ -106,44 +109,46 @@ pub const fn primes() -> [Underlying; N] { /// # Example /// Basic usage: /// ``` -/// # use const_primes::primes_lt; -/// const PRIMES: [u64; 10] = primes_lt(100); -/// +/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt}; +/// const PRIMES: SegmentedGenerationResult<10> = primes_lt(100); +/// assert_eq!(PRIMES[2], 61); /// assert_eq!(PRIMES, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::primes_lt; +/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt}; /// const N: usize = 70711; /// # #[allow(long_running_const_eval)] -/// const BIG_PRIMES: [u64; N] = primes_lt(5_000_000_030); +/// // If the generation results in a completely filled array, it can be extracted like this: +/// const BIG_PRIMES: [u64; N] = match primes_lt(5_000_000_030).complete() {Some(array) => array, _ => panic!()}; /// -/// assert_eq!(&BIG_PRIMES[..3], &[4_998_417_421, 4_998_417_427, 4_998_417_443]); -/// assert_eq!(&BIG_PRIMES[N - 3..], &[4_999_999_903, 4_999_999_937, 5_000_000_029]); +/// assert_eq!(BIG_PRIMES[..3], [4_998_417_421, 4_998_417_427, 4_998_417_443]); +/// assert_eq!(BIG_PRIMES[N - 3..], [4_999_999_903, 4_999_999_937, 5_000_000_029]); /// ``` -/// If there are not enough primes to fill the requested array, the first -/// elements will have a value of zero: +/// If there are not enough primes to fill the requested array, +/// the output will be the [`SegmentedGenerationResult::Partial`] variant, +/// which contains fewer primes than requested: /// ``` -/// # use const_primes::primes_lt; -/// const PRIMES: [u64; 9] = primes_lt(10); -/// -/// assert_eq!(PRIMES, [0, 0, 0, 0, 0, 2, 3, 5, 7]); +/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt}; +/// const PRIMES: SegmentedGenerationResult<9> = primes_lt(10); +/// // There are only four primes less than 10: +/// assert_eq!(PRIMES, [2, 3, 5, 7]); /// ``` /// # Panics /// Panics if `N^2` does not fit in a `u64` or if `upper_limit` is larger than `N^2`. This is a compile error /// in const contexts: /// ```compile_fail -/// # use const_primes::primes_lt; -/// // N is too large -/// const PRIMES: [u64; u32::MAX as usize + 1] = primes_lt(100); +/// # use const_primes::generation::{SegmentedGenerationResult,primes_lt}; +/// // N is too large +/// const PRIMES: SegmentedGenerationResult<{u32::MAX as usize + 1}> = primes_lt(100); /// ``` /// ```compile_fail -/// # use const_primes::primes_lt; -/// // N upper_limit > N^2 -/// const PRIMES: [u64; 5] = primes_lt(26); +/// # use const_primes::generation::{primes_lt, SegmentedGenerationResult}; +/// // N upper_limit > N^2 +/// const PRIMES: SegmentedGenerationResult<5> = primes_lt(26); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { +pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerationResult { let n64 = N as u64; match (n64).checked_mul(n64) { Some(prod) => assert!( @@ -159,7 +164,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { let base_sieve: [bool; N] = sieve(); let mut total_primes_found: usize = 0; - 'generate: while total_primes_found < N && upper_limit > 2 { + 'generate: while total_primes_found < N { // This is the smallest prime we have found so far. let mut smallest_found_prime = primes[N - 1 - total_primes_found]; // Sieve for primes in the segment. @@ -181,9 +186,13 @@ pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { i += 1; } upper_limit = smallest_found_prime; + if upper_limit <= 2 { + let restricted = RestrictedArray::new(N - total_primes_found..N, primes); + return SegmentedGenerationResult::Partial(restricted); + } } - primes + SegmentedGenerationResult::Complete(primes) } /// Returns the `N` smallest primes greater than or equal to `lower_limit`. @@ -222,7 +231,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> [u64; N] { /// const P: [u64; u32::MAX as usize + 1] = primes_geq(0); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { +pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenerationResult { let n64 = N as u64; if n64.checked_mul(n64).is_none() { panic!("`N^2` must fit in a `u64`"); @@ -243,7 +252,8 @@ pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { // We do not know if this is actually a prime // since the base sieve does not contain information about // the prime status of numbers larger than or equal to N. - break 'generate; + let restricted = RestrictedArray::new(0..total_found_primes, primes); + return SegmentedGenerationResult::Partial(restricted); } primes[total_found_primes] = largest_found_prime; total_found_primes += 1; @@ -256,7 +266,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> [u64; N] { } lower_limit = largest_found_prime + 1; } - primes + SegmentedGenerationResult::Complete(primes) } #[cfg(test)] @@ -268,20 +278,20 @@ mod test { #[test] fn sanity_check_primes_geq() { { - const P: [u64; 5] = primes_geq(10); + const P: [u64; 5] = match primes_geq(10).complete() {Some(a) => a, _ => panic!()}; assert_eq!(P, [11, 13, 17, 19, 23]); } { - const P: [u64; 5] = primes_geq(0); + const P: [u64; 5] = match primes_geq(0).complete() {Some(a) => a, _ => panic!()}; assert_eq!(P, [2, 3, 5, 7, 11]); } { - const P: [u64; 0] = primes_geq(0); - assert_eq!(P, []); + const P: SegmentedGenerationResult<0> = primes_geq(0); + assert_eq!(P.as_ref(), []); } { - const P: [u64; 1] = primes_geq(0); - assert_eq!(P, [0]); + const P: SegmentedGenerationResult<1> = primes_geq(0); + assert_eq!(P, [2]); } for prime in primes_geq::<2_000>(3_998_000) { if prime == 0 { diff --git a/src/generation/segmented_generation_result.rs b/src/generation/segmented_generation_result.rs new file mode 100644 index 0000000..8ac2739 --- /dev/null +++ b/src/generation/segmented_generation_result.rs @@ -0,0 +1,148 @@ +use core::iter::FusedIterator; + +use crate::restricted_array::RestrictedArray; + +/// An enum describing whether the requested array could be filled completely, or only a partially. +/// A partial array can be returned by [`primes_lt`](crate::generation::primes_lt) if the size of the requested +/// array is larger than the actual number of primes less than the given `upper_limit`. +/// It can also be returned by [`primes_geq`](crate::generation::primes_geq) if it needs to sieve into a +/// region of numbers that exceed the square of the size of the requested array. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum SegmentedGenerationResult { + /// The complete range could be sieved, and the entire output array contains prime numbers. + Complete([u64; N]), + /// Only a part of the range could be sieved before the primes either exceeded `N^2` or ran out, + /// and so only a part of the output array contains prime numbers. + Partial(RestrictedArray), +} + +// region: enum convenience method impls +impl SegmentedGenerationResult { + /// Returns the complete array, if there is one. + pub const fn complete(self) -> Option<[u64; N]> { + match self { + Self::Complete(array) => Some(array), + _ => None, + } + } + + /// Returns the restriced array, if there is one. + pub const fn partial(self) -> Option> { + match self { + Self::Partial(restricted_array) => Some(restricted_array), + _ => None, + } + } + + /// Returns `true` if this is the `Complete` variant. + pub const fn is_complete(&self) -> bool { + match self { + Self::Complete(_) => true, + _ => false, + } + } + + /// Returns `true` if this is the `Partial` variant. + pub const fn is_partial(&self) -> bool { + match self { + Self::Partial(_) => true, + _ => false, + } + } +} +// endregion: enum convenience method impls + +// region: IntoIterator impl +/// An iterator created by the [`IntoIterator`] impl on [`SegmentedGenerationResult`]. +pub enum SegmentedGenerationResultIntoIter { + Complete(<[u64; N] as IntoIterator>::IntoIter), + Partial( as IntoIterator>::IntoIter), +} + +impl Iterator for SegmentedGenerationResultIntoIter { + type Item = u64; + fn next(&mut self) -> Option { + match self { + Self::Complete(array_iter) => array_iter.next(), + Self::Partial(restricted_array_iter) => restricted_array_iter.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match self { + Self::Complete(array_iter) => array_iter.size_hint(), + Self::Partial(restricted_array_iter) => restricted_array_iter.size_hint(), + } + } +} + +impl DoubleEndedIterator for SegmentedGenerationResultIntoIter { + fn next_back(&mut self) -> Option { + match self { + Self::Complete(array_iter) => array_iter.next_back(), + Self::Partial(restricted_array_iter) => restricted_array_iter.next_back(), + } + } +} + +impl FusedIterator for SegmentedGenerationResultIntoIter {} +impl ExactSizeIterator for SegmentedGenerationResultIntoIter {} + +impl IntoIterator for SegmentedGenerationResult { + type IntoIter = SegmentedGenerationResultIntoIter; + type Item = u64; + fn into_iter(self) -> Self::IntoIter { + match self { + Self::Complete(array) => SegmentedGenerationResultIntoIter::Complete(array.into_iter()), + Self::Partial(restricted_array) => { + SegmentedGenerationResultIntoIter::Partial(restricted_array.into_iter()) + } + } + } +} +// endregion: IntoIterator impl + +// region: PartialEq impls +impl PartialEq for SegmentedGenerationResult +where + T: PartialEq<[u64]>, +{ + fn eq(&self, other: &T) -> bool { + match self { + Self::Complete(array) => other == array.as_ref(), + Self::Partial(restricted_array) => other == restricted_array.as_slice(), + } + } +} + +impl PartialEq> for [T] +where + T: PartialEq, +{ + fn eq(&self, other: &SegmentedGenerationResult) -> bool { + match other { + SegmentedGenerationResult::Complete(array) => self == array, + SegmentedGenerationResult::Partial(restricted_array) => restricted_array == self, + } + } +} +// endregion: PartialEq impls + +impl AsRef<[u64]> for SegmentedGenerationResult { + fn as_ref(&self) -> &[u64] { + match self { + Self::Complete(array) => array.as_slice(), + Self::Partial(restricted_array) => restricted_array.as_slice(), + } + } +} + +impl core::ops::Index for SegmentedGenerationResult { + type Output = u64; + fn index(&self, index: usize) -> &Self::Output { + match self { + Self::Complete(array) => &array[index], + Self::Partial(restricted_array) => &restricted_array[index], + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index a14a0da..8152add 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,10 +47,10 @@ //! ``` //! ## Arbitrary ranges //! -//! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] +//! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`](crate::generation::primes_geq) and [`sieve_lt`] //! that can be used to work with ranges that don't start at zero. //! ``` -//! # use const_primes::primes_geq; +//! # use const_primes::generation::primes_geq; //! const N: usize = 70722; //! # #[allow(long_running_const_eval)] //! const PRIMES_GEQ: [u64; N] = primes_geq(5_000_000_031); @@ -97,7 +97,7 @@ // This is used since there is currently no way to be generic over types that can do arithmetic at compile time. type Underlying = u32; -mod generation; +pub mod generation; mod imath; mod miller_rabin; mod other_prime; @@ -105,7 +105,7 @@ pub mod restricted_array; mod sieving; mod wrapper; -pub use generation::{primes, primes_geq, primes_lt}; +pub use generation::primes; use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; @@ -150,6 +150,8 @@ pub const fn prime_counts() -> [usize; N] { mod test { use super::*; + use generation::{primes_lt, segmented_generation_result::SegmentedGenerationResult}; + #[test] fn check_primes_is_prime() { const SET: Primes<3> = Primes::new(); @@ -233,8 +235,10 @@ mod test { ($($n:expr),+) => { $( { - const P: [u64; $n] = primes_lt(100); - assert_eq!(PRECOMPUTED_PRIMES[25-$n..25], P.map(|i|i as u32)); + const P: SegmentedGenerationResult<$n> = primes_lt(100); + for (i, prime) in P.into_iter().enumerate() { + assert_eq!(PRECOMPUTED_PRIMES[25-$n..25][i], prime as u32); + } assert_eq!( PRECOMPUTED_PRIMES[25-$n..25], primes_lt::<$n>(100).into_iter().map(|i|i as u32).collect::>() @@ -246,9 +250,9 @@ mod test { test_n_below_100!(10, 15, 20); - assert_eq!([0, 0, 0, 0, 0, 2, 3, 5, 7], primes_lt(10)); + assert_eq!([2, 3, 5, 7], primes_lt::<9>(10).as_ref()); - assert_eq!([0, 2], primes_lt(3)); + assert_eq!(primes_lt::<2>(3), [2]); } #[test] diff --git a/src/restricted_array.rs b/src/restricted_array.rs index aefe596..755eadd 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -156,13 +156,29 @@ impl IntoIterator for RestrictedArray { } } -impl PartialEq for RestrictedArray +// region: PartialEq impls +impl PartialEq<[U]> for RestrictedArray where - U: PartialEq<[T]>, + U: PartialEq, { /// This method tests for `self` and `other` values to be equal, and is used by `==`. /// Only compares the *unrestricted* part of `self` against `other`. - fn eq(&self, other: &U) -> bool { + fn eq(&self, other: &[U]) -> bool { other == self.as_slice() } } + +impl> PartialEq> for [U] { + /// This method tests for `self` and `other` values to be equal, and is used by `==`. + /// Only compares the *unrestricted* part of `other` against `self`. + fn eq(&self, other: &RestrictedArray) -> bool { + self == other.as_slice() + } +} +// endregion: PartialEq impls + +impl AsRef<[T]> for RestrictedArray { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} \ No newline at end of file From 5a131630bf5acb4300513a52fc537eb8879fd527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 30 Oct 2023 12:18:35 +0100 Subject: [PATCH 060/212] Shorten docstring of enum variants --- src/generation/segmented_generation_result.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/generation/segmented_generation_result.rs b/src/generation/segmented_generation_result.rs index 8ac2739..ac90520 100644 --- a/src/generation/segmented_generation_result.rs +++ b/src/generation/segmented_generation_result.rs @@ -9,10 +9,9 @@ use crate::restricted_array::RestrictedArray; /// region of numbers that exceed the square of the size of the requested array. #[derive(Debug, Clone, Copy, PartialEq)] pub enum SegmentedGenerationResult { - /// The complete range could be sieved, and the entire output array contains prime numbers. + /// The entire output array contains prime numbers. Complete([u64; N]), - /// Only a part of the range could be sieved before the primes either exceeded `N^2` or ran out, - /// and so only a part of the output array contains prime numbers. + /// Only a part of the output array contains prime numbers as they either exceeded `N^2` or ran out. Partial(RestrictedArray), } From f41eeea8bf45c50fcdd5e76f9b5906f858586348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 30 Oct 2023 12:28:17 +0100 Subject: [PATCH 061/212] Talk about RestrictedArray in terms of visiblillity --- benches/prime_benches.rs | 5 ++- src/generation/mod.rs | 12 +++++-- src/generation/segmented_generation_result.rs | 2 +- src/restricted_array.rs | 34 ++++++++++--------- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index a4604b3..21c2cdf 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,4 +1,7 @@ -use const_primes::{is_prime, primes, generation::{primes_geq, primes_lt}, sieve, sieve_geq, sieve_lt}; +use const_primes::{ + generation::{primes_geq, primes_lt}, + is_prime, primes, sieve, sieve_geq, sieve_lt, +}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; use rand::prelude::*; use std::hint::black_box; diff --git a/src/generation/mod.rs b/src/generation/mod.rs index bcf62cd..201c0a1 100644 --- a/src/generation/mod.rs +++ b/src/generation/mod.rs @@ -125,7 +125,7 @@ pub const fn primes() -> [Underlying; N] { /// assert_eq!(BIG_PRIMES[..3], [4_998_417_421, 4_998_417_427, 4_998_417_443]); /// assert_eq!(BIG_PRIMES[N - 3..], [4_999_999_903, 4_999_999_937, 5_000_000_029]); /// ``` -/// If there are not enough primes to fill the requested array, +/// If there are not enough primes to fill the requested array, /// the output will be the [`SegmentedGenerationResult::Partial`] variant, /// which contains fewer primes than requested: /// ``` @@ -278,11 +278,17 @@ mod test { #[test] fn sanity_check_primes_geq() { { - const P: [u64; 5] = match primes_geq(10).complete() {Some(a) => a, _ => panic!()}; + const P: [u64; 5] = match primes_geq(10).complete() { + Some(a) => a, + _ => panic!(), + }; assert_eq!(P, [11, 13, 17, 19, 23]); } { - const P: [u64; 5] = match primes_geq(0).complete() {Some(a) => a, _ => panic!()}; + const P: [u64; 5] = match primes_geq(0).complete() { + Some(a) => a, + _ => panic!(), + }; assert_eq!(P, [2, 3, 5, 7, 11]); } { diff --git a/src/generation/segmented_generation_result.rs b/src/generation/segmented_generation_result.rs index ac90520..f817ff7 100644 --- a/src/generation/segmented_generation_result.rs +++ b/src/generation/segmented_generation_result.rs @@ -144,4 +144,4 @@ impl core::ops::Index for SegmentedGenerationResult { Self::Partial(restricted_array) => &restricted_array[index], } } -} \ No newline at end of file +} diff --git a/src/restricted_array.rs b/src/restricted_array.rs index 755eadd..ef69ea3 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -23,7 +23,7 @@ impl Copy for RestrictedArray {} impl> PartialEq> for RestrictedArray { /// This method tests for `self` and `other` values to be equal, and is used by `==`. - /// Only compares the *unrestricted* part of `self` against the *unrestricted* part of `other`. + /// Only compares the *visible* part of `self` against the *visible* part of `other`. fn eq(&self, other: &RestrictedArray) -> bool { self.as_slice() == other.as_slice() } @@ -32,7 +32,7 @@ impl> PartialEq> for Restr impl Eq for RestrictedArray {} impl RestrictedArray { - /// Restrict an array so that only elements within the given range are viewable. + /// Restrict an array so that only elements within the given range are visible. /// /// # Panics /// Panics if the range of indices is out of bounds of the array. @@ -57,51 +57,53 @@ impl RestrictedArray { } } - /// Returns a reference to the full underlying array. + /// Returns a reference to the full underlying array. There is no guarantee about the data + /// outside the visible region. pub const fn as_full_array(&self) -> &[T; N] { &self.array } - /// Converts `self` into the full underlying array. + /// Converts `self` into the full underlying array. There is no guarantee about the data + /// outside the visible region. pub fn into_full_array(self) -> [T; N] { self.array } - /// Returns the unrestricted part of the array as a slice. + /// Returns the visible part of the array as a slice. pub const fn as_slice(&self) -> &[T] { let (_, tail) = self.array.split_at(self.start); tail.split_at(self.end - self.start).0 } - /// Returns the index of the first element of the underlying array that's not restricted. + /// Returns the index of the first element of the underlying array that's inside the visible region. pub const fn start(&self) -> usize { self.start } /// Returns the index of the first element of the underlying array that is - /// restricted again after the end of the unrestricted part. + /// invisible again after the end of the visible part. pub const fn end(&self) -> usize { self.end } - /// Returns the length of the unrestricted part of the array. + /// Returns the length of the visible part of the array. pub const fn len(&self) -> usize { self.end - self.start } - /// Returns whether the unrestricted part is empty. + /// Returns whether the visible part is empty. pub const fn is_empty(&self) -> bool { self.len() == 0 } - /// Returns whether there are parts of the array that are restricted. - /// If this is `false` it is completely fine to call [`as_full_array`](RestrictedArray::as_full_array) + /// Returns whether there are no parts of the array that are invisible. + /// If this is `true` it is completely fine to call [`as_full_array`](RestrictedArray::as_full_array) /// or [`into_full_array`](RestrictedArray::into_full_array). - pub const fn is_restricted(&self) -> bool { + pub const fn is_fully_visible(&self) -> bool { self.len() == N } - /// Returns an iterator over the unrestricted section. + /// Returns an iterator over the visible section. pub fn iter(&self) -> core::slice::Iter<'_, T> { self.as_slice().iter() } @@ -162,7 +164,7 @@ where U: PartialEq, { /// This method tests for `self` and `other` values to be equal, and is used by `==`. - /// Only compares the *unrestricted* part of `self` against `other`. + /// Only compares the *visible* part of `self` against `other`. fn eq(&self, other: &[U]) -> bool { other == self.as_slice() } @@ -170,7 +172,7 @@ where impl> PartialEq> for [U] { /// This method tests for `self` and `other` values to be equal, and is used by `==`. - /// Only compares the *unrestricted* part of `other` against `self`. + /// Only compares the *visible* part of `other` against `self`. fn eq(&self, other: &RestrictedArray) -> bool { self == other.as_slice() } @@ -181,4 +183,4 @@ impl AsRef<[T]> for RestrictedArray { fn as_ref(&self) -> &[T] { self.as_slice() } -} \ No newline at end of file +} From c5e5daf4cf06740b4b73b17173b401bc55af6f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 30 Oct 2023 12:32:10 +0100 Subject: [PATCH 062/212] Improve docstring for RestrictedArrayIntoIter --- src/restricted_array.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index ef69ea3..9ee059e 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -125,7 +125,7 @@ impl core::ops::Index for RestrictedArray { } } -/// Created by the [`IntoIterator`] trait implementation on [`RestrictedArray`]. +/// Created by the [`into_iter`](RestrictedArray::into_iter) function on [`RestrictedArray`], see it for more information. pub struct RestrictedArrayIntoIter( core::iter::Take>>, ); From 1e38b8947cd4b27f73d15c3c5e242e127b840e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 30 Oct 2023 12:35:10 +0100 Subject: [PATCH 063/212] Add note about comparisons to RestirctedArray docstring --- src/restricted_array.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index 9ee059e..36a6524 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -1,7 +1,9 @@ use core::{iter::FusedIterator, ops::Range}; /// An array where only a section of the data may be viewed, -/// as the other data may e.g. not uphold some invariant. +/// as the other data may e.g. not uphold some invariant. +/// When this type is compared against some other type, only +/// data in the visible part is compared. #[derive(Debug)] pub struct RestrictedArray { start: usize, From 9445e49211e9f4fc245f62728ea2cc9ba91a12aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 Nov 2023 08:31:04 +0100 Subject: [PATCH 064/212] Refactor result --- src/{generation/mod.rs => generation.rs} | 120 +++++++++----- src/generation/segmented_generation_result.rs | 147 ------------------ src/lib.rs | 28 +++- 3 files changed, 103 insertions(+), 192 deletions(-) rename src/{generation/mod.rs => generation.rs} (70%) delete mode 100644 src/generation/segmented_generation_result.rs diff --git a/src/generation/mod.rs b/src/generation.rs similarity index 70% rename from src/generation/mod.rs rename to src/generation.rs index 201c0a1..a28edf5 100644 --- a/src/generation/mod.rs +++ b/src/generation.rs @@ -1,8 +1,10 @@ +use core::fmt; + use crate::{restricted_array::RestrictedArray, sieve, sieving::sieve_segment, Underlying}; -pub mod segmented_generation_result; +/// Type alias for the type returned by the segmented generation functions, that otherwise has two generics that must be the same. +pub type SegmentedGenerationResult = Result<[u64; N], SegmentedGenerationError>; -pub use segmented_generation_result::SegmentedGenerationResult; /// Returns the `N` first prime numbers. /// /// [`Primes`](crate::Primes) might be relevant for you if you intend to later use these prime numbers for related computations. @@ -109,30 +111,35 @@ pub const fn primes() -> [Underlying; N] { /// # Example /// Basic usage: /// ``` -/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt}; +/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt, SegmentedGenerationError}; /// const PRIMES: SegmentedGenerationResult<10> = primes_lt(100); -/// assert_eq!(PRIMES[2], 61); -/// assert_eq!(PRIMES, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); +/// assert_eq!(PRIMES?, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); +/// # Ok::<(), SegmentedGenerationError<10>>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt}; +/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt, SegmentedGenerationError}; /// const N: usize = 70711; /// # #[allow(long_running_const_eval)] /// // If the generation results in a completely filled array, it can be extracted like this: -/// const BIG_PRIMES: [u64; N] = match primes_lt(5_000_000_030).complete() {Some(array) => array, _ => panic!()}; +/// const BIG_PRIMES: SegmentedGenerationResult = primes_lt(5_000_000_030); /// -/// assert_eq!(BIG_PRIMES[..3], [4_998_417_421, 4_998_417_427, 4_998_417_443]); -/// assert_eq!(BIG_PRIMES[N - 3..], [4_999_999_903, 4_999_999_937, 5_000_000_029]); +/// assert_eq!(BIG_PRIMES?[..3], [4_998_417_421, 4_998_417_427, 4_998_417_443]); +/// assert_eq!(BIG_PRIMES?[N - 3..], [4_999_999_903, 4_999_999_937, 5_000_000_029]); +/// # Ok::<(), SegmentedGenerationError>(()) /// ``` /// If there are not enough primes to fill the requested array, -/// the output will be the [`SegmentedGenerationResult::Partial`] variant, +/// the output will be the [`SegmentedGenerationError::PartialOk`] variant, /// which contains fewer primes than requested: /// ``` /// # use const_primes::generation::{SegmentedGenerationResult, primes_lt}; /// const PRIMES: SegmentedGenerationResult<9> = primes_lt(10); -/// // There are only four primes less than 10: -/// assert_eq!(PRIMES, [2, 3, 5, 7]); +/// match PRIMES.err() { +/// Some(SegmentedGenerationError::PartialOk(arr)) => { +/// // There are only four primes less than 10: +/// assert_eq!(arr.as_slice(), [2, 3, 5, 7]);} +/// _ => panic!(), +/// } /// ``` /// # Panics /// Panics if `N^2` does not fit in a `u64` or if `upper_limit` is larger than `N^2`. This is a compile error @@ -151,11 +158,12 @@ pub const fn primes() -> [Underlying; N] { pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerationResult { let n64 = N as u64; match (n64).checked_mul(n64) { - Some(prod) => assert!( - upper_limit <= prod, - "`upper_limit` must be less than or equal to `N^2`" - ), - None => panic!("`N^2` must fit in a `u64`"), + Some(prod) => { + if upper_limit > prod { + return Err(SegmentedGenerationError::TooLargeLimit); + } + } + None => return Err(SegmentedGenerationError::TooLargeN), } let mut primes: [u64; N] = [0; N]; @@ -186,13 +194,13 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat i += 1; } upper_limit = smallest_found_prime; - if upper_limit <= 2 { + if upper_limit <= 2 && total_primes_found < N { let restricted = RestrictedArray::new(N - total_primes_found..N, primes); - return SegmentedGenerationResult::Partial(restricted); + return Err(SegmentedGenerationError::PartialOk(restricted)); } } - SegmentedGenerationResult::Complete(primes) + Ok(primes) } /// Returns the `N` smallest primes greater than or equal to `lower_limit`. @@ -234,7 +242,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenerationResult { let n64 = N as u64; if n64.checked_mul(n64).is_none() { - panic!("`N^2` must fit in a `u64`"); + return Err(SegmentedGenerationError::TooLargeN); } let mut primes = [0; N]; @@ -253,7 +261,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera // since the base sieve does not contain information about // the prime status of numbers larger than or equal to N. let restricted = RestrictedArray::new(0..total_found_primes, primes); - return SegmentedGenerationResult::Partial(restricted); + return Err(SegmentedGenerationError::PartialOk(restricted)); } primes[total_found_primes] = largest_found_prime; total_found_primes += 1; @@ -266,7 +274,47 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera } lower_limit = largest_found_prime + 1; } - SegmentedGenerationResult::Complete(primes) + Ok(primes) +} + +/// An enum describing whether the requested array could be filled completely, or only a partially. +/// A partial array can be returned by [`primes_lt`](crate::generation::primes_lt) if the size of the requested +/// array is larger than the actual number of primes less than the given `upper_limit`. +/// It can also be returned by [`primes_geq`](crate::generation::primes_geq) if it needs to sieve into a +/// region of numbers that exceed the square of the size of the requested array. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum SegmentedGenerationError { + /// `N^2`` did not fit in a `u64`. + TooLargeN, + /// the upper limit was larger than `N^2`. + TooLargeLimit, + /// Only a part of the output array contains prime numbers as they either exceeded `N^2` or ran out. + PartialOk(RestrictedArray), +} + +impl SegmentedGenerationError { + /// Returns the partial result as a restricted array, if there is one. + pub const fn partial_ok(self) -> Option> { + match self { + Self::PartialOk(restricted_array) => Some(restricted_array), + _ => None, + } + } + + /// Returns `true` if this is the `PartialOk` variant. + pub const fn is_partial_ok(&self) -> bool { + matches!(self, Self::PartialOk(_)) + } +} + +impl fmt::Display for SegmentedGenerationError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::TooLargeN => write!(f, "`N^2` did not fit in a `u64`"), + Self::TooLargeLimit => write!(f, "the upper limit was larger than `N^2`"), + Self::PartialOk(_) => write!(f, "the sieve entered into a range it's too small for, or the primes ran out. You can access the partially completed result with the function `partial_result`"), + } + } } #[cfg(test)] @@ -278,28 +326,22 @@ mod test { #[test] fn sanity_check_primes_geq() { { - const P: [u64; 5] = match primes_geq(10).complete() { - Some(a) => a, - _ => panic!(), - }; - assert_eq!(P, [11, 13, 17, 19, 23]); + const P: SegmentedGenerationResult<5> = primes_geq(10); + assert_eq!(P, Ok([11, 13, 17, 19, 23])); } { - const P: [u64; 5] = match primes_geq(0).complete() { - Some(a) => a, - _ => panic!(), - }; - assert_eq!(P, [2, 3, 5, 7, 11]); - } - { - const P: SegmentedGenerationResult<0> = primes_geq(0); - assert_eq!(P.as_ref(), []); + const P: SegmentedGenerationResult<5> = primes_geq(0); + assert_eq!(P, Ok([2, 3, 5, 7, 11])); } { const P: SegmentedGenerationResult<1> = primes_geq(0); - assert_eq!(P, [2]); + assert_eq!(P, Err(SegmentedGenerationError::TooLargeLimit)); } - for prime in primes_geq::<2_000>(3_998_000) { + for prime in primes_geq::<2_000>(3_998_000) + .unwrap_err() + .partial_ok() + .unwrap() + { if prime == 0 { break; } diff --git a/src/generation/segmented_generation_result.rs b/src/generation/segmented_generation_result.rs deleted file mode 100644 index f817ff7..0000000 --- a/src/generation/segmented_generation_result.rs +++ /dev/null @@ -1,147 +0,0 @@ -use core::iter::FusedIterator; - -use crate::restricted_array::RestrictedArray; - -/// An enum describing whether the requested array could be filled completely, or only a partially. -/// A partial array can be returned by [`primes_lt`](crate::generation::primes_lt) if the size of the requested -/// array is larger than the actual number of primes less than the given `upper_limit`. -/// It can also be returned by [`primes_geq`](crate::generation::primes_geq) if it needs to sieve into a -/// region of numbers that exceed the square of the size of the requested array. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum SegmentedGenerationResult { - /// The entire output array contains prime numbers. - Complete([u64; N]), - /// Only a part of the output array contains prime numbers as they either exceeded `N^2` or ran out. - Partial(RestrictedArray), -} - -// region: enum convenience method impls -impl SegmentedGenerationResult { - /// Returns the complete array, if there is one. - pub const fn complete(self) -> Option<[u64; N]> { - match self { - Self::Complete(array) => Some(array), - _ => None, - } - } - - /// Returns the restriced array, if there is one. - pub const fn partial(self) -> Option> { - match self { - Self::Partial(restricted_array) => Some(restricted_array), - _ => None, - } - } - - /// Returns `true` if this is the `Complete` variant. - pub const fn is_complete(&self) -> bool { - match self { - Self::Complete(_) => true, - _ => false, - } - } - - /// Returns `true` if this is the `Partial` variant. - pub const fn is_partial(&self) -> bool { - match self { - Self::Partial(_) => true, - _ => false, - } - } -} -// endregion: enum convenience method impls - -// region: IntoIterator impl -/// An iterator created by the [`IntoIterator`] impl on [`SegmentedGenerationResult`]. -pub enum SegmentedGenerationResultIntoIter { - Complete(<[u64; N] as IntoIterator>::IntoIter), - Partial( as IntoIterator>::IntoIter), -} - -impl Iterator for SegmentedGenerationResultIntoIter { - type Item = u64; - fn next(&mut self) -> Option { - match self { - Self::Complete(array_iter) => array_iter.next(), - Self::Partial(restricted_array_iter) => restricted_array_iter.next(), - } - } - - fn size_hint(&self) -> (usize, Option) { - match self { - Self::Complete(array_iter) => array_iter.size_hint(), - Self::Partial(restricted_array_iter) => restricted_array_iter.size_hint(), - } - } -} - -impl DoubleEndedIterator for SegmentedGenerationResultIntoIter { - fn next_back(&mut self) -> Option { - match self { - Self::Complete(array_iter) => array_iter.next_back(), - Self::Partial(restricted_array_iter) => restricted_array_iter.next_back(), - } - } -} - -impl FusedIterator for SegmentedGenerationResultIntoIter {} -impl ExactSizeIterator for SegmentedGenerationResultIntoIter {} - -impl IntoIterator for SegmentedGenerationResult { - type IntoIter = SegmentedGenerationResultIntoIter; - type Item = u64; - fn into_iter(self) -> Self::IntoIter { - match self { - Self::Complete(array) => SegmentedGenerationResultIntoIter::Complete(array.into_iter()), - Self::Partial(restricted_array) => { - SegmentedGenerationResultIntoIter::Partial(restricted_array.into_iter()) - } - } - } -} -// endregion: IntoIterator impl - -// region: PartialEq impls -impl PartialEq for SegmentedGenerationResult -where - T: PartialEq<[u64]>, -{ - fn eq(&self, other: &T) -> bool { - match self { - Self::Complete(array) => other == array.as_ref(), - Self::Partial(restricted_array) => other == restricted_array.as_slice(), - } - } -} - -impl PartialEq> for [T] -where - T: PartialEq, -{ - fn eq(&self, other: &SegmentedGenerationResult) -> bool { - match other { - SegmentedGenerationResult::Complete(array) => self == array, - SegmentedGenerationResult::Partial(restricted_array) => restricted_array == self, - } - } -} -// endregion: PartialEq impls - -impl AsRef<[u64]> for SegmentedGenerationResult { - fn as_ref(&self) -> &[u64] { - match self { - Self::Complete(array) => array.as_slice(), - Self::Partial(restricted_array) => restricted_array.as_slice(), - } - } -} - -impl core::ops::Index for SegmentedGenerationResult { - type Output = u64; - fn index(&self, index: usize) -> &Self::Output { - match self { - Self::Complete(array) => &array[index], - Self::Partial(restricted_array) => &restricted_array[index], - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 8152add..4f353af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,7 +150,7 @@ pub const fn prime_counts() -> [usize; N] { mod test { use super::*; - use generation::{primes_lt, segmented_generation_result::SegmentedGenerationResult}; + use generation::{primes_lt, SegmentedGenerationError}; #[test] fn check_primes_is_prime() { @@ -235,13 +235,13 @@ mod test { ($($n:expr),+) => { $( { - const P: SegmentedGenerationResult<$n> = primes_lt(100); - for (i, prime) in P.into_iter().enumerate() { + const P: Result<[u64; 10], SegmentedGenerationError<10>> = primes_lt(100); + for (i, prime) in P.unwrap().into_iter().enumerate() { assert_eq!(PRECOMPUTED_PRIMES[25-$n..25][i], prime as u32); } assert_eq!( PRECOMPUTED_PRIMES[25-$n..25], - primes_lt::<$n>(100).into_iter().map(|i|i as u32).collect::>() + primes_lt::<$n>(100).unwrap().into_iter().map(|i|i as u32).collect::>() ); } )+ @@ -250,9 +250,25 @@ mod test { test_n_below_100!(10, 15, 20); - assert_eq!([2, 3, 5, 7], primes_lt::<9>(10).as_ref()); + assert_eq!( + [2, 3, 5, 7], + primes_lt::<9>(10) + .err() + .unwrap() + .partial_ok() + .unwrap() + .as_slice() + ); - assert_eq!(primes_lt::<2>(3), [2]); + assert_eq!( + primes_lt::<2>(3) + .err() + .unwrap() + .partial_ok() + .unwrap() + .as_slice(), + [2] + ); } #[test] From 05a68b67492b5dccb245d93e397c22c219d86aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 Nov 2023 13:13:55 +0100 Subject: [PATCH 065/212] smallest_prime_geq --> next_prime --- src/wrapper.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/wrapper.rs b/src/wrapper.rs index 5d85db5..97ba1fd 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -151,21 +151,28 @@ impl Primes { } } - /// Returns the smallest prime greater than or equal to `n`. - /// If `n` is larger than the largest prime in `self` this returns `None`. + /// Returns the smallest prime greater than `n`. + /// If `n` is larger than or equal to the largest prime in `self` this returns `None`. /// /// Uses a binary search. /// # Example /// ``` /// # use const_primes::Primes; /// const CACHE: Primes<100> = Primes::new(); - /// const SPGEQ: Option = CACHE.smallest_prime_geq(400); - /// assert_eq!(SPGEQ, Some(401)); + /// const NEXT: Option = CACHE.next_prime(400); + /// assert_eq!(NEXT, Some(401)); /// ``` #[must_use = "the method only returns a new value and does not modify `self`"] - pub const fn smallest_prime_geq(&self, n: Underlying) -> Option { + pub const fn next_prime(&self, n: Underlying) -> Option { match self.binary_search(n) { - Ok(i) | Err(Some(i)) => Some(self.primes[i]), + Ok(i) => { + if i + 1 < self.len() { + Some(self.primes[i + 1]) + } else { + None + } + } + Err(Some(i)) => Some(self.primes[i]), Err(None) => None, } } @@ -494,27 +501,27 @@ mod test { } #[test] - fn check_smallest_prime_geq() { + fn check_next_prime() { const CACHE: Primes<100> = Primes::new(); - const SPGEQ0: Option = CACHE.smallest_prime_geq(0); - const SPGEQ400: Option = CACHE.smallest_prime_geq(400); - const SPGEQ541: Option = CACHE.smallest_prime_geq(541); - const SPGEQ542: Option = CACHE.smallest_prime_geq(542); + const SPGEQ0: Option = CACHE.next_prime(0); + const SPGEQ400: Option = CACHE.next_prime(400); + const SPGEQ541: Option = CACHE.next_prime(540); + const SPGEQ542: Option = CACHE.next_prime(541); assert_eq!(SPGEQ0, Some(2)); assert_eq!(SPGEQ400, Some(401)); assert_eq!(SPGEQ541, Some(541)); assert_eq!(SPGEQ542, None); - const N: usize = 32; + const N: usize = 31; const NEXT_PRIME: [u32; N] = [ - 2, 2, 2, 3, 5, 5, 7, 7, 11, 11, 11, 11, 13, 13, 17, 17, 17, 17, 19, 19, 23, 23, 23, 23, + 2, 2, 3, 5, 5, 7, 7, 11, 11, 11, 11, 13, 13, 17, 17, 17, 17, 19, 19, 23, 23, 23, 23, 29, 29, 29, 29, 29, 29, 31, 31, ]; const P: Primes = Primes::new(); for n in 0..N { println!("{n}"); - assert_eq!(P.smallest_prime_geq(n as u32), Some(NEXT_PRIME[n])); + assert_eq!(P.next_prime(n as u32), Some(NEXT_PRIME[n])); } } From 7d843a9db593d0ccceb21c6c4accd9d427369dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 Nov 2023 13:25:32 +0100 Subject: [PATCH 066/212] rename largest_prime_leq to previous_prime --- src/wrapper.rs | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/src/wrapper.rs b/src/wrapper.rs index 97ba1fd..d881541 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -127,25 +127,30 @@ impl Primes { // region: Next prime - /// Returns the largest prime less than or equal to `n`. - /// If `n` is 0, 1, or larger than the largest prime in `self` this returns `None`. + /// Returns the largest prime less than `n`. + /// If `n` is 0, 1, 2, or larger than the largest prime in `self` this returns `None`. /// /// Uses a binary search. /// # Example /// ``` /// # use const_primes::Primes; /// const CACHE: Primes<100> = Primes::new(); - /// const LPLEQ400: Option = CACHE.largest_prime_leq(400); - /// assert_eq!(LPLEQ400, Some(397)); + /// const PREV400: Option = CACHE.previous_prime(400); + /// assert_eq!(PREV400, Some(397)); /// ``` #[must_use = "the method only returns a new value and does not modify `self`"] - pub const fn largest_prime_leq(&self, n: Underlying) -> Option { - if n <= 1 { + pub const fn previous_prime(&self, n: Underlying) -> Option { + if n <= 2 { None } else { match self.binary_search(n) { - Ok(i) => Some(self.primes[i]), - Err(Some(i)) => Some(self.primes[i - 1]), + Ok(i) | Err(Some(i)) => { + if i > 0 { + Some(self.primes[i - 1]) + } else { + None + } + } Err(None) => None, } } @@ -488,16 +493,23 @@ mod test { } #[test] - fn check_largest_prime_leq() { + fn check_previous_prime() { const CACHE: Primes<100> = Primes::new(); - const LPLEQ0: Option = CACHE.largest_prime_leq(0); - const LPLEQ400: Option = CACHE.largest_prime_leq(400); - const LPLEQ541: Option = CACHE.largest_prime_leq(541); - const LPLEQ542: Option = CACHE.largest_prime_leq(542); - assert_eq!(LPLEQ0, None); - assert_eq!(LPLEQ400, Some(397)); - assert_eq!(LPLEQ541, Some(541)); - assert_eq!(LPLEQ542, None); + const PREV0: Option = CACHE.previous_prime(0); + const PREV400: Option = CACHE.previous_prime(400); + const PREV541: Option = CACHE.previous_prime(541); + const PREV542: Option = CACHE.previous_prime(542); + const PREVS: [Underlying; 18] = [ + 2, 3, 3, 5, 5, 7, 7, 7, 7, 11, 11, 13, 13, 13, 13, 17, 17, 19, + ]; + for (i, prev) in PREVS.into_iter().enumerate() { + println!("n = {}", i + 3); + assert_eq!(Some(prev), CACHE.previous_prime(i as u32 + 3)); + } + assert_eq!(PREV0, None); + assert_eq!(PREV400, Some(397)); + assert_eq!(PREV541, Some(523)); + assert_eq!(PREV542, None); } #[test] From 52f4f2bc045d6f90bdf83045376ce3181f70b429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 Nov 2023 11:21:46 +0100 Subject: [PATCH 067/212] Improve some docstrings --- src/generation.rs | 4 ++-- src/wrapper.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index a28edf5..024f9d9 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -278,9 +278,9 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera } /// An enum describing whether the requested array could be filled completely, or only a partially. -/// A partial array can be returned by [`primes_lt`](crate::generation::primes_lt) if the size of the requested +/// A partial array can be returned by [`primes_lt`] if the size of the requested /// array is larger than the actual number of primes less than the given `upper_limit`. -/// It can also be returned by [`primes_geq`](crate::generation::primes_geq) if it needs to sieve into a +/// It can also be returned by [`primes_geq`] if it needs to sieve into a /// region of numbers that exceed the square of the size of the requested array. #[derive(Debug, Clone, Copy, PartialEq)] pub enum SegmentedGenerationError { diff --git a/src/wrapper.rs b/src/wrapper.rs index d881541..6072f48 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -187,10 +187,10 @@ impl Primes { /// Searches the underlying array of primes for the target integer. /// If the target is found it returns a [`Result::Ok`] that contains the index of the matching element. /// If the target is not found in the array a [`Result::Err`] is returned that contains an [`Option`]. - /// If the target could be inserted into the array while maintaining the sorted order, the [`Some`](Option::Some) - /// variant contains the index of that location. + /// If the target could be inserted into the array while maintaining the sorted order, the [`Option::Some`] + /// variant is returned and contains the index of that location. /// If the target is larger than the largest prime in the array no information about where it might fit is available, - /// and a [`None`](Option::None) is returned. + /// and an [`Option::None`] is returned. #[must_use = "the method only returns a new value and does not modify `self`"] pub const fn binary_search(&self, target: Underlying) -> Result> { if target > *self.last() { From cfbc1ac34467b8244d03ff990e76eec2b961f3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 Nov 2023 12:35:26 +0100 Subject: [PATCH 068/212] Add std feature --- Cargo.toml | 3 +++ src/generation.rs | 6 +++++- src/lib.rs | 6 +++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0544736..497bbb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,9 @@ repository = "https://github.com/JSorngard/const-primes" criterion = {version = "0.5", features = ["html_reports"]} rand = "0.8" +[features] +std = [] + [[bench]] name = "prime_benches" harness = false \ No newline at end of file diff --git a/src/generation.rs b/src/generation.rs index 024f9d9..8c91b4f 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -244,6 +244,9 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera if n64.checked_mul(n64).is_none() { return Err(SegmentedGenerationError::TooLargeN); } + if lower_limit >= n64 * n64 { + return Err(SegmentedGenerationError::TooLargeLimit); + } let mut primes = [0; N]; let base_sieve: [bool; N] = sieve(); @@ -255,6 +258,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera // Move the found primes into the output vector. while i < N { if upper_sieve[i] { + // FIXME: BUG IDENTIFIED: THIS COULD BE 0 largest_found_prime = lower_limit + i as u64; if largest_found_prime >= n64 * n64 { // We do not know if this is actually a prime @@ -335,7 +339,7 @@ mod test { } { const P: SegmentedGenerationResult<1> = primes_geq(0); - assert_eq!(P, Err(SegmentedGenerationError::TooLargeLimit)); + assert_eq!(P, Err(SegmentedGenerationError::PartialOk(RestrictedArray::new(0..0, [0])))); } for prime in primes_geq::<2_000>(3_998_000) .unwrap_err() diff --git a/src/lib.rs b/src/lib.rs index 4f353af..50cee99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,7 +90,7 @@ //! and more! #![forbid(unsafe_code)] -#![cfg_attr(not(test), no_std)] +#![cfg_attr(all(not(test), not(feature = "std")), no_std)] /// The type that `Primes` stores, and `primes::()`` returns. Currently `u32`. // Just change this to whatever unsigned primitive integer type you want and it should work as long as it has enough bits for your purposes. @@ -150,7 +150,7 @@ pub const fn prime_counts() -> [usize; N] { mod test { use super::*; - use generation::{primes_lt, SegmentedGenerationError}; + use generation::{primes_lt, SegmentedGenerationResult}; #[test] fn check_primes_is_prime() { @@ -235,7 +235,7 @@ mod test { ($($n:expr),+) => { $( { - const P: Result<[u64; 10], SegmentedGenerationError<10>> = primes_lt(100); + const P: SegmentedGenerationResult<10> = primes_lt(100); for (i, prime) in P.unwrap().into_iter().enumerate() { assert_eq!(PRECOMPUTED_PRIMES[25-$n..25][i], prime as u32); } From a2e708633216149fe70e7f082fc5ea2ebd292fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 Nov 2023 15:26:09 +0100 Subject: [PATCH 069/212] rename std --> alloc --- Cargo.toml | 2 +- src/generation.rs | 8 +++++++- src/lib.rs | 5 ++++- src/wrapper.rs | 8 ++++++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 497bbb8..cfd6c43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ criterion = {version = "0.5", features = ["html_reports"]} rand = "0.8" [features] -std = [] +alloc = [] [[bench]] name = "prime_benches" diff --git a/src/generation.rs b/src/generation.rs index 8c91b4f..d82cd1f 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -339,7 +339,13 @@ mod test { } { const P: SegmentedGenerationResult<1> = primes_geq(0); - assert_eq!(P, Err(SegmentedGenerationError::PartialOk(RestrictedArray::new(0..0, [0])))); + assert_eq!( + P, + Err(SegmentedGenerationError::PartialOk(RestrictedArray::new( + 0..0, + [0] + ))) + ); } for prime in primes_geq::<2_000>(3_998_000) .unwrap_err() diff --git a/src/lib.rs b/src/lib.rs index 50cee99..0f148c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,7 +90,10 @@ //! and more! #![forbid(unsafe_code)] -#![cfg_attr(all(not(test), not(feature = "std")), no_std)] +#![cfg_attr(not(test), no_std)] + +#[cfg(feature = "alloc")] +extern crate alloc; /// The type that `Primes` stores, and `primes::()`` returns. Currently `u32`. // Just change this to whatever unsigned primitive integer type you want and it should work as long as it has enough bits for your purposes. diff --git a/src/wrapper.rs b/src/wrapper.rs index 6072f48..09f07e4 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -246,6 +246,14 @@ impl Primes { self.primes.as_slice() } + #[cfg(feature = "alloc")] + /// Returns a newly allocated `Vec` that contains the underlying prime numbers. + #[inline] + #[must_use = "the method only returns a new value and does not modify `self`"] + pub fn to_vec(&self) -> alloc::vec::Vec { + self.primes.to_vec() + } + // endregion: Conversions /// Returns a reference to the element at the given index if it is within bounds. From 642a535f62e313d11f8fa11e7a97dc011f100c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 Nov 2023 15:33:56 +0100 Subject: [PATCH 070/212] Remove alloc feature --- Cargo.toml | 3 --- src/lib.rs | 3 --- src/wrapper.rs | 8 -------- 3 files changed, 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cfd6c43..0544736 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,6 @@ repository = "https://github.com/JSorngard/const-primes" criterion = {version = "0.5", features = ["html_reports"]} rand = "0.8" -[features] -alloc = [] - [[bench]] name = "prime_benches" harness = false \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0f148c3..fe646b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -92,9 +92,6 @@ #![forbid(unsafe_code)] #![cfg_attr(not(test), no_std)] -#[cfg(feature = "alloc")] -extern crate alloc; - /// The type that `Primes` stores, and `primes::()`` returns. Currently `u32`. // Just change this to whatever unsigned primitive integer type you want and it should work as long as it has enough bits for your purposes. // This is used since there is currently no way to be generic over types that can do arithmetic at compile time. diff --git a/src/wrapper.rs b/src/wrapper.rs index 09f07e4..6072f48 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -246,14 +246,6 @@ impl Primes { self.primes.as_slice() } - #[cfg(feature = "alloc")] - /// Returns a newly allocated `Vec` that contains the underlying prime numbers. - #[inline] - #[must_use = "the method only returns a new value and does not modify `self`"] - pub fn to_vec(&self) -> alloc::vec::Vec { - self.primes.to_vec() - } - // endregion: Conversions /// Returns a reference to the element at the given index if it is within bounds. From 22f7d1060e5c1d58e43f3e03b6c32916e795199e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 20 Nov 2023 11:29:28 +0100 Subject: [PATCH 071/212] Add keywords field --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 0544736..42024af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ authors = ["Johanna Sörngård (jsorngard@gmail.com)"] version = "0.4.8" edition = "2021" license = "MIT OR Apache-2.0" +keywords = ["prime", "const"] description = "Generate and work with prime numbers in const contexts" repository = "https://github.com/JSorngard/const-primes" From baa6e10198fc0f3d7fead454f2a5965c0ffa18bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 5 Dec 2023 23:00:12 +0100 Subject: [PATCH 072/212] change keyword 'prime' to 'primes' --- Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 42024af..1aca1c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ authors = ["Johanna Sörngård (jsorngard@gmail.com)"] version = "0.4.8" edition = "2021" license = "MIT OR Apache-2.0" -keywords = ["prime", "const"] +keywords = ["primes", "const"] description = "Generate and work with prime numbers in const contexts" repository = "https://github.com/JSorngard/const-primes" @@ -13,9 +13,9 @@ repository = "https://github.com/JSorngard/const-primes" [dependencies] [dev-dependencies] -criterion = {version = "0.5", features = ["html_reports"]} +criterion = { version = "0.5", features = ["html_reports"] } rand = "0.8" [[bench]] name = "prime_benches" -harness = false \ No newline at end of file +harness = false From 47719ce5b347e71a5a93f90991e99d4196d1fcfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 27 Dec 2023 11:46:24 +0100 Subject: [PATCH 073/212] move clone and copy into derive --- src/restricted_array.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index 36a6524..18ac589 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -4,25 +4,13 @@ use core::{iter::FusedIterator, ops::Range}; /// as the other data may e.g. not uphold some invariant. /// When this type is compared against some other type, only /// data in the visible part is compared. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct RestrictedArray { start: usize, end: usize, array: [T; N], } -impl Clone for RestrictedArray { - fn clone(&self) -> Self { - Self { - start: self.start, - end: self.end, - array: self.array.clone(), - } - } -} - -impl Copy for RestrictedArray {} - impl> PartialEq> for RestrictedArray { /// This method tests for `self` and `other` values to be equal, and is used by `==`. /// Only compares the *visible* part of `self` against the *visible* part of `other`. From df305d924c91ec49e3dff813c59ffb95913debe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Wed, 27 Dec 2023 11:57:56 +0100 Subject: [PATCH 074/212] Move Eq impl to derive --- src/restricted_array.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index 18ac589..221f5bb 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -4,7 +4,7 @@ use core::{iter::FusedIterator, ops::Range}; /// as the other data may e.g. not uphold some invariant. /// When this type is compared against some other type, only /// data in the visible part is compared. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq)] pub struct RestrictedArray { start: usize, end: usize, @@ -19,8 +19,6 @@ impl> PartialEq> for Restr } } -impl Eq for RestrictedArray {} - impl RestrictedArray { /// Restrict an array so that only elements within the given range are visible. /// From c9cf0c844b97fb904e85b6b1039babd18ce9a6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 19 Feb 2024 17:38:24 +0100 Subject: [PATCH 075/212] impl IntoIterator for &RestrictedArray --- src/restricted_array.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index 221f5bb..b52fb6f 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -92,6 +92,7 @@ impl RestrictedArray { } /// Returns an iterator over the visible section. + #[inline] pub fn iter(&self) -> core::slice::Iter<'_, T> { self.as_slice().iter() } @@ -99,6 +100,7 @@ impl RestrictedArray { impl core::ops::Index for RestrictedArray { type Output = T; + #[inline] fn index(&self, index: usize) -> &Self::Output { let i = match self.start.checked_add(index) { Some(sum) => sum, @@ -120,17 +122,30 @@ pub struct RestrictedArrayIntoIter( impl Iterator for RestrictedArrayIntoIter { type Item = T; + #[inline] fn next(&mut self) -> Option { self.0.next() } + #[inline] fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } + + #[inline] + fn last(self) -> Option { + self.0.last() + } + + #[inline] + fn count(self) -> usize { + self.0.count() + } } impl FusedIterator for RestrictedArrayIntoIter {} impl ExactSizeIterator for RestrictedArrayIntoIter {} impl DoubleEndedIterator for RestrictedArrayIntoIter { + #[inline] fn next_back(&mut self) -> Option { self.0.next_back() } @@ -146,6 +161,15 @@ impl IntoIterator for RestrictedArray { } } +impl<'a, const N: usize, T> IntoIterator for &'a RestrictedArray { + type IntoIter = core::slice::Iter<'a, T>; + type Item = &'a as Iterator>::Item; + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.as_slice().iter() + } +} + // region: PartialEq impls impl PartialEq<[U]> for RestrictedArray where From 1f402c402f3af4016e2405bd1a77e3b187379c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 19 Feb 2024 17:42:24 +0100 Subject: [PATCH 076/212] impl IndexMut for RestrictedArray --- src/restricted_array.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index b52fb6f..ad311aa 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -115,6 +115,21 @@ impl core::ops::Index for RestrictedArray { } } +impl core::ops::IndexMut for RestrictedArray { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + let i = match self.start.checked_add(index) { + Some(sum) => sum, + None => panic!("index overflowed"), + }; + + if i >= self.end { + panic!("index was {i} when len was {}", self.end - self.start); + } + + &mut self.array[i] + } +} + /// Created by the [`into_iter`](RestrictedArray::into_iter) function on [`RestrictedArray`], see it for more information. pub struct RestrictedArrayIntoIter( core::iter::Take>>, From 1f0e96b167e743de517ef2bad643753ea14e0356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 19 Feb 2024 17:50:51 +0100 Subject: [PATCH 077/212] Add as_slice_mut --- src/restricted_array.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index ad311aa..d7108b8 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -63,6 +63,12 @@ impl RestrictedArray { tail.split_at(self.end - self.start).0 } + /// Returns the visible part of the array as a mutable slice. + pub fn as_slice_mut(&mut self) -> &mut [T] { + let (_, tail) = self.array.split_at_mut(self.start); + tail.split_at_mut(self.end - self.start).0 + } + /// Returns the index of the first element of the underlying array that's inside the visible region. pub const fn start(&self) -> usize { self.start @@ -102,20 +108,18 @@ impl core::ops::Index for RestrictedArray { type Output = T; #[inline] fn index(&self, index: usize) -> &Self::Output { - let i = match self.start.checked_add(index) { - Some(sum) => sum, - None => panic!("index overflowed"), - }; - - if i >= self.end { - panic!("index was {i} when len was {}", self.end - self.start); + match self.as_slice().get(index) { + Some(element) => element, + None => panic!( + "the index was {index} but the len was {}", + self.end - self.start + ), } - - &self.array[i] } } impl core::ops::IndexMut for RestrictedArray { + #[inline] fn index_mut(&mut self, index: usize) -> &mut Self::Output { let i = match self.start.checked_add(index) { Some(sum) => sum, From c9628b92576bfa7b8ef9eda1bcf770c7986d004e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 19 Feb 2024 17:53:13 +0100 Subject: [PATCH 078/212] implement Intex(Mut) in terms of as_slice(_mut) --- src/restricted_array.rs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index d7108b8..d58511e 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -108,29 +108,14 @@ impl core::ops::Index for RestrictedArray { type Output = T; #[inline] fn index(&self, index: usize) -> &Self::Output { - match self.as_slice().get(index) { - Some(element) => element, - None => panic!( - "the index was {index} but the len was {}", - self.end - self.start - ), - } + &self.as_slice()[index] } } impl core::ops::IndexMut for RestrictedArray { #[inline] fn index_mut(&mut self, index: usize) -> &mut Self::Output { - let i = match self.start.checked_add(index) { - Some(sum) => sum, - None => panic!("index overflowed"), - }; - - if i >= self.end { - panic!("index was {i} when len was {}", self.end - self.start); - } - - &mut self.array[i] + &mut self.as_slice_mut()[index] } } From 83d186255f8a4bdbadb3b3e3ca59476e1ac0e786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 19 Feb 2024 17:58:58 +0100 Subject: [PATCH 079/212] Add where clause for signature looks --- src/restricted_array.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index d58511e..d7ae543 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -186,7 +186,10 @@ where } } -impl> PartialEq> for [U] { +impl PartialEq> for [U] +where + U: PartialEq, +{ /// This method tests for `self` and `other` values to be equal, and is used by `==`. /// Only compares the *visible* part of `other` against `self`. fn eq(&self, other: &RestrictedArray) -> bool { From d391e9e64231df8f0d880f9d8f591d6a3a9dcb0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 19 Feb 2024 21:04:04 +0100 Subject: [PATCH 080/212] implement size_hint in terms of len --- src/restricted_array.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index d7ae543..e6dc130 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -133,7 +133,8 @@ impl Iterator for RestrictedArrayIntoIter { #[inline] fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() + let l = self.0.len(); + (l, Some(l)) } #[inline] From 5edb8a28315b1edca2312df8b8974dc3ff29be51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 19 Feb 2024 21:06:06 +0100 Subject: [PATCH 081/212] Remove mutable access --- src/restricted_array.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index e6dc130..7e74b02 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -63,12 +63,6 @@ impl RestrictedArray { tail.split_at(self.end - self.start).0 } - /// Returns the visible part of the array as a mutable slice. - pub fn as_slice_mut(&mut self) -> &mut [T] { - let (_, tail) = self.array.split_at_mut(self.start); - tail.split_at_mut(self.end - self.start).0 - } - /// Returns the index of the first element of the underlying array that's inside the visible region. pub const fn start(&self) -> usize { self.start @@ -112,13 +106,6 @@ impl core::ops::Index for RestrictedArray { } } -impl core::ops::IndexMut for RestrictedArray { - #[inline] - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.as_slice_mut()[index] - } -} - /// Created by the [`into_iter`](RestrictedArray::into_iter) function on [`RestrictedArray`], see it for more information. pub struct RestrictedArrayIntoIter( core::iter::Take>>, From 8a2565f70ce311a0bd459b0a26ed597f099274d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 22 Feb 2024 09:44:26 +0100 Subject: [PATCH 082/212] Add manual nth impl --- src/restricted_array.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index 7e74b02..4ebfef2 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -124,6 +124,11 @@ impl Iterator for RestrictedArrayIntoIter { (l, Some(l)) } + #[inline] + fn nth(&mut self, index: usize) -> Option { + self.0.nth(index) + } + #[inline] fn last(self) -> Option { self.0.last() From a54ad9492f8286cfe4f124a17eb5566e6ed9fd57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 22 Feb 2024 09:46:23 +0100 Subject: [PATCH 083/212] Inline into_iter --- src/restricted_array.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/restricted_array.rs b/src/restricted_array.rs index 4ebfef2..63aab8f 100644 --- a/src/restricted_array.rs +++ b/src/restricted_array.rs @@ -151,6 +151,7 @@ impl DoubleEndedIterator for RestrictedArrayIntoIter { impl IntoIterator for RestrictedArray { type IntoIter = RestrictedArrayIntoIter; type Item = as Iterator>::Item; + #[inline] fn into_iter(self) -> Self::IntoIter { let start = self.start; let len = self.len(); From de89b3a482165e227a47a7394363c275398e35bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 12:56:16 +0200 Subject: [PATCH 084/212] Rename RestrictedArray, impl Index --- src/array_section.rs | 274 ++++++++++++++++++++++++++++++++++++++++ src/generation.rs | 12 +- src/lib.rs | 2 +- src/restricted_array.rs | 199 ----------------------------- 4 files changed, 281 insertions(+), 206 deletions(-) create mode 100644 src/array_section.rs delete mode 100644 src/restricted_array.rs diff --git a/src/array_section.rs b/src/array_section.rs new file mode 100644 index 0000000..a6f9467 --- /dev/null +++ b/src/array_section.rs @@ -0,0 +1,274 @@ +use core::{ + iter::FusedIterator, + ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, +}; + +/// An array where only a section of the data may be viewed, +/// as the other data may e.g. not uphold some invariant. +/// When this type is compared against some other type, only +/// data in the visible part is compared. +#[derive(Debug, Clone, Copy, Eq)] +pub struct ArraySection { + start: usize, + end: usize, + array: [T; N], +} + +impl> PartialEq> for ArraySection { + /// This method tests for `self` and `other` values to be equal, and is used by `==`. + /// Only compares the *visible* part of `self` against the *visible* part of `other`. + fn eq(&self, other: &ArraySection) -> bool { + self.as_slice() == other.as_slice() + } +} + +impl ArraySection { + /// Restrict an array so that only elements within the given range are visible. + /// + /// # Panics + /// + /// Panics if the range of indices is out of bounds of the array. + pub const fn new(sub_range: Range, array: [T; N]) -> Self { + assert!( + sub_range.start < N && sub_range.end <= N, + "the sub-range must be in bounds" + ); + + if sub_range.start > sub_range.end { + Self { + start: 0, + end: 0, + array, + } + } else { + Self { + start: sub_range.start, + end: sub_range.end, + array, + } + } + } + + /// Returns a reference to the full underlying array. There is no guarantee about the data + /// outside the section. + pub const fn as_full_array(&self) -> &[T; N] { + &self.array + } + + /// Converts `self` into the full underlying array. There is no guarantee about the data + /// outside the section. + pub fn into_full_array(self) -> [T; N] { + self.array + } + + /// Returns the visible part of the array as a slice. + pub const fn as_slice(&self) -> &[T] { + let (_, tail) = self.array.split_at(self.start); + tail.split_at(self.end - self.start).0 + } + + /// Returns the length of the array section. + pub const fn len(&self) -> usize { + self.as_slice().len() + } + + /// Returns whether the array section is empty. + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns whether the section is just the entire array. + /// If this is `true` it is completely fine to call [`as_full_array`](RestrictedArray::as_full_array) + /// or [`into_full_array`](RestrictedArray::into_full_array). + pub const fn section_is_complete_array(&self) -> bool { + self.len() == N + } + + /// Returns an iterator over the array section. + #[inline] + pub fn iter(&self) -> ArraySectionIter<'_, T> { + ArraySectionIter::new(self.as_slice().iter()) + } +} + +// region: Index impls + +impl core::ops::Index for ArraySection { + type Output = T; + #[inline] + fn index(&self, index: usize) -> &Self::Output { + &self.as_slice()[index] + } +} + +macro_rules! impl_index_range { + ($($t:ty),+) => { + $( + impl ::core::ops::Index<$t> for ArraySection { + type Output = [T]; + #[inline] + fn index(&self, index: $t) -> &Self::Output { + ::core::ops::Index::index(self.as_slice(), index) + } + } + )+ + }; +} + +impl_index_range! {Range, RangeFrom, RangeFull, RangeTo, RangeInclusive, RangeToInclusive} + +// endregion: Index impls + +// region: PartialEq impls +impl PartialEq<[U]> for ArraySection +where + U: PartialEq, +{ + /// This method tests for `self` and `other` values to be equal, and is used by `==`. + /// Only compares the *visible* part of `self` against `other`. + fn eq(&self, other: &[U]) -> bool { + other == self.as_slice() + } +} + +impl PartialEq> for [U] +where + U: PartialEq, +{ + /// This method tests for `self` and `other` values to be equal, and is used by `==`. + /// Only compares the *visible* part of `other` against `self`. + fn eq(&self, other: &ArraySection) -> bool { + self == other.as_slice() + } +} +// endregion: PartialEq impls + +impl AsRef<[T]> for ArraySection { + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + +impl IntoIterator for ArraySection { + type IntoIter = ArraySectionIntoIter; + type Item = as Iterator>::Item; + #[inline] + fn into_iter(self) -> Self::IntoIter { + let start = self.start; + let len = self.len(); + ArraySectionIntoIter::new(self.array.into_iter().skip(start).take(len)) + } +} + +impl<'a, const N: usize, T> IntoIterator for &'a ArraySection { + type IntoIter = core::slice::Iter<'a, T>; + type Item = &'a as Iterator>::Item; + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.as_slice().iter() + } +} + +use array_section_iter::ArraySectionIter; +mod array_section_iter { + use super::FusedIterator; + + #[derive(Debug, Clone)] + pub struct ArraySectionIter<'a, T>(core::slice::Iter<'a, T>); + + impl<'a, T> ArraySectionIter<'a, T> { + pub const fn new(iter: core::slice::Iter<'a, T>) -> Self { + Self(iter) + } + } + + impl<'a, T> Iterator for ArraySectionIter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option { + self.0.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + fn last(self) -> Option { + self.0.last() + } + + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } + + fn count(self) -> usize { + self.0.count() + } + } + impl<'a, T> DoubleEndedIterator for ArraySectionIter<'a, T> { + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + impl<'a, T> ExactSizeIterator for ArraySectionIter<'a, T> { + fn len(&self) -> usize { + self.0.len() + } + } + impl<'a, T> FusedIterator for ArraySectionIter<'a, T> {} +} + +use array_section_into_iter::ArraySectionIntoIter; +mod array_section_into_iter { + use super::FusedIterator; + + #[derive(Debug, Clone)] + /// Created by the [`into_iter`](RestrictedArray::into_iter) function on [`RestrictedArray`], see it for more information. + pub struct ArraySectionIntoIter( + core::iter::Take>>, + ); + + impl ArraySectionIntoIter { + pub const fn new( + iter: core::iter::Take>>, + ) -> Self { + Self(iter) + } + } + + impl Iterator for ArraySectionIntoIter { + type Item = T; + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let l = self.0.len(); + (l, Some(l)) + } + + #[inline] + fn nth(&mut self, index: usize) -> Option { + self.0.nth(index) + } + + #[inline] + fn last(self) -> Option { + self.0.last() + } + + #[inline] + fn count(self) -> usize { + self.0.count() + } + } + impl FusedIterator for ArraySectionIntoIter {} + impl ExactSizeIterator for ArraySectionIntoIter {} + impl DoubleEndedIterator for ArraySectionIntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } +} diff --git a/src/generation.rs b/src/generation.rs index d82cd1f..b5f9948 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -1,6 +1,6 @@ use core::fmt; -use crate::{restricted_array::RestrictedArray, sieve, sieving::sieve_segment, Underlying}; +use crate::{array_section::ArraySection, sieve, sieving::sieve_segment, Underlying}; /// Type alias for the type returned by the segmented generation functions, that otherwise has two generics that must be the same. pub type SegmentedGenerationResult = Result<[u64; N], SegmentedGenerationError>; @@ -195,7 +195,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat } upper_limit = smallest_found_prime; if upper_limit <= 2 && total_primes_found < N { - let restricted = RestrictedArray::new(N - total_primes_found..N, primes); + let restricted = ArraySection::new(N - total_primes_found..N, primes); return Err(SegmentedGenerationError::PartialOk(restricted)); } } @@ -264,7 +264,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera // We do not know if this is actually a prime // since the base sieve does not contain information about // the prime status of numbers larger than or equal to N. - let restricted = RestrictedArray::new(0..total_found_primes, primes); + let restricted = ArraySection::new(0..total_found_primes, primes); return Err(SegmentedGenerationError::PartialOk(restricted)); } primes[total_found_primes] = largest_found_prime; @@ -293,12 +293,12 @@ pub enum SegmentedGenerationError { /// the upper limit was larger than `N^2`. TooLargeLimit, /// Only a part of the output array contains prime numbers as they either exceeded `N^2` or ran out. - PartialOk(RestrictedArray), + PartialOk(ArraySection), } impl SegmentedGenerationError { /// Returns the partial result as a restricted array, if there is one. - pub const fn partial_ok(self) -> Option> { + pub const fn partial_ok(self) -> Option> { match self { Self::PartialOk(restricted_array) => Some(restricted_array), _ => None, @@ -341,7 +341,7 @@ mod test { const P: SegmentedGenerationResult<1> = primes_geq(0); assert_eq!( P, - Err(SegmentedGenerationError::PartialOk(RestrictedArray::new( + Err(SegmentedGenerationError::PartialOk(ArraySection::new( 0..0, [0] ))) diff --git a/src/lib.rs b/src/lib.rs index fe646b7..6d580e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,11 +97,11 @@ // This is used since there is currently no way to be generic over types that can do arithmetic at compile time. type Underlying = u32; +pub mod array_section; pub mod generation; mod imath; mod miller_rabin; mod other_prime; -pub mod restricted_array; mod sieving; mod wrapper; diff --git a/src/restricted_array.rs b/src/restricted_array.rs deleted file mode 100644 index 63aab8f..0000000 --- a/src/restricted_array.rs +++ /dev/null @@ -1,199 +0,0 @@ -use core::{iter::FusedIterator, ops::Range}; - -/// An array where only a section of the data may be viewed, -/// as the other data may e.g. not uphold some invariant. -/// When this type is compared against some other type, only -/// data in the visible part is compared. -#[derive(Debug, Clone, Copy, Eq)] -pub struct RestrictedArray { - start: usize, - end: usize, - array: [T; N], -} - -impl> PartialEq> for RestrictedArray { - /// This method tests for `self` and `other` values to be equal, and is used by `==`. - /// Only compares the *visible* part of `self` against the *visible* part of `other`. - fn eq(&self, other: &RestrictedArray) -> bool { - self.as_slice() == other.as_slice() - } -} - -impl RestrictedArray { - /// Restrict an array so that only elements within the given range are visible. - /// - /// # Panics - /// Panics if the range of indices is out of bounds of the array. - pub const fn new(sub_range: Range, array: [T; N]) -> Self { - assert!( - sub_range.start < N && sub_range.end <= N, - "the sub-range must be in bounds" - ); - - if sub_range.start > sub_range.end { - Self { - start: 0, - end: 0, - array, - } - } else { - Self { - start: sub_range.start, - end: sub_range.end, - array, - } - } - } - - /// Returns a reference to the full underlying array. There is no guarantee about the data - /// outside the visible region. - pub const fn as_full_array(&self) -> &[T; N] { - &self.array - } - - /// Converts `self` into the full underlying array. There is no guarantee about the data - /// outside the visible region. - pub fn into_full_array(self) -> [T; N] { - self.array - } - - /// Returns the visible part of the array as a slice. - pub const fn as_slice(&self) -> &[T] { - let (_, tail) = self.array.split_at(self.start); - tail.split_at(self.end - self.start).0 - } - - /// Returns the index of the first element of the underlying array that's inside the visible region. - pub const fn start(&self) -> usize { - self.start - } - - /// Returns the index of the first element of the underlying array that is - /// invisible again after the end of the visible part. - pub const fn end(&self) -> usize { - self.end - } - - /// Returns the length of the visible part of the array. - pub const fn len(&self) -> usize { - self.end - self.start - } - - /// Returns whether the visible part is empty. - pub const fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns whether there are no parts of the array that are invisible. - /// If this is `true` it is completely fine to call [`as_full_array`](RestrictedArray::as_full_array) - /// or [`into_full_array`](RestrictedArray::into_full_array). - pub const fn is_fully_visible(&self) -> bool { - self.len() == N - } - - /// Returns an iterator over the visible section. - #[inline] - pub fn iter(&self) -> core::slice::Iter<'_, T> { - self.as_slice().iter() - } -} - -impl core::ops::Index for RestrictedArray { - type Output = T; - #[inline] - fn index(&self, index: usize) -> &Self::Output { - &self.as_slice()[index] - } -} - -/// Created by the [`into_iter`](RestrictedArray::into_iter) function on [`RestrictedArray`], see it for more information. -pub struct RestrictedArrayIntoIter( - core::iter::Take>>, -); - -impl Iterator for RestrictedArrayIntoIter { - type Item = T; - #[inline] - fn next(&mut self) -> Option { - self.0.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let l = self.0.len(); - (l, Some(l)) - } - - #[inline] - fn nth(&mut self, index: usize) -> Option { - self.0.nth(index) - } - - #[inline] - fn last(self) -> Option { - self.0.last() - } - - #[inline] - fn count(self) -> usize { - self.0.count() - } -} -impl FusedIterator for RestrictedArrayIntoIter {} -impl ExactSizeIterator for RestrictedArrayIntoIter {} -impl DoubleEndedIterator for RestrictedArrayIntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.0.next_back() - } -} - -impl IntoIterator for RestrictedArray { - type IntoIter = RestrictedArrayIntoIter; - type Item = as Iterator>::Item; - #[inline] - fn into_iter(self) -> Self::IntoIter { - let start = self.start; - let len = self.len(); - RestrictedArrayIntoIter(self.array.into_iter().skip(start).take(len)) - } -} - -impl<'a, const N: usize, T> IntoIterator for &'a RestrictedArray { - type IntoIter = core::slice::Iter<'a, T>; - type Item = &'a as Iterator>::Item; - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.as_slice().iter() - } -} - -// region: PartialEq impls -impl PartialEq<[U]> for RestrictedArray -where - U: PartialEq, -{ - /// This method tests for `self` and `other` values to be equal, and is used by `==`. - /// Only compares the *visible* part of `self` against `other`. - fn eq(&self, other: &[U]) -> bool { - other == self.as_slice() - } -} - -impl PartialEq> for [U] -where - U: PartialEq, -{ - /// This method tests for `self` and `other` values to be equal, and is used by `==`. - /// Only compares the *visible* part of `other` against `self`. - fn eq(&self, other: &RestrictedArray) -> bool { - self == other.as_slice() - } -} -// endregion: PartialEq impls - -impl AsRef<[T]> for RestrictedArray { - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} From b476af23cd63e4c2ddb96e295d490c34bd90b91f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:01:29 +0200 Subject: [PATCH 085/212] minor doc and name tweaks --- src/array_section.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index a6f9467..2bf7073 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -5,8 +5,6 @@ use core::{ /// An array where only a section of the data may be viewed, /// as the other data may e.g. not uphold some invariant. -/// When this type is compared against some other type, only -/// data in the visible part is compared. #[derive(Debug, Clone, Copy, Eq)] pub struct ArraySection { start: usize, @@ -14,9 +12,8 @@ pub struct ArraySection { array: [T; N], } +/// Only compares the data in the sections, and not the full arrays. impl> PartialEq> for ArraySection { - /// This method tests for `self` and `other` values to be equal, and is used by `==`. - /// Only compares the *visible* part of `self` against the *visible* part of `other`. fn eq(&self, other: &ArraySection) -> bool { self.as_slice() == other.as_slice() } @@ -61,7 +58,7 @@ impl ArraySection { self.array } - /// Returns the visible part of the array as a slice. + /// Returns the section of the array as a slice. pub const fn as_slice(&self) -> &[T] { let (_, tail) = self.array.split_at(self.start); tail.split_at(self.end - self.start).0 @@ -80,7 +77,7 @@ impl ArraySection { /// Returns whether the section is just the entire array. /// If this is `true` it is completely fine to call [`as_full_array`](RestrictedArray::as_full_array) /// or [`into_full_array`](RestrictedArray::into_full_array). - pub const fn section_is_complete_array(&self) -> bool { + pub const fn section_is_full_array(&self) -> bool { self.len() == N } From b2209648ab977e8e7b0b198af68e968985a46378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:02:29 +0200 Subject: [PATCH 086/212] Be explicit in impl of index --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index 2bf7073..955be23 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -94,7 +94,7 @@ impl core::ops::Index for ArraySection { type Output = T; #[inline] fn index(&self, index: usize) -> &Self::Output { - &self.as_slice()[index] + core::ops::Index::index(self.as_slice(), index) } } From 86e41cb8c0e9c59a16ea848d85bbb6ee8a5d685b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:03:53 +0200 Subject: [PATCH 087/212] Use arraysectioniter also for into_iter(&self) --- src/array_section.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 955be23..ee0b992 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -158,11 +158,11 @@ impl IntoIterator for ArraySection { } impl<'a, const N: usize, T> IntoIterator for &'a ArraySection { - type IntoIter = core::slice::Iter<'a, T>; + type IntoIter = ArraySectionIter<'a, T>; type Item = &'a as Iterator>::Item; #[inline] fn into_iter(self) -> Self::IntoIter { - self.as_slice().iter() + ArraySectionIter::new(self.as_slice().iter()) } } From 824d16d4b316420eff31d0c0861d9b75d4405f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:04:44 +0200 Subject: [PATCH 088/212] Simplify impl of &self.into_iter() --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index ee0b992..11ac353 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -159,7 +159,7 @@ impl IntoIterator for ArraySection { impl<'a, const N: usize, T> IntoIterator for &'a ArraySection { type IntoIter = ArraySectionIter<'a, T>; - type Item = &'a as Iterator>::Item; + type Item = as Iterator>::Item; #[inline] fn into_iter(self) -> Self::IntoIter { ArraySectionIter::new(self.as_slice().iter()) From db284dff11201a00e8f1670a1df0482609deba3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:06:33 +0200 Subject: [PATCH 089/212] Add newline before headings in docstrings --- src/generation.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index b5f9948..556d055 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -12,6 +12,7 @@ pub type SegmentedGenerationResult = Result<[u64; N], SegmentedG /// Uses a [segmented sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve). /// /// # Example +/// /// ``` /// # use const_primes::primes; /// const PRIMES: [u32; 10] = primes(); @@ -109,6 +110,7 @@ pub const fn primes() -> [Underlying; N] { /// If you want to compute primes that are larger than the input, take a look at [`primes_geq`]. /// /// # Example +/// /// Basic usage: /// ``` /// # use const_primes::generation::{SegmentedGenerationResult, primes_lt, SegmentedGenerationError}; @@ -142,6 +144,7 @@ pub const fn primes() -> [Underlying; N] { /// } /// ``` /// # Panics +/// /// Panics if `N^2` does not fit in a `u64` or if `upper_limit` is larger than `N^2`. This is a compile error /// in const contexts: /// ```compile_fail @@ -211,6 +214,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat /// If you want to compute primes smaller than the input, take a look at [`primes_lt`]. /// /// # Example +/// /// Basic usage: /// ``` /// # use const_primes::primes_geq; @@ -233,6 +237,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat /// assert_eq!(PRIMES, [5, 7, 0]); /// ``` /// # Panics +/// /// Panics if `N^2` does not fit in a `u64`. /// ```compile_fail /// # use const_primes::primes_geq; From 08f6f806a5efea4c9aaf2180eb854554059b7b48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:14:10 +0200 Subject: [PATCH 090/212] Statically assert N>0 using inline const --- src/lib.rs | 1 + src/wrapper.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6d580e7..a3cefdb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,7 @@ #![forbid(unsafe_code)] #![cfg_attr(not(test), no_std)] +#![feature(inline_const)] /// The type that `Primes` stores, and `primes::()`` returns. Currently `u32`. // Just change this to whatever unsigned primitive integer type you want and it should work as long as it has enough bits for your purposes. diff --git a/src/wrapper.rs b/src/wrapper.rs index 6072f48..cd0ef42 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -36,6 +36,7 @@ impl Primes { /// Uses a [segmented sieve of Eratosthenes](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve). /// /// # Examples + /// /// Basic usage /// ``` /// # use const_primes::Primes; @@ -54,19 +55,18 @@ impl Primes { /// assert_eq!(primes, [2, 3, 5, 7, 11]); /// ``` /// - /// # Panics - /// - /// Panics if `N` is zero. In const contexts this will fail to compile + /// Fails to compile if `N` is zero. /// ```compile_fail /// # use const_primes::Primes; /// const NO_PRIMES: Primes<0> = Primes::new(); /// ``` - /// In other contexts it may panic at runtime instead. + /// + /// # Panics + /// /// If any of the primes overflow a `u32` it will panic in const contexts or debug mode. #[must_use = "the associated method only returns a new value"] pub const fn new() -> Self { - assert!(N >= 1, "`N` must be at least 1"); - + const { assert!(N > 0, "`N` must be at least 1") } Self { primes: primes() } } From 80e284f71cd4e4a01e5f19fd33f85fd936e3144b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:18:42 +0200 Subject: [PATCH 091/212] Add info about new panic behavior to type docstring --- src/wrapper.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/wrapper.rs b/src/wrapper.rs index cd0ef42..d5f194d 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -3,9 +3,10 @@ use crate::{primes, Underlying}; // region: Primes /// A wrapper around an array that consists of the first `N` primes. -/// Can be created in const contexts, and if so it ensures that `N` is non-zero at compile time. +/// Can be created in const contexts, and ensures that `N` is non-zero at compile time. /// /// # Examples +/// /// Basic usage /// ``` /// # use const_primes::Primes; @@ -60,9 +61,9 @@ impl Primes { /// # use const_primes::Primes; /// const NO_PRIMES: Primes<0> = Primes::new(); /// ``` - /// + /// /// # Panics - /// + /// /// If any of the primes overflow a `u32` it will panic in const contexts or debug mode. #[must_use = "the associated method only returns a new value"] pub const fn new() -> Self { From 60b0865eb02c0b32641001b0c91aaf46637c8f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:32:24 +0200 Subject: [PATCH 092/212] Add space before header in docstring --- src/lib.rs | 4 ++-- src/wrapper.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a3cefdb..0beb600 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,7 +106,7 @@ mod other_prime; mod sieving; mod wrapper; -pub use generation::primes; +pub use generation::{primes, primes_geq, primes_lt}; use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; @@ -151,7 +151,7 @@ pub const fn prime_counts() -> [usize; N] { mod test { use super::*; - use generation::{primes_lt, SegmentedGenerationResult}; + use generation::SegmentedGenerationResult; #[test] fn check_primes_is_prime() { diff --git a/src/wrapper.rs b/src/wrapper.rs index d5f194d..673bb36 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -221,6 +221,7 @@ impl Primes { /// Converts `self` into an array of size `N`. /// /// # Example + /// /// Basic usage /// ``` /// # use const_primes::Primes; From 066f074289c4f13a6c8851e9714a657e4e217df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:37:33 +0200 Subject: [PATCH 093/212] Add newline around headings in docstrings --- src/sieving.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sieving.rs b/src/sieving.rs index 97e1e69..aae0478 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -56,6 +56,7 @@ pub(crate) const fn sieve_segment( /// and then uses the result to sieve the output range if needed. /// /// # Examples +/// /// Basic usage /// ``` /// # use const_primes::sieve_lt; @@ -81,6 +82,7 @@ pub(crate) const fn sieve_segment( /// ``` /// /// # Panics +/// /// Panics if `upper_limit` is not in the range `[N, N^2]`. In const contexts these are compile errors: /// ```compile_fail /// # use const_primes::sieve_lt; @@ -121,6 +123,7 @@ pub const fn sieve_lt(upper_limit: u64) -> [bool; N] { /// Uses a sieve of Eratosthenes. /// /// # Example +/// /// ``` /// # use const_primes::sieve; /// const PRIMALITY: [bool; 10] = sieve(); @@ -166,6 +169,7 @@ pub const fn sieve() -> [bool; N] { /// Returns the prime status of the `N` smallest integers greater than or equal to `lower_limit`. /// /// # Example +/// /// Basic usage: /// ``` /// # use const_primes::sieve_geq; From c89e1252973a4c97b59b33d1b2ccb06b3daf9e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:38:31 +0200 Subject: [PATCH 094/212] Add newline around headings in docstrings --- src/sieving.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sieving.rs b/src/sieving.rs index aae0478..d116b19 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -178,6 +178,7 @@ pub const fn sieve() -> [bool; N] { /// assert_eq!(PRIME_STATUS, [false, true, false, true, false]); /// ``` /// # Panics +/// /// Panics if `N + lower_limit` is larger than or equal to `N^2`. In const contexts this is a compile error: /// ```compile_fail /// # use const_primes::sieve_geq; From da8e1446c9cc6425fa821383e11db831b7c5cf68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:42:20 +0200 Subject: [PATCH 095/212] Fail to compile in primes if N = 0 --- src/generation.rs | 12 +++++++++--- src/sieving.rs | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 556d055..4326263 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -18,11 +18,17 @@ pub type SegmentedGenerationResult = Result<[u64; N], SegmentedG /// const PRIMES: [u32; 10] = primes(); /// assert_eq!(PRIMES, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); /// ``` +/// Fails to compile if `N = 0`: +/// ```compile_fail +/// # use const_primes::primes; +/// let primes: [u32; 0] = primes(); +/// ``` +/// #[must_use = "the function only returns a new value"] pub const fn primes() -> [Underlying; N] { - if N == 0 { - return [0; N]; - } else if N == 1 { + const { assert!(N > 0, "`N` must be at least 1") } + + if N == 1 { return [2; N]; } else if N == 2 { let mut primes = [0; N]; diff --git a/src/sieving.rs b/src/sieving.rs index d116b19..f2e5ff7 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -178,7 +178,7 @@ pub const fn sieve() -> [bool; N] { /// assert_eq!(PRIME_STATUS, [false, true, false, true, false]); /// ``` /// # Panics -/// +/// /// Panics if `N + lower_limit` is larger than or equal to `N^2`. In const contexts this is a compile error: /// ```compile_fail /// # use const_primes::sieve_geq; From 73a6fb5c05a7c510e7afaae5042e94f54b813350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:46:06 +0200 Subject: [PATCH 096/212] Compile fail in all sieve functions if N == 0 --- src/generation.rs | 4 ++++ src/sieving.rs | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index 4326263..c27ba83 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -165,6 +165,8 @@ pub const fn primes() -> [Underlying; N] { /// ``` #[must_use = "the function only returns a new value and does not modify its input"] pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerationResult { + const { assert!(N > 0, "`N` must be at least 1") } + let n64 = N as u64; match (n64).checked_mul(n64) { Some(prod) => { @@ -251,6 +253,8 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat /// ``` #[must_use = "the function only returns a new value and does not modify its input"] pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenerationResult { + const { assert!(N > 0, "`N` must be at least 1") } + let n64 = N as u64; if n64.checked_mul(n64).is_none() { return Err(SegmentedGenerationError::TooLargeN); diff --git a/src/sieving.rs b/src/sieving.rs index f2e5ff7..238f5b2 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -8,6 +8,8 @@ pub(crate) const fn sieve_segment( base_sieve: &[bool; N], upper_limit: u64, ) -> [bool; N] { + const { assert!(N > 0, "`N` must be at least 1") } + let mut segment_sieve = [true; N]; let lower_limit = upper_limit - N as u64; @@ -94,6 +96,8 @@ pub(crate) const fn sieve_segment( /// ``` #[must_use = "the function only returns a new value and does not modify its input"] pub const fn sieve_lt(upper_limit: u64) -> [bool; N] { + const { assert!(N > 0, "`N` must be at least 1") } + let n64 = N as u64; // Since panics are compile time errors in const contexts @@ -132,6 +136,8 @@ pub const fn sieve_lt(upper_limit: u64) -> [bool; N] { /// ``` #[must_use = "the function only returns a new value"] pub const fn sieve() -> [bool; N] { + const { assert!(N > 0, "`N` must be at least 1") } + let mut sieve = [true; N]; if N > 0 { sieve[0] = false; @@ -186,6 +192,8 @@ pub const fn sieve() -> [bool; N] { /// ``` #[must_use = "the function only returns a new value and does not modify its input"] pub const fn sieve_geq(lower_limit: u64) -> [bool; N] { + const { assert!(N > 0, "`N` must be at least 1") } + let n64 = N as u64; // Since panics are compile time errors in const contexts From 35d086a3ece1a01c97fd3201f8d7bf192b6daec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:47:14 +0200 Subject: [PATCH 097/212] Document compile fail when N=0 --- src/sieving.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sieving.rs b/src/sieving.rs index 238f5b2..9668954 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -2,7 +2,7 @@ use crate::isqrt; /// Uses the primalities of the first `N` integers in `base_sieve` to sieve the numbers in the range `[upper_limit - N, upper_limit)`. /// Assumes that the base sieve contains the prime status of the `N` fist integers. The output is only meaningful -/// for the numbers below `N^2`. +/// for the numbers below `N^2`. Fails to compile if `N` is 0. #[must_use = "the function only returns a new value and does not modify its inputs"] pub(crate) const fn sieve_segment( base_sieve: &[bool; N], @@ -57,6 +57,8 @@ pub(crate) const fn sieve_segment( /// Uses a sieve of Eratosthenes to sieve the first `N` integers /// and then uses the result to sieve the output range if needed. /// +/// Fails to compile if `N` is 0. +/// /// # Examples /// /// Basic usage @@ -123,6 +125,7 @@ pub const fn sieve_lt(upper_limit: u64) -> [bool; N] { } /// Returns an array of size `N` where the value at a given index indicates whether the index is prime. +/// Fails to compile if `N` is 0. /// /// Uses a sieve of Eratosthenes. /// @@ -173,6 +176,7 @@ pub const fn sieve() -> [bool; N] { } /// Returns the prime status of the `N` smallest integers greater than or equal to `lower_limit`. +/// Fails to compile if `N` is 0. /// /// # Example /// From 7eb8d6482ee7adabcd84e970bb435ac0d21d3fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:48:30 +0200 Subject: [PATCH 098/212] Document compile fail in generation functions --- src/generation.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index c27ba83..6244d64 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -6,6 +6,7 @@ use crate::{array_section::ArraySection, sieve, sieving::sieve_segment, Underlyi pub type SegmentedGenerationResult = Result<[u64; N], SegmentedGenerationError>; /// Returns the `N` first prime numbers. +/// Fails to compile if `N` is 0. /// /// [`Primes`](crate::Primes) might be relevant for you if you intend to later use these prime numbers for related computations. /// @@ -109,6 +110,7 @@ pub const fn primes() -> [Underlying; N] { } /// Returns the `N` largest primes less than `upper_limit`. +/// Fails to compile if `N` is 0. /// /// The return array fills from the end until either it is full or there are no more primes. /// If the primes run out before the array is filled the first elements will have a value of zero. @@ -215,6 +217,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat } /// Returns the `N` smallest primes greater than or equal to `lower_limit`. +/// Fails to compile if `N` is 0. /// /// This function will fill the output array from index 0 and stop generating primes if they exceed `N^2`. /// In that case the remaining elements of the output array will be 0. From 60c9e1c7d6a1ba746c4d651796aed55f6d598544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:51:22 +0200 Subject: [PATCH 099/212] Fix compile errors of tests --- src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0beb600..136f1ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,11 +179,11 @@ mod test { }; } test_to!( - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99 + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99 ); } @@ -210,7 +210,7 @@ mod test { }; } - test_prime_counts_up_to!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 50, 100); + test_prime_counts_up_to!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 50, 100); } #[test] @@ -227,7 +227,7 @@ mod test { }; } - test_to_n!(0, 1, 2, 3, 4, 5, 10, 100, 1000, 10000); + test_to_n!(1, 2, 3, 4, 5, 10, 100, 1000, 10000); } #[test] From 57e5511bfe6ce8234479fefe6682347777d40151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 13:54:35 +0200 Subject: [PATCH 100/212] impl Default when N>0 --- src/wrapper.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/wrapper.rs b/src/wrapper.rs index 673bb36..b99fb5a 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -304,6 +304,14 @@ impl Primes { } } +/// This statically asserts that N > 0. +impl Default for Primes { + fn default() -> Self { + const { assert!(N > 0, "`N` must be at least 1") } + Self::new() + } +} + impl core::ops::Index for Primes where I: core::slice::SliceIndex<[Underlying]>, From e73fd433246177bdce8f8855ca39f17cfce19a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:32:36 +0200 Subject: [PATCH 101/212] Make primes_geq set lower_limit to 2 if it was lower --- src/generation.rs | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 6244d64..8995ba6 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -217,7 +217,8 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat } /// Returns the `N` smallest primes greater than or equal to `lower_limit`. -/// Fails to compile if `N` is 0. +/// Fails to compile if `N` is 0. If `lower_limit` is less than 2 this functions assumes that it is 2, +/// since there are no primes smaller than 2. /// /// This function will fill the output array from index 0 and stop generating primes if they exceed `N^2`. /// In that case the remaining elements of the output array will be 0. @@ -247,22 +248,30 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat /// const PRIMES: [u64; 3] = primes_geq(5); /// assert_eq!(PRIMES, [5, 7, 0]); /// ``` -/// # Panics +/// # Errors /// -/// Panics if `N^2` does not fit in a `u64`. -/// ```compile_fail +/// Returns an error if `N^2` does not fit in a `u64`, or if `lower_limit` is larger or equal to `N^2`. +/// ``` /// # use const_primes::primes_geq; /// const P: [u64; u32::MAX as usize + 1] = primes_geq(0); /// ``` +/// ``` +/// # use const_primes::primes_geq; +/// const P: [u64; 5] = primes_geq(26); +/// ``` #[must_use = "the function only returns a new value and does not modify its input"] pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenerationResult { const { assert!(N > 0, "`N` must be at least 1") } let n64 = N as u64; - if n64.checked_mul(n64).is_none() { + let Some(n64_sqr) = n64.checked_mul(n64) else { return Err(SegmentedGenerationError::TooLargeN); - } - if lower_limit >= n64 * n64 { + }; + + // There are no primes smaller than 2, so we will always start looking at 2. + lower_limit = if lower_limit >= 2 { lower_limit } else { 2 }; + + if lower_limit >= n64_sqr { return Err(SegmentedGenerationError::TooLargeLimit); } @@ -276,7 +285,6 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera // Move the found primes into the output vector. while i < N { if upper_sieve[i] { - // FIXME: BUG IDENTIFIED: THIS COULD BE 0 largest_found_prime = lower_limit + i as u64; if largest_found_prime >= n64 * n64 { // We do not know if this is actually a prime @@ -357,13 +365,7 @@ mod test { } { const P: SegmentedGenerationResult<1> = primes_geq(0); - assert_eq!( - P, - Err(SegmentedGenerationError::PartialOk(ArraySection::new( - 0..0, - [0] - ))) - ); + assert_eq!(P, Err(SegmentedGenerationError::TooLargeLimit),); } for prime in primes_geq::<2_000>(3_998_000) .unwrap_err() From 43089c9822b223a12caa262eadd0b5a9b2b9019d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:36:08 +0200 Subject: [PATCH 102/212] primes_lt return error if upper_limit <= 2 --- src/generation.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 8995ba6..eaf9803 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -151,10 +151,11 @@ pub const fn primes() -> [Underlying; N] { /// _ => panic!(), /// } /// ``` -/// # Panics +/// # Errors +/// +/// Returns an error if `N^2` does not fit in a `u64`, +/// if `upper_limit` is larger than `N^2` or if `upper_limit` is smaller than or equal to 2. /// -/// Panics if `N^2` does not fit in a `u64` or if `upper_limit` is larger than `N^2`. This is a compile error -/// in const contexts: /// ```compile_fail /// # use const_primes::generation::{SegmentedGenerationResult,primes_lt}; /// // N is too large @@ -169,6 +170,10 @@ pub const fn primes() -> [Underlying; N] { pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerationResult { const { assert!(N > 0, "`N` must be at least 1") } + if upper_limit <= 2 { + return Err(SegmentedGenerationError::TooSmallLimit); + } + let n64 = N as u64; match (n64).checked_mul(n64) { Some(prod) => { @@ -318,6 +323,8 @@ pub enum SegmentedGenerationError { TooLargeN, /// the upper limit was larger than `N^2`. TooLargeLimit, + /// the lower limit was smaller than or equal to 2. + TooSmallLimit, /// Only a part of the output array contains prime numbers as they either exceeded `N^2` or ran out. PartialOk(ArraySection), } @@ -342,6 +349,7 @@ impl fmt::Display for SegmentedGenerationError { match self { Self::TooLargeN => write!(f, "`N^2` did not fit in a `u64`"), Self::TooLargeLimit => write!(f, "the upper limit was larger than `N^2`"), + Self::TooSmallLimit => write!(f, "the lower limit was smaller than or equal to 2"), Self::PartialOk(_) => write!(f, "the sieve entered into a range it's too small for, or the primes ran out. You can access the partially completed result with the function `partial_result`"), } } From 5bbe7a9018a81c5fa581c535e37f933494f7adad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:41:37 +0200 Subject: [PATCH 103/212] Specify type link for partial_ok --- src/generation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index eaf9803..114b828 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -330,7 +330,7 @@ pub enum SegmentedGenerationError { } impl SegmentedGenerationError { - /// Returns the partial result as a restricted array, if there is one. + /// Returns the partial result as an [`ArraySection`], if there is one. pub const fn partial_ok(self) -> Option> { match self { Self::PartialOk(restricted_array) => Some(restricted_array), From 254f345a611301b7e3aeb9335b3649c3388435ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:43:15 +0200 Subject: [PATCH 104/212] impl Hash for ArraySection and SegmentedGenerationError --- src/array_section.rs | 2 +- src/generation.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 11ac353..2830b8e 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -5,7 +5,7 @@ use core::{ /// An array where only a section of the data may be viewed, /// as the other data may e.g. not uphold some invariant. -#[derive(Debug, Clone, Copy, Eq)] +#[derive(Debug, Clone, Copy, Eq, Hash)] pub struct ArraySection { start: usize, end: usize, diff --git a/src/generation.rs b/src/generation.rs index 114b828..482032c 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -317,7 +317,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera /// array is larger than the actual number of primes less than the given `upper_limit`. /// It can also be returned by [`primes_geq`] if it needs to sieve into a /// region of numbers that exceed the square of the size of the requested array. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum SegmentedGenerationError { /// `N^2`` did not fit in a `u64`. TooLargeN, From 0b3c7193b552c978e2dd615b4d1576120a7e2959 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:47:27 +0200 Subject: [PATCH 105/212] Add start and end to ArraySection --- src/array_section.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/array_section.rs b/src/array_section.rs index 2830b8e..70e2fc9 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -46,6 +46,16 @@ impl ArraySection { } } + /// Returns the first index of the full underlying array that is part of the section. + pub const fn start(&self) -> usize { + self.start + } + + /// Returns the first index of the full underlying array that is outside the section (to the left). + pub const fn end(&self) -> usize { + self.end + } + /// Returns a reference to the full underlying array. There is no guarantee about the data /// outside the section. pub const fn as_full_array(&self) -> &[T; N] { From ef2e881b8059cdf9b2dca6c5a2c913ebdc4c8e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:48:08 +0200 Subject: [PATCH 106/212] Correct left to right in docstring of end --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index 70e2fc9..711fcae 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -51,7 +51,7 @@ impl ArraySection { self.start } - /// Returns the first index of the full underlying array that is outside the section (to the left). + /// Returns the first index of the full underlying array that is outside the section (to the right). pub const fn end(&self) -> usize { self.end } From f5e44101830eccddb0f23385b79a69af82b84b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:50:29 +0200 Subject: [PATCH 107/212] Link between start and end --- src/array_section.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/array_section.rs b/src/array_section.rs index 711fcae..b5d4d69 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -47,11 +47,13 @@ impl ArraySection { } /// Returns the first index of the full underlying array that is part of the section. + /// I.e. the section is the subrange `start ..`[`end`](ArraySection::end). pub const fn start(&self) -> usize { self.start } /// Returns the first index of the full underlying array that is outside the section (to the right). + /// I.e. the section is the subrange [`start`](ArraySection::start)`.. end`. pub const fn end(&self) -> usize { self.end } From 109fe61397ded2151e959757aeb36c5478e36f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:51:25 +0200 Subject: [PATCH 108/212] Shorten core::ops::Index with use statement --- src/array_section.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index b5d4d69..838f2bd 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -1,6 +1,6 @@ use core::{ iter::FusedIterator, - ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, + ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, }; /// An array where only a section of the data may be viewed, @@ -102,11 +102,11 @@ impl ArraySection { // region: Index impls -impl core::ops::Index for ArraySection { +impl Index for ArraySection { type Output = T; #[inline] fn index(&self, index: usize) -> &Self::Output { - core::ops::Index::index(self.as_slice(), index) + Index::index(self.as_slice(), index) } } From 37625d9fd7736d0ed16f7a19c31eb57446d15b6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:51:53 +0200 Subject: [PATCH 109/212] Specify full path of ArraySection in macro --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index 838f2bd..448fc7b 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -113,7 +113,7 @@ impl Index for ArraySection { macro_rules! impl_index_range { ($($t:ty),+) => { $( - impl ::core::ops::Index<$t> for ArraySection { + impl ::core::ops::Index<$t> for $crate::array_section::ArraySection { type Output = [T]; #[inline] fn index(&self, index: $t) -> &Self::Output { From 4d3ce4372814cd92488827e5effe1be4b8f90fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 26 Apr 2024 14:53:27 +0200 Subject: [PATCH 110/212] Simplify Index imple --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index 448fc7b..5ee39b5 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -106,7 +106,7 @@ impl Index for ArraySection { type Output = T; #[inline] fn index(&self, index: usize) -> &Self::Output { - Index::index(self.as_slice(), index) + self.as_slice().index(index) } } From 4c83e566fb47a3e3d94742203f8cb77b05de42d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 28 Apr 2024 11:16:20 +0200 Subject: [PATCH 111/212] Fixed final test, on to doctests --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 136f1ef..a82c953 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -236,7 +236,7 @@ mod test { ($($n:expr),+) => { $( { - const P: SegmentedGenerationResult<10> = primes_lt(100); + const P: SegmentedGenerationResult<$n> = primes_lt(100); for (i, prime) in P.unwrap().into_iter().enumerate() { assert_eq!(PRECOMPUTED_PRIMES[25-$n..25][i], prime as u32); } From 1d4d07eb7ba2e09dc4de27454a4a67269d73ef79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 28 Apr 2024 11:18:21 +0200 Subject: [PATCH 112/212] Add const assert to prime_counts --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a82c953..5ff1963 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -114,6 +114,7 @@ pub use sieving::{sieve, sieve_geq, sieve_lt}; pub use wrapper::Primes; /// Returns an array of size `N` where the value at a given index is how many primes are less than or equal to the index. +/// Fails to compile if `N` is 0. /// /// Sieves primes with [`sieve`] and then counts them. /// @@ -126,6 +127,7 @@ pub use wrapper::Primes; /// ``` #[must_use = "the function only returns a new value"] pub const fn prime_counts() -> [usize; N] { + const { assert!(N > 0, "`N` must be at least 1") } let mut counts = [0; N]; if N <= 2 { return counts; From 21bfd9a26d1bd4c2b8b722993e44ff3bffd14b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 28 Apr 2024 11:54:43 +0200 Subject: [PATCH 113/212] Fixed some doc tests --- src/generation.rs | 69 +++++++++++++++++++++++++++++------------------ src/lib.rs | 20 ++++---------- 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 482032c..7de964d 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -144,12 +144,7 @@ pub const fn primes() -> [Underlying; N] { /// ``` /// # use const_primes::generation::{SegmentedGenerationResult, primes_lt}; /// const PRIMES: SegmentedGenerationResult<9> = primes_lt(10); -/// match PRIMES.err() { -/// Some(SegmentedGenerationError::PartialOk(arr)) => { -/// // There are only four primes less than 10: -/// assert_eq!(arr.as_slice(), [2, 3, 5, 7]);} -/// _ => panic!(), -/// } +/// assert_eq!(PRIMES.map_err(|e| e.try_as_slice()), Err(Some(&[2, 3, 5, 7]))) /// ``` /// # Errors /// @@ -159,7 +154,7 @@ pub const fn primes() -> [Underlying; N] { /// ```compile_fail /// # use const_primes::generation::{SegmentedGenerationResult,primes_lt}; /// // N is too large -/// const PRIMES: SegmentedGenerationResult<{u32::MAX as usize + 1}> = primes_lt(100); +/// const PRIMES: SegmentedGenerationResult = primes_lt(100); /// ``` /// ```compile_fail /// # use const_primes::generation::{primes_lt, SegmentedGenerationResult}; @@ -234,35 +229,41 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat /// /// Basic usage: /// ``` -/// # use const_primes::primes_geq; -/// const PRIMES: [u64; 5] = primes_geq(10); -/// assert_eq!(PRIMES, [11, 13, 17, 19, 23]); +/// # use const_primes::{primes_geq, SegmentedGenerationResult, SegmentedGenerationError}; +/// const PRIMES: SegmentedGenerationResult<5> = primes_geq(10); +/// assert_eq!(PRIMES?, [11, 13, 17, 19, 23]); +/// # Ok::<(), SegmentedGenerationError<5>>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::primes_geq; +/// # use const_primes::{primes_geq, SegmentedGenerationResult, SegmentedGenerationError}; /// const N: usize = 71_000; /// # #[allow(long_running_const_eval)] -/// const P: [u64; N] = primes_geq(5_000_000_030); -/// assert_eq!(P[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); -/// assert_eq!(P[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); +/// const P: SegmentedGenerationResult = primes_geq(5_000_000_030); +/// assert_eq!(P?[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); +/// assert_eq!(P?[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); +/// # Ok::<(), SegmentedGenerationError>(()) /// ``` /// Only primes smaller than `N^2` will be generated: /// ``` -/// # use const_primes::primes_geq; -/// const PRIMES: [u64; 3] = primes_geq(5); -/// assert_eq!(PRIMES, [5, 7, 0]); +/// # use const_primes::{primes_geq, SegmentedGenerationResult, SegmentedGenerationError}; +/// const PRIMES: SegmentedGenerationResult<3> = primes_geq(5); +/// assert_eq!(PRIMES.map_err(|e| e.try_as_slice()), Err(Some([5, 7].as_slice()))); +/// # Ok::<(), SegmentedGenerationError<3>>(()) /// ``` +/// /// # Errors /// /// Returns an error if `N^2` does not fit in a `u64`, or if `lower_limit` is larger or equal to `N^2`. /// ``` -/// # use const_primes::primes_geq; -/// const P: [u64; u32::MAX as usize + 1] = primes_geq(0); +/// # use const_primes::{primes_geq, SegmentedGenerationResult}; +/// const P: SegmentedGenerationResult<{u32::MAX as usize + 1}> = primes_geq(0); +/// assert!(P.is_err()); /// ``` /// ``` -/// # use const_primes::primes_geq; -/// const P: [u64; 5] = primes_geq(26); +/// # use const_primes::{primes_geq, SegmentedGenerationResult}; +/// const P: SegmentedGenerationResult<5> = primes_geq(26); +/// assert!(P.is_err()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenerationResult { @@ -330,10 +331,26 @@ pub enum SegmentedGenerationError { } impl SegmentedGenerationError { - /// Returns the partial result as an [`ArraySection`], if there is one. - pub const fn partial_ok(self) -> Option> { + /// Returns the partial result as a reference to an [`ArraySection`], if there is one. + pub const fn try_as_array_section(&self) -> Option<&ArraySection> { + match self { + Self::PartialOk(ref restricted_array) => Some(&restricted_array), + _ => None, + } + } + + /// Consumes the error and returns the partial result as an [`ArraySection`], if there is one. + pub const fn try_into_array_section(self) -> Option> { + match self { + Self::PartialOk(ra) => Some(ra), + _ => None, + } + } + + /// Returns the partial result as a slice if there is one. + pub const fn try_as_slice(&self) -> Option<&[u64]> { match self { - Self::PartialOk(restricted_array) => Some(restricted_array), + Self::PartialOk(ref ra) => Some(ra.as_slice()), _ => None, } } @@ -375,9 +392,9 @@ mod test { const P: SegmentedGenerationResult<1> = primes_geq(0); assert_eq!(P, Err(SegmentedGenerationError::TooLargeLimit),); } - for prime in primes_geq::<2_000>(3_998_000) + for &prime in primes_geq::<2_000>(3_998_000) .unwrap_err() - .partial_ok() + .try_as_slice() .unwrap() { if prime == 0 { diff --git a/src/lib.rs b/src/lib.rs index 5ff1963..635317b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,7 +106,9 @@ mod other_prime; mod sieving; mod wrapper; -pub use generation::{primes, primes_geq, primes_lt}; +pub use generation::{ + primes, primes_geq, primes_lt, SegmentedGenerationError, SegmentedGenerationResult, +}; use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; @@ -153,8 +155,6 @@ pub const fn prime_counts() -> [usize; N] { mod test { use super::*; - use generation::SegmentedGenerationResult; - #[test] fn check_primes_is_prime() { const SET: Primes<3> = Primes::new(); @@ -255,21 +255,11 @@ mod test { assert_eq!( [2, 3, 5, 7], - primes_lt::<9>(10) - .err() - .unwrap() - .partial_ok() - .unwrap() - .as_slice() + primes_lt::<9>(10).err().unwrap().try_as_slice().unwrap() ); assert_eq!( - primes_lt::<2>(3) - .err() - .unwrap() - .partial_ok() - .unwrap() - .as_slice(), + primes_lt::<2>(3).err().unwrap().try_as_slice().unwrap(), [2] ); } From 44d0cbdf09fcd770b2df9fc1b51dc5c8c09c831b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 28 Apr 2024 11:56:46 +0200 Subject: [PATCH 114/212] Rename SegmentedGeneration* to * --- src/generation.rs | 86 +++++++++++++++++++++++------------------------ src/lib.rs | 6 ++-- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 7de964d..f23fb97 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -3,7 +3,7 @@ use core::fmt; use crate::{array_section::ArraySection, sieve, sieving::sieve_segment, Underlying}; /// Type alias for the type returned by the segmented generation functions, that otherwise has two generics that must be the same. -pub type SegmentedGenerationResult = Result<[u64; N], SegmentedGenerationError>; +pub type Result = core::result::Result<[u64; N], Error>; /// Returns the `N` first prime numbers. /// Fails to compile if `N` is 0. @@ -121,29 +121,29 @@ pub const fn primes() -> [Underlying; N] { /// /// Basic usage: /// ``` -/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt, SegmentedGenerationError}; -/// const PRIMES: SegmentedGenerationResult<10> = primes_lt(100); +/// # use const_primes::generation::{Result, primes_lt, Error}; +/// const PRIMES: Result<10> = primes_lt(100); /// assert_eq!(PRIMES?, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); -/// # Ok::<(), SegmentedGenerationError<10>>(()) +/// # Ok::<(), Error<10>>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt, SegmentedGenerationError}; +/// # use const_primes::generation::{Result, primes_lt, Error}; /// const N: usize = 70711; /// # #[allow(long_running_const_eval)] /// // If the generation results in a completely filled array, it can be extracted like this: -/// const BIG_PRIMES: SegmentedGenerationResult = primes_lt(5_000_000_030); +/// const BIG_PRIMES: Result = primes_lt(5_000_000_030); /// /// assert_eq!(BIG_PRIMES?[..3], [4_998_417_421, 4_998_417_427, 4_998_417_443]); /// assert_eq!(BIG_PRIMES?[N - 3..], [4_999_999_903, 4_999_999_937, 5_000_000_029]); -/// # Ok::<(), SegmentedGenerationError>(()) +/// # Ok::<(), Error>(()) /// ``` /// If there are not enough primes to fill the requested array, -/// the output will be the [`SegmentedGenerationError::PartialOk`] variant, +/// the output will be the [`Error::PartialOk`] variant, /// which contains fewer primes than requested: /// ``` -/// # use const_primes::generation::{SegmentedGenerationResult, primes_lt}; -/// const PRIMES: SegmentedGenerationResult<9> = primes_lt(10); +/// # use const_primes::generation::{Result, primes_lt}; +/// const PRIMES: Result<9> = primes_lt(10); /// assert_eq!(PRIMES.map_err(|e| e.try_as_slice()), Err(Some(&[2, 3, 5, 7]))) /// ``` /// # Errors @@ -152,31 +152,31 @@ pub const fn primes() -> [Underlying; N] { /// if `upper_limit` is larger than `N^2` or if `upper_limit` is smaller than or equal to 2. /// /// ```compile_fail -/// # use const_primes::generation::{SegmentedGenerationResult,primes_lt}; +/// # use const_primes::generation::{Result,primes_lt}; /// // N is too large -/// const PRIMES: SegmentedGenerationResult = primes_lt(100); +/// const PRIMES: Result = primes_lt(100); /// ``` /// ```compile_fail -/// # use const_primes::generation::{primes_lt, SegmentedGenerationResult}; +/// # use const_primes::generation::{primes_lt, Result}; /// // N upper_limit > N^2 -/// const PRIMES: SegmentedGenerationResult<5> = primes_lt(26); +/// const PRIMES: Result<5> = primes_lt(26); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerationResult { +pub const fn primes_lt(mut upper_limit: u64) -> Result { const { assert!(N > 0, "`N` must be at least 1") } if upper_limit <= 2 { - return Err(SegmentedGenerationError::TooSmallLimit); + return Err(Error::TooSmallLimit); } let n64 = N as u64; match (n64).checked_mul(n64) { Some(prod) => { if upper_limit > prod { - return Err(SegmentedGenerationError::TooLargeLimit); + return Err(Error::TooLargeLimit); } } - None => return Err(SegmentedGenerationError::TooLargeN), + None => return Err(Error::TooLargeN), } let mut primes: [u64; N] = [0; N]; @@ -209,7 +209,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat upper_limit = smallest_found_prime; if upper_limit <= 2 && total_primes_found < N { let restricted = ArraySection::new(N - total_primes_found..N, primes); - return Err(SegmentedGenerationError::PartialOk(restricted)); + return Err(Error::PartialOk(restricted)); } } @@ -229,56 +229,56 @@ pub const fn primes_lt(mut upper_limit: u64) -> SegmentedGenerat /// /// Basic usage: /// ``` -/// # use const_primes::{primes_geq, SegmentedGenerationResult, SegmentedGenerationError}; -/// const PRIMES: SegmentedGenerationResult<5> = primes_geq(10); +/// # use const_primes::{primes_geq, Result, Error}; +/// const PRIMES: Result<5> = primes_geq(10); /// assert_eq!(PRIMES?, [11, 13, 17, 19, 23]); -/// # Ok::<(), SegmentedGenerationError<5>>(()) +/// # Ok::<(), Error<5>>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::{primes_geq, SegmentedGenerationResult, SegmentedGenerationError}; +/// # use const_primes::{primes_geq, Result, Error}; /// const N: usize = 71_000; /// # #[allow(long_running_const_eval)] -/// const P: SegmentedGenerationResult = primes_geq(5_000_000_030); +/// const P: Result = primes_geq(5_000_000_030); /// assert_eq!(P?[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); /// assert_eq!(P?[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); -/// # Ok::<(), SegmentedGenerationError>(()) +/// # Ok::<(), Error>(()) /// ``` /// Only primes smaller than `N^2` will be generated: /// ``` -/// # use const_primes::{primes_geq, SegmentedGenerationResult, SegmentedGenerationError}; -/// const PRIMES: SegmentedGenerationResult<3> = primes_geq(5); +/// # use const_primes::{primes_geq, Result, Error}; +/// const PRIMES: Result<3> = primes_geq(5); /// assert_eq!(PRIMES.map_err(|e| e.try_as_slice()), Err(Some([5, 7].as_slice()))); -/// # Ok::<(), SegmentedGenerationError<3>>(()) +/// # Ok::<(), Error<3>>(()) /// ``` /// /// # Errors /// /// Returns an error if `N^2` does not fit in a `u64`, or if `lower_limit` is larger or equal to `N^2`. /// ``` -/// # use const_primes::{primes_geq, SegmentedGenerationResult}; -/// const P: SegmentedGenerationResult<{u32::MAX as usize + 1}> = primes_geq(0); +/// # use const_primes::{primes_geq, Result}; +/// const P: Result<{u32::MAX as usize + 1}> = primes_geq(0); /// assert!(P.is_err()); /// ``` /// ``` -/// # use const_primes::{primes_geq, SegmentedGenerationResult}; -/// const P: SegmentedGenerationResult<5> = primes_geq(26); +/// # use const_primes::{primes_geq, Result}; +/// const P: Result<5> = primes_geq(26); /// assert!(P.is_err()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenerationResult { +pub const fn primes_geq(mut lower_limit: u64) -> Result { const { assert!(N > 0, "`N` must be at least 1") } let n64 = N as u64; let Some(n64_sqr) = n64.checked_mul(n64) else { - return Err(SegmentedGenerationError::TooLargeN); + return Err(Error::TooLargeN); }; // There are no primes smaller than 2, so we will always start looking at 2. lower_limit = if lower_limit >= 2 { lower_limit } else { 2 }; if lower_limit >= n64_sqr { - return Err(SegmentedGenerationError::TooLargeLimit); + return Err(Error::TooLargeLimit); } let mut primes = [0; N]; @@ -297,7 +297,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera // since the base sieve does not contain information about // the prime status of numbers larger than or equal to N. let restricted = ArraySection::new(0..total_found_primes, primes); - return Err(SegmentedGenerationError::PartialOk(restricted)); + return Err(Error::PartialOk(restricted)); } primes[total_found_primes] = largest_found_prime; total_found_primes += 1; @@ -319,7 +319,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> SegmentedGenera /// It can also be returned by [`primes_geq`] if it needs to sieve into a /// region of numbers that exceed the square of the size of the requested array. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum SegmentedGenerationError { +pub enum Error { /// `N^2`` did not fit in a `u64`. TooLargeN, /// the upper limit was larger than `N^2`. @@ -330,7 +330,7 @@ pub enum SegmentedGenerationError { PartialOk(ArraySection), } -impl SegmentedGenerationError { +impl Error { /// Returns the partial result as a reference to an [`ArraySection`], if there is one. pub const fn try_as_array_section(&self) -> Option<&ArraySection> { match self { @@ -361,7 +361,7 @@ impl SegmentedGenerationError { } } -impl fmt::Display for SegmentedGenerationError { +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::TooLargeN => write!(f, "`N^2` did not fit in a `u64`"), @@ -381,16 +381,16 @@ mod test { #[test] fn sanity_check_primes_geq() { { - const P: SegmentedGenerationResult<5> = primes_geq(10); + const P: Result<5> = primes_geq(10); assert_eq!(P, Ok([11, 13, 17, 19, 23])); } { - const P: SegmentedGenerationResult<5> = primes_geq(0); + const P: Result<5> = primes_geq(0); assert_eq!(P, Ok([2, 3, 5, 7, 11])); } { - const P: SegmentedGenerationResult<1> = primes_geq(0); - assert_eq!(P, Err(SegmentedGenerationError::TooLargeLimit),); + const P: Result<1> = primes_geq(0); + assert_eq!(P, Err(Error::TooLargeLimit),); } for &prime in primes_geq::<2_000>(3_998_000) .unwrap_err() diff --git a/src/lib.rs b/src/lib.rs index 635317b..0a676c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,9 +106,7 @@ mod other_prime; mod sieving; mod wrapper; -pub use generation::{ - primes, primes_geq, primes_lt, SegmentedGenerationError, SegmentedGenerationResult, -}; +pub use generation::{primes, primes_geq, primes_lt, Error, Result}; use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; @@ -238,7 +236,7 @@ mod test { ($($n:expr),+) => { $( { - const P: SegmentedGenerationResult<$n> = primes_lt(100); + const P: Result<$n> = primes_lt(100); for (i, prime) in P.unwrap().into_iter().enumerate() { assert_eq!(PRECOMPUTED_PRIMES[25-$n..25][i], prime as u32); } From 8c20088943e42ec682571789d675a541ef63e272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 11:32:26 +0200 Subject: [PATCH 115/212] Impl PartialOrd and Ord for ArraySection --- src/array_section.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 5ee39b5..8eb8979 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -1,11 +1,12 @@ use core::{ + cmp::Ordering, iter::FusedIterator, ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, }; /// An array where only a section of the data may be viewed, /// as the other data may e.g. not uphold some invariant. -#[derive(Debug, Clone, Copy, Eq, Hash)] +#[derive(Debug, Clone, Copy, Eq, Hash, Ord)] pub struct ArraySection { start: usize, end: usize, @@ -13,9 +14,15 @@ pub struct ArraySection { } /// Only compares the data in the sections, and not the full arrays. -impl> PartialEq> for ArraySection { +impl PartialEq> for ArraySection { fn eq(&self, other: &ArraySection) -> bool { - self.as_slice() == other.as_slice() + self.as_slice().eq(other.as_slice()) + } +} + +impl PartialOrd for ArraySection { + fn partial_cmp(&self, other: &Self) -> Option { + self.as_slice().partial_cmp(other.as_slice()) } } From def01ae15d741ca07f8709f423d0d3c6e63cb417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 11:37:37 +0200 Subject: [PATCH 116/212] Implement non-marker traits manually for ArraySection --- src/array_section.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 8eb8979..108d07a 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -5,14 +5,21 @@ use core::{ }; /// An array where only a section of the data may be viewed, -/// as the other data may e.g. not uphold some invariant. -#[derive(Debug, Clone, Copy, Eq, Hash, Ord)] +/// as the other data may e.g. not uphold some invariant. +/// The other data is not considered in comparisons, ordering or hashing. +#[derive(Debug, Clone, Copy, Eq)] pub struct ArraySection { start: usize, end: usize, array: [T; N], } +impl core::hash::Hash for ArraySection { + fn hash(&self, state: &mut H) { + self.as_slice().hash(state); + } +} + /// Only compares the data in the sections, and not the full arrays. impl PartialEq> for ArraySection { fn eq(&self, other: &ArraySection) -> bool { @@ -20,6 +27,12 @@ impl PartialEq> for ArraySectio } } +impl Ord for ArraySection { + fn cmp(&self, other: &Self) -> Ordering { + self.as_slice().cmp(other.as_slice()) + } +} + impl PartialOrd for ArraySection { fn partial_cmp(&self, other: &Self) -> Option { self.as_slice().partial_cmp(other.as_slice()) From 1e5ae57caa05c8dcae080ff54db705819a03491d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 11:39:43 +0200 Subject: [PATCH 117/212] Import Hash and Hasher, reorder PartialOrd and Ord impl blocks --- src/array_section.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 108d07a..8c3f1ce 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -1,5 +1,6 @@ use core::{ cmp::Ordering, + hash::{Hash, Hasher}, iter::FusedIterator, ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, }; @@ -14,8 +15,8 @@ pub struct ArraySection { array: [T; N], } -impl core::hash::Hash for ArraySection { - fn hash(&self, state: &mut H) { +impl Hash for ArraySection { + fn hash(&self, state: &mut H) { self.as_slice().hash(state); } } @@ -27,18 +28,18 @@ impl PartialEq> for ArraySectio } } -impl Ord for ArraySection { - fn cmp(&self, other: &Self) -> Ordering { - self.as_slice().cmp(other.as_slice()) - } -} - impl PartialOrd for ArraySection { fn partial_cmp(&self, other: &Self) -> Option { self.as_slice().partial_cmp(other.as_slice()) } } +impl Ord for ArraySection { + fn cmp(&self, other: &Self) -> Ordering { + self.as_slice().cmp(other.as_slice()) + } +} + impl ArraySection { /// Restrict an array so that only elements within the given range are visible. /// From f61bd5e99bb99cbf6df559e6a6b60b4a8fd12e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 11:43:35 +0200 Subject: [PATCH 118/212] #[inline all the things] --- src/array_section.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/array_section.rs b/src/array_section.rs index 8c3f1ce..b20e0a8 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -16,6 +16,7 @@ pub struct ArraySection { } impl Hash for ArraySection { + #[inline] fn hash(&self, state: &mut H) { self.as_slice().hash(state); } @@ -23,18 +24,21 @@ impl Hash for ArraySection { /// Only compares the data in the sections, and not the full arrays. impl PartialEq> for ArraySection { + #[inline] fn eq(&self, other: &ArraySection) -> bool { self.as_slice().eq(other.as_slice()) } } impl PartialOrd for ArraySection { + #[inline] fn partial_cmp(&self, other: &Self) -> Option { self.as_slice().partial_cmp(other.as_slice()) } } impl Ord for ArraySection { + #[inline] fn cmp(&self, other: &Self) -> Ordering { self.as_slice().cmp(other.as_slice()) } @@ -46,6 +50,7 @@ impl ArraySection { /// # Panics /// /// Panics if the range of indices is out of bounds of the array. + #[inline] pub const fn new(sub_range: Range, array: [T; N]) -> Self { assert!( sub_range.start < N && sub_range.end <= N, @@ -69,40 +74,47 @@ impl ArraySection { /// Returns the first index of the full underlying array that is part of the section. /// I.e. the section is the subrange `start ..`[`end`](ArraySection::end). + #[inline] pub const fn start(&self) -> usize { self.start } /// Returns the first index of the full underlying array that is outside the section (to the right). /// I.e. the section is the subrange [`start`](ArraySection::start)`.. end`. + #[inline] pub const fn end(&self) -> usize { self.end } /// Returns a reference to the full underlying array. There is no guarantee about the data /// outside the section. + #[inline] pub const fn as_full_array(&self) -> &[T; N] { &self.array } /// Converts `self` into the full underlying array. There is no guarantee about the data /// outside the section. + #[inline] pub fn into_full_array(self) -> [T; N] { self.array } /// Returns the section of the array as a slice. + #[inline] pub const fn as_slice(&self) -> &[T] { let (_, tail) = self.array.split_at(self.start); tail.split_at(self.end - self.start).0 } /// Returns the length of the array section. + #[inline] pub const fn len(&self) -> usize { self.as_slice().len() } /// Returns whether the array section is empty. + #[inline] pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -110,6 +122,7 @@ impl ArraySection { /// Returns whether the section is just the entire array. /// If this is `true` it is completely fine to call [`as_full_array`](RestrictedArray::as_full_array) /// or [`into_full_array`](RestrictedArray::into_full_array). + #[inline] pub const fn section_is_full_array(&self) -> bool { self.len() == N } From 0985872effc8c232546d9804ed1f77589b5728a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 11:48:29 +0200 Subject: [PATCH 119/212] Oneliner :3 --- src/array_section.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index b20e0a8..4cb6137 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -103,8 +103,15 @@ impl ArraySection { /// Returns the section of the array as a slice. #[inline] pub const fn as_slice(&self) -> &[T] { - let (_, tail) = self.array.split_at(self.start); - tail.split_at(self.end - self.start).0 + self.array + // Split &[head, section, tail] into (&[head], &[section, tail]) + .split_at(self.start) + // and extract the second element of the tuple. + .1 + // Split &[section, tail] into (&[section], &[tail]) + .split_at(self.end - self.start) + // and extract the first element of the tuple. + .0 } /// Returns the length of the array section. From 866158bcaa2aa64d8fb28335bf3b3afe43774b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 11:50:03 +0200 Subject: [PATCH 120/212] impl is_empty with slice::is_empty --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index 4cb6137..2376f1d 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -123,7 +123,7 @@ impl ArraySection { /// Returns whether the array section is empty. #[inline] pub const fn is_empty(&self) -> bool { - self.len() == 0 + self.as_slice().is_empty() } /// Returns whether the section is just the entire array. From 14de1d4e44728592c65f975560b292b08a0b9cda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 11:53:11 +0200 Subject: [PATCH 121/212] impl AsRef<[T]> for ArraySection --- src/array_section.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/array_section.rs b/src/array_section.rs index 2376f1d..159ac41 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -141,6 +141,13 @@ impl ArraySection { } } +impl AsRef<[T]> for ArraySection { + #[inline] + fn as_ref(&self) -> &[T] { + self.as_slice() + } +} + // region: Index impls impl Index for ArraySection { From dbdbe3a63d8165b11303eb325b2a452e954555f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 11:54:10 +0200 Subject: [PATCH 122/212] Remove duplicate impl blocks --- src/array_section.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 159ac41..195c4b1 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -200,12 +200,6 @@ where } // endregion: PartialEq impls -impl AsRef<[T]> for ArraySection { - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} - impl IntoIterator for ArraySection { type IntoIter = ArraySectionIntoIter; type Item = as Iterator>::Item; From aa8e06fe7d49924378ac678b5ecbae4a61c1ebef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 11:57:08 +0200 Subject: [PATCH 123/212] Add as_full_array_mut --- src/array_section.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/array_section.rs b/src/array_section.rs index 195c4b1..d1df4f7 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -93,6 +93,13 @@ impl ArraySection { &self.array } + /// Returns a mutable reference to the full underlying array. + /// There is no guarantee about the data outside the section. + #[inline] + pub fn as_full_array_mut(&mut self) -> &mut [T; N] { + &mut self.array + } + /// Converts `self` into the full underlying array. There is no guarantee about the data /// outside the section. #[inline] From 2382adee84545ec848c7514d8292049aef57d434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 12:10:48 +0200 Subject: [PATCH 124/212] impl PartialOrd and PartialEq for different Ns --- src/array_section.rs | 29 ++++++++++++++++++++++++----- src/generation.rs | 4 ++-- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index d1df4f7..cd67347 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -7,7 +7,22 @@ use core::{ /// An array where only a section of the data may be viewed, /// as the other data may e.g. not uphold some invariant. +/// +/// Indexing into the `ArraySection` indices only into the section: +/// ``` +/// # use const_primes::array_section::ArraySection; +/// const OT: ArraySection = ArraySection::new([0, 1, 2, 0], 1..3); +/// assert_eq![OT[0], 1]; +/// assert_eq![OT[1], 2]; +/// ``` +/// /// The other data is not considered in comparisons, ordering or hashing. +/// ``` +/// use const_primes::array_section::ArraySection; +/// const AS1: ArraySection = ArraySection::new([1, 1, 2, 1], 1..3); +/// const AS2: ArraySection = ArraySection::new([0, 1, 2, 100, -5], 1..3); +/// assert_eq!(AS1, AS2); +/// ``` #[derive(Debug, Clone, Copy, Eq)] pub struct ArraySection { start: usize, @@ -23,16 +38,20 @@ impl Hash for ArraySection { } /// Only compares the data in the sections, and not the full arrays. -impl PartialEq> for ArraySection { - #[inline] +impl PartialEq> + for ArraySection +{ fn eq(&self, other: &ArraySection) -> bool { self.as_slice().eq(other.as_slice()) } } -impl PartialOrd for ArraySection { +/// Only checks the data in the sections, and not the full arrays. +impl PartialOrd> + for ArraySection +{ #[inline] - fn partial_cmp(&self, other: &Self) -> Option { + fn partial_cmp(&self, other: &ArraySection) -> Option { self.as_slice().partial_cmp(other.as_slice()) } } @@ -51,7 +70,7 @@ impl ArraySection { /// /// Panics if the range of indices is out of bounds of the array. #[inline] - pub const fn new(sub_range: Range, array: [T; N]) -> Self { + pub const fn new(array: [T; N], sub_range: Range) -> Self { assert!( sub_range.start < N && sub_range.end <= N, "the sub-range must be in bounds" diff --git a/src/generation.rs b/src/generation.rs index f23fb97..0bd970a 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -208,7 +208,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> Result { } upper_limit = smallest_found_prime; if upper_limit <= 2 && total_primes_found < N { - let restricted = ArraySection::new(N - total_primes_found..N, primes); + let restricted = ArraySection::new(primes, N - total_primes_found..N); return Err(Error::PartialOk(restricted)); } } @@ -296,7 +296,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> Result { // We do not know if this is actually a prime // since the base sieve does not contain information about // the prime status of numbers larger than or equal to N. - let restricted = ArraySection::new(0..total_found_primes, primes); + let restricted = ArraySection::new(primes, 0..total_found_primes); return Err(Error::PartialOk(restricted)); } primes[total_found_primes] = largest_found_prime; From 4d0200f6160aa937e9d361aad9422d4cdd329a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 12:42:44 +0200 Subject: [PATCH 125/212] Add examples to ArraySection --- src/array_section.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index cd67347..4f4eb8a 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -11,17 +11,23 @@ use core::{ /// Indexing into the `ArraySection` indices only into the section: /// ``` /// # use const_primes::array_section::ArraySection; +/// // v v /// const OT: ArraySection = ArraySection::new([0, 1, 2, 0], 1..3); /// assert_eq![OT[0], 1]; /// assert_eq![OT[1], 2]; /// ``` /// -/// The other data is not considered in comparisons, ordering or hashing. +/// The other data is not considered in comparisons, ordering or hashing: /// ``` -/// use const_primes::array_section::ArraySection; -/// const AS1: ArraySection = ArraySection::new([1, 1, 2, 1], 1..3); -/// const AS2: ArraySection = ArraySection::new([0, 1, 2, 100, -5], 1..3); +/// # use const_primes::array_section::ArraySection; +/// // v v +/// const A1: [i32; 4] = [1, 3, 7, 1]; +/// const A2: [i32; 5] = [0, 3, 7, 100, -5]; +/// const AS1: ArraySection = ArraySection::new(A1, 1..3); +/// const AS2: ArraySection = ArraySection::new(A2, 1..3); /// assert_eq!(AS1, AS2); +/// assert!(A1.as_slice() > A2.as_slice()); +/// assert!(!(AS1 > AS2)); /// ``` #[derive(Debug, Clone, Copy, Eq)] pub struct ArraySection { From fd6c373724a8a7594bfe2f6f650481ed9a9c7174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 15:34:45 +0200 Subject: [PATCH 126/212] Fix rustdoc error messages --- src/array_section.rs | 4 ++-- src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 4f4eb8a..d70a3dd 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -159,8 +159,8 @@ impl ArraySection { } /// Returns whether the section is just the entire array. - /// If this is `true` it is completely fine to call [`as_full_array`](RestrictedArray::as_full_array) - /// or [`into_full_array`](RestrictedArray::into_full_array). + /// If this is `true` it is completely fine to call [`as_full_array`](ArraySection::as_full_array) + /// or [`into_full_array`](ArraySection::into_full_array). #[inline] pub const fn section_is_full_array(&self) -> bool { self.len() == N diff --git a/src/lib.rs b/src/lib.rs index 0a676c6..bf9d9b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,7 @@ //! ``` //! ## Arbitrary ranges //! -//! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`](crate::generation::primes_geq) and [`sieve_lt`] +//! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] //! that can be used to work with ranges that don't start at zero. //! ``` //! # use const_primes::generation::primes_geq; From b535d9a12575a675a4584fb247b685311105e7d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 15:38:43 +0200 Subject: [PATCH 127/212] only do log2(n) once --- src/miller_rabin.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/miller_rabin.rs b/src/miller_rabin.rs index c916fce..7f28ada 100644 --- a/src/miller_rabin.rs +++ b/src/miller_rabin.rs @@ -23,7 +23,8 @@ pub const fn is_prime(n: u64) -> bool { false } else { let mut candidate_factor = 5; - while candidate_factor <= n.ilog2() as u64 { + let trial_limit = n.ilog2() as u64; + while candidate_factor <= trial_limit { if n % candidate_factor == 0 || n % (candidate_factor + 2) == 0 { return false; } From 5e559d43b94cb2c5673261aac0afa746495288c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 15:40:45 +0200 Subject: [PATCH 128/212] % -> mod in math --- src/imath.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/imath.rs b/src/imath.rs index b600d2a..d8c11ec 100644 --- a/src/imath.rs +++ b/src/imath.rs @@ -19,7 +19,7 @@ pub const fn isqrt(n: u64) -> u64 { } } -/// Calculates `(base ^ exp) % modulo` without overflow. +/// Calculates `(base ^ exp) mod modulo` without overflow. #[must_use] pub const fn mod_pow(mut base: u64, mut exp: u64, modulo: u64) -> u64 { let mut res = 1; @@ -37,7 +37,7 @@ pub const fn mod_pow(mut base: u64, mut exp: u64, modulo: u64) -> u64 { res } -/// Calculates `(a * b) % modulo` without overflow. +/// Calculates `(a * b) mod modulo` without overflow. #[must_use] pub const fn mod_mul(a: u64, b: u64, modulo: u64) -> u64 { ((a as u128 * b as u128) % modulo as u128) as u64 From 05408b02148129410cf0d8291b647962f3b6eb3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 16:34:51 +0200 Subject: [PATCH 129/212] Almost completely impl PrimesArray --- src/generation.rs | 172 +++++++++++++++++++++++++++++++++------------- src/lib.rs | 18 ++--- 2 files changed, 130 insertions(+), 60 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 0bd970a..c2eef27 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -1,9 +1,12 @@ -use core::fmt; +use core::{ + fmt, + ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, +}; use crate::{array_section::ArraySection, sieve, sieving::sieve_segment, Underlying}; /// Type alias for the type returned by the segmented generation functions, that otherwise has two generics that must be the same. -pub type Result = core::result::Result<[u64; N], Error>; +pub type Result = core::result::Result, Error>; /// Returns the `N` first prime numbers. /// Fails to compile if `N` is 0. @@ -209,11 +212,11 @@ pub const fn primes_lt(mut upper_limit: u64) -> Result { upper_limit = smallest_found_prime; if upper_limit <= 2 && total_primes_found < N { let restricted = ArraySection::new(primes, N - total_primes_found..N); - return Err(Error::PartialOk(restricted)); + return Ok(PrimesArray::Partial(restricted)); } } - Ok(primes) + Ok(PrimesArray::Full(primes)) } /// Returns the `N` smallest primes greater than or equal to `lower_limit`. @@ -231,7 +234,7 @@ pub const fn primes_lt(mut upper_limit: u64) -> Result { /// ``` /// # use const_primes::{primes_geq, Result, Error}; /// const PRIMES: Result<5> = primes_geq(10); -/// assert_eq!(PRIMES?, [11, 13, 17, 19, 23]); +/// assert_eq!(PRIMES?.as_slice(), &[11, 13, 17, 19, 23]); /// # Ok::<(), Error<5>>(()) /// ``` /// Compute larger primes without starting from zero: @@ -240,8 +243,8 @@ pub const fn primes_lt(mut upper_limit: u64) -> Result { /// const N: usize = 71_000; /// # #[allow(long_running_const_eval)] /// const P: Result = primes_geq(5_000_000_030); -/// assert_eq!(P?[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); -/// assert_eq!(P?[N - 3..], [5_001_586_727, 5_001_586_729, 5_001_586_757]); +/// assert_eq!(P?[..3], &[5_000_000_039, 5_000_000_059, 5_000_000_063]); +/// assert_eq!(P?[N - 3..], &[5_001_586_727, 5_001_586_729, 5_001_586_757]); /// # Ok::<(), Error>(()) /// ``` /// Only primes smaller than `N^2` will be generated: @@ -297,7 +300,7 @@ pub const fn primes_geq(mut lower_limit: u64) -> Result { // since the base sieve does not contain information about // the prime status of numbers larger than or equal to N. let restricted = ArraySection::new(primes, 0..total_found_primes); - return Err(Error::PartialOk(restricted)); + return Ok(PrimesArray::Partial(restricted)); } primes[total_found_primes] = largest_found_prime; total_found_primes += 1; @@ -310,64 +313,141 @@ pub const fn primes_geq(mut lower_limit: u64) -> Result { } lower_limit = largest_found_prime + 1; } - Ok(primes) + Ok(PrimesArray::Full(primes)) } -/// An enum describing whether the requested array could be filled completely, or only a partially. -/// A partial array can be returned by [`primes_lt`] if the size of the requested -/// array is larger than the actual number of primes less than the given `upper_limit`. -/// It can also be returned by [`primes_geq`] if it needs to sieve into a -/// region of numbers that exceed the square of the size of the requested array. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Error { - /// `N^2`` did not fit in a `u64`. - TooLargeN, - /// the upper limit was larger than `N^2`. - TooLargeLimit, - /// the lower limit was smaller than or equal to 2. - TooSmallLimit, - /// Only a part of the output array contains prime numbers as they either exceeded `N^2` or ran out. - PartialOk(ArraySection), +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +/// An array where potentially only a section of it contains valid prime numbers. +pub enum PrimesArray { + /// The entire allocated array contains prime numbers + Full([u64; N]), + /// Only a section of the allocated array contains prime numbers. + Partial(ArraySection), } -impl Error { - /// Returns the partial result as a reference to an [`ArraySection`], if there is one. - pub const fn try_as_array_section(&self) -> Option<&ArraySection> { +impl PrimesArray { + /// Returns a slice of all the generated prime numbers. + #[inline] + pub const fn as_slice(&self) -> &[u64] { + match self { + Self::Full(ref arr) => arr.as_slice(), + Self::Partial(ref arr_sec) => arr_sec.as_slice(), + } + } + + /// Returns the underlying array if it is full of valid primes. + #[inline] + pub const fn full(self) -> Option<[u64; N]> { + match self { + Self::Full(arr) => Some(arr), + Self::Partial(_) => None, + } + } + + /// Returns a reference to the underlying array if it is full of valid primes. + #[inline] + pub const fn as_full(&self) -> Option<&[u64; N]> { match self { - Self::PartialOk(ref restricted_array) => Some(&restricted_array), - _ => None, + Self::Full(ref arr) => Some(arr), + Self::Partial(_) => None, } } - /// Consumes the error and returns the partial result as an [`ArraySection`], if there is one. - pub const fn try_into_array_section(self) -> Option> { + /// Returns the primes as a (maybe fully populated) [`ArraySection`]. + #[inline] + pub const fn into_array_section(self) -> ArraySection { match self { - Self::PartialOk(ra) => Some(ra), - _ => None, + Self::Partial(arr_sec) => arr_sec, + Self::Full(arr) => { + let l = arr.len(); + ArraySection::new(arr, 0..l) + } } } - /// Returns the partial result as a slice if there is one. - pub const fn try_as_slice(&self) -> Option<&[u64]> { + /// Returns an [`ArraySection`] if not all of the elements + /// in the underlying array could be computed. + #[inline] + pub const fn partial(self) -> Option> { match self { - Self::PartialOk(ref ra) => Some(ra.as_slice()), - _ => None, + Self::Partial(arr_sec) => Some(arr_sec), + Self::Full(_) => None, } } - /// Returns `true` if this is the `PartialOk` variant. - pub const fn is_partial_ok(&self) -> bool { - matches!(self, Self::PartialOk(_)) + /// Returns a reference to the [`ArraySection`] if not all elements of the underlying array + /// could be computed. + #[inline] + pub const fn as_partial(&self) -> Option<&ArraySection> { + match self { + Self::Partial(ref arr_sec) => Some(arr_sec), + Self::Full(_) => None, + } + } + + /// Returns whether all the elements in the underlying array could be computed. + #[inline] + pub const fn is_full(&self) -> bool { + matches!(self, Self::Full(_)) + } + + /// Returns whether only a subset of the underlying array could be computed. + #[inline] + pub const fn is_partial(&self) -> bool { + matches!(self, Self::Partial(_)) } } +impl From> for ArraySection { + #[inline] + fn from(value: PrimesArray) -> Self { + value.into_array_section() + } +} + +impl AsRef<[u64]> for PrimesArray { + #[inline] + fn as_ref(&self) -> &[u64] { + self.as_slice() + } +} + +macro_rules! impl_index_range { + ($($n:ty),*) => { + $( + impl Index<$n> for PrimesArray { + type Output = [u64]; + fn index(&self, index: $n) -> &Self::Output { + self.as_slice().index(index) + } + } + )* + }; +} + +impl_index_range! {Range, RangeTo, RangeFrom, RangeToInclusive, RangeFull, RangeInclusive} + +/// An enum describing whether the requested array could be filled completely, or only a partially. +/// A partial array can be returned by [`primes_lt`] if the size of the requested +/// array is larger than the actual number of primes less than the given `upper_limit`. +/// It can also be returned by [`primes_geq`] if it needs to sieve into a +/// region of numbers that exceed the square of the size of the requested array. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Error { + /// `N^2`` did not fit in a `u64`. + TooLargeN, + /// the upper limit was larger than `N^2`. + TooLargeLimit, + /// the lower limit was smaller than or equal to 2. + TooSmallLimit, +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::TooLargeN => write!(f, "`N^2` did not fit in a `u64`"), Self::TooLargeLimit => write!(f, "the upper limit was larger than `N^2`"), Self::TooSmallLimit => write!(f, "the lower limit was smaller than or equal to 2"), - Self::PartialOk(_) => write!(f, "the sieve entered into a range it's too small for, or the primes ran out. You can access the partially completed result with the function `partial_result`"), } } } @@ -382,21 +462,17 @@ mod test { fn sanity_check_primes_geq() { { const P: Result<5> = primes_geq(10); - assert_eq!(P, Ok([11, 13, 17, 19, 23])); + assert_eq!(P.unwrap().as_slice(), [11, 13, 17, 19, 23]); } { const P: Result<5> = primes_geq(0); - assert_eq!(P, Ok([2, 3, 5, 7, 11])); + assert_eq!(P.unwrap().as_slice(), [2, 3, 5, 7, 11]); } { const P: Result<1> = primes_geq(0); assert_eq!(P, Err(Error::TooLargeLimit),); } - for &prime in primes_geq::<2_000>(3_998_000) - .unwrap_err() - .try_as_slice() - .unwrap() - { + for &prime in primes_geq::<2_000>(3_998_000).unwrap().as_slice() { if prime == 0 { break; } diff --git a/src/lib.rs b/src/lib.rs index bf9d9b2..a2cb0f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,7 +106,7 @@ mod other_prime; mod sieving; mod wrapper; -pub use generation::{primes, primes_geq, primes_lt, Error, Result}; +pub use generation::{primes, primes_geq, primes_lt, Error, PrimesArray, Result}; use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; @@ -237,12 +237,12 @@ mod test { $( { const P: Result<$n> = primes_lt(100); - for (i, prime) in P.unwrap().into_iter().enumerate() { - assert_eq!(PRECOMPUTED_PRIMES[25-$n..25][i], prime as u32); + for (i, prime) in P.unwrap().as_slice().into_iter().enumerate() { + assert_eq!(PRECOMPUTED_PRIMES[25-$n..25][i], *prime as u32); } assert_eq!( PRECOMPUTED_PRIMES[25-$n..25], - primes_lt::<$n>(100).unwrap().into_iter().map(|i|i as u32).collect::>() + primes_lt::<$n>(100).unwrap().as_slice().into_iter().map(|i| *i as u32).collect::>() ); } )+ @@ -251,15 +251,9 @@ mod test { test_n_below_100!(10, 15, 20); - assert_eq!( - [2, 3, 5, 7], - primes_lt::<9>(10).err().unwrap().try_as_slice().unwrap() - ); + assert_eq!([2, 3, 5, 7], primes_lt::<9>(10).unwrap().as_slice()); - assert_eq!( - primes_lt::<2>(3).err().unwrap().try_as_slice().unwrap(), - [2] - ); + assert_eq!(primes_lt::<2>(3).unwrap().as_slice(), [2]); } #[test] From 8bc620c6afce7fc886a9596507f25d8b21db94dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 16:38:51 +0200 Subject: [PATCH 130/212] impl PartialEq with slices for PrimesArray --- src/generation.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index c2eef27..acb2f41 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -398,6 +398,21 @@ impl PrimesArray { } } +impl PartialEq<[T]> for PrimesArray +where + u64: PartialEq, +{ + fn eq(&self, other: &[T]) -> bool { + self.as_slice().eq(other) + } +} + +impl> PartialEq> for [T] { + fn eq(&self, other: &PrimesArray) -> bool { + self.eq(other.as_slice()) + } +} + impl From> for ArraySection { #[inline] fn from(value: PrimesArray) -> Self { From 866d9d92560231ce3a2750ccccb71caecd5a9f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 16:59:53 +0200 Subject: [PATCH 131/212] Just stack overflows left --- src/generation.rs | 26 +++++++++++--------------- src/lib.rs | 7 ++++--- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index acb2f41..f7e81ae 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -126,28 +126,29 @@ pub const fn primes() -> [Underlying; N] { /// ``` /// # use const_primes::generation::{Result, primes_lt, Error}; /// const PRIMES: Result<10> = primes_lt(100); -/// assert_eq!(PRIMES?, [53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); +/// assert_eq!(PRIMES?.as_slice(), &[53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); /// # Ok::<(), Error<10>>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` /// # use const_primes::generation::{Result, primes_lt, Error}; -/// const N: usize = 70711; +/// const N: usize = 70_711; /// # #[allow(long_running_const_eval)] /// // If the generation results in a completely filled array, it can be extracted like this: /// const BIG_PRIMES: Result = primes_lt(5_000_000_030); /// -/// assert_eq!(BIG_PRIMES?[..3], [4_998_417_421, 4_998_417_427, 4_998_417_443]); -/// assert_eq!(BIG_PRIMES?[N - 3..], [4_999_999_903, 4_999_999_937, 5_000_000_029]); +/// assert_eq!(&BIG_PRIMES?[..3], &[4_998_417_421, 4_998_417_427, 4_998_417_443]); +/// assert_eq!(&BIG_PRIMES?[N - 3..], &[4_999_999_903, 4_999_999_937, 5_000_000_029]); /// # Ok::<(), Error>(()) /// ``` /// If there are not enough primes to fill the requested array, -/// the output will be the [`Error::PartialOk`] variant, +/// the output will be the [`PrimeArray::Partial`] variant, /// which contains fewer primes than requested: /// ``` -/// # use const_primes::generation::{Result, primes_lt}; +/// # use const_primes::generation::{Result, primes_lt, Error}; /// const PRIMES: Result<9> = primes_lt(10); -/// assert_eq!(PRIMES.map_err(|e| e.try_as_slice()), Err(Some(&[2, 3, 5, 7]))) +/// assert_eq!(PRIMES?.as_slice(), &[2, 3, 5, 7]); +/// # Ok::<(), Error<9>>(()) /// ``` /// # Errors /// @@ -243,15 +244,15 @@ pub const fn primes_lt(mut upper_limit: u64) -> Result { /// const N: usize = 71_000; /// # #[allow(long_running_const_eval)] /// const P: Result = primes_geq(5_000_000_030); -/// assert_eq!(P?[..3], &[5_000_000_039, 5_000_000_059, 5_000_000_063]); -/// assert_eq!(P?[N - 3..], &[5_001_586_727, 5_001_586_729, 5_001_586_757]); +/// assert_eq!(&P?[..3], &[5_000_000_039, 5_000_000_059, 5_000_000_063]); +/// assert_eq!(&P?[N - 3..], &[5_001_586_727, 5_001_586_729, 5_001_586_757]); /// # Ok::<(), Error>(()) /// ``` /// Only primes smaller than `N^2` will be generated: /// ``` /// # use const_primes::{primes_geq, Result, Error}; /// const PRIMES: Result<3> = primes_geq(5); -/// assert_eq!(PRIMES.map_err(|e| e.try_as_slice()), Err(Some([5, 7].as_slice()))); +/// assert_eq!(PRIMES?.as_slice(), &[5, 7]); /// # Ok::<(), Error<3>>(()) /// ``` /// @@ -260,11 +261,6 @@ pub const fn primes_lt(mut upper_limit: u64) -> Result { /// Returns an error if `N^2` does not fit in a `u64`, or if `lower_limit` is larger or equal to `N^2`. /// ``` /// # use const_primes::{primes_geq, Result}; -/// const P: Result<{u32::MAX as usize + 1}> = primes_geq(0); -/// assert!(P.is_err()); -/// ``` -/// ``` -/// # use const_primes::{primes_geq, Result}; /// const P: Result<5> = primes_geq(26); /// assert!(P.is_err()); /// ``` diff --git a/src/lib.rs b/src/lib.rs index a2cb0f9..1f93efd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,12 +50,13 @@ //! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] //! that can be used to work with ranges that don't start at zero. //! ``` -//! # use const_primes::generation::primes_geq; +//! # use const_primes::generation::{primes_geq, Result, Error}; //! const N: usize = 70722; //! # #[allow(long_running_const_eval)] -//! const PRIMES_GEQ: [u64; N] = primes_geq(5_000_000_031); +//! const PRIMES_GEQ: Result = primes_geq(5_000_000_031); //! -//! assert_eq!(PRIMES_GEQ[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); +//! assert_eq!(&PRIMES_GEQ?[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); +//! # Ok::<(), Error>(()) //! ``` //! ``` //! # use const_primes::sieve_lt; From 8aeb16af142407cbdfbf07d7f4d42c9cd193dec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 30 Apr 2024 17:00:24 +0200 Subject: [PATCH 132/212] Add TODO --- src/generation.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index f7e81ae..7de3b40 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -112,6 +112,8 @@ pub const fn primes() -> [Underlying; N] { primes } +// TODO: Make primes_lt and primes_geq take two generics? N and MEMORY. + /// Returns the `N` largest primes less than `upper_limit`. /// Fails to compile if `N` is 0. /// From 281882cfae8fb21bc7f7fcf345e8983606f3cf05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 12:39:15 +0200 Subject: [PATCH 133/212] Fix primes_lt --- src/generation.rs | 94 ++++++++++++++++++++++++++++++----------------- src/lib.rs | 8 ++-- 2 files changed, 65 insertions(+), 37 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 7de3b40..7eae59d 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -115,68 +115,75 @@ pub const fn primes() -> [Underlying; N] { // TODO: Make primes_lt and primes_geq take two generics? N and MEMORY. /// Returns the `N` largest primes less than `upper_limit`. -/// Fails to compile if `N` is 0. /// -/// The return array fills from the end until either it is full or there are no more primes. -/// If the primes run out before the array is filled the first elements will have a value of zero. +/// This function uses a segmented sieve of size `MEM` for computation, +/// but only saves the `N` requested primes in the binary. +/// +/// Set `MEM` such that `MEM*MEM >= upper_limit`. +/// +/// Fails to compile if `N` or `MEM` is 0, if `MEM < N` or if `MEM`^2 does not fit in a u64. +/// +/// The return array fills from the end until either it is full +/// (in which case the function returns the [`PrimesArray::Full`] variant) +/// or there are no more primes (in which case the function returns the [`PrimesArray::Partial`] variant). /// -/// If you want to compute primes that are larger than the input, take a look at [`primes_geq`]. +/// If you want to compute primes that are larger than some limit, take a look at [`primes_geq`]. /// /// # Example /// /// Basic usage: /// ``` /// # use const_primes::generation::{Result, primes_lt, Error}; -/// const PRIMES: Result<10> = primes_lt(100); -/// assert_eq!(PRIMES?.as_slice(), &[53, 59, 61, 67, 71, 73, 79, 83, 89, 97]); -/// # Ok::<(), Error<10>>(()) +/// const PRIMES: Result<4> = primes_lt::<4, 10>(100); +/// assert_eq!(PRIMES?, [79, 83, 89, 97]); +/// # Ok::<(), Error<4>>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` /// # use const_primes::generation::{Result, primes_lt, Error}; -/// const N: usize = 70_711; /// # #[allow(long_running_const_eval)] -/// // If the generation results in a completely filled array, it can be extracted like this: -/// const BIG_PRIMES: Result = primes_lt(5_000_000_030); +/// const BIG_PRIMES: Result<3> = primes_lt::<3, 70_711>(5_000_000_030); /// -/// assert_eq!(&BIG_PRIMES?[..3], &[4_998_417_421, 4_998_417_427, 4_998_417_443]); -/// assert_eq!(&BIG_PRIMES?[N - 3..], &[4_999_999_903, 4_999_999_937, 5_000_000_029]); -/// # Ok::<(), Error>(()) +/// assert_eq!(BIG_PRIMES?, [4_999_999_903, 4_999_999_937, 5_000_000_029]); +/// # Ok::<(), Error<3>>(()) /// ``` /// If there are not enough primes to fill the requested array, -/// the output will be the [`PrimeArray::Partial`] variant, +/// the output will be the [`PrimesArray::Partial`] variant, /// which contains fewer primes than requested: /// ``` /// # use const_primes::generation::{Result, primes_lt, Error}; -/// const PRIMES: Result<9> = primes_lt(10); +/// const PRIMES: Result<9> = primes_lt::<9, 9>(10); /// assert_eq!(PRIMES?.as_slice(), &[2, 3, 5, 7]); /// # Ok::<(), Error<9>>(()) /// ``` /// # Errors /// -/// Returns an error if `N^2` does not fit in a `u64`, -/// if `upper_limit` is larger than `N^2` or if `upper_limit` is smaller than or equal to 2. -/// -/// ```compile_fail -/// # use const_primes::generation::{Result,primes_lt}; -/// // N is too large -/// const PRIMES: Result = primes_lt(100); +/// Returns an error if `upper_limit` is larger than `MEM`^2 or if `upper_limit` is smaller than or equal to 2. /// ``` -/// ```compile_fail /// # use const_primes::generation::{primes_lt, Result}; -/// // N upper_limit > N^2 -/// const PRIMES: Result<5> = primes_lt(26); +/// const TOO_LARGE_LIMIT: Result<3> = primes_lt::<3, 5>(26); +/// const TOO_SMALL_LIMIT: Result<1> = primes_lt::<1, 1>(1); +/// assert!(TOO_LARGE_LIMIT.is_err()); +/// assert!(TOO_SMALL_LIMIT.is_err()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn primes_lt(mut upper_limit: u64) -> Result { - const { assert!(N > 0, "`N` must be at least 1") } +pub const fn primes_lt(mut upper_limit: u64) -> Result { + const { + assert!(N > 0, "`N` must be at least 1"); + assert!(MEM >= N, "`MEM` must be at least as large as `N`"); + let mem64 = MEM as u64; + assert!( + mem64.checked_mul(mem64).is_some(), + "`MEM`^2 must fit in a u64" + ); + } if upper_limit <= 2 { return Err(Error::TooSmallLimit); } - let n64 = N as u64; - match (n64).checked_mul(n64) { + let mem64 = MEM as u64; + match (mem64).checked_mul(mem64) { Some(prod) => { if upper_limit > prod { return Err(Error::TooLargeLimit); @@ -188,19 +195,19 @@ pub const fn primes_lt(mut upper_limit: u64) -> Result { let mut primes: [u64; N] = [0; N]; // This will be used to sieve all upper ranges. - let base_sieve: [bool; N] = sieve(); + let base_sieve: [bool; MEM] = sieve(); let mut total_primes_found: usize = 0; 'generate: while total_primes_found < N { // This is the smallest prime we have found so far. let mut smallest_found_prime = primes[N - 1 - total_primes_found]; // Sieve for primes in the segment. - let upper_sieve: [bool; N] = sieve_segment(&base_sieve, upper_limit); + let upper_sieve: [bool; MEM] = sieve_segment(&base_sieve, upper_limit); let mut i: usize = 0; - while i < N { + while i < MEM { // Iterate backwards through the upper sieve. - if upper_sieve[N - 1 - i] { + if upper_sieve[MEM - 1 - i] { smallest_found_prime = upper_limit - 1 - i as u64; // Write every found prime to the primes array. primes[N - 1 - total_primes_found] = smallest_found_prime; @@ -394,6 +401,27 @@ impl PrimesArray { pub const fn is_partial(&self) -> bool { matches!(self, Self::Partial(_)) } + + /// Returns the length of the populated part of the array. + #[inline] + pub const fn len(&self) -> usize { + self.as_slice().len() + } + + /// Returns wether the populated part of the array is empty. + #[inline] + pub const fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl PartialEq<[T; N]> for PrimesArray +where + u64: PartialEq, +{ + fn eq(&self, other: &[T; N]) -> bool { + self.as_slice().eq(other.as_slice()) + } } impl PartialEq<[T]> for PrimesArray diff --git a/src/lib.rs b/src/lib.rs index 1f93efd..38718cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -237,13 +237,13 @@ mod test { ($($n:expr),+) => { $( { - const P: Result<$n> = primes_lt(100); + const P: Result<$n> = primes_lt::<$n, $n>(100); for (i, prime) in P.unwrap().as_slice().into_iter().enumerate() { assert_eq!(PRECOMPUTED_PRIMES[25-$n..25][i], *prime as u32); } assert_eq!( PRECOMPUTED_PRIMES[25-$n..25], - primes_lt::<$n>(100).unwrap().as_slice().into_iter().map(|i| *i as u32).collect::>() + primes_lt::<$n, $n>(100).unwrap().as_slice().into_iter().map(|i| *i as u32).collect::>() ); } )+ @@ -252,9 +252,9 @@ mod test { test_n_below_100!(10, 15, 20); - assert_eq!([2, 3, 5, 7], primes_lt::<9>(10).unwrap().as_slice()); + assert_eq!([2, 3, 5, 7], primes_lt::<4, 9>(10).unwrap().as_slice()); - assert_eq!(primes_lt::<2>(3).unwrap().as_slice(), [2]); + assert_eq!(primes_lt::<1, 2>(3).unwrap().as_slice(), [2]); } #[test] From c23dfd5a8b1c79485424aefedf1e1813fc7bb96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 12:58:37 +0200 Subject: [PATCH 134/212] TODO: move only large enough primes into the array --- src/generation.rs | 67 +++++++++++++++++++++++++---------------------- src/lib.rs | 9 ++++--- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 7eae59d..0974281 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -112,8 +112,6 @@ pub const fn primes() -> [Underlying; N] { primes } -// TODO: Make primes_lt and primes_geq take two generics? N and MEMORY. - /// Returns the `N` largest primes less than `upper_limit`. /// /// This function uses a segmented sieve of size `MEM` for computation, @@ -238,69 +236,76 @@ pub const fn primes_lt(mut upper_limit: u64) - /// /// If you want to compute primes smaller than the input, take a look at [`primes_lt`]. /// -/// # Example +/// # Examples /// /// Basic usage: /// ``` /// # use const_primes::{primes_geq, Result, Error}; -/// const PRIMES: Result<5> = primes_geq(10); -/// assert_eq!(PRIMES?.as_slice(), &[11, 13, 17, 19, 23]); +/// const PRIMES: Result<5> = primes_geq::<5, 5>(10); +/// assert_eq!(PRIMES?, [11, 13, 17, 19, 23]); /// # Ok::<(), Error<5>>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` /// # use const_primes::{primes_geq, Result, Error}; -/// const N: usize = 71_000; /// # #[allow(long_running_const_eval)] -/// const P: Result = primes_geq(5_000_000_030); -/// assert_eq!(&P?[..3], &[5_000_000_039, 5_000_000_059, 5_000_000_063]); -/// assert_eq!(&P?[N - 3..], &[5_001_586_727, 5_001_586_729, 5_001_586_757]); -/// # Ok::<(), Error>(()) +/// const P: Result<3> = primes_geq::<3, 71_000>(5_000_000_030); +/// assert_eq!(P?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); +/// // assert_eq!(P?[N - 3..], &[5_001_586_727, 5_001_586_729, 5_001_586_757]); +/// # Ok::<(), Error<3>>(()) /// ``` -/// Only primes smaller than `N^2` will be generated: +/// Only primes smaller than `MEM^2` will be generated: /// ``` /// # use const_primes::{primes_geq, Result, Error}; -/// const PRIMES: Result<3> = primes_geq(5); +/// const PRIMES: Result<3> = primes_geq::<3, 3>(5); /// assert_eq!(PRIMES?.as_slice(), &[5, 7]); /// # Ok::<(), Error<3>>(()) /// ``` /// /// # Errors /// -/// Returns an error if `N^2` does not fit in a `u64`, or if `lower_limit` is larger or equal to `N^2`. +/// Returns an error if `lower_limit` is larger than or equal to `MEM^2`. /// ``` /// # use const_primes::{primes_geq, Result}; -/// const P: Result<5> = primes_geq(26); -/// assert!(P.is_err()); +/// const PRIMES: Result<5> = primes_geq::<5, 5>(26); +/// assert!(PRIMES.is_err()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn primes_geq(mut lower_limit: u64) -> Result { - const { assert!(N > 0, "`N` must be at least 1") } - - let n64 = N as u64; - let Some(n64_sqr) = n64.checked_mul(n64) else { - return Err(Error::TooLargeN); - }; +pub const fn primes_geq(mut lower_limit: u64) -> Result { + const { + assert!(N > 0, "`N` must be at least 1"); + assert!(MEM >= N, "`MEM` must be at least as large as `N`"); + let mem64 = MEM as u64; + assert!( + mem64.checked_mul(mem64).is_some(), + "`MEM`^2 must fit in a u64" + ); + } // There are no primes smaller than 2, so we will always start looking at 2. lower_limit = if lower_limit >= 2 { lower_limit } else { 2 }; - if lower_limit >= n64_sqr { + let mem64 = MEM as u64; + let mem_sqr = mem64 * mem64; + + if lower_limit >= mem_sqr { return Err(Error::TooLargeLimit); } let mut primes = [0; N]; - let base_sieve: [bool; N] = sieve(); + let base_sieve: [bool; MEM] = sieve(); let mut total_found_primes = 0; 'generate: while total_found_primes < N { let mut largest_found_prime = primes[total_found_primes]; - let upper_sieve = sieve_segment(&base_sieve, lower_limit + n64); + let upper_sieve = sieve_segment(&base_sieve, lower_limit + mem64); + let mut i = 0; - // Move the found primes into the output vector. + // Move all large enough primes into the output array. + // TODO: only write large enough primes while i < N { if upper_sieve[i] { largest_found_prime = lower_limit + i as u64; - if largest_found_prime >= n64 * n64 { + if largest_found_prime >= mem64 * mem64 { // We do not know if this is actually a prime // since the base sieve does not contain information about // the prime status of numbers larger than or equal to N. @@ -502,18 +507,18 @@ mod test { #[test] fn sanity_check_primes_geq() { { - const P: Result<5> = primes_geq(10); + const P: Result<5> = primes_geq::<5, 5>(10); assert_eq!(P.unwrap().as_slice(), [11, 13, 17, 19, 23]); } { - const P: Result<5> = primes_geq(0); + const P: Result<5> = primes_geq::<5, 5>(0); assert_eq!(P.unwrap().as_slice(), [2, 3, 5, 7, 11]); } { - const P: Result<1> = primes_geq(0); + const P: Result<1> = primes_geq::<1, 1>(0); assert_eq!(P, Err(Error::TooLargeLimit),); } - for &prime in primes_geq::<2_000>(3_998_000).unwrap().as_slice() { + for &prime in primes_geq::<2_000, 2_000>(3_998_000).unwrap().as_slice() { if prime == 0 { break; } diff --git a/src/lib.rs b/src/lib.rs index 38718cd..49a779b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,12 +51,13 @@ //! that can be used to work with ranges that don't start at zero. //! ``` //! # use const_primes::generation::{primes_geq, Result, Error}; -//! const N: usize = 70722; //! # #[allow(long_running_const_eval)] -//! const PRIMES_GEQ: Result = primes_geq(5_000_000_031); +//! // Use an array of size 70722 to sieve for primes, but only store +//! // the three primes after 5 million and 31 in the binary. +//! const PRIMES_GEQ: Result<3> = primes_geq::<3, 70_722>(5_000_000_031); //! -//! assert_eq!(&PRIMES_GEQ?[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); -//! # Ok::<(), Error>(()) +//! assert_eq!(PRIMES_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); +//! # Ok::<(), Error<3>>(()) //! ``` //! ``` //! # use const_primes::sieve_lt; From 26cface222136ec54b7edebf16257a7d37514856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 15:52:19 +0200 Subject: [PATCH 135/212] Fix primes_geq --- src/generation.rs | 65 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 0974281..24d3f36 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -271,7 +271,7 @@ pub const fn primes_lt(mut upper_limit: u64) - /// assert!(PRIMES.is_err()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn primes_geq(mut lower_limit: u64) -> Result { +pub const fn primes_geq(lower_limit: u64) -> Result { const { assert!(N > 0, "`N` must be at least 1"); assert!(MEM >= N, "`MEM` must be at least as large as `N`"); @@ -283,7 +283,7 @@ pub const fn primes_geq(mut lower_limit: u64) } // There are no primes smaller than 2, so we will always start looking at 2. - lower_limit = if lower_limit >= 2 { lower_limit } else { 2 }; + let lower_limit = if lower_limit >= 2 { lower_limit } else { 2 }; let mem64 = MEM as u64; let mem_sqr = mem64 * mem64; @@ -293,35 +293,60 @@ pub const fn primes_geq(mut lower_limit: u64) } let mut primes = [0; N]; - let base_sieve: [bool; MEM] = sieve(); let mut total_found_primes = 0; + let mut largest_found_prime = 0; + let base_sieve: [bool; MEM] = sieve(); + let mut sieve_limit = lower_limit; 'generate: while total_found_primes < N { - let mut largest_found_prime = primes[total_found_primes]; - let upper_sieve = sieve_segment(&base_sieve, lower_limit + mem64); + let upper_sieve = sieve_segment(&base_sieve, sieve_limit + mem64); let mut i = 0; - // Move all large enough primes into the output array. - // TODO: only write large enough primes - while i < N { + while i < MEM { if upper_sieve[i] { - largest_found_prime = lower_limit + i as u64; + largest_found_prime = sieve_limit + i as u64; + // We can not know whether this is a prime since the base sieve contains no information + // about numbers larger than or equal to `MEM`. if largest_found_prime >= mem64 * mem64 { - // We do not know if this is actually a prime - // since the base sieve does not contain information about - // the prime status of numbers larger than or equal to N. - let restricted = ArraySection::new(primes, 0..total_found_primes); - return Ok(PrimesArray::Partial(restricted)); + return Ok(PrimesArray::Partial(ArraySection::new( + primes, + 0..total_found_primes, + ))); } - primes[total_found_primes] = largest_found_prime; - total_found_primes += 1; - if total_found_primes >= N { - // We've found enough primes - break 'generate; + if largest_found_prime >= lower_limit { + primes[total_found_primes] = largest_found_prime; + total_found_primes += 1; + if total_found_primes >= N { + // We've found enough primes. + break 'generate; + } } } i += 1; } - lower_limit = largest_found_prime + 1; + sieve_limit = largest_found_prime + 1; + + // let mut i = 0; + // // Move all large enough primes into the output array. + // while i < N { + // if upper_sieve[i] { + // largest_found_prime = lower_limit + i as u64; + // if largest_found_prime >= mem64 * mem64 { + // // We do not know if this is actually a prime + // // since the base sieve does not contain information about + // // the prime status of numbers larger than or equal to N. + // let restricted = ArraySection::new(primes, 0..total_found_primes); + // return Ok(PrimesArray::Partial(restricted)); + // } + // primes[total_found_primes] = largest_found_prime; + // total_found_primes += 1; + // if total_found_primes >= N { + // // We've found enough primes + // break 'generate; + // } + // } + // i += 1; + // } + // lower_limit = largest_found_prime + 1; } Ok(PrimesArray::Full(primes)) } From 1686ba8f29bf7ce3021eb9505063a2ce37d85f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 15:53:17 +0200 Subject: [PATCH 136/212] Comment formatting --- src/generation.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 24d3f36..5a577d3 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -304,7 +304,9 @@ pub const fn primes_geq(lower_limit: u64) -> R while i < MEM { if upper_sieve[i] { largest_found_prime = sieve_limit + i as u64; - // We can not know whether this is a prime since the base sieve contains no information + + // We can not know whether this is actually a prime since + // the base sieve contains no information // about numbers larger than or equal to `MEM`. if largest_found_prime >= mem64 * mem64 { return Ok(PrimesArray::Partial(ArraySection::new( @@ -312,6 +314,7 @@ pub const fn primes_geq(lower_limit: u64) -> R 0..total_found_primes, ))); } + if largest_found_prime >= lower_limit { primes[total_found_primes] = largest_found_prime; total_found_primes += 1; From b26f79cdf9f1e6fe130cdb11d5ffdfdd2a083b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:04:22 +0200 Subject: [PATCH 137/212] Rename TooLargeN to MEMSquaredOverflow --- src/generation.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 5a577d3..1e133df 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -187,7 +187,7 @@ pub const fn primes_lt(mut upper_limit: u64) - return Err(Error::TooLargeLimit); } } - None => return Err(Error::TooLargeN), + None => return Err(Error::MEMSquaredOverflow), } let mut primes: [u64; N] = [0; N]; @@ -508,9 +508,9 @@ impl_index_range! {Range, RangeTo, RangeFrom, RangeToInclus /// region of numbers that exceed the square of the size of the requested array. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Error { - /// `N^2`` did not fit in a `u64`. - TooLargeN, - /// the upper limit was larger than `N^2`. + /// `MEM`^2 did not fit in a `u64`. + MEMSquaredOverflow, + /// the upper limit was larger than `MEM^2`. TooLargeLimit, /// the lower limit was smaller than or equal to 2. TooSmallLimit, @@ -519,7 +519,7 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::TooLargeN => write!(f, "`N^2` did not fit in a `u64`"), + Self::MEMSquaredOverflow => write!(f, "`MEM`^2 did not fit in a `u64`"), Self::TooLargeLimit => write!(f, "the upper limit was larger than `N^2`"), Self::TooSmallLimit => write!(f, "the lower limit was smaller than or equal to 2"), } From b36af3fe49b16b7b8653b96e46f28a34c9aa03a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:05:03 +0200 Subject: [PATCH 138/212] Correct display impl of Error for the TooLargeLimit variant --- src/generation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 1e133df..1d086a3 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -520,7 +520,7 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::MEMSquaredOverflow => write!(f, "`MEM`^2 did not fit in a `u64`"), - Self::TooLargeLimit => write!(f, "the upper limit was larger than `N^2`"), + Self::TooLargeLimit => write!(f, "the upper limit was larger than `MEM`^2"), Self::TooSmallLimit => write!(f, "the lower limit was smaller than or equal to 2"), } } From 1f4e9be6c8aa6a8cacca686639b88e369929cc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:06:10 +0200 Subject: [PATCH 139/212] Remove ominous no guarantee text --- src/array_section.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index d70a3dd..832b738 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -111,22 +111,19 @@ impl ArraySection { self.end } - /// Returns a reference to the full underlying array. There is no guarantee about the data - /// outside the section. + /// Returns a reference to the full underlying array. #[inline] pub const fn as_full_array(&self) -> &[T; N] { &self.array } /// Returns a mutable reference to the full underlying array. - /// There is no guarantee about the data outside the section. #[inline] pub fn as_full_array_mut(&mut self) -> &mut [T; N] { &mut self.array } - /// Converts `self` into the full underlying array. There is no guarantee about the data - /// outside the section. + /// Converts `self` into the full underlying array. #[inline] pub fn into_full_array(self) -> [T; N] { self.array From 76fa4d3e3dae3b8cd2e611dd8654191df800644e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:08:50 +0200 Subject: [PATCH 140/212] Remove ability to edit ArraySection, the point is invariant preervation --- src/array_section.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 832b738..d9d0fb7 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -117,12 +117,6 @@ impl ArraySection { &self.array } - /// Returns a mutable reference to the full underlying array. - #[inline] - pub fn as_full_array_mut(&mut self) -> &mut [T; N] { - &mut self.array - } - /// Converts `self` into the full underlying array. #[inline] pub fn into_full_array(self) -> [T; N] { From b877131db59171b7aaa37d6733faa5464ff9c508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:17:21 +0200 Subject: [PATCH 141/212] Fix broken doc links --- src/array_section.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index d9d0fb7..c4e7b18 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -243,7 +243,7 @@ impl<'a, const N: usize, T> IntoIterator for &'a ArraySection { } } -use array_section_iter::ArraySectionIter; +pub use array_section_iter::ArraySectionIter; mod array_section_iter { use super::FusedIterator; @@ -251,7 +251,7 @@ mod array_section_iter { pub struct ArraySectionIter<'a, T>(core::slice::Iter<'a, T>); impl<'a, T> ArraySectionIter<'a, T> { - pub const fn new(iter: core::slice::Iter<'a, T>) -> Self { + pub(crate) const fn new(iter: core::slice::Iter<'a, T>) -> Self { Self(iter) } } @@ -291,18 +291,18 @@ mod array_section_iter { impl<'a, T> FusedIterator for ArraySectionIter<'a, T> {} } -use array_section_into_iter::ArraySectionIntoIter; +pub use array_section_into_iter::ArraySectionIntoIter; mod array_section_into_iter { use super::FusedIterator; #[derive(Debug, Clone)] - /// Created by the [`into_iter`](RestrictedArray::into_iter) function on [`RestrictedArray`], see it for more information. + /// Created by the [`into_iter`](super::ArraySection::into_iter) function on [`ArraySection`](super::ArraySection), see it for more information. pub struct ArraySectionIntoIter( core::iter::Take>>, ); impl ArraySectionIntoIter { - pub const fn new( + pub(crate) const fn new( iter: core::iter::Take>>, ) -> Self { Self(iter) From 7d4bed8fe962fe9ed94d72a95d9de3554143aa5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:37:14 +0200 Subject: [PATCH 142/212] Add iter function and impl IntoIterator for PrimesArray --- src/generation.rs | 135 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index 1d086a3..14c08be 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -446,6 +446,10 @@ impl PrimesArray { pub const fn is_empty(&self) -> bool { self.len() == 0 } + + pub fn iter(&self) -> PrimesArrayIter<'_> { + PrimesArrayIter::new(self.as_slice().iter()) + } } impl PartialEq<[T; N]> for PrimesArray @@ -501,6 +505,137 @@ macro_rules! impl_index_range { impl_index_range! {Range, RangeTo, RangeFrom, RangeToInclusive, RangeFull, RangeInclusive} +impl IntoIterator for PrimesArray { + type IntoIter = PrimesArrayIntoIter; + type Item = u64; + fn into_iter(self) -> Self::IntoIter { + PrimesArrayIntoIter::new(self) + } +} + +impl<'a, const N: usize> IntoIterator for &'a PrimesArray { + type IntoIter = PrimesArrayIter<'a>; + type Item = &'a u64; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +pub use primes_array_iter::PrimesArrayIter; +mod primes_array_iter { + use core::iter::FusedIterator; + + #[derive(Debug, Clone)] + pub struct PrimesArrayIter<'a>(core::slice::Iter<'a, u64>); + + impl<'a> PrimesArrayIter<'a> { + pub(crate) const fn new(iter: core::slice::Iter<'a, u64>) -> Self { + Self(iter) + } + } + + impl<'a> Iterator for PrimesArrayIter<'a> { + type Item = &'a u64; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } + + #[inline] + fn count(self) -> usize { + self.0.count() + } + + #[inline] + fn last(self) -> Option { + self.0.last() + } + } + + impl<'a> DoubleEndedIterator for PrimesArrayIter<'a> { + #[inline] + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + + impl<'a> ExactSizeIterator for PrimesArrayIter<'a> { + #[inline] + fn len(&self) -> usize { + self.0.len() + } + } + + impl<'a> FusedIterator for PrimesArrayIter<'a> {} +} + +pub use primes_array_into_iter::PrimesArrayIntoIter; +mod primes_array_into_iter { + use core::iter::FusedIterator; + + use crate::{array_section::ArraySectionIntoIter, PrimesArray}; + + pub struct PrimesArrayIntoIter(ArraySectionIntoIter); + + impl PrimesArrayIntoIter { + #[inline] + pub(crate) const fn new(primes_array: PrimesArray) -> Self { + Self(primes_array.into_array_section().into_iter()) + } + } + + impl Iterator for PrimesArrayIntoIter { + type Item = u64; + + #[inline] + fn next(&mut self) -> Option { + self.0.next() + } + + #[inline] + fn last(self) -> Option { + self.0.last() + } + + #[inline] + fn count(self) -> usize { + self.0.count() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + self.0.nth(n) + } + } + + impl DoubleEndedIterator for PrimesArrayIntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.0.next_back() + } + } + + impl ExactSizeIterator for PrimesArrayIntoIter { + #[inline] + fn len(&self) -> usize { + self.0.len() + } + } + + impl FusedIterator for PrimesArrayIntoIter {} +} + /// An enum describing whether the requested array could be filled completely, or only a partially. /// A partial array can be returned by [`primes_lt`] if the size of the requested /// array is larger than the actual number of primes less than the given `upper_limit`. From 25f486f79f720469fcb52e3dd3370d2951826c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:38:08 +0200 Subject: [PATCH 143/212] Remove broken const annotation --- src/generation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 14c08be..445341d 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -590,7 +590,7 @@ mod primes_array_into_iter { impl PrimesArrayIntoIter { #[inline] - pub(crate) const fn new(primes_array: PrimesArray) -> Self { + pub(crate) fn new(primes_array: PrimesArray) -> Self { Self(primes_array.into_array_section().into_iter()) } } From f15d2d780084963da996c87ab6756052a0dd0db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:38:58 +0200 Subject: [PATCH 144/212] Override default size_hint for PrimesArrayIntoIter --- src/generation.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index 445341d..e724e7d 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -603,6 +603,11 @@ mod primes_array_into_iter { self.0.next() } + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.0.size_hint() + } + #[inline] fn last(self) -> Option { self.0.last() From 69e47a159bde47eb04e31bd075c964c0dbbc9614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:40:30 +0200 Subject: [PATCH 145/212] Define iterator impl region --- src/generation.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index e724e7d..e84ce7e 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -505,6 +505,8 @@ macro_rules! impl_index_range { impl_index_range! {Range, RangeTo, RangeFrom, RangeToInclusive, RangeFull, RangeInclusive} +// region: Iterator impls + impl IntoIterator for PrimesArray { type IntoIter = PrimesArrayIntoIter; type Item = u64; @@ -641,6 +643,8 @@ mod primes_array_into_iter { impl FusedIterator for PrimesArrayIntoIter {} } +// endregion: Iterator impls + /// An enum describing whether the requested array could be filled completely, or only a partially. /// A partial array can be returned by [`primes_lt`] if the size of the requested /// array is larger than the actual number of primes less than the given `upper_limit`. From 12e1886fb0c4d791a3b216e8f58d6a3f307846c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:41:14 +0200 Subject: [PATCH 146/212] inline new PrimesArrayIter --- src/generation.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/generation.rs b/src/generation.rs index e84ce7e..ceb00ea 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -531,6 +531,7 @@ mod primes_array_iter { pub struct PrimesArrayIter<'a>(core::slice::Iter<'a, u64>); impl<'a> PrimesArrayIter<'a> { + #[inline] pub(crate) const fn new(iter: core::slice::Iter<'a, u64>) -> Self { Self(iter) } From a254287783e2bf1e35c42d77b7d7953341905e01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:44:32 +0200 Subject: [PATCH 147/212] Make PartialEq<[T; N]> for PrimesArray symmetric --- src/generation.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index ceb00ea..dd26066 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -476,6 +476,12 @@ impl> PartialEq> for [T] { } } +impl> PartialEq> for [T; N] { + fn eq(&self, other: &PrimesArray) -> bool { + self.eq(other.as_slice()) + } +} + impl From> for ArraySection { #[inline] fn from(value: PrimesArray) -> Self { From 3581ea0f7d5481427c6f29161c5c84c33f780fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:45:58 +0200 Subject: [PATCH 148/212] computed --> populated in doctext --- src/generation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index dd26066..7382282 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -404,7 +404,7 @@ impl PrimesArray { } /// Returns an [`ArraySection`] if not all of the elements - /// in the underlying array could be computed. + /// in the underlying array could be populated. #[inline] pub const fn partial(self) -> Option> { match self { @@ -414,7 +414,7 @@ impl PrimesArray { } /// Returns a reference to the [`ArraySection`] if not all elements of the underlying array - /// could be computed. + /// could be populated. #[inline] pub const fn as_partial(&self) -> Option<&ArraySection> { match self { From bd61c12147a44e5274c4042552f8733e991b8d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:50:40 +0200 Subject: [PATCH 149/212] Clarify ArraySection docstring --- src/array_section.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index c4e7b18..752f6ca 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -25,9 +25,11 @@ use core::{ /// const A2: [i32; 5] = [0, 3, 7, 100, -5]; /// const AS1: ArraySection = ArraySection::new(A1, 1..3); /// const AS2: ArraySection = ArraySection::new(A2, 1..3); +/// +/// // Even though the arrays are different +/// assert_ne!(A1.as_slice(), A2.as_slice()); +/// // The sections are the same /// assert_eq!(AS1, AS2); -/// assert!(A1.as_slice() > A2.as_slice()); -/// assert!(!(AS1 > AS2)); /// ``` #[derive(Debug, Clone, Copy, Eq)] pub struct ArraySection { From 1f99b7758f31b3284fa187ef6a48b78cdbc4ad43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 16:53:01 +0200 Subject: [PATCH 150/212] Add note to Hash impl --- src/array_section.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/array_section.rs b/src/array_section.rs index 752f6ca..7ae055f 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -38,6 +38,7 @@ pub struct ArraySection { array: [T; N], } +/// Only hashes the data in the section, and not the full array. impl Hash for ArraySection { #[inline] fn hash(&self, state: &mut H) { From 73981dc1be02779670aadc9a37cd609f6fbd738c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 17:04:25 +0200 Subject: [PATCH 151/212] Add fallible conversions into array for ArraySection --- Cargo.toml | 3 +++ src/array_section.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 1aca1c0..b6a3134 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,9 @@ repository = "https://github.com/JSorngard/const-primes" criterion = { version = "0.5", features = ["html_reports"] } rand = "0.8" +[features] +std = [] + [[bench]] name = "prime_benches" harness = false diff --git a/src/array_section.rs b/src/array_section.rs index 7ae055f..da0c12b 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -114,6 +114,16 @@ impl ArraySection { self.end } + /// Returns a reference to the full underlying array if it is fully populated. + #[inline] + pub const fn try_as_full_array(&self) -> Option<&[T; N]> { + if self.section_is_full_array() { + Some(&self.array) + } else { + None + } + } + /// Returns a reference to the full underlying array. #[inline] pub const fn as_full_array(&self) -> &[T; N] { @@ -167,6 +177,33 @@ impl ArraySection { } } +// region: TryFrom impls + +#[derive(Debug, Clone, Copy)] +pub struct TryFromArraySectionError(ArraySection); + +impl core::fmt::Display for TryFromArraySectionError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "the array was not fully populated") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for TryFromArraySectionError {} + +impl TryFrom> for [T; N] { + type Error = TryFromArraySectionError; + fn try_from(value: ArraySection) -> Result { + if value.section_is_full_array() { + Ok(value.array) + } else { + Err(TryFromArraySectionError(value)) + } + } +} + +// endregion: TryFrom impls + impl AsRef<[T]> for ArraySection { #[inline] fn as_ref(&self) -> &[T] { From 98c04d71996416c5215a9d312fc4dcfd278252f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 17:05:51 +0200 Subject: [PATCH 152/212] Add note about sections to Ord impl --- src/array_section.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/array_section.rs b/src/array_section.rs index da0c12b..1e5c9cf 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -65,6 +65,7 @@ impl PartialOrd Ord for ArraySection { #[inline] fn cmp(&self, other: &Self) -> Ordering { From e974051e3c86169bcbb1d0c0a5798813cc823e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Thu, 2 May 2024 17:06:37 +0200 Subject: [PATCH 153/212] Inline TryFrom stuffs --- src/array_section.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/array_section.rs b/src/array_section.rs index 1e5c9cf..b6000e7 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -50,6 +50,7 @@ impl Hash for ArraySection { impl PartialEq> for ArraySection { + #[inline] fn eq(&self, other: &ArraySection) -> bool { self.as_slice().eq(other.as_slice()) } @@ -184,6 +185,7 @@ impl ArraySection { pub struct TryFromArraySectionError(ArraySection); impl core::fmt::Display for TryFromArraySectionError { + #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(f, "the array was not fully populated") } @@ -194,6 +196,7 @@ impl std::error::Error for TryFromArraySectionError {} impl TryFrom> for [T; N] { type Error = TryFromArraySectionError; + #[inline] fn try_from(value: ArraySection) -> Result { if value.section_is_full_array() { Ok(value.array) From 45280b3ae74959905f96a22373ace44db1a407da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 May 2024 13:29:42 +0200 Subject: [PATCH 154/212] Reshape return type of suffixed generation functions --- src/array_section.rs | 55 ++++++++++++++++---- src/generation.rs | 117 ++++++++++++++++++++----------------------- src/lib.rs | 24 ++++++--- 3 files changed, 113 insertions(+), 83 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index b6000e7..724c4b3 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -46,16 +46,6 @@ impl Hash for ArraySection { } } -/// Only compares the data in the sections, and not the full arrays. -impl PartialEq> - for ArraySection -{ - #[inline] - fn eq(&self, other: &ArraySection) -> bool { - self.as_slice().eq(other.as_slice()) - } -} - /// Only checks the data in the sections, and not the full arrays. impl PartialOrd> for ArraySection @@ -192,10 +182,12 @@ impl core::fmt::Display for TryFromArraySectionError { } #[cfg(feature = "std")] -impl std::error::Error for TryFromArraySectionError {} +impl std::error::Error for TryFromArraySectionError {} +/// Converts the `ArraySection` into an array if the section is actually the entire array. impl TryFrom> for [T; N] { type Error = TryFromArraySectionError; + #[inline] fn try_from(value: ArraySection) -> Result { if value.section_is_full_array() { @@ -208,6 +200,16 @@ impl TryFrom> for [T; N] { // endregion: TryFrom impls +impl From<[T; N]> for ArraySection { + fn from(value: [T; N]) -> Self { + Self { + start: 0, + end: N, + array: value, + } + } +} + impl AsRef<[T]> for ArraySection { #[inline] fn as_ref(&self) -> &[T] { @@ -244,6 +246,18 @@ impl_index_range! {Range, RangeFrom, RangeFull, RangeTo, Ra // endregion: Index impls // region: PartialEq impls + +/// Only compares the sections, and not the full arrays. +impl PartialEq> for ArraySection +where + [U]: PartialEq<[T]>, +{ + #[inline] + fn eq(&self, other: &ArraySection) -> bool { + self.as_slice().eq(other.as_slice()) + } +} + impl PartialEq<[U]> for ArraySection where U: PartialEq, @@ -265,6 +279,25 @@ where self == other.as_slice() } } + +impl PartialEq<[T; N]> for ArraySection +where + [U]: PartialEq<[T]>, +{ + fn eq(&self, other: &[T; N]) -> bool { + self.as_slice().eq(other.as_slice()) + } +} + +impl PartialEq> for [T; N] +where + [T]: PartialEq<[U]>, +{ + fn eq(&self, other: &ArraySection) -> bool { + self.as_slice().eq(other.as_slice()) + } +} + // endregion: PartialEq impls impl IntoIterator for ArraySection { diff --git a/src/generation.rs b/src/generation.rs index 7382282..fcf23a0 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -5,8 +5,8 @@ use core::{ use crate::{array_section::ArraySection, sieve, sieving::sieve_segment, Underlying}; -/// Type alias for the type returned by the segmented generation functions, that otherwise has two generics that must be the same. -pub type Result = core::result::Result, Error>; +/// Type alias for the type returned by the segmented sieving and generation functions. +pub type Result = core::result::Result, Error>; /// Returns the `N` first prime numbers. /// Fails to compile if `N` is 0. @@ -121,9 +121,8 @@ pub const fn primes() -> [Underlying; N] { /// /// Fails to compile if `N` or `MEM` is 0, if `MEM < N` or if `MEM`^2 does not fit in a u64. /// -/// The return array fills from the end until either it is full -/// (in which case the function returns the [`PrimesArray::Full`] variant) -/// or there are no more primes (in which case the function returns the [`PrimesArray::Partial`] variant). +/// The return array fills from the end until either it is full, +/// or there are no more primes. /// /// If you want to compute primes that are larger than some limit, take a look at [`primes_geq`]. /// @@ -132,9 +131,11 @@ pub const fn primes() -> [Underlying; N] { /// Basic usage: /// ``` /// # use const_primes::generation::{Result, primes_lt, Error}; +/// // Sieving up to 100 means the sieve needs to be of size sqrt(100) = 10. +/// // However, we only save the 4 largest primes in the constant. /// const PRIMES: Result<4> = primes_lt::<4, 10>(100); /// assert_eq!(PRIMES?, [79, 83, 89, 97]); -/// # Ok::<(), Error<4>>(()) +/// # Ok::<(), Error>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` @@ -143,16 +144,18 @@ pub const fn primes() -> [Underlying; N] { /// const BIG_PRIMES: Result<3> = primes_lt::<3, 70_711>(5_000_000_030); /// /// assert_eq!(BIG_PRIMES?, [4_999_999_903, 4_999_999_937, 5_000_000_029]); -/// # Ok::<(), Error<3>>(()) +/// # Ok::<(), Error>(()) /// ``` -/// If there are not enough primes to fill the requested array, -/// the output will be the [`PrimesArray::Partial`] variant, -/// which contains fewer primes than requested: +/// If the number of primes requested, `N`, is larger than +/// the number of primes that exists below the `lower_limit` we +/// will get a partial result of all the existing primes. +/// Due to limitations on const evaluation this will still +/// take up the full `N` numbers worth of memory. /// ``` /// # use const_primes::generation::{Result, primes_lt, Error}; /// const PRIMES: Result<9> = primes_lt::<9, 9>(10); -/// assert_eq!(PRIMES?.as_slice(), &[2, 3, 5, 7]); -/// # Ok::<(), Error<9>>(()) +/// assert_eq!(PRIMES?, [2, 3, 5, 7]); +/// # Ok::<(), Error>(()) /// ``` /// # Errors /// @@ -177,17 +180,17 @@ pub const fn primes_lt(mut upper_limit: u64) - } if upper_limit <= 2 { - return Err(Error::TooSmallLimit); + return Err(Error::TooSmallLimit(upper_limit)); } let mem64 = MEM as u64; match (mem64).checked_mul(mem64) { Some(prod) => { if upper_limit > prod { - return Err(Error::TooLargeLimit); + return Err(Error::TooLargeLimit(upper_limit, MEM)); } } - None => return Err(Error::MEMSquaredOverflow), + None => return Err(Error::MEMSquaredOverflow(MEM)), } let mut primes: [u64; N] = [0; N]; @@ -219,12 +222,11 @@ pub const fn primes_lt(mut upper_limit: u64) - } upper_limit = smallest_found_prime; if upper_limit <= 2 && total_primes_found < N { - let restricted = ArraySection::new(primes, N - total_primes_found..N); - return Ok(PrimesArray::Partial(restricted)); + return Ok(ArraySection::new(primes, N - total_primes_found..N)); } } - Ok(PrimesArray::Full(primes)) + Ok(ArraySection::new(primes, 0..N)) } /// Returns the `N` smallest primes greater than or equal to `lower_limit`. @@ -243,7 +245,7 @@ pub const fn primes_lt(mut upper_limit: u64) - /// # use const_primes::{primes_geq, Result, Error}; /// const PRIMES: Result<5> = primes_geq::<5, 5>(10); /// assert_eq!(PRIMES?, [11, 13, 17, 19, 23]); -/// # Ok::<(), Error<5>>(()) +/// # Ok::<(), Error>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` @@ -251,15 +253,14 @@ pub const fn primes_lt(mut upper_limit: u64) - /// # #[allow(long_running_const_eval)] /// const P: Result<3> = primes_geq::<3, 71_000>(5_000_000_030); /// assert_eq!(P?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); -/// // assert_eq!(P?[N - 3..], &[5_001_586_727, 5_001_586_729, 5_001_586_757]); -/// # Ok::<(), Error<3>>(()) +/// # Ok::<(), Error>(()) /// ``` /// Only primes smaller than `MEM^2` will be generated: /// ``` /// # use const_primes::{primes_geq, Result, Error}; /// const PRIMES: Result<3> = primes_geq::<3, 3>(5); /// assert_eq!(PRIMES?.as_slice(), &[5, 7]); -/// # Ok::<(), Error<3>>(()) +/// # Ok::<(), Error>(()) /// ``` /// /// # Errors @@ -283,15 +284,17 @@ pub const fn primes_geq(lower_limit: u64) -> R } // There are no primes smaller than 2, so we will always start looking at 2. - let lower_limit = if lower_limit >= 2 { lower_limit } else { 2 }; + let new_lower_limit = if lower_limit >= 2 { lower_limit } else { 2 }; let mem64 = MEM as u64; let mem_sqr = mem64 * mem64; - if lower_limit >= mem_sqr { - return Err(Error::TooLargeLimit); + if new_lower_limit >= mem_sqr { + return Err(Error::TooLargeLimit(lower_limit, MEM)); } + let lower_limit = new_lower_limit; + let mut primes = [0; N]; let mut total_found_primes = 0; let mut largest_found_prime = 0; @@ -309,10 +312,7 @@ pub const fn primes_geq(lower_limit: u64) -> R // the base sieve contains no information // about numbers larger than or equal to `MEM`. if largest_found_prime >= mem64 * mem64 { - return Ok(PrimesArray::Partial(ArraySection::new( - primes, - 0..total_found_primes, - ))); + return Ok(ArraySection::new(primes, 0..total_found_primes)); } if largest_found_prime >= lower_limit { @@ -327,31 +327,9 @@ pub const fn primes_geq(lower_limit: u64) -> R i += 1; } sieve_limit = largest_found_prime + 1; + } - // let mut i = 0; - // // Move all large enough primes into the output array. - // while i < N { - // if upper_sieve[i] { - // largest_found_prime = lower_limit + i as u64; - // if largest_found_prime >= mem64 * mem64 { - // // We do not know if this is actually a prime - // // since the base sieve does not contain information about - // // the prime status of numbers larger than or equal to N. - // let restricted = ArraySection::new(primes, 0..total_found_primes); - // return Ok(PrimesArray::Partial(restricted)); - // } - // primes[total_found_primes] = largest_found_prime; - // total_found_primes += 1; - // if total_found_primes >= N { - // // We've found enough primes - // break 'generate; - // } - // } - // i += 1; - // } - // lower_limit = largest_found_prime + 1; - } - Ok(PrimesArray::Full(primes)) + Ok(ArraySection::new(primes, 0..N)) } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -658,25 +636,36 @@ mod primes_array_into_iter { /// It can also be returned by [`primes_geq`] if it needs to sieve into a /// region of numbers that exceed the square of the size of the requested array. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Error { +pub enum Error { /// `MEM`^2 did not fit in a `u64`. - MEMSquaredOverflow, - /// the upper limit was larger than `MEM^2`. - TooLargeLimit, - /// the lower limit was smaller than or equal to 2. - TooSmallLimit, + MEMSquaredOverflow(usize), + /// the limit was larger than `MEM^2`. + TooLargeLimit(u64, usize), + /// the limit was smaller than or equal to 2. + TooSmallLimit(u64), } -impl fmt::Display for Error { +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::MEMSquaredOverflow => write!(f, "`MEM`^2 did not fit in a `u64`"), - Self::TooLargeLimit => write!(f, "the upper limit was larger than `MEM`^2"), - Self::TooSmallLimit => write!(f, "the lower limit was smaller than or equal to 2"), + Self::MEMSquaredOverflow(mem) => { + write!(f, "`MEM` was {mem}, and so `MEM`^2 did not fit in a `u64`") + } + Self::TooLargeLimit(limit, mem) => write!( + f, + "the limit was {limit} and `MEM` was {mem}, so the limit was larger than `MEM`^2" + ), + Self::TooSmallLimit(limit) => write!( + f, + "the limit was {limit}, which is smaller than or equal to 2" + ), } } } +#[cfg(feature = "std")] +impl std::error::Error for Error {} + #[cfg(test)] mod test { use crate::is_prime; @@ -695,7 +684,7 @@ mod test { } { const P: Result<1> = primes_geq::<1, 1>(0); - assert_eq!(P, Err(Error::TooLargeLimit),); + assert_eq!(P, Err(Error::TooLargeLimit(0, 1))); } for &prime in primes_geq::<2_000, 2_000>(3_998_000).unwrap().as_slice() { if prime == 0 { diff --git a/src/lib.rs b/src/lib.rs index 49a779b..e4b065a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,16 +48,21 @@ //! ## Arbitrary ranges //! //! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] -//! that can be used to work with ranges that don't start at zero. +//! that can be used to work with ranges that don't start at zero. They take two generics: the number of primes +//! to store in the binary, and the size of the sieve used during const evaluation +//! (which must be at least `isqrt(largest_encountered_number)`). This means that one can +//! sieve up to large numbers, but doesn't need to store the entire sieve in the binary. //! ``` //! # use const_primes::generation::{primes_geq, Result, Error}; +//! use const_primes::isqrt; //! # #[allow(long_running_const_eval)] -//! // Use an array of size 70722 to sieve for primes, but only store -//! // the three primes after 5 million and 31 in the binary. -//! const PRIMES_GEQ: Result<3> = primes_geq::<3, 70_722>(5_000_000_031); +//! // If we want the three smallest primes after 5 billion and 31 we need a large sieve +//! // to find them without returning an error. +//! // However, we don't need to store the entire sieve, we can just store the three primes. +//! const PRIMES_GEQ: Result<3> = primes_geq::<3, {isqrt(5_000_000_031) as usize + 1}>(5_000_000_031); //! //! assert_eq!(PRIMES_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); -//! # Ok::<(), Error<3>>(()) +//! # Ok::<(), Error>(()) //! ``` //! ``` //! # use const_primes::sieve_lt; @@ -90,10 +95,13 @@ //! assert_eq!(NOSUCH, None); //! ``` //! and more! +//! +//! # Features +//! +//! `std`: links the standard library and uses it to implement the [`Error`](std::error::Error) trait for the error types. #![forbid(unsafe_code)] -#![cfg_attr(not(test), no_std)] -#![feature(inline_const)] +#![cfg_attr(all(not(test), not(feature = "std")), no_std)] /// The type that `Primes` stores, and `primes::()`` returns. Currently `u32`. // Just change this to whatever unsigned primitive integer type you want and it should work as long as it has enough bits for your purposes. @@ -109,7 +117,7 @@ mod sieving; mod wrapper; pub use generation::{primes, primes_geq, primes_lt, Error, PrimesArray, Result}; -use imath::isqrt; +pub use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; pub use sieving::{sieve, sieve_geq, sieve_lt}; From 25bf22d46449b653f2acdfbd57940fc49d7dc9ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 May 2024 15:52:33 +0200 Subject: [PATCH 155/212] Improve documentation of primes_geq --- src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e4b065a..51a2166 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,16 +49,16 @@ //! //! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] //! that can be used to work with ranges that don't start at zero. They take two generics: the number of primes -//! to store in the binary, and the size of the sieve used during const evaluation -//! (which must be at least `isqrt(largest_encountered_number)`). This means that one can +//! to store in the binary, and the size of the sieve used during evaluation +//! (which must be at least `isqrt(largest_encountered_number) + 1`). This means that one can //! sieve up to large numbers, but doesn't need to store the entire sieve in the binary. //! ``` //! # use const_primes::generation::{primes_geq, Result, Error}; //! use const_primes::isqrt; //! # #[allow(long_running_const_eval)] -//! // If we want the three smallest primes after 5 billion and 31 we need a large sieve -//! // to find them without returning an error. -//! // However, we don't need to store the entire sieve, we can just store the three primes. +//! // The sieve needs to be of size sqrt(limit) + 1, +//! // but we don't need to store the entire sieve, we can just store the primes we want. +//! // For the three primes after 5 billion and 31: //! const PRIMES_GEQ: Result<3> = primes_geq::<3, {isqrt(5_000_000_031) as usize + 1}>(5_000_000_031); //! //! assert_eq!(PRIMES_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); From 881e2abc842012f749e9bf0aebf5f317dd90c9f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 May 2024 15:53:16 +0200 Subject: [PATCH 156/212] Better math notation --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 51a2166..f6d5a8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ //! # use const_primes::generation::{primes_geq, Result, Error}; //! use const_primes::isqrt; //! # #[allow(long_running_const_eval)] -//! // The sieve needs to be of size sqrt(limit) + 1, +//! // The sieve needs to be of size ceil(sqrt(limit)), //! // but we don't need to store the entire sieve, we can just store the primes we want. //! // For the three primes after 5 billion and 31: //! const PRIMES_GEQ: Result<3> = primes_geq::<3, {isqrt(5_000_000_031) as usize + 1}>(5_000_000_031); From 4b1fb6adc18f6113bf29782efce714bbb06bf161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 May 2024 16:02:50 +0200 Subject: [PATCH 157/212] Show usage of primes_geq --- src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f6d5a8f..d1d32eb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,8 +53,8 @@ //! (which must be at least `isqrt(largest_encountered_number) + 1`). This means that one can //! sieve up to large numbers, but doesn't need to store the entire sieve in the binary. //! ``` -//! # use const_primes::generation::{primes_geq, Result, Error}; -//! use const_primes::isqrt; +//! # use const_primes::Error; +//! use const_primes::{primes_geq, isqrt, Result}; //! # #[allow(long_running_const_eval)] //! // The sieve needs to be of size ceil(sqrt(limit)), //! // but we don't need to store the entire sieve, we can just store the primes we want. @@ -116,6 +116,7 @@ mod other_prime; mod sieving; mod wrapper; +pub use array_section::ArraySection; pub use generation::{primes, primes_geq, primes_lt, Error, PrimesArray, Result}; pub use imath::isqrt; pub use miller_rabin::is_prime; From 805c05a95f4806ead8eee9d54b134b46e59597f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 May 2024 16:03:56 +0200 Subject: [PATCH 158/212] Fix impl of Error for generation::Error --- src/generation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index fcf23a0..3c2ada6 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -664,7 +664,7 @@ impl fmt::Display for Error { } #[cfg(feature = "std")] -impl std::error::Error for Error {} +impl std::error::Error for Error {} #[cfg(test)] mod test { From 24448aa4c2d114c04342b28855cf9db2da1cab45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 May 2024 16:06:21 +0200 Subject: [PATCH 159/212] Simplify docstring --- src/lib.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d1d32eb..a047a91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,15 +50,13 @@ //! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] //! that can be used to work with ranges that don't start at zero. They take two generics: the number of primes //! to store in the binary, and the size of the sieve used during evaluation -//! (which must be at least `isqrt(largest_encountered_number) + 1`). This means that one can -//! sieve up to large numbers, but doesn't need to store the entire sieve in the binary. +//! (which must be at least the ceiling of the square root of the largest encountered number. +//! This means that one can sieve up to large numbers, +//! but doesn't need to store the entire sieve in the binary. //! ``` //! # use const_primes::Error; //! use const_primes::{primes_geq, isqrt, Result}; //! # #[allow(long_running_const_eval)] -//! // The sieve needs to be of size ceil(sqrt(limit)), -//! // but we don't need to store the entire sieve, we can just store the primes we want. -//! // For the three primes after 5 billion and 31: //! const PRIMES_GEQ: Result<3> = primes_geq::<3, {isqrt(5_000_000_031) as usize + 1}>(5_000_000_031); //! //! assert_eq!(PRIMES_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); From da22f4ee55e63ed80d852f2b81dd19fd0407fe7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 May 2024 17:07:44 +0200 Subject: [PATCH 160/212] close paren --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a047a91..f63daa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,16 +50,16 @@ //! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] //! that can be used to work with ranges that don't start at zero. They take two generics: the number of primes //! to store in the binary, and the size of the sieve used during evaluation -//! (which must be at least the ceiling of the square root of the largest encountered number. +//! (which must be at least the ceiling of the square root of the largest encountered number). //! This means that one can sieve up to large numbers, //! but doesn't need to store the entire sieve in the binary. //! ``` //! # use const_primes::Error; //! use const_primes::{primes_geq, isqrt, Result}; //! # #[allow(long_running_const_eval)] -//! const PRIMES_GEQ: Result<3> = primes_geq::<3, {isqrt(5_000_000_031) as usize + 1}>(5_000_000_031); +//! const P_GEQ: Result<3> = primes_geq::<3, {isqrt(5_000_000_031) as usize + 1}>(5_000_000_031); //! -//! assert_eq!(PRIMES_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); +//! assert_eq!(P_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); //! # Ok::<(), Error>(()) //! ``` //! ``` From 62e2e6521d94e8888496517d096c21a7f9419710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Fri, 3 May 2024 17:10:43 +0200 Subject: [PATCH 161/212] Better format of primes_geq demo --- src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f63daa4..a6c7172 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,8 +56,10 @@ //! ``` //! # use const_primes::Error; //! use const_primes::{primes_geq, isqrt, Result}; -//! # #[allow(long_running_const_eval)] -//! const P_GEQ: Result<3> = primes_geq::<3, {isqrt(5_000_000_031) as usize + 1}>(5_000_000_031); +//! const NUM_PRIMES: usize = 3; +//! const LIMIT: u64 = 5_000_000_031; +//! const SIEVE_SIZE: usize = isqrt(LIMIT) as usize + 1; +//! const P_GEQ: Result = primes_geq::(LIMIT); //! //! assert_eq!(P_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); //! # Ok::<(), Error>(()) From 542753b82532b2f76e4732519ee605295f3d1d4b Mon Sep 17 00:00:00 2001 From: Johan Date: Fri, 3 May 2024 22:36:11 +0200 Subject: [PATCH 162/212] Solve mem problem with macro --- src/generation.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 21 +++++++++++++----- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 3c2ada6..effc07c 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -229,6 +229,62 @@ pub const fn primes_lt(mut upper_limit: u64) - Ok(ArraySection::new(primes, 0..N)) } +/// Call [`primes_geq`] and [`primes_lt`], but automatically compute the memory requirement. +/// +/// # Example +/// +/// ``` +/// # use const_primes::{Result, primes_where, Error}; +/// const PRIMES_GEQ: Result<3> = primes_where!(3 >= 5_000_000_031_u64); +/// const PRIMES_LT: Result<3> = primes_where!(3 < 5_000_000_031_u64); +/// assert_eq!(PRIMES_GEQ?, [5000000039, 5000000059, 5000000063]); +/// assert_eq!(PRIMES_LT?, [4999999903, 4999999937, 5000000029]); +/// # Ok::<(), Error>(()) +/// ``` +#[macro_export] +macro_rules! primes_where { + ($n:literal >= $lim:literal) => { + $crate::primes_geq::< + $n, + { + (if $lim <= 1 { + $lim + } else { + let mut x0 = + ::core::primitive::u64::pow(2, ::core::primitive::u64::ilog2($lim) / 2 + 1); + let mut x1 = (x0 + $lim / x0) / 2; + while x1 < x0 { + x0 = x1; + x1 = (x0 + $lim / x0) / 2; + } + x0 + }) as ::core::primitive::usize + + 1 + }, + >($lim) + }; + ($n:literal < $lim:literal) => { + $crate::primes_lt::< + $n, + { + (if $lim <= 1 { + $lim + } else { + let mut x0 = + ::core::primitive::u64::pow(2, ::core::primitive::u64::ilog2($lim) / 2 + 1); + let mut x1 = (x0 + $lim / x0) / 2; + while x1 < x0 { + x0 = x1; + x1 = (x0 + $lim / x0) / 2; + } + x0 + }) as ::core::primitive::usize + + 1 + }, + >($lim) + }; +} + /// Returns the `N` smallest primes greater than or equal to `lower_limit`. /// Fails to compile if `N` is 0. If `lower_limit` is less than 2 this functions assumes that it is 2, /// since there are no primes smaller than 2. diff --git a/src/lib.rs b/src/lib.rs index a6c7172..64430a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,15 +55,24 @@ //! but doesn't need to store the entire sieve in the binary. //! ``` //! # use const_primes::Error; -//! use const_primes::{primes_geq, isqrt, Result}; -//! const NUM_PRIMES: usize = 3; -//! const LIMIT: u64 = 5_000_000_031; -//! const SIEVE_SIZE: usize = isqrt(LIMIT) as usize + 1; -//! const P_GEQ: Result = primes_geq::(LIMIT); +//! use const_primes::{primes_geq, Result}; +//! // ceil(isqrt(5_000_000_031)) = 70_711 +//! const P_GEQ: Result<3> = primes_geq::<3, 70_711>(5_000_000_031); //! //! assert_eq!(P_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); //! # Ok::<(), Error>(()) //! ``` +//! If you do not wish to compute the required sieve size yourself, +//! you can use the provided macro [`primes_where!`]: +//! ``` +//! # use const_primes::{primes_where, Result, Error}; +//! const PRIMES_OVER_100: Result<3> = primes_where!(3 >= 100); +//! const PRIMES_UNDER_100: Result<3> = primes_where!(3 < 100); +//! +//! assert_eq!(PRIMES_OVER_100?, [101, 103, 107]); +//! assert_eq!(PRIMES_UNDER_100?, [83, 89, 97]); +//! # Ok::<(), Error>(()) +//! ``` //! ``` //! # use const_primes::sieve_lt; //! const N: usize = 70711; @@ -118,7 +127,7 @@ mod wrapper; pub use array_section::ArraySection; pub use generation::{primes, primes_geq, primes_lt, Error, PrimesArray, Result}; -pub use imath::isqrt; +use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; pub use sieving::{sieve, sieve_geq, sieve_lt}; From a14ca683cf848ceda56fff04e2b429632920e8ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sat, 4 May 2024 17:37:51 +0200 Subject: [PATCH 163/212] Add primes() to const_primes!() --- src/generation.rs | 84 +++++++++++++++++++++++++++-------------------- src/lib.rs | 8 ++--- 2 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index effc07c..0d5aba9 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -229,57 +229,71 @@ pub const fn primes_lt(mut upper_limit: u64) - Ok(ArraySection::new(primes, 0..N)) } -/// Call [`primes_geq`] and [`primes_lt`], but automatically compute the memory requirement. +/// Same as the private const fn isqrt in the crate. +#[doc(hidden)] +#[macro_export] +macro_rules! ඞ_const_primes_isqrt { + ($n:ident) => { + if $n <= 1 { + $n + } else { + let mut x0 = ::core::primitive::u64::pow(2, ::core::primitive::u64::ilog2($n) / 2 + 1); + let mut x1 = (x0 + $n / x0) / 2; + while x1 < x0 { + x0 = x1; + x1 = (x0 + $n / x0) / 2; + } + x0 + } + }; +} + +/// Call [`primes`], [`primes_geq`] and [`primes_lt`], and automatically compute the memory requirement. /// /// # Example /// /// ``` -/// # use const_primes::{Result, primes_where, Error}; -/// const PRIMES_GEQ: Result<3> = primes_where!(3 >= 5_000_000_031_u64); -/// const PRIMES_LT: Result<3> = primes_where!(3 < 5_000_000_031_u64); +/// # use const_primes::{Result, const_primes, Error}; +/// const PRIMES: [u32; 3] = const_primes!(); +/// const LIMIT: u64 = 5_000_000_031; +/// const PRIMES_GEQ: Result<3> = const_primes!(3; >= LIMIT); +/// const PRIMES_LT: Result<3> = const_primes!(3; < LIMIT); +/// let primes = const_primes!(3); +/// +/// assert_eq!(primes, PRIMES); +/// assert_eq!(PRIMES, [2, 3, 5]); /// assert_eq!(PRIMES_GEQ?, [5000000039, 5000000059, 5000000063]); /// assert_eq!(PRIMES_LT?, [4999999903, 4999999937, 5000000029]); /// # Ok::<(), Error>(()) /// ``` #[macro_export] -macro_rules! primes_where { - ($n:literal >= $lim:literal) => { - $crate::primes_geq::< +macro_rules! const_primes { + () => { + $crate::primes() + }; + ($n:expr) => { + $crate::primes::< + { + let mem = { $n }; + mem + }, + >() + }; + ($n:expr; < $lim:expr) => { + $crate::primes_lt::< $n, { - (if $lim <= 1 { - $lim - } else { - let mut x0 = - ::core::primitive::u64::pow(2, ::core::primitive::u64::ilog2($lim) / 2 + 1); - let mut x1 = (x0 + $lim / x0) / 2; - while x1 < x0 { - x0 = x1; - x1 = (x0 + $lim / x0) / 2; - } - x0 - }) as ::core::primitive::usize - + 1 + let mem = { $lim }; + $crate::ඞ_const_primes_isqrt!(mem) as ::core::primitive::usize + 1 }, >($lim) }; - ($n:literal < $lim:literal) => { - $crate::primes_lt::< + ($n:expr; >= $lim:expr) => { + $crate::primes_geq::< $n, { - (if $lim <= 1 { - $lim - } else { - let mut x0 = - ::core::primitive::u64::pow(2, ::core::primitive::u64::ilog2($lim) / 2 + 1); - let mut x1 = (x0 + $lim / x0) / 2; - while x1 < x0 { - x0 = x1; - x1 = (x0 + $lim / x0) / 2; - } - x0 - }) as ::core::primitive::usize - + 1 + let mem = { $lim }; + $crate::ඞ_const_primes_isqrt!(mem) as ::core::primitive::usize + 1 }, >($lim) }; diff --git a/src/lib.rs b/src/lib.rs index 64430a3..e6ab20b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,11 +63,11 @@ //! # Ok::<(), Error>(()) //! ``` //! If you do not wish to compute the required sieve size yourself, -//! you can use the provided macro [`primes_where!`]: +//! you can use the provided macro [`const_primes!`]: //! ``` -//! # use const_primes::{primes_where, Result, Error}; -//! const PRIMES_OVER_100: Result<3> = primes_where!(3 >= 100); -//! const PRIMES_UNDER_100: Result<3> = primes_where!(3 < 100); +//! # use const_primes::{const_primes, Result, Error}; +//! const PRIMES_OVER_100: Result<3> = const_primes!(3; >= 100); +//! const PRIMES_UNDER_100: Result<3> = const_primes!(3; < 100); //! //! assert_eq!(PRIMES_OVER_100?, [101, 103, 107]); //! assert_eq!(PRIMES_UNDER_100?, [83, 89, 97]); From ed92250e418498c62170a68f4ea19896605175bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:15:12 +0200 Subject: [PATCH 164/212] Only compute MEM^2 once --- src/generation.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 0d5aba9..9369fc5 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -172,25 +172,22 @@ pub const fn primes_lt(mut upper_limit: u64) - const { assert!(N > 0, "`N` must be at least 1"); assert!(MEM >= N, "`MEM` must be at least as large as `N`"); + } + let mem64 = const { let mem64 = MEM as u64; assert!( mem64.checked_mul(mem64).is_some(), "`MEM`^2 must fit in a u64" ); - } + mem64 + }; if upper_limit <= 2 { return Err(Error::TooSmallLimit(upper_limit)); } - let mem64 = MEM as u64; - match (mem64).checked_mul(mem64) { - Some(prod) => { - if upper_limit > prod { - return Err(Error::TooLargeLimit(upper_limit, MEM)); - } - } - None => return Err(Error::MEMSquaredOverflow(MEM)), + if upper_limit > mem64 * mem64 { + return Err(Error::TooLargeLimit(upper_limit, MEM)); } let mut primes: [u64; N] = [0; N]; @@ -346,19 +343,16 @@ pub const fn primes_geq(lower_limit: u64) -> R const { assert!(N > 0, "`N` must be at least 1"); assert!(MEM >= N, "`MEM` must be at least as large as `N`"); - let mem64 = MEM as u64; - assert!( - mem64.checked_mul(mem64).is_some(), - "`MEM`^2 must fit in a u64" - ); } + let (mem64, mem_sqr) = const { + let mem64 = MEM as u64; + let Some(mem_sqr) = mem64.checked_mul(mem64) else {panic!("`MEM`^2 must fit in a `u64`")}; + (mem64, mem_sqr) + }; // There are no primes smaller than 2, so we will always start looking at 2. let new_lower_limit = if lower_limit >= 2 { lower_limit } else { 2 }; - let mem64 = MEM as u64; - let mem_sqr = mem64 * mem64; - if new_lower_limit >= mem_sqr { return Err(Error::TooLargeLimit(lower_limit, MEM)); } From 6d71375a6d48cffc457a8be58fc710335464f9c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:20:39 +0200 Subject: [PATCH 165/212] rustfmt let else --- src/generation.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 9369fc5..3275f7f 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -346,7 +346,9 @@ pub const fn primes_geq(lower_limit: u64) -> R } let (mem64, mem_sqr) = const { let mem64 = MEM as u64; - let Some(mem_sqr) = mem64.checked_mul(mem64) else {panic!("`MEM`^2 must fit in a `u64`")}; + let Some(mem_sqr) = mem64.checked_mul(mem64) else { + panic!("`MEM`^2 must fit in a `u64`") + }; (mem64, mem_sqr) }; From 0587e03ae16bae81d6452287402ec2cf1e6ba4f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:21:00 +0200 Subject: [PATCH 166/212] call index in index --- src/wrapper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wrapper.rs b/src/wrapper.rs index b99fb5a..a73cc6a 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -319,7 +319,7 @@ where type Output = I::Output; #[inline] fn index(&self, index: I) -> &Self::Output { - &self.primes[index] + &self.primes.index(index) } } From f96035c4f60ca3f01c1c03430f99d3f3e9933504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:21:26 +0200 Subject: [PATCH 167/212] Impl all indexing operations with one generic impl instead of many expansions of a macro --- src/array_section.rs | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 724c4b3..2616fc1 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -2,7 +2,7 @@ use core::{ cmp::Ordering, hash::{Hash, Hasher}, iter::FusedIterator, - ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, + ops::Range, }; /// An array where only a section of the data may be viewed, @@ -217,34 +217,17 @@ impl AsRef<[T]> for ArraySection { } } -// region: Index impls - -impl Index for ArraySection { - type Output = T; +impl core::ops::Index for ArraySection +where + I: core::slice::SliceIndex<[T]>, +{ + type Output = I::Output; #[inline] - fn index(&self, index: usize) -> &Self::Output { - self.as_slice().index(index) + fn index(&self, index: I) -> &Self::Output { + &self.as_slice().index(index) } } -macro_rules! impl_index_range { - ($($t:ty),+) => { - $( - impl ::core::ops::Index<$t> for $crate::array_section::ArraySection { - type Output = [T]; - #[inline] - fn index(&self, index: $t) -> &Self::Output { - ::core::ops::Index::index(self.as_slice(), index) - } - } - )+ - }; -} - -impl_index_range! {Range, RangeFrom, RangeFull, RangeTo, RangeInclusive, RangeToInclusive} - -// endregion: Index impls - // region: PartialEq impls /// Only compares the sections, and not the full arrays. From 8bce044d18d47528eb4c7fa3c49df56a7712aff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:22:41 +0200 Subject: [PATCH 168/212] use statement, and remove where --- src/array_section.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 2616fc1..3b0341d 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -2,7 +2,8 @@ use core::{ cmp::Ordering, hash::{Hash, Hasher}, iter::FusedIterator, - ops::Range, + ops::{Index, Range}, + slice::SliceIndex, }; /// An array where only a section of the data may be viewed, @@ -217,10 +218,7 @@ impl AsRef<[T]> for ArraySection { } } -impl core::ops::Index for ArraySection -where - I: core::slice::SliceIndex<[T]>, -{ +impl> Index for ArraySection { type Output = I::Output; #[inline] fn index(&self, index: I) -> &Self::Output { From 4b3f21b98861b7bc9b1f7788b44e8e27e64f95fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:26:15 +0200 Subject: [PATCH 169/212] inline more trivial things --- src/array_section.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 3b0341d..0a841a1 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -202,6 +202,7 @@ impl TryFrom> for [T; N] { // endregion: TryFrom impls impl From<[T; N]> for ArraySection { + #[inline] fn from(value: [T; N]) -> Self { Self { start: 0, @@ -228,7 +229,6 @@ impl> Index for ArraySection { // region: PartialEq impls -/// Only compares the sections, and not the full arrays. impl PartialEq> for ArraySection where [U]: PartialEq<[T]>, @@ -243,8 +243,7 @@ impl PartialEq<[U]> for ArraySection where U: PartialEq, { - /// This method tests for `self` and `other` values to be equal, and is used by `==`. - /// Only compares the *visible* part of `self` against `other`. + #[inline] fn eq(&self, other: &[U]) -> bool { other == self.as_slice() } @@ -254,8 +253,7 @@ impl PartialEq> for [U] where U: PartialEq, { - /// This method tests for `self` and `other` values to be equal, and is used by `==`. - /// Only compares the *visible* part of `other` against `self`. + #[inline] fn eq(&self, other: &ArraySection) -> bool { self == other.as_slice() } @@ -265,6 +263,7 @@ impl PartialEq<[T; N]> for ArraySection, { + #[inline] fn eq(&self, other: &[T; N]) -> bool { self.as_slice().eq(other.as_slice()) } @@ -274,6 +273,7 @@ impl PartialEq> for [T; where [T]: PartialEq<[U]>, { + #[inline] fn eq(&self, other: &ArraySection) -> bool { self.as_slice().eq(other.as_slice()) } From 4d99fc01e92970839cc7b73362d05cff85b47072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:27:48 +0200 Subject: [PATCH 170/212] Clearer variable name in example --- src/array_section.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 0a841a1..ac95b63 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -13,9 +13,9 @@ use core::{ /// ``` /// # use const_primes::array_section::ArraySection; /// // v v -/// const OT: ArraySection = ArraySection::new([0, 1, 2, 0], 1..3); -/// assert_eq![OT[0], 1]; -/// assert_eq![OT[1], 2]; +/// const AS: ArraySection = ArraySection::new([0, 1, 2, 0], 1..3); +/// assert_eq![AS[0], 1]; +/// assert_eq![AS[1], 2]; /// ``` /// /// The other data is not considered in comparisons, ordering or hashing: From 250fe929af885fb70e42d65250fa1675b29e33fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:45:59 +0200 Subject: [PATCH 171/212] impl From for ArraySection --- src/array_section.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/array_section.rs b/src/array_section.rs index ac95b63..1198b7a 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -172,9 +172,17 @@ impl ArraySection { // region: TryFrom impls +/// Returned by `TryFrom> for [T; N]` if the [`ArraySection`] was not fully valid. +/// Contains the original [`ArraySection`], which can be retrieved via the [`array_section`](TryFromArraySectionError::array_section) function. #[derive(Debug, Clone, Copy)] pub struct TryFromArraySectionError(ArraySection); +impl TryFromArraySectionError { + fn array_section(self) -> ArraySection { + self.0 + } +} + impl core::fmt::Display for TryFromArraySectionError { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -185,6 +193,12 @@ impl core::fmt::Display for TryFromArraySectionError { #[cfg(feature = "std")] impl std::error::Error for TryFromArraySectionError {} +impl From> for ArraySection { + fn from(value: TryFromArraySectionError) -> Self { + value.0 + } +} + /// Converts the `ArraySection` into an array if the section is actually the entire array. impl TryFrom> for [T; N] { type Error = TryFromArraySectionError; From 8e9c2ceea61138f542b573b019cb604a116d3680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:46:34 +0200 Subject: [PATCH 172/212] it must be pub --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index 1198b7a..900bdea 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -178,7 +178,7 @@ impl ArraySection { pub struct TryFromArraySectionError(ArraySection); impl TryFromArraySectionError { - fn array_section(self) -> ArraySection { + pub fn array_section(self) -> ArraySection { self.0 } } From 345f637e167b29a2243c5efb580e15daee82d321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:47:04 +0200 Subject: [PATCH 173/212] Add docstring to array_section --- src/array_section.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/array_section.rs b/src/array_section.rs index 900bdea..e68e02c 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -178,6 +178,7 @@ impl ArraySection { pub struct TryFromArraySectionError(ArraySection); impl TryFromArraySectionError { + /// Returns the original [`ArraySection`]. pub fn array_section(self) -> ArraySection { self.0 } From 4ef0691543c655c151a41720535dd3de21dd5574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:48:16 +0200 Subject: [PATCH 174/212] clarity in docstring --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index e68e02c..b199606 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -172,7 +172,7 @@ impl ArraySection { // region: TryFrom impls -/// Returned by `TryFrom> for [T; N]` if the [`ArraySection`] was not fully valid. +/// Returned by `TryFrom> for [T; N]` if the [`ArraySection`] was not the full array. /// Contains the original [`ArraySection`], which can be retrieved via the [`array_section`](TryFromArraySectionError::array_section) function. #[derive(Debug, Clone, Copy)] pub struct TryFromArraySectionError(ArraySection); From 97ea44498487b9c9977c2a9b1406ef99680d3cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:48:55 +0200 Subject: [PATCH 175/212] Remove double doc-links --- src/array_section.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index b199606..0effc41 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -173,7 +173,7 @@ impl ArraySection { // region: TryFrom impls /// Returned by `TryFrom> for [T; N]` if the [`ArraySection`] was not the full array. -/// Contains the original [`ArraySection`], which can be retrieved via the [`array_section`](TryFromArraySectionError::array_section) function. +/// Contains the original `ArraySection`, which can be retrieved via the [`array_section`](TryFromArraySectionError::array_section) function. #[derive(Debug, Clone, Copy)] pub struct TryFromArraySectionError(ArraySection); From 0064cdba6da8ab04f2b115422949fc92c1c14ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:53:05 +0200 Subject: [PATCH 176/212] impl into_full_array_const for ArraySection --- src/array_section.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/array_section.rs b/src/array_section.rs index 0effc41..7afa4b7 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -124,6 +124,9 @@ impl ArraySection { } /// Converts `self` into the full underlying array. + /// + /// If you wish to use this in const context the destructor of `T` must be trivial, + /// use [`into_full_array_const`](ArraySection::into_full_array_const) #[inline] pub fn into_full_array(self) -> [T; N] { self.array @@ -170,6 +173,13 @@ impl ArraySection { } } +impl ArraySection { + /// Converts `self` into the full underlying array. + pub const fn into_full_array_const(self) -> [T; N] { + self.array + } +} + // region: TryFrom impls /// Returned by `TryFrom> for [T; N]` if the [`ArraySection`] was not the full array. From 6f6aad5e89fe6e64bd98d432c9c095db4e0151f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:57:24 +0200 Subject: [PATCH 177/212] rename ArraySection::new range argument --- src/array_section.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/array_section.rs b/src/array_section.rs index 7afa4b7..a7c3cd7 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -72,13 +72,13 @@ impl ArraySection { /// /// Panics if the range of indices is out of bounds of the array. #[inline] - pub const fn new(array: [T; N], sub_range: Range) -> Self { + pub const fn new(array: [T; N], section: Range) -> Self { assert!( - sub_range.start < N && sub_range.end <= N, + section.start < N && section.end <= N, "the sub-range must be in bounds" ); - if sub_range.start > sub_range.end { + if section.start > section.end { Self { start: 0, end: 0, @@ -86,8 +86,8 @@ impl ArraySection { } } else { Self { - start: sub_range.start, - end: sub_range.end, + start: section.start, + end: section.end, array, } } From ad524303ec780eb86d502c550bc9385b20ca5a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 08:59:57 +0200 Subject: [PATCH 178/212] Simplify doctest --- src/generation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 3275f7f..82e4a24 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -326,7 +326,7 @@ macro_rules! const_primes { /// ``` /// # use const_primes::{primes_geq, Result, Error}; /// const PRIMES: Result<3> = primes_geq::<3, 3>(5); -/// assert_eq!(PRIMES?.as_slice(), &[5, 7]); +/// assert_eq!(PRIMES?, [5, 7]); /// # Ok::<(), Error>(()) /// ``` /// From 0ee8c807f50c752623d9d28035386528fdc7206b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 09:04:47 +0200 Subject: [PATCH 179/212] Clarify what the result type is in doctests --- src/generation.rs | 36 ++++++++++++++++++------------------ src/lib.rs | 10 +++++----- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 82e4a24..23c9baa 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -130,18 +130,18 @@ pub const fn primes() -> [Underlying; N] { /// /// Basic usage: /// ``` -/// # use const_primes::generation::{Result, primes_lt, Error}; +/// # use const_primes::generation::{primes_lt, Error}; /// // Sieving up to 100 means the sieve needs to be of size sqrt(100) = 10. /// // However, we only save the 4 largest primes in the constant. -/// const PRIMES: Result<4> = primes_lt::<4, 10>(100); +/// const PRIMES: const_primes::Result<4> = primes_lt::<4, 10>(100); /// assert_eq!(PRIMES?, [79, 83, 89, 97]); /// # Ok::<(), Error>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::generation::{Result, primes_lt, Error}; +/// # use const_primes::generation::{primes_lt, Error}; /// # #[allow(long_running_const_eval)] -/// const BIG_PRIMES: Result<3> = primes_lt::<3, 70_711>(5_000_000_030); +/// const BIG_PRIMES: const_primes::Result<3> = primes_lt::<3, 70_711>(5_000_000_030); /// /// assert_eq!(BIG_PRIMES?, [4_999_999_903, 4_999_999_937, 5_000_000_029]); /// # Ok::<(), Error>(()) @@ -152,8 +152,8 @@ pub const fn primes() -> [Underlying; N] { /// Due to limitations on const evaluation this will still /// take up the full `N` numbers worth of memory. /// ``` -/// # use const_primes::generation::{Result, primes_lt, Error}; -/// const PRIMES: Result<9> = primes_lt::<9, 9>(10); +/// # use const_primes::generation::{primes_lt, Error}; +/// const PRIMES: const_primes::Result<9> = primes_lt::<9, 9>(10); /// assert_eq!(PRIMES?, [2, 3, 5, 7]); /// # Ok::<(), Error>(()) /// ``` @@ -161,9 +161,9 @@ pub const fn primes() -> [Underlying; N] { /// /// Returns an error if `upper_limit` is larger than `MEM`^2 or if `upper_limit` is smaller than or equal to 2. /// ``` -/// # use const_primes::generation::{primes_lt, Result}; -/// const TOO_LARGE_LIMIT: Result<3> = primes_lt::<3, 5>(26); -/// const TOO_SMALL_LIMIT: Result<1> = primes_lt::<1, 1>(1); +/// # use const_primes::generation::primes_lt; +/// const TOO_LARGE_LIMIT: const_primes::Result<3> = primes_lt::<3, 5>(26); +/// const TOO_SMALL_LIMIT: const_primes::Result<1> = primes_lt::<1, 1>(1); /// assert!(TOO_LARGE_LIMIT.is_err()); /// assert!(TOO_SMALL_LIMIT.is_err()); /// ``` @@ -250,11 +250,11 @@ macro_rules! ඞ_const_primes_isqrt { /// # Example /// /// ``` -/// # use const_primes::{Result, const_primes, Error}; +/// # use const_primes::{const_primes, Error}; /// const PRIMES: [u32; 3] = const_primes!(); /// const LIMIT: u64 = 5_000_000_031; -/// const PRIMES_GEQ: Result<3> = const_primes!(3; >= LIMIT); -/// const PRIMES_LT: Result<3> = const_primes!(3; < LIMIT); +/// const PRIMES_GEQ: const_primes::Result<3> = const_primes!(3; >= LIMIT); +/// const PRIMES_LT: const_primes::Result<3> = const_primes!(3; < LIMIT); /// let primes = const_primes!(3); /// /// assert_eq!(primes, PRIMES); @@ -309,23 +309,23 @@ macro_rules! const_primes { /// /// Basic usage: /// ``` -/// # use const_primes::{primes_geq, Result, Error}; -/// const PRIMES: Result<5> = primes_geq::<5, 5>(10); +/// # use const_primes::{primes_geq, Error}; +/// const PRIMES: const_primes::Result<5> = primes_geq::<5, 5>(10); /// assert_eq!(PRIMES?, [11, 13, 17, 19, 23]); /// # Ok::<(), Error>(()) /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::{primes_geq, Result, Error}; +/// # use const_primes::{primes_geq, Error}; /// # #[allow(long_running_const_eval)] -/// const P: Result<3> = primes_geq::<3, 71_000>(5_000_000_030); +/// const P: const_primes::Result<3> = primes_geq::<3, 71_000>(5_000_000_030); /// assert_eq!(P?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); /// # Ok::<(), Error>(()) /// ``` /// Only primes smaller than `MEM^2` will be generated: /// ``` -/// # use const_primes::{primes_geq, Result, Error}; -/// const PRIMES: Result<3> = primes_geq::<3, 3>(5); +/// # use const_primes::{primes_geq, Error}; +/// const PRIMES: const_primes::Result<3> = primes_geq::<3, 3>(5); /// assert_eq!(PRIMES?, [5, 7]); /// # Ok::<(), Error>(()) /// ``` diff --git a/src/lib.rs b/src/lib.rs index e6ab20b..a793152 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,9 +55,9 @@ //! but doesn't need to store the entire sieve in the binary. //! ``` //! # use const_primes::Error; -//! use const_primes::{primes_geq, Result}; +//! use const_primes::primes_geq; //! // ceil(isqrt(5_000_000_031)) = 70_711 -//! const P_GEQ: Result<3> = primes_geq::<3, 70_711>(5_000_000_031); +//! const P_GEQ: const_primes::Result<3> = primes_geq::<3, 70_711>(5_000_000_031); //! //! assert_eq!(P_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); //! # Ok::<(), Error>(()) @@ -65,9 +65,9 @@ //! If you do not wish to compute the required sieve size yourself, //! you can use the provided macro [`const_primes!`]: //! ``` -//! # use const_primes::{const_primes, Result, Error}; -//! const PRIMES_OVER_100: Result<3> = const_primes!(3; >= 100); -//! const PRIMES_UNDER_100: Result<3> = const_primes!(3; < 100); +//! # use const_primes::{const_primes, Error}; +//! const PRIMES_OVER_100: const_primes::Result<3> = const_primes!(3; >= 100); +//! const PRIMES_UNDER_100: const_primes::Result<3> = const_primes!(3; < 100); //! //! assert_eq!(PRIMES_OVER_100?, [101, 103, 107]); //! assert_eq!(PRIMES_UNDER_100?, [83, 89, 97]); From bfcd802acc11cccebdc9ce9b1743da11329a4a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 09:05:43 +0200 Subject: [PATCH 180/212] remove i --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index a793152..d136def 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ //! ``` //! # use const_primes::Error; //! use const_primes::primes_geq; -//! // ceil(isqrt(5_000_000_031)) = 70_711 +//! // ceil(sqrt(5_000_000_031)) = 70_711 //! const P_GEQ: const_primes::Result<3> = primes_geq::<3, 70_711>(5_000_000_031); //! //! assert_eq!(P_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); From 48ab2316b909229416ae3baf94dcbc97c06a8643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 09:06:35 +0200 Subject: [PATCH 181/212] 31 -> 63 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d136def..cb6fa08 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ //! ``` //! # use const_primes::Error; //! use const_primes::primes_geq; -//! // ceil(sqrt(5_000_000_031)) = 70_711 +//! // ceil(sqrt(5_000_000_063)) = 70_711 //! const P_GEQ: const_primes::Result<3> = primes_geq::<3, 70_711>(5_000_000_031); //! //! assert_eq!(P_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); From 1b340581dbbb00e56b3427a371708869a1b86112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 09:11:12 +0200 Subject: [PATCH 182/212] Simplify module structure --- src/generation.rs | 8 ++++---- src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 23c9baa..f792755 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -130,7 +130,7 @@ pub const fn primes() -> [Underlying; N] { /// /// Basic usage: /// ``` -/// # use const_primes::generation::{primes_lt, Error}; +/// # use const_primes::{primes_lt, Error}; /// // Sieving up to 100 means the sieve needs to be of size sqrt(100) = 10. /// // However, we only save the 4 largest primes in the constant. /// const PRIMES: const_primes::Result<4> = primes_lt::<4, 10>(100); @@ -139,7 +139,7 @@ pub const fn primes() -> [Underlying; N] { /// ``` /// Compute larger primes without starting from zero: /// ``` -/// # use const_primes::generation::{primes_lt, Error}; +/// # use const_primes::{primes_lt, Error}; /// # #[allow(long_running_const_eval)] /// const BIG_PRIMES: const_primes::Result<3> = primes_lt::<3, 70_711>(5_000_000_030); /// @@ -152,7 +152,7 @@ pub const fn primes() -> [Underlying; N] { /// Due to limitations on const evaluation this will still /// take up the full `N` numbers worth of memory. /// ``` -/// # use const_primes::generation::{primes_lt, Error}; +/// # use const_primes::{primes_lt, Error}; /// const PRIMES: const_primes::Result<9> = primes_lt::<9, 9>(10); /// assert_eq!(PRIMES?, [2, 3, 5, 7]); /// # Ok::<(), Error>(()) @@ -161,7 +161,7 @@ pub const fn primes() -> [Underlying; N] { /// /// Returns an error if `upper_limit` is larger than `MEM`^2 or if `upper_limit` is smaller than or equal to 2. /// ``` -/// # use const_primes::generation::primes_lt; +/// # use const_primes::primes_lt; /// const TOO_LARGE_LIMIT: const_primes::Result<3> = primes_lt::<3, 5>(26); /// const TOO_SMALL_LIMIT: const_primes::Result<1> = primes_lt::<1, 1>(1); /// assert!(TOO_LARGE_LIMIT.is_err()); diff --git a/src/lib.rs b/src/lib.rs index cb6fa08..46322aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,7 +118,7 @@ type Underlying = u32; pub mod array_section; -pub mod generation; +mod generation; mod imath; mod miller_rabin; mod other_prime; From deebe76788e69afffe81b439d76053ab5e4341ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 09:16:56 +0200 Subject: [PATCH 183/212] Remove PrimesArray --- src/generation.rs | 303 +--------------------------------------------- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 303 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index f792755..3478d1e 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -1,7 +1,4 @@ -use core::{ - fmt, - ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}, -}; +use core::fmt; use crate::{array_section::ArraySection, sieve, sieving::sieve_segment, Underlying}; @@ -398,304 +395,6 @@ pub const fn primes_geq(lower_limit: u64) -> R Ok(ArraySection::new(primes, 0..N)) } -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -/// An array where potentially only a section of it contains valid prime numbers. -pub enum PrimesArray { - /// The entire allocated array contains prime numbers - Full([u64; N]), - /// Only a section of the allocated array contains prime numbers. - Partial(ArraySection), -} - -impl PrimesArray { - /// Returns a slice of all the generated prime numbers. - #[inline] - pub const fn as_slice(&self) -> &[u64] { - match self { - Self::Full(ref arr) => arr.as_slice(), - Self::Partial(ref arr_sec) => arr_sec.as_slice(), - } - } - - /// Returns the underlying array if it is full of valid primes. - #[inline] - pub const fn full(self) -> Option<[u64; N]> { - match self { - Self::Full(arr) => Some(arr), - Self::Partial(_) => None, - } - } - - /// Returns a reference to the underlying array if it is full of valid primes. - #[inline] - pub const fn as_full(&self) -> Option<&[u64; N]> { - match self { - Self::Full(ref arr) => Some(arr), - Self::Partial(_) => None, - } - } - - /// Returns the primes as a (maybe fully populated) [`ArraySection`]. - #[inline] - pub const fn into_array_section(self) -> ArraySection { - match self { - Self::Partial(arr_sec) => arr_sec, - Self::Full(arr) => { - let l = arr.len(); - ArraySection::new(arr, 0..l) - } - } - } - - /// Returns an [`ArraySection`] if not all of the elements - /// in the underlying array could be populated. - #[inline] - pub const fn partial(self) -> Option> { - match self { - Self::Partial(arr_sec) => Some(arr_sec), - Self::Full(_) => None, - } - } - - /// Returns a reference to the [`ArraySection`] if not all elements of the underlying array - /// could be populated. - #[inline] - pub const fn as_partial(&self) -> Option<&ArraySection> { - match self { - Self::Partial(ref arr_sec) => Some(arr_sec), - Self::Full(_) => None, - } - } - - /// Returns whether all the elements in the underlying array could be computed. - #[inline] - pub const fn is_full(&self) -> bool { - matches!(self, Self::Full(_)) - } - - /// Returns whether only a subset of the underlying array could be computed. - #[inline] - pub const fn is_partial(&self) -> bool { - matches!(self, Self::Partial(_)) - } - - /// Returns the length of the populated part of the array. - #[inline] - pub const fn len(&self) -> usize { - self.as_slice().len() - } - - /// Returns wether the populated part of the array is empty. - #[inline] - pub const fn is_empty(&self) -> bool { - self.len() == 0 - } - - pub fn iter(&self) -> PrimesArrayIter<'_> { - PrimesArrayIter::new(self.as_slice().iter()) - } -} - -impl PartialEq<[T; N]> for PrimesArray -where - u64: PartialEq, -{ - fn eq(&self, other: &[T; N]) -> bool { - self.as_slice().eq(other.as_slice()) - } -} - -impl PartialEq<[T]> for PrimesArray -where - u64: PartialEq, -{ - fn eq(&self, other: &[T]) -> bool { - self.as_slice().eq(other) - } -} - -impl> PartialEq> for [T] { - fn eq(&self, other: &PrimesArray) -> bool { - self.eq(other.as_slice()) - } -} - -impl> PartialEq> for [T; N] { - fn eq(&self, other: &PrimesArray) -> bool { - self.eq(other.as_slice()) - } -} - -impl From> for ArraySection { - #[inline] - fn from(value: PrimesArray) -> Self { - value.into_array_section() - } -} - -impl AsRef<[u64]> for PrimesArray { - #[inline] - fn as_ref(&self) -> &[u64] { - self.as_slice() - } -} - -macro_rules! impl_index_range { - ($($n:ty),*) => { - $( - impl Index<$n> for PrimesArray { - type Output = [u64]; - fn index(&self, index: $n) -> &Self::Output { - self.as_slice().index(index) - } - } - )* - }; -} - -impl_index_range! {Range, RangeTo, RangeFrom, RangeToInclusive, RangeFull, RangeInclusive} - -// region: Iterator impls - -impl IntoIterator for PrimesArray { - type IntoIter = PrimesArrayIntoIter; - type Item = u64; - fn into_iter(self) -> Self::IntoIter { - PrimesArrayIntoIter::new(self) - } -} - -impl<'a, const N: usize> IntoIterator for &'a PrimesArray { - type IntoIter = PrimesArrayIter<'a>; - type Item = &'a u64; - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -pub use primes_array_iter::PrimesArrayIter; -mod primes_array_iter { - use core::iter::FusedIterator; - - #[derive(Debug, Clone)] - pub struct PrimesArrayIter<'a>(core::slice::Iter<'a, u64>); - - impl<'a> PrimesArrayIter<'a> { - #[inline] - pub(crate) const fn new(iter: core::slice::Iter<'a, u64>) -> Self { - Self(iter) - } - } - - impl<'a> Iterator for PrimesArrayIter<'a> { - type Item = &'a u64; - - #[inline] - fn next(&mut self) -> Option { - self.0.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.0.nth(n) - } - - #[inline] - fn count(self) -> usize { - self.0.count() - } - - #[inline] - fn last(self) -> Option { - self.0.last() - } - } - - impl<'a> DoubleEndedIterator for PrimesArrayIter<'a> { - #[inline] - fn next_back(&mut self) -> Option { - self.0.next_back() - } - } - - impl<'a> ExactSizeIterator for PrimesArrayIter<'a> { - #[inline] - fn len(&self) -> usize { - self.0.len() - } - } - - impl<'a> FusedIterator for PrimesArrayIter<'a> {} -} - -pub use primes_array_into_iter::PrimesArrayIntoIter; -mod primes_array_into_iter { - use core::iter::FusedIterator; - - use crate::{array_section::ArraySectionIntoIter, PrimesArray}; - - pub struct PrimesArrayIntoIter(ArraySectionIntoIter); - - impl PrimesArrayIntoIter { - #[inline] - pub(crate) fn new(primes_array: PrimesArray) -> Self { - Self(primes_array.into_array_section().into_iter()) - } - } - - impl Iterator for PrimesArrayIntoIter { - type Item = u64; - - #[inline] - fn next(&mut self) -> Option { - self.0.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } - - #[inline] - fn last(self) -> Option { - self.0.last() - } - - #[inline] - fn count(self) -> usize { - self.0.count() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.0.nth(n) - } - } - - impl DoubleEndedIterator for PrimesArrayIntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.0.next_back() - } - } - - impl ExactSizeIterator for PrimesArrayIntoIter { - #[inline] - fn len(&self) -> usize { - self.0.len() - } - } - - impl FusedIterator for PrimesArrayIntoIter {} -} - -// endregion: Iterator impls - /// An enum describing whether the requested array could be filled completely, or only a partially. /// A partial array can be returned by [`primes_lt`] if the size of the requested /// array is larger than the actual number of primes less than the given `upper_limit`. diff --git a/src/lib.rs b/src/lib.rs index 46322aa..07a437c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,7 +126,7 @@ mod sieving; mod wrapper; pub use array_section::ArraySection; -pub use generation::{primes, primes_geq, primes_lt, Error, PrimesArray, Result}; +pub use generation::{primes, primes_geq, primes_lt, Error, Result}; use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; From cf139081063d2e9538acf926dcc5047337b556d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 09:21:22 +0200 Subject: [PATCH 184/212] more docstrings --- src/array_section.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/array_section.rs b/src/array_section.rs index a7c3cd7..42a2e43 100644 --- a/src/array_section.rs +++ b/src/array_section.rs @@ -1,3 +1,5 @@ +//! Contains the definition of [`ArraySection`] as well as related types. + use core::{ cmp::Ordering, hash::{Hash, Hasher}, @@ -182,7 +184,8 @@ impl ArraySection { // region: TryFrom impls -/// Returned by `TryFrom> for [T; N]` if the [`ArraySection`] was not the full array. +/// Returned when a `TryFrom` conversion of an [`ArraySection`] into an array fails. +/// /// Contains the original `ArraySection`, which can be retrieved via the [`array_section`](TryFromArraySectionError::array_section) function. #[derive(Debug, Clone, Copy)] pub struct TryFromArraySectionError(ArraySection); @@ -330,6 +333,7 @@ pub use array_section_iter::ArraySectionIter; mod array_section_iter { use super::FusedIterator; + /// Created by the [`iter`](super::ArraySection::iter) function on [`ArraySection`](super::ArraySection), see it for more information. #[derive(Debug, Clone)] pub struct ArraySectionIter<'a, T>(core::slice::Iter<'a, T>); From 43c8eae308ab34110ce29bd9b1056d0e3a24faa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 10:24:38 +0200 Subject: [PATCH 185/212] Split ArraySection into its own crate --- Cargo.toml | 1 + src/array_section.rs | 435 ------------------------------------------- src/generation.rs | 4 +- src/lib.rs | 2 - 4 files changed, 4 insertions(+), 438 deletions(-) delete mode 100644 src/array_section.rs diff --git a/Cargo.toml b/Cargo.toml index b6a3134..769af06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/JSorngard/const-primes" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +array-section = "0.1.0" [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } diff --git a/src/array_section.rs b/src/array_section.rs deleted file mode 100644 index 42a2e43..0000000 --- a/src/array_section.rs +++ /dev/null @@ -1,435 +0,0 @@ -//! Contains the definition of [`ArraySection`] as well as related types. - -use core::{ - cmp::Ordering, - hash::{Hash, Hasher}, - iter::FusedIterator, - ops::{Index, Range}, - slice::SliceIndex, -}; - -/// An array where only a section of the data may be viewed, -/// as the other data may e.g. not uphold some invariant. -/// -/// Indexing into the `ArraySection` indices only into the section: -/// ``` -/// # use const_primes::array_section::ArraySection; -/// // v v -/// const AS: ArraySection = ArraySection::new([0, 1, 2, 0], 1..3); -/// assert_eq![AS[0], 1]; -/// assert_eq![AS[1], 2]; -/// ``` -/// -/// The other data is not considered in comparisons, ordering or hashing: -/// ``` -/// # use const_primes::array_section::ArraySection; -/// // v v -/// const A1: [i32; 4] = [1, 3, 7, 1]; -/// const A2: [i32; 5] = [0, 3, 7, 100, -5]; -/// const AS1: ArraySection = ArraySection::new(A1, 1..3); -/// const AS2: ArraySection = ArraySection::new(A2, 1..3); -/// -/// // Even though the arrays are different -/// assert_ne!(A1.as_slice(), A2.as_slice()); -/// // The sections are the same -/// assert_eq!(AS1, AS2); -/// ``` -#[derive(Debug, Clone, Copy, Eq)] -pub struct ArraySection { - start: usize, - end: usize, - array: [T; N], -} - -/// Only hashes the data in the section, and not the full array. -impl Hash for ArraySection { - #[inline] - fn hash(&self, state: &mut H) { - self.as_slice().hash(state); - } -} - -/// Only checks the data in the sections, and not the full arrays. -impl PartialOrd> - for ArraySection -{ - #[inline] - fn partial_cmp(&self, other: &ArraySection) -> Option { - self.as_slice().partial_cmp(other.as_slice()) - } -} - -/// Only compares the data in the sections and not the full arrays. -impl Ord for ArraySection { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.as_slice().cmp(other.as_slice()) - } -} - -impl ArraySection { - /// Restrict an array so that only elements within the given range are visible. - /// - /// # Panics - /// - /// Panics if the range of indices is out of bounds of the array. - #[inline] - pub const fn new(array: [T; N], section: Range) -> Self { - assert!( - section.start < N && section.end <= N, - "the sub-range must be in bounds" - ); - - if section.start > section.end { - Self { - start: 0, - end: 0, - array, - } - } else { - Self { - start: section.start, - end: section.end, - array, - } - } - } - - /// Returns the first index of the full underlying array that is part of the section. - /// I.e. the section is the subrange `start ..`[`end`](ArraySection::end). - #[inline] - pub const fn start(&self) -> usize { - self.start - } - - /// Returns the first index of the full underlying array that is outside the section (to the right). - /// I.e. the section is the subrange [`start`](ArraySection::start)`.. end`. - #[inline] - pub const fn end(&self) -> usize { - self.end - } - - /// Returns a reference to the full underlying array if it is fully populated. - #[inline] - pub const fn try_as_full_array(&self) -> Option<&[T; N]> { - if self.section_is_full_array() { - Some(&self.array) - } else { - None - } - } - - /// Returns a reference to the full underlying array. - #[inline] - pub const fn as_full_array(&self) -> &[T; N] { - &self.array - } - - /// Converts `self` into the full underlying array. - /// - /// If you wish to use this in const context the destructor of `T` must be trivial, - /// use [`into_full_array_const`](ArraySection::into_full_array_const) - #[inline] - pub fn into_full_array(self) -> [T; N] { - self.array - } - - /// Returns the section of the array as a slice. - #[inline] - pub const fn as_slice(&self) -> &[T] { - self.array - // Split &[head, section, tail] into (&[head], &[section, tail]) - .split_at(self.start) - // and extract the second element of the tuple. - .1 - // Split &[section, tail] into (&[section], &[tail]) - .split_at(self.end - self.start) - // and extract the first element of the tuple. - .0 - } - - /// Returns the length of the array section. - #[inline] - pub const fn len(&self) -> usize { - self.as_slice().len() - } - - /// Returns whether the array section is empty. - #[inline] - pub const fn is_empty(&self) -> bool { - self.as_slice().is_empty() - } - - /// Returns whether the section is just the entire array. - /// If this is `true` it is completely fine to call [`as_full_array`](ArraySection::as_full_array) - /// or [`into_full_array`](ArraySection::into_full_array). - #[inline] - pub const fn section_is_full_array(&self) -> bool { - self.len() == N - } - - /// Returns an iterator over the array section. - #[inline] - pub fn iter(&self) -> ArraySectionIter<'_, T> { - ArraySectionIter::new(self.as_slice().iter()) - } -} - -impl ArraySection { - /// Converts `self` into the full underlying array. - pub const fn into_full_array_const(self) -> [T; N] { - self.array - } -} - -// region: TryFrom impls - -/// Returned when a `TryFrom` conversion of an [`ArraySection`] into an array fails. -/// -/// Contains the original `ArraySection`, which can be retrieved via the [`array_section`](TryFromArraySectionError::array_section) function. -#[derive(Debug, Clone, Copy)] -pub struct TryFromArraySectionError(ArraySection); - -impl TryFromArraySectionError { - /// Returns the original [`ArraySection`]. - pub fn array_section(self) -> ArraySection { - self.0 - } -} - -impl core::fmt::Display for TryFromArraySectionError { - #[inline] - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "the array was not fully populated") - } -} - -#[cfg(feature = "std")] -impl std::error::Error for TryFromArraySectionError {} - -impl From> for ArraySection { - fn from(value: TryFromArraySectionError) -> Self { - value.0 - } -} - -/// Converts the `ArraySection` into an array if the section is actually the entire array. -impl TryFrom> for [T; N] { - type Error = TryFromArraySectionError; - - #[inline] - fn try_from(value: ArraySection) -> Result { - if value.section_is_full_array() { - Ok(value.array) - } else { - Err(TryFromArraySectionError(value)) - } - } -} - -// endregion: TryFrom impls - -impl From<[T; N]> for ArraySection { - #[inline] - fn from(value: [T; N]) -> Self { - Self { - start: 0, - end: N, - array: value, - } - } -} - -impl AsRef<[T]> for ArraySection { - #[inline] - fn as_ref(&self) -> &[T] { - self.as_slice() - } -} - -impl> Index for ArraySection { - type Output = I::Output; - #[inline] - fn index(&self, index: I) -> &Self::Output { - &self.as_slice().index(index) - } -} - -// region: PartialEq impls - -impl PartialEq> for ArraySection -where - [U]: PartialEq<[T]>, -{ - #[inline] - fn eq(&self, other: &ArraySection) -> bool { - self.as_slice().eq(other.as_slice()) - } -} - -impl PartialEq<[U]> for ArraySection -where - U: PartialEq, -{ - #[inline] - fn eq(&self, other: &[U]) -> bool { - other == self.as_slice() - } -} - -impl PartialEq> for [U] -where - U: PartialEq, -{ - #[inline] - fn eq(&self, other: &ArraySection) -> bool { - self == other.as_slice() - } -} - -impl PartialEq<[T; N]> for ArraySection -where - [U]: PartialEq<[T]>, -{ - #[inline] - fn eq(&self, other: &[T; N]) -> bool { - self.as_slice().eq(other.as_slice()) - } -} - -impl PartialEq> for [T; N] -where - [T]: PartialEq<[U]>, -{ - #[inline] - fn eq(&self, other: &ArraySection) -> bool { - self.as_slice().eq(other.as_slice()) - } -} - -// endregion: PartialEq impls - -impl IntoIterator for ArraySection { - type IntoIter = ArraySectionIntoIter; - type Item = as Iterator>::Item; - #[inline] - fn into_iter(self) -> Self::IntoIter { - let start = self.start; - let len = self.len(); - ArraySectionIntoIter::new(self.array.into_iter().skip(start).take(len)) - } -} - -impl<'a, const N: usize, T> IntoIterator for &'a ArraySection { - type IntoIter = ArraySectionIter<'a, T>; - type Item = as Iterator>::Item; - #[inline] - fn into_iter(self) -> Self::IntoIter { - ArraySectionIter::new(self.as_slice().iter()) - } -} - -pub use array_section_iter::ArraySectionIter; -mod array_section_iter { - use super::FusedIterator; - - /// Created by the [`iter`](super::ArraySection::iter) function on [`ArraySection`](super::ArraySection), see it for more information. - #[derive(Debug, Clone)] - pub struct ArraySectionIter<'a, T>(core::slice::Iter<'a, T>); - - impl<'a, T> ArraySectionIter<'a, T> { - pub(crate) const fn new(iter: core::slice::Iter<'a, T>) -> Self { - Self(iter) - } - } - - impl<'a, T> Iterator for ArraySectionIter<'a, T> { - type Item = &'a T; - fn next(&mut self) -> Option { - self.0.next() - } - - fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() - } - - fn last(self) -> Option { - self.0.last() - } - - fn nth(&mut self, n: usize) -> Option { - self.0.nth(n) - } - - fn count(self) -> usize { - self.0.count() - } - } - impl<'a, T> DoubleEndedIterator for ArraySectionIter<'a, T> { - fn next_back(&mut self) -> Option { - self.0.next_back() - } - } - impl<'a, T> ExactSizeIterator for ArraySectionIter<'a, T> { - fn len(&self) -> usize { - self.0.len() - } - } - impl<'a, T> FusedIterator for ArraySectionIter<'a, T> {} -} - -pub use array_section_into_iter::ArraySectionIntoIter; -mod array_section_into_iter { - use super::FusedIterator; - - #[derive(Debug, Clone)] - /// Created by the [`into_iter`](super::ArraySection::into_iter) function on [`ArraySection`](super::ArraySection), see it for more information. - pub struct ArraySectionIntoIter( - core::iter::Take>>, - ); - - impl ArraySectionIntoIter { - pub(crate) const fn new( - iter: core::iter::Take>>, - ) -> Self { - Self(iter) - } - } - - impl Iterator for ArraySectionIntoIter { - type Item = T; - #[inline] - fn next(&mut self) -> Option { - self.0.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let l = self.0.len(); - (l, Some(l)) - } - - #[inline] - fn nth(&mut self, index: usize) -> Option { - self.0.nth(index) - } - - #[inline] - fn last(self) -> Option { - self.0.last() - } - - #[inline] - fn count(self) -> usize { - self.0.count() - } - } - impl FusedIterator for ArraySectionIntoIter {} - impl ExactSizeIterator for ArraySectionIntoIter {} - impl DoubleEndedIterator for ArraySectionIntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.0.next_back() - } - } -} diff --git a/src/generation.rs b/src/generation.rs index 3478d1e..0811af5 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -1,6 +1,8 @@ use core::fmt; -use crate::{array_section::ArraySection, sieve, sieving::sieve_segment, Underlying}; +use array_section::ArraySection; + +use crate::{sieve, sieving::sieve_segment, Underlying}; /// Type alias for the type returned by the segmented sieving and generation functions. pub type Result = core::result::Result, Error>; diff --git a/src/lib.rs b/src/lib.rs index 07a437c..2e1c7d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -117,7 +117,6 @@ // This is used since there is currently no way to be generic over types that can do arithmetic at compile time. type Underlying = u32; -pub mod array_section; mod generation; mod imath; mod miller_rabin; @@ -125,7 +124,6 @@ mod other_prime; mod sieving; mod wrapper; -pub use array_section::ArraySection; pub use generation::{primes, primes_geq, primes_lt, Error, Result}; use imath::isqrt; pub use miller_rabin::is_prime; From 61fd56a60865b196ac2b40ed11db95d86d33009d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Sun, 5 May 2024 15:52:02 +0200 Subject: [PATCH 186/212] Docstring formatting --- src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2e1c7d8..b7d0403 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,8 +51,7 @@ //! that can be used to work with ranges that don't start at zero. They take two generics: the number of primes //! to store in the binary, and the size of the sieve used during evaluation //! (which must be at least the ceiling of the square root of the largest encountered number). -//! This means that one can sieve up to large numbers, -//! but doesn't need to store the entire sieve in the binary. +//! This means that one can sieve to large numbers, but doesn't need to store the entire sieve in the binary. //! ``` //! # use const_primes::Error; //! use const_primes::primes_geq; From d64b58cb0bfaf0f60004fd72c5fb073d737dc3f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 10:30:13 +0200 Subject: [PATCH 187/212] Add note of runtime use to const_primes! --- src/generation.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index 0811af5..89cf453 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -254,7 +254,9 @@ macro_rules! ඞ_const_primes_isqrt { /// const LIMIT: u64 = 5_000_000_031; /// const PRIMES_GEQ: const_primes::Result<3> = const_primes!(3; >= LIMIT); /// const PRIMES_LT: const_primes::Result<3> = const_primes!(3; < LIMIT); +/// // Can also be used at runtime: /// let primes = const_primes!(3); +/// let hmm = const_primes!(3; >= 100); /// /// assert_eq!(primes, PRIMES); /// assert_eq!(PRIMES, [2, 3, 5]); From 96973ab41d94d27d1bc6a03472ea3f2c1fe14e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 10:32:06 +0200 Subject: [PATCH 188/212] improve docstring --- src/generation.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 89cf453..bb4232c 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -256,10 +256,11 @@ macro_rules! ඞ_const_primes_isqrt { /// const PRIMES_LT: const_primes::Result<3> = const_primes!(3; < LIMIT); /// // Can also be used at runtime: /// let primes = const_primes!(3); -/// let hmm = const_primes!(3; >= 100); +/// let primes_geq = const_primes!(3; >= LIMIT)?; /// /// assert_eq!(primes, PRIMES); /// assert_eq!(PRIMES, [2, 3, 5]); +/// assert_eq!(PRIMES_GEQ?, primes_geq); /// assert_eq!(PRIMES_GEQ?, [5000000039, 5000000059, 5000000063]); /// assert_eq!(PRIMES_LT?, [4999999903, 4999999937, 5000000029]); /// # Ok::<(), Error>(()) From c6a2eef6c66ec112c67787f10c674b7d8e9a4fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 13:22:16 +0200 Subject: [PATCH 189/212] Add categories 'Mathematics' and 'No standard library::no dynamic allocation' --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 769af06..ecc39ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ version = "0.4.8" edition = "2021" license = "MIT OR Apache-2.0" keywords = ["primes", "const"] +categories = ["mathematics", "no-std::no-alloc"] description = "Generate and work with prime numbers in const contexts" repository = "https://github.com/JSorngard/const-primes" From c3177f5c4a8b96682bd8563eeedd76ff9b2b714c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 13:29:05 +0200 Subject: [PATCH 190/212] Add alloc features --- Cargo.toml | 3 ++- src/lib.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ecc39ea..86f1471 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ criterion = { version = "0.5", features = ["html_reports"] } rand = "0.8" [features] -std = [] +std = ["array-section/std", "alloc"] +alloc = ["array-section/alloc"] [[bench]] name = "prime_benches" diff --git a/src/lib.rs b/src/lib.rs index b7d0403..aed009b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,6 +107,7 @@ //! # Features //! //! `std`: links the standard library and uses it to implement the [`Error`](std::error::Error) trait for the error types. +//! `alloc`: enables conversion of the type returned by [`primes_geq`] and [`primes_lt`] into [`Vec`]s and [`Box`]ed slices. #![forbid(unsafe_code)] #![cfg_attr(all(not(test), not(feature = "std")), no_std)] From eb94512aedc50718948fec4d8e24b22de16640bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 13:38:09 +0200 Subject: [PATCH 191/212] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e10b91a..a7ba367 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,13 @@ assert_eq!(PRIME_STATUS, [false, false, true, true, false, true, false, true, fa ## Arbitrary ranges The crate provides prime generation and sieving functions with suffixes, e.g. `primes_geq` and `sieve_lt` -that can be used to work with ranges that don't start at zero. +that can be used to work with ranges that don't start at zero. They take two generics: the number of elements +to store in the binary and the size of the sieve used during evaluation. The sieve size must be the cieling +of the square root of the largest encountered value: ```rust -const N: usize = 70722; -const PRIMES_GEQ: [u64; N] = primes_geq(5_000_000_031); -assert_eq!(PRIMES_GEQ[..3], [5_000_000_039, 5_000_000_059, 5_000_000_063]); +// ceil(sqrt(5_000_000_063)) = 70_711 +const PRIMES_GEQ: const_primes::Result<3> = primes_geq::<3, 70_711>(5_000_000_031); +assert_eq!(PRIMES_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); ``` ```rust const N: usize = 70711; @@ -62,9 +64,7 @@ const PRIME_STATUS_LT: [bool; N] = sieve_lt(5_000_000_031); // 5_000_000_028 5_000_000_029 5_000_000_030 assert_eq!(PRIME_STATUS_LT[N - 3..], [false, true, false]); ``` -Unfortunately the output array must be large enough to contain the prime sieve, which scales with -the square root of largest relavant number, which is why the examples use a size of over 70000 even though -they're only interested in three numbers. +The sieving functions have yet to be modified for two generics, and must save the entire sieve in the binary. ## Other functionality Use `is_prime` to test whether a given number is prime: ```rust From 4af54542c8b9eba575a9149e85b509189d8d24bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 13:39:51 +0200 Subject: [PATCH 192/212] Add features to README --- README.md | 5 +++++ src/lib.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a7ba367..5b38ac5 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,11 @@ assert_eq!(NOSUCH, None); ``` and more! +## Features + +`std`: derives the `Error` trait for the error types. +`alloc`: enables conversion of the type returned by [`primes_geq`] and [`primes_lt`] into [`Vec`]s and [`Box`]ed slices. + ## License Licensed under either of diff --git a/src/lib.rs b/src/lib.rs index aed009b..c0b8c94 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,7 +106,7 @@ //! //! # Features //! -//! `std`: links the standard library and uses it to implement the [`Error`](std::error::Error) trait for the error types. +//! `std`: derives the [`Error`](std::error::Error) trait for the error types. //! `alloc`: enables conversion of the type returned by [`primes_geq`] and [`primes_lt`] into [`Vec`]s and [`Box`]ed slices. #![forbid(unsafe_code)] From 0d7dabcd4cca20725b70782ac9fbb51ff4cf43e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 13:40:03 +0200 Subject: [PATCH 193/212] newline --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b38ac5..c52ce32 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ and more! ## Features -`std`: derives the `Error` trait for the error types. +`std`: derives the `Error` trait for the error types. `alloc`: enables conversion of the type returned by [`primes_geq`] and [`primes_lt`] into [`Vec`]s and [`Box`]ed slices. ## License From de8dd9efb54f4fc9a15231f1909c05eb3e20b942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 13:40:48 +0200 Subject: [PATCH 194/212] The readme can't doclink --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c52ce32..d7b73dd 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ and more! ## Features `std`: derives the `Error` trait for the error types. -`alloc`: enables conversion of the type returned by [`primes_geq`] and [`primes_lt`] into [`Vec`]s and [`Box`]ed slices. +`alloc`: enables conversion of the type returned by `primes_geq` and `primes_lt` into `Vec`s and `Box`ed slices. ## License From 7b55a59d883824ec5726bf052ceb9ac69057d0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 16:42:56 +0200 Subject: [PATCH 195/212] Ignore rust-toolchain.toml, I just want my vscode to stop yelling at me --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4fffb2f..482c89d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /Cargo.lock +rust-toolchain.toml \ No newline at end of file From d189cb6ad62ad48eb5e99951502b111e4dc19346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Mon, 6 May 2024 16:54:10 +0200 Subject: [PATCH 196/212] fix benchmark code --- benches/prime_benches.rs | 12 +++++------- src/sieving.rs | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/benches/prime_benches.rs b/benches/prime_benches.rs index 21c2cdf..3794bb1 100644 --- a/benches/prime_benches.rs +++ b/benches/prime_benches.rs @@ -1,7 +1,4 @@ -use const_primes::{ - generation::{primes_geq, primes_lt}, - is_prime, primes, sieve, sieve_geq, sieve_lt, -}; +use const_primes::{is_prime, primes, primes_geq, primes_lt, sieve, sieve_geq, sieve_lt}; use criterion::{criterion_group, criterion_main, BatchSize, Criterion, Throughput}; use rand::prelude::*; use std::hint::black_box; @@ -14,20 +11,21 @@ fn benchmarks(c: &mut Criterion) { b.iter(|| black_box(primes::())) }); prime_generation.bench_function(format!("{N} primes < 100000000"), |b| { - b.iter(|| black_box(primes_lt::(100000000))) + b.iter(|| black_box(primes_lt::(100000000))) }); prime_generation.bench_function(format!("{N} primes >= 99990000"), |b| { - b.iter(|| black_box(primes_geq::(99990000))) + b.iter(|| black_box(primes_geq::(99990000))) }); } { const N: u64 = 10_000; + let mut rng = StdRng::seed_from_u64(1234567890); let mut primality_testing = c.benchmark_group("primality testing"); primality_testing.throughput(Throughput::Elements(N)); primality_testing.bench_function(format!("is_prime on {N} random numbers"), |b| { b.iter_batched( - || (0..N).map(|_| random()).collect::>(), + || (0..N).map(|_| rng.gen()).collect::>(), |data| { for number in data.iter() { black_box(is_prime(*number)); diff --git a/src/sieving.rs b/src/sieving.rs index 9668954..3476c65 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -209,7 +209,7 @@ pub const fn sieve_geq(lower_limit: u64) -> [bool; N] { }; if let Some(n_sqr) = n64.checked_mul(n64) { assert!( - upper_limit < n_sqr, + upper_limit <= n_sqr, "`lower_limit + N` must be less than or equal to `N^2`" ); } else { From 472ee923336366c8cf2211dd1aa8a9af3380907f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 12:01:01 +0200 Subject: [PATCH 197/212] Simplify API --- Cargo.toml | 9 +-- src/generation.rs | 146 ++++++++++++++++++++++------------------------ src/lib.rs | 21 +++---- src/sieving.rs | 4 +- 4 files changed, 83 insertions(+), 97 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 86f1471..d8f4fbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,18 +9,13 @@ categories = ["mathematics", "no-std::no-alloc"] description = "Generate and work with prime numbers in const contexts" repository = "https://github.com/JSorngard/const-primes" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -array-section = "0.1.0" - [dev-dependencies] criterion = { version = "0.5", features = ["html_reports"] } rand = "0.8" [features] -std = ["array-section/std", "alloc"] -alloc = ["array-section/alloc"] +std = [ "alloc"] +alloc = [] [[bench]] name = "prime_benches" diff --git a/src/generation.rs b/src/generation.rs index bb4232c..18e73e0 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -1,12 +1,7 @@ use core::fmt; -use array_section::ArraySection; - use crate::{sieve, sieving::sieve_segment, Underlying}; -/// Type alias for the type returned by the segmented sieving and generation functions. -pub type Result = core::result::Result, Error>; - /// Returns the `N` first prime numbers. /// Fails to compile if `N` is 0. /// @@ -132,61 +127,60 @@ pub const fn primes() -> [Underlying; N] { /// # use const_primes::{primes_lt, Error}; /// // Sieving up to 100 means the sieve needs to be of size sqrt(100) = 10. /// // However, we only save the 4 largest primes in the constant. -/// const PRIMES: const_primes::Result<4> = primes_lt::<4, 10>(100); -/// assert_eq!(PRIMES?, [79, 83, 89, 97]); -/// # Ok::<(), Error>(()) +/// const PRIMES: [u64;4] = match primes_lt::<4, 10>(100) {Ok(ps) => ps, Err(_) => panic!()}; +/// assert_eq!(PRIMES, [79, 83, 89, 97]); /// ``` /// Compute larger primes without starting from zero: /// ``` /// # use const_primes::{primes_lt, Error}; /// # #[allow(long_running_const_eval)] -/// const BIG_PRIMES: const_primes::Result<3> = primes_lt::<3, 70_711>(5_000_000_030); +/// const BIG_PRIMES: Result<[u64; 3], Error> = primes_lt::<3, 70_711>(5_000_000_030); /// /// assert_eq!(BIG_PRIMES?, [4_999_999_903, 4_999_999_937, 5_000_000_029]); /// # Ok::<(), Error>(()) /// ``` +/// # Errors +/// /// If the number of primes requested, `N`, is larger than /// the number of primes that exists below the `lower_limit` we -/// will get a partial result of all the existing primes. -/// Due to limitations on const evaluation this will still -/// take up the full `N` numbers worth of memory. +/// will get an error: /// ``` /// # use const_primes::{primes_lt, Error}; -/// const PRIMES: const_primes::Result<9> = primes_lt::<9, 9>(10); -/// assert_eq!(PRIMES?, [2, 3, 5, 7]); -/// # Ok::<(), Error>(()) +/// const PRIMES: Result<[u64; 9], Error> = primes_lt::<9, 9>(10); +/// assert_eq!(PRIMES, Err(Error::OutOfPrimes)); /// ``` -/// # Errors /// -/// Returns an error if `upper_limit` is larger than `MEM`^2 or if `upper_limit` is smaller than or equal to 2. +/// We will also get an error if `upper_limit` is larger than `MEM`^2 or if `upper_limit` is smaller than or equal to 2. /// ``` -/// # use const_primes::primes_lt; -/// const TOO_LARGE_LIMIT: const_primes::Result<3> = primes_lt::<3, 5>(26); -/// const TOO_SMALL_LIMIT: const_primes::Result<1> = primes_lt::<1, 1>(1); +/// # use const_primes::{primes_lt, Error}; +/// const TOO_LARGE_LIMIT: Result<[u64; 3], Error> = primes_lt::<3, 5>(26); +/// const TOO_SMALL_LIMIT: Result<[u64 ;1], Error> = primes_lt::<1, 1>(1); /// assert!(TOO_LARGE_LIMIT.is_err()); /// assert!(TOO_SMALL_LIMIT.is_err()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn primes_lt(mut upper_limit: u64) -> Result { +pub const fn primes_lt( + mut upper_limit: u64, +) -> Result<[u64; N], Error> { const { assert!(N > 0, "`N` must be at least 1"); assert!(MEM >= N, "`MEM` must be at least as large as `N`"); } - let mem64 = const { + let mem_sqr = const { let mem64 = MEM as u64; - assert!( - mem64.checked_mul(mem64).is_some(), - "`MEM`^2 must fit in a u64" - ); - mem64 + let mem_sqr = match mem64.checked_mul(mem64) { + Some(prod) => prod, + None => panic!("`MEM`^2 must fit in a u64"), + }; + mem_sqr }; if upper_limit <= 2 { return Err(Error::TooSmallLimit(upper_limit)); } - if upper_limit > mem64 * mem64 { - return Err(Error::TooLargeLimit(upper_limit, MEM)); + if upper_limit > mem_sqr { + return Err(Error::TooLargeLimit(upper_limit, mem_sqr)); } let mut primes: [u64; N] = [0; N]; @@ -218,11 +212,12 @@ pub const fn primes_lt(mut upper_limit: u64) - } upper_limit = smallest_found_prime; if upper_limit <= 2 && total_primes_found < N { - return Ok(ArraySection::new(primes, N - total_primes_found..N)); + return Err(Error::OutOfPrimes); + //return Err(ArraySection::new(primes, N - total_primes_found..N)); } } - Ok(ArraySection::new(primes, 0..N)) + Ok(primes) } /// Same as the private const fn isqrt in the crate. @@ -252,17 +247,17 @@ macro_rules! ඞ_const_primes_isqrt { /// # use const_primes::{const_primes, Error}; /// const PRIMES: [u32; 3] = const_primes!(); /// const LIMIT: u64 = 5_000_000_031; -/// const PRIMES_GEQ: const_primes::Result<3> = const_primes!(3; >= LIMIT); -/// const PRIMES_LT: const_primes::Result<3> = const_primes!(3; < LIMIT); +/// const PRIMES_GEQ: Result<[u64; 3], Error> = const_primes!(3; >= LIMIT); +/// const PRIMES_LT: Result<[u64; 3], Error> = const_primes!(3; < LIMIT); /// // Can also be used at runtime: /// let primes = const_primes!(3); -/// let primes_geq = const_primes!(3; >= LIMIT)?; +/// let primes_geq = const_primes!(3; >= LIMIT); /// /// assert_eq!(primes, PRIMES); /// assert_eq!(PRIMES, [2, 3, 5]); -/// assert_eq!(PRIMES_GEQ?, primes_geq); -/// assert_eq!(PRIMES_GEQ?, [5000000039, 5000000059, 5000000063]); -/// assert_eq!(PRIMES_LT?, [4999999903, 4999999937, 5000000029]); +/// assert_eq!(PRIMES_GEQ, primes_geq); +/// assert_eq!(PRIMES_GEQ, Ok([5000000039, 5000000059, 5000000063])); +/// assert_eq!(PRIMES_LT, Ok([4999999903, 4999999937, 5000000029])); /// # Ok::<(), Error>(()) /// ``` #[macro_export] @@ -311,37 +306,38 @@ macro_rules! const_primes { /// /// Basic usage: /// ``` -/// # use const_primes::{primes_geq, Error}; -/// const PRIMES: const_primes::Result<5> = primes_geq::<5, 5>(10); -/// assert_eq!(PRIMES?, [11, 13, 17, 19, 23]); -/// # Ok::<(), Error>(()) +/// use const_primes::{primes_geq, Error}; +/// const PRIMES: [u64; 5] = match primes_geq::<5, 5>(10) {Ok(ps) => ps, Err(_) => panic!()}; +/// assert_eq!(PRIMES, [11, 13, 17, 19, 23]); /// ``` /// Compute larger primes without starting from zero: /// ``` /// # use const_primes::{primes_geq, Error}; /// # #[allow(long_running_const_eval)] -/// const P: const_primes::Result<3> = primes_geq::<3, 71_000>(5_000_000_030); +/// const P: Result<[u64; 3], Error> = primes_geq::<3, 71_000>(5_000_000_030); /// assert_eq!(P?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); /// # Ok::<(), Error>(()) /// ``` -/// Only primes smaller than `MEM^2` will be generated: +/// # Errors +/// +/// Only primes smaller than `MEM^2` can be generated, so if the sieve +/// encounters a number larger than that it results in an error: /// ``` /// # use const_primes::{primes_geq, Error}; -/// const PRIMES: const_primes::Result<3> = primes_geq::<3, 3>(5); -/// assert_eq!(PRIMES?, [5, 7]); -/// # Ok::<(), Error>(()) +/// const PRIMES: Result<[u64; 3], Error> = primes_geq::<3, 3>(5); +/// assert!(matches!(PRIMES, Err(Error::SieveOverrun(_)))); /// ``` /// -/// # Errors -/// /// Returns an error if `lower_limit` is larger than or equal to `MEM^2`. /// ``` -/// # use const_primes::{primes_geq, Result}; -/// const PRIMES: Result<5> = primes_geq::<5, 5>(26); +/// # use const_primes::{primes_geq, Error}; +/// const PRIMES: Result<[u64; 5], Error> = primes_geq::<5, 5>(26); /// assert!(PRIMES.is_err()); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] -pub const fn primes_geq(lower_limit: u64) -> Result { +pub const fn primes_geq( + lower_limit: u64, +) -> Result<[u64; N], Error> { const { assert!(N > 0, "`N` must be at least 1"); assert!(MEM >= N, "`MEM` must be at least as large as `N`"); @@ -358,7 +354,7 @@ pub const fn primes_geq(lower_limit: u64) -> R let new_lower_limit = if lower_limit >= 2 { lower_limit } else { 2 }; if new_lower_limit >= mem_sqr { - return Err(Error::TooLargeLimit(lower_limit, MEM)); + return Err(Error::TooLargeLimit(lower_limit, mem_sqr)); } let lower_limit = new_lower_limit; @@ -378,9 +374,9 @@ pub const fn primes_geq(lower_limit: u64) -> R // We can not know whether this is actually a prime since // the base sieve contains no information - // about numbers larger than or equal to `MEM`. - if largest_found_prime >= mem64 * mem64 { - return Ok(ArraySection::new(primes, 0..total_found_primes)); + // about numbers larger than or equal to `MEM`^2. + if largest_found_prime >= mem_sqr { + return Err(Error::SieveOverrun(largest_found_prime)); } if largest_found_prime >= lower_limit { @@ -397,38 +393,39 @@ pub const fn primes_geq(lower_limit: u64) -> R sieve_limit = largest_found_prime + 1; } - Ok(ArraySection::new(primes, 0..N)) + Ok(primes) } -/// An enum describing whether the requested array could be filled completely, or only a partially. -/// A partial array can be returned by [`primes_lt`] if the size of the requested -/// array is larger than the actual number of primes less than the given `upper_limit`. -/// It can also be returned by [`primes_geq`] if it needs to sieve into a -/// region of numbers that exceed the square of the size of the requested array. +/// The error returned by [`primes_lt`] and [`primes_geq`] if the input +/// is invalid or does not work to produce the requested primes. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Error { - /// `MEM`^2 did not fit in a `u64`. - MEMSquaredOverflow(usize), /// the limit was larger than `MEM^2`. - TooLargeLimit(u64, usize), + TooLargeLimit(u64, u64), /// the limit was smaller than or equal to 2. TooSmallLimit(u64), + /// encountered a number larger than `MEM`^2. + SieveOverrun(u64), + /// ran out of primes. + OutOfPrimes, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::MEMSquaredOverflow(mem) => { - write!(f, "`MEM` was {mem}, and so `MEM`^2 did not fit in a `u64`") - } - Self::TooLargeLimit(limit, mem) => write!( + Self::TooLargeLimit(limit, mem_sqr) => write!( f, - "the limit was {limit} and `MEM` was {mem}, so the limit was larger than `MEM`^2" + "the limit ({limit}) was larger than `MEM`^2 ({mem_sqr})" ), Self::TooSmallLimit(limit) => write!( f, "the limit was {limit}, which is smaller than or equal to 2" ), + Self::SieveOverrun(number) => write!( + f, + "encountered the number {number} which would have needed `MEM` to be at least {} to sieve", crate::imath::isqrt(*number) + 1 + ), + Self::OutOfPrimes => write!(f, "ran out of primes before the array was filled"), } } } @@ -445,21 +442,18 @@ mod test { #[test] fn sanity_check_primes_geq() { { - const P: Result<5> = primes_geq::<5, 5>(10); + const P: Result<[u64; 5], Error> = primes_geq::<5, 5>(10); assert_eq!(P.unwrap().as_slice(), [11, 13, 17, 19, 23]); } { - const P: Result<5> = primes_geq::<5, 5>(0); + const P: Result<[u64; 5], Error> = primes_geq::<5, 5>(0); assert_eq!(P.unwrap().as_slice(), [2, 3, 5, 7, 11]); } { - const P: Result<1> = primes_geq::<1, 1>(0); + const P: Result<[u64; 1], Error> = primes_geq::<1, 1>(0); assert_eq!(P, Err(Error::TooLargeLimit(0, 1))); } - for &prime in primes_geq::<2_000, 2_000>(3_998_000).unwrap().as_slice() { - if prime == 0 { - break; - } + for &prime in primes_geq::<2_000, 2_008>(3_998_000).unwrap().as_slice() { assert!(is_prime(prime)); } } diff --git a/src/lib.rs b/src/lib.rs index c0b8c94..435c197 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,24 +53,21 @@ //! (which must be at least the ceiling of the square root of the largest encountered number). //! This means that one can sieve to large numbers, but doesn't need to store the entire sieve in the binary. //! ``` -//! # use const_primes::Error; -//! use const_primes::primes_geq; +//! use const_primes::{primes_geq, Error}; //! // ceil(sqrt(5_000_000_063)) = 70_711 -//! const P_GEQ: const_primes::Result<3> = primes_geq::<3, 70_711>(5_000_000_031); +//! const P_GEQ: Result<[u64; 3], Error> = primes_geq::<3, 70_711>(5_000_000_031); //! -//! assert_eq!(P_GEQ?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); -//! # Ok::<(), Error>(()) +//! assert_eq!(P_GEQ, Ok([5_000_000_039, 5_000_000_059, 5_000_000_063])); //! ``` //! If you do not wish to compute the required sieve size yourself, //! you can use the provided macro [`const_primes!`]: //! ``` //! # use const_primes::{const_primes, Error}; -//! const PRIMES_OVER_100: const_primes::Result<3> = const_primes!(3; >= 100); -//! const PRIMES_UNDER_100: const_primes::Result<3> = const_primes!(3; < 100); +//! const PRIMES_OVER_100: Result<[u64; 3], Error> = const_primes!(3; >= 100); +//! const PRIMES_UNDER_100: Result<[u64; 3], Error> = const_primes!(3; < 100); //! -//! assert_eq!(PRIMES_OVER_100?, [101, 103, 107]); -//! assert_eq!(PRIMES_UNDER_100?, [83, 89, 97]); -//! # Ok::<(), Error>(()) +//! assert_eq!(PRIMES_OVER_100, Ok([101, 103, 107])); +//! assert_eq!(PRIMES_UNDER_100, Ok([83, 89, 97])); //! ``` //! ``` //! # use const_primes::sieve_lt; @@ -124,7 +121,7 @@ mod other_prime; mod sieving; mod wrapper; -pub use generation::{primes, primes_geq, primes_lt, Error, Result}; +pub use generation::{primes, primes_geq, primes_lt, Error}; use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; @@ -254,7 +251,7 @@ mod test { ($($n:expr),+) => { $( { - const P: Result<$n> = primes_lt::<$n, $n>(100); + const P: Result<[u64; $n], Error> = primes_lt::<$n, $n>(100); for (i, prime) in P.unwrap().as_slice().into_iter().enumerate() { assert_eq!(PRECOMPUTED_PRIMES[25-$n..25][i], *prime as u32); } diff --git a/src/sieving.rs b/src/sieving.rs index 3476c65..aa0e861 100644 --- a/src/sieving.rs +++ b/src/sieving.rs @@ -189,10 +189,10 @@ pub const fn sieve() -> [bool; N] { /// ``` /// # Panics /// -/// Panics if `N + lower_limit` is larger than or equal to `N^2`. In const contexts this is a compile error: +/// Panics if `N + lower_limit` is larger than to `N^2`. In const contexts this is a compile error: /// ```compile_fail /// # use const_primes::sieve_geq; -/// const P: [bool; 5] = sieve_geq(20); +/// const P: [bool; 5] = sieve_geq(21); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] pub const fn sieve_geq(lower_limit: u64) -> [bool; N] { From 11a52e96f05b2d3dab36376262b64625ac6f701e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 12:33:39 +0200 Subject: [PATCH 198/212] Use heuristic to estimate geq memory requirements --- src/generation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 18e73e0..9c60bf6 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -287,7 +287,7 @@ macro_rules! const_primes { $n, { let mem = { $lim }; - $crate::ඞ_const_primes_isqrt!(mem) as ::core::primitive::usize + 1 + $crate::ඞ_const_primes_isqrt!(mem) as ::core::primitive::usize + 1 + { $n } }, >($lim) }; From 2f63369cbf29e2c97f6ff48f672da0430a6fa76c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 12:39:54 +0200 Subject: [PATCH 199/212] const_primes! -> primes_segment! --- src/generation.rs | 28 ++++++---------------------- src/lib.rs | 8 ++++---- 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 9c60bf6..66db981 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -239,40 +239,24 @@ macro_rules! ඞ_const_primes_isqrt { }; } -/// Call [`primes`], [`primes_geq`] and [`primes_lt`], and automatically compute the memory requirement. +/// Call [`primes_geq`] and [`primes_lt`], and automatically compute the memory requirement. /// /// # Example /// /// ``` -/// # use const_primes::{const_primes, Error}; -/// const PRIMES: [u32; 3] = const_primes!(); +/// # use const_primes::{primes_segment, Error}; /// const LIMIT: u64 = 5_000_000_031; -/// const PRIMES_GEQ: Result<[u64; 3], Error> = const_primes!(3; >= LIMIT); -/// const PRIMES_LT: Result<[u64; 3], Error> = const_primes!(3; < LIMIT); +/// const PRIMES_GEQ: Result<[u64; 3], Error> = primes_segment!(3; >= LIMIT); +/// const PRIMES_LT: Result<[u64; 3], Error> = primes_segment!(3; < LIMIT); /// // Can also be used at runtime: -/// let primes = const_primes!(3); -/// let primes_geq = const_primes!(3; >= LIMIT); +/// let primes_geq = primes_segment!(3; >= LIMIT); /// -/// assert_eq!(primes, PRIMES); -/// assert_eq!(PRIMES, [2, 3, 5]); /// assert_eq!(PRIMES_GEQ, primes_geq); /// assert_eq!(PRIMES_GEQ, Ok([5000000039, 5000000059, 5000000063])); /// assert_eq!(PRIMES_LT, Ok([4999999903, 4999999937, 5000000029])); -/// # Ok::<(), Error>(()) /// ``` #[macro_export] -macro_rules! const_primes { - () => { - $crate::primes() - }; - ($n:expr) => { - $crate::primes::< - { - let mem = { $n }; - mem - }, - >() - }; +macro_rules! primes_segment { ($n:expr; < $lim:expr) => { $crate::primes_lt::< $n, diff --git a/src/lib.rs b/src/lib.rs index 435c197..8ad8992 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,11 +60,11 @@ //! assert_eq!(P_GEQ, Ok([5_000_000_039, 5_000_000_059, 5_000_000_063])); //! ``` //! If you do not wish to compute the required sieve size yourself, -//! you can use the provided macro [`const_primes!`]: +//! you can use the provided macro [`primes_segment!`]: //! ``` -//! # use const_primes::{const_primes, Error}; -//! const PRIMES_OVER_100: Result<[u64; 3], Error> = const_primes!(3; >= 100); -//! const PRIMES_UNDER_100: Result<[u64; 3], Error> = const_primes!(3; < 100); +//! # use const_primes::{primes_segment, Error}; +//! const PRIMES_OVER_100: Result<[u64; 3], Error> = primes_segment!(3; >= 100); +//! const PRIMES_UNDER_100: Result<[u64; 3], Error> = primes_segment!(3; < 100); //! //! assert_eq!(PRIMES_OVER_100, Ok([101, 103, 107])); //! assert_eq!(PRIMES_UNDER_100, Ok([83, 89, 97])); From eb0b14ec141a2994277e74d196c6f27dae684a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 12:44:48 +0200 Subject: [PATCH 200/212] Add test to primes_segment --- src/generation.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/generation.rs b/src/generation.rs index 66db981..85b3780 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -441,4 +441,16 @@ mod test { assert!(is_prime(prime)); } } + + #[test] + fn check_primes_segment() { + const P_GEQ: Result<[u64; 10], Error> = primes_segment!(10; >= 1000); + const P_LT: Result<[u64; 10], Error> = primes_segment!(10; < 1000); + + assert_eq!( + P_GEQ, + Ok([1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061]) + ); + assert_eq!(P_LT, Ok([937, 941, 947, 953, 967, 971, 977, 983, 991, 997])); + } } From 8be89a40735f2bbd43aa8952789d98edd7ba41f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 12:47:18 +0200 Subject: [PATCH 201/212] Simplify test code --- src/generation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 85b3780..9433403 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -427,11 +427,11 @@ mod test { fn sanity_check_primes_geq() { { const P: Result<[u64; 5], Error> = primes_geq::<5, 5>(10); - assert_eq!(P.unwrap().as_slice(), [11, 13, 17, 19, 23]); + assert_eq!(P, Ok([11, 13, 17, 19, 23])); } { const P: Result<[u64; 5], Error> = primes_geq::<5, 5>(0); - assert_eq!(P.unwrap().as_slice(), [2, 3, 5, 7, 11]); + assert_eq!(P, Ok([2, 3, 5, 7, 11])); } { const P: Result<[u64; 1], Error> = primes_geq::<1, 1>(0); From b4599d94bcb15fdf9e3332ebe29538202359d99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 12:49:14 +0200 Subject: [PATCH 202/212] Add note about overestimating sieve size --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 8ad8992..0d393c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,6 +69,8 @@ //! assert_eq!(PRIMES_OVER_100, Ok([101, 103, 107])); //! assert_eq!(PRIMES_UNDER_100, Ok([83, 89, 97])); //! ``` +//! it may, however, overestimate the required sieve size. +//! //! ``` //! # use const_primes::sieve_lt; //! const N: usize = 70711; From e13fdaeb53e92d4d7fce83877a8d11de317aa4bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 12:59:40 +0200 Subject: [PATCH 203/212] Clarify docstirng --- src/generation.rs | 16 +++++----------- src/lib.rs | 12 ++++++------ 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 9433403..b59e709 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -115,9 +115,6 @@ pub const fn primes() -> [Underlying; N] { /// /// Fails to compile if `N` or `MEM` is 0, if `MEM < N` or if `MEM`^2 does not fit in a u64. /// -/// The return array fills from the end until either it is full, -/// or there are no more primes. -/// /// If you want to compute primes that are larger than some limit, take a look at [`primes_geq`]. /// /// # Example @@ -281,10 +278,7 @@ macro_rules! primes_segment { /// Fails to compile if `N` is 0. If `lower_limit` is less than 2 this functions assumes that it is 2, /// since there are no primes smaller than 2. /// -/// This function will fill the output array from index 0 and stop generating primes if they exceed `N^2`. -/// In that case the remaining elements of the output array will be 0. -/// -/// If you want to compute primes smaller than the input, take a look at [`primes_lt`]. +/// If you want to compute primes smaller than some limit, take a look at [`primes_lt`]. /// /// # Examples /// @@ -384,13 +378,13 @@ pub const fn primes_geq( /// is invalid or does not work to produce the requested primes. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Error { - /// the limit was larger than `MEM^2`. + /// The limit was larger than `MEM^2`. TooLargeLimit(u64, u64), - /// the limit was smaller than or equal to 2. + /// The limit was smaller than or equal to 2. TooSmallLimit(u64), - /// encountered a number larger than `MEM`^2. + /// Encountered a number larger than `MEM`^2. SieveOverrun(u64), - /// ran out of primes. + /// Ran out of primes. OutOfPrimes, } diff --git a/src/lib.rs b/src/lib.rs index 0d393c5..6cd9b10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,17 +47,17 @@ //! ``` //! ## Arbitrary ranges //! -//! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_geq`] and [`sieve_lt`] +//! The crate provides prime generation and sieving functions with suffixes, e.g. [`primes_lt`] and [`sieve_geq`] //! that can be used to work with ranges that don't start at zero. They take two generics: the number of primes //! to store in the binary, and the size of the sieve used during evaluation //! (which must be at least the ceiling of the square root of the largest encountered number). //! This means that one can sieve to large numbers, but doesn't need to store the entire sieve in the binary. //! ``` -//! use const_primes::{primes_geq, Error}; -//! // ceil(sqrt(5_000_000_063)) = 70_711 -//! const P_GEQ: Result<[u64; 3], Error> = primes_geq::<3, 70_711>(5_000_000_031); +//! use const_primes::{primes_lt, Error, isqrt}; +//! const LIMIT: u64 = 5_000_000_031; +//! const PRIMES_LT: Result<[u64; 3], Error> = primes_lt::<3, {isqrt(LIMIT) as usize + 1}>(LIMIT); //! -//! assert_eq!(P_GEQ, Ok([5_000_000_039, 5_000_000_059, 5_000_000_063])); +//! assert_eq!(PRIMES_LT, Ok([4_999_999_903, 4_999_999_937, 5_000_000_029])); //! ``` //! If you do not wish to compute the required sieve size yourself, //! you can use the provided macro [`primes_segment!`]: @@ -124,7 +124,7 @@ mod sieving; mod wrapper; pub use generation::{primes, primes_geq, primes_lt, Error}; -use imath::isqrt; +pub use imath::isqrt; pub use miller_rabin::is_prime; pub use other_prime::{next_prime, previous_prime}; pub use sieving::{sieve, sieve_geq, sieve_lt}; From 4c557e78926f7f87e3460816e069b0ebf473cef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 13:21:27 +0200 Subject: [PATCH 204/212] move 3 into const N --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6cd9b10..6c0df6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,7 +55,8 @@ //! ``` //! use const_primes::{primes_lt, Error, isqrt}; //! const LIMIT: u64 = 5_000_000_031; -//! const PRIMES_LT: Result<[u64; 3], Error> = primes_lt::<3, {isqrt(LIMIT) as usize + 1}>(LIMIT); +//! const N: usize = 3; +//! const PRIMES_LT: Result<[u64; N], Error> = primes_lt::(LIMIT); //! //! assert_eq!(PRIMES_LT, Ok([4_999_999_903, 4_999_999_937, 5_000_000_029])); //! ``` From 4ddcaddeab85e415d03d7a9c757276e5af78d913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 13:23:37 +0200 Subject: [PATCH 205/212] use isqrt in primes_segment! --- src/generation.rs | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index b59e709..0306b59 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -217,25 +217,6 @@ pub const fn primes_lt( Ok(primes) } -/// Same as the private const fn isqrt in the crate. -#[doc(hidden)] -#[macro_export] -macro_rules! ඞ_const_primes_isqrt { - ($n:ident) => { - if $n <= 1 { - $n - } else { - let mut x0 = ::core::primitive::u64::pow(2, ::core::primitive::u64::ilog2($n) / 2 + 1); - let mut x1 = (x0 + $n / x0) / 2; - while x1 < x0 { - x0 = x1; - x1 = (x0 + $n / x0) / 2; - } - x0 - } - }; -} - /// Call [`primes_geq`] and [`primes_lt`], and automatically compute the memory requirement. /// /// # Example @@ -259,7 +240,7 @@ macro_rules! primes_segment { $n, { let mem = { $lim }; - $crate::ඞ_const_primes_isqrt!(mem) as ::core::primitive::usize + 1 + $crate::isqrt(mem) as ::core::primitive::usize + 1 }, >($lim) }; @@ -268,7 +249,7 @@ macro_rules! primes_segment { $n, { let mem = { $lim }; - $crate::ඞ_const_primes_isqrt!(mem) as ::core::primitive::usize + 1 + { $n } + $crate::isqrt(mem) as ::core::primitive::usize + 1 + { $n } }, >($lim) }; From 3c5c687cbcb915091f35fe2b0c9222899f581f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 13:25:23 +0200 Subject: [PATCH 206/212] and -> or --- src/generation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 0306b59..288dea8 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -217,7 +217,7 @@ pub const fn primes_lt( Ok(primes) } -/// Call [`primes_geq`] and [`primes_lt`], and automatically compute the memory requirement. +/// Call [`primes_geq`] or [`primes_lt`], and automatically compute the memory requirement. /// /// # Example /// From 2185d55d5651d17bf3e8e03806e9e4459ac1bc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 13:28:56 +0200 Subject: [PATCH 207/212] Clearer error in docstring of primes_geq --- src/generation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 288dea8..d84cc4d 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -287,11 +287,11 @@ macro_rules! primes_segment { /// assert!(matches!(PRIMES, Err(Error::SieveOverrun(_)))); /// ``` /// -/// Returns an error if `lower_limit` is larger than or equal to `MEM^2`. +/// Also returns an error if `lower_limit` is larger than or equal to `MEM^2`: /// ``` /// # use const_primes::{primes_geq, Error}; /// const PRIMES: Result<[u64; 5], Error> = primes_geq::<5, 5>(26); -/// assert!(PRIMES.is_err()); +/// assert!(matches!(PRIMES, Err(Error::TooLargeLimit(_, _)))); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] pub const fn primes_geq( From 9f69a8fcc9095d01b588f840033a1987bacb569b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 13:35:52 +0200 Subject: [PATCH 208/212] docstring tweaks of primes_lt --- src/generation.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index d84cc4d..b9a214c 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -122,28 +122,30 @@ pub const fn primes() -> [Underlying; N] { /// Basic usage: /// ``` /// # use const_primes::{primes_lt, Error}; -/// // Sieving up to 100 means the sieve needs to be of size sqrt(100) = 10. +/// // Sieving up to 100 means the sieve needs to be of size ceil(sqrt(100)) = 10. /// // However, we only save the 4 largest primes in the constant. /// const PRIMES: [u64;4] = match primes_lt::<4, 10>(100) {Ok(ps) => ps, Err(_) => panic!()}; /// assert_eq!(PRIMES, [79, 83, 89, 97]); /// ``` -/// Compute larger primes without starting from zero: +/// Compute larger primes: /// ``` /// # use const_primes::{primes_lt, Error}; +/// use const_primes::isqrt; +/// const LIMIT: u64 = 5_000_000_030; /// # #[allow(long_running_const_eval)] -/// const BIG_PRIMES: Result<[u64; 3], Error> = primes_lt::<3, 70_711>(5_000_000_030); +/// const BIG_PRIMES: Result<[u64; 3], Error> = primes_lt::<3, {isqrt(LIMIT) as usize + 1}>(LIMIT); /// -/// assert_eq!(BIG_PRIMES?, [4_999_999_903, 4_999_999_937, 5_000_000_029]); -/// # Ok::<(), Error>(()) +/// assert_eq!(BIG_PRIMES, Ok([4_999_999_903, 4_999_999_937, 5_000_000_029])); /// ``` /// # Errors /// /// If the number of primes requested, `N`, is larger than -/// the number of primes that exists below the `lower_limit` we +/// the number of primes that exists below the `upper_limit` we /// will get an error: /// ``` /// # use const_primes::{primes_lt, Error}; -/// const PRIMES: Result<[u64; 9], Error> = primes_lt::<9, 9>(10); +/// const N: usize = 9; +/// const PRIMES: Result<[u64; N], Error> = primes_lt::(10); /// assert_eq!(PRIMES, Err(Error::OutOfPrimes)); /// ``` /// @@ -152,8 +154,8 @@ pub const fn primes() -> [Underlying; N] { /// # use const_primes::{primes_lt, Error}; /// const TOO_LARGE_LIMIT: Result<[u64; 3], Error> = primes_lt::<3, 5>(26); /// const TOO_SMALL_LIMIT: Result<[u64 ;1], Error> = primes_lt::<1, 1>(1); -/// assert!(TOO_LARGE_LIMIT.is_err()); -/// assert!(TOO_SMALL_LIMIT.is_err()); +/// assert!(matches!(TOO_LARGE_LIMIT, Err(Error::TooLargeLimit(_, _)))); +/// assert!(matches!(TOO_SMALL_LIMIT, Err(Error::TooSmallLimit(_)))); /// ``` #[must_use = "the function only returns a new value and does not modify its input"] pub const fn primes_lt( From a7f8aaa6a9bf6fd4663c7c2317ec5703d8dabdad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 13:36:20 +0200 Subject: [PATCH 209/212] Force nightly for now --- .gitignore | 3 +-- rust-toolchain.toml | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/.gitignore b/.gitignore index 482c89d..2ebc5ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target -/Cargo.lock -rust-toolchain.toml \ No newline at end of file +/Cargo.lock \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file From 1d7b69c268a8e16cc78103a5b2de9201a2aefaf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 13:48:55 +0200 Subject: [PATCH 210/212] All hail clippy --- src/generation.rs | 5 ++--- src/wrapper.rs | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index b9a214c..17bf84f 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -167,11 +167,10 @@ pub const fn primes_lt( } let mem_sqr = const { let mem64 = MEM as u64; - let mem_sqr = match mem64.checked_mul(mem64) { + match mem64.checked_mul(mem64) { Some(prod) => prod, None => panic!("`MEM`^2 must fit in a u64"), - }; - mem_sqr + } }; if upper_limit <= 2 { diff --git a/src/wrapper.rs b/src/wrapper.rs index 5fb3cd0..8781115 100644 --- a/src/wrapper.rs +++ b/src/wrapper.rs @@ -325,7 +325,7 @@ where type Output = I::Output; #[inline] fn index(&self, index: I) -> &Self::Output { - &self.primes.index(index) + self.primes.index(index) } } From 64ba7c9f6b34fe5e2aaf815e5f6c58a94812b831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 13:50:00 +0200 Subject: [PATCH 211/212] Remove commented line --- src/generation.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/generation.rs b/src/generation.rs index 17bf84f..116cb39 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -211,7 +211,6 @@ pub const fn primes_lt( upper_limit = smallest_found_prime; if upper_limit <= 2 && total_primes_found < N { return Err(Error::OutOfPrimes); - //return Err(ArraySection::new(primes, N - total_primes_found..N)); } } From f820bba453b336cdee5a949d3f49edfa8a21b049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johanna=20S=C3=B6rng=C3=A5rd?= Date: Tue, 7 May 2024 14:01:58 +0200 Subject: [PATCH 212/212] primes_geq docstring tweaks --- src/generation.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/generation.rs b/src/generation.rs index 116cb39..f773702 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -266,15 +266,19 @@ macro_rules! primes_segment { /// Basic usage: /// ``` /// use const_primes::{primes_geq, Error}; -/// const PRIMES: [u64; 5] = match primes_geq::<5, 5>(10) {Ok(ps) => ps, Err(_) => panic!()}; -/// assert_eq!(PRIMES, [11, 13, 17, 19, 23]); +/// // Compute 5 primes larger than 40. The largest will be 59, so `MEM` needs to be at least 8. +/// const PRIMES: [u64; 5] = match primes_geq::<5, 8>(40) {Ok(ps) => ps, Err(_) => panic!()}; +/// assert_eq!(PRIMES, [41, 43, 47, 53, 59]); /// ``` -/// Compute larger primes without starting from zero: +/// Estimate the size of `MEM` using the square root of the limit (and some extra, proportional to `N`): /// ``` /// # use const_primes::{primes_geq, Error}; +/// use const_primes::isqrt; +/// const N: usize = 3; +/// const LIMIT: u64 = 5_000_000_030; /// # #[allow(long_running_const_eval)] -/// const P: Result<[u64; 3], Error> = primes_geq::<3, 71_000>(5_000_000_030); -/// assert_eq!(P?, [5_000_000_039, 5_000_000_059, 5_000_000_063]); +/// const PRIMES_GEQ: Result<[u64; N], Error> = primes_geq::(LIMIT); +/// assert_eq!(PRIMES_GEQ, Ok([5_000_000_039, 5_000_000_059, 5_000_000_063])); /// # Ok::<(), Error>(()) /// ``` /// # Errors