|
| 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 | +} |
0 commit comments