#!/usr/bin/env python3
"""
FastMCP Server for Factorial Calculations

This server provides two tools:
1. factorial_value - Returns the exact value of n!
2. factorial_digits - Returns the number of digits in n!
"""

import math
from fastmcp import FastMCP

# Initialize FastMCP server
mcp = FastMCP("Factorial Calculator")


def compute_factorial(n: int) -> int:
    """
    Helper function to compute factorial of n.
    
    Args:
        n: Non-negative integer
        
    Returns:
        The factorial of n (n!)
        
    Raises:
        ValueError: If n is negative
    """
    if n < 0:
        raise ValueError("Factorial is not defined for negative numbers")
    return math.factorial(n)


@mcp.tool()
def factorial_value(n: int) -> str:
    """
    Calculate and return the exact value of n! (factorial of n).
    
    Args:
        n: A non-negative integer
        
    Returns:
        The exact factorial value as a string
        
    Example:
        factorial_value(5) returns "120"
        factorial_value(10) returns "3628800"
    """
    try:
        result = compute_factorial(n)
        return f"The factorial of {n} is: {result}"
    except ValueError as e:
        return f"Error: {str(e)}"
    except Exception as e:
        return f"Unexpected error: {str(e)}"


@mcp.tool()
def factorial_digits(n: int) -> str:
    """
    Return the number of decimal digits in n! without computing the full value.
    This is useful when n! is too large to display in full.
    
    Args:
        n: A non-negative integer
        
    Returns:
        The number of digits in n! as a string
        
    Example:
        factorial_digits(5) returns "3" (120 has 3 digits)
        factorial_digits(100) returns "158" (100! has 158 digits)
    """
    try:
        if n < 0:
            raise ValueError("Factorial is not defined for negative numbers")
        
        # For small values, compute directly
        if n <= 20:
            result = compute_factorial(n)
            num_digits = len(str(result))
            return f"The factorial of {n} has {num_digits} digit(s)"
        
        # For larger values, use Stirling's approximation via logarithms
        # Number of digits in n! = floor(log10(n!)) + 1
        # Using Stirling's approximation: log10(n!) ≈ n*log10(n) - n*log10(e) + 0.5*log10(2*pi*n)
        import math
        log_factorial = sum(math.log10(i) for i in range(1, n + 1))
        num_digits = int(log_factorial) + 1
        
        return f"The factorial of {n} has approximately {num_digits} digit(s)"
    except ValueError as e:
        return f"Error: {str(e)}"
    except Exception as e:
        return f"Unexpected error: {str(e)}"


if __name__ == "__main__":
    # Run the server
    mcp.run()

# Made with Bob
