use std::collections::HashMap;
use bitvec::prelude::*;
use rayon::prelude::*;
use crate::common::{
PRIME_ROOTS, align_to_cycle, find_inv_prime_root_vec, get_cyclic_composite_vec,
};
#[derive(Debug, PartialEq)]
pub enum SieveError {
EmptyRange,
InvalidSize,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum SieveMode {
Sieve,
Window,
}
pub struct Sieve {
root: u8, mode: SieveMode, q_hat: Vec<u8>, l: Vec<u8>, composites: BitVec,
b_start: u128,
b_end: u128,
i_limit: u128,
}
impl Sieve {
pub fn new(root_idx: u8, start: u128, end: u128, mode: SieveMode) -> Result<Self, SieveError> {
let root = PRIME_ROOTS[root_idx as usize];
let (b_start, b_end) = align_to_cycle(start, end);
if b_end <= b_start {
return Err(SieveError::EmptyRange);
}
let storage_size = (b_end - b_start)
.try_into()
.map_err(|_| SieveError::InvalidSize)?;
let q_hat = find_inv_prime_root_vec(root_idx as usize);
let l = get_cyclic_composite_vec(root_idx as usize, &q_hat);
let mut composites = bitvec![0; storage_size];
let i_limit = end.isqrt().div_ceil(210);
let (b_start_max, b_end_min) = Self::align_to_root(root, start, end);
Self::poison_storage(
&mut composites,
(b_start_max - b_start) as usize,
(b_end - b_end_min) as usize,
);
Ok(Self {
root,
mode,
l,
q_hat,
composites,
b_start,
b_end,
i_limit,
})
}
fn align_to_root(root: u8, start: u128, end: u128) -> (u128, u128) {
let r = root as u128;
let b_start = (210 - 1 + start).saturating_sub(r) / 210; let b_end = (210 + end - r) / 210;
(b_start, b_end)
}
fn poison_storage(composites: &mut BitVec, num_start: usize, num_end: usize) {
for i in 0..num_start {
composites.set(i, true);
}
let sz = composites.len() - 1;
for i in 0..num_end {
composites.set(sz - i, true);
}
}
pub fn get_primes(&mut self) -> Vec<u128> {
self.get_composites();
self.hydrate_primes()
}
pub fn count_primes(&mut self) -> usize {
self.get_composites();
self.count_primes_in_storage()
}
fn find_simple_composites(&mut self) {
for (idx, root) in PRIME_ROOTS.iter().enumerate() {
let q = *root as u128;
let offset = self.l[idx] as u128 - 1;
let i = (self.b_start + q - 1).saturating_sub(offset) / q; let mut b_val = offset + i * q;
while b_val < self.b_end {
self.composites.set((b_val - self.b_start) as usize, true); b_val += q;
}
}
}
fn get_complex_composites(&mut self, i_start: u128, i_end: u128) {
for (idx, root) in PRIME_ROOTS.iter().enumerate() {
let q = *root as u128;
let l = self.l[idx] as u128;
for i in i_start..i_end {
let offset = l + i * q - 1;
let scalar = self.q_hat[idx] as u128 + i * 210;
let j = ((self.b_start + scalar - 1).saturating_sub(offset)) / scalar; let mut b_val = offset + j * scalar;
while b_val < self.b_end {
self.composites.set((b_val - self.b_start) as usize, true); b_val += scalar;
}
}
}
}
fn get_composites_by_window(&mut self, i_start: u128, i_end: u128) -> bool {
let mut num_composites = 0;
for b in self.b_start..self.b_end {
if self.composites[(b - self.b_start) as usize] {
num_composites += 1;
continue;
}
let mut found = false;
for i in i_start..i_end {
for (idx, root) in PRIME_ROOTS.iter().enumerate() {
let q = *root as u128;
let q_hat = self.q_hat[idx] as u128;
let l = self.l[idx] as u128;
let i_max = (b + q + 210 - 1).saturating_sub(l) / (q + 210);
if i < i_max {
let offset = l + i * q - 1;
let scalar = q_hat + i * 210;
if (((b + scalar).saturating_sub(offset)) % scalar) == 0 {
self.composites.set((b - self.b_start) as usize, true); found = true;
break;
}
}
}
if found {
num_composites += 1;
break;
}
}
}
num_composites != self.composites.len() as u128
}
fn get_composites(&mut self) {
self.find_simple_composites();
const SMALL_LIMIT: u128 = 50_000; const BIG_LIMIT: u128 = 5_000_000;
if self.mode == SieveMode::Window {
let mut offset = 1;
while offset < self.i_limit {
if !self.get_composites_by_window(offset, (offset + SMALL_LIMIT).min(self.i_limit))
{
return;
}
offset += SMALL_LIMIT
}
} else {
let mut offset = 1;
while offset < self.i_limit {
self.get_complex_composites(offset, (offset + BIG_LIMIT).min(self.i_limit));
offset += BIG_LIMIT;
}
}
}
fn hydrate_primes(&self) -> Vec<u128> {
let mut primes = Vec::new();
let root = self.root as u128;
for (i, val) in self.composites.iter().enumerate() {
if !val {
primes.push(root + (self.b_start + i as u128) * 210);
}
}
primes
}
fn count_primes_in_storage(&self) -> usize {
self.composites.count_zeros()
}
}
fn get_composites(root_idx: u8, start: u128, end: u128) -> BitVec {
match Sieve::new(root_idx, start, end, SieveMode::Sieve) {
Ok(mut sieve) => {
sieve.get_composites();
sieve.composites
}
Err(e) => {
panic!("{:?}", e)
}
}
}
pub fn get_raw_composites(
root_indices: Vec<u8>,
start: u128,
end: u128,
mode: &SieveMode,
) -> HashMap<u8, BitVec> {
root_indices
.into_par_iter()
.map(|i| (i, get_composites(i, start, end)))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sieve_primes() {
let primes;
let mut inst = Sieve::new(0, 0, 1_000_000, SieveMode::Sieve).unwrap();
primes = inst.get_primes();
assert_eq!(primes.len(), 1649); assert_eq!(primes[0], 11); assert_eq!(primes[1649 - 1], 999_611); }
#[test]
fn test_windowed_primes() {
let primes;
let mut inst = Sieve::new(1, 0, 1_000, SieveMode::Window).unwrap();
primes = inst.get_primes();
assert_eq!(primes.len(), 5); assert_eq!(primes[0], 13); assert_eq!(primes[5 - 1], 853); }
}