From f7c58e4c4b66750cbb3afd9ad29e9c246b2480ab Mon Sep 17 00:00:00 2001
From: Nikos Giachoudis <ngiachou@gmail.com>
Date: Mon, 11 Jul 2022 10:36:57 -0400
Subject: [PATCH] Unify primality checking (#6228)

* renames prime functions and occurances in comments

* changes implementation of primality testing to be uniform

* adds static typing as per conventions

* updating DIRECTORY.md

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
---
 DIRECTORY.md                                  |  1 -
 data_structures/hashing/double_hash.py        |  4 +-
 .../hashing/number_theory/prime_numbers.py    | 52 ++++++++++++----
 maths/prime_check.py                          | 61 ++++++++++---------
 maths/primelib.py                             | 22 +++----
 5 files changed, 87 insertions(+), 53 deletions(-)

diff --git a/DIRECTORY.md b/DIRECTORY.md
index c8f03658c..2e9c03cbc 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -444,7 +444,6 @@
   * [Scoring Functions](machine_learning/scoring_functions.py)
   * [Sequential Minimum Optimization](machine_learning/sequential_minimum_optimization.py)
   * [Similarity Search](machine_learning/similarity_search.py)
-  * [Support Vector Machines](machine_learning/support_vector_machines.py)
   * [Word Frequency Functions](machine_learning/word_frequency_functions.py)
 
 ## Maths
diff --git a/data_structures/hashing/double_hash.py b/data_structures/hashing/double_hash.py
index 57b1ffff4..bd1355fca 100644
--- a/data_structures/hashing/double_hash.py
+++ b/data_structures/hashing/double_hash.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python3
 from .hash_table import HashTable
-from .number_theory.prime_numbers import check_prime, next_prime
+from .number_theory.prime_numbers import is_prime, next_prime
 
 
 class DoubleHash(HashTable):
@@ -15,7 +15,7 @@ class DoubleHash(HashTable):
 
         next_prime_gt = (
             next_prime(value % self.size_table)
-            if not check_prime(value % self.size_table)
+            if not is_prime(value % self.size_table)
             else value % self.size_table
         )  # gt = bigger than
         return next_prime_gt - (data % next_prime_gt)
diff --git a/data_structures/hashing/number_theory/prime_numbers.py b/data_structures/hashing/number_theory/prime_numbers.py
index bf614e7d4..b88ab76ec 100644
--- a/data_structures/hashing/number_theory/prime_numbers.py
+++ b/data_structures/hashing/number_theory/prime_numbers.py
@@ -3,25 +3,55 @@
     module to operations with prime numbers
 """
 
+import math
 
-def check_prime(number):
-    """
-    it's not the best solution
-    """
-    special_non_primes = [0, 1, 2]
-    if number in special_non_primes[:2]:
-        return 2
-    elif number == special_non_primes[-1]:
-        return 3
 
-    return all(number % i for i in range(2, number))
+def is_prime(number: int) -> bool:
+    """Checks to see if a number is a prime in O(sqrt(n)).
+
+    A number is prime if it has exactly two factors: 1 and itself.
+
+    >>> is_prime(0)
+    False
+    >>> is_prime(1)
+    False
+    >>> is_prime(2)
+    True
+    >>> is_prime(3)
+    True
+    >>> is_prime(27)
+    False
+    >>> is_prime(87)
+    False
+    >>> is_prime(563)
+    True
+    >>> is_prime(2999)
+    True
+    >>> is_prime(67483)
+    False
+    """
+
+    # precondition
+    assert isinstance(number, int) and (
+        number >= 0
+    ), "'number' must been an int and positive"
+
+    if 1 < number < 4:
+        # 2 and 3 are primes
+        return True
+    elif number < 2 or not number % 2:
+        # Negatives, 0, 1 and all even numbers are not primes
+        return False
+
+    odd_numbers = range(3, int(math.sqrt(number) + 1), 2)
+    return not any(not number % i for i in odd_numbers)
 
 
 def next_prime(value, factor=1, **kwargs):
     value = factor * value
     first_value_val = value
 
-    while not check_prime(value):
+    while not is_prime(value):
         value += 1 if not ("desc" in kwargs.keys() and kwargs["desc"] is True) else -1
 
     if value == first_value_val:
diff --git a/maths/prime_check.py b/maths/prime_check.py
index 92d31cfee..315492054 100644
--- a/maths/prime_check.py
+++ b/maths/prime_check.py
@@ -4,31 +4,36 @@ import math
 import unittest
 
 
-def prime_check(number: int) -> bool:
+def is_prime(number: int) -> bool:
     """Checks to see if a number is a prime in O(sqrt(n)).
 
     A number is prime if it has exactly two factors: 1 and itself.
 
-    >>> prime_check(0)
+    >>> is_prime(0)
     False
-    >>> prime_check(1)
+    >>> is_prime(1)
     False
-    >>> prime_check(2)
+    >>> is_prime(2)
     True
-    >>> prime_check(3)
+    >>> is_prime(3)
     True
-    >>> prime_check(27)
+    >>> is_prime(27)
     False
-    >>> prime_check(87)
+    >>> is_prime(87)
     False
-    >>> prime_check(563)
+    >>> is_prime(563)
     True
-    >>> prime_check(2999)
+    >>> is_prime(2999)
     True
-    >>> prime_check(67483)
+    >>> is_prime(67483)
     False
     """
 
+    # precondition
+    assert isinstance(number, int) and (
+        number >= 0
+    ), "'number' must been an int and positive"
+
     if 1 < number < 4:
         # 2 and 3 are primes
         return True
@@ -42,35 +47,35 @@ def prime_check(number: int) -> bool:
 
 class Test(unittest.TestCase):
     def test_primes(self):
-        self.assertTrue(prime_check(2))
-        self.assertTrue(prime_check(3))
-        self.assertTrue(prime_check(5))
-        self.assertTrue(prime_check(7))
-        self.assertTrue(prime_check(11))
-        self.assertTrue(prime_check(13))
-        self.assertTrue(prime_check(17))
-        self.assertTrue(prime_check(19))
-        self.assertTrue(prime_check(23))
-        self.assertTrue(prime_check(29))
+        self.assertTrue(is_prime(2))
+        self.assertTrue(is_prime(3))
+        self.assertTrue(is_prime(5))
+        self.assertTrue(is_prime(7))
+        self.assertTrue(is_prime(11))
+        self.assertTrue(is_prime(13))
+        self.assertTrue(is_prime(17))
+        self.assertTrue(is_prime(19))
+        self.assertTrue(is_prime(23))
+        self.assertTrue(is_prime(29))
 
     def test_not_primes(self):
         self.assertFalse(
-            prime_check(-19),
+            is_prime(-19),
             "Negative numbers are excluded by definition of prime numbers.",
         )
         self.assertFalse(
-            prime_check(0),
+            is_prime(0),
             "Zero doesn't have any positive factors, primes must have exactly two.",
         )
         self.assertFalse(
-            prime_check(1),
+            is_prime(1),
             "One only has 1 positive factor, primes must have exactly two.",
         )
-        self.assertFalse(prime_check(2 * 2))
-        self.assertFalse(prime_check(2 * 3))
-        self.assertFalse(prime_check(3 * 3))
-        self.assertFalse(prime_check(3 * 5))
-        self.assertFalse(prime_check(3 * 5 * 7))
+        self.assertFalse(is_prime(2 * 2))
+        self.assertFalse(is_prime(2 * 3))
+        self.assertFalse(is_prime(3 * 3))
+        self.assertFalse(is_prime(3 * 5))
+        self.assertFalse(is_prime(3 * 5 * 7))
 
 
 if __name__ == "__main__":
diff --git a/maths/primelib.py b/maths/primelib.py
index 37883d9cf..3da9c56f6 100644
--- a/maths/primelib.py
+++ b/maths/primelib.py
@@ -41,7 +41,7 @@ goldbach(number)  // Goldbach's assumption
 from math import sqrt
 
 
-def isPrime(number):
+def is_prime(number: int) -> bool:
     """
     input: positive integer 'number'
     returns true if 'number' is prime otherwise false.
@@ -129,7 +129,7 @@ def getPrimeNumbers(N):
     # if a number is prime then appends to list 'ans'
     for number in range(2, N + 1):
 
-        if isPrime(number):
+        if is_prime(number):
 
             ans.append(number)
 
@@ -164,11 +164,11 @@ def primeFactorization(number):
         ans.append(number)
 
     # if 'number' not prime then builds the prime factorization of 'number'
-    elif not isPrime(number):
+    elif not is_prime(number):
 
         while quotient != 1:
 
-            if isPrime(factor) and (quotient % factor == 0):
+            if is_prime(factor) and (quotient % factor == 0):
                 ans.append(factor)
                 quotient /= factor
             else:
@@ -317,8 +317,8 @@ def goldbach(number):
         isinstance(ans, list)
         and (len(ans) == 2)
         and (ans[0] + ans[1] == number)
-        and isPrime(ans[0])
-        and isPrime(ans[1])
+        and is_prime(ans[0])
+        and is_prime(ans[1])
     ), "'ans' must contains two primes. And sum of elements must been eq 'number'"
 
     return ans
@@ -462,11 +462,11 @@ def getPrime(n):
 
         # if ans not prime then
         # runs to the next prime number.
-        while not isPrime(ans):
+        while not is_prime(ans):
             ans += 1
 
     # precondition
-    assert isinstance(ans, int) and isPrime(
+    assert isinstance(ans, int) and is_prime(
         ans
     ), "'ans' must been a prime number and from type int"
 
@@ -486,7 +486,7 @@ def getPrimesBetween(pNumber1, pNumber2):
 
     # precondition
     assert (
-        isPrime(pNumber1) and isPrime(pNumber2) and (pNumber1 < pNumber2)
+        is_prime(pNumber1) and is_prime(pNumber2) and (pNumber1 < pNumber2)
     ), "The arguments must been prime numbers and 'pNumber1' < 'pNumber2'"
 
     number = pNumber1 + 1  # jump to the next number
@@ -495,7 +495,7 @@ def getPrimesBetween(pNumber1, pNumber2):
 
     # if number is not prime then
     # fetch the next prime number.
-    while not isPrime(number):
+    while not is_prime(number):
         number += 1
 
     while number < pNumber2:
@@ -505,7 +505,7 @@ def getPrimesBetween(pNumber1, pNumber2):
         number += 1
 
         # fetch the next prime number.
-        while not isPrime(number):
+        while not is_prime(number):
             number += 1
 
     # precondition