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}