prime_formula/prime/mod.rs
1//! Library for generating likely primes in the range 10^9 to 10^38
2//!
3//! The algorithm uses The Periodic Table of Primes to select candidates
4//! for prime numbers within the given range, and then in parallel runs
5//! a u128 optimized version of the Miller-Rabin tests to find likely primes
6//!
7//! For primes below 10^18 then a deterministic algorithm will be used
8//! which greatly increases the likelihood of finding true primes. Above
9//! this limit, the probabilistic variant of the algorithm will be used
10//! which may return pseudoprimes unless the number of iterations (k) is
11//! set sufficiently high.
12//
13// Copyright: Adam Cottrell (cottrela@gmail.com)
14
15use rayon::prelude::*;
16
17use crate::common::{PRIME_ROOTS, align_to_cycle};
18pub mod primality;
19
20/// Generate huge prime numbers using a primality test
21///
22/// Using primality tests is quicker for very large prime
23/// numbers, but considerably longer than a sieve for smaller
24/// prime numbers.
25
26#[derive(Debug, PartialEq)]
27pub enum PrimalityTest {
28 MillerRabinDeterministic,
29 MillerRabinProbabilistic,
30}
31
32/// Use the formula of primes to find large primes
33pub struct PrimeFormula {
34 k: u32, // Number of tests to use (limited to 48 in deterministic mode)
35 mode: PrimalityTest, // Primality test
36}
37
38impl PrimeFormula {
39 pub fn new(mode: PrimalityTest, k: u32) -> Self {
40 PrimeFormula { k, mode }
41 }
42
43 /// Get primes within the start/end range (inclusive)
44 ///
45 /// Note that primes below 11 will not be found using this
46 /// formula, and so must be manually added by the caller if
47 /// needed.
48 ///
49 /// This function returns the primes in order of their roots,
50 /// therefore may need sorting before use. Set get_primes.
51 pub fn get_unsorted_primes(&self, start: u128, end: u128) -> Vec<u128> {
52 let (b_start, b_end) = align_to_cycle(start, end);
53 let primes: Vec<u128> = PRIME_ROOTS
54 .into_par_iter()
55 .flat_map(|root| {
56 (b_start..b_end)
57 .map(|i| root as u128 + i * 210)
58 .filter(|p| *p >= start && *p <= end) // Filter by range
59 .filter(|p| {
60 primality::is_prime(
61 p,
62 self.k,
63 self.mode == PrimalityTest::MillerRabinDeterministic,
64 )
65 })
66 .collect::<Vec<u128>>() // Collect inner results
67 })
68 .collect(); // Collect parallel results
69
70 primes
71 }
72
73 /// Get primes within the start/end range (inclusive)
74 ///
75 /// Note that primes below 11 will not be found using this
76 /// formula, and so must be manually added by the caller if
77 /// needed.
78 ///
79 /// This function returns the primes sorted. To get the unsorted
80 /// primes, which is faster, use get_unsorted_primes.
81 pub fn get_primes(&self, start: u128, end: u128) -> Vec<u128> {
82 let mut primes = self.get_unsorted_primes(start, end);
83
84 primes.par_sort_unstable();
85 primes
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_deterministic_primes() {
95 let primes;
96 let inst = PrimeFormula::new(PrimalityTest::MillerRabinDeterministic, 2);
97
98 // Check the first million primes
99 primes = inst.get_primes(11, 1_000_000);
100 assert_eq!(primes.len(), 78498 - 4); // Number of primes below 1e6, excluding 2,3,5,7
101 assert_eq!(primes[0], 11); // First prime from root 11
102 assert_eq!(primes[78498 - 4 - 1], 999_983); // Last prime before 1e6
103 }
104
105 #[test]
106 fn test_probabilistic_primes() {
107 // Very low chance of failure for low order primes
108 let primes;
109 let inst = PrimeFormula::new(PrimalityTest::MillerRabinProbabilistic, 4);
110
111 // Check the first million primes
112 primes = inst.get_primes(11, 1_000);
113 assert_eq!(primes.len(), 168 - 4); // Number of primes below 1e6, excluding 2,3,5,7
114 assert_eq!(primes[0], 11); // First prime from root 11
115 assert_eq!(primes[168 - 4 - 1], 997); // Last prime before 1e6
116 }
117}