use crate::core::sign_prime::{is_sign_prime, SIGN_PRIME};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use thiserror::Error;
#[derive(Error, Debug, Clone, PartialEq)]
pub enum PPrimeError {
#[error("Invalid prime: {0}")]
InvalidPrime(i64),
#[error("Cache error: {0}")]
CacheError(String),
#[error("Number too large for efficient computation")]
NumberTooLarge,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PPrimeType {
SignPrime,
MagnitudePrime,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PPrime {
value: i64,
prime_type: PPrimeType,
}
impl PPrime {
pub fn new(value: i64) -> Result<Self, PPrimeError> {
if is_sign_prime(value) {
Ok(PPrime {
value,
prime_type: PPrimeType::SignPrime,
})
} else if value > 1 && is_magnitude_prime(value) {
Ok(PPrime {
value,
prime_type: PPrimeType::MagnitudePrime,
})
} else {
Err(PPrimeError::InvalidPrime(value))
}
}
pub fn value(&self) -> i64 {
self.value
}
pub fn prime_type(&self) -> PPrimeType {
self.prime_type
}
pub fn is_sign_prime(&self) -> bool {
self.prime_type == PPrimeType::SignPrime
}
pub fn is_magnitude_prime(&self) -> bool {
self.prime_type == PPrimeType::MagnitudePrime
}
pub fn sign_prime() -> Self {
PPrime {
value: SIGN_PRIME,
prime_type: PPrimeType::SignPrime,
}
}
pub fn apply_to(&self, n: i64) -> i64 {
if self.is_sign_prime() {
-n
} else {
self.value * n
}
}
}
#[derive(Debug)]
pub struct PrimeCache {
magnitude_primes: Arc<Mutex<Vec<i64>>>,
primality_cache: Arc<Mutex<HashMap<i64, bool>>>,
max_computed: Arc<Mutex<i64>>,
}
impl PrimeCache {
pub fn new() -> Self {
let initial_primes = vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47];
let mut cache = HashMap::new();
for &p in &initial_primes {
cache.insert(p, true);
}
for i in 4..=50 {
if !initial_primes.contains(&i) {
cache.insert(i, false);
}
}
PrimeCache {
magnitude_primes: Arc::new(Mutex::new(initial_primes)),
primality_cache: Arc::new(Mutex::new(cache)),
max_computed: Arc::new(Mutex::new(47)),
}
}
pub fn is_magnitude_prime(&self, n: i64) -> bool {
if n <= 1 {
return false;
}
{
let cache = self.primality_cache.lock().unwrap();
if let Some(&is_prime) = cache.get(&n) {
return is_prime;
}
}
let is_prime = self.compute_primality(n);
{
let mut cache = self.primality_cache.lock().unwrap();
cache.insert(n, is_prime);
}
is_prime
}
fn compute_primality(&self, n: i64) -> bool {
if n <= 1 {
return false;
}
if n <= 3 {
return true;
}
if n % 2 == 0 || n % 3 == 0 {
return false;
}
let sqrt_n = (n as f64).sqrt() as i64;
let mut i = 5;
while i <= sqrt_n {
if n % i == 0 || n % (i + 2) == 0 {
return false;
}
i += 6;
}
true
}
pub fn magnitude_primes_up_to(&self, limit: i64) -> Vec<i64> {
let mut primes = self.magnitude_primes.lock().unwrap();
let max_computed = *self.max_computed.lock().unwrap();
if limit > max_computed {
self.extend_prime_cache(limit, &mut primes);
}
primes.iter().filter(|&&p| p <= limit).copied().collect()
}
fn extend_prime_cache(&self, limit: i64, primes: &mut Vec<i64>) {
let start = primes.last().copied().unwrap_or(2) + 1;
for candidate in start..=limit {
if self.compute_primality(candidate) {
primes.push(candidate);
}
}
*self.max_computed.lock().unwrap() = limit;
}
pub fn nth_magnitude_prime(&self, n: usize) -> Option<i64> {
let mut primes = self.magnitude_primes.lock().unwrap();
while primes.len() <= n {
let next_candidate = primes.last().copied().unwrap_or(2) + 1;
let mut candidate = next_candidate;
while !self.compute_primality(candidate) {
candidate += 1;
}
primes.push(candidate);
}
primes.get(n).copied()
}
}
impl Default for PrimeCache {
fn default() -> Self {
Self::new()
}
}
static GLOBAL_CACHE: std::sync::OnceLock<PrimeCache> = std::sync::OnceLock::new();
pub fn global_cache() -> &'static PrimeCache {
GLOBAL_CACHE.get_or_init(PrimeCache::new)
}
pub fn is_magnitude_prime(n: i64) -> bool {
global_cache().is_magnitude_prime(n)
}
pub fn is_p_prime(n: i64) -> bool {
is_sign_prime(n) || (n > 1 && is_magnitude_prime(n))
}
pub struct PPrimeIterator {
cache: &'static PrimeCache,
magnitude_index: usize,
include_sign_prime: bool,
returned_sign_prime: bool,
}
impl PPrimeIterator {
pub fn new(include_sign_prime: bool) -> Self {
PPrimeIterator {
cache: global_cache(),
magnitude_index: 0,
include_sign_prime,
returned_sign_prime: false,
}
}
pub fn magnitude_primes() -> Self {
Self::new(false)
}
pub fn all_p_primes() -> Self {
Self::new(true)
}
}
impl Iterator for PPrimeIterator {
type Item = PPrime;
fn next(&mut self) -> Option<Self::Item> {
if self.include_sign_prime && !self.returned_sign_prime {
self.returned_sign_prime = true;
return Some(PPrime::sign_prime());
}
if let Some(prime_value) = self.cache.nth_magnitude_prime(self.magnitude_index) {
self.magnitude_index += 1;
Some(PPrime {
value: prime_value,
prime_type: PPrimeType::MagnitudePrime,
})
} else {
None
}
}
}
pub fn p_primes_up_to(limit: i64, include_sign_prime: bool) -> Vec<PPrime> {
let mut result = Vec::new();
if include_sign_prime && limit >= -1 {
result.push(PPrime::sign_prime());
}
let magnitude_primes = global_cache().magnitude_primes_up_to(limit);
for prime in magnitude_primes {
result.push(PPrime {
value: prime,
prime_type: PPrimeType::MagnitudePrime,
});
}
result
}
pub fn first_n_p_primes(n: usize, include_sign_prime: bool) -> Vec<PPrime> {
PPrimeIterator::new(include_sign_prime).take(n).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pprime_creation() {
let sign_prime = PPrime::new(-1).unwrap();
assert!(sign_prime.is_sign_prime());
assert_eq!(sign_prime.value(), -1);
let magnitude_prime = PPrime::new(7).unwrap();
assert!(magnitude_prime.is_magnitude_prime());
assert_eq!(magnitude_prime.value(), 7);
assert!(PPrime::new(4).is_err()); assert!(PPrime::new(-2).is_err()); }
#[test]
fn test_is_p_prime() {
assert!(is_p_prime(-1)); assert!(is_p_prime(2)); assert!(is_p_prime(17)); assert!(!is_p_prime(4)); assert!(!is_p_prime(-2)); assert!(!is_p_prime(0)); assert!(!is_p_prime(1)); }
#[test]
fn test_magnitude_prime_detection() {
assert!(is_magnitude_prime(2));
assert!(is_magnitude_prime(3));
assert!(is_magnitude_prime(5));
assert!(is_magnitude_prime(7));
assert!(is_magnitude_prime(11));
assert!(is_magnitude_prime(97));
assert!(!is_magnitude_prime(1));
assert!(!is_magnitude_prime(4));
assert!(!is_magnitude_prime(6));
assert!(!is_magnitude_prime(8));
assert!(!is_magnitude_prime(9));
assert!(!is_magnitude_prime(100));
}
#[test]
fn test_prime_cache() {
let cache = PrimeCache::new();
assert!(cache.is_magnitude_prime(97));
assert!(!cache.is_magnitude_prime(98));
let primes = cache.magnitude_primes_up_to(20);
assert_eq!(primes, vec![2, 3, 5, 7, 11, 13, 17, 19]);
assert_eq!(cache.nth_magnitude_prime(0), Some(2));
assert_eq!(cache.nth_magnitude_prime(1), Some(3));
assert_eq!(cache.nth_magnitude_prime(4), Some(11));
}
#[test]
fn test_pprime_iterator() {
let primes: Vec<i64> = PPrimeIterator::magnitude_primes()
.take(5)
.map(|p| p.value())
.collect();
assert_eq!(primes, vec![2, 3, 5, 7, 11]);
let all_primes: Vec<i64> = PPrimeIterator::all_p_primes()
.take(6)
.map(|p| p.value())
.collect();
assert_eq!(all_primes, vec![-1, 2, 3, 5, 7, 11]);
}
#[test]
fn test_p_primes_up_to() {
let primes = p_primes_up_to(10, false);
let values: Vec<i64> = primes.iter().map(|p| p.value()).collect();
assert_eq!(values, vec![2, 3, 5, 7]);
let primes_with_sign = p_primes_up_to(10, true);
let values_with_sign: Vec<i64> = primes_with_sign.iter().map(|p| p.value()).collect();
assert_eq!(values_with_sign, vec![-1, 2, 3, 5, 7]);
}
#[test]
fn test_pprime_application() {
let sign_prime = PPrime::sign_prime();
assert_eq!(sign_prime.apply_to(5), -5);
assert_eq!(sign_prime.apply_to(-3), 3);
let magnitude_prime = PPrime::new(7).unwrap();
assert_eq!(magnitude_prime.apply_to(3), 21);
assert_eq!(magnitude_prime.apply_to(-2), -14);
}
#[test]
fn test_first_n_p_primes() {
let primes = first_n_p_primes(4, false);
let values: Vec<i64> = primes.iter().map(|p| p.value()).collect();
assert_eq!(values, vec![2, 3, 5, 7]);
let primes_with_sign = first_n_p_primes(4, true);
let values_with_sign: Vec<i64> = primes_with_sign.iter().map(|p| p.value()).collect();
assert_eq!(values_with_sign, vec![-1, 2, 3, 5]);
}
}