mod prime_factors;
mod primes_into_iter;
mod primes_iter;
pub use prime_factors::{PrimeFactorization, PrimeFactors};
pub use primes_into_iter::PrimesIntoIter;
pub use primes_iter::PrimesIter;
use crate::{primes, Underlying};
#[cfg(feature = "rkyv")]
use rkyv::bytecheck::{
rancor::{fail, Fallible, Source},
CheckBytes, Verify,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
feature = "zerocopy",
derive(zerocopy::IntoBytes, zerocopy::Immutable, zerocopy::KnownLayout)
)]
#[cfg_attr(
feature = "rkyv",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, CheckBytes)
)]
#[cfg_attr(feature = "rkyv", bytecheck(verify))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(try_from = "MaybePrimes<N>"))]
#[repr(transparent)]
pub struct Primes<const N: usize>(
#[cfg_attr(feature = "serde", serde(with = "serde_arrays"))] [Underlying; N],
);
#[cfg(feature = "rkyv")]
unsafe impl<const N: usize, C> Verify<C> for Primes<N>
where
C: Fallible + ?Sized,
C::Error: Source,
{
fn verify(&self, _context: &mut C) -> Result<(), C::Error> {
if self.0 == primes() {
Ok(())
} else {
fail!(NotPrimesError)
}
}
}
#[cfg(any(feature = "serde", feature = "rkyv"))]
#[derive(Debug)]
struct NotPrimesError;
#[cfg(any(feature = "serde", feature = "rkyv"))]
impl core::fmt::Display for NotPrimesError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "The array does not contain the first N primes")
}
}
#[cfg(any(feature = "serde", feature = "rkyv"))]
impl core::error::Error for NotPrimesError {}
#[cfg(feature = "serde")]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
struct MaybePrimes<const N: usize>(
#[cfg_attr(feature = "serde", serde(with = "serde_arrays"))] [Underlying; N],
);
#[cfg(feature = "serde")]
impl<const N: usize> TryFrom<MaybePrimes<N>> for Primes<N> {
type Error = NotPrimesError;
fn try_from(value: MaybePrimes<N>) -> Result<Self, Self::Error> {
if value.0 == primes() {
Ok(Primes(value.0))
} else {
Err(NotPrimesError)
}
}
}
impl<const N: usize> Primes<N> {
#[must_use = "the associated method only returns a new value"]
pub const fn new() -> Self {
const { assert!(N > 0, "`N` must be at least 1") }
Self(primes())
}
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn is_prime(&self, n: u32) -> Option<bool> {
match self.binary_search(n) {
Ok(_) => Some(true),
Err(i) => {
if i < N {
Some(false)
} else {
None
}
}
}
}
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn prime_pi(&self, n: Underlying) -> Option<usize> {
match self.binary_search(n) {
Ok(i) => Some(i + 1),
Err(maybe_i) => {
if maybe_i < N {
Some(maybe_i)
} else {
None
}
}
}
}
#[inline]
pub fn prime_factorization(&self, number: Underlying) -> PrimeFactorization<'_> {
PrimeFactorization::new(&self.0, number)
}
#[inline]
pub fn prime_factors(&self, number: Underlying) -> PrimeFactors<'_> {
PrimeFactors::new(&self.0, number)
}
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn previous_prime(&self, n: Underlying) -> Option<Underlying> {
if n <= 2 {
None
} else {
match self.binary_search(n) {
Ok(i) | Err(i) => {
if i > 0 && i < N {
Some(self.0[i - 1])
} else {
None
}
}
}
}
}
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn next_prime(&self, n: Underlying) -> Option<Underlying> {
match self.binary_search(n) {
Ok(i) => {
if i + 1 < self.len() {
Some(self.0[i + 1])
} else {
None
}
}
Err(i) => {
if i < N {
Some(self.0[i])
} else {
None
}
}
}
}
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn binary_search(&self, target: Underlying) -> Result<usize, usize> {
let mut size = N;
let mut left = 0;
let mut right = size;
while left < right {
let mid = left + size / 2;
let candidate = self.0[mid];
if candidate < target {
left = mid + 1;
} else if candidate > target {
right = mid;
} else {
return Ok(mid);
}
size = right - left;
}
Err(left)
}
#[inline]
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn into_array(self) -> [Underlying; N] {
self.0
}
#[inline]
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn as_array(&self) -> &[Underlying; N] {
&self.0
}
#[inline]
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn as_slice(&self) -> &[Underlying] {
self.0.as_slice()
}
#[inline]
pub fn iter(&self) -> PrimesIter<'_> {
PrimesIter::new(IntoIterator::into_iter(&self.0))
}
#[inline]
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn get(&self, index: usize) -> Option<&Underlying> {
if index < N {
Some(&self.0[index])
} else {
None
}
}
#[inline]
#[must_use = "the method only returns a new value and does not modify `self`"]
pub const fn last(&self) -> &Underlying {
match self.0.last() {
Some(l) => l,
None => panic!("unreachable: an empty `Primes<N>` can not be created"),
}
}
#[inline]
#[must_use = "the method only returns a new value and does not modify `self`"]
#[allow(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
N
}
pub const fn totient(&self, mut n: Underlying) -> Result<Underlying, PartialTotient> {
if n == 0 {
return Ok(0);
}
let mut i = 0;
let mut ans = 1;
while let Some(&prime) = self.get(i) {
let mut count = 0;
while n % prime == 0 {
n /= prime;
count += 1;
}
if count > 0 {
ans *= prime.pow(count - 1) * (prime - 1);
}
if n == 1 {
break;
}
i += 1;
}
if n == 1 {
Ok(ans)
} else {
Err(PartialTotient {
totient_using_known_primes: ans,
product_of_unknown_prime_factors: n,
})
}
}
}
#[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 struct PartialTotient {
pub totient_using_known_primes: Underlying,
pub product_of_unknown_prime_factors: Underlying,
}
impl<const N: usize> Default for Primes<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize, I> core::ops::Index<I> for Primes<N>
where
I: core::slice::SliceIndex<[Underlying]>,
{
type Output = I::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.0.index(index)
}
}
impl<const N: usize> From<Primes<N>> for [Underlying; N] {
#[inline]
fn from(const_primes: Primes<N>) -> Self {
const_primes.0
}
}
impl<const N: usize> AsRef<[Underlying]> for Primes<N> {
#[inline]
fn as_ref(&self) -> &[Underlying] {
&self.0
}
}
impl<const N: usize> AsRef<[Underlying; N]> for Primes<N> {
#[inline]
fn as_ref(&self) -> &[Underlying; N] {
&self.0
}
}
impl<const N: usize> IntoIterator for Primes<N> {
type Item = Underlying;
type IntoIter = PrimesIntoIter<N>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
PrimesIntoIter::new(self.0.into_iter())
}
}
impl<'a, const N: usize> IntoIterator for &'a Primes<N> {
type IntoIter = PrimesIter<'a>;
type Item = &'a Underlying;
fn into_iter(self) -> Self::IntoIter {
PrimesIter::new(IntoIterator::into_iter(&self.0))
}
}
#[cfg(test)]
mod test {
use crate::next_prime;
use super::*;
#[test]
fn verify_impl_from_primes_traits() {
const N: usize = 10;
const P: Primes<N> = Primes::new();
let p: [Underlying; N] = P.into();
assert_eq!(p, P.as_ref());
assert_eq!(
P.as_array(),
<Primes<N> as AsRef<[Underlying; N]>>::as_ref(&P)
);
}
#[test]
fn check_into_iter() {
const P: Primes<10> = Primes::new();
for (i, prime) in P.into_iter().enumerate() {
assert_eq!(prime, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29][i]);
}
}
#[test]
fn check_binary_search() {
const CACHE: Primes<100> = Primes::new();
type BSResult = Result<usize, usize>;
const FOUND2: BSResult = CACHE.binary_search(2);
const INSERT0: BSResult = CACHE.binary_search(0);
const INSERT4: BSResult = CACHE.binary_search(4);
const FOUND541: BSResult = CACHE.binary_search(541);
const NOINFO542: BSResult = CACHE.binary_search(542);
const BIG: BSResult = CACHE.binary_search(1000000);
assert_eq!(FOUND2, Ok(0));
assert_eq!(INSERT0, Err(0));
assert_eq!(INSERT4, Err(2));
assert_eq!(FOUND541, Ok(99));
assert_eq!(NOINFO542, Err(100));
assert_eq!(BIG, Err(100));
}
#[test]
fn test_into_iter() {
const PRIMES: Primes<10> = Primes::new();
for (&prime, ans) in (&PRIMES)
.into_iter()
.zip([2, 3, 5, 7, 11, 13, 17, 19, 23, 29])
{
assert_eq!(prime, ans);
}
}
#[test]
fn check_previous_prime() {
const CACHE: Primes<100> = Primes::new();
const PREV0: Option<Underlying> = CACHE.previous_prime(0);
const PREV400: Option<Underlying> = CACHE.previous_prime(400);
const PREV541: Option<Underlying> = CACHE.previous_prime(541);
const PREV542: Option<Underlying> = 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() {
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]
fn check_prime_factorization() {
const CACHE: Primes<3> = Primes::new();
let mut factorization_of_14 = CACHE.prime_factorization(14);
assert_eq!(factorization_of_14.next(), Some((2, 1)));
assert_eq!(factorization_of_14.next(), None);
assert_eq!(factorization_of_14.remainder(), Some(7));
let mut factorization_of_15 = CACHE.prime_factorization(15);
assert_eq!(factorization_of_15.next(), Some((3, 1)));
assert_eq!(factorization_of_15.next(), Some((5, 1)));
assert!(factorization_of_15.remainder().is_none());
let mut factorization_of_270 = CACHE.prime_factorization(2 * 3 * 3 * 3 * 5);
assert_eq!(factorization_of_270.next(), Some((2, 1)));
assert_eq!(factorization_of_270.next(), Some((3, 3)));
assert_eq!(factorization_of_270.next(), Some((5, 1)));
}
#[test]
fn check_prime_factors() {
const CACHE: Primes<3> = Primes::new();
let mut factors_of_14 = CACHE.prime_factors(14);
assert_eq!(factors_of_14.next(), Some(2));
assert_eq!(factors_of_14.next(), None);
assert_eq!(factors_of_14.remainder(), Some(7));
let mut factors_of_15 = CACHE.prime_factors(15);
assert_eq!(factors_of_15.next(), Some(3));
assert_eq!(factors_of_15.next(), Some(5));
assert!(factors_of_15.remainder().is_none());
let mut factors_of_270 = CACHE.prime_factors(2 * 3 * 3 * 3 * 5);
assert_eq!(factors_of_270.next(), Some(2));
assert_eq!(factors_of_270.next(), Some(3));
assert_eq!(factors_of_270.next(), Some(5));
}
#[test]
fn check_next_prime() {
const CACHE: Primes<100> = Primes::new();
const SPGEQ0: Option<Underlying> = CACHE.next_prime(0);
const SPGEQ400: Option<Underlying> = CACHE.next_prime(400);
const SPGEQ541: Option<Underlying> = CACHE.next_prime(540);
const SPGEQ542: Option<Underlying> = 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 = 31;
const NEXT_PRIME: [u32; N] = [
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<N> = Primes::new();
for (n, next) in NEXT_PRIME.iter().enumerate().take(N) {
assert_eq!(P.next_prime(n as u32), Some(*next));
}
}
#[test]
fn verify_into_array() {
const N: usize = 10;
const P: Primes<N> = Primes::new();
const A: [Underlying; N] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
assert_eq!(P.into_array(), A);
}
#[test]
fn verify_as_slice() {
const N: usize = 10;
const P: Primes<N> = Primes::new();
const A: [Underlying; N] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
assert_eq!(P.as_slice(), &A);
}
#[test]
fn verify_as_array() {
const N: usize = 10;
const P: Primes<N> = Primes::new();
const A: [Underlying; N] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
assert_eq!(P.as_array(), &A);
}
#[test]
fn check_get() {
const N: usize = 10;
const P: Primes<N> = Primes::new();
const A: [Underlying; N] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
for (n, gotten) in A.iter().enumerate().take(N) {
assert_eq!(P.get(n), Some(gotten));
}
for n in N + 1..2 * N {
assert!(P.get(n).is_none());
}
}
#[test]
fn check_last_and_len() {
const PRIMES: [Underlying; 10] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
macro_rules! check_last_n {
($($n:literal),+) => {
$(
{
let p: Primes<$n> = Primes::new();
assert_eq!(*p.last(), PRIMES[$n - 1]);
assert_eq!(p.len(), $n);
assert_eq!(*p.last(), p[$n - 1]);
}
)+
};
}
check_last_n!(1, 2, 3, 4, 5, 6, 7, 8, 9);
}
#[test]
fn check_count_primes_leq() {
const N: usize = 79;
const PRIME_COUNTS: [usize; N] = [
0, 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,
10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
15, 15, 16, 16, 16, 16, 16, 16, 17, 17, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20,
21, 21, 21, 21, 21, 21,
];
const P: Primes<N> = Primes::new();
for (n, count) in PRIME_COUNTS.iter().enumerate().take(N) {
assert_eq!(P.prime_pi(n as u32), Some(*count));
}
for n in *P.last() + 1..*P.last() * 2 {
assert!(P.prime_pi(n).is_none());
}
}
#[test]
fn check_iter() {
const P: Primes<10> = Primes::new();
for (p1, p2) in P.iter().zip([2, 3, 5, 7, 11, 13, 17, 19, 23, 29].iter()) {
assert_eq!(p1, p2);
}
}
#[test]
fn check_totient() {
const TOTIENTS: [Underlying; 101] = [
0, 1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8, 12, 10, 22, 8, 20,
12, 18, 12, 28, 8, 30, 16, 20, 16, 24, 12, 36, 18, 24, 16, 40, 12, 42, 20, 24, 22, 46,
16, 42, 20, 32, 24, 52, 18, 40, 24, 36, 28, 58, 16, 60, 30, 36, 32, 48, 20, 66, 32, 44,
24, 70, 24, 72, 36, 40, 36, 60, 24, 78, 32, 54, 40, 82, 24, 64, 42, 56, 40, 88, 24, 72,
44, 60, 46, 72, 32, 96, 42, 60, 40,
];
const NEXT_OUTSIDE: Underlying = match next_prime(*BIG_CACHE.last() as u64) {
Some(np) => np as Underlying,
None => panic!(),
};
const SMALL_CACHE: Primes<3> = Primes::new();
const BIG_CACHE: Primes<100> = Primes::new();
assert_eq!(SMALL_CACHE.totient(6), Ok(2));
assert_eq!(
SMALL_CACHE.totient(2 * 5 * 5 * 7 * 7),
Err(PartialTotient {
totient_using_known_primes: 20,
product_of_unknown_prime_factors: 49
})
);
for (i, totient) in TOTIENTS.into_iter().enumerate() {
assert_eq!(BIG_CACHE.totient(i as Underlying), Ok(totient));
if i != 0 {
assert_eq!(
BIG_CACHE.totient((i as Underlying) * NEXT_OUTSIDE),
Err(PartialTotient {
totient_using_known_primes: totient,
product_of_unknown_prime_factors: NEXT_OUTSIDE
})
);
}
}
}
#[cfg(feature = "zerocopy")]
#[test]
fn test_as_bytes() {
use zerocopy::IntoBytes;
const P: Primes<3> = Primes::new();
assert_eq!(P.as_bytes(), &[2, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0]);
}
#[cfg(feature = "serde")]
#[test]
fn test_serde() {
const P: Primes<3> = Primes::new();
const STRING_VERSION: &str = "[2,3,5]";
assert_eq!(serde_json::to_string(&P).unwrap(), STRING_VERSION);
assert_eq!(P, serde_json::from_str(STRING_VERSION).unwrap());
assert!(serde_json::from_str::<Primes<3>>("[2,3,4]").is_err());
}
}