diff --git a/src/gleam_community/maths/predicates.gleam b/src/gleam_community/maths/predicates.gleam
index f8d357c..ca81d33 100644
--- a/src/gleam_community/maths/predicates.gleam
+++ b/src/gleam_community/maths/predicates.gleam
@@ -61,7 +61,7 @@ import gleam_community/maths/arithmetics
/// Example
///
/// import gleeunit/should
-/// import gleam_community/maths/tests
+/// import gleam_community/maths/predicates
///
/// pub fn example () {
/// let val: Float = 99.
@@ -115,7 +115,7 @@ fn float_absolute_difference(a: Float, b: Float) -> Float {
///
/// import gleeunit/should
/// import gleam/list
-/// import gleam_community/maths/tests
+/// import gleam_community/maths/predicates
///
/// pub fn example () {
/// let val: Float = 99.
@@ -126,7 +126,7 @@ fn float_absolute_difference(a: Float, b: Float) -> Float {
/// // if 'val' is within 1 percent of 'ref_val' +/- 0.1
/// let rtol: Float = 0.01
/// let atol: Float = 0.10
-/// tests.all_close(xarr, yarr, rtol, atol)
+/// predicates.all_close(xarr, yarr, rtol, atol)
/// |> fn(zarr: Result(List(Bool), String)) -> Result(Bool, Nil) {
/// case zarr {
/// Ok(arr) ->
@@ -181,13 +181,13 @@ pub fn all_close(
/// Example
///
/// import gleeunit/should
-/// import gleam_community/maths/tests
+/// import gleam_community/maths/predicates
///
/// pub fn example () {
-/// tests.is_fractional(0.3333)
+/// predicates.is_fractional(0.3333)
/// |> should.equal(True)
///
-/// tests.is_fractional(1.0)
+/// predicates.is_fractional(1.0)
/// |> should.equal(False)
/// }
///
@@ -218,15 +218,15 @@ fn do_ceiling(a: Float) -> Float
/// Example:
///
/// import gleeunit/should
-/// import gleam_community/maths/tests
+/// import gleam_community/maths/predicates
///
/// pub fn example() {
/// // Check if 4 is a power of 2 (it is)
-/// tests.is_power(4, 2)
+/// predicates.is_power(4, 2)
/// |> should.equal(True)
///
/// // Check if 5 is a power of 2 (it is not)
-/// tests.is_power(5, 2)
+/// predicates.is_power(5, 2)
/// |> should.equal(False)
/// }
///
@@ -266,13 +266,13 @@ pub fn is_power(x: Int, y: Int) -> Bool {
/// Example:
///
/// import gleeunit/should
-/// import gleam_community/maths/tests
+/// import gleam_community/maths/predicates
///
/// pub fn example() {
-/// tests.is_perfect(6)
+/// predicates.is_perfect(6)
/// |> should.equal(True)
///
-/// tests.is_perfect(28)
+/// predicates.is_perfect(28)
/// |> should.equal(True)
/// }
///
@@ -308,13 +308,13 @@ fn do_sum(arr: List(Int)) -> Int {
/// Example:
///
/// import gleeunit/should
-/// import gleam_community/maths/tests
+/// import gleam_community/maths/predicates
///
/// pub fn example() {
-/// tests.is_even(-3)
+/// predicates.is_even(-3)
/// |> should.equal(False)
///
-/// tests.is_even(-4)
+/// predicates.is_even(-4)
/// |> should.equal(True)
/// }
///
@@ -341,13 +341,13 @@ pub fn is_even(x: Int) -> Bool {
/// Example:
///
/// import gleeunit/should
-/// import gleam_community/maths/tests
+/// import gleam_community/maths/predicates
///
/// pub fn example() {
-/// tests.is_odd(-3)
+/// predicates.is_odd(-3)
/// |> should.equal(True)
///
-/// tests.is_odd(-4)
+/// predicates.is_odd(-4)
/// |> should.equal(False)
/// }
///
@@ -361,3 +361,94 @@ pub fn is_even(x: Int) -> Bool {
pub fn is_odd(x: Int) -> Bool {
x % 2 != 0
}
+
+///
+///
+/// A function that tests whether a given integer value $$x \in \mathbb{Z}$$ is a prime number.
+/// A prime number is a natural number greater than 1 that has no positive divisors other than 1 and itself.
+///
+/// The function uses the Miller-Rabin primality test to assess if $$x$$ is prime. It is a probabilistic
+/// test, so it can mistakenly identify a composite number as prime. However, the probability of such errors decreases
+/// with more testing iterations (the function uses 64 iterations internally, which is typically more than sufficient).
+/// The Miller-Rabin test is particularly useful for large numbers.
+///
+///
+/// Details
+///
+/// Examples of prime numbers:
+/// - $$2$$ is a prime number since it has only two divisors: $$1$$ and $$2$$.
+/// - $$7$$ is a prime number since it has only two divisors: $$1$$ and $$7$$.
+/// - $$4$$ is not a prime number since it has divisors other than $$1$$ and itself, such as $$2$$.
+///
+///
+///
+///
+/// Example:
+///
+/// import gleeunit/should
+/// import gleam_community/maths/predicates
+///
+/// pub fn example() {
+/// predicates.is_prime(2)
+/// |> should.equal(True)
+///
+/// predicates.is_prime(4)
+/// |> should.equal(False)
+///
+/// // Test the 2nd Carmichael number
+/// predicates.is_prime(1105)
+/// |> should.equal(False)
+/// }
+///
+///
+///
+///
+pub fn is_prime(x: Int) -> Bool {
+ case x {
+ x if x < 2 -> {
+ False
+ }
+ x if x == 2 -> {
+ True
+ }
+ _ -> {
+ miller_rabin_test(x, 64)
+ }
+ }
+}
+
+fn miller_rabin_test(n: Int, k: Int) -> Bool {
+ case n, k {
+ _, 0 -> True
+ _, _ -> {
+ // Generate a random int in the range [2, n]
+ let random_candidate: Int = 2 + int.random(n - 2)
+ case powmod_with_check(random_candidate, n - 1, n) == 1 {
+ True -> miller_rabin_test(n, k - 1)
+ False -> False
+ }
+ }
+ }
+}
+
+fn powmod_with_check(base: Int, exponent: Int, modulus: Int) -> Int {
+ case exponent, { exponent % 2 } == 0 {
+ 0, _ -> 1
+ _, True -> {
+ let x: Int = powmod_with_check(base, exponent / 2, modulus)
+ case { x * x } % modulus, x != 1 && x != { modulus - 1 } {
+ 1, True -> 0
+ _, _ -> { x * x } % modulus
+ }
+ }
+ _, _ -> { base * powmod_with_check(base, exponent - 1, modulus) } % modulus
+ }
+}
diff --git a/test/gleam_community/maths/predicates_test.gleam b/test/gleam_community/maths/predicates_test.gleam
index a7a13d8..4130aab 100644
--- a/test/gleam_community/maths/predicates_test.gleam
+++ b/test/gleam_community/maths/predicates_test.gleam
@@ -136,3 +136,37 @@ pub fn int_is_perfect_test() {
predicates.is_perfect(13)
|> should.equal(False)
}
+
+pub fn int_is_prime_test() {
+ predicates.is_prime(1)
+ |> should.equal(False)
+
+ predicates.is_prime(2)
+ |> should.equal(True)
+
+ predicates.is_prime(3)
+ |> should.equal(True)
+
+ predicates.is_prime(5)
+ |> should.equal(True)
+
+ predicates.is_prime(7)
+ |> should.equal(True)
+
+ predicates.is_prime(11)
+ |> should.equal(True)
+
+ predicates.is_prime(42)
+ |> should.equal(False)
+
+ predicates.is_prime(7919)
+ |> should.equal(True)
+
+ // Test 1st Carmichael number
+ predicates.is_prime(561)
+ |> should.equal(False)
+
+ // Test 2nd Carmichael number
+ predicates.is_prime(1105)
+ |> should.equal(False)
+}