Skip to content

Commit 4c15555

Browse files
author
Kunwartejpal Sidhu
committed
feat(math): Upgrade Derivative Calculator to Complete Calculus Engine
1 parent 4c51831 commit 4c15555

15 files changed

Lines changed: 856 additions & 607 deletions
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
import re
2+
import math
3+
4+
def parse_polynomial(raw):
5+
clean_str = raw.replace(" ", "")
6+
if not clean_str:
7+
return None
8+
9+
term_pattern = r'([+-]?\d*\.?\d*\*?x(?:\^[+-]?\d+)?|[+-]?\d+\.?\d*)'
10+
matches = re.findall(term_pattern, clean_str)
11+
12+
if not matches:
13+
return None
14+
15+
terms_map = {}
16+
for term in matches:
17+
if not term or term in ('+', '-'):
18+
continue
19+
20+
coeff = 1.0
21+
power = 0
22+
23+
if 'x' in term:
24+
parts = term.split('x')
25+
coeff_str = parts[0].replace('*', '')
26+
if coeff_str == '+' or coeff_str == '':
27+
coeff = 1.0
28+
elif coeff_str == '-':
29+
coeff = -1.0
30+
else:
31+
try:
32+
coeff = float(coeff_str)
33+
except ValueError:
34+
return None
35+
36+
if len(parts) > 1 and parts[1].startswith('^'):
37+
try:
38+
power = int(parts[1][1:])
39+
except ValueError:
40+
return None
41+
else:
42+
power = 1
43+
else:
44+
try:
45+
coeff = float(term)
46+
except ValueError:
47+
return None
48+
power = 0
49+
50+
if power not in terms_map:
51+
terms_map[power] = 0.0
52+
terms_map[power] += coeff
53+
54+
result = []
55+
for p in sorted(terms_map.keys(), reverse=True):
56+
c = terms_map[p]
57+
if abs(c) > 1e-12:
58+
result.append({"coeff": c, "power": p, "is_ln": False})
59+
60+
if not result:
61+
result.append({"coeff": 0.0, "power": 0, "is_ln": False})
62+
63+
return result
64+
65+
def poly_to_string(terms, is_integral=False):
66+
if not terms:
67+
return "C" if is_integral else "0"
68+
69+
parts = []
70+
for i, term in enumerate(terms):
71+
c = term['coeff']
72+
p = term['power']
73+
is_ln = term['is_ln']
74+
75+
if abs(c) < 1e-12:
76+
continue
77+
78+
sign = "+" if c >= 0 else "-"
79+
abs_c = abs(c)
80+
c_str = str(int(round(abs_c))) if abs(abs_c - round(abs_c)) < 1e-9 else f"{abs_c:.6f}".rstrip("0").rstrip(".")
81+
82+
if is_ln:
83+
body = "ln|x|" if abs(abs_c - 1.0) < 1e-12 else f"{c_str}ln|x|"
84+
else:
85+
if p == 0:
86+
body = c_str
87+
elif p == 1:
88+
body = "x" if abs(abs_c - 1.0) < 1e-12 else f"{c_str}x"
89+
else:
90+
body = f"x^{p}" if abs(abs_c - 1.0) < 1e-12 else f"{c_str}x^{p}"
91+
92+
if len(parts) == 0:
93+
parts.append(body if sign == "+" else f"-{body}")
94+
else:
95+
parts.append(f"{sign} {body}")
96+
97+
res = " ".join(parts)
98+
if not res:
99+
res = "0"
100+
101+
if is_integral:
102+
if res == "0":
103+
res = "C"
104+
else:
105+
res += " + C"
106+
return res
107+
108+
def derivative(terms):
109+
res = []
110+
for t in terms:
111+
c = t['coeff']
112+
p = t['power']
113+
if p != 0:
114+
res.append({"coeff": c * p, "power": p - 1, "is_ln": False})
115+
if not res:
116+
res.append({"coeff": 0.0, "power": 0, "is_ln": False})
117+
return res
118+
119+
def integral(terms):
120+
res = []
121+
for t in terms:
122+
c = t['coeff']
123+
p = t['power']
124+
if p == -1:
125+
res.append({"coeff": c, "power": 0, "is_ln": True})
126+
else:
127+
res.append({"coeff": c / (p + 1), "power": p + 1, "is_ln": False})
128+
if not res:
129+
res.append({"coeff": 0.0, "power": 0, "is_ln": False})
130+
131+
# Sort for consistent display (ln at the end or powers sorted)
132+
res.sort(key=lambda x: (x['is_ln'], -x['power']))
133+
return res
134+
135+
def evaluate(terms, x):
136+
val = 0.0
137+
for t in terms:
138+
c = t['coeff']
139+
p = t['power']
140+
is_ln = t['is_ln']
141+
if is_ln:
142+
if x == 0:
143+
raise ValueError("Cannot evaluate ln|0|")
144+
val += c * math.log(abs(x))
145+
else:
146+
if x == 0 and p < 0:
147+
raise ValueError("Division by zero in negative exponent")
148+
val += c * math.pow(x, p)
149+
return val
150+
151+
def main():
152+
print("=" * 58)
153+
print("📈 COMPLETE CALCULUS ENGINE 📈")
154+
print("=" * 58)
155+
156+
while True:
157+
print("\nChoose an option:")
158+
print("1️⃣ Find 1st derivative")
159+
print("2️⃣ Find nth derivative")
160+
print("3️⃣ Evaluate derivative at x")
161+
print("4️⃣ Find indefinite integral")
162+
print("5️⃣ Evaluate definite integral")
163+
print("6️⃣ Exit")
164+
165+
choice = input("🎯 Enter your choice (1-6): ").strip()
166+
167+
if choice == "6":
168+
print("\n👋 Thanks for using Complete Calculus Engine! Goodbye! ✨\n")
169+
break
170+
171+
if choice not in {"1", "2", "3", "4", "5"}:
172+
print("❌ Please choose between 1 and 6.")
173+
continue
174+
175+
raw = input("\n📝 Enter equation (e.g. 3x^2 - x^-1 + 5): ").strip()
176+
if not raw:
177+
print("❌ Error: Input cannot be empty.")
178+
continue
179+
180+
terms = parse_polynomial(raw)
181+
if terms is None:
182+
print("❌ Error: Could not parse equation. Check your formatting.")
183+
continue
184+
185+
print(f"\n✨ Equation: {poly_to_string(terms)}")
186+
187+
if choice == "1":
188+
derived = derivative(terms)
189+
print(f"✅ First derivative: {poly_to_string(derived)}")
190+
191+
elif choice == "2":
192+
try:
193+
n_order = int(input("🎯 Enter derivative order n (>= 1): ").strip())
194+
if n_order < 1:
195+
raise ValueError
196+
except ValueError:
197+
print("❌ n must be an integer >= 1.")
198+
continue
199+
200+
derived = terms
201+
for _ in range(n_order):
202+
derived = derivative(derived)
203+
print(f"✅ {n_order}th derivative: {poly_to_string(derived)}")
204+
205+
elif choice == "3":
206+
try:
207+
x_val = float(input("🎯 Enter x value: ").strip())
208+
order = int(input("🎯 Derivative order to evaluate (>= 1): ").strip())
209+
if order < 1:
210+
raise ValueError
211+
except ValueError:
212+
print("❌ Enter valid numeric x and integer order >= 1.")
213+
continue
214+
215+
derived = terms
216+
for _ in range(order):
217+
derived = derivative(derived)
218+
219+
try:
220+
val_eval = evaluate(derived, x_val)
221+
x_str = str(int(round(x_val))) if abs(x_val - round(x_val)) < 1e-9 else f"{x_val:.6f}".rstrip("0").rstrip(".")
222+
val_eval_str = str(int(round(val_eval))) if abs(val_eval - round(val_eval)) < 1e-9 else f"{val_eval:.6f}".rstrip("0").rstrip(".")
223+
print(f"🔍 Derivative used: {poly_to_string(derived)}")
224+
print(f"✅ Value at x = {x_str}: {val_eval_str}")
225+
except ValueError as e:
226+
print(f"❌ Error during evaluation: {e}")
227+
228+
elif choice == "4":
229+
integral_terms = integral(terms)
230+
print(f"✅ Indefinite integral: {poly_to_string(integral_terms, is_integral=True)}")
231+
232+
elif choice == "5":
233+
try:
234+
lower = float(input("🎯 Enter lower bound: ").strip())
235+
upper = float(input("🎯 Enter upper bound: ").strip())
236+
except ValueError:
237+
print("❌ Enter valid numeric bounds.")
238+
continue
239+
240+
integral_terms = integral(terms)
241+
try:
242+
val_upper = evaluate(integral_terms, upper)
243+
val_lower = evaluate(integral_terms, lower)
244+
result = val_upper - val_lower
245+
result_str = str(int(round(result))) if abs(result - round(result)) < 1e-9 else f"{result:.6f}".rstrip("0").rstrip(".")
246+
print(f"🔍 Integral used: {poly_to_string(integral_terms)}")
247+
print(f"✅ Definite integral from {lower} to {upper}: {result_str}")
248+
except ValueError as e:
249+
print(f"❌ Error during evaluation: {e}")
250+
251+
if __name__ == "__main__":
252+
main()

0 commit comments

Comments
 (0)