Skip to main content

num_prime/
buffer.rs

1//! Implementations and extensions for [`PrimeBuffer`], which represents a container of primes
2//!
3//! In `num-prime`, there is no global instance to store primes, the user has to generate
4//! and store the primes themselves. The trait [`PrimeBuffer`] defines a unified interface
5//! for a prime number container. Some methods that can take advantage of pre-generated
6//! primes will be implemented in the [`PrimeBufferExt`] trait.
7//!
8//! We also provide [`NaiveBuffer`] as a simple implementation of [`PrimeBuffer`] without any
9//! external dependencies. The performance of the [`NaiveBuffer`] will not be extremely optimized,
10//! but it will be efficient enough for most applications.
11//!
12
13use crate::factor::{pollard_rho, trial_division};
14use crate::nt_funcs::{
15    factorize128, is_prime64, next_prime, nth_prime_bounds, nth_prime_est, prev_prime,
16};
17use crate::primality::{PrimalityBase, PrimalityRefBase};
18use crate::tables::{SMALL_PRIMES, SMALL_PRIMES_NEXT};
19use crate::traits::{
20    FactorizationConfig, Primality, PrimalityTestConfig, PrimalityUtils, PrimeBuffer,
21};
22use bitvec::{bitvec, prelude::Msb0};
23use lru::LruCache;
24use num_integer::Roots;
25use rand::random;
26use std::collections::BTreeMap;
27use std::num::NonZeroUsize;
28
29/// Extension functions that can utilize pre-generated primes
30pub trait PrimeBufferExt: for<'a> PrimeBuffer<'a> {
31    /// Test if an integer is a prime.
32    ///
33    /// For targets smaller than 2^64, the deterministic [`is_prime64`] will be used, otherwise
34    /// the primality test algorithms can be specified by the `config` argument.
35    ///
36    /// The primality test can be either deterministic or probabilistic for large integers depending on the `config`.
37    /// The return value is represented by the enum [Primality], which tells whether the primality test is deterministic
38    /// or probabilistic.
39    fn is_prime<T: PrimalityBase>(
40        &self,
41        target: &T,
42        config: Option<PrimalityTestConfig>,
43    ) -> Primality
44    where
45        for<'r> &'r T: PrimalityRefBase<T>,
46    {
47        // shortcuts
48        if target.is_even() {
49            return if target == &T::from_u8(2u8).unwrap() {
50                Primality::Yes
51            } else {
52                Primality::No
53            };
54        }
55
56        // do deterministic test if target is under 2^64
57        if let Some(x) = target.to_u64() {
58            return match is_prime64(x) {
59                true => Primality::Yes,
60                false => Primality::No,
61            };
62        }
63
64        let config = config.unwrap_or_default();
65        let mut probability = 1.;
66
67        // miller-rabin test
68        let mut witness_list: Vec<u64> = Vec::new();
69        if config.sprp_trials > 0 {
70            witness_list.extend(self.iter().take(config.sprp_trials));
71            probability *= 1. - 0.25f32.powi(config.sprp_trials as i32);
72        }
73        if config.sprp_random_trials > 0 {
74            for _ in 0..config.sprp_random_trials {
75                // we have ensured target is larger than 2^64
76                let mut w: u64 = rand::random();
77                while witness_list.iter().any(|x| x == &w) {
78                    w = rand::random();
79                }
80                witness_list.push(w);
81            }
82            probability *= 1. - 0.25f32.powi(config.sprp_random_trials as i32);
83        }
84        if !witness_list
85            .into_iter()
86            .all(|x| target.is_sprp(T::from_u64(x).unwrap()))
87        {
88            return Primality::No;
89        }
90
91        // lucas probable prime test
92        if config.slprp_test {
93            probability *= 1. - 4f32 / 15f32;
94            if !target.is_slprp(None, None) {
95                return Primality::No;
96            }
97        }
98        if config.eslprp_test {
99            probability *= 1. - 4f32 / 15f32;
100            if !target.is_eslprp(None) {
101                return Primality::No;
102            }
103        }
104
105        Primality::Probable(probability)
106    }
107
108    /// Factorize an integer.
109    ///
110    /// For targets smaller than 2^64, the efficient [`crate::nt_funcs::factorize64`] will be used, otherwise
111    /// the primality test and factorization algorithms can be specified by the `config` argument.
112    ///
113    /// The factorization result consists of two parts. The first is a map from prime factors to their exponents.
114    /// If the factorization failed, the second part will be the remaining cofactors that are not factored, otherwise None
115    /// is returned for the second part. It's ensured that the product of prime factors (and remaining cofactors if exist)
116    /// is equal to the original target.
117    fn factors<T: PrimalityBase>(
118        &self,
119        target: T,
120        config: Option<FactorizationConfig>,
121    ) -> (BTreeMap<T, usize>, Option<Vec<T>>)
122    where
123        for<'r> &'r T: PrimalityRefBase<T>,
124    {
125        // shortcut if the target is in u128 range
126        if let Some(x) = target.to_u128() {
127            let factors = factorize128(x)
128                .into_iter()
129                .map(|(k, v)| (T::from_u128(k).unwrap(), v))
130                .collect();
131            return (factors, None);
132        }
133        let config = config.unwrap_or_default();
134
135        // test the existing primes
136        let (result, factored) = trial_division(self.iter().copied(), target, config.td_limit);
137        let mut result: BTreeMap<T, usize> = result
138            .into_iter()
139            .map(|(k, v)| (T::from_u64(k).unwrap(), v))
140            .collect();
141
142        // TODO: check is_perfect_power before other methods
143
144        // find factors by dividing
145        let mut failed = Vec::new();
146        let mut config = config;
147        config.td_limit = Some(0); // disable trial division when finding divisor
148        match factored {
149            Ok(res) => {
150                if !res.is_one() {
151                    result.insert(res, 1);
152                }
153            }
154            Err(res) => {
155                let mut todo = vec![res];
156                while let Some(target) = todo.pop() {
157                    if self
158                        .is_prime(&target, Some(config.primality_config))
159                        .probably()
160                    {
161                        *result.entry(target).or_insert(0) += 1;
162                    } else if let Some(divisor) = self.divisor(&target, &mut config) {
163                        todo.push(divisor.clone());
164                        todo.push(target / divisor);
165                    } else {
166                        failed.push(target);
167                    }
168                }
169            }
170        };
171
172        if failed.is_empty() {
173            (result, None)
174        } else {
175            (result, Some(failed))
176        }
177    }
178
179    /// Factorize an integer until all prime factors are found.
180    ///
181    /// This function will try to call [`PrimeBufferExt::factors`] function repeatedly until the target
182    /// is fully factorized.
183    fn factorize<T: PrimalityBase>(&self, target: T) -> BTreeMap<T, usize>
184    where
185        for<'r> &'r T: PrimalityRefBase<T>,
186    {
187        // TODO: prevent overhead of repeat trial division
188        loop {
189            let (result, remainder) = self.factors(target.clone(), None);
190            if remainder.is_none() {
191                break result;
192            }
193        }
194    }
195
196    /// Return a proper divisor of target (randomly), even works for very large numbers.
197    /// Return `None` if no factor is found.
198    ///
199    /// Note: this method will not do a primality check
200    fn divisor<T: PrimalityBase>(&self, target: &T, config: &mut FactorizationConfig) -> Option<T>
201    where
202        for<'r> &'r T: PrimalityRefBase<T>,
203    {
204        if matches!(config.td_limit, Some(0)) {
205            // try to get a factor by trial division
206            let tsqrt: T = Roots::sqrt(target) + T::one();
207            let limit = if let Some(l) = config.td_limit {
208                tsqrt.clone().min(T::from_u64(l).unwrap())
209            } else {
210                tsqrt.clone()
211            };
212
213            for p in self.iter().map(|p| T::from_u64(*p).unwrap()) {
214                if p > tsqrt {
215                    return None; // the number is a prime
216                }
217                if p > limit {
218                    break;
219                }
220                if target.is_multiple_of(&p) {
221                    return Some(p);
222                }
223            }
224        }
225
226        // try to get a factor using pollard_rho with 4x4 trials
227        let below64 = target.to_u64().is_some();
228        while config.rho_trials > 0 {
229            let (start, offset) = if below64 {
230                (
231                    T::from_u8(random::<u8>()).unwrap() % target,
232                    T::from_u8(random::<u8>()).unwrap() % target,
233                )
234            } else {
235                (
236                    T::from_u64(random::<u64>()).unwrap() % target,
237                    T::from_u64(random::<u64>()).unwrap() % target,
238                )
239            };
240            config.rho_trials -= 1;
241            // TODO: change to a reasonable pollard rho limit
242            // TODO: add other factorization methods
243            if let (Some(p), _) = pollard_rho(target, start, offset, 1_048_576) {
244                return Some(p);
245            }
246        }
247
248        None
249    }
250}
251
252impl<T> PrimeBufferExt for T where for<'a> T: PrimeBuffer<'a> {}
253
254/// `NaiveBuffer` implements a very simple Sieve of Eratosthenes
255pub struct NaiveBuffer {
256    list: Vec<u64>, // list of found prime numbers
257    next: u64, // all primes smaller than this value has to be in the prime list, should be an odd number
258}
259
260impl Default for NaiveBuffer {
261    fn default() -> Self {
262        Self::new()
263    }
264}
265
266impl NaiveBuffer {
267    #[inline]
268    #[must_use]
269    pub fn new() -> Self {
270        let list = SMALL_PRIMES.iter().map(|&p| u64::from(p)).collect();
271        NaiveBuffer {
272            list,
273            next: SMALL_PRIMES_NEXT,
274        }
275    }
276}
277
278impl<'a> PrimeBuffer<'a> for NaiveBuffer {
279    type PrimeIter = std::slice::Iter<'a, u64>;
280
281    fn contains(&self, num: u64) -> bool {
282        self.list.binary_search(&num).is_ok()
283    }
284
285    fn clear(&mut self) {
286        self.list.truncate(16);
287        self.list.shrink_to_fit();
288        self.next = 55; // 16-th prime is 53
289    }
290
291    fn iter(&'a self) -> Self::PrimeIter {
292        self.list.iter()
293    }
294
295    fn bound(&self) -> u64 {
296        *self.list.last().unwrap()
297    }
298
299    fn reserve(&mut self, limit: u64) {
300        let sieve_limit = (limit | 1) + 2; // make sure sieving limit is odd and larger than limit
301        let current = self.next; // prevent borrowing self
302        debug_assert!(current % 2 == 1);
303        if sieve_limit < current {
304            return;
305        }
306
307        // create sieve and filter with existing primes
308        let mut sieve = bitvec![usize, Msb0; 0; ((sieve_limit - current) / 2) as usize];
309        for p in self.list.iter().skip(1) {
310            // skip pre-filtered 2
311            let start = if p * p < current {
312                p * ((current / p) | 1) // start from an odd factor
313            } else {
314                p * p
315            };
316            for multi in (start..sieve_limit).step_by(2 * (*p as usize)) {
317                if multi >= current {
318                    sieve.set(((multi - current) / 2) as usize, true);
319                }
320            }
321        }
322
323        // sieve with new primes
324        for p in (current..Roots::sqrt(&sieve_limit) + 1).step_by(2) {
325            for multi in (p * p..sieve_limit).step_by(2 * (p as usize)) {
326                if multi >= current {
327                    sieve.set(((multi - current) / 2) as usize, true);
328                }
329            }
330        }
331
332        // collect the sieve
333        self.list
334            .extend(sieve.iter_zeros().map(|x| (x as u64) * 2 + current));
335        self.next = sieve_limit;
336    }
337}
338
339impl NaiveBuffer {
340    // FIXME: These functions could be implemented in the trait, but only after
341    // RFC 2071 and https://github.com/cramertj/impl-trait-goals/issues/3
342
343    /// Calculate the primorial function
344    pub fn primorial<T: PrimalityBase + std::iter::Product>(&mut self, n: usize) -> T {
345        self.nprimes(n).map(|&p| T::from_u64(p).unwrap()).product()
346    }
347
348    /// Returns all primes ≤ `limit`. The primes are sorted.
349    // TODO: let primes return &[u64] instead of iterator, and create a separate iterator
350    //       for endless prime iter. This can be a method in this trait, or standalone function,
351    //       or implement as IntoIter. We can try to implement PrimeBuffer on primal first and see
352    //       if it's reasonable to unifiy
353    pub fn primes(&mut self, limit: u64) -> std::iter::Take<<Self as PrimeBuffer<'_>>::PrimeIter> {
354        self.reserve(limit);
355        let position = match self.list.binary_search(&limit) {
356            Ok(p) => p + 1,
357            Err(p) => p,
358        }; // into_ok_or_err()
359        self.list.iter().take(position)
360    }
361
362    /// Returns all primes ≤ `limit` and takes ownership. The primes are sorted.
363    #[must_use]
364    pub fn into_primes(mut self, limit: u64) -> std::vec::IntoIter<u64> {
365        self.reserve(limit);
366        let position = match self.list.binary_search(&limit) {
367            Ok(p) => p + 1,
368            Err(p) => p,
369        }; // into_ok_or_err()
370        self.list.truncate(position);
371        self.list.into_iter()
372    }
373
374    /// Returns primes of certain amount counting from 2. The primes are sorted.
375    pub fn nprimes(
376        &mut self,
377        count: usize,
378    ) -> std::iter::Take<<Self as PrimeBuffer<'_>>::PrimeIter> {
379        let (_, bound) = nth_prime_bounds(&(count as u64))
380            .expect("Estimated size of the largest prime will be larger than u64 limit");
381        self.reserve(bound);
382        debug_assert!(self.list.len() >= count);
383        self.list.iter().take(count)
384    }
385
386    /// Returns primes of certain amount counting from 2 and takes ownership. The primes are sorted.
387    #[must_use]
388    pub fn into_nprimes(mut self, count: usize) -> std::vec::IntoIter<u64> {
389        let (_, bound) = nth_prime_bounds(&(count as u64))
390            .expect("Estimated size of the largest prime will be larger than u64 limit");
391        self.reserve(bound);
392        debug_assert!(self.list.len() >= count);
393        self.list.truncate(count);
394        self.list.into_iter()
395    }
396
397    /// Get the n-th prime (n counts from 1).
398    ///
399    /// Theoretically the result can be larger than 2^64, but it will takes forever to
400    /// calculate that so we just return `u64` instead of `Option<u64>` here.
401    pub fn nth_prime(&mut self, n: u64) -> u64 {
402        if n < self.list.len() as u64 {
403            return self.list[n as usize - 1];
404        }
405
406        // Directly sieve if the limit is small
407        const THRESHOLD_NTH_PRIME_SIEVE: u64 = 4096;
408        if n <= THRESHOLD_NTH_PRIME_SIEVE {
409            return *self.nprimes(n as usize).next_back().unwrap();
410        }
411
412        // Check primes starting from estimation
413        let mut x = prev_prime(&nth_prime_est(&n).unwrap(), None).unwrap();
414        let mut pi = self.prime_pi(x);
415
416        while pi > n {
417            x = prev_prime(&x, None).unwrap();
418            pi -= 1;
419        }
420        while pi < n {
421            x = next_prime(&x, None).unwrap();
422            pi += 1;
423        }
424        x
425    }
426
427    /// Legendre's phi function, used as a helper function for [`Self::prime_pi`]
428    pub fn prime_phi(&mut self, x: u64, a: usize, cache: &mut LruCache<(u64, usize), u64>) -> u64 {
429        if a == 1 {
430            // TODO(MSRV>=1.73): feature(int_roundings1): use div_ceil
431            return crate::util::div_ceil_u64(x, 2);
432        }
433        if let Some(v) = cache.get(&(x, a)) {
434            return *v;
435        }
436        let t1 = self.prime_phi(x, a - 1, cache);
437        let pa = self.nth_prime(a as u64);
438        let t2 = self.prime_phi(x / pa, a - 1, cache);
439        let t = t1 - t2;
440        cache.put((x, a), t);
441        t
442    }
443
444    /// Calculate the prime π function, i.e. number of primes ≤ `limit`.
445    ///
446    /// Meissel-Lehmer method will be used if the input `limit` is large enough.
447    pub fn prime_pi(&mut self, limit: u64) -> u64 {
448        // Directly sieve if the limit is small
449        const THRESHOLD_PRIME_PI_SIEVE: u64 = 38873; // 4096th prime
450        if &limit <= self.list.last().unwrap() || limit <= THRESHOLD_PRIME_PI_SIEVE {
451            self.reserve(limit);
452
453            return match self.list.binary_search(&limit) {
454                Ok(pos) => (pos + 1) as u64,
455                Err(pos) => pos as u64,
456            };
457        }
458
459        // Then use Meissel-Lehmer method.
460        let b = limit.sqrt();
461        let a = b.sqrt();
462        let c = limit.cbrt();
463        self.reserve(b);
464
465        let a = self.prime_pi(a);
466        let b = self.prime_pi(b);
467        let c = self.prime_pi(c);
468
469        let cache_cap = NonZeroUsize::new(a as usize).unwrap(); // a > 0 because limit > THRESHOLD_PRIME_PI_SIEVE
470        let mut phi_cache = LruCache::new(cache_cap);
471        let mut sum =
472            self.prime_phi(limit, a as usize, &mut phi_cache) + (b + a - 2) * (b - a + 1) / 2;
473        for i in (a + 1)..=b {
474            let w = limit / self.nth_prime(i);
475            sum -= self.prime_pi(w);
476            if i <= c {
477                let l = self.prime_pi(w.sqrt());
478                sum += (l * (l - 1) - i * (i - 3)) / 2 - 1;
479                for j in i..=l {
480                    let pj = self.nth_prime(j);
481                    sum -= self.prime_pi(w / pj);
482                }
483            }
484        }
485        sum
486    }
487}
488
489#[cfg(test)]
490mod tests {
491    use super::*;
492    use crate::mint::SmallMint;
493    #[cfg(feature = "num-bigint")]
494    use core::str::FromStr;
495    #[cfg(feature = "num-bigint")]
496    use num_bigint::BigUint;
497    use rand::random;
498
499    #[test]
500    fn prime_generation_test() {
501        const PRIME50: [u64; 15] = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47];
502        const PRIME300: [u64; 62] = [
503            2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83,
504            89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179,
505            181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271,
506            277, 281, 283, 293,
507        ];
508
509        let mut pb = NaiveBuffer::new();
510        assert_eq!(pb.primes(50).copied().collect::<Vec<_>>(), PRIME50);
511        assert_eq!(pb.primes(300).copied().collect::<Vec<_>>(), PRIME300);
512
513        // test when limit itself is a prime
514        pb.clear();
515        assert_eq!(pb.primes(293).copied().collect::<Vec<_>>(), PRIME300);
516        pb = NaiveBuffer::new();
517        assert_eq!(*pb.primes(257).next_back().unwrap(), 257); // boundary of small table
518        pb = NaiveBuffer::new();
519        assert_eq!(*pb.primes(8167).next_back().unwrap(), 8167); // boundary of large table
520    }
521
522    #[test]
523    fn nth_prime_test() {
524        let mut pb = NaiveBuffer::new();
525        assert_eq!(pb.nth_prime(10000), 104_729);
526        assert_eq!(pb.nth_prime(20000), 224_737);
527        assert_eq!(pb.nth_prime(10000), 104_729); // use existing primes
528
529        // Riemann zeta based, test on OEIS:A006988
530        assert_eq!(pb.nth_prime(10u64.pow(4)), 104_729);
531        assert_eq!(pb.nth_prime(10u64.pow(5)), 1_299_709);
532        assert_eq!(pb.nth_prime(10u64.pow(6)), 15_485_863);
533        assert_eq!(pb.nth_prime(10u64.pow(7)), 179_424_673);
534    }
535
536    #[test]
537    fn prime_pi_test() {
538        let mut pb = NaiveBuffer::new();
539        assert_eq!(pb.prime_pi(8161), 1024); // 8161 is the 1024th prime
540        assert_eq!(pb.prime_pi(10000), 1229);
541        assert_eq!(pb.prime_pi(20000), 2262);
542        assert_eq!(pb.prime_pi(38873), 4096);
543
544        pb.clear(); // sieving from scratch
545        assert_eq!(pb.prime_pi(8161), 1024);
546        assert_eq!(pb.prime_pi(10000), 1229);
547        assert_eq!(pb.prime_pi(20000), 2262);
548        assert_eq!(pb.prime_pi(10000), 1229); // use existing primes
549
550        // Meissel–Lehmer algorithm, test on OEIS:A006880
551        assert_eq!(pb.prime_pi(10u64.pow(5)), 9592);
552        assert_eq!(pb.prime_pi(10u64.pow(6)), 78498);
553        assert_eq!(pb.prime_pi(10u64.pow(7)), 664_579);
554        assert_eq!(pb.prime_pi(10u64.pow(8)), 5_761_455);
555    }
556
557    #[test]
558    fn is_prime_test() {
559        // test for is_prime
560        let pb = NaiveBuffer::new();
561
562        // some mersenne numbers
563        assert_eq!(pb.is_prime(&(2u32.pow(19) - 1), None), Primality::Yes);
564        assert_eq!(pb.is_prime(&(2u32.pow(23) - 1), None), Primality::No);
565        assert!(matches!(
566            pb.is_prime(&(2u128.pow(89) - 1), None),
567            Primality::Probable(_)
568        ));
569        assert!(matches!(
570            pb.is_prime(&SmallMint::from(2u128.pow(89) - 1), None),
571            Primality::Probable(_)
572        ));
573
574        // test against small prime assertion
575        for _ in 0..100 {
576            let target = random::<u64>();
577            assert_eq!(
578                !is_prime64(target),
579                matches!(
580                    pb.is_prime(&target, Some(PrimalityTestConfig::bpsw())),
581                    Primality::No
582                )
583            );
584        }
585
586        // test large numbers
587        const P: u128 = 18_699_199_384_836_356_663; // https://golang.org/issue/638
588        assert!(matches!(pb.is_prime(&P, None), Primality::Probable(_)));
589        assert!(matches!(
590            pb.is_prime(&P, Some(PrimalityTestConfig::bpsw())),
591            Primality::Probable(_)
592        ));
593        assert!(matches!(
594            pb.is_prime(&SmallMint::from(P), None),
595            Primality::Probable(_)
596        ));
597        assert!(matches!(
598            pb.is_prime(&SmallMint::from(P), Some(PrimalityTestConfig::bpsw())),
599            Primality::Probable(_)
600        ));
601
602        const P2: u128 = 2_019_922_777_445_599_503_530_083;
603        assert!(matches!(pb.is_prime(&P2, None), Primality::Probable(_)));
604        assert!(matches!(
605            pb.is_prime(&P2, Some(PrimalityTestConfig::bpsw())),
606            Primality::Probable(_)
607        ));
608        assert!(matches!(
609            pb.is_prime(&SmallMint::from(P2), None),
610            Primality::Probable(_)
611        ));
612        assert!(matches!(
613            pb.is_prime(&SmallMint::from(P2), Some(PrimalityTestConfig::bpsw())),
614            Primality::Probable(_)
615        ));
616
617        #[cfg(feature = "num-bigint")]
618        {
619            let large_primes = [
620                "98920366548084643601728869055592650835572950932266967461790948584315647051443",
621                "94560208308847015747498523884063394671606671904944666360068158221458669711639",
622
623                // http://primes.utm.edu/lists/small/small3.html
624                "449417999055441493994709297093108513015373787049558499205492347871729927573118262811508386655998299074566974373711472560655026288668094291699357843464363003144674940345912431129144354948751003607115263071543163",
625                "230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593",
626                "5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993",
627                "203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123",
628                // ECC primes: http://tools.ietf.org/html/draft-ladd-safecurves-02
629                "3618502788666131106986593281521497120414687020801267626233049500247285301239",                                                                                  // Curve1174: 2^251-9
630                "57896044618658097711785492504343953926634992332820282019728792003956564819949",                                                                                 // Curve25519: 2^255-19
631                "9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576599",                                           // E-382: 2^382-105
632                "42307582002575910332922579714097346549017899709713998034217522897561970639123926132812109468141778230245837569601494931472367",                                 // Curve41417: 2^414-17
633                "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", // E-521: 2^521-1
634
635                // https://github.com/AtropineTears/num-primes/issues/1#issuecomment-934629597
636                "169511182982703321453314585423962898651587669459838234386506572286328885534468792292646838949809616446341407457141008401355628947670484184607678853094537849610289912805960069455687743151708433319901176932959509872662610091644590437761688516626993416011399330087939042347256922771590903190536793274742859624657"
637            ];
638            for pstr in large_primes {
639                assert!(
640                    pb.is_prime(&BigUint::from_str(pstr).unwrap(), None)
641                        .probably(),
642                    "false negative prime {}",
643                    pstr
644                )
645            }
646
647            let large_composites = [
648                "21284175091214687912771199898307297748211672914763848041968395774954376176754",
649                "6084766654921918907427900243509372380954290099172559290432744450051395395951",
650                "84594350493221918389213352992032324280367711247940675652888030554255915464401",
651                "82793403787388584738507275144194252681",
652
653                // Arnault, "Rabin-Miller Primality Test: Composite Numbers Which Pass It",
654                // Mathematics of Computation, 64(209) (January 1995), pp. 335-361.
655                "1195068768795265792518361315725116351898245581", // strong pseudoprime to prime bases 2 through 29
656                // strong pseudoprime to all prime bases up to 200
657                "8038374574536394912570796143419421081388376882875581458374889175222974273765333652186502336163960045457915042023603208766569966760987284043965408232928738791850869166857328267761771029389697739470167082304286871099974399765441448453411558724506334092790222752962294149842306881685404326457534018329786111298960644845216191652872597534901",
658            ];
659            for cstr in large_composites {
660                assert!(
661                    !pb.is_prime(
662                        &BigUint::from_str(cstr).unwrap(),
663                        Some(PrimalityTestConfig::bpsw())
664                    )
665                    .probably(),
666                    "false positive prime {}",
667                    cstr
668                )
669            }
670        }
671    }
672
673    #[test]
674    fn pb_factors_test() {
675        #[cfg(feature = "num-bigint")]
676        {
677            let pb = NaiveBuffer::new();
678            let m131 = BigUint::from(2u8).pow(131) - 1u8; // m131/263 is a large prime
679            let (fac, r) = pb.factors(m131, None);
680            assert!(fac.len() == 2 && r.is_none());
681        }
682    }
683
684    // --- NaiveBuffer::clear() + re-reserve ---
685    #[test]
686    fn test_clear_and_re_reserve() {
687        let mut pb = NaiveBuffer::new();
688        pb.reserve(100);
689        assert!(pb.bound() >= 100);
690        assert!(pb.contains(97));
691        pb.clear();
692        // After clear, only 16 primes remain (up to 53)
693        assert_eq!(pb.bound(), 53);
694        assert!(!pb.contains(97)); // 97 should be gone
695                                   // Re-reserve brings primes back
696        pb.reserve(100);
697        assert!(pb.bound() >= 100);
698        assert!(pb.contains(97));
699    }
700
701    // --- into_primes() ---
702    #[test]
703    fn test_into_primes() {
704        let pb = NaiveBuffer::new();
705        let primes: Vec<u64> = pb.into_primes(20).collect();
706        assert_eq!(primes, vec![2, 3, 5, 7, 11, 13, 17, 19]);
707    }
708
709    #[test]
710    fn test_into_primes_exact_boundary() {
711        let pb = NaiveBuffer::new();
712        // 23 is a prime, should be included
713        let primes: Vec<u64> = pb.into_primes(23).collect();
714        assert_eq!(*primes.last().unwrap(), 23);
715    }
716
717    // --- into_nprimes() ---
718    #[test]
719    fn test_into_nprimes() {
720        let pb = NaiveBuffer::new();
721        let primes: Vec<u64> = pb.into_nprimes(10).collect();
722        assert_eq!(primes.len(), 10);
723        assert_eq!(primes[0], 2);
724        assert_eq!(primes[9], 29);
725    }
726
727    // --- primorial() ---
728    #[test]
729    fn test_primorial() {
730        let mut pb = NaiveBuffer::new();
731        // primorial(4) = 2 * 3 * 5 * 7 = 210
732        let result: u64 = pb.primorial(4);
733        assert_eq!(result, 210);
734        // primorial(1) = 2
735        let result: u64 = pb.primorial(1);
736        assert_eq!(result, 2);
737    }
738
739    // --- is_prime with eslprp config ---
740    #[test]
741    fn test_is_prime_eslprp_config() {
742        let pb = NaiveBuffer::new();
743        let config = PrimalityTestConfig {
744            sprp_trials: 1,
745            sprp_random_trials: 0,
746            slprp_test: false,
747            eslprp_test: true,
748        };
749        let p: u128 = 18_699_199_384_836_356_663;
750        assert!(pb.is_prime(&p, Some(config)).probably());
751    }
752
753    // --- is_prime with strict config ---
754    #[test]
755    fn test_is_prime_strict_config() {
756        let pb = NaiveBuffer::new();
757        let p: u128 = 2_019_922_777_445_599_503_530_083;
758        assert!(pb
759            .is_prime(&p, Some(PrimalityTestConfig::strict()))
760            .probably());
761
762        // composite should fail
763        let c: u128 = 2_019_922_777_445_599_503_530_083 * 3;
764        assert!(!pb
765            .is_prime(&c, Some(PrimalityTestConfig::strict()))
766            .probably());
767    }
768
769    // --- factors() for u128 composites ---
770    #[test]
771    fn test_factors_u128() {
772        let pb = NaiveBuffer::new();
773        // Factor a u128 composite: 2^3 * 3^2 * 5 = 360
774        let (factors, remainder) = pb.factors(360u128, None);
775        assert!(remainder.is_none());
776        assert_eq!(factors[&2], 3);
777        assert_eq!(factors[&3], 2);
778        assert_eq!(factors[&5], 1);
779    }
780
781    #[test]
782    fn test_factors_u128_large() {
783        let pb = NaiveBuffer::new();
784        // 2^64 + 1 = 274177 * 67280421310721
785        let n: u128 = (1u128 << 64) + 1;
786        let (factors, remainder) = pb.factors(n, None);
787        assert!(remainder.is_none());
788        assert!(factors.contains_key(&274177u128));
789    }
790
791    // --- is_prime on even numbers ---
792    #[test]
793    fn test_is_prime_even() {
794        let pb = NaiveBuffer::new();
795        assert_eq!(pb.is_prime(&2u128, None), Primality::Yes);
796        assert_eq!(pb.is_prime(&4u128, None), Primality::No);
797    }
798
799    // --- factors with strict config ---
800    #[test]
801    fn test_factors_with_strict_config() {
802        let pb = NaiveBuffer::new();
803        let (factors, remainder) = pb.factors(100u64, Some(FactorizationConfig::strict()));
804        assert!(remainder.is_none());
805        assert_eq!(factors[&2], 2);
806        assert_eq!(factors[&5], 2);
807    }
808}