def ft_pow(): if (isinstance(self.right, X_class.X) and self.right.power) or isinstance(self.right, Operator): ft_error("Error : Invalid power term.") if isinstance(self.right, X_class.X): self.right = float(self.right.factor) self.value = self.left**self.right
def __truediv__(self, other): if not other or (isinstance(other, X_class.X) and not other.factor): ft_error("Error : division by zero.") if isinstance(other, X_class.X) or isinstance(other, float): return self.left / other + self.right / other if isinstance(other, Operator) and self == other: return X_class.X(power=0.0) else: ft_error("Equation non reductible and hence unsolvable.")
def __pow__(self, other): if other % 1.0: ft_error("Error : power of operations must be integer.") if other == 1: return self elif other == 0: return X_class.X(power=0.0) elif other < 0: return X_class.X(power=0.0) / (self**(-other)) elif isinstance(other, float): return self * self.__pow__(other - 1)
def __truediv__(self, other): if isinstance(other, X) and not other.factor: ft_error("Error : division by zero.") if isinstance(other, X): new_obj = X() new_obj.factor = self.factor / other.factor new_obj.power = self.power - other.power return new_obj if isinstance(other, float): self.factor = self.factor / other return self ft_error("Equation non reductible and unsolvable.") return None
def build_trees(self): """ Method which builds the left and right trees from the input string, using a syntactic binary tree through the shunting_yard algorithm """ operator_stack = deque() list_eq = self.equation_str.split( '=')[0] + '-' + '(' + self.equation_str.split('=')[1] + ')' list_eq = list_eq.replace('(+', '(0+').replace('(-', '(0-') j = 0 while j < len(list_eq): n = '' while j < len(list_eq) and ('0' <= list_eq[j] <= '9' or list_eq[j] == '.'): n = n + list_eq[j] j += 1 if n: j -= 1 self.tree.append(X(factor=float(n), power=0.0)) elif list_eq[j] == 'X': self.tree.append(X()) elif list_eq[j] in dic_precedence.keys(): while operator_stack and operator_stack[-1] != '('\ and dic_precedence[operator_stack[-1].oper] >= dic_precedence[list_eq[j]]: self.tree.append(operator_stack.pop()) operator_stack.append(Operator(list_eq[j])) elif list_eq[j] == '(': operator_stack.append(list_eq[j]) elif list_eq[j] == ')': while operator_stack and operator_stack[-1] != '(': self.tree.append(operator_stack.pop()) if not operator_stack: ft_error("Error : mismatched parentheses.") if operator_stack[-1] == '(': operator_stack.pop() j += 1 while operator_stack: if operator_stack[-1] == '(': ft_error("Error : mismatched parentheses.") else: self.tree.append(operator_stack.pop()) return None
def parsing_errors(self): if not self.equation_str: ft_error("Syntax error : please provide a valid equation.") self.equation_str = self.equation_str.replace(' ', '') if '=' not in self.equation_str: ft_error("Syntax error : please provide a valid equation.") dic_error = { r"(^=|=$|=.*=)": "Syntax error : please provide a valid equation.", r"[^X\+0-9\(\)=\^\*\-\./]": "Syntax error : unauthorized token in equation.", r"(^[\*\^/]|=[\*\^/])": "Syntax error : first element is a wrong operator (ony + and - allowed)", r"[\*\^/\+\-=]$": "Syntax error : last element is not a valid token to end the equation.", r"([X0-9]\(|\)[X0-9])": "Syntax error : missing operator around parentheses", r"(\.[^0-9]|[^0-9]\.)": "Syntax error : need a digit before and after a dot.", r"([0-9]X|X[0-9])": "Syntax error : an operator has to precede or succeed an X element.", r"([=\(\-\+\^\*/][\*/\^]|[\*\^\+\-/\(]=|[\-\+\^\*/][\+\-\*/\^])": "Syntax error : two operators side by side which cannot be combined.", r"\(\)": "Syntax error : empty parentheses.", r"(\([^\)]*=|.*=[^\(]*\))": "Syntax error : error in parentheses.", } for k, v in dic_error.items(): reg = re.compile(k) if reg.search(self.equation_str): ft_error(v) return None
def solve_equation(self): def deal_negatives(): new_dico_elem = {} for k, v in self.reduced_elem.items(): new_dico_elem[k - self.min_degree] = v * X(power=-self.min_degree) self.reduced_elem = new_dico_elem self.ft_getdegree() self.new_reduced_str = self.reduced_equation_str() print( "There is at least one degree strictly lower than 0. Therefore, we multiply each element by the " "absolute value of the lowest degree to try to solve this equation.\n" ) print(f"New reduced form : {self.new_reduced_str}") print( f"Polynomial degree of the new equation : {self.max_degree}\n") return None a = 0 b = 0 c = 0 if Equation.flag_n: if self.min_degree < 0: deal_negatives() elif self.min_degree < 0: ft_error( "There exists a degree strictly lower than 0. This cannot be solved. Use -n flag to try to solve this equation.\n" ) if self.max_degree > 2: ft_error( "This equation is of a degree higher than 2. This algorithm is not built to solve it.\n" ) for k, v in self.reduced_elem.items(): if k == 2: a = v.factor elif k == 1: b = v.factor elif k == 0: c = v.factor if not a and not b and not c: if 'X' not in self.equation_str: print("Always true.\n") else: print("Always true. All real numbers are solutions.\n") sys.exit(0) elif not a and not b and c: ft_error("Impossible equation. No solution.\n") elif (not a and b and not c) or (a and not b and not c): self.solution1 = 0 self.solution1_str = f"X = {self.solution1}" elif not a and b and c: self.solution1 = -c / b self.solution1_str = f"X = -({self.num_format(c)}) / {self.num_format(b)} = {self.num_format(self.solution1)}" else: self.delta = b**2 - 4 * a * c self.delta_str = f"Δ = {self.num_format(b)}^2 - 4 * {self.num_format(a)} * {self.num_format(c)} = {self.num_format(self.delta)}" if self.delta == 0: self.solution1 = -b / (2 * a) self.solution1_str = f"X = -({self.num_format(b)}) / (2 * {self.num_format(a)}) = {self.num_format(self.solution1)}" elif self.delta > 0: sq_delta_pos = sqrt(self.delta) self.solution1 = (-b - sq_delta_pos) / (2 * a) self.solution2 = (-b + sq_delta_pos) / (2 * a) self.solution1_str = ( f"X1 = (-({self.num_format(b)}) - sqrt({self.num_format(self.delta)}) / (2 * {a}) = " f"(-({self.num_format(b)}) - {self.num_format(sq_delta_pos)} / (2 * {self.num_format(a)}) = {self.num_format(self.solution1)}" ) self.solution2_str = ( f"X2 = (-({self.num_format(b)}) + sqrt({self.num_format(self.delta)}) / (2 * {self.num_format(a)}) = " f"(-({self.num_format(b)}) + {self.num_format(sq_delta_pos)} / (2 * {self.num_format(a)}) = {self.num_format(self.solution2)}" ) elif self.delta < 0: sq_delta = sqrt(-self.delta) if not b: factor = sq_delta / (2 * a) if factor == 1: self.solution1 = "-i" self.solution2 = "i" else: self.solution1 = f"-i * {self.num_format(factor)}" self.solution2 = f"i * {self.num_format(factor)}" else: self.solution1 = f"({self.num_format(-b)} - i * {self.num_format(sq_delta)}) / {self.num_format(2 * a)}" self.solution2 = f"({self.num_format(-b)} + i * {self.num_format(sq_delta)}) / {self.num_format(2 * a)}" self.solution1_str = ( f"X1 = (-({self.num_format(b)}) - i * sqrt({self.num_format(-self.delta)}) / (2 * {self.num_format(a)}) " f"= (-({self.num_format(b)}) - i * {self.num_format(sq_delta)}) / {self.num_format(2 * a)} = {self.num_format(self.solution1)}" ) self.solution2_str = ( f"X2 = (-({self.num_format(b)}) + i * sqrt({self.num_format(-self.delta)}) / (2 * {self.num_format(a)}) " f"= (-({self.num_format(b)}) + i * {self.num_format(sq_delta)} / {self.num_format(2 * a)} = {self.num_format(self.solution2)}" ) return None