Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
136 changes: 136 additions & 0 deletions src/math/fermats_little_theorem.rs
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • please change the type of p to be u64. In order to use modular_exponential you can just cast it,
  • why not to add seed being Option<u64> - if it would be None, the seed would be selected at random,
  • consider changing the return type to some enum with values Composite and ProbablyPrime.

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),
Comment on lines +127 to +131
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something what I do not understand: how this function could return false for these numbers? By the definition the result should be true for them.

(6601, 10, true),
(8911, 10, false),
],
}
Comment on lines +70 to +135
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not something like that:

Suggested change
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),
],
}
macro_rules! test_fermats_little_theorem {
($($name:ident: $inputs:expr,)*) => {
$(
#[test]
fn $name() {
let (p, k, expected) = $inputs;
assert_eq!(fermats_little_theorem(p, k), expected);
}
)*
};
}
test_fermats_little_theorem! {
prime_2: (2, 10, true),
prime_3: (3, 10, true),
prime_5: (5, 10, true),
prime_13: (13, 10, true),
prime_101: (101, 10, true),
prime_997: (997, 10, true),
prime_7919: (7919, 10, true),
prime_104729: (104729, 10, true),
composite_0: (0, 10, false),
composite_1: (1, 10, false),
composite_4: (4, 10, false),
composite_15: (15, 10, false),
composite_100: (100, 10, false),
composite_1001: (1001, 10, false),
composite_104730: (104730, 10, false),
carmichael_561: (561, 10, false),
carmichael_1105: (1105 , 10, false),
carmichael_1729: (1729, 10, false),
carmichael_2465: (2465, 10, false),
carmichael_2821: (2821, 10, false),
carmichael_6601: (6601, 10, true),
carmichael_8911: (8911, 10, false),
}

}
2 changes: 2 additions & 0 deletions src/math/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down