import gradio as gr import numpy as np from typing import Dict, Any, Tuple import traceback from calculator.core import ( InfinitePrecisionCalculator, ScientificCalculator, ProgrammingCalculator, ComplexNumber ) from engineering.impedance import ( PowerSystemImpedanceCalculator, ConductorParams, Coordinate, create_test_data ) class CalculatorApp: """Main calculator application with multiple modes.""" def __init__(self): self.standard_calc = InfinitePrecisionCalculator() self.scientific_calc = ScientificCalculator() self.programming_calc = ProgrammingCalculator() self.impedance_calc = PowerSystemImpedanceCalculator() self.current_display = "0" self.current_operation = None self.operand = None self.chat_history = [] def standard_button_click(self, button: str, display: str) -> str: """Handle standard calculator button clicks.""" try: if button == "C": return "0" elif button == "CE": return "0" elif button == "±": if display != "0": return str(-float(display)) return display elif button == "=": if self.current_operation and self.operand is not None: try: if self.current_operation == "+": result = self.standard_calc.add(self.operand, float(display)) elif self.current_operation == "-": result = self.standard_calc.subtract(self.operand, float(display)) elif self.current_operation == "×": result = self.standard_calc.multiply(self.operand, float(display)) elif self.current_operation == "÷": result = self.standard_calc.divide(self.operand, float(display)) self.current_operation = None self.operand = None return str(result) except Exception as e: return f"Error: {str(e)}" elif button in ["+", "-", "×", "÷"]: self.current_operation = button self.operand = float(display) return "0" elif button == ".": if "." not in display: return display + "." return display else: # Number button if display == "0": return button else: return display + button except Exception as e: return f"Error: {str(e)}" return display def scientific_calculate(self, function: str, x: str, y: str = None) -> str: """Handle scientific calculator functions.""" try: if not x.strip(): return "Error: Please enter a value" x_val = float(x) if function == "sin": result = self.scientific_calc.sin(x_val) elif function == "cos": result = self.scientific_calc.cos(x_val) elif function == "tan": result = self.scientific_calc.tan(x_val) elif function == "asin": result = self.scientific_calc.asin(x_val) elif function == "acos": result = self.scientific_calc.acos(x_val) elif function == "atan": result = self.scientific_calc.atan(x_val) elif function == "csc": result = self.scientific_calc.csc(x_val) elif function == "sec": result = self.scientific_calc.sec(x_val) elif function == "cot": result = self.scientific_calc.cot(x_val) elif function == "log": if y and y.strip(): result = self.scientific_calc.log(x_val, float(y)) else: result = self.scientific_calc.log(x_val) elif function == "ln": result = self.scientific_calc.log(x_val) elif function == "sqrt": result = self.scientific_calc.square_root(x_val) elif function == "factorial": result = self.scientific_calc.factorial(int(x_val)) elif function == "power": if y and y.strip(): result = self.scientific_calc.power(x_val, float(y)) else: return "Error: Power function requires two values" else: return f"Error: Unknown function {function}" return str(result) except Exception as e: return f"Error: {str(e)}" def programming_calculate(self, operation: str, input_val: str, base_from: str = "decimal", base_to: str = "decimal") -> str: """Handle programming calculator operations.""" try: if operation == "convert": if base_from == "decimal": num = int(input_val) if base_to == "binary": return self.programming_calc.to_binary(num) elif base_to == "hexadecimal": return self.programming_calc.to_hexadecimal(num) elif base_to == "octal": return self.programming_calc.to_octal(num) elif base_from == "binary": num = self.programming_calc.from_binary(input_val) if base_to == "decimal": return str(num) elif base_to == "hexadecimal": return self.programming_calc.to_hexadecimal(num) elif base_to == "octal": return self.programming_calc.to_octal(num) elif base_from == "hexadecimal": num = self.programming_calc.from_hexadecimal(input_val) if base_to == "decimal": return str(num) elif base_to == "binary": return self.programming_calc.to_binary(num) elif base_to == "octal": return self.programming_calc.to_octal(num) elif base_from == "octal": num = self.programming_calc.from_octal(input_val) if base_to == "decimal": return str(num) elif base_to == "binary": return self.programming_calc.to_binary(num) elif base_to == "hexadecimal": return self.programming_calc.to_hexadecimal(num) return "Conversion completed" except Exception as e: return f"Error: {str(e)}" def engineering_calculate(self, function: str, **kwargs) -> str: """Handle engineering calculations.""" try: if function == "3ph_impedance": return self.calculate_3ph_impedance(**kwargs) else: return f"Error: Unknown engineering function {function}" except Exception as e: return f"Error: {str(e)}\n{traceback.format_exc()}" def calculate_3ph_impedance(self, coord_a_x: float, coord_a_y: float, coord_b_x: float, coord_b_y: float, coord_c_x: float, coord_c_y: float, coord_n_x: float, coord_n_y: float, coord_pe_x: float, coord_pe_y: float, resist_a: float, gmr_a: float, resist_b: float, gmr_b: float, resist_c: float, gmr_c: float, resist_n: float, gmr_n: float, resist_pe: float, gmr_pe: float) -> str: """Calculate 3-phase impedance from input parameters.""" # Create coordinates coordinates = { 'a': Coordinate(x=coord_a_x, y=coord_a_y), 'b': Coordinate(x=coord_b_x, y=coord_b_y), 'c': Coordinate(x=coord_c_x, y=coord_c_y), 'n': Coordinate(x=coord_n_x, y=coord_n_y), 'pe': Coordinate(x=coord_pe_x, y=coord_pe_y) } # Create conductor parameters conductor_params = { 'a': ConductorParams(resistance=resist_a, gmr=gmr_a), 'b': ConductorParams(resistance=resist_b, gmr=gmr_b), 'c': ConductorParams(resistance=resist_c, gmr=gmr_c), 'n': ConductorParams(resistance=resist_n, gmr=gmr_n), 'pe': ConductorParams(resistance=resist_pe, gmr=gmr_pe) } # Calculate impedance primitive_matrix, reduced_matrix, distances = self.impedance_calc.calculate_impedance_from_coordinates( coordinates, conductor_params ) # Format results return self.impedance_calc.format_results_for_display( primitive_matrix, reduced_matrix, distances ) def load_test_data(self) -> Tuple[float, ...]: """Load test data for 3-phase impedance calculation.""" conductor_params, coordinates = create_test_data() return ( # Coordinates coordinates['a'].x, coordinates['a'].y, coordinates['b'].x, coordinates['b'].y, coordinates['c'].x, coordinates['c'].y, coordinates['n'].x, coordinates['n'].y, coordinates['pe'].x, coordinates['pe'].y, # Conductor parameters conductor_params['a'].resistance, conductor_params['a'].gmr, conductor_params['b'].resistance, conductor_params['b'].gmr, conductor_params['c'].resistance, conductor_params['c'].gmr, conductor_params['n'].resistance, conductor_params['n'].gmr, conductor_params['pe'].resistance, conductor_params['pe'].gmr ) def get_history(self) -> str: """Get calculation history.""" history = self.standard_calc.get_history() if not history: return "No calculations yet" return "\n".join(history[-10:]) # Show last 10 calculations def clear_history(self) -> str: """Clear calculation history.""" self.standard_calc.clear_history() self.scientific_calc.clear_history() self.programming_calc.clear_history() return "History cleared" def chat_with_ai(self, message: str, history: list) -> Tuple[str, list]: """Simple AI chat interface (placeholder).""" # This is a placeholder for AI integration response = f"I received your message: '{message}'. This is a placeholder for AI chat functionality. In a real implementation, this would connect to an AI service to help with calculations and mathematical questions." history.append([message, response]) return "", history def create_interface(): """Create the Gradio interface.""" app = CalculatorApp() with gr.Blocks(title="Engineering Calculator", theme=gr.themes.Soft()) as interface: gr.Markdown("# 🧮 Engineering Calculator") gr.Markdown("Multi-mode calculator with standard, scientific, engineering, and programming capabilities") with gr.Tabs(): # Standard Calculator Tab with gr.Tab("Standard"): with gr.Row(): with gr.Column(scale=2): display = gr.Textbox(value="0", label="Display", interactive=False, text_align="right") with gr.Row(): gr.Button("C", size="sm").click(lambda: app.standard_button_click("C", ""), outputs=display) gr.Button("CE", size="sm").click(lambda: app.standard_button_click("CE", ""), outputs=display) gr.Button("±", size="sm").click(lambda x: app.standard_button_click("±", x), inputs=display, outputs=display) gr.Button("÷", size="sm").click(lambda x: app.standard_button_click("÷", x), inputs=display, outputs=display) with gr.Row(): gr.Button("7", size="sm").click(lambda x: app.standard_button_click("7", x), inputs=display, outputs=display) gr.Button("8", size="sm").click(lambda x: app.standard_button_click("8", x), inputs=display, outputs=display) gr.Button("9", size="sm").click(lambda x: app.standard_button_click("9", x), inputs=display, outputs=display) gr.Button("×", size="sm").click(lambda x: app.standard_button_click("×", x), inputs=display, outputs=display) with gr.Row(): gr.Button("4", size="sm").click(lambda x: app.standard_button_click("4", x), inputs=display, outputs=display) gr.Button("5", size="sm").click(lambda x: app.standard_button_click("5", x), inputs=display, outputs=display) gr.Button("6", size="sm").click(lambda x: app.standard_button_click("6", x), inputs=display, outputs=display) gr.Button("-", size="sm").click(lambda x: app.standard_button_click("-", x), inputs=display, outputs=display) with gr.Row(): gr.Button("1", size="sm").click(lambda x: app.standard_button_click("1", x), inputs=display, outputs=display) gr.Button("2", size="sm").click(lambda x: app.standard_button_click("2", x), inputs=display, outputs=display) gr.Button("3", size="sm").click(lambda x: app.standard_button_click("3", x), inputs=display, outputs=display) gr.Button("+", size="sm").click(lambda x: app.standard_button_click("+", x), inputs=display, outputs=display) with gr.Row(): gr.Button("0", size="sm").click(lambda x: app.standard_button_click("0", x), inputs=display, outputs=display) gr.Button(".", size="sm").click(lambda x: app.standard_button_click(".", x), inputs=display, outputs=display) gr.Button("=", size="sm").click(lambda x: app.standard_button_click("=", x), inputs=display, outputs=display) with gr.Column(scale=1): gr.Markdown("### Memory & History") with gr.Row(): gr.Button("MS").click(lambda x: app.standard_calc.memory_store(x), inputs=display) gr.Button("MR").click(lambda: str(app.standard_calc.memory_recall()), outputs=display) gr.Button("MC").click(lambda: app.standard_calc.memory_clear()) history_display = gr.Textbox(label="History", lines=10, interactive=False) gr.Button("Show History").click(app.get_history, outputs=history_display) gr.Button("Clear History").click(app.clear_history, outputs=history_display) # Scientific Calculator Tab with gr.Tab("Scientific"): with gr.Row(): with gr.Column(): angle_mode = gr.Radio(["degrees", "radians"], value="degrees", label="Angle Mode") x_input = gr.Number(label="X Value", value=0) y_input = gr.Number(label="Y Value (for functions requiring 2 inputs)", value=0) function_dropdown = gr.Dropdown([ "sin", "cos", "tan", "asin", "acos", "atan", "csc", "sec", "cot", "log", "ln", "sqrt", "factorial", "power" ], label="Function", value="sin") calculate_btn = gr.Button("Calculate", variant="primary") result_display = gr.Textbox(label="Result", interactive=False) def scientific_wrapper(func, x, y, mode): app.scientific_calc.set_angle_mode('deg' if mode == 'degrees' else 'rad') return app.scientific_calculate(func, str(x), str(y) if y != 0 else None) calculate_btn.click( scientific_wrapper, inputs=[function_dropdown, x_input, y_input, angle_mode], outputs=result_display ) with gr.Column(): gr.Markdown("### Scientific Functions Reference") gr.Markdown(""" **Trigonometric Functions:** - sin, cos, tan: Basic trigonometric functions - asin, acos, atan: Inverse trigonometric functions - csc, sec, cot: Reciprocal trigonometric functions **Other Functions:** - log: Logarithm (base 10 or custom base if Y value provided) - ln: Natural logarithm (base e) - sqrt: Square root - factorial: Factorial (integers only) - power: X raised to the power of Y """) # Programming Calculator Tab with gr.Tab("Programming"): with gr.Row(): with gr.Column(): gr.Markdown("### Base Conversion") input_value = gr.Textbox(label="Input Value", placeholder="Enter number to convert") base_from = gr.Dropdown(["decimal", "binary", "hexadecimal", "octal"], label="From Base", value="decimal") base_to = gr.Dropdown(["decimal", "binary", "hexadecimal", "octal"], label="To Base", value="binary") convert_btn = gr.Button("Convert", variant="primary") conversion_result = gr.Textbox(label="Result", interactive=False) convert_btn.click( lambda val, from_base, to_base: app.programming_calculate("convert", val, from_base, to_base), inputs=[input_value, base_from, base_to], outputs=conversion_result ) with gr.Column(): gr.Markdown("### Bitwise Operations (Coming Soon)") gr.Markdown(""" **Available Conversions:** - Decimal ↔ Binary - Decimal ↔ Hexadecimal - Decimal ↔ Octal - Cross conversions between all bases **Input Formats:** - Decimal: Regular numbers (123) - Binary: Binary string (1010) - Hexadecimal: Hex string (FF or 0xFF) - Octal: Octal string (755 or 0o755) """) # Engineering Calculator Tab with gr.Tab("Engineering"): with gr.Row(): with gr.Column(): engineering_function = gr.Dropdown( ["3ph_impedance"], label="Engineering Function", value="3ph_impedance" ) gr.Markdown("### 3-Phase Impedance Calculator") gr.Markdown("Calculate equivalent impedance for 5-wire (A,B,C,N,PE) power systems") with gr.Accordion("Conductor Coordinates (feet)", open=True): with gr.Row(): coord_a_x = gr.Number(label="Phase A - X", value=0) coord_a_y = gr.Number(label="Phase A - Y", value=42) with gr.Row(): coord_b_x = gr.Number(label="Phase B - X", value=23.5) coord_b_y = gr.Number(label="Phase B - Y", value=42) with gr.Row(): coord_c_x = gr.Number(label="Phase C - X", value=47) coord_c_y = gr.Number(label="Phase C - Y", value=42) with gr.Row(): coord_n_x = gr.Number(label="Neutral - X", value=10) coord_n_y = gr.Number(label="Neutral - Y", value=74) with gr.Row(): coord_pe_x = gr.Number(label="PE - X", value=37) coord_pe_y = gr.Number(label="PE - Y", value=72) with gr.Accordion("Conductor Parameters", open=True): with gr.Row(): resist_a = gr.Number(label="Phase A Resistance (Ω/mile)", value=0.055) gmr_a = gr.Number(label="Phase A GMR (feet)", value=0.038) with gr.Row(): resist_b = gr.Number(label="Phase B Resistance (Ω/mile)", value=0.055) gmr_b = gr.Number(label="Phase B GMR (feet)", value=0.038) with gr.Row(): resist_c = gr.Number(label="Phase C Resistance (Ω/mile)", value=0.055) gmr_c = gr.Number(label="Phase C GMR (feet)", value=0.038) with gr.Row(): resist_n = gr.Number(label="Neutral Resistance (Ω/mile)", value=8.0) gmr_n = gr.Number(label="Neutral GMR (feet)", value=0.012) with gr.Row(): resist_pe = gr.Number(label="PE Resistance (Ω/mile)", value=8.0) gmr_pe = gr.Number(label="PE GMR (feet)", value=0.012) with gr.Row(): load_test_btn = gr.Button("Load Test Data") calculate_imp_btn = gr.Button("Calculate Impedance", variant="primary") engineering_result = gr.Textbox(label="Results", lines=20, interactive=False) # Load test data functionality test_data_outputs = [ coord_a_x, coord_a_y, coord_b_x, coord_b_y, coord_c_x, coord_c_y, coord_n_x, coord_n_y, coord_pe_x, coord_pe_y, resist_a, gmr_a, resist_b, gmr_b, resist_c, gmr_c, resist_n, gmr_n, resist_pe, gmr_pe ] load_test_btn.click(app.load_test_data, outputs=test_data_outputs) # Calculate impedance functionality impedance_inputs = [ coord_a_x, coord_a_y, coord_b_x, coord_b_y, coord_c_x, coord_c_y, coord_n_x, coord_n_y, coord_pe_x, coord_pe_y, resist_a, gmr_a, resist_b, gmr_b, resist_c, gmr_c, resist_n, gmr_n, resist_pe, gmr_pe ] calculate_imp_btn.click( app.calculate_3ph_impedance, inputs=impedance_inputs, outputs=engineering_result ) # AI Chat Tab with gr.Tab("AI Assistant"): gr.Markdown("### Calculator AI Assistant") gr.Markdown("Get help with calculations, mathematical concepts, and engineering problems") chatbot = gr.Chatbot(label="Chat History", height=400) msg = gr.Textbox(label="Your Message", placeholder="Ask me about calculations or math...") def chat_fn(message, history): return app.chat_with_ai(message, history) msg.submit(chat_fn, inputs=[msg, chatbot], outputs=[msg, chatbot]) gr.Markdown(""" **Note:** This is a placeholder for AI integration. In a full implementation, this would connect to an AI service to provide: - Help with mathematical concepts - Step-by-step calculation guidance - Engineering problem assistance - Formula explanations """) return interface if __name__ == "__main__": interface = create_interface() interface.launch(server_name="0.0.0.0", server_port=7860, share=False)