Skip to content
Open
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
91 changes: 91 additions & 0 deletions src/gcd_calculator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from typing import List, Union
from math import sqrt

def get_prime_factors(n: int) -> List[int]:
"""
Decompose a number into its prime factors.

Args:
n (int): The number to factorize (must be a positive integer)

Returns:
List[int]: A list of prime factors

Raises:
ValueError: If input is less than 1
"""
if n < 1:
raise ValueError("Input must be a positive integer")

# Handle special cases
if n == 1:
return [1]

factors = []

# Check for 2 as a factor
while n % 2 == 0:
factors.append(2)
n //= 2

# Check for odd prime factors
for i in range(3, int(sqrt(n)) + 1, 2):
while n % i == 0:
factors.append(i)
n //= i

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

return factors

def gcd_prime_factors(a: int, b: int) -> int:
"""
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 either input is less than 1
"""
# Handle zero cases
if a == 0 and b == 0:
return 0

# Take absolute values to handle negative inputs
a, b = abs(a), abs(b)

# If either number is 0, return the other number
if a == 0:
return b
if b == 0:
return a

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

# Calculate GCD by multiplying common prime factors
gcd = 1
a_factor_counts = {}
b_factor_counts = {}

# Count occurrences of factors
for factor in a_factors:
a_factor_counts[factor] = a_factor_counts.get(factor, 0) + 1
for factor in b_factors:
b_factor_counts[factor] = b_factor_counts.get(factor, 0) + 1

# Find common factors with minimum count
for factor, count in a_factor_counts.items():
if factor in b_factor_counts:
common_count = min(count, b_factor_counts[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: int) -> bool:
"""
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 special 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
47 changes: 47 additions & 0 deletions src/prime_factorization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import List

def prime_factorization(n: int) -> List[int]:
"""
Compute the prime factorization of a given positive integer.

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

Returns:
List[int]: A list of prime factors in ascending order.

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

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

# Handle special cases
if n == 1:
return []

# Prime factorization algorithm
factors = []

# First, handle 2 as a special case to optimize odd number checking
while n % 2 == 0:
factors.append(2)
n //= 2

# Check for odd prime factors
factor = 3
while factor * factor <= n:
while n % factor == 0:
factors.append(factor)
n //= factor
factor += 2

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

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

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

def test_prime_factors_edge_cases():
# Test edge cases
with pytest.raises(ValueError):
get_prime_factors(0)
with pytest.raises(ValueError):
get_prime_factors(-5)

def test_gcd_prime_factors():
# Test various GCD scenarios
assert gcd_prime_factors(48, 18) == 6
assert gcd_prime_factors(54, 24) == 6
assert gcd_prime_factors(17, 23) == 1 # Coprime numbers
assert gcd_prime_factors(0, 5) == 5
assert gcd_prime_factors(5, 0) == 5
assert gcd_prime_factors(0, 0) == 0

def test_gcd_with_negative_numbers():
# Test GCD 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_large_numbers():
# Test large numbers
assert gcd_prime_factors(1234567, 7654321) == 1 # Coprime large numbers
assert gcd_prime_factors(1000000, 10000) == 10000
37 changes: 37 additions & 0 deletions tests/test_prime_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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, 31, 37, 41, 43, 47]
for prime in prime_numbers:
assert is_prime(prime) == True, f"{prime} should be prime"

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

def test_large_prime():
"""Test a large prime number"""
assert is_prime(104729) == True, "Large prime number not recognized"

def test_large_non_prime():
"""Test a large non-prime number"""
assert is_prime(100000) == False, "Large non-prime number not detected"

def test_negative_numbers():
"""Test negative numbers"""
assert is_prime(-7) == False, "Negative numbers should not be prime"

def test_invalid_input_type():
"""Test invalid input types"""
with pytest.raises(TypeError):
is_prime("not a number")

with pytest.raises(TypeError):
is_prime(3.14)

with pytest.raises(TypeError):
is_prime(None)
40 changes: 40 additions & 0 deletions tests/test_prime_factorization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
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(17) == [17]

def test_prime_factorization_edge_cases():
assert prime_factorization(1) == []
assert prime_factorization(2) == [2]

def test_prime_factorization_large_number():
result = prime_factorization(84)
assert result == [2, 2, 3, 7]
assert all(is_prime(factor) for factor in result)

def test_prime_factorization_invalid_inputs():
with pytest.raises(ValueError):
prime_factorization(0)
with pytest.raises(ValueError):
prime_factorization(-5)
with pytest.raises(TypeError):
prime_factorization(3.14)
with pytest.raises(TypeError):
prime_factorization("not a number")

def is_prime(n: int) -> bool:
"""Helper function to check if a number is prime."""
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True