Skip to content

Commit fa1eab2

Browse files
committed
feat: add fermats little theorem
1 parent bc8d6fa commit fa1eab2

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

src/math/fermats_little_theorem.rs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use rand::Rng;
2+
3+
/// Performs modular exponentiation.
4+
///
5+
/// This function computes `(base ^ exp) mod modulus` efficiently using the method
6+
/// of exponentiation by squaring.
7+
///
8+
/// # Parameters
9+
///
10+
/// - `base`: The base value (u64).
11+
/// - `exp`: The exponent (u64).
12+
/// - `modulus`: The modulus (u64).
13+
///
14+
/// # Returns
15+
///
16+
/// The result of `(base ^ exp) mod modulus`.
17+
fn mod_exp(base: u64, exp: u64, modulus: u64) -> u64 {
18+
let mut result = 1;
19+
let mut base = base % modulus;
20+
let mut exp = exp;
21+
22+
while exp > 0 {
23+
if exp % 2 == 1 {
24+
result = (result * base) % modulus;
25+
}
26+
exp /= 2;
27+
base = (base * base) % modulus;
28+
}
29+
30+
result
31+
}
32+
33+
/// Implements Fermat's Little Theorem for probabilistic primality testing.
34+
///
35+
/// Fermat's Little Theorem states that if `p` is a prime number, then for any integer
36+
/// `a` such that `1 < a < p`, it holds that:
37+
///
38+
/// ```text
39+
/// a^(p-1) ≡ 1 (mod p)
40+
/// ```
41+
///
42+
/// This function tests if the given number `p` is prime by selecting `k` random integers `a`
43+
/// in the range `[2, p-1]` and checking if the above condition holds. If it fails for any `a`,
44+
/// the number is classified as composite. However, if it passes for all chosen values, it is
45+
/// considered likely prime.
46+
///
47+
/// # Parameters
48+
///
49+
/// - `p`: The number to test for primality (u64).
50+
/// - `k`: The number of random tests to perform (u32). More tests provide a higher confidence level.
51+
///
52+
/// # Returns
53+
///
54+
/// `true` if `p` is likely prime, `false` if `p` is composite.
55+
///
56+
/// # Panics
57+
///
58+
/// The function does not panic but will return false for inputs less than or equal to 1.
59+
///
60+
/// # Note
61+
///
62+
/// This method can classify some composite numbers as prime. These are known as Carmichael numbers.
63+
///
64+
/// ## Carmichael Numbers
65+
///
66+
/// Carmichael numbers are composite numbers that satisfy Fermat's Little Theorem for all integers
67+
/// `a` that are coprime to them. In other words, if `n` is a Carmichael number, it will pass the
68+
/// Fermat primality test for every `a` such that `gcd(a, n) = 1`. Therefore, Carmichael numbers can
69+
/// fool Fermat's test into incorrectly identifying them as primes. The first few Carmichael numbers
70+
/// are 561, 1105, 1729, 2465, 2821, and 6601.
71+
pub fn fermats_little_theorem(p: u64, k: u32) -> bool {
72+
if p <= 1 {
73+
return false;
74+
}
75+
if p <= 3 {
76+
return true;
77+
}
78+
79+
let mut rng = rand::thread_rng();
80+
81+
for _ in 0..k {
82+
let a = rng.gen_range(2..p - 1);
83+
if mod_exp(a, p - 1, p) != 1 {
84+
return false;
85+
}
86+
}
87+
88+
true
89+
}
90+
91+
#[cfg(test)]
92+
mod tests {
93+
use super::fermats_little_theorem;
94+
95+
#[test]
96+
fn test_prime_numbers() {
97+
assert!(fermats_little_theorem(5, 10));
98+
assert!(fermats_little_theorem(13, 10));
99+
assert!(fermats_little_theorem(101, 10));
100+
assert!(fermats_little_theorem(997, 10));
101+
assert!(fermats_little_theorem(7919, 10));
102+
}
103+
104+
#[test]
105+
fn test_composite_numbers() {
106+
assert!(!fermats_little_theorem(4, 10));
107+
assert!(!fermats_little_theorem(15, 10));
108+
assert!(!fermats_little_theorem(100, 10));
109+
assert!(!fermats_little_theorem(1001, 10));
110+
}
111+
112+
#[test]
113+
fn test_small_numbers() {
114+
assert!(!fermats_little_theorem(1, 10));
115+
assert!(fermats_little_theorem(2, 10));
116+
assert!(fermats_little_theorem(3, 10));
117+
assert!(!fermats_little_theorem(0, 10));
118+
}
119+
120+
#[test]
121+
fn test_large_numbers() {
122+
assert!(fermats_little_theorem(104729, 10));
123+
assert!(!fermats_little_theorem(104730, 10));
124+
}
125+
126+
#[test]
127+
fn test_carmichael_numbers() {
128+
let carmichael_numbers = vec![561, 1105, 1729, 2465, 2821, 6601];
129+
for &n in &carmichael_numbers {
130+
let result = fermats_little_theorem(n, 10);
131+
assert!(result == false || result == true);
132+
}
133+
}
134+
}

src/math/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ mod factors;
2626
mod fast_fourier_transform;
2727
mod fast_power;
2828
mod faster_perfect_numbers;
29+
mod fermats_little_theorem;
2930
mod field;
3031
mod frizzy_number;
3132
mod gaussian_elimination;
@@ -113,6 +114,7 @@ pub use self::fast_fourier_transform::{
113114
};
114115
pub use self::fast_power::fast_power;
115116
pub use self::faster_perfect_numbers::generate_perfect_numbers;
117+
pub use self::fermats_little_theorem::fermats_little_theorem;
116118
pub use self::field::{Field, PrimeField};
117119
pub use self::frizzy_number::get_nth_frizzy;
118120
pub use self::gaussian_elimination::gaussian_elimination;

0 commit comments

Comments
 (0)