num_prime/
nt_funcs.rs

1//! Standalone number theoretic functions
2//!
3//! The functions in this module can be called without an instance of [crate::traits::PrimeBuffer].
4//! However, some functions do internally call the implementation on [PrimeBufferExt]
5//! (especially those dependent of integer factorization). For these functions, if you have
6//! to call them repeatedly, it's recommended to create a [crate::traits::PrimeBuffer]
7//! instance and use its associated methods for better performance.
8//!
9//! For number theoretic functions that depends on integer factorization, strongest primality
10//! check will be used in factorization, since for these functions we prefer correctness
11//! over speed.
12//!
13
14use crate::buffer::{NaiveBuffer, PrimeBufferExt};
15use crate::factor::{one_line, pollard_rho, squfof, SQUFOF_MULTIPLIERS};
16use crate::mint::SmallMint;
17use crate::primality::{PrimalityBase, PrimalityRefBase};
18use crate::tables::{
19    MOEBIUS_ODD, SMALL_PRIMES, SMALL_PRIMES_NEXT, WHEEL_NEXT, WHEEL_PREV,
20    WHEEL_SIZE,
21};
22#[cfg(feature = "big-table")]
23use crate::tables::{SMALL_PRIMES_INV, ZETA_LOG_TABLE};
24use crate::traits::{FactorizationConfig, Primality, PrimalityTestConfig, PrimalityUtils};
25use crate::{BitTest, ExactRoots};
26use num_integer::Roots;
27#[cfg(feature = "num-bigint")]
28use num_modular::DivExact;
29use num_modular::{ModularCoreOps, ModularInteger, MontgomeryInt};
30use num_traits::{CheckedAdd, FromPrimitive, Num, RefNum, ToPrimitive};
31use rand::random;
32use std::collections::BTreeMap;
33use std::convert::TryFrom;
34
35#[cfg(feature = "big-table")]
36use crate::tables::{MILLER_RABIN_BASE64, MILLER_RABIN_BASE32};
37
38/// Fast primality test on a u64 integer. It's based on
39/// deterministic Miller-rabin tests. If target is larger than 2^64 or more
40/// controlled primality tests are desired, please use [is_prime()]
41#[cfg(not(feature = "big-table"))]
42pub fn is_prime64(target: u64) -> bool {
43    // shortcuts
44    if target < 2 {
45        return false;
46    }
47    if target & 1 == 0 {
48        return target == 2;
49    }
50    if let Ok(u) = u8::try_from(target) {
51        // find in the prime list if the target is small enough
52        return SMALL_PRIMES.binary_search(&u).is_ok();
53    } else {
54        // check remainder against the wheel table
55        // this step eliminates any number that is not coprime to WHEEL_SIZE
56        let pos = (target % WHEEL_SIZE as u64) as usize;
57        if pos == 0 || WHEEL_NEXT[pos] < WHEEL_NEXT[pos - 1] {
58            return false;
59        }
60    }
61
62    // Then do a deterministic Miller-rabin test
63    is_prime64_miller(target)
64}
65
66/// Very fast primality test on a u64 integer is a prime number. It's based on
67/// deterministic Miller-rabin tests with hashing. if target is larger than 2^64 or more controlled
68/// primality tests are desired, please use [is_prime()]
69#[cfg(feature = "big-table")]
70pub fn is_prime64(target: u64) -> bool {
71    // shortcuts
72    if target < 2 {
73        return false;
74    }
75    if target & 1 == 0 {
76        return target == 2;
77    }
78
79    // remove small factors
80    if target < SMALL_PRIMES_NEXT {
81        // find in the prime list if the target is small enough
82        return SMALL_PRIMES.binary_search(&(target as u16)).is_ok();
83    } else {
84        // check remainder against the wheel table
85        // this step eliminates any number that is not coprime to WHEEL_SIZE
86        let pos = (target % WHEEL_SIZE as u64) as usize;
87        if pos == 0 || WHEEL_NEXT[pos] < WHEEL_NEXT[pos - 1] {
88            return false;
89        }
90    }
91
92    // Then do a deterministic Miller-rabin test
93    is_prime64_miller(target)
94}
95
96// Primality test for u64 with only miller-rabin tests, used during factorization.
97// It assumes the target is odd, not too small and cannot be divided small primes
98#[cfg(not(feature = "big-table"))]
99fn is_prime64_miller(target: u64) -> bool {
100    // The collection of witnesses are from http://miller-rabin.appspot.com/
101    if let Ok(u) = u32::try_from(target) {
102        const WITNESS32: [u32; 3] = [2, 7, 61];
103        let u = SmallMint::from(u);
104        WITNESS32.iter().all(|&x| u.is_sprp(SmallMint::from(x)))
105    } else {
106        const WITNESS64: [u64; 7] = [2, 325, 9375, 28178, 450775, 9780504, 1795265022];
107        let u = SmallMint::from(target);
108        WITNESS64.iter().all(|&x| u.is_sprp(SmallMint::from(x)))
109    }
110}
111
112#[cfg(feature = "big-table")]
113fn is_prime32_miller(target: u32) -> bool {
114    let h = target as u64;
115    let h = ((h >> 16) ^ h).wrapping_mul(0x45d9f3b);
116    let h = ((h >> 16) ^ h).wrapping_mul(0x45d9f3b);
117    let h = ((h >> 16) ^ h) & 255;
118    let u = SmallMint::from(target);
119    return u.is_sprp(SmallMint::from(MILLER_RABIN_BASE32[h as usize] as u32));
120}
121
122// Primality test for u64 with only miller-rabin tests, used during factorization.
123// It assumes the target is odd, not too small and cannot be divided small primes
124#[cfg(feature = "big-table")]
125fn is_prime64_miller(target: u64) -> bool {
126    if let Ok(u) = u32::try_from(target) {
127        return is_prime32_miller(u);
128    }
129
130    let u = SmallMint::from(target);
131    if !u.is_sprp(2.into()) {
132        return false;
133    }
134
135    let h = target;
136    let h = ((h >> 32) ^ h).wrapping_mul(0x45d9f3b3335b369);
137    let h = ((h >> 32) ^ h).wrapping_mul(0x3335b36945d9f3b);
138    let h = ((h >> 32) ^ h) & 16383;
139    let b = MILLER_RABIN_BASE64[h as usize];
140    return u.is_sprp((b as u64 & 4095).into()) && u.is_sprp((b as u64 >> 12).into());
141}
142
143/// Fast integer factorization on a u64 target. It's based on a selection of factorization methods.
144/// if target is larger than 2^128 or more controlled primality tests are desired, please use [factors()][crate::buffer::PrimeBufferExt::factors].
145///
146/// The factorization can be quite faster under 2^64 because: 1) faster and deterministic primality check,
147/// 2) efficient montgomery multiplication implementation of u64
148pub fn factorize64(target: u64) -> BTreeMap<u64, usize> {
149    // TODO: improve factorization performance
150    // REF: http://flintlib.org/doc/ulong_extras.html#factorisation
151    //      https://mathoverflow.net/questions/114018/fastest-way-to-factor-integers-260
152    //      https://hal.inria.fr/inria-00188645v3/document
153    //      https://github.com/coreutils/coreutils/blob/master/src/factor.c
154    //      https://github.com/uutils/coreutils/blob/master/src/uu/factor/src/cli.rs
155    //      https://github.com/elmomoilanen/prime-factorization
156    //      https://github.com/radii/msieve
157    //      https://github.com/zademn/facto-rs
158    //      Pari/GP: ifac_crack
159    let mut result = BTreeMap::new();
160
161    // quick check on factors of 2
162    let f2 = target.trailing_zeros();
163    if f2 == 0 {
164        if is_prime64(target) {
165            result.insert(target, 1);
166            return result;
167        }
168    } else {
169        result.insert(2, f2 as usize);
170    }
171
172    // trial division using primes in the table
173    let tsqrt = target.sqrt() + 1;
174
175    let mut residual = target >> f2;
176    let mut factored = false;
177
178    #[cfg(not(feature = "big-table"))]
179    for p in SMALL_PRIMES.iter().skip(1).map(|&v| v as u64) {
180        if p > tsqrt {
181            factored = true;
182            break;
183        }
184
185        while residual % p == 0 {
186            residual = residual / p;
187            *result.entry(p).or_insert(0) += 1;
188        }
189        if residual == 1 {
190            factored = true;
191            break;
192        }
193    }
194
195    #[cfg(feature = "big-table")]
196    // divisibility check with pre-computed tables, see comments on SMALL_PRIMES_INV for reference
197    for (p, &pinv) in SMALL_PRIMES
198        .iter()
199        .map(|&p| p as u64)
200        .zip(SMALL_PRIMES_INV.iter())
201        .skip(1)
202    {
203        // only need to test primes up to sqrt(target)
204        if p > tsqrt {
205            factored = true;
206            break;
207        }
208
209        let mut exp: usize = 0;
210        while let Some(q) = residual.div_exact(p, &pinv) {
211            exp += 1;
212            residual = q;
213        }
214        if exp > 0 {
215            result.insert(p, exp);
216        }
217
218        if residual == 1 {
219            factored = true;
220            break;
221        }
222    }
223
224    if factored {
225        if residual != 1 {
226            result.insert(residual, 1);
227        }
228        return result;
229    }
230
231    // then try advanced methods to find a divisor util fully factored
232    for (p, exp) in factorize64_advanced(&[(residual, 1usize)]).into_iter() {
233        *result.entry(p).or_insert(0) += exp;
234    }
235    result
236}
237
238// This function factorize all cofactors after some trivial division steps
239pub(crate) fn factorize64_advanced(cofactors: &[(u64, usize)]) -> Vec<(u64, usize)> {
240    let mut todo: Vec<_> = cofactors.iter().cloned().collect();
241    let mut factored: Vec<(u64, usize)> = Vec::new(); // prime factor, exponent
242
243    while let Some((target, exp)) = todo.pop() {
244        if is_prime64_miller(target) {
245            factored.push((target, exp));
246            continue;
247        }
248
249        // check perfect powers before other methods, this is required for SQUFOF
250        // it suffices to check square and cubic if big-table is enabled, since fifth power of
251        // the smallest prime that haven't been checked is 8167^5 > 2^64
252        if let Some(d) = target.sqrt_exact() {
253            todo.push((d, exp * 2));
254            continue;
255        }
256        if let Some(d) = target.cbrt_exact() {
257            todo.push((d, exp * 3));
258            continue;
259        }
260
261        // try to find a divisor
262        let mut i = 0usize;
263        let mut max_iter_ratio = 1; // increase max_iter after factorization round
264        let divisor = loop {
265            // try various factorization method iteratively
266            const NMETHODS: usize = 3;
267            match i % NMETHODS {
268                0 => {
269                    // Pollard's rho (quick check)
270                    let start = MontgomeryInt::new(random::<u64>(), &target);
271                    let offset = start.convert(random::<u64>());
272                    let max_iter = max_iter_ratio << (target.bits() / 6); // unoptimized heuristic
273                    if let (Some(p), _) = pollard_rho(
274                        &SmallMint::from(target),
275                        start.into(),
276                        offset.into(),
277                        max_iter,
278                    ) {
279                        break p.value();
280                    }
281                }
282                1 => {
283                    // Hart's one-line (quick check)
284                    let mul_target = target.checked_mul(480).unwrap_or(target);
285                    let max_iter = max_iter_ratio << (mul_target.bits() / 6); // unoptimized heuristic
286                    if let (Some(p), _) = one_line(&target, mul_target, max_iter) {
287                        break p;
288                    }
289                }
290                2 => {
291                    // Shanks's squfof (main power)
292                    let mut d = None;
293                    for &k in SQUFOF_MULTIPLIERS.iter() {
294                        if let Some(mul_target) = target.checked_mul(k as u64) {
295                            let max_iter = max_iter_ratio * 2 * mul_target.sqrt().sqrt() as usize;
296                            if let (Some(p), _) = squfof(&target, mul_target, max_iter) {
297                                d = Some(p);
298                                break;
299                            }
300                        }
301                    }
302                    if let Some(p) = d {
303                        break p;
304                    }
305                }
306                _ => unreachable!(),
307            }
308            i += 1;
309
310            // increase max iterations after trying all methods
311            if i % NMETHODS == 0 {
312                max_iter_ratio *= 2;
313            }
314        };
315        todo.push((divisor, exp));
316        todo.push((target / divisor, exp));
317    }
318    factored
319}
320
321/// Fast integer factorization on a u128 target. It's based on a selection of factorization methods.
322/// if target is larger than 2^128 or more controlled primality tests are desired, please use [factors()][crate::buffer::PrimeBufferExt::factors].
323pub fn factorize128(target: u128) -> BTreeMap<u128, usize> {
324    // shortcut for u64
325    if target < (1u128 << 64) {
326        return factorize64(target as u64)
327            .into_iter()
328            .map(|(k, v)| (k as u128, v))
329            .collect();
330    }
331
332    let mut result = BTreeMap::new();
333
334    // quick check on factors of 2
335    let f2 = target.trailing_zeros();
336    if f2 != 0 {
337        result.insert(2, f2 as usize);
338    }
339    let mut residual = target >> f2;
340
341    // trial division using primes in the table
342    // note that p^2 is never larger than target (at least 64 bits), so we don't need to shortcut trial division
343    #[cfg(not(feature = "big-table"))]
344    for p in SMALL_PRIMES.iter().skip(1).map(|&v| v as u128) {
345        while residual % p == 0 {
346            residual = residual / p;
347            *result.entry(p).or_insert(0) += 1;
348        }
349        if residual == 1 {
350            return result;
351        }
352    }
353
354    #[cfg(feature = "big-table")]
355    // divisibility check with pre-computed tables, see comments on SMALL_PRIMES_INV for reference
356    for (p, &pinv) in SMALL_PRIMES
357        .iter()
358        .map(|&p| p as u64)
359        .zip(SMALL_PRIMES_INV.iter())
360        .skip(1)
361    {
362        let mut exp: usize = 0;
363        while let Some(q) = residual.div_exact(p, &pinv) {
364            exp += 1;
365            residual = q;
366        }
367        if exp > 0 {
368            result.insert(p as u128, exp);
369        }
370
371        if residual == 1 {
372            return result;
373        }
374    }
375
376    // then try advanced methods to find a divisor util fully factored
377    for (p, exp) in factorize128_advanced(&[(residual, 1usize)]).into_iter() {
378        *result.entry(p).or_insert(0) += exp;
379    }
380    result
381}
382
383pub(crate) fn factorize128_advanced(cofactors: &[(u128, usize)]) -> Vec<(u128, usize)> {
384    let (mut todo128, mut todo64) = (Vec::new(), Vec::new()); // cofactors to be processed
385    let mut factored: Vec<(u128, usize)> = Vec::new(); // prime factor, exponent
386    for &(co, e) in cofactors.iter() {
387        if let Ok(co64) = u64::try_from(co) {
388            todo64.push((co64, e));
389        } else {
390            todo128.push((co, e));
391        };
392    }
393
394    while let Some((target, exp)) = todo128.pop() {
395        if is_prime(&SmallMint::from(target), Some(PrimalityTestConfig::bpsw())).probably() {
396            factored.push((target, exp));
397            continue;
398        }
399
400        // check perfect powers before other methods
401        // it suffices to check 2, 3, 5, 7 power if big-table is enabled, since tenth power of
402        // the smallest prime that haven't been checked is 8167^10 > 2^128
403        if let Some(d) = target.sqrt_exact() {
404            if let Ok(d64) = u64::try_from(d) {
405                todo64.push((d64, exp * 2));
406            } else {
407                todo128.push((d, exp * 2));
408            }
409            continue;
410        }
411        if let Some(d) = target.cbrt_exact() {
412            if let Ok(d64) = u64::try_from(d) {
413                todo64.push((d64, exp * 3));
414            } else {
415                todo128.push((d, exp * 3));
416            }
417            continue;
418        }
419        // TODO: check 5-th, 7-th power
420
421        // try to find a divisor
422        let mut i = 0usize;
423        let mut max_iter_ratio = 1;
424
425        let divisor = loop {
426            // try various factorization method iteratively, sort by time per iteration
427            const NMETHODS: usize = 3;
428            match i % NMETHODS {
429                0 => {
430                    // Pollard's rho
431                    let start = MontgomeryInt::new(random::<u128>(), &target);
432                    let offset = start.convert(random::<u128>());
433                    let max_iter = max_iter_ratio << (target.bits() / 6); // unoptimized heuristic
434                    if let (Some(p), _) = pollard_rho(
435                        &SmallMint::from(target),
436                        start.into(),
437                        offset.into(),
438                        max_iter,
439                    ) {
440                        break p.value();
441                    }
442                }
443                1 => {
444                    // Hart's one-line
445                    let mul_target = target.checked_mul(480).unwrap_or(target);
446                    let max_iter = max_iter_ratio << (mul_target.bits() / 6); // unoptimized heuristic
447                    if let (Some(p), _) = one_line(&target, mul_target, max_iter) {
448                        break p;
449                    }
450                }
451                2 => {
452                    // Shanks's squfof, try all mutipliers
453                    let mut d = None;
454                    for &k in SQUFOF_MULTIPLIERS.iter() {
455                        if let Some(mul_target) = target.checked_mul(k as u128) {
456                            let max_iter = max_iter_ratio * 2 * mul_target.sqrt().sqrt() as usize;
457                            if let (Some(p), _) = squfof(&target, mul_target, max_iter) {
458                                d = Some(p);
459                                break;
460                            }
461                        }
462                    }
463                    if let Some(p) = d {
464                        break p;
465                    }
466                }
467                _ => unreachable!(),
468            }
469            i += 1;
470
471            // increase max iterations after trying all methods
472            if i % NMETHODS == 0 {
473                max_iter_ratio *= 2;
474            }
475        };
476
477        if let Ok(d64) = u64::try_from(divisor) {
478            todo64.push((d64, exp));
479        } else {
480            todo128.push((divisor, exp));
481        }
482        let co = target / divisor;
483        if let Ok(d64) = u64::try_from(co) {
484            todo64.push((d64, exp));
485        } else {
486            todo128.push((co, exp));
487        }
488    }
489
490    // forward 64 bit cofactors
491    factored.extend(
492        factorize64_advanced(&todo64)
493            .into_iter()
494            .map(|(p, exp)| (p as u128, exp)),
495    );
496    factored
497}
498
499/// Primality test
500///
501/// This function re-exports [PrimeBufferExt::is_prime()][crate::buffer::PrimeBufferExt::is_prime()] with a new [NaiveBuffer] distance
502pub fn is_prime<T: PrimalityBase>(target: &T, config: Option<PrimalityTestConfig>) -> Primality
503where
504    for<'r> &'r T: PrimalityRefBase<T>,
505{
506    NaiveBuffer::new().is_prime(target, config)
507}
508
509/// Faillible factorization
510///
511/// This function re-exports [PrimeBufferExt::factors()][crate::buffer::PrimeBufferExt::factors()] with a new [NaiveBuffer] instance
512pub fn factors<T: PrimalityBase>(
513    target: T,
514    config: Option<FactorizationConfig>,
515) -> (BTreeMap<T, usize>, Option<Vec<T>>)
516where
517    for<'r> &'r T: PrimalityRefBase<T>,
518{
519    NaiveBuffer::new().factors(target, config)
520}
521
522/// Infaillible factorization
523///
524/// This function re-exports [PrimeBufferExt::factorize()][crate::buffer::PrimeBufferExt::factorize()] with a new [NaiveBuffer] instance
525pub fn factorize<T: PrimalityBase>(target: T) -> BTreeMap<T, usize>
526where
527    for<'r> &'r T: PrimalityRefBase<T>,
528{
529    NaiveBuffer::new().factorize(target)
530}
531
532/// Get a list of primes under a limit
533///
534/// This function re-exports [NaiveBuffer::primes()] and collect result as a vector.
535pub fn primes(limit: u64) -> Vec<u64> {
536    NaiveBuffer::new().into_primes(limit).collect()
537}
538
539/// Get the first n primes
540///
541/// This function re-exports [NaiveBuffer::nprimes()] and collect result as a vector.
542pub fn nprimes(count: usize) -> Vec<u64> {
543    NaiveBuffer::new().into_nprimes(count).collect()
544}
545
546/// Calculate and return the prime π function
547///
548/// This function re-exports [NaiveBuffer::prime_pi()]
549pub fn prime_pi(limit: u64) -> u64 {
550    NaiveBuffer::new().prime_pi(limit)
551}
552
553/// Get the n-th prime (n counts from 1).
554///
555/// This function re-exports [NaiveBuffer::nth_prime()]
556pub fn nth_prime(n: u64) -> u64 {
557    NaiveBuffer::new().nth_prime(n)
558}
559
560/// Calculate the primorial function
561pub fn primorial<T: PrimalityBase + std::iter::Product>(n: usize) -> T {
562    NaiveBuffer::new()
563        .into_nprimes(n)
564        .map(|p| T::from_u64(p).unwrap())
565        .product()
566}
567
568/// This function calculate the Möbius `μ(n)` function of the input integer `n`
569///
570/// This function behaves like `moebius_factorized(factorize(target))`.
571/// If the input integer is very hard to factorize, it's better to use
572/// the [factors()] function to control how the factorization is done, and then call
573/// [moebius_factorized()].
574///
575/// # Panics
576/// if the factorization failed on target.
577pub fn moebius<T: PrimalityBase>(target: &T) -> i8
578where
579    for<'r> &'r T: PrimalityRefBase<T>,
580{
581    // remove factor 2
582    if target.is_even() {
583        let two = T::one() + T::one();
584        let four = &two + &two;
585        if (target % four).is_zero() {
586            return 0;
587        } else {
588            return -moebius(&(target / &two));
589        }
590    }
591
592    // look up tables when input is smaller than 256
593    if let Some(v) = (target - T::one()).to_u8() {
594        let m = MOEBIUS_ODD[(v >> 6) as usize];
595        let m = m & (3 << (v & 63));
596        let m = m >> (v & 63);
597        return m as i8 - 1;
598    }
599
600    // short cut for common primes
601    let three_sq = T::from_u8(9).unwrap();
602    let five_sq = T::from_u8(25).unwrap();
603    let seven_sq = T::from_u8(49).unwrap();
604    if (target % three_sq).is_zero()
605        || (target % five_sq).is_zero()
606        || (target % seven_sq).is_zero()
607    {
608        return 0;
609    }
610
611    // then try complete factorization
612    moebius_factorized(&factorize(target.clone()))
613}
614
615/// This function calculate the Möbius `μ(n)` function given the factorization
616/// result of `n`
617pub fn moebius_factorized<T>(factors: &BTreeMap<T, usize>) -> i8 {
618    if factors.values().any(|exp| exp > &1) {
619        0
620    } else if factors.len() % 2 == 0 {
621        1
622    } else {
623        -1
624    }
625}
626
627/// Tests if the integer doesn't have any square number factor.
628///
629/// # Panics
630/// if the factorization failed on target.
631pub fn is_square_free<T: PrimalityBase>(target: &T) -> bool
632where
633    for<'r> &'r T: PrimalityRefBase<T>,
634{
635    moebius(target) != 0
636}
637
638/// Returns the estimated bounds (low, high) of prime π function, such that
639/// low <= π(target) <= high
640///
641/// # Reference:
642/// - \[1] Dusart, Pierre. "Estimates of Some Functions Over Primes without R.H."
643/// [arxiv:1002.0442](http://arxiv.org/abs/1002.0442). 2010.
644/// - \[2] Dusart, Pierre. "Explicit estimates of some functions over primes."
645/// The Ramanujan Journal 45.1 (2018): 227-251.
646pub fn prime_pi_bounds<T: ToPrimitive + FromPrimitive>(target: &T) -> (T, T) {
647    if let Some(x) = target.to_u64() {
648        // use existing primes and return exact value
649        if x <= (*SMALL_PRIMES.last().unwrap()) as u64 {
650            #[cfg(not(feature = "big-table"))]
651            let pos = SMALL_PRIMES.binary_search(&(x as u8));
652            #[cfg(feature = "big-table")]
653            let pos = SMALL_PRIMES.binary_search(&(x as u16));
654
655            let n = match pos {
656                Ok(p) => p + 1,
657                Err(p) => p,
658            };
659            return (T::from_usize(n).unwrap(), T::from_usize(n).unwrap());
660        }
661
662        // use function approximation
663        let n = x as f64;
664        let ln = n.ln();
665        let invln = ln.recip();
666
667        let lo = match () {
668            // [2] Collary 5.3
669            _ if x >= 468049 => n / (ln - 1. - invln),
670            // [2] Collary 5.2
671            _ if x >= 88789 => n * invln * (1. + invln * (1. + 2. * invln)),
672            // [2] Collary 5.3
673            _ if x >= 5393 => n / (ln - 1.),
674            // [2] Collary 5.2
675            _ if x >= 599 => n * invln * (1. + invln),
676            // [2] Collary 5.2
677            _ => n * invln,
678        };
679        let hi = match () {
680            // [2] Theorem 5.1, valid for x > 4e9, intersects at 7.3986e9
681            _ if x >= 7398600000 => n * invln * (1. + invln * (1. + invln * (2. + invln * 7.59))),
682            // [1] Theorem 6.9
683            _ if x >= 2953652287 => n * invln * (1. + invln * (1. + invln * 2.334)),
684            // [2] Collary 5.3, valid for x > 5.6, intersects at 5668
685            _ if x >= 467345 => n / (ln - 1. - 1.2311 * invln),
686            // [2] Collary 5.2, valid for x > 1, intersects at 29927
687            _ if x >= 29927 => n * invln * (1. + invln * (1. + invln * 2.53816)),
688            // [2] Collary 5.3, valid for x > exp(1.112), intersects at 5668
689            _ if x >= 5668 => n / (ln - 1.112),
690            // [2] Collary 5.2, valid for x > 1, intersects at 148
691            _ if x >= 148 => n * invln * (1. + invln * 1.2762),
692            // [2] Collary 5.2, valid for x > 1
693            _ => 1.25506 * n * invln,
694        };
695        (T::from_f64(lo).unwrap(), T::from_f64(hi).unwrap())
696    } else {
697        let n = target.to_f64().unwrap();
698        let ln = n.ln();
699        let invln = ln.recip();
700
701        // best bounds so far
702        let lo = n / (ln - 1. - invln);
703        let hi = n * invln * (1. + invln * (1. + invln * (2. + invln * 7.59)));
704        (T::from_f64(lo).unwrap(), T::from_f64(hi).unwrap())
705    }
706}
707
708/// Returns the estimated inclusive bounds (low, high) of the n-th prime. If the result
709/// is larger than maximum of `T`, [None] will be returned.
710///
711/// # Reference:
712/// - \[1] Dusart, Pierre. "Estimates of Some Functions Over Primes without R.H."
713/// arXiv preprint [arXiv:1002.0442](https://arxiv.org/abs/1002.0442) (2010).
714/// - \[2] Rosser, J. Barkley, and Lowell Schoenfeld. "Approximate formulas for some
715/// functions of prime numbers." Illinois Journal of Mathematics 6.1 (1962): 64-94.
716/// - \[3] Dusart, Pierre. "The k th prime is greater than k (ln k+ ln ln k-1) for k≥ 2."
717/// Mathematics of computation (1999): 411-415.
718/// - \[4] Axler, Christian. ["New Estimates for the nth Prime Number."](https://www.emis.de/journals/JIS/VOL22/Axler/axler17.pdf)
719/// Journal of Integer Sequences 22.2 (2019): 3.
720/// - \[5] Axler, Christian. [Uber die Primzahl-Zählfunktion, die n-te Primzahl und verallgemeinerte Ramanujan-Primzahlen. Diss.](http://docserv.uniduesseldorf.de/servlets/DerivateServlet/Derivate-28284/pdfa-1b.pdf)
721/// PhD thesis, Düsseldorf, 2013.
722///
723/// Note that some of the results might depend on the Riemann Hypothesis. If you find
724/// any prime that doesn't fall in the bound, then it might be a big discovery!
725pub fn nth_prime_bounds<T: ToPrimitive + FromPrimitive>(target: &T) -> Option<(T, T)> {
726    if let Some(x) = target.to_usize() {
727        if x == 0 {
728            return Some((T::from_u8(0).unwrap(), T::from_u8(0).unwrap()));
729        }
730
731        // use existing primes and return exact value
732        if x <= SMALL_PRIMES.len() {
733            let p = SMALL_PRIMES[x - 1];
734
735            #[cfg(not(feature = "big-table"))]
736            return Some((T::from_u8(p).unwrap(), T::from_u8(p).unwrap()));
737
738            #[cfg(feature = "big-table")]
739            return Some((T::from_u16(p).unwrap(), T::from_u16(p).unwrap()));
740        }
741
742        // use function approximation
743        let n = x as f64;
744        let ln = n.ln();
745        let lnln = ln.ln();
746
747        let lo = match () {
748            // [4] Theroem 4, valid for x >= 2, intersects as 3.172e5
749            _ if x >= 317200 => {
750                n * (ln + lnln - 1. + (lnln - 2.) / ln
751                    - (lnln * lnln - 6. * lnln + 11.321) / (2. * ln * ln))
752            }
753            // [1] Proposition 6.7, valid for x >= 3, intersects at 3520
754            _ if x >= 3520 => n * (ln + lnln - 1. + (lnln - 2.1) / ln),
755            // [3] title
756            _ => n * (ln + lnln - 1.),
757        };
758        let hi = match () {
759            // [4] Theroem 1, valid for x >= 46254381
760            _ if x >= 46254381 => {
761                n * (ln + lnln - 1. + (lnln - 2.) / ln
762                    - (lnln * lnln - 6. * lnln + 10.667) / (2. * ln * ln))
763            }
764            // [5] Korollar 2.11, valid for x >= 8009824
765            _ if x >= 8009824 => {
766                n * (ln + lnln - 1. + (lnln - 2.) / ln
767                    - (lnln * lnln - 6. * lnln + 10.273) / (2. * ln * ln))
768            }
769            // [1] Proposition 6.6
770            _ if x >= 688383 => n * (ln + lnln - 1. + (lnln - 2.) / ln),
771            // [1] Lemma 6.5
772            _ if x >= 178974 => n * (ln + lnln - 1. + (lnln - 1.95) / ln),
773            // [3] in "Further Results"
774            _ if x >= 39017 => n * (ln + lnln - 0.9484),
775            // [3] in "Further Results"
776            _ if x >= 27076 => n * (ln + lnln - 1. + (lnln - 1.8) / ln),
777            // [2] Theorem 3, valid for x >= 20
778            _ => n * (ln + lnln - 0.5),
779        };
780        Some((T::from_f64(lo)?, T::from_f64(hi)?))
781    } else {
782        let n = target.to_f64().unwrap();
783        let ln = n.ln();
784        let lnln = ln.ln();
785
786        // best bounds so far
787        let lo = n
788            * (ln + lnln - 1. + (lnln - 2.) / ln
789                - (lnln * lnln - 6. * lnln + 11.321) / (2. * ln * ln));
790        let hi = n
791            * (ln + lnln - 1. + (lnln - 2.) / ln
792                - (lnln * lnln - 6. * lnln + 10.667) / (2. * ln * ln));
793        Some((T::from_f64(lo)?, T::from_f64(hi)?))
794    }
795}
796
797/// Test if the target is a safe prime under [Sophie German's definition](https://en.wikipedia.org/wiki/Safe_and_Sophie_Germain_primes). It will use the
798/// [strict primality test configuration][FactorizationConfig::strict()].
799pub fn is_safe_prime<T: PrimalityBase>(target: &T) -> Primality
800where
801    for<'r> &'r T: PrimalityRefBase<T>,
802{
803    let buf = NaiveBuffer::new();
804    let config = Some(PrimalityTestConfig::strict());
805
806    // test (n-1)/2 first since its smaller
807    let sophie_p = buf.is_prime(&(target >> 1), config);
808    if matches!(sophie_p, Primality::No) {
809        return sophie_p;
810    }
811
812    // and then test target itself
813    let target_p = buf.is_prime(target, config);
814    target_p & sophie_p
815}
816
817/// Find the first prime number larger than `target`. If the result causes an overflow,
818/// then [None] will be returned
819#[cfg(not(feature = "big-table"))]
820pub fn next_prime<T: PrimalityBase + CheckedAdd>(
821    target: &T,
822    config: Option<PrimalityTestConfig>,
823) -> Option<T>
824where
825    for<'r> &'r T: PrimalityRefBase<T>,
826{
827    // first search in small primes
828    if let Some(x) = target.to_u8() {
829        return match SMALL_PRIMES.binary_search(&x) {
830            Ok(pos) => {
831                if pos + 1 == SMALL_PRIMES.len() {
832                    T::from_u64(SMALL_PRIMES_NEXT)
833                } else {
834                    T::from_u8(SMALL_PRIMES[pos + 1])
835                }
836            }
837            Err(pos) => T::from_u8(SMALL_PRIMES[pos]),
838        };
839    }
840
841    // then moving along the wheel
842    let mut i = (target % T::from_u8(WHEEL_SIZE).unwrap()).to_u8().unwrap();
843    let mut t = target.clone();
844    loop {
845        let offset = WHEEL_NEXT[i as usize];
846        t = t.checked_add(&T::from_u8(offset).unwrap())?;
847        i = i.addm(offset, &WHEEL_SIZE);
848        if is_prime(&t, config).probably() {
849            break Some(t);
850        }
851    }
852}
853
854/// Find the first prime number larger than `target`. If the result causes an overflow,
855/// then [None] will be returned
856#[cfg(feature = "big-table")]
857pub fn next_prime<T: PrimalityBase + CheckedAdd>(
858    target: &T,
859    config: Option<PrimalityTestConfig>,
860) -> Option<T>
861where
862    for<'r> &'r T: PrimalityRefBase<T>,
863{
864    // first search in small primes
865    if target <= &T::from_u8(255).unwrap() // shortcut for T=u8
866        || target < &T::from_u16(*SMALL_PRIMES.last().unwrap()).unwrap()
867    {
868        let next = match SMALL_PRIMES.binary_search(&target.to_u16().unwrap()) {
869            Ok(pos) => SMALL_PRIMES[pos + 1],
870            Err(pos) => SMALL_PRIMES[pos],
871        };
872        return T::from_u16(next);
873    }
874
875    // then moving along the wheel
876    let mut i = (target % T::from_u16(WHEEL_SIZE).unwrap())
877        .to_u16()
878        .unwrap();
879    let mut t = target.clone();
880    loop {
881        let offset = WHEEL_NEXT[i as usize];
882        t = t.checked_add(&T::from_u8(offset).unwrap())?;
883        i = i.addm(offset as u16, &WHEEL_SIZE);
884        if is_prime(&t, config).probably() {
885            break Some(t);
886        }
887    }
888}
889
890/// Find the first prime number smaller than `target`. If target is less than 3, then [None]
891/// will be returned.
892#[cfg(not(feature = "big-table"))]
893pub fn prev_prime<T: PrimalityBase>(target: &T, config: Option<PrimalityTestConfig>) -> Option<T>
894where
895    for<'r> &'r T: PrimalityRefBase<T>,
896{
897    if target <= &(T::one() + T::one()) {
898        return None;
899    }
900
901    // first search in small primes
902    if let Some(x) = target.to_u8() {
903        let next = match SMALL_PRIMES.binary_search(&x) {
904            Ok(pos) => SMALL_PRIMES[pos - 1],
905            Err(pos) => SMALL_PRIMES[pos - 1],
906        };
907        return Some(T::from_u8(next).unwrap());
908    }
909
910    // then moving along the wheel
911    let mut i = (target % T::from_u8(WHEEL_SIZE).unwrap()).to_u8().unwrap();
912    let mut t = target.clone();
913    loop {
914        let offset = WHEEL_PREV[i as usize];
915        t = t - T::from_u8(offset).unwrap();
916        i = i.subm(offset, &WHEEL_SIZE);
917        if is_prime(&t, config).probably() {
918            break Some(t);
919        }
920    }
921}
922
923/// Find the first prime number smaller than `target`. If target is less than 3, then [None]
924/// will be returned.
925#[cfg(feature = "big-table")]
926pub fn prev_prime<T: PrimalityBase>(target: &T, config: Option<PrimalityTestConfig>) -> Option<T>
927where
928    for<'r> &'r T: PrimalityRefBase<T>,
929{
930    if target <= &(T::one() + T::one()) {
931        return None;
932    }
933
934    // first search in small primes
935    if target <= &T::from_u8(255).unwrap() // shortcut for u8
936        || target < &T::from_u16(*SMALL_PRIMES.last().unwrap()).unwrap()
937    {
938        let next = match SMALL_PRIMES.binary_search(&target.to_u16().unwrap()) {
939            Ok(pos) => SMALL_PRIMES[pos - 1],
940            Err(pos) => SMALL_PRIMES[pos - 1],
941        };
942        return Some(T::from_u16(next).unwrap());
943    }
944
945    // then moving along the wheel
946    let mut i = (target % T::from_u16(WHEEL_SIZE).unwrap())
947        .to_u16()
948        .unwrap();
949    let mut t = target.clone();
950    loop {
951        let offset = WHEEL_PREV[i as usize];
952        t = t - T::from_u8(offset).unwrap();
953        i = i.subm(offset as u16, &WHEEL_SIZE);
954        if is_prime(&t, config).probably() {
955            break Some(t);
956        }
957    }
958}
959
960/// Estimate the value of prime π() function by averaging the estimated bounds.
961#[cfg(not(feature = "big-table"))]
962pub fn prime_pi_est<T: Num + ToPrimitive + FromPrimitive>(target: &T) -> T {
963    let (lo, hi) = prime_pi_bounds(target);
964    (lo + hi) / T::from_u8(2).unwrap()
965}
966
967/// Estimate the value of prime π() function by Riemann's R function. The estimation
968/// error is roughly of scale O(sqrt(x)log(x)).
969///
970/// Reference: <https://primes.utm.edu/howmany.html#better>
971#[cfg(feature = "big-table")]
972pub fn prime_pi_est<T: ToPrimitive + FromPrimitive>(target: &T) -> T {
973    // shortcut
974    if let Some(x) = target.to_u16() {
975        if x <= (*SMALL_PRIMES.last().unwrap()) as u16 {
976            let (lo, hi) = prime_pi_bounds(&x);
977            debug_assert_eq!(lo, hi);
978            return T::from_u16(lo).unwrap();
979        }
980    }
981
982    // Gram expansion with logarithm arithmetics
983    let lnln = target.to_f64().unwrap().ln().ln();
984    let mut total = 0f64;
985    let mut lnp = 0f64; // k*ln(ln(x))
986    let mut lnfac = 0f64; // ln(k!)
987
988    for k in 1usize..100 {
989        lnp += lnln;
990        let lnk = (k as f64).ln();
991        lnfac += lnk;
992        let lnzeta = if k > 64 { 0f64 } else { ZETA_LOG_TABLE[k - 1] };
993        let t = lnp - lnk - lnfac - lnzeta;
994        if t < -4. {
995            // stop if the increment is too small
996            break;
997        }
998        total += t.exp();
999    }
1000    T::from_f64(total + 1f64).unwrap()
1001}
1002
1003/// Estimate the value of nth prime by bisecting on [prime_pi_est].
1004/// If the result is larger than maximum of `T`, [None] will be returned.
1005pub fn nth_prime_est<T: ToPrimitive + FromPrimitive + Num + PartialOrd>(target: &T) -> Option<T>
1006where
1007    for<'r> &'r T: RefNum<T>,
1008{
1009    let (mut lo, mut hi) = nth_prime_bounds(target)?;
1010    if lo == hi {
1011        return Some(lo);
1012    }
1013
1014    while lo != &hi - T::from_u8(1).unwrap() {
1015        let x = (&lo + &hi) / T::from_u8(2).unwrap();
1016        let mid = prime_pi_est(&x);
1017        if &mid < target {
1018            lo = x
1019        } else if &mid > target {
1020            hi = x
1021        } else {
1022            return Some(x);
1023        }
1024    }
1025    return Some(lo);
1026}
1027
1028// TODO: More functions
1029// REF: http://www.numbertheory.org/gnubc/bc_programs.html
1030// REF: https://github.com/TilmanNeumann/java-math-library
1031// - is_smooth: checks if the smoothness bound is at least b
1032// - euler_phi: Euler's totient function
1033// - jordan_tot: Jordan's totient function
1034// Others include Louiville function, Mangoldt function, Dedekind psi function, Dickman rho function, etc..
1035
1036#[cfg(test)]
1037mod tests {
1038    use super::*;
1039    use rand::{prelude::SliceRandom, random};
1040    use std::iter::FromIterator;
1041
1042    #[test]
1043    fn is_prime64_test() {
1044        // test small primes
1045        for x in 2..100 {
1046            assert_eq!(SMALL_PRIMES.contains(&x), is_prime64(x as u64));
1047        }
1048        assert!(is_prime64(677));
1049        
1050        // from PR #7
1051        assert!(!is_prime64(9773));
1052        assert!(!is_prime64(13357));
1053        assert!(!is_prime64(18769));
1054
1055        // some large primes
1056        assert!(is_prime64(6469693333));
1057        assert!(is_prime64(13756265695458089029));
1058        assert!(is_prime64(13496181268022124907));
1059        assert!(is_prime64(10953742525620032441));
1060        assert!(is_prime64(17908251027575790097));
1061
1062        // primes from examples in Bradley Berg's hash method
1063        assert!(is_prime64(480194653));
1064        assert!(!is_prime64(20074069));
1065        assert!(is_prime64(8718775377449));
1066        assert!(is_prime64(3315293452192821991));
1067        assert!(!is_prime64(8651776913431));
1068        assert!(!is_prime64(1152965996591997761));
1069
1070        // false positives reported by JASory (#4)
1071        assert!(!is_prime64(600437059821397));
1072        assert!(!is_prime64(3866032210719337));
1073        assert!(!is_prime64(4100599722623587));
1074
1075        // ensure no factor for 100 random primes
1076        let mut rng = rand::thread_rng();
1077        for _ in 0..100 {
1078            let x = random();
1079            if !is_prime64(x) {
1080                continue;
1081            }
1082            assert_ne!(x % (*SMALL_PRIMES.choose(&mut rng).unwrap() as u64), 0);
1083        }
1084
1085        // create random composites
1086        for _ in 0..100 {
1087            let x = random::<u32>() as u64;
1088            let y = random::<u32>() as u64;
1089            assert!(!is_prime64(x * y));
1090        }
1091    }
1092
1093    #[test]
1094    fn factorize64_test() {
1095        // some simple cases
1096        let fac4095 = BTreeMap::from_iter([(3, 2), (5, 1), (7, 1), (13, 1)]);
1097        let fac = factorize64(4095);
1098        assert_eq!(fac, fac4095);
1099
1100        let fac123456789 = BTreeMap::from_iter([(3, 2), (3803, 1), (3607, 1)]);
1101        let fac = factorize64(123456789);
1102        assert_eq!(fac, fac123456789);
1103
1104        let fac1_17 = BTreeMap::from_iter([(2071723, 1), (5363222357, 1)]);
1105        let fac = factorize64(11111111111111111);
1106        assert_eq!(fac, fac1_17);
1107
1108        // perfect powers
1109        for exp in 2u32..5 {
1110            assert_eq!(
1111                factorize128(8167u128.pow(exp)),
1112                BTreeMap::from_iter([(8167, exp as usize)])
1113            );
1114        }
1115
1116        // 100 random factorization tests
1117        for _ in 0..100 {
1118            let x = random();
1119            let fac = factorize64(x);
1120            let mut prod = 1;
1121            for (p, exp) in fac {
1122                assert!(
1123                    is_prime64(p),
1124                    "factorization result should have prime factors! (get {})",
1125                    p
1126                );
1127                prod *= p.pow(exp as u32);
1128            }
1129            assert_eq!(x, prod, "factorization check failed! ({} != {})", x, prod);
1130        }
1131    }
1132
1133    #[test]
1134    fn factorize128_test() {
1135        // some simple cases
1136        let fac_primorial19 =
1137            BTreeMap::from_iter(SMALL_PRIMES.iter().take(19).map(|&p| (p as u128, 1)));
1138        let fac = factorize128(7858321551080267055879090);
1139        assert_eq!(fac, fac_primorial19);
1140
1141        let fac_smallbig = BTreeMap::from_iter([(167, 1), (2417851639229258349412369, 1)]);
1142        let fac = factorize128(403781223751286144351865623);
1143        assert_eq!(fac, fac_smallbig);
1144
1145        // perfect powers
1146        for exp in 5u32..10 {
1147            // 2^64 < 8167^5 < 8167^9 < 2^128
1148            assert_eq!(
1149                factorize128(8167u128.pow(exp)),
1150                BTreeMap::from_iter([(8167, exp as usize)])
1151            );
1152        }
1153
1154        // random factorization tests
1155        for _ in 0..4 {
1156            let x = random::<u128>() >> 28; // test 100 bit numbers
1157            let fac = factorize128(x);
1158            let mut prod = 1;
1159            for (p, exp) in fac {
1160                assert!(
1161                    is_prime(&p, None).probably(),
1162                    "factorization result should have prime factors! (get {})",
1163                    p
1164                );
1165                prod *= p.pow(exp as u32);
1166            }
1167            assert_eq!(x, prod, "factorization check failed! ({} != {})", x, prod);
1168        }
1169    }
1170
1171    #[test]
1172    fn is_safe_prime_test() {
1173        // OEIS:A005385
1174        let safe_primes = [
1175            5u16, 7, 11, 23, 47, 59, 83, 107, 167, 179, 227, 263, 347, 359, 383, 467, 479, 503,
1176            563, 587, 719, 839, 863, 887, 983, 1019, 1187, 1283, 1307, 1319, 1367, 1439, 1487,
1177            1523, 1619, 1823, 1907,
1178        ];
1179        for p in SMALL_PRIMES {
1180            let p = p as u16;
1181            if p > 1500 {
1182                break;
1183            }
1184            assert_eq!(
1185                is_safe_prime(&p).probably(),
1186                safe_primes.iter().find(|&v| &p == v).is_some()
1187            );
1188        }
1189    }
1190
1191    #[test]
1192    fn moebius_test() {
1193        // test small examples
1194        let mu20: [i8; 20] = [
1195            1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0, -1, 0,
1196        ];
1197        for i in 0..20 {
1198            assert_eq!(moebius(&(i + 1)), mu20[i], "moebius on {}", i);
1199        }
1200
1201        // some square numbers
1202        assert_eq!(moebius(&1024u32), 0);
1203        assert_eq!(moebius(&(8081u32 * 8081)), 0);
1204
1205        // sphenic numbers
1206        let sphenic3: [u8; 20] = [
1207            30, 42, 66, 70, 78, 102, 105, 110, 114, 130, 138, 154, 165, 170, 174, 182, 186, 190,
1208            195, 222,
1209        ]; // OEIS:A007304
1210        for i in 0..20 {
1211            assert_eq!(moebius(&sphenic3[i]), -1i8, "moebius on {}", sphenic3[i]);
1212        }
1213        let sphenic5: [u16; 23] = [
1214            2310, 2730, 3570, 3990, 4290, 4830, 5610, 6006, 6090, 6270, 6510, 6630, 7410, 7590,
1215            7770, 7854, 8610, 8778, 8970, 9030, 9282, 9570, 9690,
1216        ]; // OEIS:A046387
1217        for i in 0..20 {
1218            assert_eq!(moebius(&sphenic5[i]), -1i8, "moebius on {}", sphenic5[i]);
1219        }
1220    }
1221
1222    #[test]
1223    fn prime_pi_bounds_test() {
1224        fn check(n: u64, pi: u64) {
1225            let (lo, hi) = prime_pi_bounds(&n);
1226            let est = prime_pi_est(&n);
1227            assert!(
1228                lo <= pi && pi <= hi,
1229                "fail to satisfy {} <= pi({}) = {} <= {}",
1230                lo,
1231                n,
1232                pi,
1233                hi
1234            );
1235            assert!(lo <= est && est <= hi);
1236        }
1237
1238        // test with sieved primes
1239        let mut pb = NaiveBuffer::new();
1240        let mut last = 0;
1241        for (i, p) in pb.primes(100000).cloned().enumerate() {
1242            for j in last..p {
1243                check(j, i as u64);
1244            }
1245            last = p;
1246        }
1247
1248        // test with some known cases with input as 10^n, OEIS:A006880
1249        let pow10_values = [
1250            0,
1251            4,
1252            25,
1253            168,
1254            1229,
1255            9592,
1256            78498,
1257            664579,
1258            5761455,
1259            50847534,
1260            455052511,
1261            4118054813,
1262            37607912018,
1263            346065536839,
1264            3204941750802,
1265            29844570422669,
1266            279238341033925,
1267            2623557157654233,
1268        ];
1269        for (exponent, gt) in pow10_values.iter().enumerate() {
1270            let n = 10u64.pow(exponent as u32);
1271            check(n, *gt);
1272        }
1273    }
1274
1275    #[test]
1276    fn nth_prime_bounds_test() {
1277        fn check(n: u64, p: u64) {
1278            let (lo, hi) = super::nth_prime_bounds(&n).unwrap();
1279            assert!(
1280                lo <= p && p <= hi,
1281                "fail to satisfy: {} <= {}-th prime = {} <= {}",
1282                lo,
1283                n,
1284                p,
1285                hi
1286            );
1287            let est = super::nth_prime_est(&n).unwrap();
1288            assert!(lo <= est && est <= hi);
1289        }
1290
1291        // test with sieved primes
1292        let mut pb = NaiveBuffer::new();
1293        for (i, p) in pb.primes(100000).cloned().enumerate() {
1294            check(i as u64 + 1, p as u64);
1295        }
1296
1297        // test with some known cases with input as 10^n, OEIS:A006988
1298        let pow10_values = [
1299            2,
1300            29,
1301            541,
1302            7919,
1303            104729,
1304            1299709,
1305            15485863,
1306            179424673,
1307            2038074743,
1308            22801763489,
1309            252097800623,
1310            2760727302517,
1311            29996224275833,
1312            323780508946331,
1313            3475385758524527,
1314            37124508045065437,
1315        ];
1316        for (exponent, nth_prime) in pow10_values.iter().enumerate() {
1317            let n = 10u64.pow(exponent as u32);
1318            check(n, *nth_prime);
1319        }
1320    }
1321
1322    #[test]
1323    fn prev_next_test() {
1324        assert_eq!(prev_prime(&2u32, None), None);
1325
1326        // prime table boundary test
1327        assert_eq!(prev_prime(&257u16, None), Some(251));
1328        assert_eq!(next_prime(&251u16, None), Some(257));
1329        assert_eq!(next_prime(&251u8, None), None);
1330        assert_eq!(prev_prime(&8167u16, None), Some(8161));
1331        assert_eq!(next_prime(&8161u16, None), Some(8167));
1332
1333        // OEIS:A077800
1334        let twine_primes: [(u32, u32); 8] = [
1335            (2, 3), // not exactly twine
1336            (3, 5),
1337            (5, 7),
1338            (11, 13),
1339            (17, 19),
1340            (29, 31),
1341            (41, 43),
1342            (617, 619),
1343        ];
1344        for (p1, p2) in twine_primes {
1345            assert_eq!(prev_prime(&p2, None).unwrap(), p1);
1346            assert_eq!(next_prime(&p1, None).unwrap(), p2);
1347        }
1348
1349        let adj10_primes: [(u32, u32); 7] = [
1350            (7, 11),
1351            (97, 101),
1352            (997, 1009),
1353            (9973, 10007),
1354            (99991, 100003),
1355            (999983, 1000003),
1356            (9999991, 10000019),
1357        ];
1358        for (i, (p1, p2)) in adj10_primes.iter().enumerate() {
1359            assert_eq!(prev_prime(p2, None).unwrap(), *p1);
1360            assert_eq!(next_prime(p1, None).unwrap(), *p2);
1361
1362            let pow = 10u32.pow((i + 1) as u32);
1363            assert_eq!(prev_prime(&pow, None).unwrap(), *p1);
1364            assert_eq!(next_prime(&pow, None).unwrap(), *p2);
1365        }
1366    }
1367}