diff --git a/DIRECTORY.md b/DIRECTORY.md index 1dd188f69a8..c558c75905f 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -194,6 +194,7 @@ * [Fast Fourier Transform](https://github.com/TheAlgorithms/Rust/blob/master/src/math/fast_fourier_transform.rs) * [Fast Power](https://github.com/TheAlgorithms/Rust/blob/master/src/math/fast_power.rs) * [Faster Perfect Numbers](https://github.com/TheAlgorithms/Rust/blob/master/src/math/faster_perfect_numbers.rs) + * [Fermat's Little Theorem](https://github.com/TheAlgorithms/Rust/blob/master/src/math/fermats_little_theorem.rs) * [Field](https://github.com/TheAlgorithms/Rust/blob/master/src/math/field.rs) * [Frizzy Number](https://github.com/TheAlgorithms/Rust/blob/master/src/math/frizzy_number.rs) * [Gaussian Elimination](https://github.com/TheAlgorithms/Rust/blob/master/src/math/gaussian_elimination.rs) diff --git a/src/math/fermats_little_theorem.rs b/src/math/fermats_little_theorem.rs new file mode 100644 index 00000000000..5b92260f4f6 --- /dev/null +++ b/src/math/fermats_little_theorem.rs @@ -0,0 +1,136 @@ +use rand::prelude::*; +use rand::rngs::StdRng; + +use super::modular_exponential; + +/// Implements Fermat's Little Theorem for probabilistic primality testing. +/// +/// Fermat's Little Theorem states that if `p` is a prime number, then for any integer +/// `a` such that `1 < a < p`, it holds that: +/// +/// ```text +/// a^(p-1) ≡ 1 (mod p) +/// ``` +/// +/// This function tests if the given number `p` is prime by selecting `k` random integers `a` +/// in the range `[2, p-1]` and checking if the above condition holds. If it fails for any `a`, +/// the number is classified as composite. However, if it passes for all chosen values, it is +/// considered likely prime. +/// +/// # Parameters +/// +/// - `p`: The number to test for primality (u64). +/// - `k`: The number of random tests to perform (u32). More tests provide a higher confidence level. +/// +/// # Returns +/// +/// `true` if `p` is likely prime, `false` if `p` is composite. +/// +/// # Panics +/// +/// The function does not panic but will return false for inputs less than or equal to 1. +/// +/// # Note +/// +/// This method can classify some composite numbers as prime. These are known as Carmichael numbers. +/// +/// ## Carmichael Numbers +/// +/// Carmichael numbers are composite numbers that satisfy Fermat's Little Theorem for all integers +/// `a` that are coprime to them. In other words, if `n` is a Carmichael number, it will pass the +/// Fermat primality test for every `a` such that `gcd(a, n) = 1`. Therefore, Carmichael numbers can +/// fool Fermat's test into incorrectly identifying them as primes. The first few Carmichael numbers +/// are 561, 1105, 1729, 2465, 2821, and 6601. +pub fn fermats_little_theorem(p: i64, k: i32) -> bool { + if p <= 1 { + return false; + } + if p <= 3 { + return true; + } + + // Choosing a constant seed for consistency in test. It can be any number. + let seed = 32; + let mut rng = StdRng::seed_from_u64(seed); + + for _ in 0..k { + let a = rng.gen_range(2..p - 1); + if modular_exponential(a, p - 1, p) != 1 { + return false; + } + } + + true +} + +#[cfg(test)] +mod tests { + use super::fermats_little_theorem; + + macro_rules! test_cases { + ($( + $test_name:ident: [ + $(($n:expr, $a:expr, $expected:expr)),+ $(,)? + ] + ),+ $(,)?) => { + $( + #[test] + fn $test_name() { + $( + assert_eq!( + fermats_little_theorem($n, $a), + $expected, + "Failed for n={}, a={}", + $n, + $a + ); + )+ + } + )+ + }; + } + + test_cases! { + // Test cases for prime numbers + test_prime_numbers: [ + (5, 10, true), + (13, 10, true), + (101, 10, true), + (997, 10, true), + (7919, 10, true), + ], + + // Test cases for composite numbers + test_composite_numbers: [ + (4, 10, false), + (15, 10, false), + (100, 10, false), + (1001, 10, false), + ], + + // Test cases for small numbers + test_small_numbers: [ + (1, 10, false), + (2, 10, true), + (3, 10, true), + (0, 10, false), + ], + + // Test cases for large numbers + test_large_numbers: [ + (104729, 10, true), + (104730, 10, false), + ], + + // Test cases for Carmichael numbers + test_carmichael_numbers: [ + (561, 10, false), + (1105, 10, false), + (1729, 10, false), + (2465, 10, false), + (2821, 10, false), + (6601, 10, true), + (8911, 10, false), + ], + } +} diff --git a/src/math/mod.rs b/src/math/mod.rs index 7407465c3b0..ff2c8c61bdb 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -26,6 +26,7 @@ mod factors; mod fast_fourier_transform; mod fast_power; mod faster_perfect_numbers; +mod fermats_little_theorem; mod field; mod frizzy_number; mod gaussian_elimination; @@ -113,6 +114,7 @@ pub use self::fast_fourier_transform::{ }; pub use self::fast_power::fast_power; pub use self::faster_perfect_numbers::generate_perfect_numbers; +pub use self::fermats_little_theorem::fermats_little_theorem; pub use self::field::{Field, PrimeField}; pub use self::frizzy_number::get_nth_frizzy; pub use self::gaussian_elimination::gaussian_elimination;