Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
Binary file not shown.
95 changes: 95 additions & 0 deletions src/gcd_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
def get_prime_factors(n):
"""
Decompose a number into its prime factors.

Args:
n (int): The number to factorize.

Returns:
list: A list of prime factors.

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

# Handle special cases
if n < 0:
n = abs(n)

if n <= 1:
return []

prime_factors = []
divisor = 2

while divisor * divisor <= n:
if n % divisor == 0:
prime_factors.append(divisor)
n //= divisor
else:
divisor += 1

# If n is still greater than 1, it's a prime factor itself
if n > 1:
prime_factors.append(n)

return prime_factors

def gcd_prime_factors(a, b):
"""
Calculate the Greatest Common Divisor (GCD) using prime factorization.

Args:
a (int): First number.
b (int): Second number.

Returns:
int: The Greatest Common Divisor.

Raises:
ValueError: If inputs are not integers.
"""
# Validate inputs
if not isinstance(a, int) or not isinstance(b, int):
raise ValueError("Inputs must be integers")

# Handle special cases
if a == 0 and b == 0:
raise ValueError("GCD is undefined when both numbers are zero")

# Handle zero case
if a == 0:
return abs(b)
if b == 0:
return abs(a)

# Get absolute values
a, b = abs(a), abs(b)

# Get prime factors
a_factors = get_prime_factors(a)
b_factors = get_prime_factors(b)

# Find common prime factors
gcd = 1
a_factor_count = {}
b_factor_count = {}

# Count factors in a
for factor in a_factors:
a_factor_count[factor] = a_factor_count.get(factor, 0) + 1

# Count factors in b
for factor in b_factors:
b_factor_count[factor] = b_factor_count.get(factor, 0) + 1

# Calculate GCD by multiplying common factors
for factor, count in a_factor_count.items():
if factor in b_factor_count:
common_count = min(count, b_factor_count[factor])
gcd *= factor ** common_count

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

Args:
number (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 2.
"""
# Validate input type
if not isinstance(number, int):
raise TypeError("Input must be an integer")

# Handle edge cases
if number < 2:
return False

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

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

Args:
n (int): Number to check for primality.

Returns:
bool: True if the number is prime, False otherwise.
"""
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True

def prime_factorization(n):
"""
Compute the prime factorization of a given integer.

Args:
n (int): The number to factorize.

Returns:
dict: A dictionary of prime factors and their frequencies.

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

if n < 0:
raise ValueError("Input must be a non-negative integer")

if n < 2:
return {}

# Initialize factors dictionary
factors = {}

# Validate primality of initial value
if not is_prime(n) or n == 1:
# Handle 2 as a special case to optimize odd number factorization
while n % 2 == 0:
factors[2] = factors.get(2, 0) + 1
n //= 2

# Check odd factors up to square root of n
factor = 3
while factor * factor <= n:
# Validate primality of factor
if is_prime(factor):
while n % factor == 0:
factors[factor] = factors.get(factor, 0) + 1
n //= factor
factor += 2

# If n is a prime number greater than 2
if n > 2 and is_prime(n):
factors[n] = factors.get(n, 0) + 1
else:
# n itself is prime
factors[n] = 1

return factors
Binary file not shown.
48 changes: 48 additions & 0 deletions tests/test_gcd_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import pytest
from src.gcd_calculator import get_prime_factors, gcd_prime_factors

def test_get_prime_factors():
# Test basic prime factorization
assert get_prime_factors(12) == [2, 2, 3]
assert get_prime_factors(100) == [2, 2, 5, 5]
assert get_prime_factors(7) == [7]
assert get_prime_factors(1) == []

# Test zero and negative numbers
assert get_prime_factors(0) == []
assert get_prime_factors(-12) == [2, 2, 3]

def test_gcd_prime_factors():
# Test basic GCD calculations
assert gcd_prime_factors(48, 18) == 6
assert gcd_prime_factors(54, 24) == 6
assert gcd_prime_factors(17, 23) == 1
assert gcd_prime_factors(100, 75) == 25

# Test with zero
assert gcd_prime_factors(0, 5) == 5
assert gcd_prime_factors(5, 0) == 5

# Test with negative numbers
assert gcd_prime_factors(-48, 18) == 6
assert gcd_prime_factors(48, -18) == 6
assert gcd_prime_factors(-48, -18) == 6

def test_gcd_error_handling():
# Test invalid inputs
with pytest.raises(ValueError, match="Inputs must be integers"):
gcd_prime_factors(3.14, 5)

with pytest.raises(ValueError, match="Inputs must be integers"):
gcd_prime_factors("10", 5)

with pytest.raises(ValueError, match="GCD is undefined when both numbers are zero"):
gcd_prime_factors(0, 0)

def test_get_prime_factors_error_handling():
# Test invalid inputs
with pytest.raises(ValueError, match="Input must be an integer"):
get_prime_factors(3.14)

with pytest.raises(ValueError, match="Input must be an integer"):
get_prime_factors("10")
35 changes: 35 additions & 0 deletions tests/test_prime_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import pytest
from src.prime_checker import is_prime

def test_prime_numbers():
"""Test numbers that are known to be prime."""
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 numbers that are known to be non-prime."""
non_prime_numbers = [0, 1, 4, 6, 8, 9, 10, 12, 14, 15]
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 large prime number."""
assert is_prime(104729) is True, "Large prime number not correctly identified"

def test_large_non_prime_number():
"""Test a large non-prime number."""
assert is_prime(104730) is False, "Large non-prime number not correctly identified"

def test_negative_numbers():
"""Test that negative numbers are not considered prime."""
negative_numbers = [-1, -2, -3, -5, -7]
for num in negative_numbers:
assert is_prime(num) is False, f"{num} should not be prime"

def test_invalid_input_type():
"""Test that non-integer inputs raise a TypeError."""
invalid_inputs = [3.14, "7", [7], None]
for invalid_input in invalid_inputs:
with pytest.raises(TypeError):
is_prime(invalid_input)
59 changes: 59 additions & 0 deletions tests/test_prime_factorization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import pytest
from src.prime_factorization import prime_factorization, is_prime

def test_is_prime():
"""Test prime number checking function."""
assert is_prime(2) == True
assert is_prime(3) == True
assert is_prime(7) == True
assert is_prime(11) == True
assert is_prime(4) == False
assert is_prime(15) == False
assert is_prime(1) == False
assert is_prime(0) == False

def test_prime_number_factorization():
"""Test factorization of prime numbers."""
assert prime_factorization(7) == {7: 1}
assert prime_factorization(11) == {11: 1}
assert prime_factorization(13) == {13: 1}

def test_composite_number_factorization():
"""Test factorization of composite numbers."""
assert prime_factorization(24) == {2: 3, 3: 1}
assert prime_factorization(100) == {2: 2, 5: 2}
assert prime_factorization(84) == {2: 2, 3: 1, 7: 1}

def test_edge_cases():
"""Test edge cases like 0, 1, and small numbers."""
assert prime_factorization(0) == {}
assert prime_factorization(1) == {}
assert prime_factorization(2) == {2: 1}

def test_error_handling():
"""Test error handling for invalid inputs."""
with pytest.raises(ValueError):
prime_factorization(-5)

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

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

def test_large_number():
"""Test factorization of a relatively large number."""
result = prime_factorization(123456)

# Verify the result contains only prime keys
assert all(is_prime(key) for key in result.keys())

# Verify the product of prime factors equals the original number
product = 1
for prime, freq in result.items():
product *= (prime ** freq)
assert product == 123456

# Verify the number of prime factors
total_factors = sum(result.values())
assert total_factors > 0