num_prime/
traits.rs

1use core::default::Default;
2use std::ops::{BitAnd, BitOr};
3
4use either::Either;
5use num_integer::{Integer, Roots};
6use num_traits::Pow;
7
8/// This trait support unified bit testing for (unsigned) integers
9pub trait BitTest {
10    /// Get the minimum required number of bits to represent this integer
11    fn bits(&self) -> usize;
12
13    /// Get the i-th bit of the integer, with i specified by `position`
14    fn bit(&self, position: usize) -> bool;
15
16    /// Get the number of trailing zeros in the integer
17    fn trailing_zeros(&self) -> usize;
18}
19
20/// This enum describes the result of primality checks
21#[derive(Debug, Clone, Copy, PartialEq)]
22pub enum Primality {
23    /// The number passes deterministic primality check.
24    Yes,
25    /// The number is a composite and failed at least one specific primality check.
26    No,
27    /// The number passes several probabilistic primality check.
28    /// The associated float number carries the probability of the number being a prime
29    /// (conditioned on that it's already a probable prime)
30    Probable(f32),
31}
32
33impl Primality {
34    /// Check whether the resule indicates that the number is
35    /// (very) probably a prime. Return false only on [Primality::No]
36    #[inline(always)]
37    pub fn probably(self) -> bool {
38        match self {
39            Primality::No => false,
40            _ => true,
41        }
42    }
43}
44
45impl BitAnd<Primality> for Primality {
46    type Output = Primality;
47
48    /// Combine two primality results by ensuring both numbers are prime
49    fn bitand(self, rhs: Primality) -> Self::Output {
50        match self {
51            Primality::No => Primality::No,
52            Primality::Yes => rhs,
53            Primality::Probable(p) => match rhs {
54                Primality::No => Primality::No,
55                Primality::Yes => Primality::Probable(p),
56                Primality::Probable(p2) => Primality::Probable(p * p2),
57            },
58        }
59    }
60}
61
62impl BitOr<Primality> for Primality {
63    type Output = Primality;
64
65    /// Combine two primality results by ensuring either numbers is prime
66    fn bitor(self, rhs: Primality) -> Self::Output {
67        match self {
68            Primality::No => rhs,
69            Primality::Yes => Primality::Yes,
70            Primality::Probable(p) => match rhs {
71                Primality::No => Primality::Probable(p),
72                Primality::Yes => Primality::Yes,
73                Primality::Probable(p2) => Primality::Probable(1. - (1. - p) * (1. - p2)),
74            },
75        }
76    }
77}
78
79/// Represents a configuration for a primality test
80#[derive(Debug, Clone, Copy)]
81#[non_exhaustive]
82pub struct PrimalityTestConfig {
83    // TODO: add option to divides small primes in the table
84    //       and this option should be enabled if the probabilistic test is used for strict config
85    /// Number of strong probable prime test, starting from base 2
86    pub sprp_trials: usize,
87
88    /// Number of strong probable prime test with random bases
89    pub sprp_random_trials: usize,
90
91    /// Whether perform strong lucas probable prime test (with automatically selected parameters)
92    pub slprp_test: bool,
93
94    /// Whether perform extra strong lucas probable prime test (with automatically selected parameters)
95    pub eslprp_test: bool,
96}
97
98impl Default for PrimalityTestConfig {
99    /// Create a defalt primality testing configuration. This config will eliminate most
100    /// composites with little computation
101    fn default() -> Self {
102        Self {
103            sprp_trials: 2,        // test base 2 and 3
104            sprp_random_trials: 3, // choose other 3 random bases
105            slprp_test: false,
106            eslprp_test: false,
107        }
108    }
109}
110
111impl PrimalityTestConfig {
112    /// Create a configuration with a very strong primality check. It's based on
113    /// the **strongest deterministic primality testing** and some SPRP tests with
114    /// random bases.
115    pub fn strict() -> Self {
116        let mut config = Self::bpsw();
117        config.sprp_random_trials = 1;
118        config
119    }
120
121    /// Create a configuration for Baillie-PSW test (base 2 SPRP test + SLPRP test)
122    pub fn bpsw() -> Self {
123        Self {
124            sprp_trials: 1,
125            sprp_random_trials: 0,
126            slprp_test: true,
127            eslprp_test: false,
128        }
129    }
130
131    /// Create a configuration for PSW test (base 2 SPRP + Fibonacci test)
132    fn psw() {
133        todo!() // TODO: implement Fibonacci PRP
134    }
135}
136
137/// Represents a configuration for integer factorization
138#[derive(Debug, Clone, Copy)]
139#[non_exhaustive]
140pub struct FactorizationConfig {
141    /// Config for testing if a factor is prime
142    pub primality_config: PrimalityTestConfig,
143
144    /// Prime limit of trial division, you also need to reserve the primes in the buffer
145    /// if all primes under the limit are to be tested. `None` means using all available primes.
146    pub td_limit: Option<u64>,
147
148    /// Number of trials with Pollard's rho method
149    pub rho_trials: usize,
150
151    /// Number of trials with Pollard's p-1 method
152    pm1_trials: usize,
153
154    /// Number of trials with William's p+1 method
155    pp1_trials: usize,
156}
157
158impl Default for FactorizationConfig {
159    /// Create a defalt primality testing configuration. This config will factorize
160    /// most integers within decent time
161    fn default() -> Self {
162        const THRESHOLD_DEFAULT_TD: u64 = 1 << 14;
163        Self {
164            primality_config: PrimalityTestConfig::default(),
165            td_limit: Some(THRESHOLD_DEFAULT_TD),
166            rho_trials: 4,
167            pm1_trials: 0,
168            pp1_trials: 0,
169        }
170    }
171}
172
173impl FactorizationConfig {
174    /// Same as the default configuration but with strict primality check
175    pub fn strict() -> Self {
176        let mut config = Self::default();
177        config.primality_config = PrimalityTestConfig::strict();
178        config
179    }
180}
181
182// FIXME: backport to num_integer (see https://github.com/rust-num/num-traits/issues/233)
183/// Extension on [num_integer::Roots] to support perfect power check on integers
184pub trait ExactRoots: Roots + Pow<u32, Output = Self> + Clone {
185    fn nth_root_exact(&self, n: u32) -> Option<Self> {
186        let r = self.nth_root(n);
187        if &r.clone().pow(n) == self {
188            Some(r)
189        } else {
190            None
191        }
192    }
193    fn sqrt_exact(&self) -> Option<Self> {
194        self.nth_root_exact(2)
195    }
196    fn cbrt_exact(&self) -> Option<Self> {
197        self.nth_root_exact(3)
198    }
199    fn is_nth_power(&self, n: u32) -> bool {
200        self.nth_root_exact(n).is_some()
201    }
202    fn is_square(&self) -> bool {
203        self.sqrt_exact().is_some()
204    }
205    fn is_cubic(&self) -> bool {
206        self.cbrt_exact().is_some()
207    }
208}
209
210// TODO: implement is_perfect_power, this could be used during factorization
211//       to filter out perfect powers when factorize large integers
212// REF: PARI/GP `Z_ispowerall`, `is_357_power`
213//      FLINT `n_is_perfect_power235`, `fmpz_is_perfect_power`
214//      GMP `mpz_perfect_power_p`
215
216/// This trait represents a general data structure that stores primes.
217///
218/// It's recommended to store at least a bunch of small primes in the buffer
219/// to make some of the algorithms more efficient.
220pub trait PrimeBuffer<'a> {
221    type PrimeIter: Iterator<Item = &'a u64>;
222
223    /// Directly return an iterator of existing primes
224    fn iter(&'a self) -> Self::PrimeIter;
225
226    /// Generate primes until the largest prime in the buffer is equal or larger than limit
227    fn reserve(&mut self, limit: u64);
228
229    /// Get the largest primes in the list
230    fn bound(&self) -> u64;
231
232    /// Test if the number is in the buffer. If a number is not in the buffer,
233    /// then it's either a composite or large than [PrimeBuffer::bound()]
234    fn contains(&self, num: u64) -> bool;
235
236    /// clear the prime buffer to save memory
237    fn clear(&mut self);
238}
239
240/// This trait implements various primality testing algorithms
241///
242/// Reference:
243/// - <http://ntheory.org/pseudoprimes.html>
244pub trait PrimalityUtils: Integer + Clone {
245    /// Test if the integer is a (Fermat) probable prime
246    fn is_prp(&self, base: Self) -> bool;
247
248    /// Test if the integer is a strong probable prime (based on Miller-Rabin test).
249    fn is_sprp(&self, base: Self) -> bool;
250
251    /// Do a Miller-Rabin test. The return value is a integer if it finds a factor of
252    /// the integer, otherwise it reports the test result.
253    fn test_sprp(&self, base: Self) -> Either<bool, Self>;
254
255    /// Test if the integer is a Lucas probable prime
256    /// If either of p, q is not specified, then we will use Selfridge's Method A to choose p, q
257    fn is_lprp(&self, p: Option<usize>, q: Option<isize>) -> bool;
258
259    /// Test if the integer is a strong Lucas probable prime
260    /// If either of p, q is not specified, then we will use Selfridge's Method A to choose p, q
261    fn is_slprp(&self, p: Option<usize>, q: Option<isize>) -> bool;
262
263    /// Test if the integer is an extra strong Lucas probable prime
264    /// If p is not specified, then first p starting from 3 such that Jacobi symbol is -1 will be chosen, which is sometimes refered as "Method C"
265    fn is_eslprp(&self, p: Option<usize>) -> bool;
266
267    // TODO: implement ECPP test
268    // https://en.wikipedia.org/wiki/Elliptic_curve_primality
269
270    // TODO: implement is_vprp (Lucas-V probable prime test)
271    // https://arxiv.org/pdf/2006.14425.pdf
272}
273
274/// Supports random generation of primes
275pub trait RandPrime<T> {
276    /// Generate a random prime within the given bit size limit
277    ///
278    /// # Panics
279    /// if the bit_size is 0 or it's larger than the bit width of the integer
280    fn gen_prime(&mut self, bit_size: usize, config: Option<PrimalityTestConfig>) -> T;
281
282    /// Generate a random prime with **exact** the given bit size
283    ///
284    /// # Panics
285    /// if the bit_size is 0 or it's larger than the bit width of the integer
286    fn gen_prime_exact(&mut self, bit_size: usize, config: Option<PrimalityTestConfig>) -> T;
287
288    /// Generate a random (Sophie German) safe prime within the given bit size limit. The generated prime
289    /// is guaranteed to pass the [is_safe_prime][crate::nt_funcs::is_safe_prime] test
290    ///
291    /// # Panics
292    /// if the bit_size is 0 or it's larger than the bit width of the integer
293    fn gen_safe_prime(&mut self, bit_size: usize) -> T;
294
295    /// Generate a random (Sophie German) safe prime with the **exact** given bit size. The generated prime
296    /// is guaranteed to pass the [is_safe_prime][crate::nt_funcs::is_safe_prime] test
297    ///
298    /// # Panics
299    /// if the bit_size is 0 or it's larger than the bit width of the integer
300    fn gen_safe_prime_exact(&mut self, bit_size: usize) -> T;
301}