""" Core proportion calculation tools with Gradio MCP server capabilities. Provides reliable mathematical operations for LLM agents. """ import logging from typing import Optional, Tuple import gradio as gr from config import get_settings, setup_logging, get_deployment_info # Setup logging setup_logging() logger = logging.getLogger(__name__) settings = get_settings() def percent_of(part: float, whole: float) -> float: """ Calculate what percentage 'part' is of 'whole'. Args: part: The part value whole: The whole value (must not be zero) Returns: The percentage value Mathematical preconditions: - whole != 0 (to avoid division by zero) """ # Mathematical preconditions assert whole != 0, "Division by zero: whole cannot be zero" # Calculate percentage percentage = (part / whole) * 100 logger.debug(f"Calculated {part} is {percentage:.2f}% of {whole}") return percentage def solve_proportion( a: Optional[float] = None, b: Optional[float] = None, c: Optional[float] = None, d: Optional[float] = None ) -> float: """ Solve missing term in proportion a/b = c/d. Exactly one parameter must be None (missing). Args: a: First numerator (optional) b: First denominator (optional, cannot be zero) c: Second numerator (optional) d: Second denominator (optional, cannot be zero) Returns: The missing value Mathematical preconditions: - Exactly one value must be None (missing) - Division denominators != 0 (varies by missing value) """ # Mathematical preconditions - exactly one value must be None values = [a, b, c, d] none_count = sum(1 for v in values if v is None) assert none_count == 1, "Exactly one value must be None" # Solve for missing value using cross multiplication: a*d = b*c if a is None: assert d != 0, "Division by zero: denominator" missing = (b * c) / d elif b is None: assert c != 0, "Division by zero: denominator" missing = (a * d) / c elif c is None: assert b != 0, "Division by zero: denominator" missing = (a * d) / b else: # d is None assert a != 0, "Division by zero: denominator" missing = (b * c) / a logger.debug(f"Solved proportion: missing value = {missing}") return missing def scale_by_ratio(value: float, ratio: float) -> float: """ Scale a value by a given ratio. Args: value: The value to scale ratio: The scaling ratio Returns: The scaled value """ # Calculate scaled result scaled_result = value * ratio logger.debug(f"Scaled {value} by ratio {ratio} = {scaled_result}") return scaled_result def direct_k(x: float, y: float) -> float: """ Find constant of proportionality k in direct variation y = kx. Args: x: The x value (cannot be zero) y: The y value Returns: The proportionality constant k Mathematical preconditions: - x != 0 (to avoid division by zero) """ # Mathematical preconditions assert x != 0, "Division by zero: x cannot be zero" # Calculate proportionality constant k = y / x logger.debug(f"Found proportionality constant k = {k} for y = {y}, x = {x}") return k def resize_dimensions(width: float, height: float, scale: float) -> Tuple[float, float]: """ Resize dimensions with uniform scale factor. Args: width: Original width (must be non-negative) height: Original height (must be non-negative) scale: Scale factor (must be positive) Returns: Tuple of (new_width, new_height) Mathematical preconditions: - width >= 0 (dimensions must be non-negative) - height >= 0 (dimensions must be non-negative) - scale > 0 (scale factor must be positive) """ # Mathematical preconditions assert width >= 0, "Width must be non-negative" assert height >= 0, "Height must be non-negative" assert scale > 0, "Scale factor must be positive" # Calculate new dimensions new_width = width * scale new_height = height * scale logger.debug(f"Resized {width}x{height} by {scale} = {new_width}x{new_height}") return (new_width, new_height) # Demo functions for Gradio interface def demo_percent_of(part: float, whole: float) -> str: """Demo function for percent_of calculation.""" result = percent_of(part, whole) return f"{part} is {result:.2f}% of {whole}" def demo_solve_proportion(a: Optional[str], b: Optional[str], c: Optional[str], d: Optional[str]) -> str: """Demo function for proportion solving.""" # Convert string inputs to float or None a_val = None if a == "" or a is None else float(a) b_val = None if b == "" or b is None else float(b) c_val = None if c == "" or c is None else float(c) d_val = None if d == "" or d is None else float(d) result = solve_proportion(a_val, b_val, c_val, d_val) return f"Missing value: {result:.4f}" def demo_scale_by_ratio(value: float, ratio: float) -> str: """Demo function for scaling by ratio.""" result = scale_by_ratio(value, ratio) return f"{value} ร {ratio} = {result:.4f}" def demo_direct_k(x: float, y: float) -> str: """Demo function for finding proportionality constant.""" result = direct_k(x, y) return f"k = {result:.4f} (where y = kx)" def demo_resize_dimensions(width: float, height: float, scale: float) -> str: """Demo function for resizing dimensions.""" new_width, new_height = resize_dimensions(width, height, scale) return f"New dimensions: {new_width:.2f} ร {new_height:.2f}" def create_gradio_app(): """Create and return the Gradio interface.""" with gr.Blocks( title="PROPORTIO", css_paths=["styles.css"], theme=gr.themes.Base(), head='' ) as demo: with gr.Row(): with gr.Column(scale=1, min_width=72): gr.Image("logo.png", height=72, width=72, show_label=False, show_download_button=False, show_fullscreen_button=False, container=False, interactive=False) with gr.Column(scale=10): gr.HTML("""