Ethosoft-Math 0.1.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.
@@ -0,0 +1,858 @@
1
+ """MathTool v1: fonksiyon kaydi (registry), cagri ayristirici ve calistirici.
2
+
3
+ Tek dogruluk kaynagi bu dosya. PROTOCOL.md buradan uretilir:
4
+ python toolproto.py > PROTOCOL.md
5
+ """
6
+ import ast
7
+ import math
8
+ import re
9
+ import sys
10
+ from decimal import Decimal, getcontext
11
+ from fractions import Fraction
12
+
13
+ import sympy
14
+
15
+ getcontext().prec = 120
16
+
17
+ CALL_OPEN, CALL_CLOSE = "<tool_call>", "</tool_call>"
18
+ RES_OPEN, RES_CLOSE = "<tool_result>", "</tool_result>"
19
+
20
+
21
+ class ToolError(Exception):
22
+ pass
23
+
24
+
25
+ # ---------------------------------------------------------------- sayi bicimi
26
+ def fmt_frac(x: Fraction) -> str:
27
+ if x.denominator == 1:
28
+ return str(x.numerator)
29
+ d = x.denominator
30
+ while d % 2 == 0:
31
+ d //= 2
32
+ while d % 5 == 0:
33
+ d //= 5
34
+ if d == 1:
35
+ s = format(Decimal(x.numerator) / Decimal(x.denominator), "f")
36
+ return s.rstrip("0").rstrip(".") if "." in s else s
37
+ return f"{x.numerator}/{x.denominator}"
38
+
39
+
40
+ def to_text(v) -> str:
41
+ if isinstance(v, Fraction):
42
+ return fmt_frac(v)
43
+ if isinstance(v, bool):
44
+ return "true" if v else "false"
45
+ if isinstance(v, str):
46
+ return v
47
+ if isinstance(v, (list, tuple)):
48
+ return "[" + ", ".join(to_text(i) for i in v) + "]"
49
+ if isinstance(v, sympy.Basic):
50
+ if v.is_Rational:
51
+ return fmt_frac(Fraction(int(v.p), int(v.q)))
52
+ return sympy.sstr(v).replace("**", "^")
53
+ return str(v)
54
+
55
+
56
+ def _sy(x):
57
+ if isinstance(x, Fraction):
58
+ return sympy.Rational(x.numerator, x.denominator)
59
+ return x
60
+
61
+
62
+ def _back(e):
63
+ if isinstance(e, sympy.Basic) and e.is_Rational:
64
+ return Fraction(int(e.p), int(e.q))
65
+ return e
66
+
67
+
68
+ def _is_f(*xs):
69
+ return all(isinstance(x, Fraction) for x in xs)
70
+
71
+
72
+ def _int(x, name="arg"):
73
+ if isinstance(x, Fraction) and x.denominator == 1:
74
+ return int(x)
75
+ raise ToolError(f"{name}: tam sayi bekleniyor")
76
+
77
+
78
+ # ------------------------------------------------------------------- kayit
79
+ REG = {}
80
+ CATEGORY_ORDER = ["arithmetic", "percent", "number_theory", "exp_log", "trig",
81
+ "geometry", "stats_prob", "sequences", "algebra", "calculus",
82
+ "linear_algebra", "functions"]
83
+
84
+
85
+ def reg(name, cat, sig, doc):
86
+ def deco(fn):
87
+ REG[name] = dict(fn=fn, cat=cat, sig=sig, doc=doc)
88
+ return fn
89
+ return deco
90
+
91
+
92
+ # --- aritmetik
93
+ @reg("add", "arithmetic", "add(a, b, ...)", "toplama (+)")
94
+ def _add(*a):
95
+ if len(a) < 2:
96
+ raise ToolError("add en az 2 arguman")
97
+ if _is_f(*a):
98
+ return sum(a, Fraction(0))
99
+ return _back(sum((_sy(x) for x in a), sympy.Integer(0)))
100
+
101
+
102
+ @reg("sub", "arithmetic", "sub(a, b)", "cikarma (-)")
103
+ def _sub(a, b):
104
+ return a - b if _is_f(a, b) else _back(_sy(a) - _sy(b))
105
+
106
+
107
+ @reg("mul", "arithmetic", "mul(a, b, ...)", "carpma (*, x, ×, ·)")
108
+ def _mul(*a):
109
+ if len(a) < 2:
110
+ raise ToolError("mul en az 2 arguman")
111
+ if _is_f(*a):
112
+ r = Fraction(1)
113
+ for x in a:
114
+ r *= x
115
+ return r
116
+ r = sympy.Integer(1)
117
+ for x in a:
118
+ r *= _sy(x)
119
+ return _back(r)
120
+
121
+
122
+ @reg("div", "arithmetic", "div(a, b)", "bolme, kesin oran (/, ÷)")
123
+ def _div(a, b):
124
+ if b == 0:
125
+ raise ToolError("sifira bolme")
126
+ return a / b if _is_f(a, b) else _back(_sy(a) / _sy(b))
127
+
128
+
129
+ @reg("idiv", "arithmetic", "idiv(a, b)", "tam bolme, tabana yuvarlar (//)")
130
+ def _idiv(a, b):
131
+ if b == 0:
132
+ raise ToolError("sifira bolme")
133
+ return Fraction(math.floor(a / b))
134
+
135
+
136
+ @reg("mod", "arithmetic", "mod(a, b)", "mod, kalan (%)")
137
+ def _mod(a, b):
138
+ if b == 0:
139
+ raise ToolError("sifira bolme")
140
+ return a - b * math.floor(a / b)
141
+
142
+
143
+ @reg("pow", "arithmetic", "pow(a, b)", "us alma (^)")
144
+ def _pow(a, b):
145
+ if _is_f(a, b) and b.denominator == 1:
146
+ if abs(b) > 2000 or (abs(a.numerator) > 10**6 and abs(b) > 200):
147
+ raise ToolError("us cok buyuk")
148
+ if a == 0 and b < 0:
149
+ raise ToolError("sifirin negatif kuvveti")
150
+ return a ** int(b)
151
+ return _back(sympy.Pow(_sy(a), _sy(b)))
152
+
153
+
154
+ @reg("neg", "arithmetic", "neg(a)", "isaret degistirme (-a)")
155
+ def _neg(a):
156
+ return -a if _is_f(a) else _back(-_sy(a))
157
+
158
+
159
+ @reg("abs", "arithmetic", "abs(a)", "mutlak deger |a|")
160
+ def _abs(a):
161
+ return abs(a) if _is_f(a) else _back(sympy.Abs(_sy(a)))
162
+
163
+
164
+ @reg("sqrt", "arithmetic", "sqrt(a)", "karekok (√)")
165
+ def _sqrt(a):
166
+ if _is_f(a):
167
+ if a < 0:
168
+ raise ToolError("negatif sayinin karekoku")
169
+ n, d = a.numerator, a.denominator
170
+ rn, rd = math.isqrt(n), math.isqrt(d)
171
+ if rn * rn == n and rd * rd == d:
172
+ return Fraction(rn, rd)
173
+ return _back(sympy.sqrt(_sy(a)))
174
+
175
+
176
+ @reg("root", "arithmetic", "root(n, a)", "n. dereceden kok")
177
+ def _root(n, a):
178
+ return _back(sympy.root(_sy(a), _sy(n)))
179
+
180
+
181
+ def _round_half_up(x: Fraction, nd: int = 0) -> Fraction:
182
+ q = x * (Fraction(10) ** nd)
183
+ r = math.floor(q + Fraction(1, 2)) if q >= 0 else -math.floor(-q + Fraction(1, 2))
184
+ return Fraction(r) / (Fraction(10) ** nd)
185
+
186
+
187
+ @reg("floor", "arithmetic", "floor(a)", "tabana yuvarlama")
188
+ def _floor(a):
189
+ return Fraction(math.floor(a))
190
+
191
+
192
+ @reg("ceil", "arithmetic", "ceil(a)", "tavana yuvarlama")
193
+ def _ceil(a):
194
+ return Fraction(math.ceil(a))
195
+
196
+
197
+ @reg("round", "arithmetic", "round(a, nd=0)", "nd basamaga yuvarla (yarim yukari)")
198
+ def _round(a, nd=Fraction(0)):
199
+ return _round_half_up(a, int(nd))
200
+
201
+
202
+ @reg("min", "arithmetic", "min(a, b, ...)", "en kucuk")
203
+ def _min(*a):
204
+ return min(a)
205
+
206
+
207
+ @reg("max", "arithmetic", "max(a, b, ...)", "en buyuk")
208
+ def _max(*a):
209
+ return max(a)
210
+
211
+
212
+ @reg("avg", "arithmetic", "avg(a, b, ...)", "aritmetik ortalama")
213
+ def _avg(*a):
214
+ return sum(a, Fraction(0)) / len(a)
215
+
216
+
217
+ # --- yuzde
218
+ @reg("pct_of", "percent", "pct_of(p, x)", "x'in yuzde p'si = p*x/100")
219
+ def _pct_of(p, x):
220
+ return p * x / 100
221
+
222
+
223
+ @reg("pct", "percent", "pct(a, b)", "a, b'nin yuzde kaci = a/b*100")
224
+ def _pct(a, b):
225
+ if b == 0:
226
+ raise ToolError("sifira bolme")
227
+ return a / b * 100
228
+
229
+
230
+ @reg("pct_change", "percent", "pct_change(old, new)", "yuzde degisim = (new-old)/old*100")
231
+ def _pct_change(old, new):
232
+ if old == 0:
233
+ raise ToolError("sifira bolme")
234
+ return (new - old) / old * 100
235
+
236
+
237
+ # --- sayi teorisi
238
+ @reg("fact", "number_theory", "fact(n)", "faktoriyel (n!), n <= 500")
239
+ def _fact(n):
240
+ n = _int(n)
241
+ if not 0 <= n <= 500:
242
+ raise ToolError("fact aralik disi")
243
+ return Fraction(math.factorial(n))
244
+
245
+
246
+ @reg("comb", "number_theory", "comb(n, k)", "kombinasyon C(n,k)")
247
+ def _comb(n, k):
248
+ n, k = _int(n), _int(k)
249
+ if n < 0 or k < 0 or n > 5000:
250
+ raise ToolError("comb aralik disi")
251
+ return Fraction(math.comb(n, k))
252
+
253
+
254
+ @reg("perm", "number_theory", "perm(n, k)", "permutasyon P(n,k)")
255
+ def _perm(n, k):
256
+ n, k = _int(n), _int(k)
257
+ if n < 0 or k < 0 or n > 5000:
258
+ raise ToolError("perm aralik disi")
259
+ return Fraction(math.perm(n, k))
260
+
261
+
262
+ @reg("gcd", "number_theory", "gcd(a, b, ...)", "OBEB")
263
+ def _gcd(*a):
264
+ return Fraction(math.gcd(*[_int(x) for x in a]))
265
+
266
+
267
+ @reg("lcm", "number_theory", "lcm(a, b, ...)", "OKEK")
268
+ def _lcm(*a):
269
+ return Fraction(math.lcm(*[_int(x) for x in a]))
270
+
271
+
272
+ @reg("is_prime", "number_theory", "is_prime(n)", "asal mi? true/false")
273
+ def _is_prime(n):
274
+ return bool(sympy.isprime(_int(n)))
275
+
276
+
277
+ @reg("factorint", "number_theory", "factorint(n)", "asal carpanlara ayirma, orn. 2^3 * 3")
278
+ def _factorint(n):
279
+ f = sympy.factorint(_int(n))
280
+ return " * ".join(f"{p}^{e}" if e > 1 else str(p) for p, e in sorted(f.items()))
281
+
282
+
283
+ @reg("n_divisors", "number_theory", "n_divisors(n)", "bolen sayisi")
284
+ def _n_div(n):
285
+ return Fraction(int(sympy.divisor_count(_int(n))))
286
+
287
+
288
+ @reg("digit_sum", "number_theory", "digit_sum(n)", "rakamlar toplami")
289
+ def _digit_sum(n):
290
+ return Fraction(sum(int(c) for c in str(abs(_int(n)))))
291
+
292
+
293
+ @reg("to_base", "number_theory", "to_base(n, b)", "n'yi b tabaninda yaz (2..36)")
294
+ def _to_base(n, b):
295
+ n, b = _int(n), _int(b)
296
+ if not 2 <= b <= 36:
297
+ raise ToolError("taban 2..36")
298
+ digs = "0123456789abcdefghijklmnopqrstuvwxyz"
299
+ s, m = "", abs(n)
300
+ while True:
301
+ s = digs[m % b] + s
302
+ m //= b
303
+ if m == 0:
304
+ break
305
+ return ("-" if n < 0 else "") + s
306
+
307
+
308
+ @reg("from_base", "number_theory", "from_base(\"s\", b)", "b tabanindaki s'yi onluga cevir")
309
+ def _from_base(s, b):
310
+ return Fraction(int(str(s), _int(b)))
311
+
312
+
313
+ # --- us / log / sabitler
314
+ @reg("exp", "exp_log", "exp(x)", "e^x")
315
+ def _exp(x):
316
+ return _back(sympy.exp(_sy(x)))
317
+
318
+
319
+ @reg("ln", "exp_log", "ln(x)", "dogal logaritma")
320
+ def _ln(x):
321
+ if x <= 0:
322
+ raise ToolError("ln alan disi")
323
+ return _back(sympy.log(_sy(x)))
324
+
325
+
326
+ @reg("log", "exp_log", "log(x, base=10)", "logaritma")
327
+ def _log(x, base=Fraction(10)):
328
+ if x <= 0 or base <= 0 or base == 1:
329
+ raise ToolError("log alan disi")
330
+ return _back(sympy.log(_sy(x), _sy(base)))
331
+
332
+
333
+ @reg("pi", "exp_log", "pi()", "π sabiti")
334
+ def _pi():
335
+ return sympy.pi
336
+
337
+
338
+ @reg("e", "exp_log", "e()", "Euler sabiti")
339
+ def _e():
340
+ return sympy.E
341
+
342
+
343
+ # --- trigonometri (radyan; derece icin deg())
344
+ @reg("deg", "trig", "deg(x)", "dereceyi radyana cevir: x*pi/180")
345
+ def _deg(x):
346
+ return _back(_sy(x) * sympy.pi / 180)
347
+
348
+
349
+ @reg("rad2deg", "trig", "rad2deg(x)", "radyani dereceye cevir")
350
+ def _rad2deg(x):
351
+ return _back(sympy.simplify(_sy(x) * 180 / sympy.pi))
352
+
353
+
354
+ def _trig(name, fn, doc):
355
+ @reg(name, "trig", f"{name}(x)", doc)
356
+ def f(x):
357
+ return _back(sympy.simplify(fn(_sy(x))))
358
+ return f
359
+
360
+
361
+ for _n, _f, _d in [("sin", sympy.sin, "sinus (radyan)"), ("cos", sympy.cos, "kosinus (radyan)"),
362
+ ("tan", sympy.tan, "tanjant (radyan)"), ("cot", sympy.cot, "kotanjant"),
363
+ ("sec", sympy.sec, "sekant"), ("csc", sympy.csc, "kosekant"),
364
+ ("asin", sympy.asin, "arcsin"), ("acos", sympy.acos, "arccos"),
365
+ ("atan", sympy.atan, "arctan"), ("sinh", sympy.sinh, "sinh"),
366
+ ("cosh", sympy.cosh, "cosh"), ("tanh", sympy.tanh, "tanh")]:
367
+ _trig(_n, _f, _d)
368
+
369
+
370
+ @reg("atan2", "trig", "atan2(y, x)", "iki arguman arctan")
371
+ def _atan2(y, x):
372
+ return _back(sympy.atan2(_sy(y), _sy(x)))
373
+
374
+
375
+ # --- geometri
376
+ @reg("hypot", "geometry", "hypot(a, b)", "hipotenus = sqrt(a^2+b^2)")
377
+ def _hypot(a, b):
378
+ return _sqrt(a * a + b * b)
379
+
380
+
381
+ @reg("dist", "geometry", "dist(x1, y1, x2, y2)", "iki nokta arasi uzaklik")
382
+ def _dist(x1, y1, x2, y2):
383
+ return _sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
384
+
385
+
386
+ @reg("area_circle", "geometry", "area_circle(r)", "daire alani pi*r^2")
387
+ def _area_circle(r):
388
+ return _back(sympy.pi * _sy(r) ** 2)
389
+
390
+
391
+ @reg("circumference", "geometry", "circumference(r)", "cember uzunlugu 2*pi*r")
392
+ def _circumference(r):
393
+ return _back(2 * sympy.pi * _sy(r))
394
+
395
+
396
+ @reg("area_triangle", "geometry", "area_triangle(b, h)", "ucgen alani b*h/2")
397
+ def _area_tri(b, h):
398
+ return b * h / 2
399
+
400
+
401
+ @reg("area_heron", "geometry", "area_heron(a, b, c)", "Heron formulu")
402
+ def _area_heron(a, b, c):
403
+ s = (a + b + c) / 2
404
+ return _sqrt(s * (s - a) * (s - b) * (s - c))
405
+
406
+
407
+ @reg("area_trapezoid", "geometry", "area_trapezoid(a, b, h)", "yamuk alani (a+b)*h/2")
408
+ def _area_trap(a, b, h):
409
+ return (a + b) * h / 2
410
+
411
+
412
+ @reg("vol_sphere", "geometry", "vol_sphere(r)", "kure hacmi 4/3*pi*r^3")
413
+ def _vol_sphere(r):
414
+ return _back(sympy.Rational(4, 3) * sympy.pi * _sy(r) ** 3)
415
+
416
+
417
+ @reg("area_sphere", "geometry", "area_sphere(r)", "kure yuzeyi 4*pi*r^2")
418
+ def _area_sphere(r):
419
+ return _back(4 * sympy.pi * _sy(r) ** 2)
420
+
421
+
422
+ @reg("vol_cylinder", "geometry", "vol_cylinder(r, h)", "silindir hacmi pi*r^2*h")
423
+ def _vol_cyl(r, h):
424
+ return _back(sympy.pi * _sy(r) ** 2 * _sy(h))
425
+
426
+
427
+ @reg("vol_cone", "geometry", "vol_cone(r, h)", "koni hacmi pi*r^2*h/3")
428
+ def _vol_cone(r, h):
429
+ return _back(sympy.pi * _sy(r) ** 2 * _sy(h) / 3)
430
+
431
+
432
+ @reg("polygon_angle_sum", "geometry", "polygon_angle_sum(n)", "n-genin ic acilar toplami (n-2)*180")
433
+ def _poly_angle(n):
434
+ return (n - 2) * 180
435
+
436
+
437
+ # --- istatistik / olasilik
438
+ @reg("median", "stats_prob", "median(a, b, ...)", "medyan")
439
+ def _median(*a):
440
+ s = sorted(a)
441
+ n = len(s)
442
+ return s[n // 2] if n % 2 else (s[n // 2 - 1] + s[n // 2]) / 2
443
+
444
+
445
+ @reg("mode", "stats_prob", "mode(a, b, ...)", "tepe deger (esitlikte en kucuk)")
446
+ def _mode(*a):
447
+ best = max(a.count(x) for x in a)
448
+ return min(x for x in a if a.count(x) == best)
449
+
450
+
451
+ @reg("variance", "stats_prob", "variance(a, b, ...)", "populasyon varyansi")
452
+ def _variance(*a):
453
+ m = sum(a, Fraction(0)) / len(a)
454
+ return sum(((x - m) ** 2 for x in a), Fraction(0)) / len(a)
455
+
456
+
457
+ @reg("stdev", "stats_prob", "stdev(a, b, ...)", "populasyon standart sapmasi")
458
+ def _stdev(*a):
459
+ return _sqrt(_variance(*a))
460
+
461
+
462
+ @reg("binom_pmf", "stats_prob", "binom_pmf(n, k, p)", "binom olasilik C(n,k) p^k (1-p)^(n-k)")
463
+ def _binom(n, k, p):
464
+ n, k = _int(n), _int(k)
465
+ return math.comb(n, k) * p ** k * (1 - p) ** (n - k)
466
+
467
+
468
+ # --- diziler
469
+ @reg("arith_nth", "sequences", "arith_nth(a1, d, n)", "aritmetik dizi n. terim")
470
+ def _arith_nth(a1, d, n):
471
+ return a1 + (n - 1) * d
472
+
473
+
474
+ @reg("arith_sum", "sequences", "arith_sum(a1, d, n)", "aritmetik dizi ilk n terim toplami")
475
+ def _arith_sum(a1, d, n):
476
+ return n * (2 * a1 + (n - 1) * d) / 2
477
+
478
+
479
+ @reg("geom_nth", "sequences", "geom_nth(a1, r, n)", "geometrik dizi n. terim")
480
+ def _geom_nth(a1, r, n):
481
+ return a1 * _pow(r, n - 1)
482
+
483
+
484
+ @reg("sum_range", "sequences", "sum_range(\"expr\", \"i\", a, b)", "toplam sum_{i=a}^{b} expr (b-a <= 100000)")
485
+ def _sum_range(e, var, a, b):
486
+ a, b = _int(a), _int(b)
487
+ if b - a > 100000:
488
+ raise ToolError("aralik cok buyuk")
489
+ return _back(sympy.summation(_sym(e), (sympy.Symbol(str(var)), a, b)))
490
+
491
+
492
+ @reg("prod_range", "sequences", "prod_range(\"expr\", \"i\", a, b)", "carpim prod_{i=a}^{b} expr (b-a <= 2000)")
493
+ def _prod_range(e, var, a, b):
494
+ a, b = _int(a), _int(b)
495
+ if b - a > 2000:
496
+ raise ToolError("aralik cok buyuk")
497
+ return _back(sympy.product(_sym(e), (sympy.Symbol(str(var)), a, b)))
498
+
499
+
500
+ @reg("geom_sum", "sequences", "geom_sum(a1, r, n)", "geometrik dizi ilk n terim toplami")
501
+ def _geom_sum(a1, r, n):
502
+ if r == 1:
503
+ return a1 * n
504
+ return a1 * (1 - _pow(r, n)) / (1 - r)
505
+
506
+
507
+ # --- sembolik (string argumanli)
508
+ _SYM_BAD = re.compile(r"__|[A-Za-z_]\s*\.|\[|\]|lambda|import|exec|eval|open\(|;|\\")
509
+ _SYM_NAMES = {n: getattr(sympy, n) for n in [
510
+ "sqrt", "sin", "cos", "tan", "cot", "sec", "csc", "asin", "acos", "atan", "sinh", "cosh",
511
+ "tanh", "exp", "log", "ln", "Abs", "pi", "E", "I", "oo", "factorial", "binomial", "Rational",
512
+ "Integer", "Float", "Symbol", "floor", "ceiling", "gcd", "lcm", "Eq", "Function"]}
513
+ _SYM_NAMES["e"] = sympy.E
514
+ _SYM_NAMES["comb"] = sympy.binomial
515
+ _SYM_NAMES["abs"] = sympy.Abs
516
+ _SYM_NAMES["ln"] = sympy.log
517
+
518
+
519
+ def _sym(s):
520
+ s = str(s)
521
+ if _SYM_BAD.search(s) or len(s) > 2000:
522
+ raise ToolError("gecersiz sembolik ifade")
523
+ from sympy.parsing.sympy_parser import (parse_expr, standard_transformations,
524
+ implicit_multiplication_application, convert_xor)
525
+ tr = standard_transformations + (implicit_multiplication_application, convert_xor)
526
+ try:
527
+ return parse_expr(s, local_dict=dict(_SYM_NAMES), global_dict={"__builtins__": {}},
528
+ transformations=tr, evaluate=True)
529
+ except Exception as ex:
530
+ raise ToolError(f"ayristirilamadi: {ex}")
531
+
532
+
533
+ def _sym_eq(s):
534
+ s = str(s)
535
+ if "=" in s:
536
+ l, r = s.split("=", 1)
537
+ return sympy.Eq(_sym(l), _sym(r))
538
+ return _sym(s)
539
+
540
+
541
+ @reg("simplify", "algebra", "simplify(\"expr\")", "sadelestir")
542
+ def _simplify(e):
543
+ return sympy.simplify(_sym(e))
544
+
545
+
546
+ @reg("expand", "algebra", "expand(\"expr\")", "parantez ac")
547
+ def _expand(e):
548
+ return sympy.expand(_sym(e))
549
+
550
+
551
+ @reg("factor", "algebra", "factor(\"expr\")", "carpanlara ayir")
552
+ def _factor(e):
553
+ return sympy.factor(_sym(e))
554
+
555
+
556
+ def _set_text(s):
557
+ if s == sympy.S.EmptySet:
558
+ return "empty"
559
+ if isinstance(s, sympy.Union):
560
+ return " U ".join(_set_text(a) for a in sorted(
561
+ s.args, key=lambda x: float(x.start) if getattr(x, "start", None) is not None
562
+ and x.start.is_finite else -1e18))
563
+ if isinstance(s, sympy.Interval):
564
+ l = "(" if s.left_open else "["
565
+ r = ")" if s.right_open else "]"
566
+ return f"{l}{to_text(s.start)}, {to_text(s.end)}{r}"
567
+ if isinstance(s, sympy.FiniteSet):
568
+ return "{" + ", ".join(to_text(x) for x in sorted(s.args, key=float)) + "}"
569
+ return str(s)
570
+
571
+
572
+ @reg("solve", "algebra", "solve(\"eq\", \"x\")", "denklem veya esitsizlik coz (esitlik yoksa =0 varsayilir)")
573
+ def _solve(eq, var):
574
+ v = sympy.Symbol(str(var))
575
+ if re.search(r"[<>]", str(eq)):
576
+ rel = _sym(str(eq))
577
+ return _set_text(sympy.solve_univariate_inequality(rel, v, relational=False))
578
+ sols = sympy.solve(_sym_eq(eq), v)
579
+ return sorted(sols, key=lambda z: (not z.is_real, float(sympy.re(z)) if z.is_number else 0)) \
580
+ if all(getattr(z, "is_number", False) for z in sols) else sols
581
+
582
+
583
+ @reg("solve_system", "algebra", "solve_system(\"eq1; eq2\", \"x, y\")", "denklem sistemi coz")
584
+ def _solve_system(eqs, vars_):
585
+ eq_list = [_sym_eq(e) for e in str(eqs).split(";")]
586
+ vs = [sympy.Symbol(v.strip()) for v in str(vars_).split(",")]
587
+ r = sympy.solve(eq_list, vs, dict=True)
588
+ return "; ".join(", ".join(f"{k}={to_text(v)}" for k, v in d.items()) for d in r)
589
+
590
+
591
+ @reg("equal", "algebra", "equal(a, b)", "iki ifade ozdes/esit mi? true/false (sayi veya \"ifade\")")
592
+ def _equal_tool(a, b):
593
+ def conv(x):
594
+ return _sy(x) if isinstance(x, Fraction) else _sym(x)
595
+ d = conv(a) - conv(b)
596
+ if d == 0:
597
+ return True
598
+ if not d.free_symbols:
599
+ return bool(abs(sympy.N(d, 40)) < sympy.Float("1e-30"))
600
+ d = sympy.expand(d)
601
+ return bool(d == 0 or sympy.cancel(d) == 0)
602
+
603
+
604
+ @reg("subs", "algebra", "subs(\"expr\", \"x\", value)", "degisken yerine deger koy")
605
+ def _subs(e, var, val):
606
+ return _back(_sym(e).subs(sympy.Symbol(str(var)), _sy(val) if not isinstance(val, str) else _sym(val)))
607
+
608
+
609
+ @reg("evalf", "algebra", "evalf(\"expr\", digits=10)", "ondalik yaklasik deger")
610
+ def _evalf(e, digits=Fraction(10)):
611
+ v = _sym(e) if isinstance(e, str) else _sy(e)
612
+ return str(sympy.N(v, int(digits)))
613
+
614
+
615
+ @reg("diff", "calculus", "diff(\"expr\", \"x\")", "turev")
616
+ def _diff(e, var):
617
+ return sympy.diff(_sym(e), sympy.Symbol(str(var)))
618
+
619
+
620
+ @reg("integrate", "calculus", "integrate(\"expr\", \"x\", a?, b?)", "integral (belirsiz veya belirli)")
621
+ def _integrate(e, var, a=None, b=None):
622
+ v = sympy.Symbol(str(var))
623
+ if a is None:
624
+ return sympy.integrate(_sym(e), v)
625
+ return _back(sympy.integrate(_sym(e), (v, _sy(a), _sy(b))))
626
+
627
+
628
+ @reg("limit", "calculus", "limit(\"expr\", \"x\", point)", "limit (point: sayi veya \"oo\")")
629
+ def _limit(e, var, point):
630
+ p = _sym(point) if isinstance(point, str) else _sy(point)
631
+ return _back(sympy.limit(_sym(e), sympy.Symbol(str(var)), p))
632
+
633
+
634
+ # --- lineer cebir (matris/vektor: "[[1,2],[3,4]]")
635
+ def _mat(s):
636
+ try:
637
+ return sympy.Matrix(ast.literal_eval(str(s)))
638
+ except Exception:
639
+ raise ToolError("matris ayristirilamadi")
640
+
641
+
642
+ def _mat_text(m):
643
+ return str(m.tolist()).replace(" ", "")
644
+
645
+
646
+ @reg("det", "linear_algebra", "det(\"[[1,2],[3,4]]\")", "determinant")
647
+ def _det(m):
648
+ return _back(_mat(m).det())
649
+
650
+
651
+ @reg("inv", "linear_algebra", "inv(\"[[1,2],[3,4]]\")", "ters matris")
652
+ def _inv(m):
653
+ return _mat_text(_mat(m).inv())
654
+
655
+
656
+ @reg("matmul", "linear_algebra", "matmul(A, B)", "matris carpimi")
657
+ def _matmul(a, b):
658
+ return _mat_text(_mat(a) * _mat(b))
659
+
660
+
661
+ @reg("transpose", "linear_algebra", "transpose(A)", "devrik matris")
662
+ def _transpose(m):
663
+ return _mat_text(_mat(m).T)
664
+
665
+
666
+ @reg("dot", "linear_algebra", "dot(u, v)", "ic carpim")
667
+ def _dot(u, v):
668
+ return _back(_mat(u).dot(_mat(v)))
669
+
670
+
671
+ @reg("cross", "linear_algebra", "cross(u, v)", "vektorel carpim (3B)")
672
+ def _cross(u, v):
673
+ return _mat_text(sympy.Matrix(_mat(u)).cross(sympy.Matrix(_mat(v))))
674
+
675
+
676
+ @reg("norm", "linear_algebra", "norm(u)", "vektor uzunlugu")
677
+ def _norm(u):
678
+ return _back(_mat(u).norm())
679
+
680
+
681
+ # --- fonksiyon tanimlama
682
+ @reg("define", "functions", "define(\"f\", \"x, y\", \"x^2 + y\")",
683
+ "f(x,y) fonksiyonu tanimla; sonra f(2, 3) diye cagrilir")
684
+ def _define(name, vars_, body):
685
+ return f"{name}({vars_}) = {body}" # kayit run() icinde
686
+
687
+
688
+ # ---------------------------------------------------------- cagri ayristirma
689
+ _TOK = re.compile(r'\s*(?:(?P<num>-?\d+(?:\.\d+)?)|(?P<id>[A-Za-z_][A-Za-z_0-9]*)|'
690
+ r'(?P<str>"(?:[^"\\]|\\.)*")|(?P<p>[(),]))')
691
+
692
+
693
+ def _tokenize(s):
694
+ pos, out = 0, []
695
+ s = s.strip()
696
+ while pos < len(s):
697
+ m = _TOK.match(s, pos)
698
+ if not m:
699
+ raise ToolError(f"gecersiz karakter: {s[pos:pos+10]!r}")
700
+ pos = m.end()
701
+ for k in ("num", "id", "str", "p"):
702
+ if m.group(k) is not None:
703
+ out.append((k, m.group(k)))
704
+ break
705
+ return out
706
+
707
+
708
+ def parse_call(text: str):
709
+ toks = _tokenize(text)
710
+ pos = 0
711
+
712
+ def parse_val():
713
+ nonlocal pos
714
+ if pos >= len(toks):
715
+ raise ToolError("beklenmeyen son")
716
+ k, v = toks[pos]
717
+ if k == "num":
718
+ pos += 1
719
+ return Fraction(Decimal(v))
720
+ if k == "str":
721
+ pos += 1
722
+ return ast.literal_eval(v)
723
+ if k == "id":
724
+ pos += 1
725
+ if pos < len(toks) and toks[pos] == ("p", "("):
726
+ pos += 1
727
+ args = []
728
+ if toks[pos] == ("p", ")"):
729
+ pos += 1
730
+ return ("call", v, args)
731
+ while True:
732
+ args.append(parse_val())
733
+ if toks[pos] == ("p", ","):
734
+ pos += 1
735
+ continue
736
+ if toks[pos] == ("p", ")"):
737
+ pos += 1
738
+ break
739
+ raise ToolError("',' veya ')' bekleniyordu")
740
+ return ("call", v, args)
741
+ return v # cplak isim -> string
742
+ raise ToolError(f"beklenmeyen: {v}")
743
+
744
+ node = parse_val()
745
+ if pos != len(toks):
746
+ raise ToolError("fazla token")
747
+ return node
748
+
749
+
750
+ def run(node, env=None):
751
+ env = env if env is not None else {}
752
+ if isinstance(node, (Fraction, str)):
753
+ return node
754
+ _, name, args = node
755
+ if name in env:
756
+ vars_, body = env[name]
757
+ vals = [run(a, env) for a in args]
758
+ if len(vals) != len(vars_):
759
+ raise ToolError(f"{name}: {len(vars_)} arguman bekleniyor")
760
+ return _back(body.subs({sympy.Symbol(v): _sy(x) for v, x in zip(vars_, vals)}))
761
+ if name not in REG:
762
+ raise ToolError(f"bilinmeyen fonksiyon: {name}")
763
+ if name == "define":
764
+ n, vs, body = [run(a, env) for a in args]
765
+ env[str(n)] = ([v.strip() for v in str(vs).split(",")], _sym(body))
766
+ return f"{n}({vs}) = {body}"
767
+ vals = [run(a, env) for a in args]
768
+ try:
769
+ return REG[name]["fn"](*vals)
770
+ except ToolError:
771
+ raise
772
+ except (TypeError, ValueError, ZeroDivisionError, OverflowError, ArithmeticError) as ex:
773
+ raise ToolError(f"{name}: {ex}")
774
+
775
+
776
+ def execute(call_text: str, env=None) -> str:
777
+ return to_text(run(parse_call(call_text), env))
778
+
779
+
780
+ # ------------------------------------------------------------ dokuman uretimi
781
+ _HEADER = '''# MathTool v1 - Matematik Tool-Use Protokolu
782
+
783
+ Amac: Matematik cozumlerindeki **her hesabi** bir arac cagrisina cevirmek. Model sayiyi
784
+ "kafadan" yazmaz; hesabi araca yaptirir, sonucu araçtan alir.
785
+
786
+ ## Bicim
787
+
788
+ ```
789
+ <tool_call>FONKSIYON(arg1, arg2, ...)</tool_call><tool_result>SONUC</tool_result>
790
+ ```
791
+
792
+ - `<tool_call>` blogunu model uretir, `<tool_result>` blogunu **calistirici** yazar (egitimde loss maskelenir).
793
+ - Her satir tek bir fonksiyon agaci: ic ice cagri serbest. `(3+4)*2` -> `mul(add(3, 4), 2)`.
794
+ - Argumanlar: sayi (`3`, `-2`, `0.5`), ic cagri, ya da `"..."` sembolik ifade.
795
+ - Sayilar her zaman **nokta ondalikli**, binlik ayirac yok (`1000`, `0.25`). Turkce metinde de bu korunur.
796
+ - Kesin sonuc: tam sayi `7`, tam ondalik `0.25`, aksi halde kesir `1/3`. Irrasyonel sonuc kesin yazilir: `sqrt(2)`, `pi`, `2*pi`.
797
+ - Yaklasik deger istenirse `evalf("expr", 6)` kullanilir (sonuc string).
798
+ - Hata: `<tool_result>ERROR: neden</tool_result>`. Model hatadan sonra duzeltilmis cagri yapar.
799
+
800
+ ## Operator -> fonksiyon tablosu
801
+
802
+ | Yazi | Fonksiyon | Yazi | Fonksiyon |
803
+ |---|---|---|---|
804
+ | `a + b` | `add(a, b)` | `a ^ b`, `a**b` | `pow(a, b)` |
805
+ | `a - b` | `sub(a, b)` | `-a` | `neg(a)` |
806
+ | `a * b`, `a x b`, `a × b`, `a · b` | `mul(a, b)` | `n!` | `fact(n)` |
807
+ | `a / b`, `a ÷ b`, `\\\\frac{a}{b}` | `div(a, b)` | `√a`, `\\\\sqrt{a}` | `sqrt(a)` |
808
+ | `a % b` (kalan) | `mod(a, b)` | `p% of x` | `pct_of(p, x)` |
809
+ | `a // b` | `idiv(a, b)` | `\\\\binom{n}{k}`, `C(n,k)` | `comb(n, k)` |
810
+
811
+ Toplama/carpma ayni seviyede ardisiksa tek cagri: `1+2+3` -> `add(1, 2, 3)`.
812
+ Aciyi dereceyle verirsen `deg()` ile radyana cevir: `sin(30°)` -> `sin(deg(30))` -> `0.5`.
813
+
814
+ ## Fonksiyon tanimlama ve kullanma
815
+
816
+ ```
817
+ <tool_call>define("f", "x", "x^2 + 1")</tool_call><tool_result>f(x) = x^2 + 1</tool_result>
818
+ <tool_call>f(3)</tool_call><tool_result>10</tool_result>
819
+ ```
820
+
821
+ Tanim, ayni cozum (konusma) icinde gecerlidir.
822
+
823
+ ## Ornekler
824
+
825
+ | Metin | Tool-use hali |
826
+ |---|---|
827
+ | `3 + 4 = 7` | `<tool_call>add(3, 4)</tool_call><tool_result>7</tool_result>` |
828
+ | `48 / 2 = 24` | `<tool_call>div(48, 2)</tool_call><tool_result>24</tool_result>` |
829
+ | `sin 30° = 1/2` | `<tool_call>sin(deg(30))</tool_call><tool_result>0.5</tool_result>` |
830
+ | `C(10, 3) = 120` | `<tool_call>comb(10, 3)</tool_call><tool_result>120</tool_result>` |
831
+ | `x^2 - 4 = 0 -> x = ±2` | `<tool_call>solve("x^2 - 4 = 0", "x")</tool_call><tool_result>[-2, 2]</tool_result>` |
832
+ | `d/dx x^3 = 3x^2` | `<tool_call>diff("x^3", "x")</tool_call><tool_result>3*x^2</tool_result>` |
833
+
834
+ ## Fonksiyon listesi
835
+ '''
836
+
837
+ _CAT_TR = {"arithmetic": "Aritmetik", "percent": "Yuzde", "number_theory": "Sayi teorisi / sayma",
838
+ "exp_log": "Us, logaritma, sabitler", "trig": "Trigonometri", "geometry": "Geometri",
839
+ "stats_prob": "Istatistik / olasilik", "sequences": "Diziler", "algebra": "Cebir (sembolik)",
840
+ "calculus": "Analiz", "linear_algebra": "Lineer cebir", "functions": "Fonksiyon tanimlama"}
841
+
842
+
843
+ def render_protocol_md() -> str:
844
+ out = [_HEADER]
845
+ for cat in CATEGORY_ORDER:
846
+ items = [(n, r) for n, r in REG.items() if r["cat"] == cat]
847
+ if not items:
848
+ continue
849
+ out.append(f"\n### {_CAT_TR[cat]}\n\n| Cagri | Aciklama |\n|---|---|")
850
+ for n, r in items:
851
+ out.append(f"| `{r['sig']}` | {r['doc']} |")
852
+ out.append(f"\nToplam: **{len(REG)} fonksiyon**.\n")
853
+ return "\n".join(out)
854
+
855
+
856
+ if __name__ == "__main__":
857
+ sys.stdout.reconfigure(encoding="utf-8")
858
+ print(render_protocol_md())