diff --git a/python_symb/__pycache__/tools.cpython-311.pyc b/python_symb/__pycache__/tools.cpython-311.pyc new file mode 100644 index 0000000..9c4ba3f Binary files /dev/null and b/python_symb/__pycache__/tools.cpython-311.pyc differ diff --git a/python_symb/__pycache__/tree.cpython-311.pyc b/python_symb/__pycache__/tree.cpython-311.pyc new file mode 100644 index 0000000..15179bf Binary files /dev/null and b/python_symb/__pycache__/tree.cpython-311.pyc differ diff --git a/python_symb/expr.py b/python_symb/expr.py new file mode 100644 index 0000000..9c090b8 --- /dev/null +++ b/python_symb/expr.py @@ -0,0 +1,13 @@ +from __future__ import annotations +from typing import Union, List, Tuple, Optional, Dict, Callable +from tree import Tree +from operator import Add, Mul, Neg, Parenthesis + + +class Expr(Tree): + + def __init__(self, value, children=None): + super().__init__(value, children) + + + diff --git a/python_symb/fractions.py b/python_symb/fractions.py new file mode 100644 index 0000000..5c4d00d --- /dev/null +++ b/python_symb/fractions.py @@ -0,0 +1,131 @@ +from __future__ import annotations +from typing import Iterable, Generator +from tools import gcd + + +class Fractions: + """ + 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 Fractions(Fractions(a, b), Fractions(c, d)): + fract.num = a * d + fract.den = b * c + case Fractions(num, Fractions(a, b)): + fract.num = num * b + fract.den = a + case Fractions(Fractions(a, b), den): + fract.num = a + fract.den = b * den + + if rec: + num, den = self.num, self.den + if isinstance(num, Fractions) or isinstance(den, Fractions): + 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 Fractions(self.num + self.den*x, self.den) + case Fractions(num, den): + result = Fractions(self.num*den + num*self.den, self.den*den) + return result + return ValueError + + def __radd__(self, other): + return other + self + + def __neg__(self): + return Fractions(-self.num, self.den) + + def __mul__(self, other): + match other: + case int(x): + return Fractions(self.num*x, self.den) + case Fractions(num, den): + result = Fractions(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 Fractions(self.num, self.den*x) + case Fractions(num, den): + return Fractions(self.num*den, self.den*num) + + def __rtruediv__(self, other): + res = self/other + return Fractions(res.den, res.num) + + +if __name__ == "__main__": + a = Fractions(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.py b/python_symb/operator.py new file mode 100644 index 0000000..038fd7e --- /dev/null +++ b/python_symb/operator.py @@ -0,0 +1,56 @@ +from __future__ import annotations +from typing import Dict, Callable + + +class Operator: + __slots__ = 'name', 'precedence', 'call' + + def __init__(self, name: str, precedence: int, call: Callable): + self.name = name + self.precedence = precedence + self.call = call + + +class UnaryOperator(Operator): + __slots__ = 'name', 'precedence' + + def __init__(self, name: str, precedence: int, call: Callable): + super().__init__(name, precedence, call) + + def __call__(self, expr): + return self.call(expr) + + +class BinProperties: + __slots__ = 'associativity', 'commutativity', 'left_distributivity', 'right_distributivity' + + 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): + __slots__ = 'name', 'precedence', 'properties' + + def __init__(self, name: str, precedence: int, properties: BinProperties, call: Callable): + 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('+', 1, AddProperties, lambda x, y: x + y) + + +MulProperties = BinProperties(True, True, {'+': True}, {'+': True}) +Mul = BinOperator('*', 2, MulProperties, lambda x, y: x * y) + +Neg = UnaryOperator('-', -1, lambda x: -x) +Parenthesis = UnaryOperator('()', 0, lambda x: x) + diff --git a/python_symb/symbols.py b/python_symb/symbols.py new file mode 100644 index 0000000..1ec2079 --- /dev/null +++ b/python_symb/symbols.py @@ -0,0 +1,8 @@ +from __future__ import annotations + + +class Symbols: + __slots__ = 'name' + + def __init__(self, name): + self.name = name 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..758339b --- /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})' + + 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