binary-equalab 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- binary_equalab/__init__.py +23 -0
- binary_equalab/cli.py +180 -0
- binary_equalab/engine.py +405 -0
- binary_equalab/functions.py +18 -0
- binary_equalab-1.0.0.dist-info/METADATA +160 -0
- binary_equalab-1.0.0.dist-info/RECORD +8 -0
- binary_equalab-1.0.0.dist-info/WHEEL +4 -0
- binary_equalab-1.0.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Binary EquaLab CLI
|
|
3
|
+
Command-line CAS calculator with Spanish math functions.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__version__ = "1.0.0"
|
|
7
|
+
__author__ = "BinaryEquaLab Team"
|
|
8
|
+
|
|
9
|
+
from .engine import MathEngine
|
|
10
|
+
from .functions import (
|
|
11
|
+
derivar, integrar, limite, sumatoria,
|
|
12
|
+
simplificar, expandir, factorizar, resolver,
|
|
13
|
+
van, tir, depreciar, interes_simple, interes_compuesto,
|
|
14
|
+
media, mediana, desviacion, varianza
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"MathEngine",
|
|
19
|
+
"derivar", "integrar", "limite", "sumatoria",
|
|
20
|
+
"simplificar", "expandir", "factorizar", "resolver",
|
|
21
|
+
"van", "tir", "depreciar", "interes_simple", "interes_compuesto",
|
|
22
|
+
"media", "mediana", "desviacion", "varianza",
|
|
23
|
+
]
|
binary_equalab/cli.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Binary EquaLab CLI
|
|
3
|
+
Interactive REPL and command-line interface.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.panel import Panel
|
|
10
|
+
from rich.markdown import Markdown
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
from prompt_toolkit import PromptSession
|
|
13
|
+
from prompt_toolkit.history import FileHistory
|
|
14
|
+
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
|
15
|
+
from prompt_toolkit.styles import Style
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
from .engine import MathEngine
|
|
19
|
+
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
BANNER = """
|
|
23
|
+
[bold orange1]╔══════════════════════════════════════════════════════════╗
|
|
24
|
+
║ [white]Binary EquaLab CLI[/white] [dim]v1.0.0[/dim] ║
|
|
25
|
+
║ [dim italic]"El álgebra también siente"[/dim italic] ║
|
|
26
|
+
╚══════════════════════════════════════════════════════════╝[/bold orange1]
|
|
27
|
+
|
|
28
|
+
[dim]Comandos:[/dim]
|
|
29
|
+
[cyan]help[/cyan] - Lista de funciones disponibles
|
|
30
|
+
[cyan]exit[/cyan] - Salir
|
|
31
|
+
[cyan]cls[/cyan] - Limpiar pantalla
|
|
32
|
+
|
|
33
|
+
[dim]Ejemplos:[/dim]
|
|
34
|
+
derivar(x^2 + 3x, x)
|
|
35
|
+
integrar(sin(x), x)
|
|
36
|
+
van(0.10, -1000, 300, 400, 500)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
HELP_TEXT = """
|
|
40
|
+
## Funciones Disponibles
|
|
41
|
+
|
|
42
|
+
### Cálculo
|
|
43
|
+
| Función | Ejemplo |
|
|
44
|
+
|---------|---------|
|
|
45
|
+
| `derivar(expr, var)` | `derivar(x^2, x)` → `2*x` |
|
|
46
|
+
| `integrar(expr, var)` | `integrar(sin(x), x)` → `-cos(x)` |
|
|
47
|
+
| `limite(expr, var, punto)` | `limite(sin(x)/x, x, 0)` → `1` |
|
|
48
|
+
| `sumatoria(expr, var, a, b)` | `sumatoria(n^2, n, 1, 10)` |
|
|
49
|
+
|
|
50
|
+
### Álgebra
|
|
51
|
+
| Función | Ejemplo |
|
|
52
|
+
|---------|---------|
|
|
53
|
+
| `simplificar(expr)` | `simplificar((x^2-1)/(x-1))` |
|
|
54
|
+
| `expandir(expr)` | `expandir((x+1)^2)` |
|
|
55
|
+
| `factorizar(expr)` | `factorizar(x^2-1)` |
|
|
56
|
+
| `resolver(expr, var)` | `resolver(x^2-4, x)` → `[-2, 2]` |
|
|
57
|
+
|
|
58
|
+
### Estadística
|
|
59
|
+
| Función | Ejemplo |
|
|
60
|
+
|---------|---------|
|
|
61
|
+
| `media(...)` | `media(1, 2, 3, 4, 5)` → `3` |
|
|
62
|
+
| `mediana(...)` | `mediana(1, 2, 3, 4, 5)` → `3` |
|
|
63
|
+
| `desviacion(...)` | `desviacion(1, 2, 3, 4, 5)` |
|
|
64
|
+
| `varianza(...)` | `varianza(1, 2, 3, 4, 5)` |
|
|
65
|
+
|
|
66
|
+
### Finanzas
|
|
67
|
+
| Función | Ejemplo |
|
|
68
|
+
|---------|---------|
|
|
69
|
+
| `van(tasa, flujo0, flujo1, ...)` | `van(0.10, -1000, 300, 400)` |
|
|
70
|
+
| `tir(flujo0, flujo1, ...)` | `tir(-1000, 300, 400, 500)` |
|
|
71
|
+
| `depreciar(costo, residual, años)` | `depreciar(10000, 1000, 5)` |
|
|
72
|
+
| `interes_simple(capital, tasa, tiempo)` | `interes_simple(1000, 0.05, 3)` |
|
|
73
|
+
| `interes_compuesto(capital, tasa, n, tiempo)` | `interes_compuesto(1000, 0.05, 12, 3)` |
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_prompt_style():
|
|
78
|
+
return Style.from_dict({
|
|
79
|
+
'prompt': '#ff6b35 bold',
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def repl():
|
|
84
|
+
"""Start the interactive REPL."""
|
|
85
|
+
console.print(BANNER)
|
|
86
|
+
|
|
87
|
+
engine = MathEngine()
|
|
88
|
+
|
|
89
|
+
# Setup history file
|
|
90
|
+
history_path = os.path.expanduser("~/.binary_math_history")
|
|
91
|
+
session: PromptSession = PromptSession(
|
|
92
|
+
history=FileHistory(history_path),
|
|
93
|
+
auto_suggest=AutoSuggestFromHistory(),
|
|
94
|
+
style=get_prompt_style(),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
while True:
|
|
98
|
+
try:
|
|
99
|
+
# Read input
|
|
100
|
+
user_input = session.prompt([('class:prompt', '>>> ')]).strip()
|
|
101
|
+
|
|
102
|
+
if not user_input:
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
# Handle special commands
|
|
106
|
+
if user_input.lower() in ('exit', 'quit', 'q'):
|
|
107
|
+
console.print("[dim]¡Hasta luego![/dim]")
|
|
108
|
+
break
|
|
109
|
+
|
|
110
|
+
if user_input.lower() in ('cls', 'clear'):
|
|
111
|
+
console.clear()
|
|
112
|
+
console.print(BANNER)
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
if user_input.lower() == 'help':
|
|
116
|
+
console.print(Markdown(HELP_TEXT))
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
if user_input.lower() == 'history':
|
|
120
|
+
for i, h in enumerate(engine.history[-10:], 1):
|
|
121
|
+
console.print(f"[dim]{i}.[/dim] {h}")
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
# Evaluate expression
|
|
125
|
+
try:
|
|
126
|
+
result = engine.evaluate(user_input)
|
|
127
|
+
|
|
128
|
+
if result is None:
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
# Format output
|
|
132
|
+
if isinstance(result, (list, tuple)):
|
|
133
|
+
console.print(f"[bold green]→[/bold green] {list(result)}")
|
|
134
|
+
elif isinstance(result, dict):
|
|
135
|
+
for key, value in result.items():
|
|
136
|
+
console.print(f" [cyan]{key}:[/cyan] {value}")
|
|
137
|
+
else:
|
|
138
|
+
console.print(f"[bold green]→[/bold green] {result}")
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
console.print(f"[bold red]Error:[/bold red] {e}")
|
|
142
|
+
|
|
143
|
+
except KeyboardInterrupt:
|
|
144
|
+
console.print()
|
|
145
|
+
continue
|
|
146
|
+
except EOFError:
|
|
147
|
+
console.print("\n[dim]¡Hasta luego![/dim]")
|
|
148
|
+
break
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def one_liner(expression: str):
|
|
152
|
+
"""Evaluate a single expression from command line."""
|
|
153
|
+
engine = MathEngine()
|
|
154
|
+
try:
|
|
155
|
+
result = engine.evaluate(expression)
|
|
156
|
+
if isinstance(result, (list, tuple)):
|
|
157
|
+
print(list(result))
|
|
158
|
+
elif isinstance(result, dict):
|
|
159
|
+
for key, value in result.items():
|
|
160
|
+
print(f"{key}: {value}")
|
|
161
|
+
else:
|
|
162
|
+
print(result)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
165
|
+
sys.exit(1)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def main():
|
|
169
|
+
"""CLI entry point."""
|
|
170
|
+
if len(sys.argv) > 1:
|
|
171
|
+
# One-liner mode
|
|
172
|
+
expression = " ".join(sys.argv[1:])
|
|
173
|
+
one_liner(expression)
|
|
174
|
+
else:
|
|
175
|
+
# REPL mode
|
|
176
|
+
repl()
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
main()
|
binary_equalab/engine.py
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Binary EquaLab - Math Engine
|
|
3
|
+
Core symbolic computation using SymPy with Spanish function translations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sympy as sp
|
|
7
|
+
from sympy import (
|
|
8
|
+
Symbol, symbols, sin, cos, tan, sqrt, exp, log, ln, pi, E, I,
|
|
9
|
+
diff, integrate, limit, summation, simplify, expand, factor, solve,
|
|
10
|
+
Abs, factorial, gamma, binomial, floor, ceiling,
|
|
11
|
+
Matrix, det, Transpose
|
|
12
|
+
)
|
|
13
|
+
from sympy.parsing.sympy_parser import (
|
|
14
|
+
parse_expr, standard_transformations, implicit_multiplication_application,
|
|
15
|
+
convert_xor, function_exponentiation
|
|
16
|
+
)
|
|
17
|
+
from typing import Any, Union, List, Optional
|
|
18
|
+
import re
|
|
19
|
+
|
|
20
|
+
# Symbol shortcuts
|
|
21
|
+
x, y, z, t, n, k = symbols('x y z t n k')
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class MathEngine:
|
|
25
|
+
"""
|
|
26
|
+
Core math engine with Spanish function support.
|
|
27
|
+
Wraps SymPy with user-friendly Spanish aliases.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self.symbols = {'x': x, 'y': y, 'z': z, 't': t, 'n': n, 'k': k}
|
|
32
|
+
self.last_result = None
|
|
33
|
+
self.history: List[str] = []
|
|
34
|
+
|
|
35
|
+
# Spanish → SymPy function mapping
|
|
36
|
+
self.function_map = {
|
|
37
|
+
# Calculus
|
|
38
|
+
'derivar': self._derivar,
|
|
39
|
+
'integrar': self._integrar,
|
|
40
|
+
'limite': self._limite,
|
|
41
|
+
'sumatoria': self._sumatoria,
|
|
42
|
+
|
|
43
|
+
# Algebra
|
|
44
|
+
'simplificar': self._simplificar,
|
|
45
|
+
'expandir': self._expandir,
|
|
46
|
+
'factorizar': self._factorizar,
|
|
47
|
+
'resolver': self._resolver,
|
|
48
|
+
|
|
49
|
+
# Statistics
|
|
50
|
+
'media': self._media,
|
|
51
|
+
'mediana': self._mediana,
|
|
52
|
+
'desviacion': self._desviacion,
|
|
53
|
+
'varianza': self._varianza,
|
|
54
|
+
|
|
55
|
+
# Finance
|
|
56
|
+
'van': self._van,
|
|
57
|
+
'tir': self._tir,
|
|
58
|
+
'depreciar': self._depreciar,
|
|
59
|
+
'interes_simple': self._interes_simple,
|
|
60
|
+
'interes_compuesto': self._interes_compuesto,
|
|
61
|
+
|
|
62
|
+
# Trigonometry aliases
|
|
63
|
+
'seno': sin,
|
|
64
|
+
'coseno': cos,
|
|
65
|
+
'tangente': tan,
|
|
66
|
+
'arcoseno': sp.asin,
|
|
67
|
+
'arcocoseno': sp.acos,
|
|
68
|
+
'arcotangente': sp.atan,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
def parse(self, expression: str) -> Any:
|
|
72
|
+
"""Parse a math expression string into SymPy."""
|
|
73
|
+
# Preprocess Spanish functions
|
|
74
|
+
expr = self._preprocess(expression)
|
|
75
|
+
|
|
76
|
+
# Parse with transformations
|
|
77
|
+
transformations = (
|
|
78
|
+
standard_transformations +
|
|
79
|
+
(implicit_multiplication_application, convert_xor, function_exponentiation)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
result = parse_expr(expr, local_dict=self.symbols, transformations=transformations)
|
|
84
|
+
return result
|
|
85
|
+
except Exception as e:
|
|
86
|
+
raise ValueError(f"Parse error: {e}")
|
|
87
|
+
|
|
88
|
+
def evaluate(self, expression: str) -> Any:
|
|
89
|
+
"""Evaluate a math expression and return the result."""
|
|
90
|
+
expression = expression.strip()
|
|
91
|
+
|
|
92
|
+
if not expression:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
self.history.append(expression)
|
|
96
|
+
|
|
97
|
+
# Check for function calls
|
|
98
|
+
for func_name, func in self.function_map.items():
|
|
99
|
+
if expression.startswith(f'{func_name}('):
|
|
100
|
+
result = self._call_function(expression)
|
|
101
|
+
self.last_result = result
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
# Standard expression evaluation
|
|
105
|
+
try:
|
|
106
|
+
parsed = self.parse(expression)
|
|
107
|
+
result = sp.simplify(parsed)
|
|
108
|
+
self.last_result = result
|
|
109
|
+
return result
|
|
110
|
+
except Exception as e:
|
|
111
|
+
raise ValueError(f"Evaluation error: {e}")
|
|
112
|
+
|
|
113
|
+
def _preprocess(self, expr: str) -> str:
|
|
114
|
+
"""Convert Spanish function names and shortcuts."""
|
|
115
|
+
# Replace Spanish trig
|
|
116
|
+
replacements = {
|
|
117
|
+
'seno': 'sin',
|
|
118
|
+
'coseno': 'cos',
|
|
119
|
+
'tangente': 'tan',
|
|
120
|
+
'arcoseno': 'asin',
|
|
121
|
+
'arcocoseno': 'acos',
|
|
122
|
+
'arcotangente': 'atan',
|
|
123
|
+
'raiz': 'sqrt',
|
|
124
|
+
'absoluto': 'Abs',
|
|
125
|
+
'logaritmo': 'log',
|
|
126
|
+
'exponencial': 'exp',
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for es, en in replacements.items():
|
|
130
|
+
expr = re.sub(rf'\b{es}\b', en, expr, flags=re.IGNORECASE)
|
|
131
|
+
|
|
132
|
+
# Handle ^ as power
|
|
133
|
+
expr = expr.replace('^', '**')
|
|
134
|
+
|
|
135
|
+
return expr
|
|
136
|
+
|
|
137
|
+
def _call_function(self, expression: str) -> Any:
|
|
138
|
+
"""Call a Spanish function with arguments."""
|
|
139
|
+
# Extract function name and args
|
|
140
|
+
match = re.match(r'(\w+)\((.*)\)$', expression, re.DOTALL)
|
|
141
|
+
if not match:
|
|
142
|
+
raise ValueError(f"Invalid function call: {expression}")
|
|
143
|
+
|
|
144
|
+
func_name = match.group(1)
|
|
145
|
+
args_str = match.group(2)
|
|
146
|
+
|
|
147
|
+
if func_name not in self.function_map:
|
|
148
|
+
raise ValueError(f"Unknown function: {func_name}")
|
|
149
|
+
|
|
150
|
+
func = self.function_map[func_name]
|
|
151
|
+
|
|
152
|
+
# Parse arguments
|
|
153
|
+
args = self._parse_args(args_str)
|
|
154
|
+
|
|
155
|
+
return func(*args)
|
|
156
|
+
|
|
157
|
+
def _parse_args(self, args_str: str) -> List[Any]:
|
|
158
|
+
"""Parse function arguments, handling nested parentheses."""
|
|
159
|
+
args = []
|
|
160
|
+
current = ""
|
|
161
|
+
depth = 0
|
|
162
|
+
|
|
163
|
+
for char in args_str:
|
|
164
|
+
if char == '(':
|
|
165
|
+
depth += 1
|
|
166
|
+
current += char
|
|
167
|
+
elif char == ')':
|
|
168
|
+
depth -= 1
|
|
169
|
+
current += char
|
|
170
|
+
elif char == ',' and depth == 0:
|
|
171
|
+
if current.strip():
|
|
172
|
+
args.append(self._parse_single_arg(current.strip()))
|
|
173
|
+
current = ""
|
|
174
|
+
else:
|
|
175
|
+
current += char
|
|
176
|
+
|
|
177
|
+
if current.strip():
|
|
178
|
+
args.append(self._parse_single_arg(current.strip()))
|
|
179
|
+
|
|
180
|
+
return args
|
|
181
|
+
|
|
182
|
+
def _parse_single_arg(self, arg: str) -> Any:
|
|
183
|
+
"""Parse a single argument - could be number, symbol, or expression."""
|
|
184
|
+
try:
|
|
185
|
+
return float(arg)
|
|
186
|
+
except ValueError:
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
if arg in self.symbols:
|
|
190
|
+
return self.symbols[arg]
|
|
191
|
+
|
|
192
|
+
return self.parse(arg)
|
|
193
|
+
|
|
194
|
+
# ============ CALCULUS ============
|
|
195
|
+
|
|
196
|
+
def _derivar(self, expr, var=None, n=1):
|
|
197
|
+
"""Derivative: derivar(x^2 + 3x, x) → 2x + 3"""
|
|
198
|
+
if var is None:
|
|
199
|
+
var = x
|
|
200
|
+
return diff(expr, var, n)
|
|
201
|
+
|
|
202
|
+
def _integrar(self, expr, var=None, a=None, b=None):
|
|
203
|
+
"""Integral: integrar(x^2, x) or integrar(x^2, x, 0, 1)"""
|
|
204
|
+
if var is None:
|
|
205
|
+
var = x
|
|
206
|
+
if a is not None and b is not None:
|
|
207
|
+
return integrate(expr, (var, a, b))
|
|
208
|
+
return integrate(expr, var)
|
|
209
|
+
|
|
210
|
+
def _limite(self, expr, var=None, punto=0, direccion=None):
|
|
211
|
+
"""Limit: limite(sin(x)/x, x, 0) → 1"""
|
|
212
|
+
if var is None:
|
|
213
|
+
var = x
|
|
214
|
+
if direccion:
|
|
215
|
+
return limit(expr, var, punto, direccion)
|
|
216
|
+
return limit(expr, var, punto)
|
|
217
|
+
|
|
218
|
+
def _sumatoria(self, expr, var=None, a=0, b=10):
|
|
219
|
+
"""Sum: sumatoria(n^2, n, 1, 10) → 385"""
|
|
220
|
+
if var is None:
|
|
221
|
+
var = n
|
|
222
|
+
return summation(expr, (var, a, b))
|
|
223
|
+
|
|
224
|
+
# ============ ALGEBRA ============
|
|
225
|
+
|
|
226
|
+
def _simplificar(self, expr):
|
|
227
|
+
"""Simplify: simplificar((x^2-1)/(x-1)) → x+1"""
|
|
228
|
+
return simplify(expr)
|
|
229
|
+
|
|
230
|
+
def _expandir(self, expr):
|
|
231
|
+
"""Expand: expandir((x+1)^2) → x^2 + 2x + 1"""
|
|
232
|
+
return expand(expr)
|
|
233
|
+
|
|
234
|
+
def _factorizar(self, expr):
|
|
235
|
+
"""Factor: factorizar(x^2 - 1) → (x-1)(x+1)"""
|
|
236
|
+
return factor(expr)
|
|
237
|
+
|
|
238
|
+
def _resolver(self, expr, var=None):
|
|
239
|
+
"""Solve: resolver(x^2 - 4, x) → [-2, 2]"""
|
|
240
|
+
if var is None:
|
|
241
|
+
var = x
|
|
242
|
+
return solve(expr, var)
|
|
243
|
+
|
|
244
|
+
# ============ STATISTICS ============
|
|
245
|
+
|
|
246
|
+
def _media(self, *values):
|
|
247
|
+
"""Mean: media(1, 2, 3, 4, 5) → 3"""
|
|
248
|
+
nums = [float(v) for v in values]
|
|
249
|
+
return sum(nums) / len(nums)
|
|
250
|
+
|
|
251
|
+
def _mediana(self, *values):
|
|
252
|
+
"""Median: mediana(1, 2, 3, 4, 5) → 3"""
|
|
253
|
+
nums = sorted([float(v) for v in values])
|
|
254
|
+
n = len(nums)
|
|
255
|
+
mid = n // 2
|
|
256
|
+
if n % 2 == 0:
|
|
257
|
+
return (nums[mid - 1] + nums[mid]) / 2
|
|
258
|
+
return nums[mid]
|
|
259
|
+
|
|
260
|
+
def _desviacion(self, *values):
|
|
261
|
+
"""Standard deviation: desviacion(1, 2, 3, 4, 5)"""
|
|
262
|
+
nums = [float(v) for v in values]
|
|
263
|
+
mean = sum(nums) / len(nums)
|
|
264
|
+
variance = sum((x - mean) ** 2 for x in nums) / len(nums)
|
|
265
|
+
return variance ** 0.5
|
|
266
|
+
|
|
267
|
+
def _varianza(self, *values):
|
|
268
|
+
"""Variance: varianza(1, 2, 3, 4, 5)"""
|
|
269
|
+
nums = [float(v) for v in values]
|
|
270
|
+
mean = sum(nums) / len(nums)
|
|
271
|
+
return sum((x - mean) ** 2 for x in nums) / len(nums)
|
|
272
|
+
|
|
273
|
+
# ============ FINANCE ============
|
|
274
|
+
|
|
275
|
+
def _van(self, tasa, *flujos):
|
|
276
|
+
"""NPV: van(0.10, -1000, 300, 400, 500)"""
|
|
277
|
+
r = float(tasa)
|
|
278
|
+
result = 0
|
|
279
|
+
for i, flujo in enumerate(flujos):
|
|
280
|
+
result += float(flujo) / ((1 + r) ** i)
|
|
281
|
+
return round(result, 2)
|
|
282
|
+
|
|
283
|
+
def _tir(self, *flujos):
|
|
284
|
+
"""IRR: tir(-1000, 300, 400, 500) using Newton-Raphson"""
|
|
285
|
+
flows = [float(f) for f in flujos]
|
|
286
|
+
|
|
287
|
+
def npv(r):
|
|
288
|
+
return sum(f / ((1 + r) ** i) for i, f in enumerate(flows))
|
|
289
|
+
|
|
290
|
+
def npv_deriv(r):
|
|
291
|
+
return sum(-i * f / ((1 + r) ** (i + 1)) for i, f in enumerate(flows))
|
|
292
|
+
|
|
293
|
+
r = 0.1 # Initial guess
|
|
294
|
+
for _ in range(100):
|
|
295
|
+
npv_val = npv(r)
|
|
296
|
+
if abs(npv_val) < 1e-10:
|
|
297
|
+
break
|
|
298
|
+
deriv = npv_deriv(r)
|
|
299
|
+
if deriv == 0:
|
|
300
|
+
break
|
|
301
|
+
r = r - npv_val / deriv
|
|
302
|
+
|
|
303
|
+
return round(r * 100, 2) # Return as percentage
|
|
304
|
+
|
|
305
|
+
def _depreciar(self, costo, residual, años):
|
|
306
|
+
"""Straight-line depreciation: depreciar(10000, 1000, 5)"""
|
|
307
|
+
c, r, n = float(costo), float(residual), int(años)
|
|
308
|
+
annual = (c - r) / n
|
|
309
|
+
schedule = []
|
|
310
|
+
for i in range(n):
|
|
311
|
+
schedule.append({
|
|
312
|
+
'año': i + 1,
|
|
313
|
+
'depreciacion': round(annual, 2),
|
|
314
|
+
'acumulado': round(annual * (i + 1), 2),
|
|
315
|
+
'valor_libro': round(c - annual * (i + 1), 2)
|
|
316
|
+
})
|
|
317
|
+
return schedule
|
|
318
|
+
|
|
319
|
+
def _interes_simple(self, capital, tasa, tiempo):
|
|
320
|
+
"""Simple interest: interes_simple(1000, 0.05, 3)"""
|
|
321
|
+
c, r, t = float(capital), float(tasa), float(tiempo)
|
|
322
|
+
interest = c * r * t
|
|
323
|
+
return {
|
|
324
|
+
'interes': round(interest, 2),
|
|
325
|
+
'monto_final': round(c + interest, 2)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
def _interes_compuesto(self, capital, tasa, n, tiempo):
|
|
329
|
+
"""Compound interest: interes_compuesto(1000, 0.05, 12, 3)"""
|
|
330
|
+
c, r, periods, t = float(capital), float(tasa), int(n), float(tiempo)
|
|
331
|
+
monto = c * ((1 + r / periods) ** (periods * t))
|
|
332
|
+
return {
|
|
333
|
+
'monto_final': round(monto, 2),
|
|
334
|
+
'interes': round(monto - c, 2)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
# Convenience functions for direct import
|
|
339
|
+
def derivar(expr, var=None, n=1):
|
|
340
|
+
engine = MathEngine()
|
|
341
|
+
return engine._derivar(engine.parse(str(expr)), var, n)
|
|
342
|
+
|
|
343
|
+
def integrar(expr, var=None, a=None, b=None):
|
|
344
|
+
engine = MathEngine()
|
|
345
|
+
return engine._integrar(engine.parse(str(expr)), var, a, b)
|
|
346
|
+
|
|
347
|
+
def limite(expr, var=None, punto=0):
|
|
348
|
+
engine = MathEngine()
|
|
349
|
+
return engine._limite(engine.parse(str(expr)), var, punto)
|
|
350
|
+
|
|
351
|
+
def sumatoria(expr, var=None, a=0, b=10):
|
|
352
|
+
engine = MathEngine()
|
|
353
|
+
return engine._sumatoria(engine.parse(str(expr)), var, a, b)
|
|
354
|
+
|
|
355
|
+
def simplificar(expr):
|
|
356
|
+
engine = MathEngine()
|
|
357
|
+
return engine._simplificar(engine.parse(str(expr)))
|
|
358
|
+
|
|
359
|
+
def expandir(expr):
|
|
360
|
+
engine = MathEngine()
|
|
361
|
+
return engine._expandir(engine.parse(str(expr)))
|
|
362
|
+
|
|
363
|
+
def factorizar(expr):
|
|
364
|
+
engine = MathEngine()
|
|
365
|
+
return engine._factorizar(engine.parse(str(expr)))
|
|
366
|
+
|
|
367
|
+
def resolver(expr, var=None):
|
|
368
|
+
engine = MathEngine()
|
|
369
|
+
return engine._resolver(engine.parse(str(expr)), var)
|
|
370
|
+
|
|
371
|
+
def van(tasa, *flujos):
|
|
372
|
+
engine = MathEngine()
|
|
373
|
+
return engine._van(tasa, *flujos)
|
|
374
|
+
|
|
375
|
+
def tir(*flujos):
|
|
376
|
+
engine = MathEngine()
|
|
377
|
+
return engine._tir(*flujos)
|
|
378
|
+
|
|
379
|
+
def depreciar(costo, residual, años):
|
|
380
|
+
engine = MathEngine()
|
|
381
|
+
return engine._depreciar(costo, residual, años)
|
|
382
|
+
|
|
383
|
+
def interes_simple(capital, tasa, tiempo):
|
|
384
|
+
engine = MathEngine()
|
|
385
|
+
return engine._interes_simple(capital, tasa, tiempo)
|
|
386
|
+
|
|
387
|
+
def interes_compuesto(capital, tasa, n, tiempo):
|
|
388
|
+
engine = MathEngine()
|
|
389
|
+
return engine._interes_compuesto(capital, tasa, n, tiempo)
|
|
390
|
+
|
|
391
|
+
def media(*values):
|
|
392
|
+
engine = MathEngine()
|
|
393
|
+
return engine._media(*values)
|
|
394
|
+
|
|
395
|
+
def mediana(*values):
|
|
396
|
+
engine = MathEngine()
|
|
397
|
+
return engine._mediana(*values)
|
|
398
|
+
|
|
399
|
+
def desviacion(*values):
|
|
400
|
+
engine = MathEngine()
|
|
401
|
+
return engine._desviacion(*values)
|
|
402
|
+
|
|
403
|
+
def varianza(*values):
|
|
404
|
+
engine = MathEngine()
|
|
405
|
+
return engine._varianza(*values)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Binary EquaLab - Exported Functions
|
|
3
|
+
Convenience re-exports for direct import usage.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .engine import (
|
|
7
|
+
derivar, integrar, limite, sumatoria,
|
|
8
|
+
simplificar, expandir, factorizar, resolver,
|
|
9
|
+
van, tir, depreciar, interes_simple, interes_compuesto,
|
|
10
|
+
media, mediana, desviacion, varianza
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"derivar", "integrar", "limite", "sumatoria",
|
|
15
|
+
"simplificar", "expandir", "factorizar", "resolver",
|
|
16
|
+
"van", "tir", "depreciar", "interes_simple", "interes_compuesto",
|
|
17
|
+
"media", "mediana", "desviacion", "varianza",
|
|
18
|
+
]
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: binary-equalab
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Command-line CAS calculator with Spanish math functions
|
|
5
|
+
Project-URL: Homepage, https://github.com/Malexnnn/BinaryEquaLab
|
|
6
|
+
Project-URL: Repository, https://github.com/Malexnnn/BinaryEquaLab
|
|
7
|
+
Project-URL: Documentation, https://github.com/Malexnnn/BinaryEquaLab#readme
|
|
8
|
+
Author: BinaryEquaLab Team
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: algebra,calculator,calculus,cas,math,spanish,symbolic
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Education
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Requires-Dist: numpy>=1.24
|
|
25
|
+
Requires-Dist: prompt-toolkit>=3.0
|
|
26
|
+
Requires-Dist: rich>=13.0
|
|
27
|
+
Requires-Dist: sympy>=1.12
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: black>=23.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Binary EquaLab CLI
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<img src="../docs/banner_cli.png" alt="Binary EquaLab CLI" width="500">
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<em>⌨️ Calculadora CAS en la terminal</em>
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🚀 Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install binary-equalab
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or from source:
|
|
54
|
+
```bash
|
|
55
|
+
cd binary-cli
|
|
56
|
+
pip install -e .
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 📖 Usage
|
|
62
|
+
|
|
63
|
+
### REPL Mode
|
|
64
|
+
```bash
|
|
65
|
+
binary-math
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Binary EquaLab CLI v1.0.0
|
|
70
|
+
>>> derivar(x^2 + 3x, x)
|
|
71
|
+
→ 2*x + 3
|
|
72
|
+
|
|
73
|
+
>>> integrar(sin(x), x)
|
|
74
|
+
→ -cos(x)
|
|
75
|
+
|
|
76
|
+
>>> factorial(5)
|
|
77
|
+
→ 120
|
|
78
|
+
|
|
79
|
+
>>> van(0.10, -1000, 300, 400, 500)
|
|
80
|
+
→ 78.82
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### One-liner Mode
|
|
84
|
+
```bash
|
|
85
|
+
binary-math "derivar(x^3, x)"
|
|
86
|
+
# Output: 3*x^2
|
|
87
|
+
|
|
88
|
+
binary-math "factorial(10)"
|
|
89
|
+
# Output: 3628800
|
|
90
|
+
|
|
91
|
+
binary-math "0b1010 + 0b0101"
|
|
92
|
+
# Output: 15
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 🔢 Functions
|
|
98
|
+
|
|
99
|
+
### Calculus
|
|
100
|
+
| Function | Example | Result |
|
|
101
|
+
| ----------------------- | -------------------------- | --------- |
|
|
102
|
+
| `derivar(f, x)` | `derivar(x^2, x)` | `2*x` |
|
|
103
|
+
| `integrar(f, x)` | `integrar(sin(x), x)` | `-cos(x)` |
|
|
104
|
+
| `limite(f, x, a)` | `limite(sin(x)/x, x, 0)` | `1` |
|
|
105
|
+
| `sumatoria(f, n, a, b)` | `sumatoria(n^2, n, 1, 10)` | `385` |
|
|
106
|
+
|
|
107
|
+
### Algebra
|
|
108
|
+
| Function | Example | Result |
|
|
109
|
+
| ---------------- | ---------------------------- | ------------- |
|
|
110
|
+
| `simplificar(f)` | `simplificar((x^2-1)/(x-1))` | `x+1` |
|
|
111
|
+
| `expandir(f)` | `expandir((x+1)^2)` | `x^2+2*x+1` |
|
|
112
|
+
| `factorizar(f)` | `factorizar(x^2-1)` | `(x-1)*(x+1)` |
|
|
113
|
+
| `resolver(f, x)` | `resolver(x^2-4, x)` | `[-2, 2]` |
|
|
114
|
+
|
|
115
|
+
### Statistics
|
|
116
|
+
| Function | Example |
|
|
117
|
+
| ----------------- | ------------------------------ |
|
|
118
|
+
| `media(...)` | `media(1, 2, 3, 4, 5)` → `3` |
|
|
119
|
+
| `mediana(...)` | `mediana(1, 2, 3, 4, 5)` → `3` |
|
|
120
|
+
| `desviacion(...)` | Standard deviation |
|
|
121
|
+
| `varianza(...)` | Variance |
|
|
122
|
+
|
|
123
|
+
### Finance
|
|
124
|
+
| Function | Example |
|
|
125
|
+
| ------------------------------- | -------------------------------------- |
|
|
126
|
+
| `van(r, cf0, cf1, ...)` | `van(0.10, -1000, 300, 400)` |
|
|
127
|
+
| `tir(cf0, cf1, ...)` | `tir(-1000, 300, 400, 500)` |
|
|
128
|
+
| `depreciar(cost, res, years)` | `depreciar(10000, 1000, 5)` |
|
|
129
|
+
| `interes_simple(c, r, t)` | `interes_simple(1000, 0.05, 3)` |
|
|
130
|
+
| `interes_compuesto(c, r, n, t)` | `interes_compuesto(1000, 0.05, 12, 3)` |
|
|
131
|
+
|
|
132
|
+
### Number Systems
|
|
133
|
+
```
|
|
134
|
+
0b1010 → 10 (binary)
|
|
135
|
+
0xFF → 255 (hexadecimal)
|
|
136
|
+
0o17 → 15 (octal)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 🥚 Easter Eggs
|
|
142
|
+
|
|
143
|
+
Try these:
|
|
144
|
+
- `1+1`
|
|
145
|
+
- `(-1)*(-1)`
|
|
146
|
+
- `0b101010`
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 🛠️ Development
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
cd binary-cli
|
|
154
|
+
pip install -e ".[dev]"
|
|
155
|
+
pytest
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
MIT © Malexnnn/ Aldra ORG.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
binary_equalab/__init__.py,sha256=h6Fi1RtNgWo6PnSqoRZX1ee2uvn9faJfneI16L14XR8,652
|
|
2
|
+
binary_equalab/cli.py,sha256=mepUJ_5XBz93k0edpVsXXhgEgt1EqXEHJJ-K6cx69Vw,5759
|
|
3
|
+
binary_equalab/engine.py,sha256=KTlpNvHMTODAl1haTw8tZUfxqU9CHNMx4XqVtyAa_SY,12831
|
|
4
|
+
binary_equalab/functions.py,sha256=czx4m5ZZP8VL27TdC6p8cH4FNpkabTF-EymP5iC64nw,551
|
|
5
|
+
binary_equalab-1.0.0.dist-info/METADATA,sha256=sCqgMV3c2YMq2Gn8c53IZ1N_o-hU0D24juie3yUHoiU,4215
|
|
6
|
+
binary_equalab-1.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
7
|
+
binary_equalab-1.0.0.dist-info/entry_points.txt,sha256=KAAmBj-EOydDlCxIFK_NycbqimMK9_yp_VODFvoaaEI,86
|
|
8
|
+
binary_equalab-1.0.0.dist-info/RECORD,,
|