"""
Math Utilities Module

This module provides mathematical utility functions including
factorial calculation, prime checking, Fibonacci sequence generation,
and greatest common divisor calculation.
"""


def factorial(n: int) -> int:
    """
    Calculate the factorial of a non-negative integer.
    
    Args:
        n: A non-negative integer
        
    Returns:
        The factorial of n (n!)
        
    Raises:
        ValueError: If n is negative
        
    Example:
        >>> factorial(5)
        120
        >>> factorial(0)
        1
    """
    if not isinstance(n, int):
        raise TypeError("Input must be an integer")
    if n < 0:
        raise ValueError("Factorial is not defined for negative numbers")
    if n == 0 or n == 1:
        return 1
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result


def is_prime(n: int) -> bool:
    """
    Check if a number is prime.
    
    Args:
        n: An integer to check
        
    Returns:
        True if n is prime, False otherwise
        
    Example:
        >>> is_prime(7)
        True
        >>> is_prime(4)
        False
    """
    if not isinstance(n, int):
        raise TypeError("Input must be an integer")
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    for i in range(3, int(n ** 0.5) + 1, 2):
        if n % i == 0:
            return False
    return True


def fibonacci(n: int) -> list:
    """
    Generate the first n numbers in the Fibonacci sequence.
    
    Args:
        n: The number of Fibonacci numbers to generate
        
    Returns:
        A list containing the first n Fibonacci numbers
        
    Raises:
        ValueError: If n is negative
        
    Example:
        >>> fibonacci(7)
        [0, 1, 1, 2, 3, 5, 8]
        >>> fibonacci(1)
        [0]
    """
    if not isinstance(n, int):
        raise TypeError("Input must be an integer")
    if n < 0:
        raise ValueError("Number of terms must be non-negative")
    if n == 0:
        return []
    if n == 1:
        return [0]
    
    fib_sequence = [0, 1]
    for i in range(2, n):
        fib_sequence.append(fib_sequence[i-1] + fib_sequence[i-2])
    return fib_sequence


def gcd(a: int, b: int) -> int:
    """
    Calculate the Greatest Common Divisor (GCD) of two integers.
    
    Uses the Euclidean algorithm to find the GCD.
    
    Args:
        a: First integer
        b: Second integer
        
    Returns:
        The greatest common divisor of a and b
        
    Example:
        >>> gcd(48, 18)
        6
        >>> gcd(17, 5)
        1
    """
    if not isinstance(a, int) or not isinstance(b, int):
        raise TypeError("Both inputs must be integers")
    a, b = abs(a), abs(b)
    while b:
        a, b = b, a % b
    return a
