use core::fmt;
use crate::{sieve, sieve::sieve_segment, Underlying};
#[must_use = "the function only returns a new value"]
pub const fn primes<const N: usize>() -> [Underlying; N] {
if N <= 1 {
return [2; N];
} else if N == 2 {
let mut primes = [0; N];
primes[0] = 2;
primes[1] = 3;
return primes;
}
let mut primes = [0; N];
let mut prime_count = 0;
let mut sieve: [bool; N] = sieve();
let mut number = 0;
while number < N {
if sieve[number] {
primes[prime_count] = number as Underlying;
prime_count += 1;
}
number += 1;
}
let mut low = N - 1;
let mut high = 2 * N - 1;
'generate: while prime_count < N {
sieve = [true; N];
let mut i = 0;
while i < prime_count {
let prime = primes[i] as usize;
let mut composite = (low / prime) * prime;
if composite < low {
composite += prime;
}
while composite < high {
sieve[composite - low] = false;
composite += prime;
}
i += 1;
}
i = low;
while i < high {
if sieve[i - low] {
primes[prime_count] = i as Underlying;
prime_count += 1;
if prime_count >= N {
break 'generate;
}
}
i += 1;
}
low += N;
high += N;
}
primes
}
#[must_use = "the function only returns a new value and does not modify its input"]
pub const fn primes_lt<const N: usize, const MEM: usize>(
mut upper_limit: u64,
) -> Result<[u64; N], GenerationError> {
const { assert!(MEM >= N, "`MEM` must be at least as large as `N`") }
let mem_sqr = const {
let mem64 = MEM as u64;
match mem64.checked_mul(mem64) {
Some(mem_sqr) => mem_sqr,
None => panic!("`MEM`^2 must fit in a u64"),
}
};
if upper_limit <= 2 {
return Err(GenerationError::TooSmallLimit);
}
if upper_limit > mem_sqr {
return Err(GenerationError::TooSmallSieveSize);
}
let mut primes: [u64; N] = [0; N];
if N == 0 {
return Ok(primes);
}
let base_sieve: [bool; MEM] = sieve();
let mut total_primes_found: usize = 0;
'generate: while total_primes_found < N {
let mut smallest_found_prime = primes[N - 1 - total_primes_found];
let (offset, upper_sieve) = match sieve_segment(&base_sieve, upper_limit) {
Ok(res) => (0, res),
Err(_) => ((MEM as u64 - upper_limit) as usize, base_sieve),
};
let mut i: usize = 0;
while i < MEM - offset {
if upper_sieve[MEM - 1 - i - offset] {
smallest_found_prime = upper_limit - 1 - i as u64;
primes[N - 1 - total_primes_found] = smallest_found_prime;
total_primes_found += 1;
if total_primes_found >= N {
break 'generate;
}
}
i += 1;
}
upper_limit = smallest_found_prime;
if upper_limit <= 2 && total_primes_found < N {
return Err(GenerationError::OutOfPrimes);
}
}
Ok(primes)
}
#[macro_export]
macro_rules! primes_segment {
($n:expr; < $lim:expr) => {
$crate::primes_lt::<
{ $n },
{
let mem: u64 = { $lim };
$crate::isqrt(mem) as ::core::primitive::usize + 1
},
>({ $lim })
};
($n:expr; >= $lim:expr) => {
$crate::primes_geq::<
{ $n },
{
let mem: u64 = { $lim };
$crate::isqrt(mem) as ::core::primitive::usize + 1 + { $n }
},
>({ $lim })
};
}
#[must_use = "the function only returns a new value and does not modify its input"]
pub const fn primes_geq<const N: usize, const MEM: usize>(
lower_limit: u64,
) -> Result<[u64; N], GenerationError> {
const { assert!(MEM >= N, "`MEM` must be at least as large as `N`") }
let (mem64, mem_sqr) = const {
let mem64 = MEM as u64;
match mem64.checked_mul(mem64) {
Some(mem_sqr) => (mem64, mem_sqr),
None => panic!("`MEM`^2 must fit in a `u64`"),
}
};
if N == 0 {
return Ok([0; N]);
}
if lower_limit <= 2 {
let ans32: [u32; N] = primes();
let mut ans64 = [0; N];
let mut i = 0;
while i < N {
ans64[i] = ans32[i] as u64;
i += 1;
}
return Ok(ans64);
}
if lower_limit >= mem_sqr {
return Err(GenerationError::TooSmallSieveSize);
}
let mut primes = [0; N];
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 Ok(upper_sieve) = sieve_segment(&base_sieve, sieve_limit + mem64) else {
panic!("can not happen since we set upper limit to mem + nonzero stuff")
};
let mut i = 0;
while i < MEM {
if upper_sieve[i] {
largest_found_prime = sieve_limit + i as u64;
if largest_found_prime >= mem_sqr {
return Err(GenerationError::SieveOverrun(largest_found_prime));
}
if largest_found_prime >= lower_limit {
primes[total_found_primes] = largest_found_prime;
total_found_primes += 1;
if total_found_primes >= N {
break 'generate;
}
}
}
i += 1;
}
sieve_limit = largest_found_prime + 1;
}
Ok(primes)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
pub enum GenerationError {
TooSmallSieveSize,
TooSmallLimit,
SieveOverrun(u64),
OutOfPrimes,
}
impl fmt::Display for GenerationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TooSmallSieveSize => write!(
f,
"the limit was larger than `MEM`^2"
),
Self::TooSmallLimit => write!(
f,
"the limit was 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::integer_math::isqrt(*number) + 1
),
Self::OutOfPrimes => write!(f, "ran out of primes before the array was filled"),
}
}
}
impl core::error::Error for GenerationError {}
#[cfg(test)]
mod test {
use crate::is_prime;
use super::*;
#[test]
fn sanity_check_primes_geq() {
{
const P: Result<[u64; 5], GenerationError> = primes_geq::<5, 5>(10);
assert_eq!(P, Ok([11, 13, 17, 19, 23]));
}
{
const P: Result<[u64; 5], GenerationError> = primes_geq::<5, 5>(0);
assert_eq!(P, Ok([2, 3, 5, 7, 11]));
}
{
const P: Result<[u64; 1], GenerationError> = primes_geq::<1, 1>(0);
assert_eq!(P, Ok([2]));
}
for &prime in primes_geq::<2_000, 2_008>(3_998_000).unwrap().as_slice() {
assert!(is_prime(prime));
}
assert_eq!(primes_geq::<0, 0>(10), Ok([]));
assert_eq!(primes_geq::<3, 3>(2), Ok([2, 3, 5]));
assert_eq!(
primes_geq::<3, 3>(10),
Err(GenerationError::TooSmallSieveSize)
);
assert_eq!(primes_geq::<2, 2>(3), Err(GenerationError::SieveOverrun(4)));
}
#[test]
fn sanity_check_primes_lt() {
{
const P: Result<[u64; 5], GenerationError> = primes_lt::<5, 5>(20);
assert_eq!(P, Ok([7, 11, 13, 17, 19]));
}
{
const P: Result<[u64; 5], GenerationError> = primes_lt::<5, 5>(12);
assert_eq!(P, Ok([2, 3, 5, 7, 11]));
}
{
const P: Result<[u64; 1], GenerationError> = primes_lt::<1, 2>(3);
assert_eq!(P, Ok([2]));
}
assert_eq!(primes_lt::<2, 2>(2), Err(GenerationError::TooSmallLimit));
assert_eq!(
primes_lt::<2, 2>(5),
Err(GenerationError::TooSmallSieveSize)
);
assert_eq!(primes_lt::<0, 2>(3), Ok([]));
assert_eq!(primes_lt::<3, 5>(4), Err(GenerationError::OutOfPrimes));
}
#[test]
fn check_primes_segment() {
const P_GEQ: Result<[u64; 10], GenerationError> = primes_segment!(10; >= 1000);
const P_LT: Result<[u64; 10], GenerationError> = 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]));
}
}