Delete python_symb directory
This commit is contained in:
parent
7e1ff3b6b2
commit
299fc1a058
16 changed files with 0 additions and 565 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,38 +0,0 @@
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,131 +0,0 @@
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
"""
|
|
||||||
Python int is already an arbitrary precision integer, so we don't need to implement it.
|
|
||||||
"""
|
|
|
@ -1,62 +0,0 @@
|
||||||
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)
|
|
||||||
|
|
|
@ -1,114 +0,0 @@
|
||||||
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))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
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)
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
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)
|
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
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)
|
|
|
@ -1,92 +0,0 @@
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue