Calc/sympy_Base.py

246 lines
7.7 KiB
Python

"""
Clase base híbrida que combina SymPy con funcionalidad especializada
"""
import sympy
from sympy import Basic, Symbol, sympify
from typing import Any, Optional, Dict
import re
class SympyClassBase(Basic):
"""
Clase base híbrida que combina SymPy Basic con funcionalidad de calculadora
Todas las clases especializadas deben heredar de esta
"""
def __new__(cls, *args, **kwargs):
"""Crear objeto SymPy válido"""
obj = Basic.__new__(cls)
return obj
def __init__(self, value, original_str=""):
"""Inicialización de funcionalidad especializada"""
self._value = value
self._original_str = original_str
self._init_specialized()
def _init_specialized(self):
"""Override en subclases para inicialización especializada"""
pass
@property
def value(self):
"""Acceso al valor interno"""
return self._value
@property
def original_str(self):
"""String original de entrada"""
return self._original_str
# Propiedades requeridas por SymPy
@property
def args(self):
"""Argumentos para SymPy - retorna valor como argumento"""
return (sympify(self._value),)
@property
def func(self):
"""Función constructora para SymPy"""
return self.__class__
def _sympystr(self, printer):
"""Representación SymPy string"""
return f"{self.__class__.__name__}({self._original_str})"
def _latex(self, printer):
"""Representación LaTeX"""
return self._sympystr(printer)
def __str__(self):
"""Representación string para display"""
return str(self._value)
def __repr__(self):
"""Representación para debugging"""
return f"{self.__class__.__name__}('{self._original_str}')"
def evalf(self, n=15, **options):
"""Evaluación numérica si es posible"""
if isinstance(self._value, (int, float, complex)):
return sympy.Float(self._value)
return self
def _eval_evalf(self, prec):
"""Evaluación numérica para SymPy"""
if isinstance(self._value, (int, float, complex)):
return sympy.Float(self._value, prec)
return None
def __dec__(self):
"""Conversión a decimal para compatibilidad"""
if isinstance(self._value, (int, float, complex)):
return self._value
raise TypeError(f"Cannot convert {self.__class__.__name__} to decimal")
# Métodos requeridos por SymPy para manipulación algebraica
def as_coeff_Mul(self, rational=True):
"""Descomponer como coeficiente * términos"""
if isinstance(self._value, (int, float)):
return sympify(self._value), sympify(1)
return sympify(1), self
def as_coeff_Add(self, rational=True):
"""Descomponer como coeficiente + términos"""
if isinstance(self._value, (int, float)):
return sympify(self._value), sympify(0)
return sympify(0), self
def as_base_exp(self):
"""Descomponer como base^exponente"""
return self, sympify(1)
def as_numer_denom(self):
"""Descomponer como numerador/denominador"""
return self, sympify(1)
# Propiedades de tipo para SymPy
@property
def is_number(self):
"""Indica si es un número"""
return isinstance(self._value, (int, float, complex))
@property
def is_real(self):
"""Indica si es real"""
return isinstance(self._value, (int, float)) and not isinstance(self._value, complex)
@property
def is_integer(self):
"""Indica si es entero"""
return isinstance(self._value, int)
@property
def is_rational(self):
"""Indica si es racional"""
return isinstance(self._value, (int, float))
@property
def is_irrational(self):
"""Indica si es irracional"""
return False
@property
def is_positive(self):
"""Indica si es positivo"""
if isinstance(self._value, (int, float)):
return self._value > 0
return None
@property
def is_negative(self):
"""Indica si es negativo"""
if isinstance(self._value, (int, float)):
return self._value < 0
return None
@property
def is_zero(self):
"""Indica si es cero"""
if isinstance(self._value, (int, float)):
return self._value == 0
return None
@property
def is_finite(self):
"""Indica si es finito"""
return isinstance(self._value, (int, float, complex))
@property
def is_infinite(self):
"""Indica si es infinito"""
return False
@property
def is_commutative(self):
"""Indica si es conmutativo"""
return True
# Operaciones aritméticas simples que retornan el valor numérico para integración con SymPy
def __add__(self, other):
"""Suma que integra con SymPy"""
if isinstance(other, (int, float, complex)):
# Retornar valor numérico para que SymPy maneje la operación
return sympify(self._value + other)
elif hasattr(other, '_value'):
return sympify(self._value + other._value)
else:
# Dejar que SymPy maneje la operación
return sympify(self._value) + sympify(other)
def __radd__(self, other):
"""Suma reversa"""
return sympify(other) + sympify(self._value)
def __mul__(self, other):
"""Multiplicación que integra con SymPy"""
if isinstance(other, (int, float, complex)):
return sympify(self._value * other)
elif hasattr(other, '_value'):
return sympify(self._value * other._value)
else:
return sympify(self._value) * sympify(other)
def __rmul__(self, other):
"""Multiplicación reversa"""
return sympify(other) * sympify(self._value)
def __sub__(self, other):
"""Resta"""
if isinstance(other, (int, float, complex)):
return sympify(self._value - other)
elif hasattr(other, '_value'):
return sympify(self._value - other._value)
else:
return sympify(self._value) - sympify(other)
def __rsub__(self, other):
"""Resta reversa"""
return sympify(other) - sympify(self._value)
def __truediv__(self, other):
"""División"""
if isinstance(other, (int, float, complex)):
if other == 0:
raise ZeroDivisionError("Division by zero")
return sympify(self._value / other)
elif hasattr(other, '_value'):
if other._value == 0:
raise ZeroDivisionError("Division by zero")
return sympify(self._value / other._value)
else:
return sympify(self._value) / sympify(other)
def __rtruediv__(self, other):
"""División reversa"""
if self._value == 0:
raise ZeroDivisionError("Division by zero")
return sympify(other) / sympify(self._value)
def __pow__(self, other):
"""Potencia"""
if isinstance(other, (int, float, complex)):
return sympify(self._value ** other)
elif hasattr(other, '_value'):
return sympify(self._value ** other._value)
else:
return sympify(self._value) ** sympify(other)
def __rpow__(self, other):
"""Potencia reversa"""
return sympify(other) ** sympify(self._value)
@staticmethod
def Helper(input_str):
"""Override en subclases para ayuda contextual"""
return None