Spaces:
Running
Running
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) |