From 7e1ff3b6b2128e440ff45724d710ffede0b0dae9 Mon Sep 17 00:00:00 2001 From: Crizomb <62544756+Crizomb@users.noreply.github.com> Date: Wed, 2 Aug 2023 00:39:13 +0200 Subject: [PATCH] Add files via upload --- python_symb/__pycache__/expr.cpython-311.pyc | Bin 0 -> 2465 bytes .../__pycache__/fraction.cpython-311.pyc | Bin 0 -> 6417 bytes .../__pycache__/operator_file.cpython-311.pyc | Bin 0 -> 4547 bytes python_symb/__pycache__/parse.cpython-311.pyc | Bin 0 -> 5617 bytes .../__pycache__/symbols.cpython-311.pyc | Bin 0 -> 1907 bytes python_symb/__pycache__/tools.cpython-311.pyc | Bin 0 -> 550 bytes python_symb/__pycache__/tree.cpython-311.pyc | Bin 0 -> 5318 bytes python_symb/expr.py | 38 +++++ python_symb/fraction.py | 131 ++++++++++++++++++ python_symb/integers.py | 3 + python_symb/operator_file.py | 62 +++++++++ python_symb/parse.py | 114 +++++++++++++++ python_symb/symbols.py | 31 +++++ python_symb/tools.py | 14 ++ python_symb/tree.py | 80 +++++++++++ python_symb/visual.py | 92 ++++++++++++ 16 files changed, 565 insertions(+) create mode 100644 python_symb/__pycache__/expr.cpython-311.pyc create mode 100644 python_symb/__pycache__/fraction.cpython-311.pyc create mode 100644 python_symb/__pycache__/operator_file.cpython-311.pyc create mode 100644 python_symb/__pycache__/parse.cpython-311.pyc create mode 100644 python_symb/__pycache__/symbols.cpython-311.pyc create mode 100644 python_symb/__pycache__/tools.cpython-311.pyc create mode 100644 python_symb/__pycache__/tree.cpython-311.pyc create mode 100644 python_symb/expr.py create mode 100644 python_symb/fraction.py create mode 100644 python_symb/integers.py create mode 100644 python_symb/operator_file.py create mode 100644 python_symb/parse.py create mode 100644 python_symb/symbols.py create mode 100644 python_symb/tools.py create mode 100644 python_symb/tree.py create mode 100644 python_symb/visual.py diff --git a/python_symb/__pycache__/expr.cpython-311.pyc b/python_symb/__pycache__/expr.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a46f0474614c2e1a5cabbd11eef1c63537c1558d GIT binary patch literal 2465 zcmZ`*O=ufO6rS1DN-Nv4lGt)WloFA|20^iU2yP%hw56eiG;Y9g4@<&!t6kYD`=idR zQrS4crH05Q2fNhRffjUfZIVlS>b|>Y7>WQlp-(Lk|S%f;AK1FD4OC# zwWyFIAjhw3Q77d@AQ7`e?XD=P)^ho%|GH_JBgiD}nhbDx(iX80Zp`adeV zZ2@CKZUeJM z*NIO%77v-K9hli)vp^RlCM`%VogteN90-14L)53pHk|~R6kZ010#(1#E0?z|$8!DLt-1WH!JJ#n z``XI;JXf0YXFazi=1jvk5aM#)Z_Vg@&iN{5f$!s`Id&YZ&LttD~btVro-G=Uy1&Dct z`khb^NT&_ZeJ9_6ZSDyQypQmCE zytCKY8QOhWrf`oa523}DwF_vi`xL#ldNngruXVhP0Pr@+MgzIzl>)iw`V3#m5DA8^ zGV~`a2TGyHxF5(yxx!9?iet)T78ltFE;vq|U8OuT8B?htH}QH>Q7?+L#WX$E`7xUZl^~ zl*gBUzFZ$3Yoso$-CfbcXB9>^wf0!aoHQlMh}z`7D71;?y=5O@grtN$NM4>E->U4KzO;M#QZ@M^IrKa^`YbtG^LCOGyUB^VIuXV}9I6!uEHqgaOQ~x( zmaYeVy6$*p*+zaq*S{$nb~qzST`<7$3zl~*D3Ye|hFt&?L84V?^OkKxL8dH)$HMdu z;~RR95MYk*3J{21`)Nv4xz;3aBT*7P(Ub<1RFkyHD;ac4cS7wMF)t}_5rA~2+6tlF zCm{%Ul*#Nd^tVv<%MAQwcprj@58q{x#6iMqeTnr#(Y6@=t6*3~_p>{Q`g5hCJ0l8q z5W`P{XLC^p0;OayZXJj{-v99BnD}qhDqqqlIa%-SM`_0Dq^mchBNTA$CDn_sQ2XBzQ^Lvr0d}=QG5`Po literal 0 HcmV?d00001 diff --git a/python_symb/__pycache__/fraction.cpython-311.pyc b/python_symb/__pycache__/fraction.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07a9c8a38ea9b1faccdfe68defbf9236a6bda5e1 GIT binary patch literal 6417 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 literal 0 HcmV?d00001 diff --git a/python_symb/__pycache__/operator_file.cpython-311.pyc b/python_symb/__pycache__/operator_file.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59cbcc937a66476a65eaff7d7b1bfc8941849823 GIT binary patch literal 4547 zcmc&1O>Y~=b%wiK4k^);ELl;USe6{gHf7nekv6DX!%gBOZ4Yr$SiP_<7OUNrz4G!S zv&+_WiG~LQl>-CSp@oU$qC=rt@Ws9PC-g`f1WYUtpg>N(F;EVLPJM57xm=Q>0_4)+ z?%Ox>=Ht!#=0DTv6oK};&3_xG6NG$&oo<6ZHoBjqgghn&F{n-QG)F1oF*}x*a#CK- z$)YdW@w}2#D2b6@5JP@IjJT=nN`yRx*KfImpeaC0n27-z6Ep_28QoDnNe7&CD8YeOq1jM-Zx`}!+nG)uiPtxzbsnrjscPB!Mr zA6UBUu^XCgYwNZN6B4&7`Sqgh=)u**ClD{)Uxw~qKv5>iQG?`Sw@FSil3Qeh8i@zv zIoSZ==HgrA9?dCmY|3MwluWLOq z)6`vOwN!EM6bnIj)zDl`1(}z1H=w8vD0sCPs72kdZ1Ym7!Y5(>`;%R5UF!0V3_BkI z_-~DT&C(AOj}kB0>VT=J!j(|iq~oI!`MG1{xDdMQ=S7mc!w z__(TmR@Q8v!jWSkH+~W@FC8dLH*H&0_b8u%QNirHf?1(_0nnnHUjqQ4c7N@O6*pSV(ar=l%vq=BS3`jhTIUc z8Ct+j=!+3DSizuoxk0ADSp%LQjTqr3RSKRi^+!s~Azh1#^D{8lSK$<569~`=_%wi? zyZNdU-Z9fBi_k@cOyJG-+%I>CcE5XmmA$j zC{B+G19c_@xB>_|#twXk+hB+bE(qp_0Vf+1F5}7Oy%KkD`}v;dV9*868M>A~{yI!{ z(8R%gGE;x8d{L=SKm7dB=iuN{5UBjK)u+Ba8+7)2*SLJXMQwlv&}Ri<2>lW$ z|ECZt4~9@-Z=tRBEKK1)1}+Z`sP6zFtN6CoD{yesPq`vbVot>RX1*4QxYH3yhW3V* z?7Rm+#M`MBJN1$+zF>5`wuzI38N!Q9-vF z>;DH4gk8P_>#|Z1bTJxuapCO_o&vY?FNdM={=nt@sEgL_pgX+L*Z8_3IS3_gkLGX)FGolipI}k_#F*=pTaZNJi2^9FTBDK z)tBGG7>BhNin>bg1J)Jf+ko%IJbKTgm1yERVApIdziw#P`TH;hkJv{+)W|dK`NF@< znjaKip}fEMJh0>FdHm0_(FQLnyr zVAuw2`o;Biz6M>qwDS;PN1~Lr$(cs~( z6zYg!u>H$#arC<$X@*fqBf|jaUB!6;btXx{HHN|Vc|7|po*mF<$>iL2x^C=z@Z{}h z@{|Q{qF^`$ausn~7w{ik-*`tHiDnH@2&&RucnkQ-bE5Y}(`;;UAvl%f_p9Zz>fwu50@8?^2_Z-t%V~oOWoMRbyTx+j^ zrqE~5ykG00a0hVf7s&Ibw&k2 zl%sN76r`X@WiP}fSv9{QKtE_!nbRDnGC!P8KoJ*I=b59t74Kr7EAC+UXYK?EX1xx zNpgmW*Z7z$9ZN09S4E-PJ;p`lXhf39d`zxlA`-0oSSm^+{%C69u_MIAV{%MPrX~s1 zk!Eev4^lKdGf3m&bkDiQoyV4W>~3BZr$a<*)r%?h_h!HC>?jrMjr3tgUEmLK?Z}p zI~O+`0Z221PZ2T3OA;>yq5;sZKhJ=>if$jqcU2{{!jAi~iSyx1)(TS0~Ylz@j4%l>>2s2Ie z0X%bDkX6T<(ZoFe3Lzp1T1hvhck05A&<&|Lsp7b5o)hO`1iq%S?}&txsPRyRfTW2> zb?l0SJP%0f@uc%0^DL^kP)lp}eA(&#aN^d)`=QlPc8YdFw?cU)f3fHcl$?R=)I*z7 z7tP%pU-ge|^^X<%Pn7yk0IcXbS%T8`e988F_H@~1hr;3fAeI|`KfW5jHM%ld_Oz~; zA9~wzKhF>3FRt~k_g9eJ-li2Rr^_wvc?^it9}VTr>o2ZMmEA3OZYb`jAb;WiL(gw| z?)Ck)4>~}2cT#ByLN5FJzw#g2@*n#2#wJ%f{9@4`D)~c-mbZ6s(ibN_Uif68=s#Za zA6NXx%N}p;M{AZGE_d|g%&@AQx#C8iLlqzLv{n$dpT`ey)$Y1q1O!GL8jg2SqVmKoFj zfkcKeI%kYlf0D*P&&b-PLZ7r67PkW&ADGWCs(j!|QcG1n5z2?F3mOb_fgC|!#=mjJ z_#nZ=-d#OuzRhvi*#UwSkk7y1Yk6ur+WA^t^jm%#`Q-4b2P4_n!09&Sl)>bkC#{*x z87Z#qe6b9(+rCci7m=CoTAt*varS+_VVjX+cTJ7!+nhPLW`X2>hrza1H$h6l?|NUG zz>&J=X)Qt_!yvq%lN^N#&gFy{l?jcH3Ds&cN}=wVD1ocYRbGlq7x+0@_0$0_#;;tJ zRVF2-2>m+5PDjw=JH~Uo5aWZa9tGIM6nMaB?7C`AiIO}QPZN50sagnbbg^5qc3lsYDu~u)IzhpMe`t!$Xt@p~F_KoJ-&3RJv43<2Dik8b> zU(TDmeE0ROwr3PAmpvUD?%VEyspuIhd4?1%SIPd~GqTk-qG-9?)|V&iiBH9?wv$TR z$+EwDWBT@VVf^#ZuR|YCell6~kCpsmihr!ap!WWH(ruW*b=$SpyxyFHr|b>f>CN@) z&l=Q?D}bc{7vI1?y*;^J{aNdU1wi=VeXcP6#hFh+U%s?8bVeCE^B}FvT>NvUboOV; zWv+Bt+&U{NXT>M1?!bKL(v8BQOS2+h|5|UwhMXhth4k@-nu!}0qw$qoLZak={8_*7_321Df`UqXtTrPki?Y$Wq9Vr+Fo(`G_RXZ*6 zB$QkIDeKo&6i)McHGLJRE>lVx-DlB5XZPK61-aNWQtH`rxT>~DBpHnhk;r2Ek@Pb! z1fO1bMv4nZ#!ifmJ$>Z(=*eJkx@t6fV9^U4wVQhl%2I#^rGZByl;VSfsv{Dan}=&Z zABhmUf7K!{q~L<3@gh`OYLi&~e-fFCCwM}SSwhbULhq!SDQiZpTB~<2LT}ibXHac( z)&D8Fx1j$eh?^FZw4f1<=2FP_pru--wfmZCj|<=fz$-{3Na5xb-1uZ&&NNoh#ZF~O zrKDX!BD(uv>4iXLsaMe5mUi3Gu2Qy8Qsm|n{3Mx$fixbH=)b@UgE20nLB*KMs8881 zm(ll?gXJ;`DEs9y>Q#(++v>6)E>heq3=6mF&Y=%eMBTr|?ec@HqV9DSA(pyr;6w+sK?X zXN4kaEumHgwN_XwW-Im!gOvs zhv_w|b!8&Q6`kEBXE$_Nd-j!;;hX1|&(r%@x8m&1PZga5CFj6iB&AdwU6Au9ijF|Z W5h$_)C3ZlmWw_U{T`-Sk*8c?syX@Ei literal 0 HcmV?d00001 diff --git a/python_symb/__pycache__/symbols.cpython-311.pyc b/python_symb/__pycache__/symbols.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60a6d735c0a11d4b315e2b0b21dfc5561088b0cf GIT binary patch literal 1907 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 literal 0 HcmV?d00001 diff --git a/python_symb/expr.py b/python_symb/expr.py new file mode 100644 index 0000000..a8b2c4f --- /dev/null +++ b/python_symb/expr.py @@ -0,0 +1,38 @@ +from __future__ import annotations +from tree import Tree +from operator_file import Add, Mul +from operator_file import UnaryOperator, BinOperator +from typing import List +from symbols import Var +from parse import infix_str_to_postfix + + +class Expr(Tree): + def __init__(self, value, children=None): + super().__init__(value, children if children else []) + + @staticmethod + def from_postfix_list(postfix: List): + + def aux(): + first = postfix.pop() + match first: + case int() | Var(): + return Tree(first) + case UnaryOperator(): + return Tree(first, [aux()]) + case BinOperator(): + return Tree(first, [aux(), aux()]) + + return aux() + + @staticmethod + def from_infix_str(expr_str): + expr_rev_polish = infix_str_to_postfix(expr_str) + return Expr.from_postfix_list(expr_rev_polish) + + + + + + diff --git a/python_symb/fraction.py b/python_symb/fraction.py new file mode 100644 index 0000000..8f09354 --- /dev/null +++ b/python_symb/fraction.py @@ -0,0 +1,131 @@ +from __future__ import annotations +from typing import Iterable, Generator +from tools import gcd + + +class Fraction: + """ + Should represent a fraction not a division + """ + __slots__ = ['num', 'den'] + __match_args__ = ("num", "den") + + #todo check if num and den are part of a domain, so a/b as a meaning a gcd work well + #todo implement __iadd__ etc... if performance needed + + def __init__(self, *args): + match args: + case num, den: + self.num = num + self.den = den + case x, : + self.num = x + self.den = 1 + + def __repr__(self): + return f'Fractions({self.num}, {self.den})' + + def simplify_gcd(self): + """Simplify fraction by diving num and den by their gcd + return None""" + match self.num, self.den: + case int(num), int(den): + gcd_ = gcd(num, den) + self.num //= gcd_ + self.den //= gcd_ + # can be completed with others objects that support gcd like polynomials etc... + + def simplify_to_num(self): + """from frac(a, 1) return a.""" + if self.den == 1: + return self.num + def simplify_nested(self, rec=True): + """simplify nested fractions. + Fractions(1, Fractions(1, Fractions(1, Fractions(1, 2)))) -> Fractions(2, 1) + For one simplification step put rec=False + + return None""" + + def aux(fract): + match fract: + case Fraction(Fraction(a, b), Fraction(c, d)): + fract.num = a * d + fract.den = b * c + case Fraction(num, Fraction(a, b)): + fract.num = num * b + fract.den = a + case Fraction(Fraction(a, b), den): + fract.num = a + fract.den = b * den + + if rec: + num, den = self.num, self.den + if isinstance(num, Fraction) or isinstance(den, Fraction): + aux(fract) + + aux(self) + + def simplify_all_(self): + self.simplify_gcd() + self.simplify_nested() + res = self.simplify_to_num() + if res: + return res + + return self + + def __add__(self, other): + match other: + case int(x): + return Fraction(self.num + self.den * x, self.den) + case Fraction(num, den): + result = Fraction(self.num * den + num * self.den, self.den * den) + return result + return ValueError + + def __radd__(self, other): + return other + self + + def __neg__(self): + return Fraction(-self.num, self.den) + + def __mul__(self, other): + match other: + case int(x): + return Fraction(self.num * x, self.den) + case Fraction(num, den): + result = Fraction(self.num * num, self.den * den) + return result + return ValueError + + def __rmul__(self, other): + return self*other + + def __truediv__(self, other): + match other: + case int(x): + return Fraction(self.num, self.den * x) + case Fraction(num, den): + return Fraction(self.num * den, self.den * num) + + def __rtruediv__(self, other): + res = self/other + return Fraction(res.den, res.num) + + +if __name__ == "__main__": + a = Fraction(1, 2) + a += 1 + print(a) + + + + + + + + + + + + diff --git a/python_symb/integers.py b/python_symb/integers.py new file mode 100644 index 0000000..cc56d3b --- /dev/null +++ b/python_symb/integers.py @@ -0,0 +1,3 @@ +""" +Python int is already an arbitrary precision integer, so we don't need to implement it. +""" diff --git a/python_symb/operator_file.py b/python_symb/operator_file.py new file mode 100644 index 0000000..1c935e9 --- /dev/null +++ b/python_symb/operator_file.py @@ -0,0 +1,62 @@ +from __future__ import annotations +from typing import Dict, Callable +from symbols import Symbols + + +class Operator(Symbols): + instances = [] + def __init__(self, name : str, precedence: int, call: Callable): + super().__init__(name) + self.precedence = precedence + self.call = call + Operator.instances.append(self) + + def __repr__(self): + return f'{self.name}' + + +class UnaryOperator(Operator): + instances = [] + + def __init__(self, name: str, precedence: int, call: Callable): + UnaryOperator.instances.append(self) + super().__init__(name, precedence, call) + + def __call__(self, expr): + return self.call(expr) + + +class BinProperties: + + def __init__(self, associativity: bool, commutativity: True, + left_distributivity: Dict[str, bool], right_distributivity: Dict[str, bool]): + + self.associativity = associativity + self.commutativity = commutativity + self.left_distributivity = left_distributivity + self.right_distributivity = right_distributivity + + +class BinOperator(Operator): + instances = [] + + def __init__(self, name: str, precedence: int, properties: BinProperties, call: Callable): + BinOperator.instances.append(self) + super().__init__(name, precedence, call) + self.properties = properties + + def __call__(self, left, right): + return self.call(left, right) + + +AddProperties = BinProperties(True, True, {'*': True}, {'*': True}) +Add = BinOperator('+', 2, AddProperties, lambda x, y: x + y) + + +MulProperties = BinProperties(True, True, {'+': True}, {'+': True}) +Mul = BinOperator('*', 3, MulProperties, lambda x, y: x * y) +Sin = UnaryOperator('sin', 0, lambda x: x) + +Min = BinOperator('-', 1, AddProperties, lambda x, y: x - y) +# Pns = UnaryOperator('()', 0, lambda x: x) + diff --git a/python_symb/parse.py b/python_symb/parse.py new file mode 100644 index 0000000..da16714 --- /dev/null +++ b/python_symb/parse.py @@ -0,0 +1,114 @@ +from __future__ import annotations +from typing import List, Union +from operator_file import Add, Mul, Min, BinOperator, UnaryOperator +from symbols import Symbols, Var +from fraction import Fraction + +x, y = Var('x'), Var('y') + +ParenthesisLeft = Symbols('(') +ParenthesisRight = Symbols(')') + +Number = Union[int, float, Fraction] + +name_to_symbol = {sy.name:sy for sy in Symbols.instances} + +print(name_to_symbol) + +""" +example1 = "a + b * c + d" +- +example2 = 2*x+3*y*(4+sin(5)) +- +example3 = "(a + b) * (c + -d))" +should return Tree(Expr('*', [Expr('+', [Expr('a'), Expr('b')]), Expr('+', [Expr('c'), Expr('-', [Expr('d')])])])) +""" + + +def preprocess(expr: str) -> List: + """ + Preprocesses a string expression to a list of symbols and numbers + :param expr: string expression + :return: list of symbols and numbers + """ + return_list = [] + expr = expr.strip() + expr = expr.replace(' ', '') + m = max([len(sy) for sy in name_to_symbol.keys()]) + i = 0 + while i < len(expr): + found = False + for j in range(m, 0, -1): + word = expr[i:i+j] + if word in name_to_symbol or word.isdigit(): + if word in name_to_symbol: + return_list.append(name_to_symbol[word]) + else: + return_list.append(int(word)) + i += j + found = True + break + + if not found: + raise ValueError(f'Invalid expression: {expr} at index {i}\n') + return return_list + + +def return_to_string(expr: List) -> str: + """ + Returns a string expression from a list of symbols and numbers + :param expr: list of symbols and numbers + :return: string expression + """ + return ' '.join([str(sy) for sy in expr]) + + +def infix_to_postfix(expr: List) -> List: + global ParenthesisLeft, ParenthesisRight + """ + Converts an infix string expression (standard) to a postfix expression (reverse polish notation) + :param expr: infix expression + :return: postfix expression + + use shunting yard algorithm + """ + op_stack = [] + postfix = [] + for sy in expr: + match sy: + case int() | float() | Fraction() | Var(): + postfix.append(sy) + case _ if sy == ParenthesisLeft: + op_stack.append(sy) + case _ if sy == ParenthesisRight: + while op_stack[-1] != ParenthesisLeft: + postfix.append(op_stack.pop()) + op_stack.pop() + case UnaryOperator(): + op_stack.append(sy) + case BinOperator(): + while op_stack and op_stack[-1] != ParenthesisLeft and op_stack[-1].precedence >= sy.precedence: + postfix.append(op_stack.pop()) + op_stack.append(sy) + while op_stack: + postfix.append(op_stack.pop()) + return postfix + + +def infix_str_to_postfix(expr): + return infix_to_postfix(preprocess(expr)) + + +if __name__ == "__main__": + + expr = "(x+7)*y+sin(24-2*(1-5))" + prep = preprocess(expr) + print(prep) + post = infix_to_postfix(prep) + print(post) + print(return_to_string(post)) + + + + + diff --git a/python_symb/symbols.py b/python_symb/symbols.py new file mode 100644 index 0000000..f853677 --- /dev/null +++ b/python_symb/symbols.py @@ -0,0 +1,31 @@ +from __future__ import annotations + + +class Symbols: + """ + All maths things (other than number) that will be parsed need to be of "Symbols" class + """ + instances = [] + + def __init__(self, name): + self.name = name + Symbols.instances.append(self) + + def __repr__(self): + return self.name + + def __str__(self): + return self.name + + +class Var(Symbols): + """ + variable, like 'x' in x+2 + """ + instances = [] + + def __init__(self, name): + super().__init__(name) + print(self.__class__) + self.__class__.instances.append(self) + diff --git a/python_symb/tools.py b/python_symb/tools.py new file mode 100644 index 0000000..3d3b00a --- /dev/null +++ b/python_symb/tools.py @@ -0,0 +1,14 @@ +from __future__ import annotations +from typing import * + + +def gcd(a, b): + + if b > a: + return gcd(b, a) + + if b == 0: + return a + + return gcd(b, a % b) + diff --git a/python_symb/tree.py b/python_symb/tree.py new file mode 100644 index 0000000..ec1a252 --- /dev/null +++ b/python_symb/tree.py @@ -0,0 +1,80 @@ +from __future__ import annotations +from typing import Iterable, Generator +from collections import deque + + +class Tree: + """ + Ultra generic Test class. Can be used to represent any Test structure. + + value : value of the node. Can be a binary operator like "+", a ternary operator like "if", a number etc... + + depth_first_order : the default order of the node in the depth first traversal. Used to implement the depth_first method. + 0 is pre-order, 1 is in-order (for binary Test), -1 is post-order. + for instance to write "a ? b : c" you need to write Tree("?", [Tree("a"), Tree("b"), Tree("c")]) + and set the depth_first_order of the "?" node to 1. + + children : the children of the node. Can be empty. + """ + __slots__ = ['value', 'children', 'depth_first_order'] + + def __init__(self, value, children: Iterable[Tree] = None, depth_first_order: int = 0): + self.value = value + self.depth_first_order = depth_first_order + self.children = children if children else [] + + def __repr__(self) -> str: + return f'Tree({self.value}, {self.children})' if self.children else f'Leaf({self.value})' + + def height(self) -> int: + return 1 + max((child.height() for child in self.children), default=0) + + def size(self) -> int: + return 1 + sum(child.size() for child in self.children) + + def breadth_first(self) -> Generator[Tree]: + + queue = deque([self]) + + while queue: + poped = queue.popleft() + for child in poped.children: + queue.append(child) + + yield poped + + def depth_first_default(self) -> Generator[Tree]: + + def aux(tree): + n = len(tree.children) + if not tree.children: + yield tree + + for i, child in enumerate(tree.children): + if i == tree.depth_first_order: + yield tree + + yield from aux(child) + + if tree.depth_first_order == -1: + yield tree + + yield from aux(self) + + def depth_first_pre_order(self) -> Generator[Tree]: + + def aux(tree): + yield tree + for child in tree.children: + yield from aux(child) + + yield from aux(self) + + def depth_first_post_order(self) -> Generator[Tree]: + + def aux(tree): + for child in tree.children: + yield from aux(child) + yield tree + + yield from aux(self) \ No newline at end of file diff --git a/python_symb/visual.py b/python_symb/visual.py new file mode 100644 index 0000000..758d02a --- /dev/null +++ b/python_symb/visual.py @@ -0,0 +1,92 @@ +import tkinter as tk +from expr import Expr + +class Visual: + def __init__(self): + self.root = tk.Tk() + self.root.title("Visual") + self.root.geometry("800x800") + + # Add a canvas on the left side of the window + self.tree_canvas = tk.Canvas(self.root, width=500, height=500, bg="white") + self.tree_canvas.pack(side=tk.LEFT, expand=False) + + # Create right frame + self.right_frame = tk.Frame(self.root, width=300, height=500, background="grey") + + + # Add a label with the text "Input" on top of the right frame, should not take all the space + self.input_label = tk.Label(self.right_frame, text="Input (infix string):") + + + # Add a entry below the label with default text "(5+2)*3" + self.input_entry = tk.Entry(self.right_frame, width=50) + self.input_entry.insert(0, "(5+2)*3") + + + + # Add a button below the entry with the text "To Tree" + self.input_button = tk.Button(self.right_frame, text="To Tree", command=self.show_tree) + + # Pack the widgets, make sure they are not expanding, and all are fixed size + + + self.input_label.pack() + self.input_entry.pack() + self.input_button.pack() + self.right_frame.pack(side=tk.LEFT, expand=False) + + + + + + + self.root.mainloop() + + def show_tree(self): + # Get the text from the entry + text = self.input_entry.get() + + # Clear the canvas + self.tree_canvas.delete("all") + tree = Expr.from_infix_str(text) + + # Draw the tree + self.draw_tree(tree) + + def create_circle(self, x, y, r, color): # center coordinates, radius + x0 = x - r + y0 = y - r + x1 = x + r + y1 = y + r + return self.tree_canvas.create_oval(x0, y0, x1, y1, fill=color) + + def draw_tree(self, tree, first_x=250, first_y=50, x_offset=100, y_offset=100): + + children = tree.children + n = len(children) + for i in range(n): + child = children[i] + x = first_x + (i - (n - 1) / 2) * x_offset + y = first_y + y_offset + + #Link the node to the parent + self.tree_canvas.create_line(first_x, first_y, x, y) + # Draw the node + self.draw_tree(child, int(x), y, x_offset, y_offset) + + # Draw the root node + self.create_circle(first_x, first_y, 20, "red") + self.tree_canvas.create_text(first_x, first_y, text=str(tree.value)) + +if __name__ == "__main__": + Visual() + + + + + + + + +