Skip to content
33 changes: 33 additions & 0 deletions src/gcd_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from src.prime_factorization import prime_factorization
from collections import Counter

def gcd_using_prime_factors(a: int, b: int) -> int:
"""
Calculate the Greatest Common Divisor (GCD) of two numbers using prime factorization.

Args:
a (int): First positive integer
b (int): Second positive integer

Returns:
int: The Greatest Common Divisor of a and b

Raises:
ValueError: If either input is less than or equal to 0
"""
# Validate inputs
if a <= 0 or b <= 0:
raise ValueError("Inputs must be positive integers")

# Get prime factorizations of both numbers
a_factors = Counter(prime_factorization(a))
b_factors = Counter(prime_factorization(b))

# Find common prime factors
gcd = 1
for prime, count in a_factors.items():
if prime in b_factors:
# Take the minimum count of each common prime factor
gcd *= prime ** min(count, b_factors[prime])

return gcd
40 changes: 40 additions & 0 deletions src/prime_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
def is_prime(n):
"""
Check if a given number is prime.

A prime number is a natural number greater than 1 that is only divisible by 1 and itself.

Args:
n (int): The number to check for primality.

Returns:
bool: True if the number is prime, False otherwise.

Raises:
TypeError: If the input is not an integer.
ValueError: If the input is less than or equal to 0.
"""
# Check input type
if not isinstance(n, int):
raise TypeError("Input must be an integer")

# Check input range
if n <= 0:
raise ValueError("Input must be a positive integer greater than 0")

# Handle small prime numbers
if n == 1:
return False
if n <= 3:
return True

# Optimize for even numbers
if n % 2 == 0:
return False

# Check for divisibility up to the square root of n
for i in range(3, int(n**0.5) + 1, 2):
if n % i == 0:
return False

return True
47 changes: 47 additions & 0 deletions src/prime_factorization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
def prime_factorization(n):
"""
Compute the prime factorization of a given positive integer.

Args:
n (int): A positive integer to factorize.

Returns:
list: A list of prime factors in ascending order.

Raises:
ValueError: If the input is not a positive integer.
"""
# Validate input
if not isinstance(n, int):
raise ValueError("Input must be an integer")

if n <= 0:
raise ValueError("Input must be a positive integer")

# Special case for 1
if n == 1:
return []

# Initialize list to store prime factors
factors = []

# Handle 2 as a special case to optimize the loop
while n % 2 == 0:
factors.append(2)
n //= 2

# Check for odd prime factors
# We only need to check up to sqrt(n)
factor = 3
while factor * factor <= n:
if n % factor == 0:
factors.append(factor)
n //= factor
else:
factor += 2

# If n is a prime number greater than 2
if n > 2:
factors.append(n)

return factors
38 changes: 38 additions & 0 deletions tests/test_gcd_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pytest
from src.gcd_calculator import gcd_using_prime_factors

def test_gcd_using_prime_factors_basic():
"""Test basic GCD calculations"""
assert gcd_using_prime_factors(48, 18) == 6
assert gcd_using_prime_factors(54, 24) == 6
assert gcd_using_prime_factors(17, 23) == 1
assert gcd_using_prime_factors(100, 75) == 25

def test_gcd_using_prime_factors_same_number():
"""Test GCD when both numbers are the same"""
assert gcd_using_prime_factors(7, 7) == 7
assert gcd_using_prime_factors(100, 100) == 100

def test_gcd_using_prime_factors_one_is_multiple():
"""Test GCD when one number is a multiple of the other"""
assert gcd_using_prime_factors(12, 36) == 12
assert gcd_using_prime_factors(36, 12) == 12

def test_gcd_using_prime_factors_coprime():
"""Test GCD of coprime numbers"""
assert gcd_using_prime_factors(5, 11) == 1
assert gcd_using_prime_factors(7, 13) == 1

def test_gcd_using_prime_factors_invalid_input():
"""Test error handling for invalid inputs"""
with pytest.raises(ValueError, match="Inputs must be positive integers"):
gcd_using_prime_factors(0, 5)

with pytest.raises(ValueError, match="Inputs must be positive integers"):
gcd_using_prime_factors(5, 0)

with pytest.raises(ValueError, match="Inputs must be positive integers"):
gcd_using_prime_factors(-5, 5)

with pytest.raises(ValueError, match="Inputs must be positive integers"):
gcd_using_prime_factors(5, -5)
38 changes: 38 additions & 0 deletions tests/test_prime_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pytest
from src.prime_checker import is_prime

def test_prime_numbers():
"""Test known prime numbers"""
prime_numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
for num in prime_numbers:
assert is_prime(num) is True, f"{num} should be prime"

def test_non_prime_numbers():
"""Test known non-prime numbers"""
non_prime_numbers = [1, 4, 6, 8, 9, 10, 12, 14, 15, 16]
for num in non_prime_numbers:
assert is_prime(num) is False, f"{num} should not be prime"

def test_large_prime_number():
"""Test a larger prime number"""
assert is_prime(997) is True

def test_large_non_prime_number():
"""Test a larger non-prime number"""
assert is_prime(1000) is False

def test_invalid_input_types():
"""Test invalid input types"""
with pytest.raises(TypeError):
is_prime(3.14)
with pytest.raises(TypeError):
is_prime("7")
with pytest.raises(TypeError):
is_prime([7])

def test_invalid_input_range():
"""Test invalid input ranges"""
with pytest.raises(ValueError):
is_prime(0)
with pytest.raises(ValueError):
is_prime(-5)
31 changes: 31 additions & 0 deletions tests/test_prime_factorization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import pytest
from src.prime_factorization import prime_factorization

def test_prime_factorization_basic():
assert prime_factorization(12) == [2, 2, 3]
assert prime_factorization(15) == [3, 5]
assert prime_factorization(100) == [2, 2, 5, 5]

def test_prime_factorization_prime_numbers():
assert prime_factorization(7) == [7]
assert prime_factorization(11) == [11]
assert prime_factorization(23) == [23]

def test_prime_factorization_one():
assert prime_factorization(1) == []

def test_prime_factorization_large_number():
assert prime_factorization(84) == [2, 2, 3, 7]

def test_prime_factorization_edge_cases():
with pytest.raises(ValueError):
prime_factorization(0)

with pytest.raises(ValueError):
prime_factorization(-5)

with pytest.raises(ValueError):
prime_factorization(3.14)

with pytest.raises(ValueError):
prime_factorization("not a number")