#![allow(clippy::len_without_is_empty)]
use std::{
array::IntoIter,
iter::{from_fn, Enumerate, FilterMap},
};
#[derive(Debug, Clone, Copy)]
pub struct LinearSieve<const LEN: usize> {
arr: [usize; LEN],
}
impl<const LEN: usize> LinearSieve<LEN> {
pub const fn new() -> Self {
let mut primes = [0; LEN];
let mut pc = 0;
let mut arr = [usize::MAX; LEN];
let mut i = 2;
while i < LEN {
if arr[i] == usize::MAX {
primes[pc] = i;
pc += 1;
arr[i] = i;
}
let mut j = 0;
while j < pc && primes[j] <= arr[i] && primes[j].saturating_mul(i) < LEN {
arr[primes[j] * i] = primes[j];
j += 1;
}
i += 1;
}
Self { arr }
}
pub const fn len(&self) -> usize {
self.arr.len()
}
pub const fn is_prime(&self, value: usize) -> bool {
self.arr[value] == value
}
pub fn factors(&self, mut value: usize) -> impl Iterator<Item = usize> + '_ {
from_fn(move || {
(value > 1).then(|| {
let res = self.arr[value];
value /= res;
res
})
})
}
}
impl<const LEN: usize> IntoIterator for LinearSieve<LEN> {
type Item = usize;
type IntoIter = FilterMap<Enumerate<IntoIter<usize, LEN>>, fn((usize, usize)) -> Option<usize>>;
fn into_iter(self) -> Self::IntoIter {
self.arr
.into_iter()
.enumerate()
.filter_map(|(i, p)| (i == p).then_some(p))
}
}
impl<const LEN: usize> Default for LinearSieve<LEN> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use std::ops::Mul;
use crate::*;
const LEN: usize = 10000;
const S: LinearSieve<LEN> = LinearSieve::new();
#[test]
fn prime_check_test() {
for i in 0..LEN {
assert_eq!(i.is_prime(), S.is_prime(i));
}
}
#[test]
fn sieve_iterator_test() {
assert!(S.into_iter().all(|p| p.is_prime()))
}
#[test]
fn factors_test() {
for i in 0..LEN {
let f = S.factors(i).collect::<Vec<_>>();
assert!(f.windows(2).all(|v| v[0] <= v[1]));
if i > 1 {
assert_eq!(f.into_iter().reduce(Mul::mul).unwrap(), i);
} else {
assert!(f.is_empty());
}
}
}
}